Components/FollowTargetComponent.cs
using Andicraft.SecondOrderDynamics;
using Sandbox;

namespace Andicraft.SecondOrderDynamics.Components;

[Title( "Second Order Dynamics - Follow Target" ), Category("Second Order Dynamics")]
public class FollowTargetComponent : Component
{
	[Property] public GameObject FollowTarget { get; set; }

	/// <summary>
	/// Reset the dynamics if the object moves further than this distance in one frame
	/// </summary>
	[Property] public float TeleportDistanceThreshold { get; set; } = 32 * 10;

	[Property] public DynamicsParameters PositionParameters { get; set; } = new();
	[Property] public DynamicsParameters RotationParameters { get; set; } = new();

	private Vector3Dynamics _positionDynamics;
	private RotationDynamics _rotationDynamics;

	private Vector3 _previousFollowPos = default;
	
	protected override void OnStart()
	{
		_positionDynamics = new Vector3Dynamics( PositionParameters, FollowTarget.WorldPosition );
		_rotationDynamics = new RotationDynamics( RotationParameters, FollowTarget.WorldRotation );

		_previousFollowPos = FollowTarget.WorldPosition;
	}

	protected override void OnUpdate()
	{
		if ( FollowTarget == null ) return;

		if ( FollowTarget.WorldPosition.DistanceSquared( _previousFollowPos ) >
		     TeleportDistanceThreshold * TeleportDistanceThreshold )
		{
			Warp();
			_previousFollowPos = FollowTarget.WorldPosition;
			return;
		}
		
		WorldPosition = _positionDynamics.Update( Time.Delta, FollowTarget.WorldPosition );
		WorldRotation = _rotationDynamics.Update( Time.Delta, FollowTarget.WorldRotation );

		_previousFollowPos = FollowTarget.WorldPosition;
	}

	/// <summary>
	/// Resets the following target, useful for when you teleport or jump somewhere
	/// </summary>
	public void Warp()
	{
		_positionDynamics.Reset( FollowTarget.WorldPosition );
		_rotationDynamics.Reset( FollowTarget.WorldRotation );

		WorldPosition = FollowTarget.WorldPosition;
		WorldRotation = FollowTarget.WorldRotation;
	}

	public void UpdatePositionParameters( DynamicsParameters p )
	{
		PositionParameters = p;
		_positionDynamics.UpdateParameters( p );
	}

	public void UpdateRotationParameters( DynamicsParameters p )
	{
		RotationParameters = p;
		_rotationDynamics.UpdateParameters( p );
	}
}