Player/JumpControllerAnimator.cs
using static Sandbox.PhysicsContact;

namespace Sandbox;

public sealed class JumpControllerAnimator : Component
{
	SkinnedModelRenderer _renderer;
	/// <summary>
	/// The body will usually be a child object with SkinnedModelRenderer
	/// </summary>
	[Property, Feature( "Animator" )]
	public SkinnedModelRenderer Renderer
	{
		get => _renderer;
		set
		{
			if ( _renderer == value ) return;

			_renderer = value;
		}
	}
	[Property, RequireComponent] PlayerController Controller { get; set; }

	[Sync] public Angles TargetAngles { get; set; }

	protected override void OnUpdate()
	{
		WithVelocity( Controller.Velocity );
		WithWishVelocity( Controller.WishVelocity );
		IsGrounded = Controller.IsOnGround;

		Vector3 dir = default;

		// 1) If we have move input, use that to define facing
		var moveInput = Input.AnalogMove;
		if ( moveInput.Length > 0.01f )
		{
			// Convert 2D move into world-space using eye yaw (pitch ignored)
			var eye = Controller.EyeAngles;
			eye.pitch = 0;
			eye.roll = 0;

			dir = (eye.ToRotation() * moveInput).WithZ( 0 );
		}
		// 2) Otherwise, fall back to velocity (e.g. sliding / being pushed)
		else if ( Controller.WishVelocity.Length > 0.01f )
		{
			dir = Controller.WishVelocity.WithZ( 0 );
		}

		if ( !dir.IsNearlyZero( 0.001f ) && Controller.IsOnGround )
		{
			TargetAngles = Rotation
				.LookAt( dir.Normal )
				.Angles()
				.WithRoll( 0 );
		}

		if ( !Controller.IsOnGround )
		{
			TargetAngles = Rotation.LookAt( Controller.Velocity.WithZ( 0 ).Normal ).Angles().WithRoll( 0 );
		}

		Renderer.WorldRotation = Rotation.Slerp(
			Renderer.WorldRotation,
			Rotation.From( TargetAngles ),
			8f * Time.Delta
		);

		WithLook( Controller.EyeAngles.Forward, 1, 1, 1.0f );
	}

	public void WithVelocity( Vector3 Velocity )
	{
		var dir = Velocity;
		var forward = Renderer.WorldRotation.Forward.Dot( dir );
		var sideward = Renderer.WorldRotation.Right.Dot( dir );

		var angle = MathF.Atan2( sideward, forward ).RadianToDegree().NormalizeDegrees();

		Renderer.Set( "move_direction", angle );
		Renderer.Set( "move_speed", Velocity.Length );
		Renderer.Set( "move_groundspeed", Velocity.WithZ( 0 ).Length );
		Renderer.Set( "move_y", sideward );
		Renderer.Set( "move_x", forward );
		Renderer.Set( "move_z", Velocity.z );
	}

	public void WithWishVelocity( Vector3 Velocity )
	{
		var dir = Velocity;
		var forward = Renderer.WorldRotation.Forward.Dot( dir );
		var sideward = Renderer.WorldRotation.Right.Dot( dir );

		var angle = MathF.Atan2( sideward, forward ).RadianToDegree().NormalizeDegrees();

		Renderer.Set( "wish_direction", angle );
		Renderer.Set( "wish_speed", Velocity.Length );
		Renderer.Set( "wish_groundspeed", Velocity.WithZ( 0 ).Length );
		Renderer.Set( "wish_y", sideward );
		Renderer.Set( "wish_x", forward );
		Renderer.Set( "wish_z", Velocity.z );
	}

	public bool IsGrounded
	{
		get => Renderer.GetBool( "b_grounded" );
		set => Renderer.Set( "b_grounded", value );
	}

	public void WithLook( Vector3 lookDirection, float eyesWeight = 1.0f, float headWeight = 1.0f, float bodyWeight = 1.0f )
	{
		Renderer.SetLookDirection( "aim_eyes", lookDirection, eyesWeight );
		Renderer.SetLookDirection( "aim_head", lookDirection, headWeight );
		Renderer.SetLookDirection( "aim_body", lookDirection, bodyWeight );
	}

	public float DuckLevel
	{
		get => Renderer.GetFloat( "duck" );
		set => Renderer.Set( "duck", value );
	}
	public void TriggerJump()
	{
		Renderer.Set( "b_jump", true );
	}
}