NPCs/Implementation/Crackhead/AttackingState.cs
namespace Opium.AI;

public partial class AttackingState : ChaseState
{
	const float AttackRange = 64f;

	public TimeSince TimeSinceAttack = 100;

	private Actor AttackTarget { get; set; }

	public override UpperBodyState GetUpperAnimationState()
	{
		if ( TimeSinceAttack < 1f ) return UpperBodyState.Attack;
		return base.GetUpperAnimationState();
	}

	protected bool InRange()
	{
		return Agent.LastStimulus.Position.Distance( Agent.Transform.Position ) < AttackRange;
	}

	public override bool ShouldEnterState( StateMachine machine )
	{
		if ( Agent.LastStimulus is null || Agent.LastStimulus.HasExpired ) return false;
		return InRange();
	}

	public override void OnStateExit( StateMachine.State next )
	{
		if ( Agent.Weapon is MeleeWeapon melee && melee.IsValid() )
		{
			melee.TimeSinceShoot = 0;
			melee.MainAttack.State = AttackState.Inactive;
			melee.MainAttack.OnAttackReleased( false );

			melee.BlockAttack.State = AttackState.Inactive;
			melee.BlockAttack.OnAttackReleased( false );
		}
	}

	public override void Tick()
	{
		var target = Agent.FindClosestEnemyInLineOfSight( AttackRange );
		if ( target.IsValid() )
		{
			AttackTarget = target;
		}
		else
			return;

		var globalDirection = AttackTarget.Transform.Position - Agent.Transform.Position; // Global direction to the target
		var distance = globalDirection.Length;
		if ( distance > (AttackRange / 1.5f) )
		{
			var localDirection = Agent.Transform.Rotation.Inverse * globalDirection; // Convert to local space
			var direction = localDirection.Normal; // Normalize the direction
			Agent.WalkTo( AttackTarget.Transform.Position );
		}
		else
		{
			Agent.CancelWalk();
		}

		Agent.LookAt( AttackTarget );

		if ( Agent.Weapon is MeleeWeapon melee )
		{
			if ( melee.CanShoot() )
			{
				TimeSinceAttack = 0;
				melee.TimeSinceShoot = 0;
				melee.MainAttack.State = AttackState.Windup;
			}

			if ( melee.MainAttack.State == AttackState.ReadyToAttack )
			{
				melee.MainAttack.State = AttackState.Swinging;
				melee.MainAttack.OnAttackReleased();
			}
		}
	}
}