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: Node = main_scene.get_node("SimulationManager") var view: Node = main_scene.get_node("WorldViewManager") var player: CharacterBody3D = main_scene.get_node("Player") var demo: Node = main_scene.get_node("DemoController") var field_note := main_scene.get_node("VillagerInspectionLayer/VillagerFieldNote") manager.set_process(false) player.set_physics_process(false) _freeze_visuals(view) var staged: Dictionary = demo.call("stage_pantry_crisis") _check(not staged.is_empty(), "The runtime proof should stage a real open pantry need") if staged.is_empty(): _finish() return var interested: SimNPC = staged["interested"] var pantry_position: Vector3 = ( StorageNode.get_by_id(SimulationIds.STORAGE_VILLAGE_PANTRY).get_interaction_position() ) player.global_position = pantry_position _move_population_away(manager, view, interested.id, pantry_position) _set_loaded_position(manager, view, interested, pantry_position + Vector3(1.0, 0.0, 0.0)) var checksum_before: String = manager.get_state_checksum() var serialized_before: String = manager.serialize_state() var events_before: int = manager.economic_events.size() for _query in 3: player.call("get_nearby_villager_inspection") field_note.call("refresh_note", true) var need_label := field_note.get_node("Copy/Need") as Label _check( ( manager.get_state_checksum() == checksum_before and manager.serialize_state() == serialized_before and manager.economic_events.size() == events_before and field_note.visible and need_label.visible and "Need" in need_label.text ), "The inspected interested villager should surface the real need without mutating state", ) if not need_label.visible: _finish() return var helper: OpportunityHelperResult = manager.get_active_opportunity_helper() var helper_npc := _find_npc(manager.npcs, helper.helper_npc_id) if helper != null else null _check( ( helper != null and helper_npc != null and helper_npc.npc_name in need_label.text and "bringing" in need_label.text.to_lower() ), "The open need with a capable helper should name that helper on the field note", ) var checksum_after_helper: String = manager.get_state_checksum() var food_node: ResourceNode = _find_food_node(manager) _check(food_node != null, "The resolution proof needs a stocked player-usable food source") if food_node == null: _finish() return var harvested: float = manager.harvest_resource_node(food_node) var deposited: float = manager.player_deposit(SimulationIds.RESOURCE_FOOD) _check( ( harvested > 0.0 and deposited > 0.0 and manager.get_active_opportunity() == null and ( manager.opportunity_system.get_latest().get_status() == OpportunityStateRecord.STATUS_RESOLVED ) ), "An ordinary finite-resource harvest and pantry deposit should resolve the same open need", ) field_note.call("refresh_note", true) _check( not need_label.visible and manager.get_state_checksum() != checksum_after_helper, "Resolving the need should clear the surfaced need segment from the field note", ) _finish() func _find_food_node(manager: Node) -> ResourceNode: for node in ResourceNode.get_all(): var state: ResourceStateRecord = ( manager.get_resource_state(node.node_id) as ResourceStateRecord ) if ( node.resource_id == SimulationIds.RESOURCE_FOOD and state != null and state.can_player_use_resource() and state.can_extract() and state.get_amount_remaining() >= 1.0 ): return node return null func _find_npc(npcs: Array[SimNPC], npc_id: int) -> SimNPC: for npc in npcs: if npc.id == npc_id: return npc return null func _move_population_away(manager: Node, view: Node, keep_id: int, origin: Vector3) -> void: for npc in manager.npcs: if npc.id == keep_id: continue _set_loaded_position( manager, view, npc, origin + Vector3(40.0 + float(npc.id) * 2.0, 0.0, 20.0) ) func _set_loaded_position(manager: Node, view: Node, npc: SimNPC, position: Vector3) -> void: manager.synchronize_npc_position(npc.id, position) var visual := view.active_npc_visuals.get(npc.id) as Node3D if visual != null: visual.set_physics_process(false) if visual.has_method("stop_travel"): visual.stop_travel() visual.global_position = position func _freeze_visuals(view: Node) -> void: for visual in view.active_npc_visuals.values(): visual.set_physics_process(false) if visual.has_method("stop_travel"): visual.stop_travel() func _check(condition: bool, message: String) -> void: if not condition: failures.append(message) func _finish() -> void: if failures.is_empty(): print("[TEST] Field-note opportunity surfacing passed") quit(0) return for failure in failures: push_error("[TEST] " + failure) quit(1)