way too many changes to list, oops. big rewrite.

This commit is contained in:
2025-05-27 03:38:03 +10:00
parent 16951a9beb
commit 4a21701a35
663 changed files with 7389 additions and 3283 deletions

View File

@ -0,0 +1,14 @@
class_name Affector extends Node
var damage_particle_scene: PackedScene = preload("res://Scenes/damage_particle.tscn")
func apply_effect(effect: Effect, targets: Array[EnemyController]) -> void:
pass
func spawn_damage_indicator(damage: int, pos: Vector3) -> void:
var marker: Sprite3D = damage_particle_scene.instantiate()
get_tree().root.add_child(marker)
marker.set_number(damage)
marker.position = pos

View File

@ -0,0 +1 @@
uid://5gl7yyrvjeow

View File

@ -0,0 +1,13 @@
class_name AreaAffector extends Affector
@export var shapecast: ShapeCast3D
func apply_effect(effect: Effect, targets: Array[EnemyController]) -> void:
for i: int in shapecast.get_collision_count():
var enemy: EnemyController = shapecast.get_collider(i) as EnemyController
#print(shapecast.get_collider(i))
if targets.has(enemy):
enemy.apply_effect(effect)
if Data.preferences.display_tower_damage_indicators and effect.damage > 0:
spawn_damage_indicator(effect.damage, enemy.sprite.global_position)

View File

@ -0,0 +1 @@
uid://duvfverjdqodj

View File

@ -0,0 +1,8 @@
class_name DirectAffect extends Affector
func apply_effect(effect: Effect, targets: Array[EnemyController]) -> void:
for enemy: EnemyController in targets:
enemy.apply_effect(effect)
if Data.preferences.display_tower_damage_indicators and effect.damage > 0:
spawn_damage_indicator(effect.damage, enemy.sprite.global_position)

View File

@ -0,0 +1 @@
uid://8d0a4uc2i0ti

View File

@ -0,0 +1,21 @@
class_name SpawnAffect extends Affector
@export var spawn_scene: PackedScene
@export var tower: Tower
var force: float = 150.0
var projectile_id: int = 0
func apply_effect(effect: Effect, targets: Array[EnemyController]) -> void:
for target: EnemyController in targets:
var projectile: Projectile = spawn_scene.instantiate() as Projectile
if projectile is HomingProjectile:
projectile.target = target
projectile.position = tower.yaw_model.global_position
projectile.effect = effect
projectile.direction = -tower.yaw_model.global_transform.basis.z
projectile.force = force
projectile.name = tower.base_name + str(tower.owner_id) + str(projectile_id)
get_tree().root.add_child(projectile)
projectile_id += 1

View File

@ -0,0 +1 @@
uid://ccu1u6nqkjxki

View File

@ -0,0 +1 @@
uid://d147vuqksqhis

View File

@ -2,4 +2,5 @@ class_name EnemyMovement extends Node
@export var character: CharacterBody3D
var astar: AStarGraph3D
var distance_remaining: float = 0.0

View File

@ -0,0 +1 @@
uid://cy0htr7710hnn

View File

@ -0,0 +1,143 @@
class_name LeapingController extends PathingController
@export var eastl: Label
@export var westl: Label
@export var northl: Label
@export var southl: Label
@export var easts: Sprite3D
@export var wests: Sprite3D
@export var norths: Sprite3D
@export var souths: Sprite3D
@export var box: CSGBox3D
var tolerance: float = 50.0
var jumping: bool = false
func _process(delta: float) -> void:
tolerance = remap(character.health.current_health, 10, 50, character.health.max_health * 0.20, character.health.max_health)
func _physics_process(delta: float) -> void:
if !path or jumping:
return
var distance_travelled: float = (character.stats.movement_speed * clampf(character.movement_speed_penalty, 0.0, 1.0)) * delta
distance_remaining -= distance_travelled
path_progress += distance_travelled
var sample: Transform3D = path.sample_baked_with_rotation(path_progress, true)
character.global_position = sample.origin
character.look_at(character.global_position + -sample.basis.z)
var closest_point: int = astar.astar.get_closest_point(character.global_position, false)
box.global_position = astar.astar.get_point_position(closest_point)
var east: int = astar.get_east_point(closest_point)
var west: int = astar.get_west_point(closest_point)
var north: int = astar.get_north_point(closest_point)
var south: int = astar.get_south_point(closest_point)
#if east >= 0 and astar.astar.is_point_disabled(east):
#eastl.text = "fuck no"
#else:
#eastl.text = "yeah"
#if west >= 0 and astar.astar.is_point_disabled(west):
#westl.text = "fuck no"
#else:
#westl.text = "yeah"
#if north >= 0 and astar.astar.is_point_disabled(north):
#northl.text = "fuck no"
#else:
#northl.text = "yeah"
#if south >= 0 and astar.astar.is_point_disabled(south):
#southl.text = "fuck no"
#else:
#southl.text = "yeah"
norths.global_position = character.global_position + Vector3(-1.0, 1.0, 0.0)
souths.global_position = character.global_position + Vector3(1.0, 1.0, 0.0)
easts.global_position = character.global_position + Vector3(0.0, 1.0, -1.0)
wests.global_position = character.global_position + Vector3(0.0, 1.0, 1.0)
if east >= 0:
if astar.astar.is_point_disabled(east):
var further_point: int = astar.get_east_point(east)
if further_point >= 0 and !astar.astar.is_point_disabled(further_point):
var expected_offset: float = path.get_closest_offset(character.global_position + Vector3(0.0, 0.0, -4.0))
var current_offset: float = path.get_closest_offset(character.global_position)
var gain: float = expected_offset - current_offset
if gain >= tolerance:
distance_remaining -= gain
path_progress += gain
leap(Vector3(0.0, 0.0, -4.0))
#eastl.text = str(gain)
#easts.visible = true
else:
eastl.text = "cant"
else:
eastl.text = "clear"
else:
eastl.text = "invalid"
if west >= 0:
if astar.astar.is_point_disabled(west):
var further_point: int = astar.get_west_point(west)
if further_point >= 0 and !astar.astar.is_point_disabled(further_point):
var expected_offset: float = path.get_closest_offset(character.global_position + Vector3(0.0, 0.0, 4.0))
var current_offset: float = path.get_closest_offset(character.global_position)
var gain: float = expected_offset - current_offset
if gain >= tolerance:
distance_remaining -= gain
path_progress += gain
leap(Vector3(0.0, 0.0, 4.0))
#westl.text = str(gain)
#wests.visible = true
else:
westl.text = "cant"
else:
westl.text = "clear"
else:
westl.text = "invalid"
if north >= 0:
if astar.astar.is_point_disabled(north):
var further_point: int = astar.get_north_point(north)
if further_point >= 0 and !astar.astar.is_point_disabled(further_point):
var expected_offset: float = path.get_closest_offset(character.global_position + Vector3(-4.0, 0.0, 0.0))
var current_offset: float = path.get_closest_offset(character.global_position)
var gain: float = expected_offset - current_offset
if gain >= tolerance:
distance_remaining -= gain
path_progress += gain
leap(Vector3(-4.0, 0.0, 0.0))
#northl.text = str(gain)
#norths.visible = true
else:
northl.text = "cant"
else:
northl.text = "clear"
else:
northl.text = "invalid"
if south >= 0:
if astar.astar.is_point_disabled(south):
var further_point: int = astar.get_south_point(south)
if further_point >= 0 and !astar.astar.is_point_disabled(further_point):
var expected_offset: float = path.get_closest_offset(character.global_position + Vector3(4.0, 0.0, 0.0))
var current_offset: float = path.get_closest_offset(character.global_position)
var gain: float = expected_offset - current_offset
if gain >= tolerance:
distance_remaining -= gain
path_progress += gain
leap(Vector3(4.0, 0.0, 0.0))
#southl.text = str(gain)
#souths.visible = true
else:
southl.text = "cant"
else:
southl.text = "clear"
else:
southl.text = "invalid"
func finish_jump() -> void:
jumping = false
func leap(to_point: Vector3) -> void:
jumping = true
var tween: Tween = create_tween()
tween.tween_property(character, "global_position", character.global_position + (to_point / 2.0) + Vector3.UP, 1.0)
tween.tween_property(character, "global_position", character.global_position + to_point, 1.0)
tween.tween_callback(finish_jump)

