129 lines
4.6 KiB
GDScript
129 lines
4.6 KiB
GDScript
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
|
|
await physics_frame
|
|
|
|
var simulation_manager: Node = main_scene.get_node("SimulationManager")
|
|
simulation_manager.set_process(false)
|
|
|
|
_check(simulation_manager.npcs.size() == 6, "Baseline should create six NPCs")
|
|
_check(
|
|
main_scene.has_node("JajceWorld/TerrainRoot/Terrain3D"),
|
|
"Playable runtime should instance the Jajce Terrain3D world"
|
|
)
|
|
var terrain: Terrain3D = main_scene.get_node("JajceWorld/TerrainRoot/Terrain3D")
|
|
_check(
|
|
terrain.get_camera() == main_scene.get_node("CameraRig/Camera3D"),
|
|
"Runtime Terrain3D should bind to the gameplay camera"
|
|
)
|
|
_check(
|
|
(
|
|
not main_scene.has_node("World")
|
|
and not main_scene.has_node("ResourceNodes")
|
|
and not main_scene.has_node("ActivityMarkers")
|
|
and not main_scene.has_node("JajceWorld/LegacyActivityMarkers")
|
|
),
|
|
"Runtime should not retain duplicate flat-world objects"
|
|
)
|
|
var resource_root := main_scene.get_node("JajceWorld/WorldObjects/ResourceNodes")
|
|
_check(resource_root.get_child_count() == 12, "Runtime should contain twelve Jajce ResourceNodes")
|
|
_check(
|
|
simulation_manager.resource_states.size() == 12,
|
|
"Simulation authority should bind all twelve Jajce resources"
|
|
)
|
|
|
|
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 fixed_origins := [Vector3(-6.0, 0.0, -4.0), Vector3(0.0, 0.0, 0.0), Vector3(6.0, 0.0, 4.0)]
|
|
var destinations: Array[Vector3] = []
|
|
_check(
|
|
main_scene.has_node("JajceWorld/WorldObjects/StorageSites/VillagePantry"),
|
|
"Runtime should expose a typed VillagePantry StorageNode"
|
|
)
|
|
_check(
|
|
not main_scene.has_node("JajceWorld/LegacyActivityMarkers/PantryMarker"),
|
|
"Runtime should not keep the pantry as a legacy activity marker"
|
|
)
|
|
var pantry := main_scene.get_node("JajceWorld/WorldObjects/StorageSites/VillagePantry") as StorageNode
|
|
destinations.append(pantry.get_interaction_position())
|
|
var activity_root := main_scene.get_node("JajceWorld/WorldObjects/ActivitySites")
|
|
_check(activity_root.get_child_count() == 3, "Runtime should expose three typed activity sites")
|
|
for site in activity_root.get_children():
|
|
destinations.append((site as ActivitySite).get_interaction_position())
|
|
|
|
for resource in resource_root.get_children():
|
|
destinations.append((resource as ResourceNode).interaction_point.global_position)
|
|
|
|
for origin in fixed_origins:
|
|
for destination in destinations:
|
|
var path := NavigationServer3D.map_get_path(navigation_map, origin, destination, true)
|
|
_check(
|
|
not path.is_empty(),
|
|
"Navigation path should exist from %s to %s" % [origin, destination]
|
|
)
|
|
|
|
var village := SimVillage.new()
|
|
var worker := SimNPC.new(100, "BaselineWorker", SimulationIds.PROFESSION_SCHOLAR, 5.0, 5.0)
|
|
worker.set_task(SimulationIds.ACTION_STUDY, 2.0)
|
|
worker.start_working()
|
|
var executor := ActionExecutionSystem.new()
|
|
executor.advance_npc(worker, village)
|
|
executor.advance_npc(worker, village)
|
|
_check(worker.task_complete, "A two-tick working task should complete")
|
|
|
|
var starving := SimNPC.new(101, "BaselineStarving", SimulationIds.PROFESSION_WANDERER, 5.0, 5.0)
|
|
starving.hunger = 100.0
|
|
starving.starvation_death_threshold = 1
|
|
executor.advance_npc(starving, village)
|
|
_check(starving.is_dead, "Starvation threshold should still kill an NPC")
|
|
|
|
var adapter := main_scene.get_node("ActiveWorldAdapter") as ActiveWorldAdapter
|
|
var storage_target := adapter.get_activity_target(SimulationIds.ACTION_DEPOSIT_FOOD)
|
|
_check(
|
|
storage_target.get("target_id", "") == String(SimulationIds.STORAGE_VILLAGE_PANTRY),
|
|
"Runtime adapter should route storage actions to village_pantry"
|
|
)
|
|
var study_target := adapter.get_activity_target(SimulationIds.ACTION_STUDY)
|
|
_check(
|
|
study_target.get("target_id", "") == "study_desk",
|
|
"Runtime adapter should route study to the typed study desk"
|
|
)
|
|
|
|
if failures.is_empty():
|
|
print("[TEST] Jajce runtime passed: 6 NPCs, 12 resources, typed sites")
|
|
quit(0)
|
|
return
|
|
|
|
for failure in failures:
|
|
push_error("[TEST] " + failure)
|
|
quit(1)
|
|
|
|
|
|
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, "Navigation map did not synchronize within 30 physics frames")
|
|
|
|
|
|
func _check(condition: bool, message: String) -> void:
|
|
if not condition:
|
|
failures.append(message)
|