Files
Bullosseum/levels/matador_spawn.gd
T
richard 09d4a60f43 Reorganize into role-based folders and consolidate art
Move every script and scene into role-based directories (actors/, systems/, ui/,
levels/, props/) and all raw art under art/ (grouped by role, snake_case,
Extract-Textures GLBs), dropping Blender version-suffix noise. Filenames are
snake_case (PascalCase reserved for class_name) to avoid case-insensitive
filesystem aliasing on the web/macOS targets. Routes the bull's hits through the
typed DamageEvent pipeline and wires the bull's abilities to the Fx library.
Updates CLAUDE.md's folder-layout overview to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CCyaJPTHCH9EDthpf4A5Sj
2026-09-08 14:56:22 +03:00

92 lines
2.4 KiB
GDScript

extends Node3D
signal matador_killed
signal all_defeated
const MATADOR := preload("res://actors/matador/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
# 8 waves total.
if _wave > DP.f("mat_wave_size"):
return
# Wave 1 = 1 matador, Wave 2 = 2, ... Wave 8 = 8.
var count := _wave
# Shuffle the spawn points so the spawn locations are random.
var points: Array[Node3D] = _spawn_points.duplicate()
points.shuffle()
for i in range(count):
# Reuse spawn points when we have more matadors than points.
var sp: Node3D = points[i % points.size()]
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 >= DP.f("mat_wave_size"):
DP.set_value("gate_unlocked_bear", true)
_resolved = true
all_defeated.emit()
else:
await get_tree().create_timer(DP.f("mat_wave_pause")).timeout
_start_next_wave()