neon-sprawl/client/scripts/player.gd

93 lines
3.0 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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.
const MOVE_SPEED: float = 5.0
const ARRIVE_EPS: float = 0.35
## Must match NS-19 shallow bumps (~0.15 m rise); dont 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
var _has_walk_goal: bool = false
var _auth_walk_goal: Vector3 = Vector3.ZERO
@onready var _nav_agent: NavigationAgent3D = $NavigationAgent3D
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
_has_walk_goal = false
_nav_agent.set_target_position(world_pos)
func _steer_toward_world_point(point: Vector3) -> void:
var delta: Vector3 = point - global_position
if delta.length_squared() < 1e-8:
velocity = Vector3.ZERO
return
var desired: Vector3 = delta.normalized() * MOVE_SPEED
desired.y = clampf(desired.y, -MAX_DESCENT_SPEED, MAX_CLIMB_SPEED)
velocity = desired
func _physics_process(_delta: float) -> void:
if not _has_walk_goal:
velocity = Vector3.ZERO
move_and_slide()
return
var full_to_goal: Vector3 = _auth_walk_goal - global_position
var horiz_dist: float = Vector2(full_to_goal.x, full_to_goal.z).length()
var vert_err: float = absf(full_to_goal.y)
if horiz_dist <= ARRIVE_EPS and vert_err <= VERT_ARRIVE_EPS:
velocity = Vector3.ZERO
_has_walk_goal = false
_nav_agent.set_target_position(global_position)
move_and_slide()
return
var nav_map: RID = _nav_agent.get_navigation_map()
if NavigationServer3D.map_get_iteration_id(nav_map) == 0:
_steer_toward_world_point(_auth_walk_goal)
move_and_slide()
return
if horiz_dist <= DIRECT_APPROACH_RADIUS:
_steer_toward_world_point(_auth_walk_goal)
else:
var next_path_position: Vector3 = _nav_agent.get_next_path_position()
var to_next_h: Vector3 = Vector3(
next_path_position.x - global_position.x, 0.0, next_path_position.z - global_position.z
)
if to_next_h.length_squared() > 0.0025:
_steer_toward_world_point(next_path_position)
else:
_steer_toward_world_point(_auth_walk_goal)
move_and_slide()