AI/ActionSystem/Actions/LoiterAction.cs
namespace HC3;

public sealed class LoiterAction : BehaviorTreeAction
{
	private TimeSince _timeSinceLastEnded;
	private bool _isLoitering;
	private float _duration;

	private Building _targetBuilding;
	private Vector3 _lookTarget;

	[Property] public RangedFloat LoiterDuration { get; set; } = new RangedFloat( 5f, 15f );
	[Property] public float LoiterFrequency { get; set; } = 60f;

	public override float Score()
	{
		if ( !ParkManager.Instance?.IsOpen() ?? false )
			return 0f;

		if ( Controller.IsOnStairs ) return 0;

		if ( _isLoitering )
			return 200f;

		return _timeSinceLastEnded > LoiterFrequency ? 50f : 0f;
	}

	protected override void OnStart()
	{
		_timeSinceLastEnded = 0;
	}

	protected override void OnTreeStart()
	{
		_duration = Game.Random.Float( LoiterDuration.Min, LoiterDuration.Max );
		_isLoitering = true;

		PickNearbyBuilding();
	}

	protected override void OnTreeStop()
	{
		if ( _isLoitering )
		{
			_timeSinceLastEnded = 0f;
			_isLoitering = false;
		}
	}

	protected override Node BuildTree()
	{
		if ( _targetBuilding.IsValid() )
		{
			return new SequenceNode( new List<Node>()
			{
				new MoveToNode( Agent, _lookTarget, $"Going to look at {_targetBuilding.Title}" ),
				new LookAtNode( Agent, _targetBuilding.WorldPosition ),
				new DelayNode( _duration, $"Having a look at {_targetBuilding.Title}" )
			} );
		}
		else
		{
			return new DelayNode( _duration, "Loitering" );
		}
	}

	private void PickNearbyBuilding()
	{
		_targetBuilding = null;

		// Pick a random walkable ride without LINQ allocation
		BasicRide pick = null;
		int count = 0;
		foreach ( var b in Building.ActiveBuildings )
		{
			if ( b is not BasicRide ride || !ride.IsValid() )
				continue;
			if ( !GridManager.IsWalkable( Agent.WorldPosition, ride.GetEntrance() ) )
				continue;

			count++;
			if ( Game.Random.Next( count ) == 0 )
				pick = ride;
		}

		if ( pick is null )
			return;

		var entrance = pick.GetQueueEnd()?.Connections.OfType<Path>().FirstOrDefault( x => x.PathType == PathType.Path );
		if ( entrance is not null )
		{
			_targetBuilding = pick;
			_lookTarget = entrance.WorldPosition;
		}
	}
}