NPCs/Implementation/Base/PatrolState.cs
namespace Opium.AI;
public partial class PatrolState : StateMachine.State
{
[Property] public PatrolPath Path { get; set; }
[Property, Range( 0, 30, 0.5f )] public RangedFloat PatrolWaitDelay { get; set; } = 0f;
[Property, Range( 0.5f, 1.5f, 0.05f )] public RangedFloat WalkSpeedMultiplier { get; set; } = 0.7f;
/// <summary>
/// While we're waiting to walk again, how long?
/// </summary>
TimeUntil TimeUntilNextWalk = 5;
/// <summary>
/// Are we actively patrolling right now? False = Waiting for <see cref="TimeUntilNextWalk"/>
/// </summary>
bool isWalking = false;
/// <summary>
/// The current index for the path list.
/// </summary>
private int _currentIndex;
public override float RequiredEntryScore => 10f;
public override float EntryScore
{
get
{
// If we don't have a path, we do NOT want to enter.
if ( Path is null ) return 0f;
// Nothing important happened recently, so lets go back to this if we can
if ( Agent.LastStimulus is null ) return RequiredEntryScore;
if ( Agent.LastStimulus.HasExpired ) return RequiredEntryScore;
return 0f;
}
}
public override void OnStateEnter( StateMachine.State prev )
{
base.OnStateEnter( prev );
OnWalkFinished();
}
float CurrentWalkSpeed = 0.75f;
public override float? GetWishSpeed()
{
return CurrentWalkSpeed;
}
public override void Tick()
{
if ( !isWalking && TimeUntilNextWalk )
{
Walk();
}
}
/// <summary>
/// Called when the Agent is done walking to a destination.
/// </summary>
void OnWalkFinished()
{
isWalking = false;
TimeUntilNextWalk = PatrolWaitDelay.GetValue();
CurrentWalkSpeed = WalkSpeedMultiplier.GetValue();
}
private void Walk()
{
_currentIndex++;
_currentIndex %= Path.Count;
var localPos = Path[_currentIndex];
var worldPos = Path.Transform.World.PointToWorld( localPos );
isWalking = true;
Agent.WalkTo( worldPos, OnWalkFinished );
}
}