test: harden jajce terrain navigation

This commit is contained in:
2026-07-08 15:47:01 +02:00
parent d2eca15ecd
commit cc0d6ef548
5 changed files with 71 additions and 3 deletions
+7 -2
View File
@@ -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
+4
View File
@@ -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.
+8 -1
View File
@@ -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,
+26
View File
@@ -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
+26
View File
@@ -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