View File

@ -0,0 +1 @@
uid://dqluvn05min37

View File

@ -18,3 +18,4 @@ func _physics_process(delta: float) -> void:
var sample: Transform3D = path.sample_baked_with_rotation(path_progress, true)
character.global_position = sample.origin
character.look_at(character.global_position + -sample.basis.z)
var closest_point: Vector3 = path.get_closest_point(character.global_position)

View File

@ -0,0 +1 @@
uid://b62xnsbki8axa

View File

@ -33,7 +33,7 @@ func explode() -> void:
func hit(target: CharacterBody3D) -> void:
target.damage(damage)
target.apply_effect(effect)
if owner_id == 0:
if Data.preferences.display_tower_damage_indicators:
spawn_damage_indicator(target.sprite.global_position)

View File

@ -0,0 +1 @@
uid://cubn2iabwg3

View File

@ -0,0 +1 @@
uid://dwr38fukjqu7r

View File

@ -9,6 +9,7 @@ var force: float = 2.0
var damage: float = 0.0
var lifetime: float = 10.0
var time_alive: float = 0.0
var effect: Effect
func _ready() -> void:
@ -20,10 +21,10 @@ func _process(delta: float) -> void:
func spawn_damage_indicator(pos: Vector3) -> void:
if damage > 0:
if effect.damage > 0:
var marker: Node3D = damage_particle_scene.instantiate()
get_tree().root.add_child(marker)
marker.set_number(damage)
marker.set_number(effect.damage)
marker.position = pos

View File

@ -0,0 +1 @@
uid://bifvnevs5y4nj

View File

@ -0,0 +1 @@
uid://ba2aqn82wo747

View File

@ -1,6 +1,10 @@
class_name Card extends Item
enum Faction {GENERIC = 0}
enum Faction {
GENERIC = 0,
ENGINEER = 1,
MAGE = 2,
}
@export var rarity: Data.Rarity
@export var faction: Faction

View File

@ -0,0 +1 @@
uid://d40isem6w5d8

View File

@ -1,6 +1,7 @@
class_name CardText extends Resource
@export var target_type: Data.TargetType
@export var energy_type: Data.EnergyType
@export var attributes: Array[StatAttribute]
@export_multiline var text: String

View File

@ -0,0 +1 @@
uid://dg7gxxqfqxcmc

View File

@ -10,3 +10,24 @@ class_name Enemy extends Resource
@export var penalty: int = 10
@export var movement_speed: float = 0.5
@export var spawn_cooldown: float = 1.0
@export_group("Spawner Card")
@export_subgroup("Common")
@export var common_group: int = 1
@export var common_cost: int = 1
@export_subgroup("Uncommon")
@export var uncommon_group: int = 1
@export var uncommon_cost: int = 1
@export_subgroup("Rare")
@export var rare_group: int = 1
@export var rare_cost: int = 1
@export_subgroup("Epic")
@export var epic_group: int = 1
@export var epic_cost: int = 1
@export_subgroup("Legendary")
@export var legendary_group: int = 1
@export var legendary_cost: int = 1

View File

@ -0,0 +1 @@
uid://cbwxa2a4hfcy4

View File

@ -4,3 +4,5 @@ class_name HeroClass extends Resource
@export var texture: Texture
@export var hand_texture: Texture
@export var deck: Array[Card]
@export var faction: Card.Faction
@export var podium: PackedScene

View File

@ -0,0 +1 @@
uid://dcwtg2gev3uia

View File

@ -0,0 +1 @@
uid://uomjb4sj4enc

View File

@ -0,0 +1 @@
uid://ctwk3deywlswg

View File

@ -9,7 +9,7 @@ const SAVE_PATH: String = "user://graphics_settings.tres"
func apply_graphical_settings(viewport: Viewport) -> void:
DisplayServer.window_set_vsync_mode(vsync_mode)
#DisplayServer.window_set_vsync_mode(vsync_mode)
match aa_mode:
0:
viewport.use_taa = false

View File

@ -0,0 +1 @@
uid://bte0pj6bwedb0

View File

@ -0,0 +1 @@
uid://vkthiwr3vq4g

View File

@ -0,0 +1 @@
uid://c78bl6r5qlday

View File

@ -0,0 +1 @@
uid://b1s4nrql8i1sm

View File

@ -0,0 +1,86 @@
class_name SaveData extends RefCounted
const SAVE_PATH: String = "user://save1.txt"
#Game History
var twenty_game_history: Array[bool] = []
var wins: int = 0
var losses: int = 0
#Engineer
var engineer_cards_bought: int = 0
#Unlocking the mage
var mage_card_seen_in_shop: bool = false
var mage_cards_bought: int = 0
var mage_unlocked: bool = 0
func add_game_outcome(outcome: bool) -> void:
if outcome:
wins += 1
else:
losses += 1
twenty_game_history.push_back(outcome)
if twenty_game_history.size() > 20:
twenty_game_history.pop_front()
func unlock_all_content() -> void:
mage_unlocked = true
func lock_all_content() -> void:
mage_unlocked = false
func bought_engineer_card() -> void:
engineer_cards_bought += 1
func saw_mage_card_in_shop() -> void:
mage_card_seen_in_shop = true
save_to_disc()
func bought_mage_card() -> void:
mage_cards_bought += 1
if mage_cards_bought >= 10:
mage_unlocked = true
save_to_disc()
func save_to_disc() -> void:
var save_file: FileAccess = FileAccess.open(SAVE_PATH, FileAccess.WRITE)
var dict: Dictionary = {
"wins" = wins,
"losses" = losses,
"twenty_game_history" = twenty_game_history,
"engineer_cards_bought" = engineer_cards_bought,
"mage_card_seen_in_shop" = mage_card_seen_in_shop,
"mage_cards_bought" = mage_cards_bought,
"mage_unlocked" = mage_unlocked,
}
var json_string: String = JSON.stringify(dict)
save_file.store_line(json_string)
static func load_profile_from_disk() -> SaveData:
if FileAccess.file_exists(SAVE_PATH):
var save_file: FileAccess = FileAccess.open(SAVE_PATH, FileAccess.READ)
var json_string: String = save_file.get_line()
var json: JSON = JSON.new()
var parse_result: Error = json.parse(json_string)
if parse_result == OK:
var dict: Dictionary = json.data
var stats: SaveData = SaveData.new()
stats.wins = dict["wins"]
stats.losses = dict["losses"]
stats.twenty_game_history.append_array(dict["twenty_game_history"])
stats.engineer_cards_bought = dict["engineer_cards_bought"]
stats.mage_card_seen_in_shop = dict["mage_card_seen_in_shop"]
stats.mage_cards_bought = dict["mage_cards_bought"]
stats.mage_unlocked = dict["mage_unlocked"]
return stats
return SaveData.new()

