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 movement_profile_id: StringName: get: return _movement_capabilities.profile_id 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 _keyboard_turn_speed_radians_per_second: float var _movement_capabilities: PlayerMovementCapabilities var _godot_world_velocity_units_per_second := Vector3.ZERO var _is_flight_enabled := false ## Creates a movement state with explicit Godot-unit speeds, acceleration and capabilities. ## The current compatibility renderer treats one Godot unit as one WoW yard. ## A null capability input fails closed to Blizzlike335 debug permissions. 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, movement_capabilities: PlayerMovementCapabilities = null, keyboard_turn_speed_radians_per_second: float = PI ) -> 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 _keyboard_turn_speed_radians_per_second = maxf( keyboard_turn_speed_radians_per_second, 0.0 ) _movement_capabilities = ( movement_capabilities if movement_capabilities != null else PlayerMovementCapabilities.blizzlike_335() ) ## Returns the Godot yaw delta for one signed keyboard-turn request. ## Positive intent turns right, which is negative rotation around Godot +Y. func calculate_yaw_delta_radians(move_intent: MoveIntent, delta_seconds: float) -> float: return ( -move_intent.turn_axis * _keyboard_turn_speed_radians_per_second * maxf(delta_seconds, 0.0) ) ## Toggles sandbox free flight and clears velocity when the profile permits it. ## Returns the resulting state; rejected compatibility-profile requests return false. func toggle_sandbox_flight() -> bool: if not _movement_capabilities.allows_debug_free_flight: return false _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 and _movement_capabilities.allows_debug_sprint 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