extends Node const ENEMY_SCENE: PackedScene = preload("res://scenes/enemy/enemy.tscn") const BOSS_STATS: Resource = preload("res://scenes/enemy/boss_stats.tres") const INVASION_COUNT := 16 const SPAWN_RADIUS := 45.0 var active: bool = false var invasion_enemies: Array[Node] = [] func _ready() -> void: EventBus.wave_timer_tick.connect(_on_wave_timer_tick) EventBus.entity_died.connect(_on_entity_died) EventBus.tavern_destroyed.connect(_on_tavern_destroyed) func _on_wave_timer_tick(seconds_remaining: float) -> void: if active: return if seconds_remaining > 0: return var has_alive_red := false for p in get_tree().get_nodes_in_group("red_portal"): if is_instance_valid(p) and PortalData.is_alive(p): has_alive_red = true break if not has_alive_red: return trigger() func trigger() -> void: active = true invasion_enemies.clear() var tavern: Node = get_tree().get_first_node_in_group("tavern") if not tavern: active = false return var world: Node = get_tree().current_scene var scale: float = PlayerData.level_scale * 10.0 for enemy in get_tree().get_nodes_in_group("red_enemies"): if is_instance_valid(enemy): _convert_to_invasion(enemy, tavern) for i in range(INVASION_COUNT): var angle: float = randf() * TAU var pos := Vector3(cos(angle) * SPAWN_RADIUS, 0, sin(angle) * SPAWN_RADIUS) var enemy: Node = ENEMY_SCENE.instantiate() enemy.spawn_scale = scale world.add_child(enemy) enemy.global_position = pos _convert_to_invasion(enemy, tavern) var boss_angle: float = randf() * TAU var boss_pos := Vector3(cos(boss_angle) * SPAWN_RADIUS, 0, sin(boss_angle) * SPAWN_RADIUS) var boss: Node = ENEMY_SCENE.instantiate() boss.add_to_group("boss") boss.stats = BOSS_STATS boss.spawn_scale = scale world.add_child(boss) boss.global_position = boss_pos _convert_to_invasion(boss, tavern) EventBus.invasion_started.emit(invasion_enemies) func _convert_to_invasion(enemy: Node, tavern: Node) -> void: enemy.add_to_group("invasion") var data_source: Node = BossData if enemy.is_in_group("boss") else EnemyData data_source.set_stat(enemy, "target", tavern) data_source.set_stat(enemy, "state", 1) invasion_enemies.append(enemy) func _on_entity_died(entity: Node) -> void: if not active: return if entity not in invasion_enemies: return invasion_enemies.erase(entity) var alive_count := 0 for e in invasion_enemies: if is_instance_valid(e): alive_count += 1 if alive_count == 0: _end_invasion(true) func _end_invasion(success: bool) -> void: active = false invasion_enemies.clear() EventBus.invasion_ended.emit(success) func _on_tavern_destroyed() -> void: active = false EventBus.game_over.emit()