View File

@ -0,0 +1 @@
uid://6tvi4ox481cp

View File

@ -1,25 +0,0 @@
class_name SaveStats extends Resource
const SAVE_PATH: String = "user://save_stats.tres"
@export var wins: int
@export var losses: int
@export var twenty_game_history: Array[bool]
func add_game_outcome(outcome: bool) -> void:
if outcome:
wins += 1
else:
losses += 1
twenty_game_history.push_back(outcome)
if twenty_game_history.size() > 20:
twenty_game_history.pop_front()
func save_profile_to_disk() -> void:
ResourceSaver.save(self, SAVE_PATH)
static func load_profile_from_disk() -> SaveStats:
if ResourceLoader.exists(SAVE_PATH):
return ResourceLoader.load(SAVE_PATH)
return SaveStats.new()

View File

@ -0,0 +1 @@
uid://yjb0uv6og430

View File

@ -0,0 +1 @@
uid://bq6jp8bwub6je

View File

@ -2,4 +2,4 @@ class_name StatusDoT extends StatusEffect
func proc(affected: EnemyController, stacks: int, _existing_effects: Dictionary) -> void:
affected.damage(stats.potency * stacks)
affected.health.take_damage(int(stats.potency * stacks))

View File

@ -0,0 +1 @@
uid://c6eghujlaqhwi

View File

@ -1,6 +1,6 @@
class_name StatusEffect extends RefCounted
class_name StatusEffect extends Resource
var stats: StatusStats
@export var stats: StatusStats
var time_since_proc: float = 0.0
var time_existed: float = 0.0

View File

@ -0,0 +1 @@
uid://chy7bx8dlwgs0

View File

@ -0,0 +1 @@
uid://bh74uangqekuk

View File

@ -1,13 +0,0 @@
class_name HitscanTower extends Tower
func shoot() -> void:
super.shoot()
target_finder.get_target().damage(damage)
if Data.preferences.display_tower_damage_indicators:
spawn_damage_indicator(target_finder.get_target().sprite.global_position)
@rpc("reliable")
func networked_shoot() -> void:
super.networked_shoot()

View File

@ -1,30 +0,0 @@
class_name ProjectileTower extends Tower
@export var projectile_scene: PackedScene
var force: float = 150.0
var projectile_id: int = 0
func shoot() -> void:
if is_multiplayer_authority():
networked_spawn_projectile.rpc(multiplayer.get_unique_id())
@rpc("reliable")
func networked_shoot() -> void:
super.networked_shoot()
shoot()
@rpc("reliable", "call_local")
func networked_spawn_projectile(peer_id: int) -> Projectile:
var projectile: Projectile = projectile_scene.instantiate() as Projectile
projectile.position = yaw_model.global_position
projectile.damage = damage
projectile.direction = -yaw_model.global_transform.basis.z
projectile.force = force
projectile.name = base_name + str(peer_id) + str(projectile_id)
get_tree().root.add_child(projectile)
projectile_id += 1
return projectile

View File

@ -1,36 +0,0 @@
class_name RangeAffectingTower extends StatusApplyingTower
func _physics_process(_delta: float) -> void:
if !is_multiplayer_authority():
return
var enemies_in_range: Array = []
for enemy: EnemyController in get_tree().get_nodes_in_group("Enemies"):
if !is_instance_valid(enemy) or !enemy.alive or global_position.distance_to(enemy.global_position) > target_range:
continue
if enemy.stats.target_type & stats.target_type:
enemies_in_range.append(enemy)
if time_since_firing >= time_between_shots:
time_since_firing -= time_between_shots
for enemy: EnemyController in enemies_in_range:
fire(enemy)
func aim() -> void:
pass
func fire(target: EnemyController) -> void:
if is_instance_valid(target) and target.alive:
target.damage(damage)
target.status_manager.add_effect(build_status_object())
if Data.preferences.display_tower_damage_indicators:
spawn_damage_indicator(target.sprite.global_position)
if is_multiplayer_authority():
networked_fire.rpc(get_tree().root.get_path_to(target))
@rpc("reliable")
func networked_fire(target_node_path: String) -> void:
var target: EnemyController = get_tree().root.get_node(target_node_path)
fire(target)

View File

@ -1,47 +0,0 @@
class_name ShapecastTower extends Tower
@export var shapecast: ShapeCast3D
@export var particlesystem: GPUParticles3D
@export var status_stats: StatusStats
func _process(delta: float) -> void:
super._process(delta)
if target_finder.get_target():
particlesystem.emitting = true
else:
particlesystem.emitting = false
func shoot() -> void:
for index: int in shapecast.get_collision_count():
var target: CharacterBody3D = shapecast.get_collider(index) as CharacterBody3D
hit(target)
func aim() -> void:
yaw_model.look_at(target_finder.get_target().global_position)
pitch_model.look_at(target_finder.get_target().global_position)
pitch_model.rotation.x = 0.0
func hit(target: CharacterBody3D) -> void:
if is_instance_valid(target) and target.alive:
target.damage(damage)
if Data.preferences.display_tower_damage_indicators:
spawn_damage_indicator(target.sprite.global_position)
target.status_manager.add_effect(build_status_object())
if is_multiplayer_authority():
networked_hit.rpc(get_tree().root.get_path_to(target))
func build_status_object() -> StatusEffect:
var status: StatusEffect = StatusEffect.new()
status.stats = status_stats
return status
@rpc("reliable")
func networked_hit(target_node_path: String) -> void:
var target: CharacterBody3D = get_tree().root.get_node(target_node_path) as CharacterBody3D
hit(target)

View File

@ -1,21 +0,0 @@
class_name StatusApplyingTower extends HitscanTower
@export var status_stats: StatusStats
func shoot() -> void:
super.shoot()
if target_finder.get_target():
target_finder.get_target().damage(damage)
target_finder.get_target().status_manager.add_effect(build_status_object())
func build_status_object() -> StatusEffect:
var status: StatusEffect = StatusEffect.new()
status.stats = status_stats
return status
@rpc("reliable")
func networked_shoot() -> void:
super.networked_shoot()

View File

@ -1,80 +0,0 @@
class_name Tower extends Node3D
@export var stats: CardText
@export var target_finder: TargetFinder
@export var animator: AnimationPlayer
@export var pitch_model: MeshInstance3D
@export var yaw_model: MeshInstance3D
@export var range_indicator: CSGSphere3D
@export var audio_player: AudioStreamPlayer3D
var owner_id: int
var damage_particle_scene: PackedScene = preload("res://Scenes/damage_particle.tscn")
var base_name: String
#var targeted_enemy: EnemyController
var time_since_firing: float = 0.0
var time_between_shots: float = 0.0
var damage: float = 0.0
var target_range: float = 0.0
func _ready() -> void:
time_between_shots = stats.get_attribute("Fire Delay")
damage = stats.get_attribute("Damage")
target_range = stats.get_attribute("Range")
range_indicator.radius = target_range
func preview_range(value: bool) -> void:
range_indicator.set_visible(value)
func _process(delta: float) -> void:
if !is_multiplayer_authority():
return
if time_since_firing < time_between_shots:
time_since_firing += delta
func _physics_process(_delta: float) -> void:
if !is_multiplayer_authority():
#only doing the graphical sort of stuff but not shoot logic
if target_finder.get_target():
aim()
return
if target_finder.get_target():
aim()
if time_since_firing >= time_between_shots:
time_since_firing -= time_between_shots
shoot()
func aim() -> void:
yaw_model.look_at(target_finder.get_target().global_position)
pitch_model.look_at(target_finder.get_target().global_position)
pitch_model.rotation.x = 0.0
func shoot() -> void:
animator.play("shoot")
audio_player.play()
if is_multiplayer_authority():
networked_shoot.rpc()
func spawn_damage_indicator(pos: Vector3) -> void:
if damage > 0:
var marker: Sprite3D = damage_particle_scene.instantiate()
get_tree().root.add_child(marker)
marker.set_number(damage)
marker.position = pos
@rpc("reliable")
func networked_shoot() -> void:
shoot()
#@rpc("reliable")
#func networked_acquire_target(target_node_path: String) -> void:
#targeted_enemy = get_tree().root.get_node(target_node_path)

