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();
}
}
}
}