AI/ActionSystem/Actions/Staff/ArrestSuspectAction.cs
namespace HC3;
public sealed class ArrestSuspectAction : AgentAction
{
private enum ArrestStage
{
Chasing,
Escorting
}
[Sync( SyncFlags.FromHost )]
private Guest Suspect { get; set; }
private Staff Staff => Agent as Staff;
private ArrestStage Stage { get; set; }
public override float Score()
{
if ( !Staff.IsValid() ) return 0f;
if ( Suspect.IsValid() ) return 999f;
// TODO: maybe we only try to arrest guests who have a high suspicion level
// that we can see, or are in a radius to us. For now just pick one.
// Pick random suspect without allocating filtered array
Guest candidate = null;
int count = 0;
foreach ( var x in Scene.GetAll<Guest>() )
{
if ( x.Building.IsValid() ) continue;
if ( x.IsArrested || x.IsPursued ) continue;
if ( x.SuspicionLevel < Guest.ArrestSuspicionThreshold ) continue;
count++;
if ( Game.Random.Next( count ) == 0 )
candidate = x;
}
Suspect = candidate;
return Suspect.IsValid() ? 999f : 0f;
}
public override void StartAction()
{
Staff.Controller.IsRunning = true;
Stage = ArrestStage.Chasing;
if ( Suspect.IsValid() )
Suspect.IsPursued = true;
Controller.ClearNavigation();
}
public override Status TickAction()
{
if ( !Suspect.IsValid() )
return Status.Failure;
if ( Suspect.GameObject.Parent is not Sandbox.Scene )
return Status.Failure;
var targetPosition = Suspect.WorldPosition;
if ( Stage == ArrestStage.Escorting )
{
var entrance = Scene.GetAll<ParkEntrance>().FirstOrDefault();
targetPosition = entrance.WorldPosition + entrance.WorldRotation.Forward * GridManager.GridSize;
}
if ( !GridManager.IsWalkable( Agent.WorldPosition, targetPosition ) )
return Status.Running;
var flags = NavFlags.Default.WithFlag( NavFlags.Owned, false );
if ( !Controller.Navigate( targetPosition, flags ) )
return Status.Running;
if ( Stage == ArrestStage.Chasing )
{
Staff.Controller.IsRunning = false;
Suspect.IsArrested = true;
Stage = ArrestStage.Escorting;
return Status.Running;
}
Staff.IncreaseStat( StaffStats.GuestsArrested );
Stats.Increment( "staff.arrests_made" );
Suspect = null;
return Status.Success;
}
public override void StopAction()
{
Staff.Controller.IsRunning = false;
if ( Suspect.IsValid() )
Suspect.IsPursued = false;
Suspect = null;
}
public override ActionDisplayInfo? GetDisplay()
{
if ( Suspect == null ) return null;
var targetPosition = Suspect.WorldPosition;
if ( Stage == ArrestStage.Escorting )
{
var entrance = Scene.GetAll<ParkEntrance>().FirstOrDefault();
targetPosition = entrance.WorldPosition + entrance.WorldRotation.Forward * GridManager.GridSize;
}
var distance = Agent.WorldPosition.Distance( targetPosition );
var progress = 1f - (distance / 1500f).Clamp( 0f, 1f );
return new ActionDisplayInfo(
"security",
$"Arresting {Suspect.FullName}",
progress
);
}
}