diff --git a/client/scripts/player.gd b/client/scripts/player.gd index d323ebb..cb3d47d 100644 --- a/client/scripts/player.gd +++ b/client/scripts/player.gd @@ -1,11 +1,15 @@ extends CharacterBody3D -## NS-23: `NavigationAgent3D` path-follow for click-to-move visuals. Server owns `PositionState` / -## `MoveCommand` (NS-16, NS-19); boot snap vs post-verify nav goal from `main.gd`. +## 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. const MOVE_SPEED: float = 5.0 -## If the nav mesh is missing or the agent gives up, steer on XZ toward `target_position`. -const FALLBACK_ARRIVE_EPS: float = 0.4 +const ARRIVE_EPS: float = 0.35 +## Outside this XZ distance to server goal, use nav corners; inside, steer straight to goal. +const DIRECT_APPROACH_RADIUS: float = 0.85 + +var _has_walk_goal: bool = false +var _auth_walk_goal: Vector3 = Vector3.ZERO @onready var _nav_agent: NavigationAgent3D = $NavigationAgent3D @@ -16,12 +20,15 @@ func _ready() -> void: ## 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) @@ -29,40 +36,47 @@ func clear_nav_goal() -> void: func snap_to_server(world_pos: Vector3) -> void: global_position = world_pos velocity = Vector3.ZERO + _has_walk_goal = false _nav_agent.set_target_position(world_pos) func _physics_process(_delta: float) -> void: - var nav_map: RID = _nav_agent.get_navigation_map() - if NavigationServer3D.map_get_iteration_id(nav_map) == 0: + if not _has_walk_goal: velocity = Vector3.ZERO move_and_slide() return - if _nav_agent.is_navigation_finished(): - var target: Vector3 = _nav_agent.target_position - var flat_target := Vector3(target.x, global_position.y, target.z) - var to_target: Vector3 = flat_target - global_position - to_target.y = 0.0 - if to_target.length_squared() > FALLBACK_ARRIVE_EPS * FALLBACK_ARRIVE_EPS: - velocity = to_target.normalized() * MOVE_SPEED - velocity.y = 0.0 - else: - velocity = Vector3.ZERO + + var to_goal: Vector3 = Vector3( + _auth_walk_goal.x - global_position.x, 0.0, _auth_walk_goal.z - global_position.z + ) + var dist_goal: float = to_goal.length() + if dist_goal <= ARRIVE_EPS: + velocity = Vector3.ZERO + _has_walk_goal = false + _nav_agent.set_target_position(global_position) move_and_slide() return - var next_path_position: Vector3 = _nav_agent.get_next_path_position() - var to_next: Vector3 = next_path_position - global_position - to_next.y = 0.0 - if to_next.length_squared() < 0.0001: - var to_final: Vector3 = _nav_agent.target_position - global_position - to_final.y = 0.0 - if to_final.length_squared() > 0.0001: - velocity = to_final.normalized() * MOVE_SPEED - velocity.y = 0.0 - else: - velocity = Vector3.ZERO - else: - velocity = to_next.normalized() * MOVE_SPEED + var nav_map: RID = _nav_agent.get_navigation_map() + if NavigationServer3D.map_get_iteration_id(nav_map) == 0: + velocity = to_goal.normalized() * MOVE_SPEED velocity.y = 0.0 + move_and_slide() + return + + var dir: Vector3 + if dist_goal <= DIRECT_APPROACH_RADIUS: + dir = to_goal.normalized() + else: + var next_path_position: Vector3 = _nav_agent.get_next_path_position() + var to_next: Vector3 = Vector3( + next_path_position.x - global_position.x, 0.0, next_path_position.z - global_position.z + ) + if to_next.length_squared() > 0.0025: + dir = to_next.normalized() + else: + dir = to_goal.normalized() + + velocity = dir * MOVE_SPEED + velocity.y = 0.0 move_and_slide()