c61d286005
Wood now follows the same gather-carry-deposit pattern as food. NPCs put harvested wood into inventory, walk to the VillageWoodpile StorageNode, and deposit it. Village wood syncs from storage. Player harvesting also routes wood to the woodpile. ActiveWorldAdapter exposes woodpile routing for DEPOSIT_WOOD action targeting. WorldViewManager recognizes wood inventory changes. Tests verify woodpile node existence, storage ID, navigation reachability, and adapter routing.
190 lines
6.4 KiB
GDScript
190 lines
6.4 KiB
GDScript
extends Node
|
|
|
|
const DEBUG_LOGS := true
|
|
|
|
@export var npc_visual_scene: PackedScene
|
|
@export var simulation_manager: Node
|
|
@export var active_npcs_parent: Node3D
|
|
|
|
var active_npc_visuals := {}
|
|
|
|
|
|
func debug_log(message: String) -> void:
|
|
if DEBUG_LOGS:
|
|
print("[WorldViewManager] ", message)
|
|
|
|
|
|
func _ready() -> void:
|
|
call_deferred("initialize_world_view")
|
|
|
|
|
|
func initialize_world_view() -> void:
|
|
await get_tree().physics_frame
|
|
if simulation_manager.has_signal("npc_target_requested"):
|
|
simulation_manager.npc_target_requested.connect(_on_npc_target_requested)
|
|
else:
|
|
push_error("WorldViewManager: missing npc_target_requested signal")
|
|
if simulation_manager.has_signal("npc_travel_requested"):
|
|
simulation_manager.npc_travel_requested.connect(_on_npc_travel_requested)
|
|
else:
|
|
push_error("WorldViewManager: missing npc_travel_requested signal")
|
|
if simulation_manager.has_signal("npc_died"):
|
|
simulation_manager.npc_died.connect(_on_npc_died)
|
|
else:
|
|
push_error("WorldViewManager: SimulationManager has no npc_died signal")
|
|
if simulation_manager.has_signal("npc_inventory_changed"):
|
|
simulation_manager.npc_inventory_changed.connect(_on_npc_inventory_changed)
|
|
else:
|
|
push_error("WorldViewManager: SimulationManager has no npc_inventory_changed signal")
|
|
if simulation_manager.has_signal("npc_task_changed"):
|
|
simulation_manager.npc_task_changed.connect(_on_npc_task_changed)
|
|
else:
|
|
push_error("WorldViewManager: SimulationManager has no npc_task_changed signal")
|
|
if simulation_manager.has_signal("state_restored"):
|
|
simulation_manager.state_restored.connect(_on_simulation_state_restored)
|
|
else:
|
|
push_error("WorldViewManager: SimulationManager has no state_restored signal")
|
|
spawn_initial_npcs()
|
|
|
|
|
|
func spawn_initial_npcs() -> void:
|
|
if simulation_manager == null:
|
|
push_error("WorldViewManager: simulation_manager is missing")
|
|
return
|
|
if npc_visual_scene == null:
|
|
push_error("WorldViewManager: npc_visual_scene is missing")
|
|
return
|
|
if active_npcs_parent == null:
|
|
push_error("WorldViewManager: active_npcs_parent is missing")
|
|
return
|
|
debug_log("Spawning NPC visuals. Count: %s" % simulation_manager.npcs.size())
|
|
for npc in simulation_manager.npcs:
|
|
spawn_npc_visual(npc)
|
|
|
|
|
|
func spawn_npc_visual(npc: SimNPC) -> void:
|
|
if active_npc_visuals.has(npc.id):
|
|
return
|
|
var visual = npc_visual_scene.instantiate()
|
|
active_npcs_parent.add_child(visual)
|
|
visual.global_position = npc.position
|
|
visual.setup_from_sim(npc)
|
|
if visual.has_signal("arrived_at_target"):
|
|
visual.arrived_at_target.connect(_on_npc_visual_arrived)
|
|
else:
|
|
push_error("WorldViewManager: NPCVisual has no arrived_at_target signal")
|
|
if visual.has_signal("navigation_failed"):
|
|
visual.navigation_failed.connect(_on_npc_visual_navigation_failed)
|
|
else:
|
|
push_error("WorldViewManager: NPCVisual has no navigation_failed signal")
|
|
if visual.has_signal("position_changed"):
|
|
visual.position_changed.connect(_on_npc_visual_position_changed)
|
|
else:
|
|
push_error("WorldViewManager: NPCVisual has no position_changed signal")
|
|
active_npc_visuals[npc.id] = visual
|
|
debug_log("Spawned visual for %s at %s" % [npc.npc_name, npc.position])
|
|
if npc.is_dead:
|
|
visual.apply_dead_visual_state()
|
|
elif npc.task_state == SimNPC.TASK_STATE_TRAVELING:
|
|
simulation_manager.request_current_travel(npc.id)
|
|
|
|
|
|
func despawn_npc_visual(npc_id: int) -> bool:
|
|
var visual = active_npc_visuals.get(npc_id) as Node3D
|
|
if visual == null:
|
|
return false
|
|
simulation_manager.synchronize_npc_position(npc_id, visual.global_position)
|
|
active_npc_visuals.erase(npc_id)
|
|
visual.queue_free()
|
|
return true
|
|
|
|
|
|
func reload_npc_visual(npc_id: int) -> bool:
|
|
if active_npc_visuals.has(npc_id):
|
|
return false
|
|
for npc in simulation_manager.npcs:
|
|
if npc.id == npc_id:
|
|
spawn_npc_visual(npc)
|
|
return true
|
|
return false
|
|
|
|
|
|
func _on_npc_target_requested(npc: SimNPC) -> void:
|
|
var visual = active_npc_visuals.get(npc.id) as Node3D
|
|
if visual == null:
|
|
return
|
|
if not simulation_manager.resolve_npc_target(npc.id, visual.global_position):
|
|
debug_log("Target resolution failed for %s" % npc.npc_name)
|
|
|
|
|
|
func _on_npc_travel_requested(npc: SimNPC, target_position: Vector3) -> void:
|
|
var visual = active_npc_visuals.get(npc.id)
|
|
if visual == null:
|
|
return
|
|
visual.set_target_position(target_position)
|
|
|
|
|
|
func _on_npc_visual_arrived(sim_id: int) -> void:
|
|
debug_log("NPC visual arrived. sim_id=%s" % sim_id)
|
|
if simulation_manager == null:
|
|
push_error("WorldViewManager: simulation_manager missing during arrival")
|
|
return
|
|
var visual = active_npc_visuals.get(sim_id) as Node3D
|
|
if visual != null:
|
|
simulation_manager.synchronize_npc_position(sim_id, visual.global_position)
|
|
simulation_manager.notify_npc_arrived(sim_id)
|
|
|
|
|
|
func _on_npc_visual_navigation_failed(sim_id: int) -> void:
|
|
debug_log("NPC visual navigation failed. sim_id=%s" % sim_id)
|
|
if simulation_manager == null:
|
|
push_error("WorldViewManager: simulation_manager missing during failure")
|
|
return
|
|
var visual = active_npc_visuals.get(sim_id) as Node3D
|
|
if visual != null:
|
|
simulation_manager.synchronize_npc_position(sim_id, visual.global_position)
|
|
simulation_manager.notify_npc_navigation_failed(sim_id)
|
|
|
|
|
|
func _on_npc_visual_position_changed(sim_id: int, active_position: Vector3) -> void:
|
|
simulation_manager.synchronize_npc_position(sim_id, active_position)
|
|
|
|
|
|
func _on_npc_died(npc: SimNPC) -> void:
|
|
if not active_npc_visuals.has(npc.id):
|
|
return
|
|
var visual = active_npc_visuals[npc.id]
|
|
if visual.has_method("apply_dead_visual_state"):
|
|
visual.apply_dead_visual_state()
|
|
else:
|
|
push_error("WorldViewManager: NPCVisual has no apply_dead_visual_state")
|
|
|
|
|
|
func _on_npc_inventory_changed(npc: SimNPC, item_id: StringName, amount: float) -> void:
|
|
var visual = active_npc_visuals.get(npc.id)
|
|
if visual == null:
|
|
return
|
|
if item_id == SimulationIds.RESOURCE_FOOD:
|
|
if visual.has_method("set_carried_food_visible"):
|
|
visual.set_carried_food_visible(amount > 0.0)
|
|
elif item_id == SimulationIds.RESOURCE_WOOD:
|
|
if visual.has_method("set_carried_wood_visible"):
|
|
visual.set_carried_wood_visible(amount > 0.0)
|
|
|
|
|
|
func _on_npc_task_changed(npc: SimNPC, _old_task: StringName, _new_task: StringName) -> void:
|
|
var visual = active_npc_visuals.get(npc.id)
|
|
if visual == null:
|
|
return
|
|
if visual.has_method("set_task_presentation"):
|
|
visual.set_task_presentation(npc.current_task, npc.task_state)
|
|
else:
|
|
push_error("WorldViewManager: NPCVisual has no set_task_presentation method")
|
|
|
|
|
|
func _on_simulation_state_restored() -> void:
|
|
for visual in active_npc_visuals.values():
|
|
visual.queue_free()
|
|
active_npc_visuals.clear()
|
|
spawn_initial_npcs()
|