111 lines
3.1 KiB
GDScript
111 lines
3.1 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 _initialized := false
|
|
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)
|
|
_initialized = true
|
|
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 _process(_delta: float) -> void:
|
|
if not _initialized:
|
|
return
|
|
for combatant_id in _hostile_ids():
|
|
if not hostiles.has(combatant_id):
|
|
_spawn_visual(combatant_id)
|
|
|
|
|
|
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
|