View File

@ -19,7 +19,7 @@ func shoot() -> void:
if raycast.is_colliding():
var target: CharacterBody3D = raycast.get_collider()
if target != null:
var target_hitbox: Hitbox = target.shape_owner_get_owner(raycast.get_collider_shape())
var target_hitbox: CollisionShape3D = target.shape_owner_get_owner(raycast.get_collider_shape())
if target_hitbox is Hitbox:
hit(target, target_hitbox)
if Data.preferences.display_self_damage_indicators:

View File

@ -0,0 +1 @@
uid://bp3o5klntwmhd

View File

@ -20,7 +20,9 @@ func networked_shoot() -> void:
func networked_spawn_projectile(peer_id: int, direction: Vector3) -> void:
var projectile: Projectile = projectile_scene.instantiate() as Projectile
projectile.position = global_position
projectile.damage = damage
var effect: Effect = Effect.new()
effect.damage = damage
projectile.effect = effect
projectile.direction = direction
projectile.force = force
projectile.owner_id = peer_id

View File

@ -0,0 +1 @@
uid://d2gdg2lcedfgg

View File

@ -0,0 +1 @@
uid://1l1qccgmp5ih

View File

@ -0,0 +1 @@
uid://dqcjfcecqhuec

View File

@ -1,6 +1,7 @@
class_name Weapon extends Node3D
signal energy_changed(energy: int)
signal energy_spent(energy: int, type: Data.EnergyType)
signal energy_recharged(energy: int, type: Data.EnergyType)
@export var stats: CardText
@export var animator: AnimationPlayer
@ -13,7 +14,7 @@ var trigger_held: bool = false
var second_trigger_held: bool = false
var time_since_firing: float = 0.0
var time_between_shots: float = 0.0
var damage: float = 0.0
var damage: int = 0
var max_energy: float = 100.0
var current_energy: float = 100.0
var energy_cost: float = 1.0
@ -21,12 +22,16 @@ var recharging: bool = false
var recharge_speed: float = 0.0
var recharge_acceleration: float = 2.0
var recharge_max_speed: float = 25.0
#var time_since_trigger: float = 0.0
var prev_energy_int: int = 0.0
func _ready() -> void:
time_between_shots = stats.get_attribute("Fire Delay")
damage = stats.get_attribute("Damage")
energy_cost = stats.get_attribute("Energy")
damage = int(stats.get_attribute("Damage"))
#energy_cost = stats.get_attribute("Energy")
max_energy = stats.get_attribute("Energy")
current_energy = max_energy
func set_hero(value: Hero) -> void:
@ -41,22 +46,33 @@ func _process(delta: float) -> void:
current_energy += recharge_speed * delta
if current_energy >= max_energy:
current_energy = max_energy
energy_changed.emit(current_energy)
recharging = false
if stats.energy_type == Data.EnergyType.CONTINUOUS:
energy_recharged.emit(recharge_speed * delta, stats.energy_type)
if stats.energy_type == Data.EnergyType.DISCRETE and int(current_energy) > prev_energy_int:
energy_recharged.emit(1, stats.energy_type)
prev_energy_int = int(current_energy)
#energy_changed.emit(current_energy)
if time_since_firing < time_between_shots:
time_since_firing += delta
if trigger_held and stats.energy_type == Data.EnergyType.CONTINUOUS:
current_energy -= delta
energy_spent.emit(delta, stats.energy_type)
func _physics_process(_delta: float) -> void:
func _physics_process(delta: float) -> void:
if trigger_held and current_energy >= energy_cost and time_since_firing >= time_between_shots:
if stats.energy_type == Data.EnergyType.DISCRETE:
current_energy -= 1
energy_spent.emit(1, stats.energy_type)
time_since_firing -= time_between_shots
current_energy -= energy_cost
energy_changed.emit(current_energy)
shoot()
networked_shoot.rpc()
func hold_trigger() -> void:
trigger_held = true
recharge_timer.stop()
func release_trigger() -> void:

View File

@ -0,0 +1 @@
uid://blh4s3v07ycwa

View File

@ -6,10 +6,8 @@ var non_build_locations: Array = []
var astar: AStar3D = AStar3D.new()
#TODO generalize this better
@export var start: Node3D
@export var end: Node3D
@export var spawner: EnemySpawner
@export var visualized_path: VisualizedPath
@export var spawners: Array[EnemySpawner]
@export var tower_path: Node
var tower_base_scene: PackedScene = load("res://Scenes/TowerBase/tower_base.tscn")
var tower_frame_scene: PackedScene = load("res://Scenes/tower_frame.tscn")
@ -19,6 +17,11 @@ var tower_frames: Array = []
var wall_id: int = 0
func _ready() -> void:
for spawner: EnemySpawner in spawners:
spawner.astar = self
func toggle_point(point_id: int, caller_id: int) -> void:
networked_toggle_point.rpc(point_id, caller_id)
@ -210,15 +213,18 @@ func place_random_towers(tower_limit: int) -> void:
func find_path() -> bool:
var path: PackedVector3Array = astar.get_point_path(astar.get_point_count() - 2, astar.get_point_count() - 1)
if !path.is_empty():
var curve: Curve3D = Curve3D.new()
for point: Vector3 in path:
curve.add_point(point)
spawner.path.curve = curve
spawner.path.spawn_visualizer_points()
return true
return false
for spawn: EnemySpawner in spawners:
var path: PackedVector3Array = astar.get_point_path(spawn.astar_point_id, astar.get_point_count() - 1)
if !path.is_empty():
var curve: Curve3D = Curve3D.new()
for point: Vector3 in path:
curve.add_point(point)
spawn.path.global_position = Vector3.ZERO
spawn.path.curve = curve
else:
return false
spawners[0].path.spawn_visualizer_points()
return true
func make_grid() -> void:
@ -247,10 +253,12 @@ func make_grid() -> void:
var west_point_id: int = grid_size.y * x + (y + 1)
astar.connect_points(point_id, west_point_id, false)
non_build_locations.append(astar.get_point_count())
astar.add_point(astar.get_point_count(), start.global_position)
for x: int in grid_size.y:
astar.connect_points(int(astar.get_point_count() - 1), x)
for spawn: EnemySpawner in spawners:
non_build_locations.append(astar.get_point_count())
spawn.astar_point_id = astar.get_point_count()
astar.add_point(astar.get_point_count(), spawn.global_position)
for x: int in grid_size.y:
astar.connect_points(int(astar.get_point_count() - 1), x)
non_build_locations.append(astar.get_point_count())
astar.add_point(astar.get_point_count(), end.global_position)
for x: int in grid_size.y:

