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 goat := main_scene.get_node("JajceWorld/WorldObjects/Animals/Dunja") as AnimalNode var state: AnimalStateRecord = manager.animal_care.get_state(SimulationIds.ANIMAL_DUNJA) var pasture := AnimalRoutineSite.get_by_id(SimulationIds.ANIMAL_SITE_DUNJA_PASTURE) var shelter := AnimalRoutineSite.get_by_id(SimulationIds.ANIMAL_SITE_DUNJA_SHELTER) _check( goat != null and state != null and pasture != null and shelter != null, "The routine proof needs Dunja and both loaded stable-ID habitat sites" ) if goat == null or state == null or pasture == null or shelter == null: _finish() return _check( not state.begin_travel(shelter.site_id, shelter.get_routine_position()), "Animal state should reject travel back to its current stable site", ) state.set_enabled(false) _check( not state.begin_travel(pasture.site_id, pasture.get_routine_position()), "Animal state should reject travel while the animal is disabled", ) state.set_enabled(true) for npc in manager.npcs: npc.set_task(SimulationIds.ACTION_WANDER, 1000.0) npc.start_working() var navigation_map: RID = main_scene.get_world_3d().navigation_map await _wait_for_navigation_map(navigation_map) NavigationServer3D.map_force_update(navigation_map) var authored_path := NavigationServer3D.map_get_path( navigation_map, shelter.get_routine_position(), pasture.get_routine_position(), true ) _check( not authored_path.is_empty(), "The shelter and pasture should be connected by the real Jajce navigation map" ) state.set_hunger(30.0) _check( state.schedule_routine_at(manager.tick_count), "The test should be able to make the content goat's routine immediately due" ) goat.move_speed = 4.0 manager.simulate_tick() _check( ( state.has_active_travel_target() and state.get_routine_site_id() == SimulationIds.ANIMAL_SITE_DUNJA_SHELTER and state.get_travel_target_site_id() == SimulationIds.ANIMAL_SITE_DUNJA_PASTURE and state.get_travel_target_position().is_equal_approx(pasture.get_routine_position()) ), "The due routine should deterministically choose pasture from shelter" ) for _frame in 3: await physics_frame _check( goat.is_navigating() and not goat.get_navigation_path().is_empty(), "The loaded goat should follow a NavigationServer path rather than a straight state teleport" ) var start_position := state.get_position() for _frame in 22: await physics_frame var mid_route_position := state.get_position() _check( ( not mid_route_position.is_equal_approx(start_position) and state.has_active_travel_target() and goat.global_position.is_equal_approx(mid_route_position) ), "Loaded movement should continuously report Dunja's authoritative mid-route position" ) var mid_route_json: String = manager.serialize_state() var mid_route_checksum: String = manager.get_state_checksum() var random_streams_before_restore := JSON.stringify( manager.create_state_record().simulation["wander_random_streams"] ) var saved_target_position := state.get_travel_target_position() _check( manager.restore_state_from_json(mid_route_json), "A schema-v11 save should accept Dunja's in-progress routine" ) state = manager.animal_care.get_state(SimulationIds.ANIMAL_DUNJA) _check( ( manager.get_state_checksum() == mid_route_checksum and state.get_position().is_equal_approx(mid_route_position) and state.get_travel_target_site_id() == SimulationIds.ANIMAL_SITE_DUNJA_PASTURE and state.get_travel_target_position().is_equal_approx(saved_target_position) and goat.global_position.is_equal_approx(mid_route_position) and ( JSON.stringify(manager.create_state_record().simulation["wander_random_streams"]) == random_streams_before_restore ) ), "Mid-route restore should preserve exact position, target, checksum, and every NPC RNG stream" ) var animals_parent := goat.get_parent() goat.queue_free() await process_frame _check( ( AnimalNode.get_by_id(SimulationIds.ANIMAL_DUNJA) == null and state.has_active_travel_target() and state.get_position().is_equal_approx(mid_route_position) and manager.get_state_checksum() == mid_route_checksum ), "Unloading Dunja's presentation should leave her in-progress simulation state untouched" ) var reloaded_goat := load("res://world/animals/goat/cozy_goat.tscn").instantiate() as AnimalNode animals_parent.add_child(reloaded_goat) await process_frame goat = reloaded_goat goat.move_speed = 4.0 _check( ( goat.state == state and goat.global_position.is_equal_approx(mid_route_position) and manager.get_state_checksum() == mid_route_checksum ), "Reloading the self-contained goat scene should bind the same state without resetting it" ) for _frame in 3: await physics_frame _check( goat.is_navigating() and not goat.get_navigation_path().is_empty(), "Restore should rebuild the loaded navigation path from saved facts" ) for _frame in 240: if not state.has_active_travel_target(): break await physics_frame _check( ( not state.has_active_travel_target() and state.get_routine_site_id() == SimulationIds.ANIMAL_SITE_DUNJA_PASTURE and state.get_position().is_equal_approx(pasture.get_routine_position()) and not goat.is_navigating() ), ( "Arrival should atomically promote pasture and retain its exact position " + ( "(active=%s site=%s state=%s target=%s navigating=%s)" % [ state.has_active_travel_target(), state.get_routine_site_id(), state.get_position(), pasture.get_routine_position(), goat.is_navigating(), ] ) ) ) var arrived_json: String = manager.serialize_state() var arrived_checksum: String = manager.get_state_checksum() _check( ( manager.restore_state_from_json(arrived_json) and manager.get_state_checksum() == arrived_checksum ), "An arrived routine should also restore without replaying movement" ) state = manager.animal_care.get_state(SimulationIds.ANIMAL_DUNJA) _check( not state.has_active_travel_target() and not goat.is_navigating(), "An arrived save should remain quietly at pasture after binding" ) state.schedule_routine_at(manager.tick_count) manager.simulate_tick() _check( ( state.has_active_travel_target() and state.get_travel_target_site_id() == SimulationIds.ANIMAL_SITE_DUNJA_SHELTER and state.get_travel_target_position().is_equal_approx(shelter.get_routine_position()) ), "The next due routine should deterministically alternate back to shelter" ) state.set_hunger(AnimalStateRecord.FEED_THRESHOLD - AnimalStateRecord.HUNGER_PER_TICK) manager.simulate_tick() _check( not state.has_active_travel_target() and not goat.is_navigating() and state.needs_feed(), "Becoming hungry should stop routine travel so NPC and player feeding target a still goat" ) _finish() func _wait_for_navigation_map(navigation_map: RID) -> void: for _attempt in 30: if ( NavigationServer3D.map_get_iteration_id(navigation_map) > 0 and not NavigationServer3D.map_get_regions(navigation_map).is_empty() ): await physics_frame return await physics_frame _check(false, "Jajce navigation map did not synchronize within 30 physics frames") func _check(condition: bool, message: String) -> void: if not condition: failures.append(message) func _finish() -> void: if failures.is_empty(): print("[TEST] Animal pasture/shelter routine passed") quit(0) return for failure in failures: push_error("[TEST] " + failure) quit(1)