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 var manager: Node = main_scene.get_node("SimulationManager") manager.set_process(false) for _frame in 3: await physics_frame var dunja_node := AnimalNode.get_by_id(SimulationIds.ANIMAL_DUNJA) var zora_node := AnimalNode.get_by_id(SimulationIds.ANIMAL_ZORA) var dunja_state: AnimalStateRecord = manager.animal_care.get_state(SimulationIds.ANIMAL_DUNJA) var zora_state: AnimalStateRecord = manager.animal_care.get_state(SimulationIds.ANIMAL_ZORA) var shared_pasture := AnimalRoutineSite.get_by_id(SimulationIds.ANIMAL_SITE_DUNJA_PASTURE) var dunja_shelter := AnimalRoutineSite.get_by_id(SimulationIds.ANIMAL_SITE_DUNJA_SHELTER) var zora_shelter := AnimalRoutineSite.get_by_id(SimulationIds.ANIMAL_SITE_ZORA_SHELTER) _check( ( AnimalNode.get_all().size() == 2 and manager.animal_care.states.size() == 2 and dunja_node != null and zora_node != null and dunja_state != null and zora_state != null and dunja_node.state == dunja_state and zora_node.state == zora_state ), "The real main scene should bind exactly two stable named-goat records", ) _check( shared_pasture != null and dunja_shelter != null and zora_shelter != null, "The pair proof needs one shared pasture and one authored shelter per goat", ) if ( dunja_node == null or zora_node == null or dunja_state == null or zora_state == null or shared_pasture == null or dunja_shelter == null or zora_shelter == null ): _finish() return for npc in manager.npcs: _park_npc(npc) var adapter := manager.active_world_adapter as ActiveWorldAdapter var dunja_sites: Array[Dictionary] = adapter.get_animal_routine_sites( SimulationIds.SPECIES_GOAT, SimulationIds.ANIMAL_DUNJA ) var zora_sites: Array[Dictionary] = adapter.get_animal_routine_sites( SimulationIds.SPECIES_GOAT, SimulationIds.ANIMAL_ZORA ) _check( ( ( _site_ids(dunja_sites) == [ SimulationIds.ANIMAL_SITE_DUNJA_PASTURE, SimulationIds.ANIMAL_SITE_DUNJA_SHELTER, ] ) and ( _site_ids(zora_sites) == [ SimulationIds.ANIMAL_SITE_DUNJA_PASTURE, SimulationIds.ANIMAL_SITE_ZORA_SHELTER, ] ) ), "Routine discovery should expose the shared pasture plus only each goat's own shelter", ) zora_state.set_hunger(30.0) _check( zora_state.schedule_routine_at(manager.tick_count), "Zora's routine should be schedulable immediately from her authored shelter", ) manager.simulate_tick() _check( ( zora_state.has_active_travel_target() and zora_state.get_routine_site_id() == SimulationIds.ANIMAL_SITE_ZORA_SHELTER and (zora_state.get_travel_target_site_id() == SimulationIds.ANIMAL_SITE_DUNJA_PASTURE) and (zora_state.get_travel_target_position().is_equal_approx( shared_pasture.get_routine_position() )) ), "Zora should deterministically choose the shared pasture from her own shelter", ) _check( zora_state.complete_travel(shared_pasture.get_routine_position(), manager.tick_count), "The routine proof should be able to complete Zora's pasture leg directly", ) _check( zora_state.schedule_routine_at(manager.tick_count), "Zora's return leg should be schedulable without waiting for long navigation", ) manager.simulate_tick() _check( ( zora_state.has_active_travel_target() and (zora_state.get_routine_site_id() == SimulationIds.ANIMAL_SITE_DUNJA_PASTURE) and (zora_state.get_travel_target_site_id() == SimulationIds.ANIMAL_SITE_ZORA_SHELTER) and (zora_state.get_travel_target_position().is_equal_approx( zora_shelter.get_routine_position() )) ), "Zora should return to her own shelter rather than entering Dunja's shelter", ) zora_state.cancel_travel(manager.tick_count + AnimalCareSystem.ROUTINE_RETRY_TICKS) dunja_state.set_hunger(72.0) zora_state.set_hunger(72.0) var zora_caretaker: SimNPC = manager.npcs[0] var dunja_caretaker: SimNPC = manager.npcs[1] _prepare_feed_task(manager, zora_caretaker) _prepare_feed_task(manager, dunja_caretaker) var shared_origin := zora_node.get_interaction_position() zora_caretaker.position = shared_origin dunja_caretaker.position = shared_origin _check( manager.resolve_npc_target(zora_caretaker.id, shared_origin), "The first caretaker should resolve a real hungry animal target", ) _check( ( zora_caretaker.target_id == SimulationIds.ANIMAL_ZORA and zora_state.get_reserved_by() == zora_caretaker.id and dunja_state.get_reserved_by() == -1 ), "The first caretaker should claim the nearest hungry goat, Zora, by exact ID", ) _check( manager.resolve_npc_target(dunja_caretaker.id, shared_origin), "The second caretaker should resolve the remaining available hungry goat", ) _check( ( dunja_caretaker.target_id == SimulationIds.ANIMAL_DUNJA and dunja_state.get_reserved_by() == dunja_caretaker.id and zora_state.get_reserved_by() == zora_caretaker.id ), "Simultaneous hunger should retain two independent exact animal reservations", ) var claimed_json: String = manager.serialize_state() var claimed_checksum: String = manager.get_state_checksum() var zora_caretaker_id := zora_caretaker.id var dunja_caretaker_id := dunja_caretaker.id _check( manager.restore_state_from_json(claimed_json), "A save containing both exact feed claims should pass the complete schema", ) dunja_state = manager.animal_care.get_state(SimulationIds.ANIMAL_DUNJA) zora_state = manager.animal_care.get_state(SimulationIds.ANIMAL_ZORA) zora_caretaker = _find_npc(manager.npcs, zora_caretaker_id) dunja_caretaker = _find_npc(manager.npcs, dunja_caretaker_id) _check( ( manager.get_state_checksum() == claimed_checksum and zora_node.state == zora_state and dunja_node.state == dunja_state and zora_caretaker != null and dunja_caretaker != null and zora_caretaker.target_id == SimulationIds.ANIMAL_ZORA and dunja_caretaker.target_id == SimulationIds.ANIMAL_DUNJA and zora_state.get_reserved_by() == zora_caretaker_id and dunja_state.get_reserved_by() == dunja_caretaker_id ), "Restore should retain both claims, bindings, target IDs, and the exact checksum", ) if zora_caretaker == null or dunja_caretaker == null: _finish() return var pantry: StorageStateRecord = manager.get_pantry() var pantry_food_before := pantry.get_amount(SimulationIds.RESOURCE_FOOD) var zora_hunger_before := zora_state.get_hunger() var zora_fed_tick_before := zora_state.get_last_fed_tick() var dunja_hunger_before := dunja_state.get_hunger() var dunja_fed_tick_before := dunja_state.get_last_fed_tick() var dunja_reservation_before := dunja_state.get_reserved_by() _check( manager.animal_care.collect_feed_supply(zora_caretaker), "The restored caretaker should collect one real pantry food for Zora", ) _check( manager.feed_animal(SimulationIds.ANIMAL_ZORA, zora_caretaker_id), "The restored exact Zora claim should deliver its carried food", ) _check( ( is_equal_approx( zora_state.get_hunger(), zora_hunger_before - AnimalStateRecord.HUNGER_RELIEF ) and zora_state.get_last_fed_tick() == manager.tick_count and zora_state.get_last_fed_tick() != zora_fed_tick_before and zora_state.get_reserved_by() == -1 and is_equal_approx(dunja_state.get_hunger(), dunja_hunger_before) and dunja_state.get_last_fed_tick() == dunja_fed_tick_before and dunja_state.get_reserved_by() == dunja_reservation_before ), "Feeding Zora should mutate only Zora's hunger, feed tick, and reservation", ) _check( is_equal_approx(pantry.get_amount(SimulationIds.RESOURCE_FOOD), pantry_food_before - 1.0), "Collecting for exactly one goat should withdraw exactly one pantry food", ) var feed_event := _latest_feed_event(manager) _check( ( feed_event != null and int(feed_event.data["actor_id"]) == zora_caretaker_id and ( StringName(feed_event.data["source_id"]) == SimulationIds.npc_inventory_id(zora_caretaker_id) ) and StringName(feed_event.data["destination_id"]) == SimulationIds.ANIMAL_ZORA and StringName(feed_event.data["item_id"]) == SimulationIds.RESOURCE_FOOD and is_equal_approx(float(feed_event.data["amount"]), 1.0) and feed_event.get_world_position().is_equal_approx(zora_state.get_position()) ), "The feed event should record the exact caretaker inventory, Zora, and amount", ) _finish() func _park_npc(npc: SimNPC) -> void: npc.set_task(SimulationIds.ACTION_WANDER, 1000.0) npc.start_working() func _prepare_feed_task(manager: Node, npc: SimNPC) -> void: manager.release_npc_reservation(npc.id) npc.target_id = &"" npc.has_travel_target = false npc.set_task(SimulationIds.ACTION_FEED_ANIMAL) func _site_ids(candidates: Array[Dictionary]) -> Array[StringName]: var ids: Array[StringName] = [] for candidate in candidates: ids.append(StringName(candidate["site_id"])) return ids func _find_npc(npcs: Array[SimNPC], npc_id: int) -> SimNPC: for npc in npcs: if npc.id == npc_id: return npc return null func _latest_feed_event(manager: Node) -> EconomicEventRecord: for index in range(manager.economic_events.size() - 1, -1, -1): var event := manager.economic_events[index] as EconomicEventRecord if StringName(event.data["event_type"]) == SimulationIds.EVENT_ANIMAL_FED: return event return null func _check(condition: bool, message: String) -> void: if not condition: failures.append(message) func _finish() -> void: if failures.is_empty(): print("[TEST] Two-goat vertical slice passed") quit(0) return for failure in failures: push_error("[TEST] " + failure) quit(1)