View File

@ -0,0 +1 @@
uid://u404brdoaku

View File

@ -0,0 +1 @@
uid://4uwd40mavufi

View File

@ -0,0 +1 @@
uid://c4ljvgrb81du6

1
Scripts/card_hand.gd.uid Normal file
View File

@ -0,0 +1 @@
uid://d6ejaumcenmg

1
Scripts/chatbox.gd.uid Normal file
View File

@ -0,0 +1 @@
uid://dmdf7tbvc3bsg

View File

@ -0,0 +1 @@
uid://yk54owkf7pgj

View File

@ -0,0 +1 @@
uid://bjt72v1wym5ie

16
Scripts/corpse.gd Normal file
View File

@ -0,0 +1,16 @@
class_name Corpse extends RigidBody3D
func set_sprite(tex: Texture) -> void:
$Sprite3D.texture = tex
func _ready() -> void:
var tween: Tween = create_tween()
tween.tween_interval(20.0)
tween.tween_property($Sprite3D, "modulate", Color(1.0, 1.0, 1.0, 0.0), 4.0)
tween.tween_callback(queue_free)
func _on_body_entered(_body: Node) -> void:
freeze = true

1
Scripts/corpse.gd.uid Normal file
View File

@ -0,0 +1 @@
uid://mm665gsfr23a

View File

@ -0,0 +1 @@
uid://blt7umrgo3tfh

View File

@ -4,16 +4,20 @@ var characters: Array[HeroClass]
var cards: Array[Card]
var enemies: Array[Enemy]
var keymaps: Array[PlayerKeymap]
var mods: Dictionary[String, String]
var graphics: PlayerGraphicsSettings
var audio: PlayerAudioSettings
var preferences: PlayerPreferences
var player_profile: PlayerProfile
var player_keymap: PlayerKeymap
var player_controller_keymap: PlayerKeymap = preload("res://Resources/Keymaps/controller.tres")
var save_stats: SaveStats
var save_data: SaveData
const DEFAULT_SERVER_PORT: int = 58008
var wall_cost: int = 1
var printer_cost: int = 15
enum EnergyType {UNDEFINED = 0, DISCRETE = 1, CONTINUOUS = 2}
enum TargetType {UNDEFINED = 0, LAND = 1, AIR = 2, BOTH = 3}
enum EnemyType {UNDEFINED = 0, LAND = 1, AIR = 2}
enum Rarity {COMMON = 0, UNCOMMON = 1, RARE = 2, EPIC = 3, LEGENDARY = 4}
@ -25,7 +29,80 @@ var rarity_weights: Dictionary = {
"LEGENDARY" = 1
}
## Recursively searches a folder for any Card resources and loads them
func load_cards(path: String) -> void:
cards = []
var dir: DirAccess = DirAccess.open(path)
if dir:
dir.list_dir_begin()
var file_name: String = dir.get_next()
while file_name != "":
if dir.current_is_dir():
load_cards(path + file_name)
else:
var card: Card = load(path + "/" + file_name)
if card:
cards.append(card)
file_name = dir.get_next()
func load_classes() -> void:
characters = []
var dir: DirAccess = DirAccess.open("res://Classes")
if dir:
dir.list_dir_begin()
var folder_name: String = dir.get_next()
while folder_name != "":
if dir.current_is_dir():
var dir2: DirAccess = DirAccess.open("res://Classes/" + folder_name)
if dir2:
dir2.list_dir_begin()
var folder_name2: String = dir2.get_next()
while folder_name2 != "":
if folder_name2 == "class.tres":
var hero_class: HeroClass = load("res://Classes/" + folder_name + "/" + folder_name2)
characters.append(hero_class)
folder_name2 = dir2.get_next()
else:
pass
folder_name = dir.get_next()
func load_mods(mod_list: Dictionary[String, bool]) -> void:
for mod_name: String in mod_list:
if mod_list[mod_name]:
var success: bool = ProjectSettings.load_resource_pack(mods[mod_name])
if success:
print("Successfully loaded mod: " + mod_name + " at path: " + mods[mod_name])
else:
print("Failed to load mod: " + mod_name + " at path: " + mods[mod_name])
load_classes()
load_cards("res://Cards")
func _ready() -> void:
var mod_dir: DirAccess = DirAccess.open("res://Mods")
if mod_dir:
mod_dir.list_dir_begin()
var file_name: String = mod_dir.get_next()
while file_name != "":
if mod_dir.current_is_dir():
var data_dir: DirAccess = DirAccess.open("res://Mods/" + file_name)
if data_dir:
data_dir.list_dir_begin()
var data_name: String = data_dir.get_next()
while data_name != "":
if data_name.ends_with(".json"):
var file: FileAccess = FileAccess.open("res://Mods/" + file_name + "/" + data_name, FileAccess.READ)
var json_string: String = file.get_line()
var json: JSON = JSON.new()
var parse_result: Error = json.parse(json_string)
if parse_result == OK:
var dict: Dictionary = json.data
mods[dict["display_name"]] = "res://Mods/" + file_name + "/" + dict["pck_path"]
data_name = data_dir.get_next()
file_name = mod_dir.get_next()
keymaps.append(preload("res://Resources/Keymaps/qwerty.tres"))
keymaps.append(preload("res://Resources/Keymaps/azerty.tres"))
keymaps.append(preload("res://Resources/Keymaps/dvorak.tres"))
@ -41,37 +118,15 @@ func _ready() -> void:
player_keymap = PlayerKeymap.load_profile_from_disk()
player_keymap.apply()
player_controller_keymap.append_input_map()
save_stats = SaveStats.load_profile_from_disk()
save_data = SaveData.load_profile_from_disk()
characters.append(preload("res://PCs/Mechanic/red.tres"))
#characters.append(preload("res://PCs/Green/green.tres"))
characters.append(preload("res://PCs/Mage/blue.tres"))
load_classes()
load_cards("res://Cards")
#Common
cards.append(preload("res://PCs/Mechanic/ClassCards/Assault/card_assault.tres"))
cards.append(preload("res://PCs/Mechanic/ClassCards/BombLauncher/card_bomb_launcher.tres"))
cards.append(preload("res://PCs/Mechanic/ClassCards/Gatling/card_gatling.tres"))
cards.append(preload("res://PCs/Mechanic/ClassCards/RocketLauncher/card_rocket_launcher.tres"))
#Uncommon
cards.append(preload("res://PCs/Mechanic/ClassCards/Sniper/card_sniper.tres"))
cards.append(preload("res://PCs/Entomologist/ClassCards/Blowdart/card_blowdart.tres"))
cards.append(preload("res://PCs/Mage/ClassCards/Refrigerator/card_refrigerator.tres"))
cards.append(preload("res://PCs/Mechanic/ClassCards/GlueLauncher/card_glue_launcher.tres"))
#Rare
cards.append(preload("res://PCs/Mechanic/ClassCards/Flamethrower/card_flamethrower.tres"))
#cards.append(preload("res://PCs/Universal/ClassCards/DamageEnhancer/card_damage_enhancer.tres"))
#cards.append(preload("res://PCs/Universal/ClassCards/SpeedEnhancer/card_speed_enhancer.tres"))
#Epic
cards.append(preload("res://PCs/Mage/ClassCards/Icicle/card_icicle.tres"))
cards.append(preload("res://PCs/Mage/ClassCards/Fireball/card_fireball.tres"))
#cards.append(preload("res://PCs/Universal/ClassCards/GammaLaser/card_gamma_laser.tres"))
#Legendary
cards.append(preload("res://PCs/Mechanic/ClassCards/Reactor/card_reactor.tres"))
#cards.append(preload("res://PCs/Universal/ClassCards/Lightning/card_lightning.tres"))
enemies.append(preload("res://Worlds/GreenPlanet/Enemies/dog.tres"))
enemies.append(preload("res://Worlds/GreenPlanet/Enemies/dog_fast.tres"))
enemies.append(preload("res://Worlds/GreenPlanet/Enemies/dog_heavy.tres"))
enemies.append(preload("res://Worlds/GreenPlanet/Enemies/dog_boss.tres"))
enemies.append(preload("res://Worlds/GreenPlanet/Enemies/airenemy.tres"))
enemies.append(preload("res://Worlds/GreenPlanet/Enemies/airenemy2.tres"))
enemies.append(preload("res://Resources/Enemies/dog.tres"))
enemies.append(preload("res://Resources/Enemies/dog_fast.tres"))
enemies.append(preload("res://Resources/Enemies/dog_heavy.tres"))
enemies.append(preload("res://Resources/Enemies/dog_boss.tres"))
enemies.append(preload("res://Resources/Enemies/airenemy.tres"))
enemies.append(preload("res://Resources/Enemies/airenemy2.tres"))
enemies.append(preload("res://Resources/Enemies/leapfrog.tres"))

