extends SceneTree var failures: Array[String] = [] func _initialize() -> void: call_deferred("_run") func _run() -> void: var main_scene: Node = load("res://main.tscn").instantiate() root.add_child(main_scene) await process_frame for _frame in 10: await physics_frame var manager := main_scene.get_node("SimulationManager") var world_view := main_scene.get_node("WorldViewManager") var demo := main_scene.get_node("DemoController") var caption := main_scene.get_node("CrisisCaptionLayer/CrisisCaption") as Label manager.set_process(false) var staged: Dictionary = demo.stage_pantry_crisis() _check(not staged.is_empty(), "The repeatable pantry crisis should stage from a fresh scene") if staged.is_empty(): _finish() return var helper: SimNPC = staged["helper"] var interested: SimNPC = staged["interested"] var shortage_actor: SimNPC = staged["shortage_actor"] var opportunity: OpportunityStateRecord = staged["opportunity"] var pantry: StorageStateRecord = manager.get_pantry() var helper_visual: Node3D = world_view.active_npc_visuals.get(helper.id) var interested_visual: Node3D = world_view.active_npc_visuals.get(interested.id) _check( is_equal_approx(pantry.get_amount(SimulationIds.RESOURCE_FOOD), 0.0), "The demo shortage should empty the authoritative pantry" ) _check( is_equal_approx(shortage_actor.get_inventory_amount(SimulationIds.RESOURCE_FOOD), 0.0), "The final pantry unit should be consumed through the real economy before the food run" ) _check( ( opportunity.get_interested_npc_id() == interested.id and interested_visual.get_node("OpportunityConcernRoot").visible and interested_visual.current_target == Vector3.INF ), "The informed hungry villager should own and visibly express the open need" ) _check( not caption.visible, "The cinematic caption should remain hidden until the full sequence starts" ) _check(demo.begin_pantry_restock(), "A trusted helper should begin the real food run") var decision: ActionSelectionResult = manager.get_latest_decision(helper.id) _check( ( decision != null and decision.action_id == SimulationIds.ACTION_GATHER_FOOD and interested.npc_name in decision.reason ), "The helper's selected action should be caused by the starving trusted villager" ) _check( ( helper.task_state == SimNPC.TASK_STATE_TRAVELING and helper.has_travel_target and not helper.target_id.is_empty() ), "The helper should reserve and travel to a finite food ResourceNode" ) var resource_state: ResourceStateRecord = manager.get_resource_state(helper.target_id) _check( ( resource_state != null and resource_state.get_resource_id() == SimulationIds.RESOURCE_FOOD and resource_state.get_reserved_by() == helper.id ), "The demo trip should use the normal finite-resource reservation contract" ) helper_visual.global_position = helper.travel_target_position manager.synchronize_npc_position(helper.id, helper_visual.global_position) manager.notify_npc_arrived(helper.id) for _tick in int(ceil(helper.task_duration)): manager.simulate_tick() _check( ( helper.get_inventory_amount(SimulationIds.RESOURCE_FOOD) > 0.0 and helper_visual.get_node("CarriedFood").visible ), "Completing the real gather action should create visible carried food" ) manager.simulate_tick() _check( ( helper.current_task == SimulationIds.ACTION_DEPOSIT_FOOD and helper.target_id == SimulationIds.STORAGE_VILLAGE_PANTRY and helper.has_travel_target ), "The helper should autonomously choose the real pantry deposit after gathering" ) helper_visual.global_position = helper.travel_target_position manager.synchronize_npc_position(helper.id, helper_visual.global_position) manager.notify_npc_arrived(helper.id) manager.simulate_tick() var pantry_visual := ( main_scene.get_node("JajceWorld/WorldObjects/StorageSites/VillagePantry/PantryPresentation") as PantryStockVisual ) var resolved: OpportunityStateRecord = manager.opportunity_system.get_latest() _check( ( manager.get_active_opportunity() == null and resolved != null and resolved.get_status() == SimulationIds.OPPORTUNITY_STATUS_RESOLVED and resolved.get_resolution_event_id() > resolved.get_trigger_event_id() ), "The exact later deposit event should resolve the same opportunity record" ) _check( ( pantry.get_amount(SimulationIds.RESOURCE_FOOD) > 0.0 and pantry_visual.get_node("RefillResponse").visible and not interested_visual.get_node("OpportunityConcernRoot").visible ), "The deposit should physically restock the pantry and clear the concern cue" ) _check( ( _has_event(manager.economic_events, SimulationIds.EVENT_RESOURCE_EXTRACTED, helper.id) and _has_event( manager.economic_events, SimulationIds.EVENT_STORAGE_DEPOSITED, helper.id ) ), "The sequence should be traceable through real extraction and deposit events" ) _finish() func _has_event(events: Array[EconomicEventRecord], event_type: StringName, actor_id: int) -> bool: for event in events: if ( StringName(event.data["event_type"]) == event_type and int(event.data["actor_id"]) == actor_id ): return true return false func _check(condition: bool, message: String) -> void: if not condition: failures.append(message) func _finish() -> void: if failures.is_empty(): print("[TEST] Pantry crisis demo scenario passed") quit(0) return for failure in failures: push_error("[TEST] " + failure) quit(1)