extends Node3D class_name WorldManager enum STATE { INIT, IDLE, SPAWN, SPAWNING, FIGHT, WAVE_END, LEVEL_END } const PACK_DELAY : float = .15 var level : Level var paths : Array[Node] var state : STATE = STATE.INIT var wave : int = 0 var currentTroop : Troop : get(): return level.waves[wave - 1].troops[currentTroopIdx] var currentTroopIdx : int = 0 var enemiesAlive : int = 0 @onready var troopTimer : Timer = $TroopTimer func _ready() -> void: EventBus.projectile_shooted.connect(onProjectileShooted) EventBus.lauch_next_wave.connect(changeState.bind(STATE.SPAWN)) troopTimer.timeout.connect(spawnTroop) func onProjectileShooted(projectile: Projectile, startPosition: Vector3) -> void: add_child(projectile) projectile.global_position = startPosition func changeState(newState: STATE) -> void: match newState: STATE.SPAWN when [STATE.IDLE, STATE.WAVE_END].has(state): state = newState func _process(_delta: float) -> void: if state == STATE.INIT: return match state: STATE.SPAWN: spawnNextWave() state = STATE.SPAWNING STATE.SPAWNING when troopTimer.is_stopped(): state = STATE.FIGHT STATE.FIGHT when enemiesAlive == 0: state = STATE.WAVE_END STATE.WAVE_END when wave == level.waves.size(): EventBus.player_has_won.emit() state = STATE.LEVEL_END STATE.WAVE_END when level.auto_launch_wave: state = STATE.SPAWN func spawnNextWave() -> void: currentTroopIdx = 0 wave += 1 EventBus.wave_has_change.emit(wave) spawnNextTroop() func spawnNextTroop() -> void: # Wave spawning is finished if currentTroopIdx == level.waves[wave - 1].troops.size(): return if currentTroop.spawn_delay == 0: spawnTroop() else: troopTimer.start(currentTroop.spawn_delay) func spawnTroop() -> void: var enemyScene : PackedScene = load(currentTroop.enemy) var totalDelay : float = 0 for n in currentTroop.number_to_spawn: spawnEnemy(enemyScene, currentTroop.lane_to_spawn, totalDelay) totalDelay += PACK_DELAY currentTroopIdx += 1 spawnNextTroop() func spawnEnemy(enemyScene: PackedScene, laneToSpawn: int, delay: float) -> void: var enemy : PathFollow3D = createEnemy(enemyScene) if delay > 0: var enemyTimer : Timer = createTimer(delay) enemyTimer.timeout.connect( func(): paths[laneToSpawn].add_child(enemy) enemyTimer.queue_free() ) add_child(enemyTimer) else: paths[laneToSpawn].add_child(enemy) enemiesAlive += 1 func createEnemy(enemyScene: PackedScene) -> PathFollow3D : var PF3D : PathFollow3D = enemyScene.instantiate() var enemy : Enemy = PF3D.find_children("*", "Enemy")[0] enemy.died.connect(func(): enemiesAlive -= 1) return PF3D func createTimer(delay: float) -> Timer : var timer : Timer = Timer.new() timer.wait_time = delay timer.one_shot = true timer.autostart = true return timer func addMap(mapScene : PackedScene) -> void: var map : Map = mapScene.instantiate() add_child(map) level = map.level paths = map.paths.get_children() Game.money = level.startingMoney Game.allowedTowers = level.allowedTowers state = STATE.SPAWN if level.auto_start else STATE.IDLE