Files
2026-08-12 10:02:45 +02:00

101 lines
2.9 KiB
GDScript

class_name CombatPresentation
extends Node
const HOSTILE_SCENE := preload("res://world/combat/HostileCombatant.tscn")
@export var simulation_manager: Node
@export var hostiles_parent: Node3D
@export var wolf_spawn_positions: PackedVector3Array = PackedVector3Array()
var hostiles: Dictionary = {}
var _wolves_spawned := false
func _ready() -> void:
add_to_group("combat_presentation")
if simulation_manager == null:
push_error("CombatPresentation: simulation_manager missing")
return
simulation_manager.combatant_spawned.connect(_on_combatant_spawned)
simulation_manager.combatant_died.connect(_on_combatant_died)
simulation_manager.state_restored.connect(_on_state_restored)
call_deferred("_spawn_wolves")
_refresh_from_state()
func _spawn_wolves() -> void:
if _wolves_spawned or simulation_manager == null:
return
_wolves_spawned = true
for position in wolf_spawn_positions:
if simulation_manager.has_method("spawn_wolf"):
simulation_manager.spawn_wolf(position)
func _hostile_ids() -> Array:
var ids: Array[StringName] = []
var living: Array = simulation_manager.get_living_hostile_combatants()
for combatant in living:
ids.append(combatant.get_combatant_id())
return ids
func _spawn_visual(combatant_id: StringName) -> void:
if hostiles.has(combatant_id):
return
var scene := HOSTILE_SCENE.instantiate() as HostileCombatant
hostiles_parent.add_child(scene)
scene.bind(combatant_id, simulation_manager)
hostiles[combatant_id] = scene
func _on_combatant_spawned(combatant_id: StringName) -> void:
_spawn_visual(combatant_id)
func _on_combatant_died(combatant_id: StringName) -> void:
var scene := hostiles.get(combatant_id) as HostileCombatant
if scene != null:
scene.play_death()
hostiles.erase(combatant_id)
func _on_state_restored() -> void:
for combatant_id in hostiles.keys():
var scene := hostiles[combatant_id] as Node
if is_instance_valid(scene):
scene.queue_free()
hostiles.clear()
_refresh_from_state()
func _refresh_from_state() -> void:
if simulation_manager == null:
return
for combatant_id in _hostile_ids():
_spawn_visual(combatant_id)
func find_hostile_in_arc(
origin: Vector3, facing: Vector3, max_distance: float, angle_degrees: float
) -> CombatantStateRecord:
var living: Array = simulation_manager.get_living_hostile_combatants()
var best: CombatantStateRecord
var best_score := INF
var facing_flat := Vector3(facing.x, 0.0, facing.z).normalized()
var half_cos := cos(deg_to_rad(angle_degrees) * 0.5)
for combatant in living:
var candidate := combatant as CombatantStateRecord
var offset := candidate.get_position() - origin
var horizontal := Vector2(offset.x, offset.z)
if horizontal.length() > max_distance:
continue
var direction := Vector3(offset.x, 0.0, offset.z).normalized()
if facing_flat.dot(direction) < half_cos:
continue
var distance := horizontal.length()
if distance < best_score:
best = candidate
best_score = distance
return best