NEON-29: Apply stashed WIP (main.tscn, movement validation, docs)

pull/41/head
VinPropane 2026-04-13 13:24:05 -04:00
parent 4a51e64f25
commit a7ad695143
10 changed files with 296 additions and 155 deletions

View File

@ -2,6 +2,9 @@
"plugins": {
"atlassian": {
"enabled": true
},
"linear": {
"enabled": true
}
}
}

View File

@ -48,7 +48,7 @@ Elevated walkables use **distinct albedo tints** in `scenes/main.tscn` so screen
### Designer / QA limits
- Floor bounds: **±22.5 m** in X and Z from world origin. Clicks outside the `NavigationRegion3D` boundary (beyond the floor edge) will be rejected by `NavigationAgent3D` path queries.
- The `MoveRejectPedestal` (orange box) top is ~2.5 m above the floor and will be rejected by the server's `MaxVerticalStep = 1.25 m` check — the only remaining move-rejection criterion. Horizontal distance is never a rejection reason.
- The `MoveRejectPedestal` (orange box) top is ~2.5 m above the floor and will be rejected by the server's `MaxVerticalStep` (**2.2 m** by default) — the only remaining move-rejection criterion with default server config. Horizontal distance is never a rejection reason when `HorizontalStepEnabled` is false.
- New obstacles are tagged `"occluder"` so the NEON-27 occlusion policy and NEON-30 click-through both apply automatically.
## Authoritative movement (NEO-7, NEO-11)
@ -71,7 +71,7 @@ With the game server running ([`server/README.md`](../server/README.md)), each v
2. If the port differs, set **`base_url`** on **`PositionAuthorityClient`** accordingly (e.g. `http://127.0.0.1:5253`).
3. Open the client in Godot and run the main scene (**F5**). The player should **snap** to the servers default position (e.g. **(-5, 0.9, -5)** per `Game:DefaultPosition` / NEO-9 walk demo).
4. **Left-click** the floor: the client **POST**s the target, then **GET**s position; the capsule **walks** in **XZ** straight toward that target (nav path only for **vertical** routing when you are already near the goal column on **flatter** floor). Server-rejected clicks show the reject label and do **not** start a path.
5. Click the **pedestal top** (orange box at ~(7.5, 0, 6.5)) to confirm **`vertical_step_exceeded`** rejection — the top is ~2.5 m above floor, which exceeds `MaxVerticalStep`. The `MoveRejectFarPad` (blue pad at (9, 9)) is a normal walkable surface; clicking it from any distance should succeed.
5. Click the **pedestal top** (orange box at ~(7.5, 0, 6.5)) to confirm **`vertical_step_exceeded`** rejection — the top is ~2.5 m above floor, which exceeds default **`MaxVerticalStep` (2.2 m)**. From the **physics ramp test block** top (**2 m**), clicking the floor should **succeed**. The `MoveRejectFarPad` (blue pad at (9, 9)) is a normal walkable surface; clicking it from any distance should succeed.
6. After a move (or at spawn), **stand idle** with no click goal for **10+ seconds** and confirm the capsule does **not** visibly vibrate or drift in **x/z** on flat floor, then repeat on the **random green bumps** (positions change each run).
If the server is **down**, boot **`GET`** fails silently (check Output for warnings); clicks while a request is in flight are ignored until it finishes.

View File

