Decompiled source of PoliceResponseOverhaul Mono v1.1.4

PoliceResponseOverhaul-Mono.dll

Decompiled 7 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using FishNet;
using FishNet.Connection;
using FishNet.Managing;
using FishNet.Object;
using HarmonyLib;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using PoliceResponseOverhaul;
using PoliceResponseOverhaul.Ambient;
using PoliceResponseOverhaul.Armor;
using PoliceResponseOverhaul.Authority;
using PoliceResponseOverhaul.Cartel;
using PoliceResponseOverhaul.Compat;
using PoliceResponseOverhaul.Config;
using PoliceResponseOverhaul.Heat;
using PoliceResponseOverhaul.Helpers;
using PoliceResponseOverhaul.Loadout;
using PoliceResponseOverhaul.Patches;
using PoliceResponseOverhaul.Pooling;
using PoliceResponseOverhaul.Recovery;
using PoliceResponseOverhaul.Response;
using PoliceResponseOverhaul.World;
using ScheduleOne;
using ScheduleOne.AvatarFramework;
using ScheduleOne.AvatarFramework.Equipping;
using ScheduleOne.Cartel;
using ScheduleOne.Clothing;
using ScheduleOne.Combat;
using ScheduleOne.Core.Items.Framework;
using ScheduleOne.DevUtilities;
using ScheduleOne.Doors;
using ScheduleOne.GameTime;
using ScheduleOne.ItemFramework;
using ScheduleOne.Law;
using ScheduleOne.Map;
using ScheduleOne.NPCs;
using ScheduleOne.NPCs.Behaviour;
using ScheduleOne.PlayerScripts;
using ScheduleOne.PlayerScripts.Health;
using ScheduleOne.Police;
using ScheduleOne.UI.Shop;
using ScheduleOne.Vehicles;
using ScheduleOne.Vehicles.AI;
using ScheduleOne.VoiceOver;
using TMPro;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: MelonInfo(typeof(PoliceResponseOverhaulMod), "Police Response Overhaul", "1.1.4", "UncleTyrone", null)]
[assembly: MelonColor(1, 0, 0, 255)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: MelonOptionalDependencies(new string[] { "MoreGuns", "K9 Patrol", "K9_Patrol", "K9Patrol", "SteamNetworkLib", "SteamNetworkLib-Mono", "SteamNetworkLib-Il2Cpp" })]
[assembly: HarmonyDontPatchAll]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("PoliceResponseOverhaul-Mono")]
[assembly: AssemblyConfiguration("Release Mono")]
[assembly: AssemblyFileVersion("1.1.4.0")]
[assembly: AssemblyInformationalVersion("1.1.4+6dcb88777ca9461846a840d9cc6732ac029eeeec")]
[assembly: AssemblyProduct("PoliceResponseOverhaul-Mono")]
[assembly: AssemblyTitle("PoliceResponseOverhaul-Mono")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.4.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
internal static class ModVersion
{
	public const string Current = "1.1.4";
}
namespace PoliceResponseOverhaul
{
	public static class BuildInfo
	{
		public const string Name = "Police Response Overhaul";

		public const string Description = "Host-authoritative police response overhaul for Schedule I.";

		public const string Author = "UncleTyrone";

		public const string Version = "1.1.4";
	}
	public class PoliceResponseOverhaulMod : MelonMod
	{
		private static Instance Logger;

		private Harmony _harmony;

		private bool _harmonyApplied;

		private bool _systemsStarted;

		private bool _bootStarted;

		public override void OnInitializeMelon()
		{
			Logger = ((MelonBase)this).LoggerInstance;
			ModConfig.Initialize();
			RoutesConfig.EnsureLoaded();
			Logger.Msg("Police Response Overhaul loaded. Uninstall EvenMoreFootPatrols DLLs.");
		}

		public override void OnDeinitializeMelon()
		{
			SteamLobbySync.Shutdown();
			PatchGate.Close();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			_harmonyApplied = false;
			_bootStarted = false;
		}

		public override void OnSceneWasLoaded(int buildIndex, string sceneName)
		{
			if (sceneName != "Main")
			{
				_systemsStarted = false;
				_bootStarted = false;
				return;
			}
			PatchGate.Close();
			HeatTracker.ResetSession();
			OfficerPool.Clear();
			ResponseDispatcher.Reset();
			FinalResponse.Reset();
			AmbientLaw.Reset();
			BodyArmor.Reset();
			LoadoutService.Reset();
			K9Compat.Reset();
			SteamLobbySync.Reset();
			if (!_bootStarted)
			{
				_bootStarted = true;
				MelonCoroutines.Start(BootAfterSaveSettles());
			}
		}

		public override void OnSceneWasUnloaded(int buildIndex, string sceneName)
		{
			if (sceneName == "Main")
			{
				PatchGate.Close();
				_systemsStarted = false;
				_bootStarted = false;
			}
		}

		private IEnumerator BootAfterSaveSettles()
		{
			while (!InstanceFinder.IsServer && !InstanceFinder.IsClient)
			{
				yield return null;
			}
			float waited = 0f;
			while ((Object)(object)GameObject.Find("Player_Local") == (Object)null && waited < 90f)
			{
				yield return (object)new WaitForSeconds(0.25f);
				waited += 0.25f;
			}
			yield return (object)new WaitForSecondsRealtime(8f);
			if (!_harmonyApplied)
			{
				if (_harmony == null)
				{
					_harmony = new Harmony("com.uncletyrone.policeresponseoverhaul");
				}
				Logger.Msg("Applying Harmony patches after save settle (batched + gated)...");
				yield return CorePatches.ApplyBatched(_harmony, Logger);
				_harmonyApplied = true;
			}
			else if (!PatchGate.IsLive)
			{
				yield return (object)new WaitForSecondsRealtime(1f);
				PatchGate.Open();
			}
			yield return StartSystems();
		}

		public override void OnUpdate()
		{
			if (_systemsStarted && ModConfig.Enabled.Value)
			{
				if (ModConfig.F6WaypointHelper.Value && Input.GetKeyDown((KeyCode)287))
				{
					LogPlayerCoordinates();
				}
				SteamLobbySync.Tick();
				if (NetworkAuthority.IsAuthoritative)
				{
					HeatTracker.Tick();
					AmbientLaw.Tick();
					ResponseDispatcher.Tick();
					FinalResponse.Tick();
					StuckRecovery.Tick();
					K9Compat.Tick();
				}
			}
		}

		public override void OnGUI()
		{
			HeatHud.OnGUI();
		}

		private IEnumerator StartSystems()
		{
			if (NetworkAuthority.IsClientOnly)
			{
				Logger.Msg("Client-only: host is authoritative. Responses will be replicated by the game.");
				yield return (object)new WaitForSeconds(1f);
				BodyArmor.OnRegistryReady();
				_systemsStarted = true;
			}
			else
			{
				Logger.Msg("Starting host police-response systems.");
				yield return (object)new WaitForSeconds(1f);
				BodyArmor.OnRegistryReady();
				_systemsStarted = true;
			}
		}

		private static void LogPlayerCoordinates()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = GameObject.Find("Player_Local");
			if ((Object)(object)val == (Object)null)
			{
				MelonLogger.Msg("Player_Local is missing, cannot fetch coordinates.");
				return;
			}
			Vector3 position = val.transform.position;
			MelonLogger.Msg($"{{ \"x\": {position.x}, \"y\": {position.y}, \"z\": {position.z} }}");
		}
	}
}
namespace PoliceResponseOverhaul.World
{
	public static class WorldHooks
	{
		public static void OnResponseDispatched(ResponsePersonality personality)
		{
			if (ModConfig.PoliceChatter.Value)
			{
				PlayChatter();
			}
			if (ModConfig.HardCurfewOnPanic.Value && HeatTracker.Stars >= 4)
			{
				TryHardCurfew();
			}
		}

		public static void OnPoliceCalled()
		{
			if (ModConfig.CivilianRadio.Value && HeatTracker.CurrentTier >= HeatTier.Heat && !ResponseDispatcher.HasActiveResponse)
			{
				ResponseDispatcher.ForceDispatch();
			}
		}

		public static void ApplyBodySearchScaling(PoliceOfficer officer)
		{
			if (ModConfig.BodySearchScaling.Value && !((Object)(object)officer == (Object)null))
			{
				officer.BodySearchChance = ((HeatTracker.CurrentTier >= HeatTier.Heat) ? 1f : 0.65f);
				officer.BodySearchDuration = ((HeatTracker.CurrentTier >= HeatTier.Surge) ? 4f : 6f);
			}
		}

		private static void PlayChatter()
		{
			try
			{
				PoliceChatterVO val = Object.FindObjectOfType<PoliceChatterVO>(true);
				if (val != null)
				{
					val.PlayChatter();
				}
			}
			catch
			{
			}
		}

		private static void TryHardCurfew()
		{
			try
			{
				CurfewManager val = Object.FindObjectOfType<CurfewManager>(true);
				if ((Object)(object)val != (Object)null)
				{
					GameAccess.Call(val, "SetCurfewModeEnabled", true);
				}
			}
			catch
			{
			}
		}
	}
	public static class HeatHud
	{
		private const float FlashHertz = 2.4f;

		private static GUIStyle _style;

		private static bool _loggedDrawError;

		public static void OnGUI()
		{
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Expected O, but got Unknown
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			if (!ModConfig.Enabled.Value || !ModConfig.HeatHud.Value || !InMainGame())
			{
				return;
			}
			try
			{
				Player player = HeatTracker.LocalPlayer();
				int num = HeatTracker.StarsFor(player);
				if (num > 0)
				{
					int num2;
					switch (HeatTracker.PhaseFor(player))
					{
					case WantedPhase.Clear:
						return;
					case WantedPhase.Searching:
						num2 = ((Time.unscaledTime * 2.4f % 1f < 0.55f) ? 1 : 0);
						break;
					default:
						num2 = 1;
						break;
					}
					bool flag = (byte)num2 != 0;
					string text = StarText(num);
					GUIStyle val = LabelStyle();
					Color val2 = default(Color);
					((Color)(ref val2))..ctor(1f, 0.78f, 0.12f, 1f);
					val.normal.textColor = (Color)((!flag) ? new Color(1f, 1f, 1f, 0f) : ((num >= 6) ? val2 : Color.white));
					Vector2 val3 = val.CalcSize(new GUIContent(text));
					float num3 = 12f;
					float num4 = (float)Screen.width - val3.x - 28f - num3;
					float num5 = 10f;
					GUI.Label(new Rect(num4, num5, val3.x + num3 * 2f, val3.y + 10f), text, val);
				}
			}
			catch (Exception ex)
			{
				if (!_loggedDrawError)
				{
					_loggedDrawError = true;
					MelonLogger.Warning("Wanted HUD draw failed: " + ex.Message);
				}
			}
		}

		private static string StarText(int stars)
		{
			int num = ((stars >= 6) ? 6 : 5);
			int num2 = Mathf.Clamp(stars, 0, num);
			return new string('★', num2) + new string('☆', num - num2);
		}

		private static GUIStyle LabelStyle()
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected O, but got Unknown
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			if (_style != null)
			{
				return _style;
			}
			_style = new GUIStyle((GUIStyle)(((Object)(object)GUI.skin != (Object)null) ? ((object)GUI.skin.label) : ((object)new GUIStyle())))
			{
				fontSize = 39,
				fontStyle = (FontStyle)1,
				alignment = (TextAnchor)2,
				richText = false
			};
			_style.normal.textColor = Color.white;
			return _style;
		}

