AI/ActionSystem/Actions/WanderAction.cs
namespace HC3;
public sealed class WanderAction : AgentAction
{
public override float Score()
{
return 1f;
}
public override void StartAction()
{
base.StartAction();
Agent.Controller.IsRunning = false;
NavFlags flags = NavFlags.Default;
if ( TryFindRoamPoint( out Vector3 goal, flags ) )
{
Controller.Navigate( goal, flags );
}
}
public override Status TickAction()
{
return Controller.IsNavigating ? Status.Running : Status.Success;
}
public bool TryFindRoamPoint( out Vector3 location, NavFlags flags )
{
location = Vector3.Zero;
var gridPos = GridManager.WorldToGridPosition3D( WorldPosition );
int regionId = GridManager.GetRegion( gridPos );
var walkableCells = GridNavigation.Instance.GetNavablePaths( regionId, flags );
if ( !walkableCells.Any() )
return false;
// Sample random candidates and pick the farthest one, no LINQ allocations
Vector3Int? farthestCell = null;
float farthestDist = -1f;
int sampled = 0;
foreach ( var pos in walkableCells )
{
if ( !IsRoamable( pos ) )
continue;
// Reservoir sampling: keep up to 20 random candidates
if ( sampled < 20 || Random.Next( sampled + 1 ) < 20 )
{
float dist = (pos - gridPos).Length;
if ( dist > farthestDist )
{
farthestDist = dist;
farthestCell = pos;
}
}
sampled++;
}
if ( farthestCell is null )
return false;
location = GridManager.GridToWorldPosition( farthestCell.Value ) + GridManager.CentreOffset;
return true;
}
private static bool IsRoamable( Vector3Int gridPos )
{
var cell = GridManager.Instance?.GetCell( new Vector2Int( gridPos.x, gridPos.y ) );
if ( cell == null ) return false;
if ( cell.HasComponent<RideEntranceExit>() ) return false;
if ( cell.HasComponent<Queue>( q => q.Ride.IsValid() ) ) return false;
return true;
}
public override ActionDisplayInfo? GetDisplay()
{
return new ActionDisplayInfo(
"explore_nearby",
"Wandering around", 0
);
}
}