NPCs/Agent.Walk.cs
namespace Opium.AI;

partial class Agent
{
	private Vector3? _walkTarget;
	private Action _walkCallback;

	public void WalkTo( Vector3 position, Action callback = null )
	{
		_walkTarget = position;
		_walkCallback = callback;
	}

	public void CancelWalk()
	{
		WishMove = Vector3.Zero; // Stop moving if there's no target/path
		_walkCallback?.Invoke();
		_walkCallback = null;
		_walkTarget = null;
	}

	private void UpdateWalk()
	{
		if ( _walkTarget == null )
		{
			CancelWalk();
			return;
		}

		var path = GetPath( _walkTarget.Value! );
		var targetIndex = 0;

		for ( int i = 0; i < path.Count; ++i )
		{
			var pDist = path[i].Distance( Transform.Position );
			var pDistRaised = path[i].WithZ( Transform.Position.z ).Distance( Transform.Position );
			var zDist = MathF.Abs( path[i].z - Transform.Position.z );

			if ( pDist > 4f )
			{
				if ( pDistRaised < 8 && zDist < 32 )
					continue;

				targetIndex = i;
				break;
			}
		}

		for ( int i = targetIndex; i < path.Count - 1; ++i )
		{
			var a = path[i];
			var b = path[i + 1];

			if ( StateMachine.DebugEnabled )
			{
				Gizmo.Draw.IgnoreDepth = true;
				Gizmo.Draw.Line( a, b );
				Gizmo.Draw.LineSphere( a, 4f );
			}
		}

		if ( path.Count > 0 ) // Make sure the path is not empty
		{
			var target = path[targetIndex];
			var globalDirection = (target - Transform.Position); // Global direction to the target
			var distance = globalDirection.Length;

			if ( distance > 0 ) // Make sure we're not already at the target
			{
				var localDirection = Transform.Rotation.Inverse * globalDirection; // Convert to local space
				var direction = localDirection.Normal; // Normalize the direction

				LookAt( target, 3f ); // Ensure the NPC looks at the target
				WishMove = direction; // Move towards the target in local space

				CheckForDoor( Transform.Position, target );

				if ( StateMachine.DebugEnabled )
				{
					Gizmo.Draw.IgnoreDepth = true;
					Gizmo.Draw.Color = Color.Green;
					Gizmo.Draw.Arrow( Transform.Position, Transform.Position + (Transform.Rotation * localDirection) );
				}
			}
			else
			{
				CancelWalk();
			}
		}
		else
		{
		}
	}
}