		private static bool InMainGame()
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Scene activeScene = SceneManager.GetActiveScene();
				if (!((Scene)(ref activeScene)).IsValid())
				{
					return false;
				}
				string text = ((Scene)(ref activeScene)).name ?? string.Empty;
				if (text.IndexOf("menu", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return false;
				}
				if (text.IndexOf("lobby", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return false;
				}
				if (text.IndexOf("boot", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return false;
				}
				return text.IndexOf("main", StringComparison.OrdinalIgnoreCase) >= 0 || text.Length > 0;
			}
			catch
			{
				return true;
			}
		}
	}
	public static class OverhaulCommands
	{
		public static bool TryHandle(string raw)
		{
			if (string.IsNullOrWhiteSpace(raw))
			{
				return false;
			}
			string[] array = raw.Trim().Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
			if (array.Length == 0 || !array[0].Equals("overhaul", StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			switch ((array.Length > 1) ? array[1].ToLowerInvariant() : "help")
			{
			case "heat":
			case "wanted":
			{
				Player player = HeatTracker.LocalPlayer();
				MelonLogger.Msg($"Wanted {HeatTracker.StarsFor(player)}★ ({HeatTracker.PhaseFor(player)}) session={HeatTracker.StarLabel} daily={HeatTracker.DailyKills} streak={HeatTracker.Streak}");
				break;
			}
			case "reset":
				HeatTracker.ResetSession();
				MelonLogger.Msg("Wanted level cleared.");
				break;
			case "stars":
			{
				int result2;
				int num = ((array.Length > 2 && int.TryParse(array[2], out result2)) ? result2 : 3);
				HeatTracker.SetStars(num, "command");
				if (num > 0)
				{
					ResponseDispatcher.ForceDispatch();
				}
				MelonLogger.Msg("Forced wanted " + HeatTracker.StarLabel + ".");
				break;
			}
			case "alert":
				HeatTracker.SetStars(1, "command");
				ResponseDispatcher.ForceDispatch();
				MelonLogger.Msg("Forced 1★ response.");
				break;
			case "surge":
				HeatTracker.SetStars(3, "command");
				ResponseDispatcher.ForceDispatch();
				MelonLogger.Msg("Forced 3★ response.");
				break;
			case "panic":
				HeatTracker.SetStars(4, "command");
				ResponseDispatcher.ForceDispatch();
				MelonLogger.Msg("Forced 4★ response.");
				break;
			case "manhunt":
				HeatTracker.SetStars(5, "command");
				ResponseDispatcher.ForceDispatch();
				MelonLogger.Msg("Forced 5★ Special Response.");
				break;
			case "juggernaut":
			case "lockdown":
				HeatTracker.SetStars(6, "command");
				ResponseDispatcher.ForceDispatch();
				MelonLogger.Msg("Forced 6★ juggernaut squad.");
				break;
			case "dispatch":
				ResponseDispatcher.ForceDispatch();
				MelonLogger.Msg("Forced vehicle response.");
				break;
			case "final":
			{
				int result;
				int tier = ((array.Length <= 2 || !int.TryParse(array[2], out result)) ? 1 : result);
				FinalResponse.ForceTier(tier);
				break;
			}
			case "armor":
				BodyArmor.GiveToLocal();
				break;
			default:
				MelonLogger.Msg("overhaul wanted|reset|stars [0-6]|alert|surge|panic|manhunt|juggernaut|dispatch|final [tier]|armor");
				break;
			}
			return true;
		}
	}
}
namespace PoliceResponseOverhaul.Response
{
	public static class JuggernautSquad
	{
		public const int MaxJuggernauts = 4;

		public const int SquadSize = 4;

		private static readonly HashSet<int> Live = new HashSet<int>();

		private static bool _rpgClaimedThisWave;

		public static int WaveSize { get; private set; } = 1;

		public static bool Enabled => ModConfig.JuggernautsEnabled != null && ModConfig.JuggernautsEnabled.Value;

		public static void Reset()
		{
			Live.Clear();
			WaveSize = 1;
			_rpgClaimedThisWave = false;
		}

		public static void BeginWaveOutfit()
		{
			_rpgClaimedThisWave = false;
		}

		public static bool ClaimRpgSlot()
		{
			if (WaveSize < 4 || _rpgClaimedThisWave)
			{
				return false;
			}
			_rpgClaimedThisWave = true;
			return true;
		}

		public static bool IsJuggernaut(PoliceOfficer officer)
		{
			return (Object)(object)officer != (Object)null && Live.Contains(((Object)officer).GetInstanceID());
		}

		public static void Mark(PoliceOfficer officer)
		{
			if (!((Object)(object)officer == (Object)null))
			{
				Live.Add(((Object)officer).GetInstanceID());
			}
		}

		public static void OnOfficerKilled(PoliceOfficer officer)
		{
			if (Enabled && !((Object)(object)officer == (Object)null))
			{
				int instanceID = ((Object)officer).GetInstanceID();
				if (Live.Remove(instanceID) && HeatTracker.Stars >= 6)
				{
					WaveSize = Mathf.Min(4, WaveSize + 1);
				}
			}
		}

		public static int CrewWanted()
		{
			return 4;
		}

		public static int JuggernautsThisWave()
		{
			return Mathf.Clamp(WaveSize, 1, 4);
		}
	}
	public class PendingResponse
	{
		public int Cars;

		public int Foot;

		public ResponsePersonality Personality;

		public LoadoutTheme Theme;

		public float Delay;

		public Vector3 Origin;

		public Player Target;

		public bool IsDelayedWave;

		public bool IsLateWave;
	}
	public static class ResponseDispatcher
	{
		private static readonly Queue<PendingResponse> Queue = new Queue<PendingResponse>();

		private static readonly List<PoliceOfficer> ActiveCrew = new List<PoliceOfficer>();

		private static readonly List<LandVehicle> ActiveVehicles = new List<LandVehicle>();

		private static readonly Dictionary<int, int> VehicleChaseSlot = new Dictionary<int, int>();

		private static readonly Dictionary<int, float> CorpseSince = new Dictionary<int, float>();

		private const float CorpseLingerSeconds = 75f;

		private static int _deathsTowardResponse;

		private static bool _running;

		private static float _lastDispatchTime;

		private static float _nextChaseRefresh;

		private static float _nextVehicleReclaim;

		private static float _nextNearbyAlert;

		private static readonly Dictionary<int, float> VehicleDeployedAt = new Dictionary<int, float>();

		private static readonly Dictionary<int, HashSet<int>> VehicleCrewIds = new Dictionary<int, HashSet<int>>();

		private static readonly HashSet<int> LocalSupport = new HashSet<int>();

		public static bool HasActiveResponse => CountLiveResponseCars() > 0 || CountLiveCrew() > 0 || Queue.Count > 0 || _running;

		public static void Reset()
		{
			Queue.Clear();
			ActiveCrew.Clear();
			ActiveVehicles.Clear();
			VehicleChaseSlot.Clear();
			VehicleDeployedAt.Clear();
			VehicleCrewIds.Clear();
			ClearLocalSupport();
			CorpseSince.Clear();
			_deathsTowardResponse = 0;
			_running = false;
			JuggernautSquad.Reset();
		}

		public static void StandDown()
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			Queue.Clear();
			_running = false;
			_deathsTowardResponse = 0;
			PoliceOfficer[] array = ActiveCrew.ToArray();
			foreach (PoliceOfficer val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					OfficerPool.Release(val);
				}
			}
			ActiveCrew.Clear();
			ClearLocalSupport();
			CorpseSince.Clear();
			PoliceStation closestStation = OfficerPool.GetClosestStation(Vector3.zero);
			LandVehicle[] array2 = ActiveVehicles.ToArray();
			foreach (LandVehicle val2 in array2)
			{
				if (!((Object)(object)val2 == (Object)null))
				{
					int instanceID = ((Object)val2).GetInstanceID();
					VehicleChaseSlot.Remove(instanceID);
					VehicleDeployedAt.Remove(instanceID);
					VehicleCrewIds.Remove(instanceID);
					OfficerPool.ReturnVehicle(closestStation, val2);
				}
			}
			ActiveVehicles.Clear();
			OfficerPool.RecoverStrandedVehicles(null, force: true);
			JuggernautSquad.Reset();
		}

		public static void OnOfficerKilled(PoliceOfficer officer)
		{
			if (NetworkAuthority.IsAuthoritative && ModConfig.Enabled.Value)
			{
				OfficerPool.FreeLease(officer);
				_deathsTowardResponse++;
				if (_deathsTowardResponse >= ModConfig.DeathsPerVehicleResponse.Value)
				{
					_deathsTowardResponse = 0;
					QueueFor(officer);
				}
			}
		}

		public static void OnOfficerDown(PoliceOfficer officer)
		{
			OfficerPool.FreeLease(officer);
		}

		public static void ForceDispatch(HeatTier? tier = null)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			Player val = ResolveTarget();
			if (!((Object)(object)val == (Object)null))
			{
				Enqueue(BuildPending(((Component)val).transform.position, val, tier ?? HeatTracker.CurrentTier, delayed: false, late: false, forceCars: true));
			}
		}

		public static void Tick()
		{
			if (!NetworkAuthority.IsAuthoritative)
			{
				return;
			}
			ReapFinishedCrew();
			ReapLocalSupport();
			EnsureActiveVehiclesVisible();
			if (!HeatTracker.IgnoreWanted)
			{
				AlertNearbyUnits();
				RefreshVehicleChases();
				if (!_running && Queue.Count > 0)
				{
					MelonCoroutines.Start(ProcessQueue());
				}
			}
		}

