class_name CockpitCameraController extends Node3D const CameraLookCommand = preload( "res://game/application/vehicle/camera_look_command.gd" ) const VehicleTelemetry = preload("res://game/application/dive/vehicle_telemetry.gd") @export var minimum_pitch_radians := -0.30 @export var maximum_pitch_radians := 0.20 @export var maximum_yaw_radians := deg_to_rad(30.0) @export var feedback_response := 4.5 @export var maximum_feedback_pitch_radians := deg_to_rad(18.0) @export var maximum_feedback_roll_radians := deg_to_rad(16.0) * 0.55 var _look_yaw := 0.0 var _look_pitch := -0.04 var _feedback_pitch := 0.0 var _feedback_roll := 0.0 var _target_feedback_pitch := 0.0 var _target_feedback_roll := 0.0 func apply_look(command: CameraLookCommand) -> void: if command == null or not command.validation_error().is_empty(): return if command.recenter_requested: _look_yaw = 0.0 _look_pitch = -0.04 else: _look_yaw = clampf( _look_yaw + command.yaw_delta_radians, -maximum_yaw_radians, maximum_yaw_radians ) _look_pitch = clampf( _look_pitch + command.pitch_delta_radians, minimum_pitch_radians, maximum_pitch_radians ) _apply_rotation() func present_telemetry(telemetry: VehicleTelemetry) -> void: if telemetry == null: return _target_feedback_pitch = ( -telemetry.vertical_effort * maximum_feedback_pitch_radians ) _target_feedback_roll = ( -telemetry.steering_effort * maximum_feedback_roll_radians ) func look_yaw_radians() -> float: return _look_yaw func look_pitch_radians() -> float: return _look_pitch func _process(delta: float) -> void: _feedback_pitch = lerpf( _feedback_pitch, _target_feedback_pitch, 1.0 - exp(-feedback_response * delta) ) _feedback_roll = lerpf( _feedback_roll, _target_feedback_roll, 1.0 - exp(-feedback_response * delta) ) _apply_rotation() func _apply_rotation() -> void: rotation = Vector3( _look_pitch + _feedback_pitch, _look_yaw, _feedback_roll )