class_name LocalPlayerMovementController extends RefCounted ## Scene-free state and velocity integrator for the current local sandbox player. ## Direction inputs use a Godot-world movement basis selected by the scene ## adapter. This class does not discover Nodes, query terrain or mutate transforms. var godot_world_velocity_units_per_second: Vector3: get: return _godot_world_velocity_units_per_second var is_flight_enabled: bool: get: return _is_flight_enabled var _run_speed_units_per_second: float var _backward_speed_units_per_second: float var _strafe_speed_units_per_second: float var _flight_vertical_speed_units_per_second: float var _acceleration_units_per_second_squared: float var _sandbox_sprint_multiplier: float var _godot_world_velocity_units_per_second := Vector3.ZERO var _is_flight_enabled := false ## Creates a movement state with explicit Godot-unit speeds and acceleration. ## The current compatibility renderer treats one Godot unit as one WoW yard. func _init( run_speed_units_per_second: float, backward_speed_units_per_second: float, strafe_speed_units_per_second: float, flight_vertical_speed_units_per_second: float, acceleration_units_per_second_squared: float, sandbox_sprint_multiplier: float ) -> void: _run_speed_units_per_second = run_speed_units_per_second _backward_speed_units_per_second = backward_speed_units_per_second _strafe_speed_units_per_second = strafe_speed_units_per_second _flight_vertical_speed_units_per_second = flight_vertical_speed_units_per_second _acceleration_units_per_second_squared = acceleration_units_per_second_squared _sandbox_sprint_multiplier = sandbox_sprint_multiplier ## Toggles sandbox free flight and clears velocity exactly like the pre-M02 controller. ## Returns the new flight state. func toggle_sandbox_flight() -> bool: _is_flight_enabled = not _is_flight_enabled _godot_world_velocity_units_per_second = Vector3.ZERO return _is_flight_enabled ## Advances velocity by one physics step and returns local-world displacement. ## `godot_world_movement_basis` is the player basis on ground and camera-pivot basis in flight. ## Negative delta values are treated as zero and do not mutate velocity. func advance( move_intent: MoveIntent, godot_world_movement_basis: Basis, delta_seconds: float ) -> Vector3: var safe_delta_seconds := maxf(delta_seconds, 0.0) if is_zero_approx(safe_delta_seconds): return Vector3.ZERO var target_velocity := _calculate_target_velocity(move_intent, godot_world_movement_basis) _godot_world_velocity_units_per_second = _godot_world_velocity_units_per_second.move_toward( target_velocity, _acceleration_units_per_second_squared * safe_delta_seconds ) return _godot_world_velocity_units_per_second * safe_delta_seconds func _calculate_target_velocity(move_intent: MoveIntent, godot_world_movement_basis: Basis) -> Vector3: var forward_direction := -godot_world_movement_basis.z var right_direction := godot_world_movement_basis.x if not _is_flight_enabled: forward_direction.y = 0.0 right_direction.y = 0.0 forward_direction = forward_direction.normalized() right_direction = right_direction.normalized() var forward_speed := ( _run_speed_units_per_second if move_intent.forward_axis > 0.0 else _backward_speed_units_per_second ) var speed_multiplier := _sandbox_sprint_multiplier if move_intent.is_sprint_requested else 1.0 var target_velocity := ( forward_direction * move_intent.forward_axis * forward_speed + right_direction * move_intent.strafe_axis * _strafe_speed_units_per_second ) * speed_multiplier if _is_flight_enabled: target_velocity.y += ( move_intent.vertical_axis * _flight_vertical_speed_units_per_second * speed_multiplier ) return target_velocity