		private static void QueueFor(PoliceOfficer officer)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			Player val = ResolveTarget(officer);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			Vector3 origin = (((Object)(object)officer != (Object)null) ? ((Component)officer).transform.position : ((Component)val).transform.position);
			HeatTier currentTier = HeatTracker.CurrentTier;
			int num = CountLiveCrew();
			if (num >= 2)
			{
				EnsureSinglePendingRefill(origin, val, currentTier);
				return;
			}
			bool flag = num <= 0 && Time.unscaledTime - _lastDispatchTime < 90f;
			Enqueue(BuildPending(origin, val, currentTier, delayed: false, late: false, flag || WantsVehicleChase(val)));
			if (num <= 1 && !QueueHasFollowUp() && HeatTracker.Stars >= 2 && Random.value < Mathf.Min(0.55f, ModConfig.DelayedWaveChance.Value))
			{
				Enqueue(BuildPending(origin, val, HeatTracker.CurrentTier, delayed: true, late: false, forceCars: true));
			}
		}

		private static void EnsureSinglePendingRefill(Vector3 origin, Player target, HeatTier tier)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			foreach (PendingResponse item in Queue)
			{
				if (item == null)
				{
					continue;
				}
				item.Origin = origin;
				if ((Object)(object)target != (Object)null)
				{
					item.Target = target;
				}
				UpgradePendingToContext(item, target, tier, forceCars: true);
				return;
			}
			PendingResponse pendingResponse = BuildPending(origin, target, tier, delayed: true, late: false, forceCars: true);
			pendingResponse.Delay = Mathf.Max(pendingResponse.Delay, 4f);
			Enqueue(pendingResponse);
		}

		private static void UpgradePendingToContext(PendingResponse pending, Player target, HeatTier tier, bool forceCars)
		{
			if (pending == null)
			{
				return;
			}
			bool flag = forceCars || WantsVehicleChase(target);
			ResponsePersonality b = PickPersonality(tier, pending.IsDelayedWave, pending.IsLateWave, flag);
			pending.Personality = StrongerPersonality(pending.Personality, b);
			if (flag || IsCarWave(pending.Personality))
			{
				if (!IsCarWave(pending.Personality))
				{
					pending.Personality = CarPersonalityFor(tier);
				}
				pending.Cars = Mathf.Max(1, Mathf.Max(pending.Cars, DesiredStreetCars(tier, pending.Personality)));
				pending.Foot = 0;
			}
			else
			{
				pending.Foot = Mathf.Max(pending.Foot, DesiredFootUnits(pending.Personality));
			}
			pending.Theme = LoadoutService.ThemeFor(pending.Personality);
		}

		private static bool QueueHasFollowUp()
		{
			foreach (PendingResponse item in Queue)
			{
				if (item != null && (item.IsDelayedWave || item.IsLateWave))
				{
					return true;
				}
			}
			return false;
		}

		private static void Enqueue(PendingResponse pending)
		{
			//IL_02b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			if (pending == null)
			{
				return;
			}
			foreach (PendingResponse item in Queue)
			{
				if (item == null)
				{
					continue;
				}
				bool flag = IsCarWave(item.Personality);
				bool flag2 = IsCarWave(pending.Personality);
				if (flag != flag2)
				{
					if (flag2 || HeatTracker.Stars >= 3)
					{
						item.Personality = StrongerPersonality(item.Personality, flag2 ? pending.Personality : CarPersonalityFor(HeatTracker.CurrentTier));
						if (!IsCarWave(item.Personality))
						{
							item.Personality = CarPersonalityFor(HeatTracker.CurrentTier);
						}
						item.Cars = Mathf.Max(1, Mathf.Max(item.Cars, pending.Cars));
						item.Foot = 0;
						item.Theme = LoadoutService.ThemeFor(item.Personality);
						item.Origin = pending.Origin;
						if ((Object)(object)pending.Target != (Object)null)
						{
							item.Target = pending.Target;
						}
						item.Delay = Mathf.Max(item.Delay, pending.Delay);
						item.IsDelayedWave = item.IsDelayedWave || pending.IsDelayedWave;
						item.IsLateWave = item.IsLateWave || pending.IsLateWave;
					}
					return;
				}
				if (!pending.IsDelayedWave && !pending.IsLateWave && (item.IsDelayedWave || item.IsLateWave) && flag2 == flag)
				{
					item.Cars = Mathf.Max(item.Cars, pending.Cars);
					item.Foot = Mathf.Max(item.Foot, pending.Foot);
					item.Personality = StrongerPersonality(item.Personality, pending.Personality);
					item.Theme = LoadoutService.ThemeFor(item.Personality);
					item.Origin = pending.Origin;
					if ((Object)(object)pending.Target != (Object)null)
					{
						item.Target = pending.Target;
					}
					if (IsCarWave(item.Personality))
					{
						item.Cars = Mathf.Max(1, item.Cars);
						item.Foot = 0;
					}
					return;
				}
				item.Cars = Mathf.Max(item.Cars, pending.Cars);
				item.Foot = Mathf.Max(item.Foot, pending.Foot);
				item.Personality = StrongerPersonality(item.Personality, pending.Personality);
				item.Theme = LoadoutService.ThemeFor(item.Personality);
				item.Origin = pending.Origin;
				if ((Object)(object)pending.Target != (Object)null)
				{
					item.Target = pending.Target;
				}
				if (pending.IsDelayedWave || pending.IsLateWave)
				{
					item.IsDelayedWave = item.IsDelayedWave || pending.IsDelayedWave;
					item.IsLateWave = item.IsLateWave || pending.IsLateWave;
					item.Delay = Mathf.Max(item.Delay, pending.Delay);
				}
				if (IsCarWave(item.Personality))
				{
					item.Cars = Mathf.Max(1, item.Cars);
					item.Foot = 0;
				}
				return;
			}
			int num = ((CountLiveCrew() >= 2) ? 1 : Mathf.Max(2, Mathf.Min(3, ModConfig.MaxPendingVehicleResponses.Value)));
			if (Queue.Count < num)
			{
				Queue.Enqueue(pending);
			}
		}

		private static PendingResponse BuildPending(Vector3 origin, Player target, HeatTier tier, bool delayed, bool late, bool forceCars = false)
		{
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			bool preferCars = forceCars || WantsVehicleChase(target);
			ResponsePersonality personality = PickPersonality(tier, delayed, late, preferCars);
			int num = 0;
			int num2 = 0;
			float num3 = ((delayed || late) ? DelayedWaveDelay() : HeatStagger(tier));
			if (IsCarWave(personality))
			{
				num = RollCars(tier, personality);
				if (num <= 0)
				{
					num = 1;
					num2 = 0;
					num3 = Mathf.Max(num3, 6f);
					delayed = true;
				}
				else
				{
					num2 = 0;
				}
			}
			else
			{
				num2 = DesiredFootUnits(personality);
			}
			return new PendingResponse
			{
				Cars = num,
				Foot = num2,
				Personality = personality,
				Theme = LoadoutService.ThemeFor(personality),
				Delay = num3,
				Origin = origin,
				Target = target,
				IsDelayedWave = delayed,
				IsLateWave = late
			};
		}

		private static bool WantsVehicleChase(Player target)
		{
			if (PlayerInVehicle(target))
			{
				return true;
			}
			if ((Object)(object)target != (Object)null)
			{
				try
				{
					if (target.TimeSinceVehicleExit < 12f)
					{
						return true;
					}
				}
				catch
				{
				}
			}
			if (HeatTracker.Stars >= 5)
			{
				return true;
			}
			return !IsNearPoliceStation(target, 100f);
		}

		private static bool PlayerInVehicle(Player target)
		{
			if ((Object)(object)target == (Object)null)
			{
				return false;
			}
			try
			{
				if (target.IsInVehicle)
				{
					return true;
				}
			}
			catch
			{
			}
			try
			{
				if ((Object)(object)target.CurrentVehicle != (Object)null)
				{
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		private static bool IsNearPoliceStation(Player target, float maxDist)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)target == (Object)null)
			{
				return false;
			}
			try
			{
				Vector3 position = ((Component)target).transform.position;
				PoliceStation closestStation = OfficerPool.GetClosestStation(position);
				if ((Object)(object)closestStation == (Object)null)
				{
					return false;
				}
				Transform val = null;
				try
				{
					val = closestStation.SpawnPoint;
				}
				catch
				{
				}
				if ((Object)(object)val == (Object)null)
				{
					val = ((Component)closestStation).transform;
				}
				return Vector3.Distance(position, val.position) <= maxDist;
			}
			catch
			{
				return false;
			}
		}

		private static float DelayedWaveDelay()
		{
			if (HeatTracker.Stars >= 4)
			{
				return Random.Range(3f, 6f);
			}
			if (HeatTracker.Stars >= 3)
			{
				return Random.Range(4f, 8f);
			}
			if (HeatTracker.Stars >= 2)
			{
				return Random.Range(5f, 9f);
			}
			return Random.Range(6f, 10f);
		}

		private static float HeatStagger(HeatTier tier)
		{
			if (HeatTracker.Stars >= 4)
			{
				return Random.Range(0.5f, 1.4f);
			}
			if (HeatTracker.Stars >= 2)
			{
				return Random.Range(0.8f, 2f);
			}
			return Random.Range(1f, 2.5f);
		}

		private static bool IsCarWave(ResponsePersonality personality)
		{
			switch (personality)
			{
			case ResponsePersonality.Standard:
			case ResponsePersonality.Swarm:
			case ResponsePersonality.Panic:
			case ResponsePersonality.AllHands:
			case ResponsePersonality.Understaffed:
			case ResponsePersonality.Aggressive:
			case ResponsePersonality.K9Unit:
			case ResponsePersonality.SpecialResponse:
			case ResponsePersonality.RifleTeam:
			case ResponsePersonality.ShotgunTeam:
			case ResponsePersonality.Juggernaut:
				return true;
			default:
				return false;
			}
		}

		private static ResponsePersonality FootFallback(HeatTier tier)
		{
			return ResponsePersonality.FootSweep;
		}

		private static ResponsePersonality CarPersonalityFor(HeatTier tier)
		{
			if (tier >= HeatTier.Juggernaut && JuggernautSquad.Enabled)
			{
				return ResponsePersonality.Juggernaut;
			}
			if (tier >= HeatTier.Manhunt)
			{
				return ResponsePersonality.SpecialResponse;
			}
			if (tier >= HeatTier.Panic)
			{
				return SwatRoll();
			}
			if (tier >= HeatTier.Surge)
			{
				return (Random.value < 0.55f) ? ResponsePersonality.RifleTeam : ResponsePersonality.ShotgunTeam;
			}
			if (tier >= HeatTier.Heat)
			{
				return (Random.value < 0.45f) ? ResponsePersonality.RifleTeam : ResponsePersonality.Standard;
			}
			return ResponsePersonality.Standard;
		}

		private static ResponsePersonality PickPersonality(HeatTier tier, bool delayed, bool late, bool preferCars)
		{
			if (tier >= HeatTier.Juggernaut && JuggernautSquad.Enabled)
			{
				return ResponsePersonality.Juggernaut;
			}
			if (HeatTracker.RevengePending)
			{
				HeatTracker.RevengePending = false;
				return CarPersonalityFor(tier);
			}
			if (preferCars || tier >= HeatTier.Manhunt)
			{
				if (late || delayed)
				{
					if (tier >= HeatTier.Manhunt)
					{
						return ResponsePersonality.SpecialResponse;
					}
					if (tier >= HeatTier.Panic)
					{
						return (Random.value < 0.35f) ? ResponsePersonality.K9Unit : SwatRoll();
					}
					if (tier >= HeatTier.Surge)
					{
						return (Random.value < 0.3f) ? ResponsePersonality.K9Unit : SwatRoll();
					}
					return CarPersonalityFor(tier);
				}
				if (tier >= HeatTier.Manhunt)
				{
					return ResponsePersonality.SpecialResponse;
				}
				if (tier >= HeatTier.Panic)
				{
					float value = Random.value;
					if (value < 0.22f)
					{
						return ResponsePersonality.K9Unit;
					}
					if (value < 0.48f)
					{
						return ResponsePersonality.RifleTeam;
					}
					if (value < 0.74f)
					{
						return ResponsePersonality.ShotgunTeam;
					}
					return ResponsePersonality.SpecialResponse;
				}
				if (tier >= HeatTier.Surge)
				{
					float value2 = Random.value;
					if (value2 < 0.18f)
					{
						return ResponsePersonality.Swarm;
					}
					if (value2 < 0.36f)
					{
						return ResponsePersonality.K9Unit;
					}
					if (value2 < 0.68f)
					{
						return ResponsePersonality.RifleTeam;
					}
					return ResponsePersonality.ShotgunTeam;
				}
				return CarPersonalityFor(tier);
			}
			if (late)
			{
				return (tier >= HeatTier.Surge) ? ResponsePersonality.FootSweep : ResponsePersonality.FootSweep;
			}
			if (delayed)
			{
				if (tier >= HeatTier.Panic)
				{
					return (Random.value < 0.45f) ? ResponsePersonality.FootSweep : SwatRoll();
				}
				if (tier >= HeatTier.Surge)
				{
					return (Random.value < 0.55f) ? ResponsePersonality.FootSweep : ResponsePersonality.RifleTeam;
				}
				return (!(Random.value < 0.55f)) ? ResponsePersonality.FootSweep : ResponsePersonality.Standard;
			}
			float value3 = Random.value;
			if (tier >= HeatTier.Panic)
			{
				if (value3 < 0.4f)
				{
					return ResponsePersonality.FootSweep;
				}
				if (value3 < 0.65f)
				{
					return ResponsePersonality.RifleTeam;
				}
				return ResponsePersonality.ShotgunTeam;
			}
			if (tier >= HeatTier.Surge)
			{
				if (value3 < 0.45f)
				{
					return ResponsePersonality.FootSweep;
				}
				if (value3 < 0.7f)
				{
					return ResponsePersonality.RifleTeam;
				}
				return ResponsePersonality.Tactical;
			}
			if (tier >= HeatTier.Heat)
			{
				if (value3 < 0.4f)
				{
					return ResponsePersonality.Standard;
				}
				if (value3 < 0.7f)
				{
					return ResponsePersonality.FootSweep;
				}
				return ResponsePersonality.RifleTeam;
			}
			return (value3 < 0.55f) ? ResponsePersonality.Understaffed : ResponsePersonality.FootSweep;
		}

		private static ResponsePersonality RollPersonality(HeatTier tier, bool delayed, bool late)
		{
			return PickPersonality(tier, delayed, late, WantsVehicleChase(ResolveTarget()) || HeatTracker.Stars >= 3);
		}

		private static ResponsePersonality SwatRoll()
		{
			float value = Random.value;
			if (value < 0.4f)
			{
				return ResponsePersonality.RifleTeam;
			}
			if (value < 0.8f)
			{
				return ResponsePersonality.ShotgunTeam;
			}
			return ResponsePersonality.SpecialResponse;
		}

		private static ResponsePersonality StrongerPersonality(ResponsePersonality a, ResponsePersonality b)
		{
			return (PersonalityRank(b) > PersonalityRank(a)) ? b : a;
		}

		private static int PersonalityRank(ResponsePersonality personality)
		{
			if (1 == 0)
			{
			}
			int result = personality switch
			{
				ResponsePersonality.Juggernaut => 10, 
				ResponsePersonality.SpecialResponse => 9, 
				ResponsePersonality.RifleTeam => 8, 
				ResponsePersonality.ShotgunTeam => 8, 
				ResponsePersonality.K9Unit => 7, 
				ResponsePersonality.Panic => 6, 
				ResponsePersonality.AllHands => 5, 
				ResponsePersonality.Aggressive => 4, 
				ResponsePersonality.Tactical => 4, 
				ResponsePersonality.Swarm => 3, 
				ResponsePersonality.FootSweep => 2, 
				ResponsePersonality.Standard => 1, 
				_ => 0, 
			};
			if (1 == 0)
			{
			}
			return result;
		}

		private static int ConcurrentCap()
		{
			return Mathf.Clamp(ModConfig.MaxConcurrentResponseCars.Value, 1, 3);
		}

		private static int DesiredStreetCars(HeatTier tier, ResponsePersonality personality)
		{
			switch (personality)
			{
			case ResponsePersonality.Understaffed:
				return 1;
			case ResponsePersonality.Juggernaut:
				return 2;
			default:
				if (IsSwat(personality) || personality == ResponsePersonality.K9Unit)
				{
					return (HeatTracker.Stars < 4) ? 1 : 2;
				}
				if (HeatTracker.Stars <= 2)
				{
					return 1;
				}
				return Mathf.Min(2, ConcurrentCap());
			}
		}

		private static int RollCars(HeatTier tier, ResponsePersonality personality)
		{
			if (!IsCarWave(personality))
			{
				return 0;
			}
			int num = CountLiveResponseCars();
			int num2 = Mathf.Max(0, ConcurrentCap() - num);
			if (num2 == 0)
			{
				return 0;
			}
			int num3 = DesiredStreetCars(tier, personality);
			return Mathf.Clamp(Mathf.Min(num3, num2), 0, ConcurrentCap());
		}

		private static int CountLiveCrew()
		{
			int num = 0;
			foreach (PoliceOfficer item in ActiveCrew)
			{
				if (OfficerPool.IsUsable(item))
				{
					num++;
				}
			}
			return num;
		}

		private static int CountLiveResponseCars()
		{
			int num = 0;
			for (int num2 = ActiveVehicles.Count - 1; num2 >= 0; num2--)
			{
				LandVehicle val = ActiveVehicles[num2];
				if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).gameObject == (Object)null)
				{
					ActiveVehicles.RemoveAt(num2);
				}
				else if (VehicleHasLivingResponseCrew(val))
				{
					num++;
				}
			}
			return num;
		}

		private static bool VehicleHasLivingResponseCrew(LandVehicle vehicle)
		{
			if ((Object)(object)vehicle == (Object)null)
			{
				return false;
			}
			if (OfficerPool.CountLivingOccupants(vehicle) > 0)
			{
				return true;
			}
			int instanceID = ((Object)vehicle).GetInstanceID();
			if (VehicleCrewIds.TryGetValue(instanceID, out var value) && value != null && value.Count > 0)
			{
				foreach (PoliceOfficer item in ActiveCrew)
				{
					if ((Object)(object)item == (Object)null || !OfficerPool.IsLive(item) || !value.Contains(((Object)item).GetInstanceID()))
					{
						continue;
					}
					return true;
				}
				return false;
			}
			foreach (PoliceOfficer item2 in ActiveCrew)
			{
				if ((Object)(object)item2 == (Object)null || !OfficerPool.IsLive(item2))
				{
					continue;
				}
				try
				{
					if ((Object)(object)((NPC)item2).CurrentVehicle == (Object)(object)vehicle || (Object)(object)item2.AssignedVehicle == (Object)(object)vehicle)
					{
						return true;
					}
				}
				catch
				{
				}
			}
			return false;
		}

		private static bool VehicleCrewConfirmedDead(LandVehicle vehicle)
		{
			if ((Object)(object)vehicle == (Object)null)
			{
				return true;
			}
			if (OfficerPool.CountLivingOccupants(vehicle) > 0)
			{
				return false;
			}
			int instanceID = ((Object)vehicle).GetInstanceID();
			if (!VehicleCrewIds.TryGetValue(instanceID, out var value) || value == null || value.Count == 0)
			{
				return false;
			}
			foreach (PoliceOfficer item in ActiveCrew)
			{
				if ((Object)(object)item == (Object)null || !OfficerPool.IsLive(item) || !value.Contains(((Object)item).GetInstanceID()))
				{
					continue;
				}
				return false;
			}
			return true;
		}

		private static void RememberVehicleCrew(LandVehicle vehicle, List<PoliceOfficer> crew)
		{
			if ((Object)(object)vehicle == (Object)null || crew == null || crew.Count == 0)
			{
				return;
			}
			HashSet<int> hashSet = new HashSet<int>();
			for (int i = 0; i < crew.Count; i++)
			{
				if ((Object)(object)crew[i] != (Object)null)
				{
					hashSet.Add(((Object)crew[i]).GetInstanceID());
				}
			}
			VehicleCrewIds[((Object)vehicle).GetInstanceID()] = hashSet;
		}

		private static IEnumerator ProcessQueue()
		{
			_running = true;
			try
			{
				while (Queue.Count > 0)
				{
					float waitedFight = 0f;
					while (CountLiveCrew() >= 3 && waitedFight < 45f)
					{
						if (Queue.Count == 0)
						{
							yield break;
						}
						yield return (object)new WaitForSeconds(0.75f);
						waitedFight += 0.75f;
					}
					if (Queue.Count == 0)
					{
						break;
					}
					PendingResponse pending = Queue.Dequeue();
					if (pending != null)
					{
						if (pending.Delay > 0f)
						{
							yield return (object)new WaitForSeconds(pending.Delay);
						}
						float waited = 0f;
						bool carWave = pending.Cars > 0 && IsCarWave(pending.Personality);
						while (carWave && CountLiveResponseCars() >= ConcurrentCap() && waited < 12f)
						{
							ReclaimFinishedResponseVehicles();
							yield return (object)new WaitForSeconds(0.5f);
							waited += 0.5f;
						}
						yield return Dispatch(pending);
						float gap = WaveGap();
						if (gap > 0f)
						{
							yield return (object)new WaitForSeconds(gap);
						}
					}
				}
			}
			finally
			{
				_running = false;
			}
		}

		private static float WaveGap()
		{
			if (Queue.Count == 0)
			{
				return 0f;
			}
			float num = Mathf.Max(8f, ModConfig.WaveGapSeconds.Value);
			if (CountLiveCrew() >= 2)
			{
				return Mathf.Max(num, 14f);
			}
			if (HeatTracker.Stars >= 5)
			{
				return Mathf.Min(num, 8f);
			}
			if (HeatTracker.Stars >= 4)
			{
				return Mathf.Min(num, 10f);
			}
			if (HeatTracker.Stars >= 3)
			{
				return Mathf.Min(num, 12f);
			}
			return num;
		}

		private static IEnumerator Dispatch(PendingResponse pending)
		{
			if (HeatTracker.IgnoreWanted)
			{
				yield break;
			}
			if ((Object)(object)pending.Target == (Object)null)
			{
				pending.Target = ResolveTarget();
			}
			if ((Object)(object)pending.Target == (Object)null)
			{
				yield break;
			}
			UpgradePendingToContext(pending, pending.Target, HeatTracker.CurrentTier, WantsVehicleChase(pending.Target));
			ReclaimFinishedResponseVehicles();
			bool carWave = pending.Cars > 0 && IsCarWave(pending.Personality);
			if (carWave && CountLiveResponseCars() >= ConcurrentCap())
			{
				float hold = 0f;
				while (CountLiveResponseCars() >= ConcurrentCap() && hold < 20f)
				{
					ReclaimFinishedResponseVehicles();
					yield return (object)new WaitForSeconds(0.5f);
					hold += 0.5f;
				}
				if (CountLiveResponseCars() >= ConcurrentCap())
				{
					pending.Delay = Mathf.Max(6f, pending.Delay);
					pending.IsDelayedWave = true;
					Enqueue(pending);
					yield break;
				}
			}
			WorldHooks.OnResponseDispatched(pending.Personality);
			OfficerPool.EnsureCapacity();
			PoliceStation station = OfficerPool.GetClosestStation(pending.Origin);
			int spawnedCars = 0;
			int spawnedFoot = 0;
			int spawnedK9 = 0;
			int carSlots = (carWave ? Mathf.Min(ConcurrentCap(), Mathf.Max(1, pending.Cars)) : 0);
			int footWanted = ((!carWave) ? Mathf.Clamp(pending.Foot, 0, IsSwat(pending.Personality) ? 4 : 3) : 0);
			if (carSlots <= 0 && footWanted <= 0)
			{
				yield break;
			}
			int concurrent = ConcurrentCap();
			int remainingPeople = ((pending.Personality == ResponsePersonality.Juggernaut) ? JuggernautSquad.CrewWanted() : (-1));
			int juggsLeft = ((pending.Personality == ResponsePersonality.Juggernaut) ? JuggernautSquad.JuggernautsThisWave() : 0);
			if (pending.Personality == ResponsePersonality.Juggernaut)
			{
				JuggernautSquad.BeginWaveOutfit();
			}
			for (int i = 0; i < carSlots; i++)
			{
				if (CountLiveResponseCars() >= concurrent)
				{
					break;
				}
				if (remainingPeople == 0)
				{
					break;
				}
				LandVehicle vehicle = OfficerPool.TryDeployVehicle(station, i, carSlots);
				if ((Object)(object)vehicle == (Object)null)
				{
					ReclaimFinishedResponseVehicles();
					yield return (object)new WaitForSeconds(0.5f);
					vehicle = OfficerPool.TryDeployVehicle(station, i, carSlots);
				}
				if ((Object)(object)vehicle == (Object)null)
				{
					continue;
				}
				ActiveVehicles.Add(vehicle);
				VehicleChaseSlot[((Object)vehicle).GetInstanceID()] = spawnedCars;
				VehicleDeployedAt[((Object)vehicle).GetInstanceID()] = Time.unscaledTime;
				OfficerPool.EnsureVehicleVisible(vehicle);
				int crewSize = CrewForVehicle(vehicle, pending.Personality);
				if (remainingPeople >= 0)
				{
					crewSize = Mathf.Min(crewSize, remainingPeople);
				}
				int seats = VehicleSeats(vehicle, crewSize);
				bool k9Car = WantVehicleK9(pending.Personality);
				int officerCap = (k9Car ? Mathf.Min(crewSize, Mathf.Max(1, seats - 1)) : crewSize);
				List<PoliceOfficer> crew = new List<PoliceOfficer>();
				int used = 0;
				Vector3 leaseNear = (((Object)(object)station != (Object)null) ? ((Component)station).transform.position : pending.Origin);
				int n = 0;
				while (crew.Count < officerCap && used < seats && n < seats + 6)
				{
					PoliceOfficer member = OfficerPool.LeaseForResponse(leaseNear);
					if ((Object)(object)member == (Object)null)
					{
						OfficerPool.EnsureCapacity();
						yield return (object)new WaitForSeconds(0.15f);
						member = OfficerPool.LeaseForResponse(leaseNear);
					}
					if ((Object)(object)member == (Object)null)
					{
						break;
					}
					int cost = 1 + K9Compat.ExtraSeats(member);
					if (used + cost > seats)
					{
						OfficerPool.FreeLease(member);
					}
					else
					{
						crew.Add(member);
						used += cost;
					}
					n++;
				}
				if (crew.Count == 0)
				{
					ActiveVehicles.Remove(vehicle);
					VehicleChaseSlot.Remove(((Object)vehicle).GetInstanceID());
					VehicleDeployedAt.Remove(((Object)vehicle).GetInstanceID());
					OfficerPool.ReturnVehicle(station, vehicle);
					break;
				}
				OutfitVehicleCrew(crew, pending, ref juggsLeft);
				if (remainingPeople >= 0)
				{
					remainingPeople -= crew.Count;
				}
				RememberVehicleCrew(vehicle, crew);
				for (int j = 0; j < crew.Count; j++)
				{
					if (!ActiveCrew.Contains(crew[j]))
					{
						ActiveCrew.Add(crew[j]);
					}
					TryEnterVehicle(crew[j], vehicle);
				}
				yield return (object)new WaitForSeconds(0.35f);
				OfficerPool.EnsureVehicleVisible(vehicle);
				for (int k = 0; k < crew.Count; k++)
				{
					BeginVehicleHunt(crew[k], pending.Target, vehicle);
					K9Compat.SeatIfRiding(crew[k]);
				}
				if (k9Car)
				{
					PoliceOfficer handler = crew[0];
					for (int l = 0; l < crew.Count; l++)
					{
						if (K9Compat.IsHandler(crew[l]))
						{
							handler = crew[l];
							break;
						}
					}
					if (K9Compat.TryAttach(handler, force: true))
					{
						spawnedK9++;
					}
					K9Compat.SeatIfRiding(handler);
				}
				spawnedCars++;
				if (IsSwat(pending.Personality) && crew.Count < 3 && carSlots < concurrent)
				{
					carSlots = Mathf.Min(concurrent, carSlots + 1);
				}
				if (i < carSlots - 1)
				{
					yield return (object)new WaitForSeconds(2.25f);
				}
			}
			if (spawnedCars == 0 && carWave)
			{
				if (ModConfig.DebugLogging.Value)
				{
					MelonLogger.Warning($"Car wave {pending.Personality} got no cruisers — requeueing instead of foot.");
				}
				pending.Cars = Mathf.Max(1, pending.Cars);
				pending.Foot = 0;
				pending.Delay = Mathf.Max(12f, pending.Delay);
				pending.IsDelayedWave = true;
				Enqueue(pending);
				yield break;
			}
			Vector3 footLeaseNear = (((Object)(object)station != (Object)null) ? ((Component)station).transform.position : pending.Origin);
			for (int m = 0; m < footWanted; m++)
			{
				PoliceOfficer extra = OfficerPool.LeaseForResponse(footLeaseNear);
				if ((Object)(object)extra == (Object)null)
				{
					ReleaseDownedCrewLeases();
					extra = OfficerPool.LeaseForResponse(footLeaseNear);
				}
				if ((Object)(object)extra == (Object)null)
				{
					break;
				}
				if (!ActiveCrew.Contains(extra))
				{
					ActiveCrew.Add(extra);
				}
				OutfitOfficer(extra, pending, null, ref juggsLeft);
				OfficerPool.DeployResponseFoot(extra, station, 40 + m);
				spawnedFoot += SendOnFoot(extra, pending.Target, pending.Personality, ref spawnedK9);
				yield return (object)new WaitForSeconds(1f);
			}
			EscalatePursuit(pending.Target, pending.Personality);
			if (spawnedCars == 0 && spawnedFoot == 0)
			{
				if (ModConfig.DebugLogging.Value)
				{
					MelonLogger.Warning($"Dispatch produced nobody ({pending.Personality}) — station pool may be empty.");
				}
				yield break;
			}
			_lastDispatchTime = Time.unscaledTime;
			MelonLogger.Msg($"Dispatched {pending.Personality} ({spawnedCars} cars, {spawnedFoot} foot, {spawnedK9} K9, {pending.Theme}) at {HeatTracker.StarLabel}.");
		}

		private static int CrewWanted(ResponsePersonality personality)
		{
			switch (personality)
			{
			case ResponsePersonality.Juggernaut:
				return JuggernautSquad.CrewWanted();
			case ResponsePersonality.K9Unit:
				if (K9Compat.CanDeploy)
				{
					return 1;
				}
				break;
			}
			if (!IsSwat(personality))
			{
				return Mathf.Clamp(ModConfig.OfficersPerCruiser.Value, 1, 2);
			}
			return (HeatTracker.Stars >= 4) ? 4 : 3;
		}

		private static bool WantVehicleK9(ResponsePersonality personality)
		{
			return personality == ResponsePersonality.K9Unit && K9Compat.CanDeploy;
		}

		private static int VehicleSeats(LandVehicle vehicle, int fallback)
		{
			try
			{
				if ((Object)(object)vehicle != (Object)null)
				{
					return Mathf.Max(1, vehicle.Capacity);
				}
			}
			catch
			{
			}
			return Mathf.Max(1, fallback);
		}

		private static int CrewForVehicle(LandVehicle vehicle, ResponsePersonality personality)
		{
			int num = 2;
			try
			{
				if ((Object)(object)vehicle != (Object)null)
				{
					num = Mathf.Max(1, vehicle.Capacity);
				}
			}
			catch
			{
				num = 2;
			}
			int num2 = CrewWanted(personality);
			if (!IsSwat(personality))
			{
				return Mathf.Clamp(num2, 1, Mathf.Min(2, num));
			}
			return Mathf.Clamp(num2, 2, num);
		}

		private static int DesiredFootUnits(ResponsePersonality personality)
		{
			if (IsCarWave(personality))
			{
				return 0;
			}
			if (IsSwat(personality))
			{
				return (HeatTracker.Stars >= 4) ? 4 : 3;
			}
			return (HeatTracker.Stars >= 3) ? 3 : 2;
		}

		private static void OutfitVehicleCrew(List<PoliceOfficer> crew, PendingResponse pending, ref int juggsLeft)
		{
			if (crew == null || crew.Count == 0)
			{
				return;
			}
			WeaponBudget budget = (IsSwat(pending.Personality) ? WeaponBudget.ForSwat(crew.Count) : WeaponBudget.ForVehicle());
			foreach (PoliceOfficer item in crew)
			{
				OutfitOfficer(item, pending, budget, ref juggsLeft);
			}
		}

		private static void OutfitOfficer(PoliceOfficer officer, PendingResponse pending, WeaponBudget budget, ref int juggsLeft)
		{
			bool flag = pending.Personality == ResponsePersonality.Juggernaut && juggsLeft > 0;
			if (flag)
			{
				juggsLeft--;
			}
			LoadoutService.Assign(officer, flag ? OfficerArchetype.Elite : LoadoutService.RollArchetype(HeatTracker.CurrentTier, pending.Personality), pending.Personality, HeatTracker.CurrentTier, pending.Theme, budget, flag);
			if (flag || (pending.Personality == ResponsePersonality.SpecialResponse && HeatTracker.Stars >= 4))
			{
				SmokeTactics.Arm(officer);
			}
			ActiveCrew.Add(officer);
		}

		private static int SendOnFoot(PoliceOfficer officer, Player target, ResponsePersonality personality, ref int k9)
		{
			if ((Object)(object)officer == (Object)null)
			{
				return 0;
			}
			BeginFootHunt(officer, target);
			bool flag = personality == ResponsePersonality.K9Unit;
			if (k9 == 0 && flag && K9Compat.TryAttach(officer, force: true))
			{
				k9++;
			}
			else if (k9 == 0 && personality == ResponsePersonality.FootSweep && K9Compat.TryAttach(officer))
			{
				k9++;
			}
			return 1;
		}

		private static bool IsSwat(ResponsePersonality personality)
		{
			if ((uint)(personality - 9) <= 3u)
			{
				return true;
			}
			return false;
		}

		private static void BeginFootHunt(PoliceOfficer officer, Player target)
		{
			if ((Object)(object)officer == (Object)null || (Object)(object)target == (Object)null)
			{
				return;
			}
			try
			{
				officer.BeginFootPursuit_Networked(target.PlayerCode, true);
			}
			catch
			{
				try
				{
					officer.BeginFootPursuit_Networked(target.PlayerCode, true);
				}
				catch
				{
					GameAccess.Call(officer, "BeginFootPursuit", target.PlayerCode);
				}
			}
		}

		private static void AlertNearbyUnits()
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			if (HeatTracker.Stars < 1 || Time.unscaledTime < _nextNearbyAlert)
			{
				return;
			}
			_nextNearbyAlert = Time.unscaledTime + 2.25f;
			Player val = ResolveTarget();
			if ((Object)(object)val == (Object)null || HeatTracker.ShouldIgnore(val))
			{
				return;
			}
			Vector3 position;
			try
			{
				position = ((Component)val).transform.position;
			}
			catch
			{
				return;
			}
			float num = ((ModConfig.LocalAlertMinRadius != null) ? ModConfig.LocalAlertMinRadius.Value : 80f);
			float num2 = ((ModConfig.LocalAlertMaxRadius != null) ? ModConfig.LocalAlertMaxRadius.Value : 120f);
			if (num2 < num)
			{
				num2 = num;
			}
			float num3 = Mathf.Clamp(num + (float)HeatTracker.Stars * 6f, num, num2);
			int num4 = ((ModConfig.LocalAlertMaxCops != null) ? Mathf.Clamp(ModConfig.LocalAlertMaxCops.Value, 0, 6) : 2);
			int num5 = Mathf.Clamp((HeatTracker.Stars <= 2) ? 1 : num4, 0, num4);
			if (num5 <= 0)
			{
				return;
			}
			int num6 = CountLiveLocalSupport();
			if (num6 >= num5)
			{
				return;
			}
			int num7 = 0;
			int num8 = num5 - num6;
			foreach (PoliceOfficer item in OfficerPool.EnumerateOfficers())
			{
				if (num7 >= num8)
				{
					break;
				}
				if ((Object)(object)item == (Object)null || !OfficerPool.IsUsable(item) || OfficerPool.IsLeased(item) || OfficerPool.IsAtStation(item))
				{
					continue;
				}
				float num9;
				try
				{
					num9 = Vector3.Distance(((Component)item).transform.position, position);
				}
				catch
				{
					continue;
				}
				if (num9 > num3)
				{
					continue;
				}
				try
				{
					if ((Object)(object)item.VehiclePursuitBehaviour != (Object)null && ((Behaviour)item.VehiclePursuitBehaviour).Active && (Object)(object)item.VehiclePursuitBehaviour.Target == (Object)(object)val)
					{
						continue;
					}
				}
				catch
				{
				}
				try
				{
					if ((Object)(object)item.PursuitBehaviour != (Object)null && ((Behaviour)item.PursuitBehaviour).Active && (Object)(object)item.PursuitBehaviour.TargetPlayer == (Object)(object)val)
					{
						continue;
					}
				}
				catch
				{
				}
				LandVehicle val2 = null;
				try
				{
					val2 = ((NPC)item).CurrentVehicle;
				}
				catch
				{
					val2 = null;
				}
				if (OfficerPool.ClaimForChase(item))
				{
					LocalSupport.Add(((Object)item).GetInstanceID());
					if ((Object)(object)val2 != (Object)null)
					{
						BeginVehicleHunt(item, val, val2, beginAsSighted: true);
					}
					else
					{
						BeginFootHunt(item, val);
					}
					try
					{
						EscalatePursuit(val, ResponsePersonality.Aggressive);
					}
					catch
					{
					}
					num7++;
				}
			}
		}

		private static int CountLiveLocalSupport()
		{
			int num = 0;
			foreach (int item in LocalSupport)
			{
				foreach (PoliceOfficer item2 in OfficerPool.EnumerateOfficers())
				{
					if ((Object)(object)item2 == (Object)null || ((Object)item2).GetInstanceID() != item)
					{
						continue;
					}
					if (OfficerPool.IsUsable(item2))
					{
						num++;
					}
					break;
				}
			}
			return num;
		}

		private static void ReapLocalSupport()
		{
			if (LocalSupport.Count == 0)
			{
				return;
			}
			int[] array = LocalSupport.ToArray();
			foreach (int num in array)
			{
				PoliceOfficer val = null;
				foreach (PoliceOfficer item in OfficerPool.EnumerateOfficers())
				{
					if ((Object)(object)item != (Object)null && ((Object)item).GetInstanceID() == num)
					{
						val = item;
						break;
					}
				}
				if ((Object)(object)val == (Object)null || !OfficerPool.IsUsable(val))
				{
					if ((Object)(object)val != (Object)null)
					{
						OfficerPool.FreeLease(val);
					}
					LocalSupport.Remove(num);
				}
			}
		}

		private static void ClearLocalSupport()
		{
			int[] array = LocalSupport.ToArray();
			foreach (int num in array)
			{
				foreach (PoliceOfficer item in OfficerPool.EnumerateOfficers())
				{
					if ((Object)(object)item != (Object)null && ((Object)item).GetInstanceID() == num)
					{
						OfficerPool.FreeLease(item);
						break;
					}
				}
			}
			LocalSupport.Clear();
		}

		public static bool TryGetChaseSlot(LandVehicle vehicle, out int slot)
		{
			slot = 0;
			return (Object)(object)vehicle != (Object)null && VehicleChaseSlot.TryGetValue(((Object)vehicle).GetInstanceID(), out slot);
		}

		public static void KeepVehicleChaseAlive(VehiclePursuitBehaviour pursuit)
		{
			if ((Object)(object)pursuit == (Object)null)
			{
				return;
			}
			PoliceOfficer val = ((Component)pursuit).GetComponent<PoliceOfficer>() ?? ((Component)pursuit).GetComponentInParent<PoliceOfficer>();
			if (!OfficerPool.IsLeased(val))
			{
				return;
			}
			try
			{
				val.IgnorePlayers = false;
			}
			catch
			{
			}
			try
			{
				NPCAwareness awareness = ((NPC)val).Awareness;
				if (awareness != null)
				{
					awareness.SetAwarenessActive(true);
				}
			}
			catch
			{
			}
			GameAccess.Set(pursuit, "visionEventReceived", true);
			try
			{
				float num = GameAccess.Get<float>(pursuit, "timeSinceLastSighting");
				if (num > 8f)
				{
					GameAccess.Set(pursuit, "timeSinceLastSighting", 4f);
				}
			}
			catch
			{
			}
		}

		private static void RefreshVehicleChases()
		{
			if (ActiveCrew.Count == 0 || Time.unscaledTime < _nextChaseRefresh)
			{
				return;
			}
			_nextChaseRefresh = Time.unscaledTime + 2.5f;
			foreach (PoliceOfficer item in ActiveCrew)
			{
				if (OfficerPool.IsUsable(item) && !((Object)(object)((NPC)item).CurrentVehicle == (Object)null))
				{
					VehiclePursuitBehaviour vehiclePursuitBehaviour = item.VehiclePursuitBehaviour;
					if (!((Object)(object)vehiclePursuitBehaviour == (Object)null) && ((Behaviour)vehiclePursuitBehaviour).Active)
					{
						KeepVehicleChaseAlive(vehiclePursuitBehaviour);
					}
				}
			}
		}

		private static void BeginVehicleHunt(PoliceOfficer officer, Player target, LandVehicle vehicle, bool beginAsSighted = false)
		{
			if ((Object)(object)officer == (Object)null || (Object)(object)target == (Object)null)
			{
				return;
			}
			try
			{
				officer.IgnorePlayers = false;
			}
			catch
			{
			}
			try
			{
				NetworkObject val = (((Object)(object)vehicle != (Object)null) ? ((NetworkBehaviour)vehicle).NetworkObject : ((NetworkBehaviour)officer).NetworkObject);
				if ((Object)(object)val == (Object)null)
				{
					val = (((Object)(object)vehicle != (Object)null) ? ((Component)vehicle).GetComponent<NetworkObject>() : ((Component)officer).GetComponent<NetworkObject>());
				}
				officer.BeginVehiclePursuit_Networked(target.PlayerCode, val, beginAsSighted);
			}
			catch
			{
				BeginFootHunt(officer, target);
			}
		}

		private static void TryEnterVehicle(PoliceOfficer officer, LandVehicle vehicle)
		{
			if ((Object)(object)officer == (Object)null || (Object)(object)vehicle == (Object)null)
			{
				return;
			}
			try
			{
				officer.IgnorePlayers = false;
			}
			catch
			{
			}
			try
			{
				officer.AssignedVehicle = vehicle;
			}
			catch
			{
				GameAccess.Set(officer, "AssignedVehicle", vehicle);
			}
			VehiclePatrolBehaviour vehiclePatrolBehaviour = officer.VehiclePatrolBehaviour;
			if ((Object)(object)vehiclePatrolBehaviour != (Object)null)
			{
				try
				{
					vehiclePatrolBehaviour.Vehicle = vehicle;
				}
				catch
				{
				}
				try
				{
					((Behaviour)vehiclePatrolBehaviour).Disable();
				}
				catch
				{
					GameAccess.Call(vehiclePatrolBehaviour, "Disable");
				}
			}
			try
			{
				((NPC)officer).EnterVehicle((NetworkConnection)null, vehicle);
			}
			catch
			{
				GameAccess.Call(officer, "EnterVehicle", null, vehicle);
			}
			try
			{
				NPCAwareness awareness = ((NPC)officer).Awareness;
				if (awareness != null)
				{
					awareness.SetAwarenessActive(true);
				}
			}
			catch
			{
			}
		}

		private static void EscalatePursuit(Player target, ResponsePersonality personality)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			if (!HeatTracker.ShouldIgnore(target) && HeatTracker.StarsFor(target) > 0 && !((Object)(object)target?.CrimeData == (Object)null))
			{
				EPursuitLevel val = (EPursuitLevel)3;
				if (ModConfig.LethalOnPanic.Value && (IsSwat(personality) || personality == ResponsePersonality.Panic || HeatTracker.StarsFor(target) >= 4))
				{
					val = (EPursuitLevel)4;
				}
				if (target.CrimeData.CurrentPursuitLevel < val)
				{
					target.CrimeData.SetPursuitLevel(val);
				}
			}
		}

		private static Player ResolveTarget(PoliceOfficer fromOfficer = null)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			Vector3 near = Vector3.zero;
			try
			{
				if ((Object)(object)fromOfficer != (Object)null)
				{
					near = ((Component)fromOfficer).transform.position;
				}
				else if ((Object)(object)Player.Local != (Object)null)
				{
					near = ((Component)Player.Local).transform.position;
				}
			}
			catch
			{
			}
			return PlayerQuery.MostWanted(near);
		}

		private static void ReapFinishedCrew()
		{
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			if (ActiveCrew.Count == 0 && ActiveVehicles.Count == 0)
			{
				return;
			}
			bool flag = false;
			PoliceOfficer[] array = ActiveCrew.ToArray();
			foreach (PoliceOfficer val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					ActiveCrew.Remove(val);
					continue;
				}
				if (!OfficerPool.IsUsable(val))
				{
					OfficerPool.FreeLease(val);
				}
				int instanceID = ((Object)val).GetInstanceID();
				if (OfficerPool.IsUsable(val))
				{
					flag = true;
					CorpseSince.Remove(instanceID);
					continue;
				}
				if (!CorpseSince.ContainsKey(instanceID))
				{
					CorpseSince[instanceID] = Time.unscaledTime;
				}
				if (!(Time.unscaledTime - CorpseSince[instanceID] < 75f))
				{
					OfficerPool.Release(val);
					ActiveCrew.Remove(val);
					CorpseSince.Remove(instanceID);
				}
			}
			if (!flag && Time.unscaledTime - _lastDispatchTime > 5f)
			{
				bool flag2 = false;
				for (int j = 0; j < ActiveVehicles.Count; j++)
				{
					if (VehicleHasLivingResponseCrew(ActiveVehicles[j]))
					{
						flag2 = true;
						break;
					}
				}
				if (flag2)
				{
					return;
				}
				HeatTracker.RevengePending = true;
				ReclaimFinishedResponseVehicles(forceAll: true);
				if (!HeatTracker.IgnoreWanted && Queue.Count == 0 && !_running && HeatTracker.Stars >= 1)
				{
					Player val2 = ResolveTarget();
					if ((Object)(object)val2 != (Object)null)
					{
						PendingResponse pendingResponse = BuildPending(((Component)val2).transform.position, val2, HeatTracker.CurrentTier, delayed: false, late: true, forceCars: true);
						pendingResponse.Delay = Mathf.Max(2.5f, pendingResponse.Delay);
						Enqueue(pendingResponse);
					}
				}
			}
			else if (Time.unscaledTime >= _nextVehicleReclaim)
			{
				_nextVehicleReclaim = Time.unscaledTime + 5f;
				ReclaimFinishedResponseVehicles();
			}
		}

		private static void ReclaimFinishedResponseVehicles(bool forceAll = false)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			if (ActiveVehicles.Count > 0)
			{
				PoliceStation closestStation = OfficerPool.GetClosestStation(Vector3.zero);
				for (int num = ActiveVehicles.Count - 1; num >= 0; num--)
				{
					LandVehicle val = ActiveVehicles[num];
					if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).gameObject == (Object)null)
					{
						ActiveVehicles.RemoveAt(num);
					}
					else
					{
						int instanceID = ((Object)val).GetInstanceID();
						if (!VehicleHasLivingResponseCrew(val))
						{
							float num2 = 999f;
							if (VehicleDeployedAt.TryGetValue(instanceID, out var value))
							{
								num2 = Time.unscaledTime - value;
							}
							if ((!(num2 < 16f) || VehicleCrewConfirmedDead(val)) && (forceAll || !(num2 < 6f)) && (forceAll || VehicleCrewConfirmedDead(val) || !(num2 < 20f)))
							{
								VehicleChaseSlot.Remove(instanceID);
								VehicleDeployedAt.Remove(instanceID);
								VehicleCrewIds.Remove(instanceID);
								OfficerPool.ReturnVehicle(closestStation, val);
								ActiveVehicles.RemoveAt(num);
							}
						}
					}
				}
			}
			if (forceAll && !_running)
			{
				OfficerPool.RecoverStrandedVehicles(ActiveVehicles, force: true);
			}
		}

		private static void EnsureActiveVehiclesVisible()
		{
			for (int i = 0; i < ActiveVehicles.Count; i++)
			{
				LandVehicle val = ActiveVehicles[i];
				if (!((Object)(object)val == (Object)null) && VehicleHasLivingResponseCrew(val))
				{
					OfficerPool.EnsureVehicleVisible(val);
				}
			}
		}

		private static void ReleaseDownedCrewLeases()
		{
			foreach (PoliceOfficer item in ActiveCrew)
			{
				if (!((Object)(object)item == (Object)null) && !OfficerPool.IsUsable(item))
				{
					OfficerPool.FreeLease(item);
				}
			}
		}
	}
	internal static class SmokeTactics
	{
		public static void Arm(PoliceOfficer officer)
		{
			if (!((Object)(object)officer == (Object)null))
			{
				MelonCoroutines.Start(ThrowLoop(officer));
			}
		}

		private static IEnumerator ThrowLoop(PoliceOfficer officer)
		{
			yield return (object)new WaitForSeconds(Random.Range(3f, 6f));
			int throws = 0;
			while ((Object)(object)officer != (Object)null && OfficerPool.IsUsable(officer) && throws < 3)
			{
				Player target = PlayerQuery.MostWanted(((Component)officer).transform.position);
				if ((Object)(object)target != (Object)null)
				{
					float dist = Vector3.Distance(((Component)officer).transform.position, ((Component)target).transform.position);
					if (dist > 7f && dist < 26f)
					{
						Vector3 dest = Vector3.Lerp(((Component)officer).transform.position, ((Component)target).transform.position, 0.6f);
						dest.y += 0.35f;
						SpawnCloud(dest);
						throws++;
					}
				}
				yield return (object)new WaitForSeconds(Random.Range(18f, 28f));
			}
		}

		private static void SpawnCloud(Vector3 pos)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("PRO_Smoke");
			val.transform.position = pos;
			Color tint = default(Color);
			((Color)(ref tint))..ctor(0.62f, 0.62f, 0.66f, 0.42f);
			for (int i = 0; i < 6; i++)
			{
				GameObject val2 = GameObject.CreatePrimitive((PrimitiveType)0);
				Object.Destroy((Object)(object)val2.GetComponent<Collider>());
				((Object)val2).name = "puff";
				val2.transform.SetParent(val.transform, false);
				val2.transform.localPosition = Random.insideUnitSphere * 1.4f;
				val2.transform.localScale = Vector3.one * Random.Range(1.8f, 3.2f);
				MeshRenderer component = val2.GetComponent<MeshRenderer>();
				if (!((Object)(object)component == (Object)null))
				{
					((Renderer)component).shadowCastingMode = (ShadowCastingMode)0;
					((Renderer)component).receiveShadows = false;
					Material val3 = MakeSmokeMaterial(tint);
					if ((Object)(object)val3 != (Object)null)
					{
						((Renderer)component).material = val3;
					}
				}
			}
			MelonCoroutines.Start(AnimateCloud(val));
		}

		private static IEnumerator AnimateCloud(GameObject root)
		{
			if ((Object)(object)root == (Object)null)
			{
				yield break;
			}
			float life = 11f;
			float t = 0f;
			Vector3 start = root.transform.localScale;
			while ((Object)(object)root != (Object)null && t < life)
			{
				t += Time.deltaTime;
				float u = t / life;
				root.transform.localScale = start * (1f + u * 1.8f);
				float alpha = ((u < 0.2f) ? (u / 0.2f * 0.45f) : Mathf.Lerp(0.45f, 0f, (u - 0.2f) / 0.8f));
				MeshRenderer[] componentsInChildren = root.GetComponentsInChildren<MeshRenderer>();
				foreach (MeshRenderer renderer in componentsInChildren)
				{
					if (!((Object)(object)renderer == (Object)null) && !((Object)(object)((Renderer)renderer).material == (Object)null))
					{
						Color c = ((Renderer)renderer).material.color;
						c.a = alpha;
						((Renderer)renderer).material.color = c;
					}
				}
				yield return null;
			}
			if ((Object)(object)root != (Object)null)
			{
				Object.Destroy((Object)(object)root);
			}
		}

		private static Material MakeSmokeMaterial(Color tint)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			Shader val = Shader.Find("Sprites/Default") ?? Shader.Find("Universal Render Pipeline/Unlit") ?? Shader.Find("Unlit/Color") ?? Shader.Find("Standard");
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			Material val2 = new Material(val)
			{
				color = tint
			};
			val2.SetInt("_SrcBlend", 5);
			val2.SetInt("_DstBlend", 10);
			val2.SetInt("_ZWrite", 0);
			val2.EnableKeyword("_SURFACE_TYPE_TRANSPARENT");
			val2.renderQueue = 3000;
			return val2;
		}
	}
	internal static class WeaponTactics
	{
		public static float CuffRange => (ModConfig.CuffRange != null) ? Mathf.Max(1f, ModConfig.CuffRange.Value) : 2.75f;

		public static WeaponClass Kind(PoliceOfficer officer)
		{
			if ((Object)(object)officer == (Object)null)
			{
				return WeaponClass.Pistol;
			}
			if (LoadoutService.TryGetAssigned(officer, out var path))
			{
				return LoadoutService.Classify(path);
			}
			CombatBehaviour val = ((Component)officer).GetComponent<CombatBehaviour>() ?? ((Component)officer).GetComponentInChildren<CombatBehaviour>();
			AvatarWeapon val2 = (((Object)(object)val != (Object)null) ? GameAccessCurrent(val) : null);
			if ((Object)(object)val2 != (Object)null && !string.IsNullOrEmpty(((AvatarEquippable)val2).AssetPath))
			{
				return LoadoutService.Classify(((AvatarEquippable)val2).AssetPath);
			}
			PursuitBehaviour val3 = officer.PursuitBehaviour ?? ((Component)officer).GetComponent<PursuitBehaviour>() ?? ((Component)officer).GetComponentInChildren<PursuitBehaviour>();
			if ((Object)(object)val3?.Weapon_Gun != (Object)null && !string.IsNullOrEmpty(((AvatarEquippable)val3.Weapon_Gun).AssetPath))
			{
				return LoadoutService.Classify(((AvatarEquippable)val3.Weapon_Gun).AssetPath);
			}
			return WeaponClass.Pistol;
		}

		public static WeaponClass Kind(CombatBehaviour combat)
		{
			PoliceOfficer val = OfficerOf(combat);
			if ((Object)(object)val != (Object)null)
			{
				return Kind(val);
			}
			AvatarWeapon val2 = GameAccessCurrent(combat);
			if ((Object)(object)val2 != (Object)null && !string.IsNullOrEmpty(((AvatarEquippable)val2).AssetPath))
			{
				return LoadoutService.Classify(((AvatarEquippable)val2).AssetPath);
			}
			return WeaponClass.Pistol;
		}

		public static bool IsLongGun(WeaponClass kind)
		{
			if ((uint)(kind - 1) <= 5u)
			{
				return true;
			}
			return false;
		}

		public static float IdealRange(WeaponClass kind)
		{
			if (1 == 0)
			{
			}
			float result = kind switch
			{
				WeaponClass.Sniper => Entry(ModConfig.IdealRangeSniper, 7.5f), 
				WeaponClass.Rifle => Entry(ModConfig.IdealRangeRifle, 5.5f), 
				WeaponClass.Explosive => Entry(ModConfig.IdealRangeExplosive, 6.5f), 
				WeaponClass.Heavy => Entry(ModConfig.IdealRangeHeavy, 5.5f), 
				WeaponClass.Shotgun => Entry(ModConfig.IdealRangeShotgun, 3.5f), 
				WeaponClass.Smg => Entry(ModConfig.IdealRangeSmg, 4f), 
				_ => Entry(ModConfig.IdealRangePistol, 3.5f), 
			};
			if (1 == 0)
			{
			}
			return result;
		}

		public static bool TryIdealRange(CombatBehaviour combat, out float range)
		{
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			range = 0f;
			PoliceOfficer val = OfficerOf(combat);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			WeaponClass kind = Kind(combat);
			float num = (range = IdealRange(kind));
			Player val2 = null;
			try
			{
				PursuitBehaviour val3 = (PursuitBehaviour)(object)((combat is PursuitBehaviour) ? combat : null);
				if (val3 != null)
				{
					val2 = val3.TargetPlayer;
				}
			}
			catch
			{
				val2 = null;
			}
			if ((Object)(object)val2 == (Object)null)
			{
				try
				{
					ICombatTargetable target = combat.Target;
					if (target != null && target.IsPlayer)
					{
						val2 = target.AsPlayer;
					}
				}
				catch
				{
					val2 = null;
				}
			}
			if ((Object)(object)val2 == (Object)null)
			{
				return true;
			}
			Vector3 val4 = ((Component)val2).transform.position;
			try
			{
				if ((Object)(object)val2.Avatar != (Object)null)
				{
					val4 = val2.Avatar.CenterPoint;
				}
			}
			catch
			{
			}
			float num2 = Vector3.Distance(((Component)combat).transform.position, val4);
			if (num2 < num)
			{
				range = Mathf.Max(Entry(ModConfig.HoldGroundMin, 2.2f), num2);
			}
			return true;
		}

		public static void ApplyUseRanges(AvatarWeapon weapon, WeaponClass kind)
		{
			if ((Object)(object)weapon == (Object)null)
			{
				return;
			}
			try
			{
				GetUseRange(kind, out var min, out var max);
				if (weapon.MaxUseRange < max * 0.6f)
				{
					weapon.MaxUseRange = max;
				}
				if (weapon.MinUseRange > min || weapon.MinUseRange <= 0.01f)
				{
					weapon.MinUseRange = min;
				}
			}
			catch
			{
			}
		}

		public static float ArrestRange(WeaponClass kind)
		{
			return CuffRange;
		}

		public static float MinShotInterval(WeaponClass kind)
		{
			if (1 == 0)
			{
			}
			float result = kind switch
			{
				WeaponClass.Sniper => Entry(ModConfig.ShotIntervalSniper, 0.95f), 
				WeaponClass.Rifle => Entry(ModConfig.ShotIntervalRifle, 0.62f), 
				WeaponClass.Explosive => Entry(ModConfig.ShotIntervalExplosive, 1.35f), 
				WeaponClass.Heavy => Entry(ModConfig.ShotIntervalHeavy, 0.38f), 
				WeaponClass.Shotgun => Entry(ModConfig.ShotIntervalShotgun, 0.55f), 
				WeaponClass.Smg => Entry(ModConfig.ShotIntervalSmg, 0.22f), 
				_ => Entry(ModConfig.ShotIntervalPistol, 0.32f), 
			};
			if (1 == 0)
			{
			}
			return result;
		}

		public static bool WantsGunCombat(WeaponClass kind)
		{
			return IsLongGun(kind);
		}

		public static bool WantsArrest(WeaponClass kind)
		{
			return true;
		}

		public static bool ShouldShootInsteadOfBaton(PursuitBehaviour pursuit)
		{
			if (!IsOfficer((CombatBehaviour)(object)pursuit))
			{
				return false;
			}
			if (CanArrestNow(pursuit))
			{
				return false;
			}
			return IsLongGun(Kind((CombatBehaviour)(object)pursuit));
		}

		public static bool CanPolice(PoliceOfficer officer)
		{
			return OfficerPool.IsLive(officer);
		}

		public static bool CanPolice(PursuitBehaviour pursuit)
		{
			return CanPolice(OfficerOf((CombatBehaviour)(object)pursuit));
		}

		public static bool CanArrestNow(PursuitBehaviour pursuit)
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)pursuit == (Object)null || !CanPolice(pursuit))
			{
				return false;
			}
			PoliceOfficer val = OfficerOf((CombatBehaviour)(object)pursuit);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			Player val2 = null;
			try
			{
				val2 = pursuit.TargetPlayer;
			}
			catch
			{
				val2 = null;
			}
			if ((Object)(object)val2 == (Object)null)
			{
				return false;
			}
			float num = Vector3.Distance(((Component)pursuit).transform.position, ((Object)(object)val2.Avatar != (Object)null) ? val2.Avatar.CenterPoint : ((Component)val2).transform.position);
			return num < ArrestRange(Kind(val));
		}

		public static void ApplyCombatStance(PursuitBehaviour pursuit)
		{
			if ((Object)(object)pursuit == (Object)null || (Object)(object)OfficerOf((CombatBehaviour)(object)pursuit) == (Object)null)
			{
				return;
			}
			WeaponClass weaponClass = Kind((CombatBehaviour)(object)pursuit);
			if (1 == 0)
			{
			}
			float num = weaponClass switch
			{
				WeaponClass.Sniper => 0.62f, 
				WeaponClass.Rifle => 0.72f, 
				WeaponClass.Explosive => 0.68f, 
				WeaponClass.Heavy => 0.7f, 
				WeaponClass.Shotgun => 0.82f, 
				WeaponClass.Smg => 0.86f, 
				_ => 0.74f, 
			};
			if (1 == 0)
			{
			}
			float num2 = num;
			try
			{
				((CombatBehaviour)pursuit).SetMovementSpeed(num2, "combat", 8);
			}
			catch
			{
			}
		}

		public static void TuneWeapon(PoliceOfficer officer, CombatBehaviour combat)
		{
			if ((Object)(object)combat == (Object)null)
			{
				return;
			}
			AvatarWeapon val = GameAccessCurrent(combat);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			WeaponClass weaponClass = Kind(officer);
			ApplyUseRanges(val, weaponClass);
			try
			{
				float num = MinShotInterval(weaponClass) * 0.9f;
				if (val.CooldownDuration < num)
				{
					val.CooldownDuration = num;
				}
			}
			catch
			{
			}
			AvatarRangedWeapon result = (AvatarRangedWeapon)(object)((val is AvatarRangedWeapon) ? val : null);
			if ((Object)(object)result == (Object)null && !Utils.Is<AvatarRangedWeapon>(val, out result))
			{
				return;
			}
			try
			{
				switch (weaponClass)
				{
				case WeaponClass.Sniper:
					if (result.MaxFireRate > 1.1f)
					{
						result.MaxFireRate = 1.1f;
					}
					result.AimTime_Min = Mathf.Max(result.AimTime_Min, 0.35f);
					result.AimTime_Max = Mathf.Max(result.AimTime_Max, 0.7f);
					return;
				case WeaponClass.Rifle:
					if (result.MaxFireRate > 2.2f)
					{
						result.MaxFireRate = 2.2f;
					}
					result.AimTime_Min = Mathf.Max(result.AimTime_Min, 0.2f);
					result.AimTime_Max = Mathf.Max(result.AimTime_Max, 0.45f);
					return;
				case WeaponClass.Explosive:
					if (result.MaxFireRate > 0.8f)
					{
						result.MaxFireRate = 0.8f;
					}
					result.AimTime_Min = Mathf.Max(result.AimTime_Min, 0.3f);
					result.AimTime_Max = Mathf.Max(result.AimTime_Max, 0.55f);
					return;
				case WeaponClass.Smg:
					if (result.MaxFireRate > 8f)
					{
						result.MaxFireRate = 8f;
						return;
					}
					break;
				}
				if (weaponClass == WeaponClass.Shotgun && result.MaxFireRate > 1.8f)
				{
					result.MaxFireRate = 1.8f;
				}
				else if (weaponClass == WeaponClass.Pistol && result.MaxFireRate > 3.2f)
				{
					result.MaxFireRate = 3.2f;
				}
			}
			catch
			{
			}
		}

		public static void SlowFire(AvatarWeapon weapon, ref bool ready)
		{
			if (!ready || !Utils.Alive((Object)(object)weapon) || !LoadoutService.IsPoliceWeapon(weapon))
			{
				return;
			}
			WeaponClass kind = WeaponClass.Pistol;
			try
			{
				if (!string.IsNullOrEmpty(((AvatarEquippable)weapon).AssetPath))
				{
					kind = LoadoutService.Classify(((AvatarEquippable)weapon).AssetPath);
				}
			}
			catch
			{
				kind = WeaponClass.Pistol;
			}
			float num = MinShotInterval(kind);
			float num2 = 0f;
			try
			{
				num2 = weapon.CooldownDuration * 1.15f;
			}
			catch
			{
			}
			float num3 = Mathf.Max(num, num2);
			if (num3 <= 0f)
			{
				return;
			}
			try
			{
				if (Time.time - weapon.LastUseTime < num3)
				{
					ready = false;
				}
			}
			catch
			{
			}
		}

		public static PoliceOfficer OfficerOf(CombatBehaviour combat)
		{
			if (!Utils.Alive((Object)(object)combat))
			{
				return null;
			}
			PursuitBehaviour val = (PursuitBehaviour)(object)((combat is PursuitBehaviour) ? combat : null);
			if (val != null)
			{
				try
				{
					PoliceOfficer officer = val.officer;
					if (Utils.Alive((Object)(object)officer))
					{
						return officer;
					}
				}
				catch
				{
				}
			}
			try
			{
				PoliceOfficer component = ((Component)combat).GetComponent<PoliceOfficer>();
				if (Utils.Alive((Object)(object)component))
				{
					return component;
				}
			}
			catch
			{
				return null;
			}
			try
			{
				PoliceOfficer componentInParent = ((Component)combat).GetComponentInParent<PoliceOfficer>();
				return Utils.Alive((Object)(object)componentInParent) ? componentInParent : null;
			}
			catch
			{
				return null;
			}
		}

		public static bool IsOfficer(CombatBehaviour combat)
		{
			return (Object)(object)OfficerOf(combat) != (Object)null;
		}

		private static void GetUseRange(WeaponClass kind, out float min, out float max)
		{
			switch (kind)
			{
			case WeaponClass.Sniper:
				min = Entry(ModConfig.MinUseSniper, 3f);
				max = Entry(ModConfig.MaxUseSniper, 28f);
				break;
			case WeaponClass.Rifle:
				min = Entry(ModConfig.MinUseRifle, 1.5f);
				max = Entry(ModConfig.MaxUseRifle, 22f);
				break;
			case WeaponClass.Heavy:
				min = Entry(ModConfig.MinUseHeavy, 2f);
				max = Entry(ModConfig.MaxUseHeavy, 20f);
				break;
			case WeaponClass.Explosive:
				min = Entry(ModConfig.MinUseExplosive, 4f);
				max = Entry(ModConfig.MaxUseExplosive, 26f);
				break;
			case WeaponClass.Smg:
				min = Entry(ModConfig.MinUseSmg, 1.2f);
				max = Entry(ModConfig.MaxUseSmg, 15f);
				break;
			case WeaponClass.Shotgun:
				min = Entry(ModConfig.MinUseShotgun, 1f);
				max = Entry(ModConfig.MaxUseShotgun, 12f);
				break;
			default:
				min = Entry(ModConfig.MinUsePistol, 0.8f);
				max = Entry(ModConfig.MaxUsePistol, 14f);
				break;
			}
		}

		private static float Entry(MelonPreferences_Entry<float> entry, float fallback)
		{
			if (entry == null)
			{
				return fallback;
			}
			float value = entry.Value;
			return (value > 0.01f) ? value : fallback;
		}

		private static AvatarWeapon GameAccessCurrent(CombatBehaviour combat)
		{
			if ((Object)(object)combat == (Object)null)
			{
				return null;
			}
			try
			{
				return combat.currentWeapon;
			}
			catch
			{
				return GameAccess.Get<AvatarWeapon>(combat, "currentWeapon");
			}
		}
	}
}
namespace PoliceResponseOverhaul.Recovery
{
	public static class StuckRecovery
	{
		private static float _nextCheck;

