PlayerSlide.cs
using Sandbox;
using Sandbox.Movement;
using System;

public sealed class PlayerSlide :
	MoveModeWalk,
	PlayerController.IEvents
{
	[Property]
	public float MinimumStartSpeed { get; set; } = 210f;

	[Property]
	public float InitialBoost { get; set; } = 30f;

	[Property]
	public float MinimumSlideSpeed { get; set; } = 150f;

	[Property]
	public float SlideDeceleration { get; set; } = 220f;

	[Property]
	public float MaximumDuration { get; set; } = 0.85f;

	[Property]
	public float SteeringStrength { get; set; } = 2.25f;

	[Property]
	public bool MaintainSlideMomentum { get; set; } = true;

	[Property]
	public float MomentumRetention { get; set; } = 0.88f;

	[Property]
	public float SlideGroundSnapDistance { get; set; } = 0f;

	[Property]
	public float AirborneEndDelay { get; set; } = 0.15f;

	[Property]
	public float HardStopCheckDelay { get; set; } = 0.25f;

	[Property]
	public float HardStopSpeed { get; set; } = 25f;

	[Property]
	public bool AllowLandingSlides { get; set; } = true;

	[Property]
	public float MinimumLandingSlideSpeed { get; set; } = 240f;

	[Property]
	public float LandingSlideBoost { get; set; } = 30f;

	[Property, Group( "Audio" ), Description( "One-shot positional sound played when a slide begins. The owning player broadcasts it so nearby players hear the same action." )]
	public SoundEvent SlideSound { get; set; }

	[Property, Group( "Audio" ), Range( 0f, 2f )]
	public float SlideSoundVolume { get; set; } = 0.22f;

	public bool IsSliding { get; private set; }

	public bool LandingSlideQueued { get; private set; }

	public float CurrentSlideSpeed =>
		_slideSpeed;

	public override bool AllowGrounding =>
		true;

	public override bool AllowFalling =>
		true;

	private Vector3 _slideDirection;

	private float _slideSpeed;

	private float _slideTime;

	private Vector3 _slideStartPosition;

	protected override void OnStart()
	{
		base.OnStart();

		if ( Priority <= 0 )
		{
			Priority =
				100;
		}

		Log.Info(
			$"PlayerSlide active | Proxy: {IsProxy} | Owner: {Network.OwnerId}"
		);
	}

	/// <summary>
	/// Locomotion decides whether Slide is contextually valid.
	///
	/// Slide itself only validates the physical requirements.
	/// </summary>
	public bool TryStartSlide()
	{
		if ( IsProxy )
			return false;

		if ( IsSliding )
			return false;

		var controller =
			Controller;

		if ( controller is null )
			return false;

		if ( !controller.IsOnGround )
			return false;

		if (
			controller.IsSwimming ||
			controller.IsClimbing
		)
			return false;

		Vector3 horizontalVelocity =
			controller.Velocity.WithZ(
				0f
			);

		return BeginSlide(
			horizontalVelocity,
			MinimumStartSpeed,
			InitialBoost,
			"GROUND"
		);
	}

	public bool QueueLandingSlide()
	{
		if ( IsProxy )
			return false;

		if ( !AllowLandingSlides )
			return false;

		if ( IsSliding )
			return false;

		var controller =
			Controller;

		if ( controller is null )
			return false;

		if ( !controller.IsAirborne )
			return false;

		float horizontalSpeed =
			controller.Velocity
				.WithZ( 0f )
				.Length;

		if (
			horizontalSpeed <
			MinimumLandingSlideSpeed
		)
		{
			LandingSlideQueued =
				false;

			return false;
		}

		if ( !LandingSlideQueued )
		{
			Log.Info(
				$"LANDING SLIDE QUEUED | Speed: {horizontalSpeed:0.0}"
			);
		}

		LandingSlideQueued =
			true;

		return true;
	}

	public void CancelQueuedLandingSlide()
	{
		LandingSlideQueued =
			false;
	}

	public void OnLanded(
		float distance,
		Vector3 impactVelocity
	)
	{
		if ( IsProxy )
			return;

		if ( !LandingSlideQueued )
			return;

		if ( IsSliding )
			return;

		LandingSlideQueued =
			false;

		var controller =
			Controller;

		if ( controller is null )
			return;

		if (
			controller.IsSwimming ||
			controller.IsClimbing
		)
			return;

		Vector3 impactHorizontal =
			impactVelocity.WithZ(
				0f
			);

		Vector3 currentHorizontal =
			controller.Velocity.WithZ(
				0f
			);

		Vector3 sourceVelocity =
			impactHorizontal.Length >=
				currentHorizontal.Length
				? impactHorizontal
				: currentHorizontal;

		BeginSlide(
			sourceVelocity,
			MinimumLandingSlideSpeed,
			LandingSlideBoost,
			"LANDING"
		);
	}

	private bool BeginSlide(
		Vector3 horizontalVelocity,
		float minimumSpeed,
		float boost,
		string source
	)
	{
		float currentSpeed =
			horizontalVelocity.Length;

		if (
			currentSpeed <
			minimumSpeed
		)
			return false;

		if (
			horizontalVelocity.IsNearlyZero(
				0.1f
			)
		)
			return false;

		_slideDirection =
			horizontalVelocity.Normal;

		_slideSpeed =
			currentSpeed +
			boost;

		_slideTime =
			0f;

		_slideStartPosition =
			WorldPosition;

		IsSliding =
			true;

		LandingSlideQueued =
			false;

		//
		// Slide physically requires the compact collider.
		//
		// This does NOT modify PlayerCrouch's user intent.
		//
		Controller?.UpdateDucking(
			true
		);

		PlaySlideStartSound();

		Log.Info(
			$"SLIDE START ({source}) | Speed: {_slideSpeed:0.0}"
		);

		return true;
	}

	public override int Score(
		PlayerController controller
	)
	{
		return IsSliding
			? Priority
			: -1000;
	}

	public override void OnModeBegin()
	{
		base.OnModeBegin();

		var controller =
			Controller;

		if ( controller is null )
			return;

		controller.UpdateDucking(
			true
		);

		ApplySlideVelocity(
			controller
		);
	}

	public override Vector3 UpdateMove(
		Rotation eyes,
		Vector3 input
	)
	{
		if ( !IsSliding )
			return Vector3.Zero;

		ApplySteering(
			eyes,
			input
		);

		return
			_slideDirection *
			_slideSpeed;
	}

	public override void UpdateRigidBody(
		Rigidbody body
	)
	{
		body.Gravity =
			true;

		body.LinearDamping =
			0.15f;

		body.AngularDamping =
			1f;
	}

	public override void PrePhysicsStep()
	{
		base.PrePhysicsStep();

		if ( !IsSliding )
			return;

		var controller =
			Controller;

		if ( controller is null )
		{
			EndSlide(
				"NO CONTROLLER"
			);

			return;
		}

		if (
			controller.IsSwimming ||
			controller.IsClimbing
		)
		{
			EndSlide(
				"INVALID MOVEMENT MODE"
			);

			return;
		}

		_slideTime +=
			Time.Delta;

		_slideSpeed =
			MathF.Max(
				0f,
				_slideSpeed -
					SlideDeceleration *
					Time.Delta
			);

		if ( MaintainSlideMomentum )
		{
			MaintainMomentum(
				controller
			);
		}

		if (
			_slideTime >=
			MaximumDuration
		)
		{
			EndSlide(
				"DURATION"
			);

			return;
		}

		if (
			_slideSpeed <=
			MinimumSlideSpeed
		)
		{
			EndSlide(
				"LOW SPEED"
			);

			return;
		}

		if (
			controller.IsAirborne &&
			controller.TimeSinceGrounded >
				AirborneEndDelay
		)
		{
			EndSlide(
				"AIRBORNE"
			);

			return;
		}
	}

	public override void PostPhysicsStep()
	{
		if ( !IsSliding )
		{
			base.PostPhysicsStep();

			return;
		}

		//
		// Do NOT use MoveModeWalk's full StepDownHeight
		// grounding while sliding.
		//
		// Keeping this at zero allows ramp launches.
		//
		if (
			SlideGroundSnapDistance >
			0f
		)
		{
			StickToGround(
				SlideGroundSnapDistance
			);
		}

		if (
			_slideTime <
			HardStopCheckDelay
		)
			return;

		var controller =
			Controller;

		if ( controller is null )
			return;

		float actualSpeed =
			controller.Velocity
				.WithZ( 0f )
				.Length;

		if (
			actualSpeed <
			HardStopSpeed
		)
		{
			EndSlide(
				"HARD STOP"
			);
		}
	}

	public override void OnModeEnd(
		MoveMode next
	)
	{
		base.OnModeEnd(
			next
		);

		if ( IsSliding )
		{
			EndSlide(
				"MODE OVERRIDE"
			);
		}
	}

	public void EndForJump()
	{
		if ( !IsSliding )
			return;

		EndSlide(
			"JUMP"
		);
	}

	public void ResetSlide()
	{
		IsSliding =
			false;

		LandingSlideQueued =
			false;

		_slideSpeed =
			0f;

		_slideTime =
			0f;
	}

	private void MaintainMomentum(
		PlayerController controller
	)
	{
		if ( controller.Body is null )
			return;

		Vector3 bodyVelocity =
			controller.Body.Velocity;

		Vector3 horizontalVelocity =
			bodyVelocity.WithZ(
				0f
			);

		float desiredMinimum =
			_slideSpeed *
			MomentumRetention;

		if (
			horizontalVelocity.Length >=
			desiredMinimum
		)
			return;

		controller.Body.Velocity =
			_slideDirection *
				_slideSpeed +
			Vector3.Up *
				bodyVelocity.z;
	}

	private void ApplySlideVelocity(
		PlayerController controller
	)
	{
		if ( controller.Body is null )
			return;

		float verticalVelocity =
			controller.Body.Velocity.z;

		controller.Body.Velocity =
			_slideDirection *
				_slideSpeed +
			Vector3.Up *
				verticalVelocity;
	}

	private void ApplySteering(
		Rotation eyes,
		Vector3 input
	)
	{
		Vector3 desiredDirection =
			(
				eyes *
				input.ClampLength(
					1f
				)
			)
			.WithZ(
				0f
			);

		if (
			desiredDirection.IsNearlyZero(
				0.1f
			)
		)
			return;

		desiredDirection =
			desiredDirection.Normal;

		float steeringAmount =
			MathX.Clamp(
				SteeringStrength *
					Time.Delta,
				0f,
				1f
			);

		Vector3 newDirection =
			_slideDirection +
			(
				desiredDirection -
					_slideDirection
			) *
			steeringAmount;

		if (
			!newDirection.IsNearlyZero(
				0.01f
			)
		)
		{
			_slideDirection =
				newDirection.Normal;
		}
	}

	private void PlaySlideStartSound()
	{
		if ( SlideSound is null )
			return;

		if ( Networking.IsActive )
		{
			BroadcastSlideStartSound();
			return;
		}

		PlaySlideStartSoundLocal();
	}

	[Rpc.Broadcast( NetFlags.Unreliable | NetFlags.OwnerOnly )]
	private void BroadcastSlideStartSound()
	{
		PlaySlideStartSoundLocal();
	}

	private void PlaySlideStartSoundLocal()
	{
		if ( SlideSound is null )
			return;

		SoundHandle handle =
			GameObject.PlaySound( SlideSound );

		handle.Volume *=
			MathF.Max( SlideSoundVolume, 0f );
	}

	private void EndSlide(
		string reason
	)
	{
		if ( !IsSliding )
			return;

		float distance =
			_slideStartPosition.Distance(
				WorldPosition
			);

		float actualSpeed =
			Controller is not null
				? Controller.Velocity
					.WithZ( 0f )
					.Length
				: 0f;

		IsSliding =
			false;

		Log.Info(
			$"SLIDE END | {reason} | Time: {_slideTime:0.00}s | Distance: {distance:0.0} | Speed: {actualSpeed:0.0}"
		);
	}
}