Player/Player.Camera.cs
using Sandbox.Movement;

public sealed partial class Player
{
	[Property, Group( "Camera" )] public float SeatedCameraDistance { get; set; } = 200f;
	[Property, Group( "Camera" )] public float SeatedCameraHeight { get; set; } = 40f;
	[Property, Group( "Camera" )] public float SeatedCameraPositionSpeed { get; set; } = 3f;
	[Property, Group( "Camera" )] public float SeatedCameraVelocityScale { get; set; } = 0.1f;

	private ISitTarget _cachedSeat;
	private bool _seatCameraInitialized;
	private float _minCameraDistance;
	private float _smoothedDistance;
	private Angles _seatedAngles;
	private Vector3 _lastSeatWorldPos;
	private List<BaseCarryable> _seatedWeapons;

	private float roll;

	void PlayerController.IEvents.OnEyeAngles( ref Angles ang )
	{
		var angles = ang;
		Local.IPlayerEvents.Post( x => x.OnCameraMove( ref angles ) );
		ang = angles;
	}

	void PlayerController.IEvents.PostCameraSetup( CameraComponent camera )
	{
		camera.FovAxis = CameraComponent.Axis.Vertical;
		camera.FieldOfView = Screen.CreateVerticalFieldOfView( Preferences.FieldOfView, 9.0f / 16.0f );

		if ( Controller.ThirdPerson )
			camera.RenderExcludeTags.Add( "firstperson" );
		else
			camera.RenderExcludeTags.Remove( "firstperson" );

		Local.IPlayerEvents.Post( x => x.OnCameraSetup( camera ) );

		ApplyMovementCameraEffects( camera );
		UpdateSeatedWeapons();
		ApplySeatedCameraSetup( camera );
		DrawSeatedWeaponHud();

		Local.IPlayerEvents.Post( x => x.OnCameraPostSetup( camera ) );
	}

	private void ApplyMovementCameraEffects( CameraComponent camera )
	{
		if ( Controller.ThirdPerson ) return;
		if ( !GamePreferences.ViewBobbing ) return;

		var r = Controller.WishVelocity.Dot( EyeTransform.Left ) / -250.0f;
		roll = MathX.Lerp( roll, r, Time.Delta * 10.0f, true );

		camera.WorldRotation *= new Angles( 0, 0, roll );
	}

	private void UpdateSeatedWeapons()
	{
		var seat = GetComponentInParent<ISitTarget>( false );
		if ( seat is null )
		{
			_cachedSeat = null;
			_seatedWeapons = null;
			return;
		}

		if ( seat != _cachedSeat )
		{
			_cachedSeat = seat;
			_seatCameraInitialized = false;
			RebuildSeatedWeapons( (seat as Component).GameObject );
		}
	}

	private void ApplySeatedCameraSetup( CameraComponent camera )
	{
		if ( !Controller.ThirdPerson )
			return;

		if ( _cachedSeat is null )
			return;

		var seatGo = (_cachedSeat as Component).GameObject;
		var seatPos = seatGo.WorldPosition + Vector3.Up * SeatedCameraHeight;

		if ( !_seatCameraInitialized )
		{
			_seatCameraInitialized = true;
			_minCameraDistance = MathF.Max( SeatedCameraDistance, RebuildContraptionBounds( seatGo ) );
			_seatedAngles = camera.WorldRotation.Angles();
			_lastSeatWorldPos = seatPos;
			_smoothedDistance = _minCameraDistance;
		}

		_seatedAngles.yaw += Input.AnalogLook.yaw;
		_seatedAngles.pitch = (_seatedAngles.pitch + Input.AnalogLook.pitch).Clamp( -89, 89 );

		// Derive velocity from position delta and add it to the target distance
		var speed = (seatPos - _lastSeatWorldPos).Length / Time.Delta;
		_lastSeatWorldPos = seatPos;
		var targetDistance = _minCameraDistance + speed * SeatedCameraVelocityScale;

		// Smooth orbit distance
		_smoothedDistance = _smoothedDistance.LerpTo( targetDistance, Time.Delta * SeatedCameraPositionSpeed );

		// Compose rotation: yaw around world up, then pitch around local right, no gimbal lock
		var camRot = Rotation.FromYaw( _seatedAngles.yaw ) * Rotation.FromPitch( _seatedAngles.pitch );
		var desiredPos = seatPos + camRot.Backward * _smoothedDistance;

		var tr = Scene.Trace.FromTo( seatPos, desiredPos ).Radius( 8f ).WithTag( "world" ).IgnoreGameObjectHierarchy( GameObject.Root ).Run();
		var camPos = tr.Hit ? tr.HitPosition + (seatPos - desiredPos).Normal * 4f : desiredPos;

		camera.WorldPosition = camPos;
		camera.WorldRotation = Rotation.LookAt( seatPos - camPos, Vector3.Up );
	}

	private float RebuildContraptionBounds( GameObject seatGo )
	{
		var builder = new LinkedGameObjectBuilder();
		builder.AddConnected( seatGo );

		var totalBounds = new BBox();
		var initialized = false;
		foreach ( var obj in builder.Objects )
		{
			if ( obj.Tags.Has( "player" ) ) continue;
			var b = obj.GetBounds();
			totalBounds = initialized ? totalBounds.AddBBox( b ) : b;
			initialized = true;
		}

		return totalBounds.Size.Length;
	}

	private void RebuildSeatedWeapons( GameObject seatGo )
	{
		var builder = new LinkedGameObjectBuilder();
		builder.AddConnected( seatGo );

		_seatedWeapons ??= new List<BaseCarryable>();
		_seatedWeapons.Clear();

		foreach ( var obj in builder.Objects )
		{
			foreach ( var weapon in obj.GetComponentsInChildren<BaseCarryable>() )
			{
				if ( !weapon.HasOwner )
					_seatedWeapons.Add( weapon );
			}
		}
	}

	private void DrawSeatedWeaponHud()
	{
		if ( _seatedWeapons == null || _seatedWeapons.Count == 0 ) return;
		if ( Scene.Camera is null ) return;
		if ( Scene.Camera.RenderExcludeTags.Has( "ui" ) ) return;

		var hud = Scene.Camera.Hud;

		foreach ( var weapon in _seatedWeapons )
		{
			if ( !weapon.IsValid() ) continue;
			if ( weapon is IPlayerControllable controllable && !controllable.CanControl( this ) ) continue;

			Vector2 aimPos;

			if ( weapon.IsTargetedAim )
			{
				aimPos = Screen.Size * 0.5f;
			}
			else
			{
				var muzzle = weapon.MuzzleTransform.WorldTransform;
				var tr = Scene.Trace.Ray( muzzle.Position, muzzle.Position + muzzle.Rotation.Forward * 4096f )
					.IgnoreGameObjectHierarchy( weapon.GameObject.Root )
					.Run();

				aimPos = Scene.Camera.PointToScreenPixels( tr.EndPosition );
			}

			weapon.DrawHud( hud, aimPos );
		}
	}
}