		public static void Tick()
		{
			if (!ModConfig.StuckRecovery.Value || Time.unscaledTime < _nextCheck)
			{
				return;
			}
			_nextCheck = Time.unscaledTime + 2.5f;
			foreach (PoliceOfficer item in OfficerPool.EnumerateOfficers())
			{
				if (OfficerPool.IsLeased(item) && OfficerPool.IsUsable(item))
				{
					TryRecover(item);
				}
			}
			UnstickStationDoor();
			UnstickClusteredVehicles();
		}

		private static void TryRecover(PoliceOfficer officer)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				LandVehicle currentVehicle = ((NPC)officer).CurrentVehicle;
				if ((Object)(object)currentVehicle == (Object)null)
				{
					return;
				}
				Transform transform = ((Component)currentVehicle).transform;
				float num = Vector3.Dot(transform.up, Vector3.up);
				bool flag = num < 0.25f;
				Rigidbody component = ((Component)currentVehicle).GetComponent<Rigidbody>();
				int num2;
				if (!((Object)(object)component == (Object)null))
				{
					Vector3 velocity = component.velocity;
					num2 = ((((Vector3)(ref velocity)).sqrMagnitude < 0.15f) ? 1 : 0);
				}
				else
				{
					num2 = 1;
				}
				bool flag2 = (byte)num2 != 0;
				if (flag && flag2)
				{
					Vector3 val = transform.position + transform.right * 2.2f + Vector3.up * 0.5f;
					NPCMovement movement = ((NPC)officer).Movement;
					if (movement != null)
					{
						movement.Warp(val);
					}
					DisableSiren(currentVehicle);
					if (ModConfig.DebugLogging.Value)
					{
						MelonLogger.Msg("Recovered officer from flipped cruiser " + ((Object)currentVehicle).name);
					}
					return;
				}
				Animator componentInChildren = ((Component)officer).GetComponentInChildren<Animator>();
				int num3;
				if ((Object)(object)componentInChildren != (Object)null)
				{
					if (!componentInChildren.GetBool("Sitting"))
					{
						AnimatorStateInfo currentAnimatorStateInfo = componentInChildren.GetCurrentAnimatorStateInfo(0);
						num3 = (((AnimatorStateInfo)(ref currentAnimatorStateInfo)).IsTag("Sit") ? 1 : 0);
					}
					else
					{
						num3 = 1;
					}
				}
				else
				{
					num3 = 0;
				}
				bool flag3 = (byte)num3 != 0;
				if (flag2 && flag3 && Time.frameCount % 120 == 0)
				{
					Vector3 val2 = transform.position + transform.right * 1.6f;
					NPCMovement movement2 = ((NPC)officer).Movement;
					if (movement2 != null)
					{
						movement2.Warp(val2);
					}
				}
			}
			catch (Exception ex)
			{
				if (ModConfig.DebugLogging.Value)
				{
					MelonLogger.Warning("Stuck recovery failed: " + ex.Message);
				}
			}
		}

		private static void UnstickStationDoor()
		{
			OfficerPool.SweepStationDoor();
		}

		private static void UnstickClusteredVehicles()
		{
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
			List<LandVehicle> list = new List<LandVehicle>();
			foreach (PoliceOfficer item in OfficerPool.EnumerateOfficers())
			{
				if (OfficerPool.IsLeased(item))
				{
					LandVehicle currentVehicle = ((NPC)item).CurrentVehicle;
					if ((Object)(object)currentVehicle != (Object)null && !list.Contains(currentVehicle))
					{
						list.Add(currentVehicle);
					}
				}
			}
			for (int i = 0; i < list.Count; i++)
			{
				LandVehicle val = list[i];
				if ((Object)(object)val == (Object)null || !IsStationary(val))
				{
					continue;
				}
				int num = 0;
				for (int j = 0; j < list.Count; j++)
				{
					if (i != j && !((Object)(object)list[j] == (Object)null) && Vector3.Distance(((Component)val).transform.position, ((Component)list[j]).transform.position) < 3.2f)
					{
						num++;
					}
				}
				if (num != 0)
				{
					Vector3 val2 = ((Component)val).transform.forward * (4.5f + (float)num * 2f) + ((Component)val).transform.right * ((i % 2 == 0) ? 3.2f : (-3.2f));
					Transform transform = ((Component)val).transform;
					transform.position += val2 + Vector3.up * 0.15f;
					Rigidbody component = ((Component)val).GetComponent<Rigidbody>();
					if ((Object)(object)component != (Object)null)
					{
						component.velocity = Vector3.zero;
						component.angularVelocity = Vector3.zero;
						component.position = ((Component)val).transform.position;
					}
				}
			}
		}

		private static bool IsStationary(LandVehicle vehicle)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			Rigidbody component = ((Component)vehicle).GetComponent<Rigidbody>();
			int result;
			if (!((Object)(object)component == (Object)null))
			{
				Vector3 velocity = component.velocity;
				result = ((((Vector3)(ref velocity)).sqrMagnitude < 0.2f) ? 1 : 0);
			}
			else
			{
				result = 1;
			}
			return (byte)result != 0;
		}

		private static void DisableSiren(LandVehicle vehicle)
		{
			Behaviour[] componentsInChildren = ((Component)vehicle).GetComponentsInChildren<Behaviour>(true);
			foreach (Behaviour val in componentsInChildren)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				string name = ((object)val).GetType().Name;
				if (name.IndexOf("PoliceLight", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Siren", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					try
					{
						val.enabled = false;
					}
					catch
					{
					}
				}
			}
		}
	}
}
namespace PoliceResponseOverhaul.Pooling
{
	public static class OfficerPool
	{
		private static readonly HashSet<int> Leased = new HashSet<int>();

