diff --git a/docs/BUILD_IN_PUBLIC_PLAN.md b/docs/BUILD_IN_PUBLIC_PLAN.md index 307c9b5..c4d3c3a 100644 --- a/docs/BUILD_IN_PUBLIC_PLAN.md +++ b/docs/BUILD_IN_PUBLIC_PLAN.md @@ -623,11 +623,16 @@ Completed: 6. `JajceWorld` runtime integration with bindings and regression coverage. 7. Bounded Jajce beauty baseline with six terrain layers, water, atmosphere, foliage motion, building blockouts, and smoke. +8. Base Terrain3D collision/navigation guardrails: runtime tests now keep + Terrain3D collision enabled, keep the old greybox ground out of physics, and + verify required navigation paths track the authored height field. Next: -1. Capture and visually review “Jajce Lookdev 01.” -2. Add the cinematic/debug toggle and repeatable demo reset. +1. Continue replacing the temporary greybox navigation source with baked + Terrain3D-derived navigation for the sculpted playable loop. + +2. Capture and visually review “Jajce Lookdev 01.” 3. Capture “Simulation Garden 01.” Do not start with GIS data, a full city, a large asset pack, or more NPC diff --git a/docs/LEARNING_ROADMAP.md b/docs/LEARNING_ROADMAP.md index d6dae2e..a82bcfd 100644 --- a/docs/LEARNING_ROADMAP.md +++ b/docs/LEARNING_ROADMAP.md @@ -670,6 +670,10 @@ Recently completed: - Activity-site capacity enforcement for rest, study, and patrol target resolution, derived from NPC target claims rather than presentation-only counters. +- Terrain3D collision/navigation hardening began: scaffold and runtime tests + now assert Terrain3D collision remains enabled, the hidden greybox ground no + longer provides physics collision, and tested navigation routes stay close to + the authored Terrain3D height field. This order strengthens the simulation while regularly producing visible progress suitable for public development updates. diff --git a/docs/PROJECT_CONTEXT.md b/docs/PROJECT_CONTEXT.md index 11b58f0..2552a53 100644 --- a/docs/PROJECT_CONTEXT.md +++ b/docs/PROJECT_CONTEXT.md @@ -193,7 +193,8 @@ study, and patrol target typed activity sites. The migration is documented in - **Main scene:** `res://main.tscn` - **Terrain:** Terrain3D 1.0.2 is installed and enabled - **Jajce runtime:** reusable 512 m Terrain3D seed, greybox landmarks, stable - ResourceNode placement, lookdev camera, and tested runtime navigation + ResourceNode placement, lookdev camera, tested runtime navigation, and base + Terrain3D collision/navigation guardrails - **Terrain compatibility floor:** Godot 4.4 according to the bundled extension - **Version control:** Git - **Primary branch:** `main` @@ -328,6 +329,9 @@ distance with resource `safety_risk`, `comfort_distance`, and - eight ResourceNodes preserving stable food/wood discovery IDs; - typed village pantry storage and typed guard, study, and rest activity sites; - a temporary baked navigation loop with 24 tested routes; +- base collision/navigation guardrails that keep Terrain3D collision enabled, + keep the hidden greybox ground out of runtime physics, and compare required + navigation paths against the Terrain3D height field; - a look-development scene and camera. This is a runtime integration proof, not the final terrain composition. @@ -534,6 +538,9 @@ These are expected prototype constraints, not necessarily isolated bugs: mutation APIs, but selection, execution, target resolution, and active-world queries now have focused collaborators. - Path failure and interruption emit a `navigation_failed` signal and send the NPC to wander; this is functional but not yet polished. +- The old greybox navigation source remains temporary; current tests now guard + against collision/nav regressions, but the next terrain base phase is a + Terrain3D-derived bake for the sculpted playable loop. - Terrain3D and the bounded Jajce beauty baseline now run in the main game scene; final visual review and character/profession readability remain. - Combat, companions, factions, politics, trade, rumours, quests, persistence, diff --git a/tests/jajce_runtime_integration_test.gd b/tests/jajce_runtime_integration_test.gd index 5c6070e..e9b0e74 100644 --- a/tests/jajce_runtime_integration_test.gd +++ b/tests/jajce_runtime_integration_test.gd @@ -26,6 +26,12 @@ func _run() -> void: terrain.get_camera() == main_scene.get_node("CameraRig/Camera3D"), "Runtime Terrain3D should bind to the gameplay camera" ) + _check(terrain.collision_mode != 0, "Runtime Terrain3D collision should be enabled") + var greybox_ground := main_scene.get_node("JajceWorld/NavigationRegion3D/GreyboxGround") as StaticBody3D + _check( + greybox_ground.collision_layer == 0 and greybox_ground.collision_mask == 0, + "Runtime GreyboxGround should not provide physics collision" + ) _check( ( not main_scene.has_node("World") @@ -73,6 +79,9 @@ func _run() -> void: not path.is_empty(), "Navigation path should exist from %s to %s" % [origin, destination] ) + _check_path_tracks_terrain( + terrain, path, "Runtime navigation path should track Terrain3D height" + ) var village := SimVillage.new() var worker := SimNPC.new(100, "BaselineWorker", SimulationIds.PROFESSION_SCHOLAR, 5.0, 5.0) @@ -126,3 +135,20 @@ func _wait_for_navigation_map(navigation_map: RID) -> void: func _check(condition: bool, message: String) -> void: if not condition: failures.append(message) + + +func _check_path_tracks_terrain(terrain: Terrain3D, path: PackedVector3Array, message: String) -> void: + if path.is_empty(): + return + + for point in path: + var terrain_height: float = terrain.data.get_height(point) + if is_nan(terrain_height): + failures.append("%s: missing terrain height at %s" % [message, point]) + return + if absf(point.y - terrain_height) > 0.85: + failures.append( + "%s: path point %s is too far from terrain height %.2f" + % [message, point, terrain_height] + ) + return diff --git a/tests/jajce_world_scaffold_test.gd b/tests/jajce_world_scaffold_test.gd index d305f00..cdf5009 100644 --- a/tests/jajce_world_scaffold_test.gd +++ b/tests/jajce_world_scaffold_test.gd @@ -26,6 +26,12 @@ func _run() -> void: terrain.data_directory == "res://terrain/jajce/data", "Terrain3D should use dedicated Jajce data" ) + _check(terrain.collision_mode != 0, "Terrain3D collision should be enabled for gameplay") + var greybox_ground := world.get_node("NavigationRegion3D/GreyboxGround") as StaticBody3D + _check( + greybox_ground.collision_layer == 0 and greybox_ground.collision_mask == 0, + "GreyboxGround should not provide runtime physics collision" + ) _check( configured_texture_count == 6, ( @@ -215,6 +221,9 @@ func _run() -> void: not path.is_empty(), "Jajce navigation path should exist from %s to %s" % [origin, destination] ) + _check_path_tracks_terrain( + terrain, path, "Jajce navigation path should track Terrain3D height" + ) var adapter := ActiveWorldAdapter.new() adapter.pantry_storage = pantry @@ -304,3 +313,20 @@ func _wait_for_navigation_map(navigation_map: RID) -> void: func _check(condition: bool, message: String) -> void: if not condition: failures.append(message) + + +func _check_path_tracks_terrain(terrain: Terrain3D, path: PackedVector3Array, message: String) -> void: + if path.is_empty(): + return + + for point in path: + var terrain_height: float = terrain.data.get_height(point) + if is_nan(terrain_height): + failures.append("%s: missing terrain height at %s" % [message, point]) + return + if absf(point.y - terrain_height) > 0.85: + failures.append( + "%s: path point %s is too far from terrain height %.2f" + % [message, point, terrain_height] + ) + return