NEON-29: Fix step assist at lips; descend floor_block only at wall

Remove the forward-velocity dot gate that blocked assist when pushing
head-on into a step (dot ~1 vs max 0.52).

Relax floor_block_on_wall only for true descends (goal below feet) when
is_on_wall or slide hits a vertical-ish normal, so platform deck motion
stays stable.
pull/41/head
VinPropane 2026-04-12 00:37:38 -04:00
parent 7d03b17136
commit 00b5be7b0c
2 changed files with 25 additions and 5 deletions

View File

@ -31,8 +31,6 @@ const IDLE_RIM_SETTLE_STEP: float = 0.004
const DESCEND_GOAL_Y_MARGIN: float = 0.06 const DESCEND_GOAL_Y_MARGIN: float = 0.06
## Y lift when blocked by wall-ish slide but nav goal is higher (stepped bumps). ## Y lift when blocked by wall-ish slide but nav goal is higher (stepped bumps).
const WALK_STEP_ASSIST_DELTA: float = 0.11 const WALK_STEP_ASSIST_DELTA: float = 0.11
## Skip assist when horizontal velocity already aligns enough with want-dir (corners slide slowly).
const WALK_STEP_ASSIST_MAX_FORWARD_DOT: float = 0.52
const WALK_STEP_ASSIST_COOLDOWN_TICKS: int = 8 const WALK_STEP_ASSIST_COOLDOWN_TICKS: int = 8
## Floor-snap length while step-climbing. Small enough that it cannot reach the lower floor ## Floor-snap length while step-climbing. Small enough that it cannot reach the lower floor
## (~0.11 m away after the first lift) but large enough to catch the step surface once the ## (~0.11 m away after the first lift) but large enough to catch the step surface once the
@ -205,8 +203,7 @@ func _try_walk_step_assist() -> bool:
return false return false
# Compute want direction before the support/blocking checks so both can use it. # Compute want direction before the support/blocking checks so both can use it.
var want: Vector3 = to_h.normalized() var want: Vector3 = to_h.normalized()
var vel_h: Vector3 = Vector3(velocity.x, 0.0, velocity.z) if not _step_assist_wallish_blocks(want):
if not _step_assist_wallish_blocks(want) or vel_h.dot(want) > WALK_STEP_ASSIST_MAX_FORWARD_DOT:
return false return false
if not _step_assist_has_support(want): if not _step_assist_has_support(want):
return false return false
@ -262,6 +259,25 @@ func _walk_floor_snap_length() -> float:
return WALK_STEP_ASSIST_SNAP if _step_assist_active else FLOOR_SNAP_MOVING return WALK_STEP_ASSIST_SNAP if _step_assist_active else FLOOR_SNAP_MOVING
## Descend only: relax `floor_block_on_wall` when pressed against a vertical face so Jolt can
## slide off terrace lips. Never when the goal is above the feet (climb) — avoids platform-wide
## toggling that broke horizontal motion.
func _apply_floor_block_for_descend_at_wall(feet_y: float) -> void:
floor_block_on_wall = true
if not _has_walk_goal:
return
if _auth_walk_goal.y >= feet_y - DESCEND_GOAL_Y_MARGIN:
return
if is_on_wall():
floor_block_on_wall = false
return
for i: int in range(get_slide_collision_count()):
var n: Vector3 = get_slide_collision(i).get_normal()
if absf(n.y) < 0.52:
floor_block_on_wall = false
return
## When the authoritative goal shares XZ with the capsule (e.g. terrace above), horizontal ## When the authoritative goal shares XZ with the capsule (e.g. terrace above), horizontal
## `full_to_goal` is zero and steering must follow the baked path toward a ramp/step, not +X. ## `full_to_goal` is zero and steering must follow the baked path toward a ramp/step, not +X.
func _set_horizontal_velocity_from_nav_path_or_goal(fallback_goal_xz: Vector3) -> void: func _set_horizontal_velocity_from_nav_path_or_goal(fallback_goal_xz: Vector3) -> void:
@ -612,7 +628,10 @@ func _physics_process(delta: float) -> void:
_idle_manual_correction_ticks = 0 _idle_manual_correction_ticks = 0
floor_block_on_wall = true var feet_y: float = capsule_feet_y(
global_position.y, CAPSULE_HALF_HEIGHT + PLAYER_CAPSULE_RADIUS
)
_apply_floor_block_for_descend_at_wall(feet_y)
var nav_map: RID = _nav_agent.get_navigation_map() var nav_map: RID = _nav_agent.get_navigation_map()
if NavigationServer3D.map_get_iteration_id(nav_map) == 0: if NavigationServer3D.map_get_iteration_id(nav_map) == 0:

View File

