From db1fe0f46b1fee14d7ec7ebfbdacde2f2b4e1347 Mon Sep 17 00:00:00 2001 From: VinPropane Date: Sun, 5 Apr 2026 01:12:11 -0400 Subject: [PATCH] NS-23: skip nav waypoints on NS-19 bump plateaus only Restore obstacle routing via waypoints elsewhere; on bump tops nav often prefers a downward segment and breaks departure. Detect plateau with is_on_floor + flat normal + ray down into group ns19_bump; then steer only toward authoritative goal xz. Slopes keep waypoint pathing. --- client/scenes/main.tscn | 4 ++-- client/scripts/player.gd | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/client/scenes/main.tscn b/client/scenes/main.tscn index 395914c..2d8b4f5 100644 --- a/client/scenes/main.tscn +++ b/client/scenes/main.tscn @@ -138,7 +138,7 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.95, 0.22, -1.05) mesh = SubResource("BoxMesh_marker") surface_material_override/0 = SubResource("Mat_marker_base") -[node name="NS19BumpA" type="StaticBody3D" parent="World/NavigationRegion3D" unique_id=1900001 groups=["walkable"]] +[node name="NS19BumpA" type="StaticBody3D" parent="World/NavigationRegion3D" unique_id=1900001 groups=["walkable", "ns19_bump"]] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.4, 0.075, -2.6) collision_layer = 1 @@ -149,7 +149,7 @@ surface_material_override/0 = SubResource("Mat_ns19_bump") [node name="CollisionShape3D" type="CollisionShape3D" parent="World/NavigationRegion3D/NS19BumpA" unique_id=1900003] shape = SubResource("Convex_ns19a_frustum") -[node name="NS19BumpB" type="StaticBody3D" parent="World/NavigationRegion3D" unique_id=1900011 groups=["walkable"]] +[node name="NS19BumpB" type="StaticBody3D" parent="World/NavigationRegion3D" unique_id=1900011 groups=["walkable", "ns19_bump"]] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1.85, 0.06, -4.1) collision_layer = 1 diff --git a/client/scripts/player.gd b/client/scripts/player.gd index 0f90763..5184f80 100644 --- a/client/scripts/player.gd +++ b/client/scripts/player.gd @@ -1,7 +1,7 @@ extends CharacterBody3D ## NS-23: Nav waypoints + stored server goal for arrival. -## NS-19: bump collision is one convex frustum (plateau + slopes); no autostep. +## NS-19: convex frustum bumps; `ns19_bump` group — on flat plateau skip nav waypoints (departure). const MOVE_SPEED: float = 5.0 const ARRIVE_EPS: float = 0.35 @@ -11,6 +11,8 @@ const VERT_ARRIVE_EPS: float = 0.055 const MAX_CLIMB_SPEED: float = 2.6 const MAX_DESCENT_SPEED: float = 2.2 const DIRECT_APPROACH_RADIUS: float = 0.85 +## Floor normal must be this “flat” to count as bump plateau (slopes use nav waypoints). +const NS19_BUMP_TOP_NORMAL_DOT: float = 0.992 var _has_walk_goal: bool = false var _auth_walk_goal: Vector3 = Vector3.ZERO @@ -41,6 +43,33 @@ func snap_to_server(world_pos: Vector3) -> void: _nav_agent.set_target_position(world_pos) +func _on_ns19_bump_plateau() -> bool: + if not is_on_floor(): + return false + if get_floor_normal().dot(Vector3.UP) < NS19_BUMP_TOP_NORMAL_DOT: + return false + return _raycast_down_hits_group("ns19_bump", 2.5) + + +func _raycast_down_hits_group(group_name: String, cast_len: float) -> bool: + var space: PhysicsDirectSpaceState3D = get_world_3d().direct_space_state + var from: Vector3 = global_position + var to: Vector3 = from + Vector3(0.0, -cast_len, 0.0) + var q: PhysicsRayQueryParameters3D = PhysicsRayQueryParameters3D.create(from, to) + q.collision_mask = 1 + q.exclude = [get_rid()] + var hit: Dictionary = space.intersect_ray(q) + if hit.is_empty(): + return false + var col: Variant = hit.get("collider") + var n: Node = col as Node + while n: + if n.is_in_group(group_name): + return true + n = n.get_parent() + return false + + func _steer_toward_world_point(point: Vector3) -> void: var pos := global_position var delta_h: Vector3 = Vector3(point.x - pos.x, 0.0, point.z - pos.z) @@ -94,6 +123,14 @@ func _physics_process(_delta: float) -> void: move_and_slide() return + # Nav waypoints on the plateau often point down the slope first; goal-only + horizontal steer + # matches the NS-19 departure fix. Slopes stay on waypoints so obstacle routing still applies + # when crossing the district. + if _on_ns19_bump_plateau(): + _steer_toward_world_point(_auth_walk_goal) + move_and_slide() + return + if horiz_dist <= DIRECT_APPROACH_RADIUS: _steer_toward_world_point(_auth_walk_goal) else: