extends Node3D signal matador_killed signal all_defeated const MATADOR := preload("res://Matador.tscn") var _alive: int = 0 var _wave: int = 0 var _resolved: bool = false var _spawn_points: Array[Node3D] = [] func _ready() -> void: add_to_group(&"matador_spawn") _spawn.call_deferred() # Each spawner node is a spawn-point marker. The first in tree order (the one the HUD # connects its all_defeated to) runs the wave logic and uses every marker — itself and # its siblings — as an entry point; the rest stay passive so waves aren't run four times. func _spawn() -> void: var markers := get_tree().get_nodes_in_group(&"matador_spawn") if markers.is_empty() or markers[0] != self: return for node: Node in markers: if node is Node3D: _spawn_points.append(node as Node3D) _place_bull(randf() * TAU, DP.f("arena_spawn_radius")) _start_next_wave() # Wave 1 sends 1 matador, wave 2 sends 2, … up to the number of placed spawn points. # Matadors are assigned to a random subset of spawn points each wave and enter staggered. func _start_next_wave() -> void: _wave += 1 var count := mini(_wave, _spawn_points.size()) var points: Array = _spawn_points.duplicate() points.shuffle() for i: int in count: var sp := points[i] as Node3D var m := _spawn_at(sp.global_position) m.call_deferred(&"enter_spawn_roll", Vector3.ZERO) if i < count - 1: await get_tree().create_timer(DP.f("mat_spawn_stagger")).timeout func _place_bull(angle: float, arena_r: float) -> void: var bull := get_tree().get_first_node_in_group(&"player") as Node3D if not bull: return bull.global_position = Vector3( cos(angle) * arena_r, bull.global_position.y, sin(angle) * arena_r ) func _spawn_at(pos: Vector3) -> Node3D: var m: Node3D = MATADOR.instantiate() m.position = pos get_parent().add_child(m) m.killed.connect(_on_matador_killed) _alive += 1 return m func _on_matador_killed() -> void: matador_killed.emit() _alive -= 1 if _alive > 0 or _resolved: return if _wave >= _spawn_points.size(): _resolved = true all_defeated.emit() else: await get_tree().create_timer(DP.f("mat_wave_pause")).timeout _start_next_wave()