1
Scripts/data.gd.uid Normal file
View File

@ -0,0 +1 @@
uid://dod20lw3c8kqm

1
Scripts/edit_tool.gd.uid Normal file
View File

@ -0,0 +1 @@
uid://ckm02cx0ai624

5
Scripts/effect.gd Normal file
View File

@ -0,0 +1,5 @@
class_name Effect extends Resource
@export var damage: int = 0
@export var status_effects: Array[StatusEffect] = []

1
Scripts/effect.gd.uid Normal file
View File

@ -0,0 +1 @@
uid://djp7tepdsda8w

View File

@ -0,0 +1 @@
uid://by8jxwui7chwl

5
Scripts/enemy_card.gd Normal file
View File

@ -0,0 +1,5 @@
class_name EnemyCard extends RefCounted
var enemy: Enemy = null
var rarity: Data.Rarity = Data.Rarity.COMMON

View File

@ -0,0 +1 @@
uid://brf6kradnuce8

View File

@ -0,0 +1 @@
uid://cxwtuxytavfu5

View File

@ -3,16 +3,20 @@ class_name EnemySpawner extends Node3D
signal enemy_spawned()
@export var land_enemy_scene: PackedScene
@export var leap_enemy_scene: PackedScene
@export var air_enemy_scene: PackedScene
@export var own_id: int = 0
@export var path: VisualizedPath
var astar: AStarGraph3D
@export var own_id: int = 0
@export var type: Data.EnemyType
@export var dest: Node3D
@export var enemy_path: Node
var astar_point_id: int = 0
var enemy_died_callback: Callable
var enemy_reached_goal_callback: Callable
var current_wave: Dictionary = {}
var current_wave: Array[EnemyCard]
var enemy_types_to_spawn: Dictionary = {}
var enemy_spawn_timers: Dictionary = {}
var enemies_spawned: Dictionary = {}
var enemies_to_spawn: int = 0
@ -26,7 +30,7 @@ func _process(delta: float) -> void:
return
for x: Enemy in enemy_spawn_timers:
if enemies_spawned[x] == current_wave[x]:
if enemies_spawned[x] == enemy_types_to_spawn[x]:
continue
var enemy_stats: Enemy = x
@ -35,12 +39,12 @@ func _process(delta: float) -> void:
if enemy_spawn_timers[x] >= enemy_stats.spawn_cooldown:
if is_multiplayer_authority():
if type == Data.EnemyType.LAND:
networked_spawn_land_enemy.rpc(var_to_str(enemy_stats), own_id, enemy_id)
networked_spawn_land_enemy.rpc(Data.enemies.find(enemy_stats), own_id, enemy_id)
if type == Data.EnemyType.AIR:
var radius: float = 10.0
var random_dir: Vector3 = Vector3(randf_range(-1, 1), randf_range(-1, 1), randf_range(-1, 1))
var random_pos: Vector3 = randf_range(0, radius) * random_dir.normalized()
networked_spawn_air_enemy.rpc(var_to_str(enemy_stats), random_pos, own_id, enemy_id)
networked_spawn_air_enemy.rpc(Data.enemies.find(enemy_stats), random_pos, own_id, enemy_id)
enemy_spawn_timers[x] -= enemy_stats.spawn_cooldown
enemy_spawned.emit()
@ -49,43 +53,64 @@ func _process(delta: float) -> void:
enemies_to_spawn -= 1
#TODO: not sure enemies need all this info over the network
#TODO: generalize enemy scene selection, i.e. store the scenes in the enemy
#card like towers do
@rpc("reliable", "call_local")
func networked_spawn_land_enemy(enemy_stats: String, id1: int, id2: int) -> void:
var enemy: EnemyController = land_enemy_scene.instantiate() as EnemyController
func networked_spawn_land_enemy(enemy_stats: int, id1: int, id2: int) -> void:
var enemy: EnemyController
if enemy_stats != 6:
enemy = land_enemy_scene.instantiate() as EnemyController
else:
enemy = leap_enemy_scene.instantiate() as EnemyController
enemy.name = str(id1) + str(id2)
enemy.stats = str_to_var(enemy_stats)
enemy.stats = Data.enemies[enemy_stats]
enemy.died.connect(enemy_died_callback)
enemy.reached_goal.connect(enemy_reached_goal_callback)
enemy.movement_controller.path = path.curve
enemy.movement_controller.astar = astar
enemy.position = global_position
enemy_path.add_child(enemy)
@rpc("reliable", "call_local")
func networked_spawn_air_enemy(enemy_stats: String, pos: Vector3, id1: int, id2: int) -> void:
func networked_spawn_air_enemy(enemy_stats: int, pos: Vector3, id1: int, id2: int) -> void:
var enemy: EnemyController = air_enemy_scene.instantiate() as EnemyController
enemy.name = str(id1) + str(id2)
enemy.position = pos + global_position
enemy.stats = str_to_var(enemy_stats)
enemy.stats = Data.enemies[enemy_stats]
enemy.died.connect(enemy_died_callback)
enemy.reached_goal.connect(enemy_reached_goal_callback)
enemy.movement_controller.goal = dest
enemy_path.add_child(enemy)
func spawn_wave(value: Dictionary) -> void:
var relevant_enemies: Dictionary = {}
var wave: Dictionary = {}
for index: int in value:
wave[Data.enemies[index]] = value[index]
for x: Enemy in wave:
if x.target_type == type:
relevant_enemies[x] = wave[x]
current_wave = relevant_enemies
func spawn_wave() -> void:
enemies_to_spawn = 0
enemy_spawn_timers = {}
for x: Enemy in current_wave:
enemies_to_spawn += current_wave[x]
enemy_spawn_timers[x] = 0.0
enemies_spawned[x] = 0
for card: EnemyCard in current_wave:
match(card.rarity):
Data.Rarity.COMMON:
enemy_types_to_spawn[card.enemy] += card.enemy.common_group
enemies_to_spawn += card.enemy.common_group
Data.Rarity.UNCOMMON:
enemy_types_to_spawn[card.enemy] += card.enemy.uncommon_group
enemies_to_spawn += card.enemy.uncommon_group
Data.Rarity.RARE:
enemy_types_to_spawn[card.enemy] += card.enemy.rare_group
enemies_to_spawn += card.enemy.rare_group
Data.Rarity.EPIC:
enemy_types_to_spawn[card.enemy] += card.enemy.epic_group
enemies_to_spawn += card.enemy.epic_group
Data.Rarity.LEGENDARY:
enemy_types_to_spawn[card.enemy] += card.enemy.legendary_group
enemies_to_spawn += card.enemy.legendary_group
enemy_spawn_timers[card.enemy] = 0.0
enemies_spawned[card.enemy] = 0
current_wave = []
done_spawning = false
func add_card(new_card: EnemyCard) -> void:
current_wave.append(new_card)
enemy_types_to_spawn[new_card.enemy] = 0