@ -67,8 +67,8 @@ dev_toggle_occluder_obstacle={
[physics]
common/physics_ticks_per_second=120
3d/default_gravity=9.81
3d/physics_engine="Jolt Physics"
3d/default_gravity=9.81
[rendering]

View File

@ -6,13 +6,11 @@
[ext_resource type="Script" uid="uid://ds5fkbscljkxi" path="res://scripts/position_authority_client.gd" id="4_auth"]
[ext_resource type="Script" uid="uid://bv0xprp660hib" path="res://scripts/interaction_request_client.gd" id="5_ix"]
[ext_resource type="Script" uid="uid://n3p4f3fky3nv" path="res://scripts/interaction_radius_indicators.gd" id="6_rad"]
[ext_resource type="Script" uid="uid://b8x7c4m3r5t8t2" path="res://scripts/isometric_follow_camera.gd" id="7_iso_cam"]
[ext_resource type="Script" uid="uid://caaj3ohikfdx2" path="res://scripts/isometric_follow_camera.gd" id="7_iso_cam"]
[ext_resource type="Resource" path="res://resources/isometric_zoom_bands.tres" id="8_zoom_bands"]
[ext_resource type="Resource" path="res://resources/isometric_occlusion_policy.tres" id="9_occ_pol"]
[sub_resource type="NavigationMesh" id="NavigationMesh_district"]
vertices = PackedVector3Array()
polygons = []
geometry_collision_mask = 1
cell_size = 0.15
cell_height = 0.15
@ -21,12 +19,12 @@ agent_height = 1.0
agent_radius = 0.4
agent_max_climb = 0.35
[sub_resource type="StandardMaterial3D" id="Mat_floor_slab"]
albedo_color = Color(0.5, 0.5, 0.52, 1)
[sub_resource type="BoxMesh" id="BoxMesh_floor"]
size = Vector3(45, 0.2, 45)
[sub_resource type="StandardMaterial3D" id="Mat_floor_slab"]
albedo_color = Color(0.5, 0.5, 0.52, 1)
[sub_resource type="BoxShape3D" id="BoxShape3D_floor"]
size = Vector3(45, 0.2, 45)
@ -63,24 +61,6 @@ size = Vector3(2, 2, 2)
[sub_resource type="BoxShape3D" id="BoxShape3D_obstacle"]
size = Vector3(2, 2, 2)
[sub_resource type="BoxMesh" id="BoxMesh_marker"]
size = Vector3(0.22, 0.35, 0.22)
[sub_resource type="StandardMaterial3D" id="Mat_marker_base"]
albedo_color = Color(0.15, 0.4, 1, 1)
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_player"]
radius = 0.4
height = 1.0
[sub_resource type="CapsuleMesh" id="CapsuleMesh_player"]
radius = 0.4
height = 1.0
[sub_resource type="StandardMaterial3D" id="Mat_player_capsule"]
specular_mode = 2
albedo_color = Color(0.42, 0.52, 0.7, 1)
[sub_resource type="BoxMesh" id="BoxMesh_obstacle_b"]
size = Vector3(3, 3, 3)
@ -99,101 +79,146 @@ size = Vector3(3, 2, 3)
[sub_resource type="BoxShape3D" id="BoxShape3D_obstacle_d"]
size = Vector3(3, 2, 3)
[sub_resource type="StandardMaterial3D" id="Mat_terrace_platform_a"]
albedo_color = Color(0.15, 0.62, 0.58, 1)
[sub_resource type="StandardMaterial3D" id="Mat_terrace_b_step"]
albedo_color = Color(0.96, 0.72, 0.15, 1)
[sub_resource type="StandardMaterial3D" id="Mat_terrace_platform_b"]
albedo_color = Color(0.58, 0.32, 0.72, 1)
[sub_resource type="StandardMaterial3D" id="Mat_terrace_platform_c"]
albedo_color = Color(0.22, 0.68, 0.38, 1)
[sub_resource type="BoxMesh" id="BoxMesh_terrace_a"]
size = Vector3(10, 0.3, 10)
[sub_resource type="StandardMaterial3D" id="Mat_terrace_platform_a"]
albedo_color = Color(0.15, 0.62, 0.58, 1)
[sub_resource type="BoxShape3D" id="BoxShape3D_terrace_a"]
size = Vector3(10, 0.3, 10)
[sub_resource type="BoxMesh" id="BoxMesh_pa_stair_ns"]
size = Vector3(9.2, 0.104, 1)
[sub_resource type="StandardMaterial3D" id="Mat_terrace_platform_a_ramp"]
albedo_color = Color(0.1, 0.48, 0.45, 1)
[sub_resource type="BoxShape3D" id="BoxShape3D_pa_stair_ns"]
size = Vector3(9.2, 0.104, 1)
[sub_resource type="BoxMesh" id="BoxMesh_pa_stair_we"]
size = Vector3(1, 0.104, 9.2)
[sub_resource type="BoxShape3D" id="BoxShape3D_pa_stair_we"]
size = Vector3(1, 0.104, 9.2)
[sub_resource type="BoxMesh" id="BoxMesh_terrace_b_step"]
size = Vector3(6, 0.3, 3)
[sub_resource type="StandardMaterial3D" id="Mat_terrace_b_step"]
albedo_color = Color(0.96, 0.72, 0.15, 1)
[sub_resource type="BoxShape3D" id="BoxShape3D_terrace_b_step"]
size = Vector3(6, 0.3, 3)
[sub_resource type="BoxMesh" id="BoxMesh_terrace_b_platform"]
size = Vector3(6, 0.6, 6)
[sub_resource type="StandardMaterial3D" id="Mat_terrace_platform_b"]
albedo_color = Color(0.58, 0.32, 0.72, 1)
[sub_resource type="BoxShape3D" id="BoxShape3D_terrace_b_platform"]
size = Vector3(6, 0.6, 6)
[sub_resource type="BoxMesh" id="BoxMesh_terrace_c"]
size = Vector3(8, 0.3, 8)
[sub_resource type="StandardMaterial3D" id="Mat_terrace_platform_c"]
albedo_color = Color(0.22, 0.68, 0.38, 1)
[sub_resource type="BoxShape3D" id="BoxShape3D_terrace_c"]
size = Vector3(8, 0.3, 8)
[sub_resource type="StandardMaterial3D" id="Mat_terrace_platform_a_ramp"]
albedo_color = Color(0.1, 0.48, 0.45, 1)
[sub_resource type="BoxMesh" id="BoxMesh_pa_stair_ns"]
size = Vector3(9.2, 0.104, 1.0)
[sub_resource type="BoxShape3D" id="BoxShape3D_pa_stair_ns"]
size = Vector3(9.2, 0.104, 1.0)
[sub_resource type="BoxMesh" id="BoxMesh_pa_stair_we"]
size = Vector3(1.0, 0.104, 9.2)
[sub_resource type="BoxShape3D" id="BoxShape3D_pa_stair_we"]
size = Vector3(1.0, 0.104, 9.2)
[sub_resource type="BoxMesh" id="BoxMesh_pc_stair_ns"]
size = Vector3(7.2, 0.104, 1)
[sub_resource type="StandardMaterial3D" id="Mat_terrace_platform_c_ramp"]
albedo_color = Color(0.12, 0.48, 0.22, 1)
[sub_resource type="BoxMesh" id="BoxMesh_pc_stair_ns"]
size = Vector3(7.2, 0.104, 1.0)
[sub_resource type="BoxShape3D" id="BoxShape3D_pc_stair_ns"]
size = Vector3(7.2, 0.104, 1.0)
size = Vector3(7.2, 0.104, 1)
[sub_resource type="BoxMesh" id="BoxMesh_pc_stair_we"]
size = Vector3(1.0, 0.104, 7.2)
size = Vector3(1, 0.104, 7.2)
[sub_resource type="BoxShape3D" id="BoxShape3D_pc_stair_we"]
size = Vector3(1.0, 0.104, 7.2)
size = Vector3(1, 0.104, 7.2)
[sub_resource type="BoxMesh" id="BoxMesh_tsb_stair_ns"]
size = Vector3(6.2, 0.104, 1)
[sub_resource type="StandardMaterial3D" id="Mat_terrace_b_step_ramp"]
albedo_color = Color(0.62, 0.45, 0.06, 1)
[sub_resource type="BoxMesh" id="BoxMesh_tsb_stair_ns"]
size = Vector3(6.2, 0.104, 1.0)
[sub_resource type="BoxShape3D" id="BoxShape3D_tsb_stair_ns"]
size = Vector3(6.2, 0.104, 1.0)
size = Vector3(6.2, 0.104, 1)
[sub_resource type="BoxMesh" id="BoxMesh_tsb_stair_we"]
size = Vector3(1.0, 0.104, 3.2)
size = Vector3(1, 0.104, 3.2)
[sub_resource type="BoxShape3D" id="BoxShape3D_tsb_stair_we"]
size = Vector3(1.0, 0.104, 3.2)
size = Vector3(1, 0.104, 3.2)
[sub_resource type="BoxMesh" id="BoxMesh_tpb_stair_ns"]
size = Vector3(6.2, 0.104, 1)
[sub_resource type="StandardMaterial3D" id="Mat_terrace_platform_b_ramp"]
albedo_color = Color(0.35, 0.18, 0.42, 1)
[sub_resource type="BoxMesh" id="BoxMesh_tpb_stair_ns"]
size = Vector3(6.2, 0.104, 1.0)
[sub_resource type="BoxShape3D" id="BoxShape3D_tpb_stair_ns"]
size = Vector3(6.2, 0.104, 1.0)
size = Vector3(6.2, 0.104, 1)
[sub_resource type="BoxMesh" id="BoxMesh_tpb_stair_we"]
size = Vector3(1.0, 0.104, 6.2)
size = Vector3(1, 0.104, 6.2)
[sub_resource type="BoxShape3D" id="BoxShape3D_tpb_stair_we"]
size = Vector3(1.0, 0.104, 6.2)
size = Vector3(1, 0.104, 6.2)
[sub_resource type="BoxMesh" id="BoxMesh_physics_ramp_test_floor"]
size = Vector3(24, 0.2, 45)
[sub_resource type="BoxShape3D" id="BoxShape3D_physics_ramp_test_floor"]
size = Vector3(24, 0.2, 45)
[sub_resource type="BoxMesh" id="BoxMesh_physics_ramp_test_block"]
size = Vector3(4, 2, 4)
[sub_resource type="StandardMaterial3D" id="Mat_physics_ramp_test"]
albedo_color = Color(0.95, 0.42, 0.38, 1)
[sub_resource type="BoxShape3D" id="BoxShape3D_physics_ramp_test_block"]
size = Vector3(4, 2, 4)
[sub_resource type="BoxMesh" id="BoxMesh_physics_ramp_test_walk"]
size = Vector3(4.5, 0.15, 6.5)
[sub_resource type="BoxShape3D" id="BoxShape3D_physics_ramp_test_walk"]
size = Vector3(4.5, 0.15, 6.5)
[sub_resource type="BoxMesh" id="BoxMesh_physics_ramp_test_steep"]
size = Vector3(2.5, 0.15, 4.5)
[sub_resource type="BoxShape3D" id="BoxShape3D_physics_ramp_test_steep"]
size = Vector3(2.5, 0.15, 4.5)
[sub_resource type="BoxMesh" id="BoxMesh_marker"]
size = Vector3(0.22, 0.35, 0.22)
[sub_resource type="StandardMaterial3D" id="Mat_marker_base"]
albedo_color = Color(0.15, 0.4, 1, 1)
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_player"]
radius = 0.4
height = 1.0
[sub_resource type="CapsuleMesh" id="CapsuleMesh_player"]
radius = 0.4
height = 1.0
[sub_resource type="StandardMaterial3D" id="Mat_player_capsule"]
specular_mode = 2
albedo_color = Color(0.42, 0.52, 0.7, 1)
[node name="Main" type="Node3D" unique_id=1358372723]
script = ExtResource("1_main")
@ -231,8 +256,8 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.1, 0)
shape = SubResource("BoxShape3D_floor")
[node name="PrototypeTerminal" type="StaticBody3D" parent="World/NavigationRegion3D" unique_id=1700001 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/PrototypeTerminal" unique_id=1700002]
mesh = SubResource("BoxMesh_terminal")
@ -242,8 +267,8 @@ surface_material_override/0 = SubResource("Mat_terminal")
shape = SubResource("BoxShape3D_terminal")
[node name="MoveRejectPedestal" type="StaticBody3D" parent="World/NavigationRegion3D" unique_id=1900021 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 7.5, 0, -6.5)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/MoveRejectPedestal" unique_id=1900022]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.25, 0)
@ -255,8 +280,8 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.25, 0)
shape = SubResource("BoxShape3D_move_reject_pedestal")
[node name="MoveRejectFarPad" type="StaticBody3D" parent="World/NavigationRegion3D" unique_id=1900031 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 9, 0.06, 9)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/MoveRejectFarPad" unique_id=1900032]
mesh = SubResource("BoxMesh_move_reject_farpad")
@ -266,8 +291,8 @@ surface_material_override/0 = SubResource("Mat_move_reject_farpad")
shape = SubResource("BoxShape3D_move_reject_farpad")
[node name="Obstacle" type="StaticBody3D" parent="World/NavigationRegion3D" unique_id=1638845763 groups=["occluder"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 6, 0, 5)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/Obstacle" unique_id=750473628]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
@ -278,8 +303,8 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
shape = SubResource("BoxShape3D_obstacle")
[node name="ObstacleB" type="StaticBody3D" parent="World/NavigationRegion3D" unique_id=3000001 groups=["occluder"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -10, 0, 5)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/ObstacleB" unique_id=3000002]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.5, 0)
@ -290,8 +315,8 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.5, 0)
shape = SubResource("BoxShape3D_obstacle_b")
[node name="ObstacleC" type="StaticBody3D" parent="World/NavigationRegion3D" unique_id=3000004 groups=["occluder"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 5, 0, -12)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/ObstacleC" unique_id=3000005]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2, 0)
@ -302,8 +327,8 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2, 0)
shape = SubResource("BoxShape3D_obstacle_c")
[node name="ObstacleD" type="StaticBody3D" parent="World/NavigationRegion3D" unique_id=3000007 groups=["occluder"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -8, 0, -16)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/ObstacleD" unique_id=3000008]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
@ -314,8 +339,8 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
shape = SubResource("BoxShape3D_obstacle_d")
[node name="TerracePlatformA" type="StaticBody3D" parent="World/NavigationRegion3D" unique_id=3000010 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 13, 0.012, -14)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformA" unique_id=3000011]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.15, 0)
@ -329,8 +354,8 @@ shape = SubResource("BoxShape3D_terrace_a")
[node name="TerracePlatformA_Approach" type="Node3D" parent="World/NavigationRegion3D" unique_id=3100000]
[node name="PAS_S1" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformA_Approach" unique_id=3100001 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 13, 0.052, -20.4)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformA_Approach/PAS_S1" unique_id=3100011]
mesh = SubResource("BoxMesh_pa_stair_ns")
@ -340,8 +365,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_a_ramp")
shape = SubResource("BoxShape3D_pa_stair_ns")
[node name="PAS_S2" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformA_Approach" unique_id=3100002 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 13, 0.156, -19.95)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformA_Approach/PAS_S2" unique_id=3100021]
mesh = SubResource("BoxMesh_pa_stair_ns")
@ -351,8 +376,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_a_ramp")
shape = SubResource("BoxShape3D_pa_stair_ns")
[node name="PAS_S3" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformA_Approach" unique_id=3100003 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 13, 0.26, -19.5)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformA_Approach/PAS_S3" unique_id=3100031]
mesh = SubResource("BoxMesh_pa_stair_ns")
@ -362,8 +387,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_a_ramp")
shape = SubResource("BoxShape3D_pa_stair_ns")
[node name="PAS_N1" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformA_Approach" unique_id=3100004 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 13, 0.052, -6.5)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformA_Approach/PAS_N1" unique_id=3100041]
mesh = SubResource("BoxMesh_pa_stair_ns")
@ -373,8 +398,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_a_ramp")
shape = SubResource("BoxShape3D_pa_stair_ns")
[node name="PAS_N2" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformA_Approach" unique_id=3100005 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 13, 0.156, -7.5)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformA_Approach/PAS_N2" unique_id=3100051]
mesh = SubResource("BoxMesh_pa_stair_ns")
@ -384,8 +409,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_a_ramp")
shape = SubResource("BoxShape3D_pa_stair_ns")
[node name="PAS_N3" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformA_Approach" unique_id=3100006 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 13, 0.26, -8.5)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformA_Approach/PAS_N3" unique_id=3100061]
mesh = SubResource("BoxMesh_pa_stair_ns")
@ -395,8 +420,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_a_ramp")
shape = SubResource("BoxShape3D_pa_stair_ns")
[node name="PAS_W1" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformA_Approach" unique_id=3100007 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 6.5, 0.052, -14)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformA_Approach/PAS_W1" unique_id=3100071]
mesh = SubResource("BoxMesh_pa_stair_we")
@ -406,8 +431,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_a_ramp")
shape = SubResource("BoxShape3D_pa_stair_we")
[node name="PAS_W2" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformA_Approach" unique_id=3100008 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 7.5, 0.156, -14)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformA_Approach/PAS_W2" unique_id=3100081]
mesh = SubResource("BoxMesh_pa_stair_we")
@ -417,8 +442,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_a_ramp")
shape = SubResource("BoxShape3D_pa_stair_we")
[node name="PAS_W3" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformA_Approach" unique_id=3100009 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 8.5, 0.26, -14)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformA_Approach/PAS_W3" unique_id=3100091]
mesh = SubResource("BoxMesh_pa_stair_we")
@ -428,8 +453,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_a_ramp")
shape = SubResource("BoxShape3D_pa_stair_we")
[node name="PAS_E1" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformA_Approach" unique_id=3100010 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 15.5, 0.052, -14)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformA_Approach/PAS_E1" unique_id=3100101]
mesh = SubResource("BoxMesh_pa_stair_we")
@ -439,8 +464,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_a_ramp")
shape = SubResource("BoxShape3D_pa_stair_we")
[node name="PAS_E2" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformA_Approach" unique_id=3100013 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 16.5, 0.156, -14)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformA_Approach/PAS_E2" unique_id=3100111]
mesh = SubResource("BoxMesh_pa_stair_we")
@ -450,8 +475,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_a_ramp")
shape = SubResource("BoxShape3D_pa_stair_we")
[node name="PAS_E3" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformA_Approach" unique_id=3100014 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 17.5, 0.26, -14)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformA_Approach/PAS_E3" unique_id=3100121]
mesh = SubResource("BoxMesh_pa_stair_we")
@ -461,8 +486,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_a_ramp")
shape = SubResource("BoxShape3D_pa_stair_we")
[node name="TerraceStepB" type="StaticBody3D" parent="World/NavigationRegion3D" unique_id=3000013 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -15, 0, 8.5)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerraceStepB" unique_id=3000014]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.15, 0)
@ -474,8 +499,8 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.15, 0)
shape = SubResource("BoxShape3D_terrace_b_step")
[node name="TerracePlatformB" type="StaticBody3D" parent="World/NavigationRegion3D" unique_id=3000016 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -15, 0, 13)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformB" unique_id=3000017]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.3, 0)
@ -487,8 +512,8 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.3, 0)
shape = SubResource("BoxShape3D_terrace_b_platform")
[node name="TerracePlatformC" type="StaticBody3D" parent="World/NavigationRegion3D" unique_id=3000019 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 14, 0, 14)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformC" unique_id=3000020]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.15, 0)
@ -502,8 +527,8 @@ shape = SubResource("BoxShape3D_terrace_c")
[node name="TerracePlatformC_Approach" type="Node3D" parent="World/NavigationRegion3D" unique_id=3150000]
[node name="PCS_S1" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformC_Approach" unique_id=3150001 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 14, 0.052, 8.6)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformC_Approach/PCS_S1" unique_id=3150011]
mesh = SubResource("BoxMesh_pc_stair_ns")
@ -513,8 +538,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_c_ramp")
shape = SubResource("BoxShape3D_pc_stair_ns")
[node name="PCS_S2" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformC_Approach" unique_id=3150002 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 14, 0.156, 9.05)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformC_Approach/PCS_S2" unique_id=3150021]
mesh = SubResource("BoxMesh_pc_stair_ns")
@ -524,8 +549,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_c_ramp")
shape = SubResource("BoxShape3D_pc_stair_ns")
[node name="PCS_S3" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformC_Approach" unique_id=3150003 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 14, 0.26, 9.5)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformC_Approach/PCS_S3" unique_id=3150031]
mesh = SubResource("BoxMesh_pc_stair_ns")
@ -535,8 +560,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_c_ramp")
shape = SubResource("BoxShape3D_pc_stair_ns")
[node name="PCS_N1" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformC_Approach" unique_id=3150004 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 14, 0.052, 19.4)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformC_Approach/PCS_N1" unique_id=3150041]
mesh = SubResource("BoxMesh_pc_stair_ns")
@ -546,8 +571,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_c_ramp")
shape = SubResource("BoxShape3D_pc_stair_ns")
[node name="PCS_N2" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformC_Approach" unique_id=3150005 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 14, 0.156, 18.95)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformC_Approach/PCS_N2" unique_id=3150051]
mesh = SubResource("BoxMesh_pc_stair_ns")
@ -557,8 +582,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_c_ramp")
shape = SubResource("BoxShape3D_pc_stair_ns")
[node name="PCS_N3" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformC_Approach" unique_id=3150006 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 14, 0.26, 18.5)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformC_Approach/PCS_N3" unique_id=3150061]
mesh = SubResource("BoxMesh_pc_stair_ns")
@ -568,8 +593,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_c_ramp")
shape = SubResource("BoxShape3D_pc_stair_ns")
[node name="PCS_W1" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformC_Approach" unique_id=3150007 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 8.5, 0.052, 14)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformC_Approach/PCS_W1" unique_id=3150071]
mesh = SubResource("BoxMesh_pc_stair_we")
@ -579,8 +604,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_c_ramp")
shape = SubResource("BoxShape3D_pc_stair_we")
[node name="PCS_W2" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformC_Approach" unique_id=3150008 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 8.95, 0.156, 14)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformC_Approach/PCS_W2" unique_id=3150081]
mesh = SubResource("BoxMesh_pc_stair_we")
@ -590,8 +615,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_c_ramp")
shape = SubResource("BoxShape3D_pc_stair_we")
[node name="PCS_W3" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformC_Approach" unique_id=3150009 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 9.5, 0.26, 14)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformC_Approach/PCS_W3" unique_id=3150091]
mesh = SubResource("BoxMesh_pc_stair_we")
@ -601,8 +626,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_c_ramp")
shape = SubResource("BoxShape3D_pc_stair_we")
[node name="PCS_E1" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformC_Approach" unique_id=3150010 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 19.4, 0.052, 14)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformC_Approach/PCS_E1" unique_id=3150101]
mesh = SubResource("BoxMesh_pc_stair_we")
@ -612,8 +637,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_c_ramp")
shape = SubResource("BoxShape3D_pc_stair_we")
[node name="PCS_E2" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformC_Approach" unique_id=3150013 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 18.95, 0.156, 14)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformC_Approach/PCS_E2" unique_id=3150111]
mesh = SubResource("BoxMesh_pc_stair_we")
@ -623,8 +648,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_c_ramp")
shape = SubResource("BoxShape3D_pc_stair_we")
[node name="PCS_E3" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformC_Approach" unique_id=3150014 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 18.5, 0.26, 14)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformC_Approach/PCS_E3" unique_id=3150121]
mesh = SubResource("BoxMesh_pc_stair_we")
@ -636,8 +661,8 @@ shape = SubResource("BoxShape3D_pc_stair_we")
[node name="TerraceStepB_Approach" type="Node3D" parent="World/NavigationRegion3D" unique_id=3160000]
[node name="TSB_S1" type="StaticBody3D" parent="World/NavigationRegion3D/TerraceStepB_Approach" unique_id=3160001 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -15, 0.052, 5.6)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerraceStepB_Approach/TSB_S1" unique_id=3160011]
mesh = SubResource("BoxMesh_tsb_stair_ns")
@ -647,8 +672,8 @@ surface_material_override/0 = SubResource("Mat_terrace_b_step_ramp")
shape = SubResource("BoxShape3D_tsb_stair_ns")
[node name="TSB_S2" type="StaticBody3D" parent="World/NavigationRegion3D/TerraceStepB_Approach" unique_id=3160002 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -15, 0.156, 6.05)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerraceStepB_Approach/TSB_S2" unique_id=3160021]
mesh = SubResource("BoxMesh_tsb_stair_ns")
@ -658,8 +683,8 @@ surface_material_override/0 = SubResource("Mat_terrace_b_step_ramp")
shape = SubResource("BoxShape3D_tsb_stair_ns")
[node name="TSB_S3" type="StaticBody3D" parent="World/NavigationRegion3D/TerraceStepB_Approach" unique_id=3160003 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -15, 0.26, 6.5)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerraceStepB_Approach/TSB_S3" unique_id=3160031]
mesh = SubResource("BoxMesh_tsb_stair_ns")
@ -669,8 +694,8 @@ surface_material_override/0 = SubResource("Mat_terrace_b_step_ramp")
shape = SubResource("BoxShape3D_tsb_stair_ns")
[node name="TSB_W1" type="StaticBody3D" parent="World/NavigationRegion3D/TerraceStepB_Approach" unique_id=3160004 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -19.5, 0.052, 8.5)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerraceStepB_Approach/TSB_W1" unique_id=3160041]
mesh = SubResource("BoxMesh_tsb_stair_we")
@ -680,8 +705,8 @@ surface_material_override/0 = SubResource("Mat_terrace_b_step_ramp")
shape = SubResource("BoxShape3D_tsb_stair_we")
[node name="TSB_W2" type="StaticBody3D" parent="World/NavigationRegion3D/TerraceStepB_Approach" unique_id=3160005 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -19.05, 0.156, 8.5)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerraceStepB_Approach/TSB_W2" unique_id=3160051]
mesh = SubResource("BoxMesh_tsb_stair_we")
@ -691,8 +716,8 @@ surface_material_override/0 = SubResource("Mat_terrace_b_step_ramp")
shape = SubResource("BoxShape3D_tsb_stair_we")
[node name="TSB_W3" type="StaticBody3D" parent="World/NavigationRegion3D/TerraceStepB_Approach" unique_id=3160006 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -18.5, 0.26, 8.5)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerraceStepB_Approach/TSB_W3" unique_id=3160061]
mesh = SubResource("BoxMesh_tsb_stair_we")
@ -702,8 +727,8 @@ surface_material_override/0 = SubResource("Mat_terrace_b_step_ramp")
shape = SubResource("BoxShape3D_tsb_stair_we")
[node name="TSB_E1" type="StaticBody3D" parent="World/NavigationRegion3D/TerraceStepB_Approach" unique_id=3160007 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -10.5, 0.052, 8.5)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerraceStepB_Approach/TSB_E1" unique_id=3160071]
mesh = SubResource("BoxMesh_tsb_stair_we")
@ -713,8 +738,8 @@ surface_material_override/0 = SubResource("Mat_terrace_b_step_ramp")
shape = SubResource("BoxShape3D_tsb_stair_we")
[node name="TSB_E2" type="StaticBody3D" parent="World/NavigationRegion3D/TerraceStepB_Approach" unique_id=3160008 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -11, 0.156, 8.5)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerraceStepB_Approach/TSB_E2" unique_id=3160081]
mesh = SubResource("BoxMesh_tsb_stair_we")
@ -724,8 +749,8 @@ surface_material_override/0 = SubResource("Mat_terrace_b_step_ramp")
shape = SubResource("BoxShape3D_tsb_stair_we")
[node name="TSB_E3" type="StaticBody3D" parent="World/NavigationRegion3D/TerraceStepB_Approach" unique_id=3160009 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -11.5, 0.26, 8.5)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerraceStepB_Approach/TSB_E3" unique_id=3160091]
mesh = SubResource("BoxMesh_tsb_stair_we")
@ -737,8 +762,8 @@ shape = SubResource("BoxShape3D_tsb_stair_we")
[node name="TerracePlatformB_Approach" type="Node3D" parent="World/NavigationRegion3D" unique_id=3170000]
[node name="TPB_N1" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach" unique_id=3170001 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -15, 0.052, 18.75)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach/TPB_N1" unique_id=3170011]
mesh = SubResource("BoxMesh_tpb_stair_ns")
@ -748,8 +773,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_b_ramp")
shape = SubResource("BoxShape3D_tpb_stair_ns")
[node name="TPB_N2" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach" unique_id=3170002 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -15, 0.156, 18.3)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach/TPB_N2" unique_id=3170021]
mesh = SubResource("BoxMesh_tpb_stair_ns")
@ -759,8 +784,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_b_ramp")
shape = SubResource("BoxShape3D_tpb_stair_ns")
[node name="TPB_N3" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach" unique_id=3170003 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -15, 0.26, 17.85)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach/TPB_N3" unique_id=3170031]
mesh = SubResource("BoxMesh_tpb_stair_ns")
@ -770,8 +795,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_b_ramp")
shape = SubResource("BoxShape3D_tpb_stair_ns")
[node name="TPB_N4" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach" unique_id=3170004 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -15, 0.364, 17.4)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach/TPB_N4" unique_id=3170041]
mesh = SubResource("BoxMesh_tpb_stair_ns")
@ -781,8 +806,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_b_ramp")
shape = SubResource("BoxShape3D_tpb_stair_ns")
[node name="TPB_N5" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach" unique_id=3170005 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -15, 0.468, 16.95)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach/TPB_N5" unique_id=3170051]
mesh = SubResource("BoxMesh_tpb_stair_ns")
@ -792,8 +817,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_b_ramp")
shape = SubResource("BoxShape3D_tpb_stair_ns")
[node name="TPB_N6" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach" unique_id=3170006 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -15, 0.572, 16.5)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach/TPB_N6" unique_id=3170061]
mesh = SubResource("BoxMesh_tpb_stair_ns")
@ -803,8 +828,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_b_ramp")
shape = SubResource("BoxShape3D_tpb_stair_ns")
[node name="TPB_W1" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach" unique_id=3170007 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -20.75, 0.052, 13)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach/TPB_W1" unique_id=3170071]
mesh = SubResource("BoxMesh_tpb_stair_we")
@ -814,8 +839,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_b_ramp")
shape = SubResource("BoxShape3D_tpb_stair_we")
[node name="TPB_W2" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach" unique_id=3170008 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -20.3, 0.156, 13)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach/TPB_W2" unique_id=3170081]
mesh = SubResource("BoxMesh_tpb_stair_we")
@ -825,8 +850,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_b_ramp")
shape = SubResource("BoxShape3D_tpb_stair_we")
[node name="TPB_W3" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach" unique_id=3170009 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -19.85, 0.26, 13)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach/TPB_W3" unique_id=3170091]
mesh = SubResource("BoxMesh_tpb_stair_we")
@ -836,8 +861,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_b_ramp")
shape = SubResource("BoxShape3D_tpb_stair_we")
[node name="TPB_W4" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach" unique_id=3170010 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -19.4, 0.364, 13)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach/TPB_W4" unique_id=3170101]
mesh = SubResource("BoxMesh_tpb_stair_we")
@ -847,8 +872,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_b_ramp")
shape = SubResource("BoxShape3D_tpb_stair_we")
[node name="TPB_W5" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach" unique_id=3170013 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -18.95, 0.468, 13)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach/TPB_W5" unique_id=3170111]
mesh = SubResource("BoxMesh_tpb_stair_we")
@ -858,8 +883,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_b_ramp")
shape = SubResource("BoxShape3D_tpb_stair_we")
[node name="TPB_W6" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach" unique_id=3170014 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -18.5, 0.572, 13)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach/TPB_W6" unique_id=3170121]
mesh = SubResource("BoxMesh_tpb_stair_we")
@ -869,8 +894,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_b_ramp")
shape = SubResource("BoxShape3D_tpb_stair_we")
[node name="TPB_E1" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach" unique_id=3170015 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -9.25, 0.052, 13)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach/TPB_E1" unique_id=3170131]
mesh = SubResource("BoxMesh_tpb_stair_we")
@ -880,8 +905,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_b_ramp")
shape = SubResource("BoxShape3D_tpb_stair_we")
[node name="TPB_E2" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach" unique_id=3170016 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -9.7, 0.156, 13)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach/TPB_E2" unique_id=3170141]
mesh = SubResource("BoxMesh_tpb_stair_we")
@ -891,8 +916,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_b_ramp")
shape = SubResource("BoxShape3D_tpb_stair_we")
[node name="TPB_E3" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach" unique_id=3170017 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -10.15, 0.26, 13)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach/TPB_E3" unique_id=3170151]
mesh = SubResource("BoxMesh_tpb_stair_we")
@ -902,8 +927,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_b_ramp")
shape = SubResource("BoxShape3D_tpb_stair_we")
[node name="TPB_E4" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach" unique_id=3170018 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -10.6, 0.364, 13)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach/TPB_E4" unique_id=3170161]
mesh = SubResource("BoxMesh_tpb_stair_we")
@ -913,8 +938,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_b_ramp")
shape = SubResource("BoxShape3D_tpb_stair_we")
[node name="TPB_E5" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach" unique_id=3170019 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -11.05, 0.468, 13)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach/TPB_E5" unique_id=3170171]
mesh = SubResource("BoxMesh_tpb_stair_we")
@ -924,8 +949,8 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_b_ramp")
shape = SubResource("BoxShape3D_tpb_stair_we")
[node name="TPB_E6" type="StaticBody3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach" unique_id=3170020 groups=["walkable"]]
collision_mask = 3
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -11.5, 0.572, 13)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach/TPB_E6" unique_id=3170181]
mesh = SubResource("BoxMesh_tpb_stair_we")
@ -934,6 +959,55 @@ surface_material_override/0 = SubResource("Mat_terrace_platform_b_ramp")
[node name="CollisionShape3D" type="CollisionShape3D" parent="World/NavigationRegion3D/TerracePlatformB_Approach/TPB_E6" unique_id=3170182]
shape = SubResource("BoxShape3D_tpb_stair_we")
[node name="PhysicsRampTestFloorExtension" type="StaticBody3D" parent="World/NavigationRegion3D" unique_id=4201001 groups=["walkable"]]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 34.5, 0, 0)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/PhysicsRampTestFloorExtension" unique_id=4201002]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.1, -0.19204712)
mesh = SubResource("BoxMesh_physics_ramp_test_floor")
surface_material_override/0 = SubResource("Mat_floor_slab")
[node name="CollisionShape3D" type="CollisionShape3D" parent="World/NavigationRegion3D/PhysicsRampTestFloorExtension" unique_id=4201003]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.1, 0)
shape = SubResource("BoxShape3D_physics_ramp_test_floor")
[node name="PhysicsRampTestBlock" type="StaticBody3D" parent="World/NavigationRegion3D" unique_id=4201010 groups=["occluder"]]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 40, 0, 0)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/PhysicsRampTestBlock" unique_id=4201011]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
mesh = SubResource("BoxMesh_physics_ramp_test_block")
surface_material_override/0 = SubResource("Mat_physics_ramp_test")
[node name="CollisionShape3D" type="CollisionShape3D" parent="World/NavigationRegion3D/PhysicsRampTestBlock" unique_id=4201012]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
shape = SubResource("BoxShape3D_physics_ramp_test_block")
[node name="PhysicsRampTestWalkRamp" type="StaticBody3D" parent="World/NavigationRegion3D" unique_id=4201020 groups=["walkable"]]
transform = Transform3D(1, 0, 0, 0, 0.948683, -0.316228, 0, 0.316228, 0.948683, 40, 1, 5)
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/PhysicsRampTestWalkRamp" unique_id=4201021]
mesh = SubResource("BoxMesh_physics_ramp_test_walk")
surface_material_override/0 = SubResource("Mat_physics_ramp_test")
[node name="CollisionShape3D" type="CollisionShape3D" parent="World/NavigationRegion3D/PhysicsRampTestWalkRamp" unique_id=4201022]
shape = SubResource("BoxShape3D_physics_ramp_test_walk")
[node name="PhysicsRampTestSteepRamp" type="StaticBody3D" parent="World" unique_id=4201030]
transform = Transform3D(-0.6, -0.8, 0, 0.8, 0.6, 0, 0, 0, 1, 42.75, 1, 0)
collision_layer = 1
collision_mask = 3
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/PhysicsRampTestSteepRamp" unique_id=4201031]
mesh = SubResource("BoxMesh_physics_ramp_test_steep")
surface_material_override/0 = SubResource("Mat_physics_ramp_test")
[node name="CollisionShape3D" type="CollisionShape3D" parent="World/PhysicsRampTestSteepRamp" unique_id=4201032]
shape = SubResource("BoxShape3D_physics_ramp_test_steep")
[node name="InteractionMarkers" type="Node3D" parent="World" unique_id=1700003]
script = ExtResource("6_rad")
@ -994,12 +1068,6 @@ grow_horizontal = 2
autowrap_mode = 3
[node name="PlayerPositionLabel" type="Label" parent="UICanvas" unique_id=9000003]
layout_mode = 0
anchors_preset = 1
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
offset_left = 8.0
offset_top = 8.0
offset_right = 220.0
@ -1010,4 +1078,6 @@ theme_override_colors/font_color = Color(0.92, 0.95, 0.98, 1)
theme_override_colors/font_outline_color = Color(0.08, 0.08, 0.1, 1)
theme_override_constants/outline_size = 6
theme_override_font_sizes/font_size = 15
text = "x: —\ny: —\nz: —"
text = "x: —
y: —
z: —"

