extends Node3D signal matador_killed signal wave_changed(wave: int) const MATADOR := preload("res://Matador.tscn") const RESPAWN_COOLDOWN := 8.0 var _respawn_cooldown: float = 0.0 var _spawn_count: int = 1 # matadors in the current wave — doubles on each clear var _wave: int = 1 # wave number, surfaced on the HUD var _alive: int = 0 func _ready() -> void: add_to_group(&"matador_spawn") _spawn_count = maxi(1, int(DP.f("mat_spawn_count"))) _start_wave(1) # Latest wave number, for a HUD that connects after the first wave has begun. func current_wave() -> int: return _wave func _process(delta: float) -> void: _respawn_cooldown = maxf(0.0, _respawn_cooldown - delta) func _unhandled_input(event: InputEvent) -> void: if event is InputEventKey and event.pressed and not event.echo: if event.physical_keycode == KEY_R and _respawn_cooldown <= 0.0: _respawn_cooldown = RESPAWN_COOLDOWN reset_spawn() # Announce a wave and spawn its matadors. Deferred spawn keeps it safe to call # from _ready and from inside a kill handler mid-signal. func _start_wave(number: int) -> void: _wave = number wave_changed.emit(_wave) _spawn.call_deferred() func _spawn() -> void: var positions := _spread_positions(_spawn_count) for pos: Vector3 in positions: _spawn_at(pos) func _spread_positions(count: int) -> Array[Vector3]: var positions: Array[Vector3] = [] var arena_r := DP.f("mat_wander_radius") * 0.72 var min_r := 5.0 for i: int in count: var sector := TAU / count var angle := sector * i + randf_range(-sector * 0.35, sector * 0.35) var radius := randf_range(min_r, arena_r) positions.append(Vector3(cos(angle) * radius, 1.0, sin(angle) * radius)) return positions func _spawn_at(pos: Vector3) -> void: var m: Node3D = MATADOR.instantiate() m.position = pos get_parent().add_child(m) m.killed.connect(_on_matador_killed) _alive += 1 func _on_matador_killed() -> void: matador_killed.emit() _alive -= 1 if _alive <= 0: _spawn_count *= 2 _start_wave(_wave + 1) func reset_spawn() -> void: for m: Node in get_tree().get_nodes_in_group(&"matador"): m.queue_free() await get_tree().process_frame _spawn_count = maxi(1, int(DP.f("mat_spawn_count"))) _alive = 0 _start_wave(1)