extends Path3D class_name WaveManager const PACK_DELAY : float = .15 @export var level : Level var current_wave : Wave var current_troop : Troop var enemies_to_spawn : int = 0 var wave : int = 0 var enemies_alive : int = 0 @onready var troopTimer := $TroopTimer func _ready() -> void: troopTimer.timeout.connect(spawn_troop) EventBus.player_defeated.connect(clearLevel) EventBus.allowedTowerHasChange.emit.call_deferred(level.allowedTowers) func _process(delta: float) -> void: if wave > 0 || level.auto_start: spawn_manager() func spawn_next_wave() -> void: if level.waves.is_empty(): queue_free.call_deferred() EventBus.player_has_won.emit() return current_wave = level.waves.pop_front() wave += 1 EventBus.waveHasChange.emit(wave) func spawn_troop() -> void: for n in current_troop.number_to_spawn: spawnEnemy(n * PACK_DELAY) func spawnEnemy(delay : float) -> void: var enemy := createEnemy() if delay > 0: var enemyTimer := createTimer(delay) enemyTimer.timeout.connect( func(): add_child(enemy) enemyTimer.queue_free() ) add_child(enemyTimer) else: add_child(enemy) enemies_alive += 1 func createEnemy() -> PathFollow3D : var PF3D : PathFollow3D = current_troop.enemy.instantiate() var enemy : Enemy = PF3D.find_children("*", "Enemy")[0] enemy.died.connect(func(): enemies_alive -= 1) return PF3D func spawn_manager() -> void: if not troopTimer.is_stopped(): return if not current_wave || enemies_alive == 0 && current_wave.troops.is_empty(): spawn_next_wave() return current_troop = current_wave.troops.pop_front() if not current_troop: return if current_troop.spawn_delay == 0: spawn_troop() else: troopTimer.start(current_troop.spawn_delay) func clearLevel() -> void: queue_free() func createTimer(delay : float, oneShot = true, autoStart = true) -> Timer : var timer := Timer.new() timer.wait_time = delay timer.one_shot = oneShot timer.autostart = autoStart return timer