extends SceneTree var failures: Array[String] = [] func _initialize() -> void: call_deferred("_run") func _run() -> void: var camera := Camera3D.new() camera.current = true root.add_child(camera) var configured_assets: Terrain3DAssets = load("res://terrain/jajce/assets.tres") var configured_texture_count := configured_assets.get_texture_count() var world: Node3D = load("res://world/jajce/JajceWorld.tscn").instantiate() root.add_child(world) await process_frame for frame in 10: await physics_frame var terrain = world.get_node("TerrainRoot/Terrain3D") _check(terrain is Terrain3D, "JajceWorld should own a Terrain3D node") _check( terrain.data_directory == "res://terrain/jajce/data", "Terrain3D should use dedicated Jajce data" ) _check( configured_texture_count == 6, ( "Jajce terrain should define the bounded six-material palette (found %s)" % configured_texture_count ) ) var texture_contract_valid := true for texture_index in configured_texture_count: var texture_asset: Terrain3DTextureAsset = configured_assets.get_texture(texture_index) texture_contract_valid = ( texture_contract_valid and texture_asset.albedo_texture != null and texture_asset.normal_texture != null and texture_asset.albedo_texture.get_image().has_mipmaps() and texture_asset.normal_texture.get_image().has_mipmaps() ) _check( texture_contract_valid, "All Terrain3D texture assets should provide matching mipmapped maps" ) _check( absf(terrain.data.get_height(Vector3.ZERO)) < 0.1, "Central gameplay area should remain level for current navigation" ) _check( terrain.data.get_height(Vector3(-72, 0, 18)) > 10.0, "Authored terrain should raise the western fortress ridge" ) _check(world.has_node("WaterRoot"), "JajceWorld should expose WaterRoot") _check(world.has_node("VillageRoot"), "JajceWorld should expose VillageRoot") _check(world.has_node("FoliageRoot"), "JajceWorld should expose FoliageRoot") _check( world.get_node("FoliageRoot").get_child_count() >= 10, "JajceWorld should include restrained authored foliage clusters" ) _check(world.has_node("FortressBlockout"), "JajceWorld should include the fortress landmark") _check(world.has_node("WaterRoot/WaterfallBody"), "WaterRoot should include a waterfall drop") _check( world.has_node("WaterRoot/WaterfallMist"), "WaterRoot should include waterfall mist particles" ) var waterfall_mist: GPUParticles3D = world.get_node("WaterRoot/WaterfallMist") _check( waterfall_mist.draw_pass_1 != null, "Waterfall mist particles should have visible billboard geometry" ) _check(world.has_node("WaterRoot/RiverSurface"), "WaterRoot should include a river surface") var environment: Environment = ( (world.get_node("WorldEnvironment") as WorldEnvironment).environment ) _check( environment.fog_enabled and environment.sky != null, "Jajce environment should provide sky and restrained valley fog" ) _check( environment.glow_enabled and environment.volumetric_fog_enabled and environment.adjustment_enabled, "WorldEnvironment should provide bounded glow, depth haze, and color grading" ) _check( world.has_node("VillageRoot/Mill") and world.has_node("VillageRoot/Bridge"), "VillageRoot should include the mill and bridge blockouts" ) var smoke_count := 0 for suffix in ["House_01", "House_02", "House_03"]: if world.has_node("VillageRoot/%s/Smoke" % suffix): var smoke: GPUParticles3D = world.get_node("VillageRoot/%s/Smoke" % suffix) if smoke.draw_pass_1 == null: continue smoke_count += 1 _check( smoke_count >= 2, "At least two houses should have chimney smoke particles (found %s)" % smoke_count ) var lookdev: Node3D = load("res://world/jajce/JajceLookdev.tscn").instantiate() _check(lookdev.has_node("JajceWorld"), "Lookdev should instance JajceWorld") _check(lookdev.has_node("BeautyCamera"), "Lookdev should provide a beauty camera") lookdev.free() var resources := world.get_node("WorldObjects/ResourceNodes").get_children() var ids: Array[String] = [] for resource in resources: ids.append(String((resource as ResourceNode).node_id)) ids.sort() _check( ids == ["berry_bush_01", "berry_bush_02", "tree_01", "tree_02"], "Jajce resources should preserve all stable IDs" ) _check(world.has_node("WorldObjects/StorageSites"), "JajceWorld should expose StorageSites") var pantry := world.get_node("WorldObjects/StorageSites/VillagePantry") as StorageNode _check(pantry != null, "JajceWorld should include a typed VillagePantry StorageNode") _check( pantry.storage_id == SimulationIds.STORAGE_VILLAGE_PANTRY, "VillagePantry should preserve the stable storage ID" ) _check( not world.has_node("LegacyActivityMarkers/PantryMarker"), "Pantry should no longer be a legacy activity marker" ) var navigation_region := world.get_node("NavigationRegion3D") as NavigationRegion3D var navigation_map: RID = NavigationServer3D.region_get_map(navigation_region.get_rid()) _check(navigation_map.is_valid(), "Jajce navigation region should be assigned to a map") _check( navigation_region.navigation_mesh.get_polygon_count() > 0, "Jajce navigation mesh should contain baked polygons" ) await _wait_for_navigation_map(navigation_map) NavigationServer3D.map_force_update(navigation_map) var origins := [Vector3(-6, 0, -4), Vector3(0, 0, 0), Vector3(6, 0, 4)] var destinations: Array[Vector3] = [] for resource in resources: destinations.append((resource as ResourceNode).interaction_point.global_position) destinations.append(pantry.get_interaction_position()) for marker in world.get_node("LegacyActivityMarkers").get_children(): destinations.append((marker as Marker3D).global_position) for origin in origins: for destination in destinations: var path := NavigationServer3D.map_get_path(navigation_map, origin, destination, true) _check( not path.is_empty(), "Jajce navigation path should exist from %s to %s" % [origin, destination] ) var adapter := ActiveWorldAdapter.new() adapter.pantry_storage = pantry root.add_child(adapter) var manager: Node = load("res://simulation/SimulationManager.gd").new() manager.debug_logs = false manager.active_world_adapter = adapter root.add_child(manager) manager.set_process(false) await process_frame var npc: SimNPC = manager.npcs[0] npc.set_task(SimulationIds.ACTION_GATHER_FOOD) _check( manager.resolve_npc_target(npc.id, Vector3.ZERO), "Target resolver should find Jajce resources without a parent path" ) var selected := ResourceNode.get_by_id(npc.target_id) if selected != null: var selected_state: ResourceStateRecord = manager.get_resource_state(selected.node_id) _check( selected_state.get_reserved_by() == npc.id, "Selected Jajce resource should be authoritatively reserved" ) manager.release_resource(selected.node_id, npc.id) var storage_target := adapter.get_activity_target(SimulationIds.ACTION_WITHDRAW_FOOD) _check( storage_target.get("target_id", "") == String(SimulationIds.STORAGE_VILLAGE_PANTRY), "Storage actions should target the typed pantry's stable ID" ) var storage_target_position: Vector3 = storage_target.get("position", Vector3(9999, 9999, 9999)) _check( storage_target_position.distance_to(pantry.get_interaction_position()) < 0.01, "Storage actions should use the pantry StorageNode interaction point" ) if failures.is_empty(): print( "[TEST] Jajce scaffold passed: Terrain3D, storage site, shader water, building blockouts" ) 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, "Jajce navigation map did not synchronize within 30 physics frames") func _check(condition: bool, message: String) -> void: if not condition: failures.append(message)