Reverts the 15mm TerraceStepB/TerracePlatformB root offset that regressed
floor-to-step movement.
When the walk goal is below the feet, apply gravity even while is_on_floor
so lip contacts are not stuck with vy cleared every tick; cap descend
floor_snap_length so the upper surface does not win over internal edges.
Raise TerraceStepB and TerracePlatformB 15mm so boxes are not coplanar
with the main floor top, reducing Jolt internal-edge hangs.
Remove descend lip stall, peel multipliers, and snap cap; keep a single
floor_block_on_wall=false policy for the whole descend branch to avoid
oscillation. Document in NEON-29 plan.
Two independent root causes kept the player from climbing terrace steps:
1. snap_to_server used the raw surface Y from the server (e.g. 0.6 m for a
platform) as global_position.y directly. With capsule total half-height
= 0.9 m this placed the bottom at -0.3 m underground. The stale
is_on_floor()==true + _floor_angle_loose_ticks==0 path skipped every
move_and_slide(), so Jolt never resolved the penetration and the capsule
froze at an intermediate Y.
Fix: clamp global_position.y >= CAPSULE_HALF_HEIGHT + PLAYER_CAPSULE_RADIUS
(0.9 m) in snap_to_server; force FLOOR_ANGLE_LOOSE_TICKS_AFTER_STOP
corrective idle ticks afterwards.
2. Each _try_walk_step_assist() lift (0.11 m) was immediately undone by
floor_snap_length = FLOOR_SNAP_MOVING = 0.32 m snapping the capsule back
to the lower floor, which was only 0.11 m below the lifted bottom.
Fix: add WALK_STEP_ASSIST_SNAP = 0.09 m constant and _step_assist_active
bool. While climbing, floor_snap_length uses the smaller constant in
both _after_walk_move_and_slide() and the main physics loop. The flag
is cleared when the capsule lands cleanly (is_on_floor && !is_on_wall),
or when the nav goal is cleared/snapped to server.
Also: is_on_wall() now counts as support in _step_assist_has_support() so
the assist fires while the capsule is floating but pressed against a step
face; _step_assist_active cleared on arrival, snap, and goal-clear; all
temporary DBG print() statements removed.
_try_walk_step_assist compared goal surface Y to the capsule centre
(~0.9 m above floor) instead of the actual capsule bottom, disabling
the assist for any surface below ~0.925 m — every terrace we added.
The lift formula used offsets 0.45/0.55 instead of the full capsule
total half-height (CAPSULE_HALF_HEIGHT + PLAYER_CAPSULE_RADIUS = 0.9),
producing zero/negative lift.
Fix: guard uses actual_feet_y = pos.y - CAPSULE_HALF_HEIGHT - PLAYER_CAPSULE_RADIUS;
lift targets goal.y + total_half_height via clampf. Flat floor clicks
and cylinder bumps are unaffected (guard still disabled when goal.y is
at or below true feet level).
Expanded 45×45 district made the 18 m MaxHorizontalStep too restrictive
(floor diagonal ~63 m). Add HorizontalStepEnabled to MovementValidationOptions
(default false, matching DistrictBoundsEnabled pattern) so the check is
skipped unless explicitly opted in. Add test for disabled case (9 pass).
Update README and plan with the decision.
Planning doc for expanding the prototype client district to ~5×
current footprint with terraces, step geometry, and extra obstacles
for camera/nav stress QA.