From ef211ab4ac8b5c47f443912e26e8fe6be32b1113 Mon Sep 17 00:00:00 2001 From: VinPropane Date: Sat, 11 Apr 2026 19:35:39 -0400 Subject: [PATCH] NEON-29: fix arrival check using actual capsule bottom for vert_err MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vertical arrival error was computed with CAPSULE_HALF_HEIGHT (0.5), placing code-feet at body_y - 0.5 = 0.4 on a floor-level player. Step surfaces at Y=0.3 are only 0.1 m away from that reference, and Jolt can nudge the body down to ~0.8 when the bottom hemisphere contacts the step edge, making code-feet = 0.3 and vert_err = 0 — triggering immediate arrival while the player is still at floor level. Fix: pass CAPSULE_HALF_HEIGHT + PLAYER_CAPSULE_RADIUS (= 0.9) to vertical_arrival_error so actual feet = body_y - 0.9 = 0.0 on the floor. Step goals (Y=0.3) now give vert_err=0.3 >> VERT_ARRIVE_EPS, preventing premature arrival. Arrival fires correctly once the step assist lifts the player onto the surface (body_y=1.2, err=0). Add two tests pinning the new pre/post-climb behaviour. --- client/scripts/player.gd | 7 ++++++- client/test/player_test.gd | 27 +++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/client/scripts/player.gd b/client/scripts/player.gd index 064670e..3e66592 100644 --- a/client/scripts/player.gd +++ b/client/scripts/player.gd @@ -491,8 +491,13 @@ func _physics_process(_delta: float) -> void: var full_to_goal: Vector3 = _auth_walk_goal - global_position var horiz_dist: float = Vector2(full_to_goal.x, full_to_goal.z).length() + # Use actual capsule bottom (CAPSULE_HALF_HEIGHT + PLAYER_CAPSULE_RADIUS = 0.9) so that a + # step-surface goal (e.g. Y=0.3) is never considered "arrived" while the player is still at + # floor height. Using only CAPSULE_HALF_HEIGHT (0.5) gives code-feet at Y=0.4, which is too + # close to step surfaces and causes premature arrival when Jolt nudges the body down to ~0.8 + # while the capsule hemisphere contacts the step edge. var vert_err: float = vertical_arrival_error( - _auth_walk_goal.y, global_position.y, CAPSULE_HALF_HEIGHT + _auth_walk_goal.y, global_position.y, CAPSULE_HALF_HEIGHT + PLAYER_CAPSULE_RADIUS ) if horiz_dist <= ARRIVE_EPS and vert_err <= VERT_ARRIVE_EPS: velocity = Vector3.ZERO diff --git a/client/test/player_test.gd b/client/test/player_test.gd index 50aee13..a912ae6 100644 --- a/client/test/player_test.gd +++ b/client/test/player_test.gd @@ -119,6 +119,33 @@ func test_vertical_arrival_error_stays_large_for_origin_height_only_match() -> v assert_that(absf(err - 0.5)).is_less(0.000001) +func test_arrival_vert_err_large_for_step_goal_from_floor() -> void: + # Player at floor height (body Y=0.9). Goal at step surface (Y=0.3). + # Using actual capsule bottom (CAPSULE_HALF_HEIGHT + PLAYER_CAPSULE_RADIUS) the error + # must exceed VERT_ARRIVE_EPS so the arrival check does NOT fire prematurely. + var goal_y := 0.3 + var body_y := 0.9 + var full_half: float = ( + PLAYER_SCRIPT.get("CAPSULE_HALF_HEIGHT") as float + + PLAYER_SCRIPT.get("PLAYER_CAPSULE_RADIUS") as float + ) + var err: float = PLAYER_SCRIPT.vertical_arrival_error(goal_y, body_y, full_half) + assert_that(err).is_greater(PLAYER_SCRIPT.get("VERT_ARRIVE_EPS") as float) + + +func test_arrival_vert_err_zero_when_standing_on_step() -> void: + # Player body centred at goal_y + total_half_height (i.e. actually standing on the step). + # Error must be within VERT_ARRIVE_EPS so arrival fires correctly once the player is up. + var goal_y := 0.3 + var full_half: float = ( + PLAYER_SCRIPT.get("CAPSULE_HALF_HEIGHT") as float + + PLAYER_SCRIPT.get("PLAYER_CAPSULE_RADIUS") as float + ) + var body_y: float = goal_y + full_half + var err: float = PLAYER_SCRIPT.vertical_arrival_error(goal_y, body_y, full_half) + assert_that(err).is_less_equal(PLAYER_SCRIPT.get("VERT_ARRIVE_EPS") as float) + + func test_flat_floor_goal_is_not_below_feet_height_margin() -> void: var goal_y := 0.0 var body_origin_y := 0.4