View File

@ -0,0 +1 @@
uid://dkuxg6ek5us4f

View File

@ -0,0 +1 @@
uid://cl2mi4bnpatwk

View File

@ -17,17 +17,16 @@ var singleplayer_lobby_scene_path: String = "res://Scenes/Menus/singleplayer_lob
var game_end_scene: PackedScene = load("res://Scenes/Menus/GameEndScreen/game_end_screen.tscn")
var connected_players_nodes: Dictionary = {}
var game_active: bool = false
var gamemode: GameMode = null
var level: Level
var enemies: int = 0
var objective_health: int = 120
var wave: int = 0
var endless_mode: bool = false
var upcoming_wave: Dictionary
var pot: float
var UILayer: CanvasLayer
var chatbox: Chatbox
var wave_limit: int = 20
var starting_cash: int = 16
var starting_cash: int = 25
var shop_chance: float = 0.0
var stats: RoundStats
var rng: FastNoiseLite
@ -171,32 +170,41 @@ func spawn_enemy_wave() -> void:
level.shop.close()
wave += 1
level.a_star_graph_3d.find_path()
level.a_star_graph_3d.visualized_path.disable_visualization()
level.a_star_graph_3d.disable_all_tower_frames()
for spawn: EnemySpawner in level.enemy_spawns:
spawn.spawn_wave(upcoming_wave)
spawn.path.disable_visualization()
spawn.spawn_wave()
wave_started.emit(wave)
func set_upcoming_wave() -> void:
if is_multiplayer_authority():
var spawn_power: int = WaveManager.calculate_spawn_power(wave + 1, connected_players_nodes.size())
var new_wave: Dictionary = WaveManager.generate_wave(spawn_power, level.enemy_pool)
networked_set_upcoming_wave.rpc(new_wave, 6 + floori(spawn_power / 70.0))
#var new_wave: Dictionary = WaveManager.generate_wave(spawn_power, level.enemy_pool)
var new_wave: Wave = WaveManager.generate_wave(spawn_power, level.enemy_pool, level.enemy_spawns)
temp_set_upcoming_wave(new_wave, floori(WaveManager.calculate_pot(wave + 1, connected_players_nodes.size()) / 20.0))
#networked_set_upcoming_wave.rpc(new_wave, 6 + floori(spawn_power / 70.0))
@rpc("reliable", "call_local")
func networked_set_upcoming_wave(wave_dict: Dictionary, coins: int) -> void:
upcoming_wave = wave_dict
func temp_set_upcoming_wave(wave: Wave, coins: int) -> void:
pot = coins
for key: int in connected_players_nodes:
connected_players_nodes[key].hud.set_upcoming_wave(upcoming_wave)
connected_players_nodes[multiplayer.get_unique_id()].hud.show_wave_generation_anim(wave)
connected_players_nodes[multiplayer.get_unique_id()].hud.set_upcoming_wave(wave.to_dict())
#TODO: You'll probably have to write a to_dict function for the new wave system
#before any of this shit works in multiplayer
#@rpc("reliable", "call_local")
#func networked_set_upcoming_wave(wave_dict: Dictionary, coins: int) -> void:
#upcoming_wave = wave_dict
#pot = coins
#for key: int in connected_players_nodes:
#connected_players_nodes[key].hud.set_upcoming_wave(upcoming_wave)
@rpc("reliable", "call_local")
func networked_set_endless(value: bool) -> void:
endless_mode = value
if endless_mode:
gamemode.endless = value
if gamemode.endless:
chatbox.append_message("SERVER", Color.TOMATO, "Endless mode enabled!")
else:
chatbox.append_message("SERVER", Color.TOMATO, "Endless mode disabled!")
@ -215,7 +223,7 @@ func enemy_died(enemy: Enemy) -> void:
return
if enemies == 0:
end_wave()
if !endless_mode and wave >= wave_limit:
if !gamemode.endless and wave >= wave_limit:
end(true)
@ -230,7 +238,7 @@ func damage_goal(enemy: Enemy, penalty: int) -> void:
end(false)
elif enemies == 0:
end_wave()
if !endless_mode and wave >= wave_limit:
if !gamemode.endless and wave >= wave_limit:
end(true)
@ -238,7 +246,8 @@ func end_wave() -> void:
for peer_id: int in connected_players_nodes:
connected_players_nodes[peer_id].currency += ceili(pot / connected_players_nodes.size())
connected_players_nodes[peer_id].unready_self()
level.a_star_graph_3d.visualized_path.enable_visualization()
for spawn: EnemySpawner in level.enemy_spawns:
spawn.path.enable_visualization()
level.a_star_graph_3d.enable_non_path_tower_frames()
if is_multiplayer_authority():
if randf_in_range(23 * wave, 0.0, 1.0) <= shop_chance:
@ -282,9 +291,9 @@ func setup() -> void:
game_setup.emit()
func start(rng_seed: int = randi()) -> void:
func start() -> void:
if is_multiplayer_authority():
set_seed.rpc(rng_seed)
set_seed.rpc(gamemode.rng_seed)
else:
await rng_seeded
@ -305,14 +314,15 @@ func start(rng_seed: int = randi()) -> void:
game_active = true
chatbox.append_message("SERVER", Color.TOMATO, "Started with seed: " + str(rng.seed))
game_started.emit()
#print("started game with seed: " + str(gamemode.rng_seed))
func end(outcome: bool) -> void:
if game_active == false:
return
game_active = false
Data.save_stats.add_game_outcome(outcome)
Data.save_stats.save_profile_to_disk()
Data.save_data.add_game_outcome(outcome)
Data.save_data.save_to_disc()
var menu: GameEndScreen = game_end_scene.instantiate() as GameEndScreen
match outcome:
false:

1
Scripts/game.gd.uid Normal file
View File

@ -0,0 +1 @@
uid://ocgd16bqo81s

7
Scripts/game_mode.gd Normal file
View File

@ -0,0 +1,7 @@
class_name GameMode extends RefCounted
var multiplayer: bool = false
var seeded: bool = false
var rng_seed: int = 0
var endless: bool = false
var daily: bool = false

1
Scripts/game_mode.gd.uid Normal file
View File

@ -0,0 +1 @@
uid://cbqxwt57eglct

View File

@ -0,0 +1 @@
uid://dtrjph756oq1f

View File

@ -0,0 +1 @@
uid://bpb5c5r1yi8um

View File

