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 journal := main_scene.get_node("QuestJournalLayer/QuestJournal") manager.set_process(false) player.set_physics_process(false) _freeze_visuals(view) var actor: SimNPC = manager.npcs[0] var interested: SimNPC = manager.npcs[2] _set_pantry_amount(manager, 1.0) for npc in manager.npcs: _set_loaded_position(manager, view, npc, Vector3(40.0 + npc.id * 10.0, 0.0, 30.0)) actor.position = Vector3.ZERO interested.position = Vector3(4.0, 0.0, 0.0) actor.hunger = 60.0 interested.hunger = 90.0 manager.economy.withdraw_to_inventory(actor, SimulationIds.RESOURCE_FOOD, 1.0) var quest: PlayerQuestRecord = manager.get_active_player_quest() _check(quest != null, "An unassisted known need should generate a player quest") if quest == null: manager.set_process(true) _finish() return _check( ( quest.get_requester_npc_id() == interested.id and journal.visible and "Standing" in (journal.get_node("Copy/Standing") as Label).text and "Stranger" in (journal.get_node("Copy/Standing") as Label).text and interested.npc_name in (journal.get_node("Copy/Quest") as Label).text ), "The quest journal should surface the standing tier and the named need", ) var food_node: ResourceNode = _find_food_node(manager) _check(food_node != null, "The runtime proof needs a stocked player-usable food source") if food_node == null: _finish() return var checksum_before: String = manager.get_state_checksum() _check( manager.harvest_resource_node(food_node) > 0.0, "The player should gather the quest resource through the ordinary finite harvest" ) _check( manager.player_deposit(SimulationIds.RESOURCE_FOOD) > 0.0, "The player should resolve the quest through the ordinary pantry deposit" ) _check( ( manager.get_active_player_quest() == null and is_equal_approx( manager.get_player_standing().get_standing(), quest.get_standing_reward() ) and is_equal_approx(manager.get_player_standing().get_gratitude(interested.id), 0.1) and manager.get_state_checksum() != checksum_before ), "Resolving the need should complete the quest and grant standing + gratitude", ) var saved_json: String = manager.serialize_state() var restored := _create_restored_manager(saved_json) _check( ( restored != null and restored.get_state_checksum() == manager.get_state_checksum() and restored.get_player_standing().get_resolved_needs() == 1 and is_equal_approx( restored.get_player_standing().get_standing(), quest.get_standing_reward() ) ), "Completed quest standing should restore deterministically through the schema", ) manager.set_process(true) _finish() func _create_restored_manager(saved_json: String) -> Node: var restored: Node = load("res://simulation/SimulationManager.gd").new() restored.simulation_seed = 909 restored.debug_logs = false var home_positions: Array[Vector3] = [ Vector3(0.0, 0.0, 0.0), Vector3(2.0, 0.0, 0.0), Vector3(10.0, 0.0, 0.0), Vector3(12.0, 0.0, 0.0), Vector3(30.0, 0.0, 30.0), Vector3(40.0, 0.0, 40.0), ] restored.home_positions = home_positions root.add_child(restored) restored.set_process(false) if not restored.restore_state_from_json(saved_json): return null return restored 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 _prepare_shared_patrol(first: SimNPC, second: SimNPC, position: Vector3) -> void: for npc in [first, second]: npc.set_task(SimulationIds.ACTION_PATROL) npc.target_id = &"guard_post" npc.start_working() first.position = position second.position = position + Vector3(1.0, 0.0, 0.0) func _set_pantry_amount(manager: Node, amount: float) -> void: var pantry: StorageStateRecord = manager.get_pantry() pantry.withdraw(SimulationIds.RESOURCE_FOOD, pantry.get_amount(SimulationIds.RESOURCE_FOOD)) pantry.deposit(SimulationIds.RESOURCE_FOOD, amount) manager.economy.sync_resource(SimulationIds.RESOURCE_FOOD) 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] Quest journal runtime surfacing passed") quit(0) return for failure in failures: push_error("[TEST] " + failure) quit(1)