feat: share village needs with helpers

This commit is contained in:
Rijad Zuzo
2026-07-16 12:13:54 +02:00
parent 0332c57a89
commit d6e6a63048
17 changed files with 716 additions and 41 deletions
+84 -8
View File
@@ -77,6 +77,15 @@ func _run() -> void:
"Restarting the transient trust blossom must not mutate simulation state"
)
var village_ui := main_scene.get_node("UI")
var village_whisper := (
main_scene.get_node("VillageWhisperLayer/VillageWhisper") as VillageWhisperHud
)
var whisper_kicker := village_whisper.get_node("Copy/Kicker") as Label
var whisper_message := village_whisper.get_node("Copy/Message") as Label
_check(
not village_whisper.visible,
"The player-facing village whisper should begin quiet instead of replaying history"
)
village_ui.selected_npc_index = witness.id
village_ui.call("_refresh_npc_inspector")
_check(
@@ -250,8 +259,9 @@ func _run() -> void:
and pantry_visual.get_stock_level() == PantryStockVisual.StockLevel.EMPTY
and pantry_visual.get_node("EmptyStock").visible
and not pantry_visual.get_node("RefillResponse").visible
and not village_whisper.visible
),
"Restoration should rebuild concern and empty stock without replaying the refill response"
"Restore should rebuild stable cues without replaying refill or player-HUD feedback"
)
var player := main_scene.get_node("Player") as Node3D
var opportunity_bush := (
@@ -270,8 +280,10 @@ func _run() -> void:
player_opportunity.get_status() == OpportunityStateRecord.STATUS_RESOLVED
and "Resolved village need" in inspector_label.text
and "◆ Player restocked the pantry" in inspector_label.text
and whisper_kicker.text == "NEED MET"
and "The player restocked the pantry" in whisper_message.text
),
"Player gathering should resolve the same need and be named from its real event"
"Player gathering should resolve the restored need in debug detail and transient HUD"
)
var woodpile_state: StorageStateRecord = simulation_manager.get_woodpile()
@@ -283,7 +295,9 @@ func _run() -> void:
main_scene.get_node("JajceWorld/WorldObjects/StorageSites/VillageWoodpile") as StorageNode
)
contributor.position = woodpile_node.get_interaction_position()
witness.position = contributor.position
for npc in simulation_manager.npcs:
if npc.id != contributor.id:
npc.position = contributor.position + Vector3(20.0 + npc.id * 2.0, 0.0, 0.0)
witness.profession = SimulationIds.PROFESSION_WOODCUTTER
contributor.set_task(SimulationIds.ACTION_STUDY, 1.0)
contributor.target_id = SimulationIds.STORAGE_VILLAGE_WOODPILE
@@ -292,14 +306,64 @@ func _run() -> void:
simulation_manager.simulate_tick()
village_ui.selected_npc_index = contributor.id
village_ui.call("_refresh_npc_inspector")
var runtime_opportunity: OpportunityStateRecord = simulation_manager.get_active_opportunity()
var runtime_trigger_id := (
runtime_opportunity.get_trigger_event_id() if runtime_opportunity != null else -1
)
var runtime_helper: OpportunityHelperResult = simulation_manager.get_active_opportunity_helper()
_check(
(
runtime_helper != null
runtime_opportunity != null
and runtime_helper == null
and not simulation_manager.npc_knows_event(witness.id, runtime_trigger_id)
and whisper_kicker.text == "VILLAGE NEED"
and contributor.npc_name in whisper_message.text
and "needs wood" in whisper_message.text
),
"The real blocked-work need should surface while the distant helper remains uninformed"
)
_check(
(
"◆ Supply wood for blocked work" in village_stats_label.text
and "could not finish Study" in village_stats_label.text
and (
"Possible helper: none informed, trusted, and able to supply"
in village_stats_label.text
)
and "◆ Find wood for Study" in inspector_label.text
and not contributor_visual.get_node("OpportunityConcernRoot").visible
),
"The missing-wood UI should honestly show that no capable informed helper exists yet"
)
contributor.set_task(SimulationIds.ACTION_PATROL)
contributor.target_id = guard_site.site_id
contributor.position = guard_site.get_interaction_position()
contributor.start_working()
witness.set_task(SimulationIds.ACTION_PATROL)
witness.target_id = guard_site.site_id
witness.position = contributor.position + Vector3(1.0, 0.0, 0.0)
simulation_manager.notify_npc_arrived(witness.id)
var communicated_trigger: KnownEventStateRecord = simulation_manager.get_known_event_record(
witness.id, runtime_trigger_id
)
runtime_helper = simulation_manager.get_active_opportunity_helper()
_check(
(
communicated_trigger != null
and (
communicated_trigger.get_acquisition_method()
== SimulationIds.KNOWLEDGE_ACQUISITION_COMMUNICATED
)
and communicated_trigger.get_source_npc_id() == contributor.id
and runtime_helper != null
and runtime_helper.helper_npc_id == witness.id
and runtime_helper.action_id == SimulationIds.ACTION_GATHER_WOOD
and whisper_kicker.text == "NEWS TRAVELS"
and contributor.npc_name in whisper_message.text
and witness.npc_name in whisper_message.text
),
"Runtime helper query should derive the informed trusted woodcutter"
"A real shared activity should report the exact need and reveal the trusted helper"
)
var runtime_helper_reason := (
"Knows the need; trust %.2f toward %s" % [runtime_helper.trust, contributor.npc_name]
@@ -315,7 +379,7 @@ func _run() -> void:
and "◆ Find wood for Study" in inspector_label.text
and not contributor_visual.get_node("OpportunityConcernRoot").visible
),
"A real missing-wood fact should surface through wood-specific UI without a food cue"
"The compact summary should update from the newly communicated simulation fact"
)
witness.current_task = SimulationIds.ACTION_IDLE
witness.task_state = SimNPC.TASK_STATE_IDLE
@@ -338,9 +402,19 @@ func _run() -> void:
and witness.task_state == SimNPC.TASK_STATE_TRAVELING
and helper_resource != null
and helper_resource.get_reserved_by() == witness.id
and whisper_kicker.text == "HELP IS ON THE WAY"
and witness.npc_name in whisper_message.text
and "gather wood" in whisper_message.text
),
"The runtime helper should autonomously enter normal target resolution and reservation"
"The informed helper and cozy HUD should reflect ordinary target resolution and reservation"
)
var demo_controller := main_scene.get_node("DemoController")
demo_controller.set_debug_overlay_visible(false)
_check(
not village_ui.visible and village_whisper.visible,
"The transient player HUD should remain legible when development overlays are hidden"
)
demo_controller.set_debug_overlay_visible(true)
contributor.add_inventory(SimulationIds.RESOURCE_WOOD, 1.0)
_check(
is_equal_approx(
@@ -354,8 +428,10 @@ func _run() -> void:
(
"Village need" not in village_stats_label.text
and "%s supplied the woodpile" % contributor.npc_name in inspector_label.text
and whisper_kicker.text == "NEED MET"
and "supplied the woodpile" in whisper_message.text
),
"The resolved wood need should name its exact supplier and leave the active summary"
"Resolution should name its supplier in both debug detail and the transient player HUD"
)
_check(
main_scene.has_node("JajceWorld/TerrainRoot/Terrain3D"),