DesktopCharacterController.cs
using Sandbox;
using System;

public sealed class DesktopCharacterController : Component
{
	[Property] private CameraComponent Camera { get; set; }

	/// <summary>World-units per second the desktop death camera rises while looking at the corpse.</summary>
	[Property] public float DeathCamRiseSpeed { get; set; } = 30f;

	/// <summary>Height above the player root the death camera looks at (approx. torso).</summary>
	[Property] public float DeathCamLookHeight { get; set; } = 40f;

	/// <summary>Eye height in units when standing upright (5'9" avatar ≈ 64).</summary>
	[Property] public float StandingEyeHeight { get; set; } = 64f;

	/// <summary>Eye height in units when fully crouched.</summary>
	[Property] public float CrouchedEyeHeight { get; set; } = 34f;

	/// <summary>How quickly duck level transitions between standing and crouched.</summary>
	[Property] public float DuckSpeed { get; set; } = 10f;

	[Property] private ScreenPanel Scoreboard { get; set; }

	private PlayerCharacterController PlayerCharacterController { get; set; }
	private HealthComponent Health { get; set; }

	private Angles desktopEyeAngles;
	private float _deathCamHeight;
	private bool _deathCamInitialized;
	private float _duckLevel;

	protected override void OnStart()
	{
		PlayerCharacterController = GetComponentInParent<PlayerCharacterController>();
		PlayerCharacterController.Camera = Camera;
		Health = GetComponentInParent<HealthComponent>();
	}

	protected override void OnFixedUpdate()
	{
		if ( IsProxy )
			return;

		if ( !NetworkManager.Instance.IsMapReady )
		{
			PlayerCharacterController.Velocity = Vector3.Zero;
			PlayerCharacterController.SyncedVelocity = Vector3.Zero;
			return;
		}

		if ( DeathmatchGame.Instance.State == MatchState.ENDED )
			return;

		if ( Health.IsDead )
			return;

		UpdateDuckCapsule();
		HandleDesktopMovement();
	}

	protected override void OnUpdate()
	{
		if ( IsProxy )
			return;

		UpdateScoreboard();
		UpdateCursor();
		UpdateDesktopDeathCam();

		if ( Health.IsDead || DeathmatchGame.Instance.State == MatchState.ENDED )
			return;

		UpdateMouseLook();
		UpdateCameraEyeHeight();
	}

	private void UpdateCursor()
	{
		Mouse.Visibility = DeathmatchGame.Instance.State == MatchState.ENDED
			? MouseVisibility.Visible
			: MouseVisibility.Hidden;
	}

	private void UpdateMouseLook()
	{
		desktopEyeAngles.pitch += Input.AnalogLook.pitch;
		desktopEyeAngles.yaw += Input.AnalogLook.yaw;
		desktopEyeAngles.pitch = desktopEyeAngles.pitch.Clamp( -89f, 89f );

		Camera.WorldRotation = Rotation.From( desktopEyeAngles );
	}

	private void UpdateDuckCapsule()
	{
		float targetDuck = Input.Down( "duck" ) ? 1f : 0f;
		_duckLevel = MathX.Lerp( _duckLevel, targetDuck, Time.Delta * DuckSpeed );
		_duckLevel = PlayerCharacterController.SetDuckLevel( _duckLevel );
	}

	private void UpdateCameraEyeHeight()
	{
		float eyeHeight = MathX.Lerp( StandingEyeHeight, CrouchedEyeHeight, PlayerCharacterController.DuckLevel );
		Camera.LocalPosition = Camera.LocalPosition.WithZ( eyeHeight );
	}

	private void HandleDesktopMovement()
	{
		var moveInput = Input.AnalogMove;
		var lookYaw = Rotation.FromYaw( desktopEyeAngles.yaw );
		var moveDir = (lookYaw.Forward * moveInput.x) + (lookYaw.Left * moveInput.y);

		if ( moveDir.Length > 1f )
			moveDir = moveDir.Normal;

		if ( PlayerCharacterController.IsGrappling )
		{
			var wishVel = moveDir * PlayerCharacterController.MoveSpeed;
			if ( !(Input.Pressed( "jump" ) && PlayerCharacterController.TryJump( wishVel.WithZ( 0 ) )) )
				PlayerCharacterController.ApplyGrappleVelocity();
		}
		else
		{
			var vel = PlayerCharacterController.Velocity;
			vel.x = moveDir.x * PlayerCharacterController.MoveSpeed;
			vel.y = moveDir.y * PlayerCharacterController.MoveSpeed;

			if ( Input.Pressed( "jump" ) && PlayerCharacterController.TryJump( vel.WithZ( 0 ) ) )
			{
				vel = PlayerCharacterController.Velocity;
			}
			else if ( PlayerCharacterController.IsOnGround )
			{
				vel.z = 0;
				PlayerCharacterController.Velocity = vel;
			}
			else
			{
				vel.z -= PlayerCharacterController.Gravity * Time.Delta;
				vel.z = MathF.Max( vel.z, -PlayerCharacterController.TerminalVelocity );
				PlayerCharacterController.Velocity = vel;
			}
		}

		PlayerCharacterController.SyncedVelocity = PlayerCharacterController.Velocity;
		PlayerCharacterController.Move();
		PlayerCharacterController.UpdateFallAndBoundsDamage();
	}

	private void UpdateDesktopDeathCam()
	{
		if ( !Health.IsDead )
			return;

		var lookTarget = WorldPosition + Vector3.Up * DeathCamLookHeight;

		if ( !_deathCamInitialized )
		{
			_deathCamHeight = (Camera.WorldPosition - lookTarget).Dot( Vector3.Up );
			_deathCamHeight = MathF.Max( _deathCamHeight, 8f );
			_deathCamInitialized = true;
		}

		_deathCamHeight += DeathCamRiseSpeed * Time.Delta;

		var camPos = lookTarget + Vector3.Up * _deathCamHeight;
		Camera.WorldPosition = camPos;
		Camera.WorldRotation = Rotation.LookAt( lookTarget - camPos, Vector3.Up );
	}

	private void UpdateScoreboard()
	{
		Scoreboard.Enabled = Input.Down( "score" ) || DeathmatchGame.Instance.State == MatchState.ENDED;
	}
}