View File

@ -1,6 +1,8 @@
extends Node3D
## NS-16: walkable ground ray pick. Emits world target for MoveCommand; camera wired from main.
## Flat tops on `occluder` bodies (horizontal normal) are valid destinations; vertical occluder
## faces still punch through so ground behind can be picked.
## NS-19: reject vertical faces (pedestal). Stepped ray: steep walkable hits (bump slopes) advance
## along the ray so the next hit can be flat floor beyond — fixes “stuck” when leaving plateau.
## (No `class_name` so headless/CI can load the project before `.godot` import exists.)
@ -64,6 +66,17 @@ func _try_pick(screen_pos: Vector2) -> void:
break
var hit_collider: Variant = hit.get("collider")
if _collider_is_occluder(hit_collider):
var hit_normal_o: Variant = hit.get("normal", Vector3.ZERO)
if hit_normal_o is not Vector3:
break
var nrm_o: Vector3 = (hit_normal_o as Vector3).normalized()
var ndot_o: float = nrm_o.dot(Vector3.UP)
if ndot_o >= MIN_WALKABLE_NORMAL_DOT_UP:
var top_pos: Variant = hit.get("position")
if top_pos is Vector3:
chosen = top_pos as Vector3
have_chosen = true
break
var occluder_pos: Variant = hit.get("position")
if occluder_pos is not Vector3:
break

View File

@ -7,6 +7,10 @@ extends Node3D
## (see `isometric_follow_camera.gd`).
## Prototype: two random short bumps (sibling StaticBody3D under NavigationRegion3D; see
## `random_floor_bumps.gd`) before nav bake.
## Prototype: `PhysicsRampTest*` (+X floor extension, block, gentle ramp on **+Z**, steep on **+X**).
## Steep ramp: **2 m / 1.5 m** rise/run (~53°) so slope exceeds `Player` `floor_max_angle` (~50°) and is not
## walkable; gentle ramp stays shallower than `NavigationMesh.agent_max_climb`. Steep is under `World`
## only (not nav source geometry).
## Prototype HUD: world `CharacterBody3D` position in `UICanvas/PlayerPositionLabel`
## (updated in `_physics_process` so it matches physics ticks).

View File

@ -53,6 +53,10 @@ const WALK_PEEL_PROBE_FALSE_FRAMES: int = 2
## already inside [member ARRIVE_EPS] → never [code]has_goal=false[/code], full [member MOVE_SPEED] path
## steer flips forever (looks like “idle” jitter; NEON-16 traces).
const WALK_COLUMN_NEAR_ARRIVE_VERT: float = 0.36
## Only apply [member WALK_COLUMN_NEAR_ARRIVE_VERT] when vertical error is already in this band — avoids
## treating a **plaza / block** drop (feet still >20 cm above goal) as “column stair wobble” while
## [code]horiz_dist[/code] is inside column steering (NEON-29-style skate + slow float after XZ).
const WALK_COLUMN_NEAR_ARRIVE_GATE_VERT_ERR: float = 0.22
## If [code]vlat && ncol[/code] and net displacement stays below this for [member WALK_COLUMN_STUCK_FRAMES],
## clear the walk goal — path lookahead limit cycle with no progress (backup if widened arrival is not enough).
const WALK_COLUMN_STUCK_FRAMES: int = 28
@ -70,6 +74,9 @@ const FLOOR_SNAP_MOVING: float = 0.32
const FLOOR_SNAP_IDLE: float = 0.11
## Below this floor-normal dot up, idle tick uses **moving** `floor_max_angle` (rim).
const IDLE_RIM_MIN_FLOOR_UP_DOT: float = 0.968
## Uniform walkable slopes (e.g. physics QA ramp ~18°): stable idle when not tread+riser ridged.
## Below ~cos(28°): treat as too steep for this fast-path (leave corrective / rim logic).
const IDLE_SLOPE_STABLE_MIN_UP_DOT: float = 0.88
## Stable flat idle support: skip corrective idle motion when support is effectively level.
## 0.998 (~3.6°): 0.999 still saw open-floor HUD drift — Jolt [code]get_floor_normal()[/code] often
## reports ~0.99910.9992 on a flat box, so the old 0.999 gate rarely latched the idle anchor and
@ -112,15 +119,25 @@ const WALK_SUPPORT_PROBE_MIN_UP_DOT: float = 0.42
## on violet stairs. Must stay **below** ~0.25 m so a gold→floor (~0.3 m) void still rejects the slab hit
## (no horizontal skate off the deck).
const WALK_CONTINUATION_MAX_BELOW_FEET: float = 0.108
## If capsule feet are farther above the clicks surface Y than this, always use walk gravity + zero
## moving snap (rays can still read “support” while [method CharacterBody3D.is_on_floor] hugs a lip).
## ~0.42 m: **TerracePlatformB** top (~0.6 m) → floor (0) qualifies; **TerraceStepB** (~0.3 m) → floor does
## not — shallow drops still rely on [method _walk_has_close_floor_probe_below].
const WALK_DEEP_DESCENT_FEET_ABOVE_GOAL: float = 0.42
## If capsule feet are farther above the clicks surface Y than this, always arm ledge peel (bypass
## probe streak). Keep this **just above** a single QA tread drop (~0.104 m) so stair treads still use
## [method _walk_has_close_floor_probe_below]; use a **small** value so tall drops (block ~2 m, terrace
## ~0.6 m) never exit this “deep” band until feet are almost on the goal surface — otherwise the last
## ~40 cm reverted to probe-only, lip [code]is_on_floor()[/code] + full snap could **horizontal skate**
## with almost no peel until XZ arrival.
const WALK_DEEP_DESCENT_FEET_ABOVE_GOAL: float = 0.12
## [method get_gravity] can be ~0 with missing/odd project defaults; ledge peel + airborne walk still need a pull.
const WALK_FALLBACK_GRAVITY_Y: float = -9.81
## Multiplier on project gravity for walk ledge peel, walk-airborne, and idle-airborne (arcade descent).
const PLAYER_VERTICAL_ACCEL_MULTIPLIER: float = 9.0
## Scale [method _apply_walk_post_slide_ledge_peel] integration when feet are still far above a **lower**
## goal (block / terrace lip). Keeps peel sub-linear time from feeling like a hover; tread-sized drops stay ~1×.
const WALK_LEDGE_PEEL_BOOST_SEP_1: float = 0.28
const WALK_LEDGE_PEEL_BOOST_SEP_2: float = 0.75
const WALK_LEDGE_PEEL_BOOST_SEP_3: float = 1.35
const WALK_LEDGE_PEEL_BOOST_MUL_1: float = 1.65
const WALK_LEDGE_PEEL_BOOST_MUL_2: float = 2.75
const WALK_LEDGE_PEEL_BOOST_MUL_3: float = 4.25
## CapsuleShape3D in scene: height = 1.0 (cylinder portion), radius = 0.4.
## Total half-height from body origin to physical bottom = 0.5 + 0.4 = 0.9
## (`CAPSULE_HALF_HEIGHT + PLAYER_CAPSULE_RADIUS`).
@ -598,7 +615,15 @@ func _apply_walk_post_slide_ledge_peel(
_walk_ledge_peel_vy = 0.0
return
# Debounce was computed pre-slide; do not re-probe here (post-slide rays re-flicker on treads).
_walk_ledge_peel_vy += _player_vertical_accel(delta).y * delta
var sep_below_feet: float = feet_y - _auth_walk_goal.y
var peel_boost: float = 1.0
if sep_below_feet > WALK_LEDGE_PEEL_BOOST_SEP_3:
peel_boost = WALK_LEDGE_PEEL_BOOST_MUL_3
elif sep_below_feet > WALK_LEDGE_PEEL_BOOST_SEP_2:
peel_boost = WALK_LEDGE_PEEL_BOOST_MUL_2
elif sep_below_feet > WALK_LEDGE_PEEL_BOOST_SEP_1:
peel_boost = WALK_LEDGE_PEEL_BOOST_MUL_1
_walk_ledge_peel_vy += _player_vertical_accel(delta).y * delta * peel_boost
var dy: float = _walk_ledge_peel_vy * delta
if absf(dy) > 1e-9:
# Raw [code]global_position[/code] edits can be ignored/overwritten vs Jolt sync after [method move_and_slide].
@ -765,6 +790,15 @@ static func idle_support_is_stable(
if not on_floor:
return false
var ridged: bool = idle_slide_contacts_are_ridged(slide_normals)
var up_dot: float = floor_normal.dot(Vector3.UP)
# Single-surface inclines (walk ramps): flatter than [member STABLE_IDLE_FLOOR_MIN_UP_DOT] but not
# stair lip geometry — latch stable idle instead of endless corrective [method _physics_idle_tick].
if (
not ridged
and up_dot >= IDLE_SLOPE_STABLE_MIN_UP_DOT
and up_dot < STABLE_IDLE_FLOOR_MIN_UP_DOT
):
return true
# Entering stable idle uses [member STABLE_IDLE_FLOOR_MIN_UP_DOT] (0.998). On stair treads,
# [code]get_floor_normal()[/code] often sits ~0.9960.997 with a riser contact — never latches,
# so idle stays in corrective [method _maybe_idle_rim_settle_nudge] + [method _physics_idle_tick]
@ -772,7 +806,7 @@ static func idle_support_is_stable(
var required_up: float = min_flat_up_dot
if ridged and min_flat_up_dot >= STABLE_IDLE_FLOOR_MIN_UP_DOT - 1e-6:
required_up = STABLE_IDLE_FLOOR_HOLD_MIN_UP_DOT
if floor_normal.dot(Vector3.UP) < required_up:
if up_dot < required_up:
return false
# Post-stop **ridged** slides (tread + riser) used to force unstable idle for the entire
# `loose_ticks` window even when the **floor** is level — e.g. idle on a flat approach tread
@ -782,7 +816,7 @@ static func idle_support_is_stable(
if (
loose_ticks > 0
and ridged
and floor_normal.dot(Vector3.UP) < IDLE_RIM_MIN_FLOOR_UP_DOT
and up_dot < IDLE_RIM_MIN_FLOOR_UP_DOT
):
return false
return true
@ -915,7 +949,12 @@ func _maybe_idle_rim_settle_nudge() -> bool:
var shallow_floor: bool = (
is_on_floor() and get_floor_normal().dot(Vector3.UP) < IDLE_RIM_MIN_FLOOR_UP_DOT
)
if not shallow_floor and not _idle_ridged_floor_contacts():
var ridged: bool = _idle_ridged_floor_contacts()
# Uniform walkable ramps (one dominant sloped floor, no tread+riser pair) still read as “shallow”
# vs [member IDLE_RIM_MIN_FLOOR_UP_DOT] — horizontal rim nudges slide along the ramp every tick → jitter.
if shallow_floor and not ridged:
return false
if not shallow_floor and not ridged:
return false
var h := Vector3.ZERO
if shallow_floor:
@ -1117,7 +1156,12 @@ func _physics_process(delta: float) -> void:
_auth_walk_goal.y, global_position.y, CAPSULE_HALF_HEIGHT + PLAYER_CAPSULE_RADIUS
)
var vert_ok: float = VERT_ARRIVE_EPS
if is_on_floor() and _walk_vert_route_latched and _walk_nav_column_steering:
if (
is_on_floor()
and _walk_vert_route_latched
and _walk_nav_column_steering
and vert_err < WALK_COLUMN_NEAR_ARRIVE_GATE_VERT_ERR
):
vert_ok = maxf(VERT_ARRIVE_EPS, WALK_COLUMN_NEAR_ARRIVE_VERT)
if horiz_dist <= ARRIVE_EPS and vert_err <= vert_ok:
velocity = Vector3.ZERO
@ -1171,7 +1215,14 @@ func _physics_process(delta: float) -> void:
# path only when vertical routing is latched **and** column hysteresis says so **and** we are
# on floor — mid-air column steering flips XZ velocity each tick against `move_and_slide`
# (NEON-16: `on_floor=false` + `vlat && ncol` while falling).
if needs_vertical_routing and _walk_nav_column_steering and is_on_floor():
# On **shallow ramps** (dot below [member IDLE_RIM_MIN_FLOOR_UP_DOT]), path waypoints sit on a
# narrow ribbon — lookahead flips 180° each tick → violent oscillation (debug: alternating ±vel).
if (
needs_vertical_routing
and _walk_nav_column_steering
and is_on_floor()
and get_floor_normal().dot(Vector3.UP) >= IDLE_RIM_MIN_FLOOR_UP_DOT
):
_set_horizontal_velocity_from_nav_path_or_goal(want_goal_h)
else:
_set_horizontal_velocity_toward(_auth_walk_goal, want_goal_h)

View File

@ -11,7 +11,7 @@ public sealed class MovementValidationOptions
public double MaxHorizontalStep { get; set; } = 18.0;
/// <summary>Max absolute delta on Y in one move (meters). Inclusive at the limit.</summary>
public double MaxVerticalStep { get; set; } = 1.25;
public double MaxVerticalStep { get; set; } = 2.2;
/// <summary>When true, target must lie inside the axis-aligned district box (inclusive min/max).</summary>
public bool DistrictBoundsEnabled { get; set; }

View File

@ -16,7 +16,7 @@
"MovementValidation": {
"HorizontalStepEnabled": false,
"MaxHorizontalStep": 18,
"MaxVerticalStep": 1.25,
"MaxVerticalStep": 2.2,
"DistrictBoundsEnabled": false
}
}

View File

@ -70,11 +70,11 @@ Unknown player ids return **404**. Full zone sync / replication is still out of
| Config key | Meaning | Default (see `appsettings.json`) |
|------------|---------|-----------------------------------|
| `Game:MovementValidation:MaxHorizontalStep` | Max XZ displacement per command (m); inclusive at limit | `18` |
| `Game:MovementValidation:MaxVerticalStep` | Max absolute Y delta per command (m); inclusive | `1.25` |
| `Game:MovementValidation:MaxVerticalStep` | Max absolute Y delta per command (m); inclusive | `2.2` |
| `Game:MovementValidation:DistrictBoundsEnabled` | Axis-aligned box on **target** (inclusive min/max) | `false` |
| `Game:MovementValidation:DistrictMinX``DistrictMaxZ` | District extents when enabled | ±12 / Y 2…24 |
**Manual QA (Godot `main.tscn`):** **Green bumps** are **two random short cylinders** each run (~**814 cm** rise) and stay under default `MaxVerticalStep` from the dev spawn; **orange reject pedestal** top is ~2.5 m above floor so a floor-level click yields **`vertical_step_exceeded`**; **far pad** is beyond default horizontal reach for **`horizontal_step_exceeded`**. The clients **`ground_pick.gd`** only accepts ray hits whose surface **normal** is mostly **upward** (`MIN_WALKABLE_NORMAL_DOT_UP`), so **vertical faces** on the pedestal do not emit move targets—this complements server per-step limits and prevents “stair-stepping” up walls.
**Manual QA (Godot `main.tscn`):** **Green bumps** are **two random short cylinders** each run (~**814 cm** rise) and stay under default `MaxVerticalStep` from the dev spawn; **orange reject pedestal** top is ~**2.5 m** above floor so a floor-level click still yields **`vertical_step_exceeded`** (default `MaxVerticalStep` is **2.2 m**); the **physics ramp test block** top is **2 m** so clicking the floor from it is **accepted**. **Far pad** is beyond default horizontal reach for **`horizontal_step_exceeded`** when horizontal limits are enabled. The clients **`ground_pick.gd`** only accepts ray hits whose surface **normal** is mostly **upward** (`MIN_WALKABLE_NORMAL_DOT_UP`), so **vertical faces** on the pedestal do not emit move targets—this complements server per-step limits and prevents “stair-stepping” up walls.
Request body (example):