@ -110,6 +110,7 @@ No new automated GDScript tests are added for this story — there is no new GDS
- **snap_to_server places capsule underground (resolved):** The server stores the raw surface Y from the client's click (e.g. 0.6 for a terrace platform), not the capsule body-centre Y (which should be 0.6 + 0.9 = 1.5). `snap_to_server` was setting `global_position.y` directly to this surface Y, putting the capsule bottom at 0.3 m. With `_floor_angle_loose_ticks = 0` and a stale `is_on_floor() = true`, the idle path's `_stable_idle_support()` check passed and held the player without calling `move_and_slide()`, preventing Jolt from resolving the penetration. Fixed: clamp `global_position.y ≥ CAPSULE_HALF_HEIGHT + PLAYER_CAPSULE_RADIUS` (= 0.9 m) in `snap_to_server`; also set `_floor_angle_loose_ticks = FLOOR_ANGLE_LOOSE_TICKS_AFTER_STOP` (96 ticks) to force corrective idle physics before the next walk goal. - **snap_to_server places capsule underground (resolved):** The server stores the raw surface Y from the client's click (e.g. 0.6 for a terrace platform), not the capsule body-centre Y (which should be 0.6 + 0.9 = 1.5). `snap_to_server` was setting `global_position.y` directly to this surface Y, putting the capsule bottom at 0.3 m. With `_floor_angle_loose_ticks = 0` and a stale `is_on_floor() = true`, the idle path's `_stable_idle_support()` check passed and held the player without calling `move_and_slide()`, preventing Jolt from resolving the penetration. Fixed: clamp `global_position.y ≥ CAPSULE_HALF_HEIGHT + PLAYER_CAPSULE_RADIUS` (= 0.9 m) in `snap_to_server`; also set `_floor_angle_loose_ticks = FLOOR_ANGLE_LOOSE_TICKS_AFTER_STOP` (96 ticks) to force corrective idle physics before the next walk goal.
- **Step assist undone by floor snap (resolved):** Each `_try_walk_step_assist()` cycle lifts the capsule 0.11 m. The prior `floor_snap_length = FLOOR_SNAP_MOVING = 0.32 m` meant the snap always reached the lower floor (0.11 m below the lifted bottom), pulling the capsule straight back down every tick. The player "nudged and stopped" because each lift was immediately cancelled. Fixed: added `WALK_STEP_ASSIST_SNAP = 0.09 m` constant (below 0.11 m so the snap cannot reach the previous floor, but above 0.03 m so it catches the step surface once cleared) and a `_step_assist_active` boolean flag. During active climbing, `floor_snap_length` is set to `WALK_STEP_ASSIST_SNAP` in both `_after_walk_move_and_slide()` and the main physics loop. The flag is cleared when the player lands cleanly on a floor without wall contact (`is_on_floor() and not is_on_wall()`), or when the nav goal is cleared/snapped. - **Step assist undone by floor snap (resolved):** Each `_try_walk_step_assist()` cycle lifts the capsule 0.11 m. The prior `floor_snap_length = FLOOR_SNAP_MOVING = 0.32 m` meant the snap always reached the lower floor (0.11 m below the lifted bottom), pulling the capsule straight back down every tick. The player "nudged and stopped" because each lift was immediately cancelled. Fixed: added `WALK_STEP_ASSIST_SNAP = 0.09 m` constant (below 0.11 m so the snap cannot reach the previous floor, but above 0.03 m so it catches the step surface once cleared) and a `_step_assist_active` boolean flag. During active climbing, `floor_snap_length` is set to `WALK_STEP_ASSIST_SNAP` in both `_after_walk_move_and_slide()` and the main physics loop. The flag is cleared when the player lands cleanly on a floor without wall contact (`is_on_floor() and not is_on_wall()`), or when the nav goal is cleared/snapped.
- **Terrace B descend / climb (resolved, iterated):** Scene lifts and **descend-bypass** bee-lines caused regressions (stuck climbs, lip oscillation, wrong-direction slides). **Removed the descend bypass:** vertical moves (e.g. platform ↔ floor) use the **baked nav path + step assist** only. **Walk gravity** is **`!is_on_floor()`** again — never “goal below feet” while still walking on an upper surface (that applied gravity across the whole **TerracePlatformB** deck and destroyed horizontal motion). **`velocity.y`** is cleared by the horizontal steer each tick as before. - **Terrace B descend / climb (resolved, iterated):** Scene lifts and **descend-bypass** bee-lines caused regressions (stuck climbs, lip oscillation, wrong-direction slides). **Removed the descend bypass:** vertical moves (e.g. platform ↔ floor) use the **baked nav path + step assist** only. **Walk gravity** is **`!is_on_floor()`** again — never “goal below feet” while still walking on an upper surface (that applied gravity across the whole **TerracePlatformB** deck and destroyed horizontal motion). **`velocity.y`** is cleared by the horizontal steer each tick as before.
- **Lip stuck (floor→platform / platform→floor):** Step assist required **`vel_h.dot(want) ≤ 0.52`**, so a **head-on** push into the step (dot ≈ 1) **never** triggered the lift — capsule jammed on the vertical face. **Removed** that gate. For **platform→floor**, **`floor_block_on_wall = false`** only when the nav goal is **below** the feet **and** **`is_on_wall()`** or a **wall-ish slide normal** exists, so open deck walking stays stable.
## Open questions / risks ## Open questions / risks