diff --git a/client/scripts/player.gd b/client/scripts/player.gd index 8173a4b..295836c 100644 --- a/client/scripts/player.gd +++ b/client/scripts/player.gd @@ -1,17 +1,19 @@ extends CharacterBody3D ## NS-23: Nav agent suggests waypoints; arrival uses stored server goal only (not agent “finished”). -## `is_navigation_finished()` / internal target can clear early while we are still far from goal. -## NS-19 bumps: allow limited Y in `velocity` so `move_and_slide` can step up/down shallow hits. +## NS-19 bumps: thin box colliders need **auto-step** (`test_move` up+forward); Y steering alone +## is not enough for vertical faces. const MOVE_SPEED: float = 5.0 const ARRIVE_EPS: float = 0.35 -## Must match NS-19 shallow bumps (~0.15 m rise); don’t clear goal while still below pick Y. const VERT_ARRIVE_EPS: float = 0.16 const MAX_CLIMB_SPEED: float = 2.6 const MAX_DESCENT_SPEED: float = 2.2 -## Outside this XZ distance to server goal, use nav corners; inside, steer straight to goal. const DIRECT_APPROACH_RADIUS: float = 0.85 +## NS-19 bumps ≤ ~0.15 m; probe slightly above for capsule + nav tolerance. +const STEP_MAX_HEIGHT: float = 0.32 +const STEP_PROBE_FWD: float = 0.38 +const STUCK_HORIZ_SPEED: float = 0.28 var _has_walk_goal: bool = false var _auth_walk_goal: Vector3 = Vector3.ZERO @@ -23,21 +25,18 @@ func _ready() -> void: _nav_agent.set_target_position(global_position) -## After successful move POST + GET: walk to server target without teleporting. func set_authoritative_nav_goal(world_pos: Vector3) -> void: _auth_walk_goal = world_pos _has_walk_goal = true _nav_agent.set_target_position(world_pos) -## NS-19 rejection or cancel pending path without moving the body. func clear_nav_goal() -> void: velocity = Vector3.ZERO _has_walk_goal = false _nav_agent.set_target_position(global_position) -## Boot (and any hard reconcile): teleport to server position and reset agent. func snap_to_server(world_pos: Vector3) -> void: global_position = world_pos velocity = Vector3.ZERO @@ -55,7 +54,33 @@ func _steer_toward_world_point(point: Vector3) -> void: velocity = desired -func _physics_process(_delta: float) -> void: +## Godot `test_move`: returns **true** if the motion would hit something (blocked). +func _try_autostep(delta: float) -> bool: + var to_goal_h: Vector3 = Vector3( + _auth_walk_goal.x - global_position.x, 0.0, _auth_walk_goal.z - global_position.z + ) + if to_goal_h.length_squared() < 0.0225: + return false + var dir_h: Vector3 = to_goal_h.normalized() + var fwd_len: float = maxf(MOVE_SPEED * delta, 0.1) + var motion_fwd: Vector3 = dir_h * minf(fwd_len, STEP_PROBE_FWD) + var motion_up: Vector3 = Vector3(0.0, STEP_MAX_HEIGHT, 0.0) + var xf: Transform3D = global_transform + if not test_move(xf, motion_fwd): + return false + if test_move(xf, motion_up): + return false + var xf_up := Transform3D(xf.basis, xf.origin + motion_up) + if test_move(xf_up, motion_fwd): + return false + global_position += motion_up + motion_fwd + velocity.x = dir_h.x * MOVE_SPEED + velocity.z = dir_h.z * MOVE_SPEED + velocity.y = 0.0 + return true + + +func _physics_process(delta: float) -> void: if not _has_walk_goal: velocity = Vector3.ZERO move_and_slide() @@ -75,6 +100,7 @@ func _physics_process(_delta: float) -> void: if NavigationServer3D.map_get_iteration_id(nav_map) == 0: _steer_toward_world_point(_auth_walk_goal) move_and_slide() + _maybe_autostep_after_slide(delta, full_to_goal, horiz_dist) return if horiz_dist <= DIRECT_APPROACH_RADIUS: @@ -90,3 +116,19 @@ func _physics_process(_delta: float) -> void: _steer_toward_world_point(_auth_walk_goal) move_and_slide() + _maybe_autostep_after_slide(delta, full_to_goal, horiz_dist) + + +func _maybe_autostep_after_slide( + delta: float, full_to_goal: Vector3, horiz_dist: float +) -> void: + if not _has_walk_goal: + return + if not is_on_floor() and not is_on_wall(): + return + var hspd: float = Vector2(velocity.x, velocity.z).length() + var want_step: bool = hspd < STUCK_HORIZ_SPEED + if not want_step and is_on_wall(): + want_step = full_to_goal.y > 0.03 and horiz_dist > 0.12 + if want_step and _try_autostep(delta): + move_and_slide()