AI/ActionSystem/Actions/Guest/PickpocketAction.cs
using Sandbox.Utility;

namespace HC3;

public sealed class PickpocketAction : AgentAction
{
	[Sync( SyncFlags.FromHost )]
	private Guest Victim { get; set; }

	private Guest Guest => Agent as Guest;

	public override float Score()
	{
		if ( !Guest.IsValid() || Guest.Delinquency < 0.7f ) return 0f;
		if ( Victim.IsValid() ) return 300f;

		// Pick random victim without allocating a filtered array (reservoir sampling)
		Guest candidate = null;
		int count = 0;
		foreach ( var g in GuestManager.Instance.GuestList )
		{
			if ( g == Guest ) continue;
			count++;
			if ( Game.Random.Next( count ) == 0 )
				candidate = g;
		}
		if ( count == 0 ) return 0f;

		Victim = candidate;
		return Victim.IsValid() ? 300f : 0f;
	}

	public override void StartAction()
	{
		Controller.ClearNavigation();
	}

	public override Status TickAction()
	{
		if ( !Victim.IsValid() )
			return Status.Failure;

		var targetPos = Victim.WorldPosition;

		if ( !GridManager.IsWalkable( Agent.WorldPosition, targetPos ) )
		{
			Victim = null;
			return Status.Failure;
		}

		if ( !Controller.Navigate( targetPos ) )
			return Status.Running;

		var moneyToTake = (Victim.Money * Game.Random.Float( 0.2f, 0.5f )).CeilToInt();

		Guest.CommitCrime( 0.2f, Victim );

		Victim.TakeMoney( moneyToTake );
		Victim = null;

		Guest.GiveMoney( moneyToTake );

		return Status.Success;
	}

	public override void StopAction()
	{
		Victim = null;
	}

	public override ActionDisplayInfo? GetDisplay()
	{
		if ( !Victim.IsValid() ) return null;

		var dist = Agent.WorldPosition.Distance( Victim.WorldPosition );
		var progress = 1f - (dist / 1500f).Clamp( 0f, 1f );

		return new ActionDisplayInfo(
			"visibility_off",
			$"Pickpocket",
			progress
		);
	}
}