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

public partial class Player
{
	[Property, Feature( "Camera" ), Step( 16 ), Range( 128, 512 )]
	public float CameraDistance = 256;

	[Property, Feature( "Camera" )]
	public float ZoomSpeed = 16;

	[Property, Feature( "Camera" )]
	public float DistanceFromBottom = 16;

	[Property, Feature( "Camera" )]
	public Vector2 CameraClamp = new Vector2(0, 90);

	private float fieldOfView => Preferences.FieldOfView;

	private void CameraControl()
	{
		if ( Scene.Camera is not CameraComponent camera ) return;

		Vector3 cameraOrigin = new Vector3( WorldPosition.x, WorldPosition.y, DistanceFromBottom + WorldPosition.z );
		CameraOrigin = cameraOrigin;

		var angels = camera.WorldRotation.Angles();

		angels += Input.AnalogLook;
		angels.pitch = MathX.Clamp( angels.pitch, CameraClamp.x, CameraClamp.y );

		if ( Input.Down( "attack2" ) || Input.UsingController )
		{
			if ( GetZoomInput() is float zoomInput )
				CameraDistance = (int)MathX.Clamp( CameraDistance + zoomInput * ZoomSpeed, 128, 512 );

			camera.WorldRotation = angels;

			Mouse.Visibility = MouseVisibility.Hidden;
		}
		else
		{
			Mouse.Visibility = MouseVisibility.Visible;
		}

		camera.WorldPosition = cameraOrigin + (-camera.WorldRotation.Forward * currentDistance);

		float GetZoomInput()
		{
			if ( Input.Down( "ZoomIn" ) ) return -1f;

			if ( Input.Down( "ZoomOut" ) ) return 1f;

			return 0f;
		}

		camera.FieldOfView = fieldOfView;
	}

	private void CheckDistanceTrace()
	{
		if ( Scene.Camera is not CameraComponent camera ) return;

		var trace = Scene.Trace.FromTo( CameraOrigin, CameraOrigin + camera.WorldRotation.Forward * -CameraDistance )
			.IgnoreGameObjectHierarchy( GameObject.Root )
			.Run();

		if ( trace.Distance < currentDistance )
		{
			currentDistance = currentDistance.LerpTo( trace.Distance, Time.Delta * 20.0f );
		}
		else
		{
			currentDistance = currentDistance.LerpTo( trace.Distance, Time.Delta * 2.0f );
		}

		if ( DrawTraces )
			DebugOverlay.Trace( trace, overlay: true );
	}
}