AI/Guests/Guest.Crime.cs
using System;

namespace HC3;

public partial class Guest
{
	/// <summary>
	/// How much suspicion a guest must have for security to arrest them.
	/// </summary>
	public const float ArrestSuspicionThreshold = 0.8f;

	/// <summary>
	/// The maximum range from which a crime can be witnessed.
	/// </summary>
	public const float MaxCrimeWitnessRange = 600f * 600f;

	/// <summary>
	/// How suspicious this guest is. The higher the suspicion, the more likely
	/// they are to be caught by security.
	/// </summary>
	public float SuspicionLevel { get; internal set; }

	/// <summary>
	/// Is this guest being persued by a member of staff?
	/// </summary>
	public bool IsPursued { get; set; }

	/// <summary>
	/// Has this guest been arrested? In which case they should leave the park.
	/// </summary>
	public bool IsArrested { get; set; }

	/// <summary>
	/// Log that this guest has committed a crime. Guests in the vicinity may
	/// notice and report the crime.
	/// </summary>
	public void CommitCrime( float suspicionGain, Guest victim = null )
	{
		var allStaff = Scene.GetAll<Staff>();
		var allGuests = Scene.GetAll<Guest>().Where( x => x != this && x != victim );

		foreach ( var staff in allStaff )
		{
			var distance = staff.WorldPosition.DistanceSquared( WorldPosition );
			if ( distance > MaxCrimeWitnessRange ) continue;

			var witnessChance = Math.Clamp( 1f - distance / MaxCrimeWitnessRange, 0f, 1f );

			if ( Game.Random.Float() >= witnessChance )
				continue;

			SuspicionLevel += suspicionGain * Game.Random.Float( 0.5f, 1.2f ) * staff.Alertness;
			SuspicionLevel = SuspicionLevel.Clamp( 0f, 1f );
		}

		foreach ( var guest in allGuests )
		{
			var distance = guest.WorldPosition.DistanceSquared( WorldPosition );
			if ( distance > MaxCrimeWitnessRange ) continue;

			var witnessChance = Math.Clamp( 1f - distance / MaxCrimeWitnessRange, 0f, 1f );

			if ( Game.Random.Float() >= witnessChance )
				continue;

			SuspicionLevel += suspicionGain * Game.Random.Float( 0.5f, 1.2f );
			SuspicionLevel = SuspicionLevel.Clamp( 0f, 1f );
		}
	}

	private void DecaySuspicionLevel()
	{
		// Very slowly decay our suspicion if we haven't been caught yet.
		SuspicionLevel = MathF.Max( 0f, SuspicionLevel - Time.Delta * 0.01f );
	}
}