NEON-29: Fix walk ledge probes for descending + same-column clicks

Degenerate XZ (velocity and want_goal_h ~0) made _walk_has_close_floor_probe_below
return after center-only, suppressing ledge gravity for the whole move.

Add _resolve_walk_probe_dir_xz: when goal surface is below feet, prefer
horizontal bearing to the goal, else nav path lookahead, else fallbacks.
Non-descending motion keeps velocity-first probes.

Remove the nav-column-only want_goal_h override (superseded by descending
branch). Reverts the regression where the capsule stayed on violet until XZ.
pull/41/head
VinPropane 2026-04-12 18:25:22 -04:00
parent db1f4da068
commit 91b802c679
1 changed files with 34 additions and 17 deletions

View File

@ -373,6 +373,39 @@ func _walk_ray_hit_up_floor(space: PhysicsDirectSpaceState3D, off_xz: Vector2) -
return n.dot(Vector3.UP) > WALK_SUPPORT_PROBE_MIN_UP_DOT
## Horizontal bearing for walk ledge / continuation rays. Must not stay ~zero while descending: a
## floor click **under** the capsule ([code]want_goal_h ≈ 0[/code]) used to leave [code]walk_move_dir_xz[/code]
## empty so [method _walk_has_close_floor_probe_below] took the center-only fast path and never
## applied ledge gravity until XZ arrival.
func _resolve_walk_probe_dir_xz(feet_y: float, want_goal_h: Vector3) -> Vector2:
var pos := global_position
var to_g := Vector2(_auth_walk_goal.x - pos.x, _auth_walk_goal.z - pos.z)
var vxz := Vector2(velocity.x, velocity.z)
var wh := Vector2(want_goal_h.x, want_goal_h.z)
var descending: bool = (
_has_walk_goal and _auth_walk_goal.y < feet_y - DESCEND_GOAL_Y_MARGIN
)
if descending:
if to_g.length_squared() >= 1e-6:
return to_g
if wh.length_squared() >= 1e-8:
return wh
var path: PackedVector3Array = _nav_agent.get_current_navigation_path()
var min_l2: float = NAV_PATH_STEER_MIN_LOOKAHEAD * NAV_PATH_STEER_MIN_LOOKAHEAD
for i: int in range(path.size()):
var d := Vector2(path[i].x - pos.x, path[i].z - pos.z)
if d.length_squared() >= min_l2:
return d
if to_g.length_squared() > 1e-12:
return to_g
if vxz.length_squared() >= 1e-8:
return vxz
return Vector2(1.0, 0.0)
if vxz.length_squared() < 1e-10:
return wh
return vxz
## Walkable support for **continued** forward motion within [member WALK_SUPPORT_PROBE_DEPTH].
## Requires a hit under the foot origin **and** at least one sample **ahead** in [param move_dir_xz]
## (same construction as [method _walk_probe_xz_offsets]). If only the center hits while the leading
@ -944,23 +977,7 @@ func _physics_process(delta: float) -> void:
else:
_set_horizontal_velocity_toward(_auth_walk_goal, want_goal_h)
# Prefer [param velocity] XZ so probes match [code]move_and_slide[/code] / path steering (NEON-16).
# Exception: with **column** path steering toward a **lower** click, velocity often hugs the baked
# path tangent on TerracePlatformB (violet) while [param want_goal_h] points at the floor — rays
# along velocity keep hitting deck ahead → false “support” and no ledge gravity until XZ closes
# (violet → gold → floor). Use click bearing for probes in that case only.
var walk_move_dir_xz := Vector2(velocity.x, velocity.z)
if walk_move_dir_xz.length_squared() < 1e-10:
walk_move_dir_xz = Vector2(want_goal_h.x, want_goal_h.z)
elif (
needs_vertical_routing
and _walk_nav_column_steering
and is_on_floor()
and _auth_walk_goal.y < feet_y - DESCEND_GOAL_Y_MARGIN
):
var wh2 := Vector2(want_goal_h.x, want_goal_h.z)
if wh2.length_squared() > 1e-10:
walk_move_dir_xz = wh2
var walk_move_dir_xz: Vector2 = _resolve_walk_probe_dir_xz(feet_y, want_goal_h)
_apply_walk_air_gravity(delta, walk_move_dir_xz)
floor_snap_length = _walk_floor_snap_length(feet_y, want_goal_h, walk_move_dir_xz)