diff --git a/.cursor/settings.json b/.cursor/settings.json index 32c28fc..fc38bf8 100644 --- a/.cursor/settings.json +++ b/.cursor/settings.json @@ -2,6 +2,9 @@ "plugins": { "atlassian": { "enabled": true + }, + "linear": { + "enabled": true } } } diff --git a/client/README.md b/client/README.md index 0493964..abfd7a0 100644 --- a/client/README.md +++ b/client/README.md @@ -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 server’s 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. diff --git a/client/project.godot b/client/project.godot index f42beef..5ab0b4b 100644 --- a/client/project.godot +++ b/client/project.godot @@ -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] diff --git a/client/scenes/main.tscn b/client/scenes/main.tscn index 1aab92b..6fafa7e 100644 --- a/client/scenes/main.tscn +++ b/client/scenes/main.tscn @@ -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: —" diff --git a/client/scripts/ground_pick.gd b/client/scripts/ground_pick.gd index 3f48957..d52b798 100644 --- a/client/scripts/ground_pick.gd +++ b/client/scripts/ground_pick.gd @@ -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 diff --git a/client/scripts/main.gd b/client/scripts/main.gd index 05faf9a..eee2139 100644 --- a/client/scripts/main.gd +++ b/client/scripts/main.gd @@ -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). diff --git a/client/scripts/player.gd b/client/scripts/player.gd index 67cffd6..80c46f9 100644 --- a/client/scripts/player.gd +++ b/client/scripts/player.gd @@ -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.9991–0.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 click’s 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 click’s 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.996–0.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) diff --git a/server/NeonSprawl.Server/Game/PositionState/MovementValidationOptions.cs b/server/NeonSprawl.Server/Game/PositionState/MovementValidationOptions.cs index 2d80cc3..94d64ed 100644 --- a/server/NeonSprawl.Server/Game/PositionState/MovementValidationOptions.cs +++ b/server/NeonSprawl.Server/Game/PositionState/MovementValidationOptions.cs @@ -11,7 +11,7 @@ public sealed class MovementValidationOptions public double MaxHorizontalStep { get; set; } = 18.0; /// Max absolute delta on Y in one move (meters). Inclusive at the limit. - public double MaxVerticalStep { get; set; } = 1.25; + public double MaxVerticalStep { get; set; } = 2.2; /// When true, target must lie inside the axis-aligned district box (inclusive min/max). public bool DistrictBoundsEnabled { get; set; } diff --git a/server/NeonSprawl.Server/appsettings.json b/server/NeonSprawl.Server/appsettings.json index 90ef224..18e0bca 100644 --- a/server/NeonSprawl.Server/appsettings.json +++ b/server/NeonSprawl.Server/appsettings.json @@ -16,7 +16,7 @@ "MovementValidation": { "HorizontalStepEnabled": false, "MaxHorizontalStep": 18, - "MaxVerticalStep": 1.25, + "MaxVerticalStep": 2.2, "DistrictBoundsEnabled": false } } diff --git a/server/README.md b/server/README.md index f63f2db..63f378d 100644 --- a/server/README.md +++ b/server/README.md @@ -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 (~**8–14 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 client’s **`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 (~**8–14 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 client’s **`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):