@ -8,6 +8,7 @@ signal health_changed(health: int)
var current_health: int
func take_damage(damage: int) -> void:
current_health -= damage
health_changed.emit(current_health)

1
Scripts/health.gd.uid Normal file
View File

@ -0,0 +1 @@
uid://bamhci3kawuyt

View File

@ -0,0 +1 @@
uid://bf06es50d0flv

View File

@ -1,7 +1,7 @@
class_name Hitbox extends CollisionShape3D
signal took_damage(amount: float)
signal took_damage(amount: int)
func damage(amount: float) -> void:
func damage(amount: int) -> void:
took_damage.emit(amount)

1
Scripts/hitbox.gd.uid Normal file
View File

@ -0,0 +1 @@
uid://cummt2be3r1gq

View File

@ -1,180 +0,0 @@
class_name HUD extends CanvasLayer
var last_lives_count: int = 120
@export var player: Hero
@export var wave_count: Label
@export var lives_count: Label
@export var currency_count: Label
@export var minimap_outline: TextureRect
@export var crosshair: Control
@export var minimap: TextureRect
@export var minimap_cam: MinimapCamera3D
@export var minimap_viewport: SubViewport
@export var fps_label: Label
@export var hover_text: RichTextLabel
var minimap_anchor: Node3D
var enemy_names: Array[String]
@export var enemy_sprites: Array[TextureRect]
@export var enemy_counts: Array[Label]
@export var weapon_energy_bar: TextureProgressBar
@export var offhand_energy_bar: TextureProgressBar
@export var pickup_notif_scene: PackedScene
@export var wave_start_label: RichTextLabel
@export var place_icon: TextureRect
@export var swap_icon: TextureRect
@export var place_text: RichTextLabel
@export var swap_text: RichTextLabel
var audio_guard: bool = false
func set_energy_visible(value: bool) -> void:
weapon_energy_bar.set_visible(value)
func set_offhand_energy_visible(value: bool) -> void:
offhand_energy_bar.set_visible(value)
func _process(_delta: float) -> void:
fps_label.text = "FPS: " + str(Engine.get_frames_per_second())
wave_start_label.text = parse_action_tag("[center]Press #Ready# to start wave")
place_text.text = parse_action_tag("[center]#Equip In Gauntlet#")
swap_text.text = parse_action_tag("[center]#Secondary Fire#")
func grow_wave_start_label() -> void:
tween_label(300.0)
func shrink_wave_start_label() -> void:
tween_label(0.0)
func tween_label(x: float) -> void:
var tween: Tween = create_tween()
tween.set_ease(Tween.EASE_IN_OUT)
tween.set_trans(Tween.TRANS_QUAD)
if x > 0.0:
tween.tween_callback(wave_start_label.set_visible.bind(true))
tween.parallel().tween_property(wave_start_label, "offset_left", -x, 0.6)
tween.parallel().tween_property(wave_start_label, "offset_right", x, 0.6)
if x <= 0.0:
tween.tween_callback(wave_start_label.set_visible.bind(false))
func set_hover_text(text: String) -> void:
hover_text.text = parse_action_tag(text)
hover_text.set_visible(true)
func unset_hover_text() -> void:
hover_text.set_visible(false)
func set_wave_count(value: int) -> void:
wave_count.text = str(value)
func set_lives_count(value: int) -> void:
lives_count.text = str(value)
for x: int in last_lives_count - value:
$LivesBar.take_life()
last_lives_count = value
func enemy_count_down(enemy: Enemy) -> void:
var index: int = enemy_names.find(enemy.title)
var num: int = enemy_counts[index].text.to_int() - 1
enemy_counts[index].text = str(num)
if num == 0:
enemy_counts[index].set_visible(false)
enemy_sprites[index].set_visible(false)
func set_upcoming_wave(value: Dictionary) -> void:
var frame_count: int = 0
enemy_names = []
var wave: Dictionary = {}
for index: int in value:
wave[Data.enemies[index]] = value[index]
for x: int in enemy_sprites.size():
enemy_sprites[x].set_visible(false)
enemy_counts[x].set_visible(false)
for enemy: Enemy in wave:
enemy_names.append(enemy.title)
enemy_sprites[frame_count].texture = enemy.icon
enemy_counts[frame_count].text = str(wave[enemy])
enemy_sprites[frame_count].set_visible(true)
enemy_counts[frame_count].set_visible(true)
frame_count += 1
func set_currency_count(value: int) -> void:
currency_count.text = str(value)
func set_crosshair_visible(value: bool) -> void:
crosshair.set_visible(value)
func set_weapon_energy(value: int) -> void:
weapon_energy_bar.value = value
if player.editing_mode:
audio_guard = true
if value == 0 and !audio_guard:
player.zeropower_audio.play()
audio_guard = true
if value == 100 and !audio_guard:
player.fullpower_audio.play()
audio_guard = true
if value > 0 and value < 100:
audio_guard = false
func set_offhand_energy(value: int) -> void:
offhand_energy_bar.value = value
func maximise_minimap(anchor: Node3D) -> void:
minimap_cam.anchor = anchor
minimap.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
minimap.offset_bottom = -40
minimap.offset_top = 40
minimap.offset_left = 40
minimap.offset_right = -40
minimap_viewport.size = Vector2(1840, 1000)
minimap_cam.size = 30
minimap_outline.set_visible(false)
currency_count.set_visible(false)
func minimize_minimap(anchor: Node3D) -> void:
minimap_cam.anchor = anchor
minimap.set_anchors_and_offsets_preset(Control.PRESET_TOP_RIGHT)
minimap.offset_right = -40
minimap.offset_top = 40
minimap.offset_left = -256
minimap.offset_bottom = 256
minimap_viewport.size = Vector2(256, 256)
minimap_cam.size = 15
minimap_outline.set_visible(true)
currency_count.set_visible(true)
func pickup(card: Card) -> void:
var notif: PickupNotification = pickup_notif_scene.instantiate()
notif.set_card(card)
$VBoxContainer.add_child(notif)
func parse_action_tag(text: String) -> String:
var string_array: PackedStringArray = text.split("#")
if string_array.size() > 1:
var event: InputEvent = InputMap.action_get_events(string_array[1])[0]
if event is InputEventKey:
string_array[1] = "[img=top,50]%s[/img]" % KeyIconMap.keys[str(event.keycode)]
if event is InputEventMouseButton:
string_array[1] = "[img=top,50]%s[/img]" % KeyIconMap.mouse_buttons[str(event.button_index)]
text = "".join(string_array)
return text

View File

@ -4,8 +4,16 @@ signal button_interacted(value: int, callback: Hero)
@export var button_press_value: int = 0
@export var press_cost: int = 0
@export var hover_text: String = "#Interact# to [do thing]"
@export var hover_text: String = "[center]#Interact# to [do thing]"
func press(callback_player: Hero) -> void:
button_interacted.emit(button_press_value, callback_player)
func enable_hover_effect() -> void:
pass
func disable_hover_effect() -> void:
pass

View File

@ -0,0 +1 @@
uid://dkfswql8ui0bt

1
Scripts/inventory.gd.uid Normal file
View File

@ -0,0 +1 @@
uid://do24iuot0j7d7

1
Scripts/item_card.gd.uid Normal file
View File

@ -0,0 +1 @@
uid://1l7xhsd5prk1

View File

@ -0,0 +1 @@
uid://c0pqprebrhakh

Some files were not shown because too many files have changed in this diff Show More