		private static readonly HashSet<int> PooledClones = new HashSet<int>();

		private static readonly HashSet<IntPtr> DeadNative = new HashSet<IntPtr>();

		private static readonly HashSet<int> ProDeployedVehicles = new HashSet<int>();

		private static readonly List<PoliceOfficer> AmbientExtras = new List<PoliceOfficer>();

		private static bool _expanded;

		private static int _staffed;

		private static int _spreadCursor;

		private const int StreetColumns = 4;

		private const float StreetColSpace = 8.5f;

		private const float StreetRowSpace = 7.5f;

		private const float StreetFirstRow = 14f;

		private const float MinOfficerSpacing = 6.5f;

		private static PoliceStation[] _stationCache;

		private static float _stationCacheUntil;

		private static Transform _launchMarker;

		private static float _nextStrandedRecover;

		private static readonly List<PoliceOfficer> OfficerSnap = new List<PoliceOfficer>(64);

		private static float _officerSnapUntil;

		public static int AmbientExtraCount => AmbientExtras.Count;

		public static bool HasExpanded => _expanded;

		public static bool IsLeased(PoliceOfficer officer)
		{
			return (Object)(object)officer != (Object)null && Leased.Contains(((Object)officer).GetInstanceID());
		}

		public static bool IsPooledClone(PoliceOfficer officer)
		{
			return (Object)(object)officer != (Object)null && PooledClones.Contains(((Object)officer).GetInstanceID());
		}

		public static void ClearBuildingOccupancy(NPC npc)
		{
			if (!((Object)(object)npc == (Object)null))
			{
				GameAccess.Set(npc, "CurrentBuilding", null);
				GameAccess.Set(npc, "OccupiedBuilding", null);
				GameAccess.Set(npc, "BuildingID", string.Empty);
				GameAccess.Set(npc, "currentBuildingID", string.Empty);
				GameAccess.Set(npc, "CurrentBuildingID", string.Empty);
				GameAccess.Set(npc, "isInBuilding", false);
				GameAccess.Set(npc, "IsInBuilding", false);
			}
		}

		public static void Release(PoliceOfficer officer)
		{
			if ((Object)(object)officer == (Object)null)
			{
				return;
			}
			Leased.Remove(((Object)officer).GetInstanceID());
			try
			{
				LoadoutService.Restore(officer);
				NPCHealth component = ((Component)officer).GetComponent<NPCHealth>();
				if ((Object)(object)component != (Object)null && (component.IsDead || component.IsKnockedOut))
				{
					try
					{
						component.Revive();
					}
					catch
					{
						GameAccess.Call(component, "Revive");
					}
					UnmarkDead(officer);
				}
				officer.AutoDeactivate = true;
				try
				{
					officer.Deactivate();
				}
				catch
				{
				}
			}
			catch (Exception ex)
			{
				if (ModConfig.DebugLogging.Value)
				{
					MelonLogger.Warning("Failed to return officer: " + ex.Message);
				}
			}
		}

		public static void FreeLease(PoliceOfficer officer)
		{
			if (!((Object)(object)officer == (Object)null))
			{
				Leased.Remove(((Object)officer).GetInstanceID());
			}
		}

		public static bool ClaimForChase(PoliceOfficer officer)
		{
			if ((Object)(object)officer == (Object)null || !IsUsable(officer) || IsLeased(officer))
			{
				return false;
			}
			Leased.Add(((Object)officer).GetInstanceID());
			try
			{
				officer.AutoDeactivate = false;
			}
			catch
			{
			}
			try
			{
				officer.IgnorePlayers = false;
			}
			catch
			{
			}
			try
			{
				officer.SetIgnorePlayers(false);
			}
			catch
			{
			}
			try
			{
				NPCAwareness awareness = ((NPC)officer).Awareness;
				if (awareness != null)
				{
					awareness.SetAwarenessActive(true);
				}
			}
			catch
			{
			}
			ClearBuildingOccupancy((NPC)(object)officer);
			return true;
		}

		public static PoliceOfficer LeaseIdle(Vector3 near)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			PoliceOfficer val = FindCandidate(near, allowPatrol: false);
			if ((Object)(object)val == (Object)null)
			{
				val = PullFromStations(near);
			}
			if ((Object)(object)val == (Object)null)
			{
				val = FindCandidate(near, allowPatrol: true);
			}
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			Leased.Add(((Object)val).GetInstanceID());
			val.AutoDeactivate = false;
			ClearBuildingOccupancy((NPC)(object)val);
			return val;
		}

		public static PoliceOfficer LeaseForResponse(Vector3 stationNear)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			PoliceOfficer val = PullFromStations(stationNear);
			if ((Object)(object)val == (Object)null)
			{
				val = FindCandidatePreferStation(stationNear, allowPatrol: false);
			}
			if ((Object)(object)val == (Object)null)
			{
				val = FindCandidatePreferStation(stationNear, allowPatrol: true);
			}
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			if (IsNearAnyPlayer(((Component)val).transform.position, 35f))
			{
				try
				{
				}
				catch
				{
				}
				PoliceOfficer val2 = FindCandidatePreferStation(stationNear, allowPatrol: true, avoidPlayers: true);
				if ((Object)(object)val2 != (Object)null)
				{
					val = val2;
				}
				else if (!IsAtStation(val) && !IsPooledClone(val))
				{
					return null;
				}
			}
			Leased.Add(((Object)val).GetInstanceID());
			val.AutoDeactivate = false;
			ClearBuildingOccupancy((NPC)(object)val);
			return val;
		}

		private static PoliceOfficer FindCandidatePreferStation(Vector3 near, bool allowPatrol, bool avoidPlayers = false)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			PoliceOfficer result = null;
			float num = float.MaxValue;
			foreach (PoliceOfficer item in EnumerateOfficers())
			{
				if (IsUsable(item) && !IsLeased(item) && (allowPatrol || !IsOnAmbientDuty(item)) && (!avoidPlayers || !IsNearAnyPlayer(((Component)item).transform.position, 35f)))
				{
					float num2 = Vector3.Distance(((Component)item).transform.position, near);
					if (IsPooledClone(item))
					{
						num2 -= 1000f;
					}
					else if (IsAtStation(item))
					{
						num2 -= 400f;
					}
					else if (IsNearAnyPlayer(((Component)item).transform.position, 35f))
					{
						num2 += 500f;
					}
					if (num2 < num)
					{
						num = num2;
						result = item;
					}
				}
			}
			return result;
		}

		private static bool IsNearAnyPlayer(Vector3 pos, float minDist)
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL o