Decompiled source of CoordinatedPolice v0.8.1

Mods/CoordinatedPolice.dll

Decompiled 2 weeks ago
using System;
using System.Diagnostics;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using CoordinatedPolice;
using HarmonyLib;
using Il2CppFishNet;
using Il2CppFishNet.Connection;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppScheduleOne.AvatarFramework.Equipping;
using Il2CppScheduleOne.Combat;
using Il2CppScheduleOne.DevUtilities;
using Il2CppScheduleOne.GameTime;
using Il2CppScheduleOne.Law;
using Il2CppScheduleOne.Map;
using Il2CppScheduleOne.NPCs;
using Il2CppScheduleOne.NPCs.Behaviour;
using Il2CppScheduleOne.Persistence;
using Il2CppScheduleOne.PlayerScripts;
using Il2CppScheduleOne.Police;
using Il2CppSystem;
using Il2CppSystem.Reflection;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.AI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Main), "Coordinated Police", "0.8.1", "holyfurries", null)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("CoordinatedPolice")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.8.1.0")]
[assembly: AssemblyInformationalVersion("0.8.1+4ce08d5f863ae500429d0387c630cf8e90c5a8bc")]
[assembly: AssemblyProduct("CoordinatedPolice")]
[assembly: AssemblyTitle("CoordinatedPolice")]
[assembly: AssemblyVersion("0.8.1.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace CoordinatedPolice
{
	internal static class ConeCop
	{
		private static GameObject? hat;

		private static Mesh? mesh;

		private static Material? orange;

		private static Material? white;

		private static float next_seconds;

		private static bool failed;

		public static void reset()
		{
			if ((Object)(object)hat != (Object)null)
			{
				Object.Destroy((Object)(object)hat);
			}
			if ((Object)(object)mesh != (Object)null)
			{
				Object.Destroy((Object)(object)mesh);
			}
			if ((Object)(object)orange != (Object)null)
			{
				Object.Destroy((Object)(object)orange);
			}
			if ((Object)(object)white != (Object)null)
			{
				Object.Destroy((Object)(object)white);
			}
			hat = null;
			mesh = null;
			orange = null;
			white = null;
			next_seconds = 0f;
			failed = false;
		}

		public static void tick()
		{
			//IL_017d: 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_01a5: Unknown result type (might be due to invalid IL or missing references)
			if (failed || Time.unscaledTime < next_seconds || !Singleton<LoadManager>.InstanceExists || !Singleton<LoadManager>.Instance.IsGameLoaded || !NetworkSingleton<TimeManager>.InstanceExists)
			{
				return;
			}
			next_seconds = Time.unscaledTime + 5f;
			try
			{
				PoliceOfficer val = null;
				uint num = uint.MaxValue;
				int num2 = Math.Min(128, PoliceOfficer.Officers.Count);
				for (int i = 0; i < num2; i++)
				{
					PoliceOfficer val2 = PoliceOfficer.Officers[i];
					if (!((Object)(object)val2 == (Object)null) && !string.IsNullOrEmpty(((NPC)val2).ID))
					{
						uint num3 = DailyChance.score(((NPC)val2).ID, NetworkSingleton<TimeManager>.Instance.ElapsedDays);
						if (num3 % 256 == 0 && num3 < num)
						{
							num = num3;
							val = val2;
						}
					}
				}
				if ((Object)(object)val == (Object)null || (Object)(object)((NPC)val).Avatar == (Object)null || (Object)(object)((NPC)val).Avatar.HeadBone == (Object)null)
				{
					if ((Object)(object)hat != (Object)null)
					{
						hat.SetActive(false);
					}
					return;
				}
				if ((Object)(object)hat == (Object)null)
				{
					create();
				}
				Transform headBone = ((NPC)val).Avatar.HeadBone;
				if ((Object)(object)hat.transform.parent != (Object)(object)headBone)
				{
					hat.layer = ((Component)((NPC)val).Avatar).gameObject.layer;
					hat.transform.SetParent(headBone, false);
					hat.transform.localPosition = new Vector3(0f, 0.12f, 0f);
					hat.transform.localRotation = Quaternion.identity;
					hat.transform.localScale = Vector3.one;
					MelonLogger.Msg("Police: An officer has questionable headwear today.");
				}
				hat.SetActive(true);
			}
			catch (Exception ex)
			{
				reset();
				failed = true;
				MelonLogger.Warning("Police: Cone cosmetic disabled for this scene: " + ex.Message);
			}
		}

		private static void create()
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_020c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_0228: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_025b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Unknown result type (might be due to invalid IL or missing references)
			//IL_0290: Unknown result type (might be due to invalid IL or missing references)
			//IL_029c: Expected O, but got Unknown
			//IL_02d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e1: Expected O, but got Unknown
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			Shader obj = Shader.Find("Universal Render Pipeline/Unlit") ?? Shader.Find("Unlit/Color") ?? Shader.Find("Standard");
			if ((Object)(object)obj == (Object)null)
			{
				throw new InvalidOperationException("No cone material shader available.");
			}
			orange = new Material(obj);
			white = new Material(obj);
			orange.color = new Color(1f, 0.3f, 0.03f, 1f);
			white.color = Color.white;
			if (orange.HasProperty("_BaseColor"))
			{
				orange.SetColor("_BaseColor", orange.color);
			}
			if (white.HasProperty("_BaseColor"))
			{
				white.SetColor("_BaseColor", white.color);
			}
			Vector3[] array = (Vector3[])(object)new Vector3[68];
			int[] array2 = new int[198];
			int[] array3 = new int[96];
			int num = 0;
			for (int i = 0; i < 4; i++)
			{
				float num2 = (float)i * 0.12f;
				float num3 = 0.15f * (1f - (float)i / 3f) + 0.012f;
				for (int j = 0; j < 16; j++)
				{
					float x = (float)j * (float)Math.PI / 8f;
					array[i * 16 + j] = new Vector3(MathF.Cos(x) * num3, num2, MathF.Sin(x) * num3);
					if (i != 3)
					{
						int num4 = i * 16 + j;
						int num5 = i * 16 + (j + 1) % 16;
						int[] obj2 = ((i == 1) ? array3 : array2);
						int num6 = ((i == 1) ? (j * 6) : num);
						obj2[num6] = num4;
						obj2[num6 + 1] = num4 + 16;
						obj2[num6 + 2] = num5;
						obj2[num6 + 3] = num5;
						obj2[num6 + 4] = num4 + 16;
						obj2[num6 + 5] = num5 + 16;
						if (i != 1)
						{
							num += 6;
						}
					}
				}
			}
			array[64] = new Vector3(-0.2f, 0f, -0.2f);
			array[65] = new Vector3(-0.2f, 0f, 0.2f);
			array[66] = new Vector3(0.2f, 0f, 0.2f);
			array[67] = new Vector3(0.2f, 0f, -0.2f);
			Array.Copy(new int[6] { 64, 65, 66, 64, 66, 67 }, 0, array2, num, 6);
			mesh = new Mesh
			{
				vertices = Il2CppStructArray<Vector3>.op_Implicit(array),
				subMeshCount = 2
			};
			mesh.SetTriangles(Il2CppStructArray<int>.op_Implicit(array2), 0);
			mesh.SetTriangles(Il2CppStructArray<int>.op_Implicit(array3), 1);
			mesh.RecalculateNormals();
			mesh.RecalculateBounds();
			hat = new GameObject("CoordinatedPolice.Cone");
			hat.AddComponent<MeshFilter>().sharedMesh = mesh;
			((Renderer)hat.AddComponent<MeshRenderer>()).sharedMaterials = Il2CppReferenceArray<Material>.op_Implicit((Material[])(object)new Material[2] { orange, white });
		}
	}
	internal static class DailyChance
	{
		public static uint score(string identity, int day)
		{
			if (identity.Length > 128 || day < 0)
			{
				return uint.MaxValue;
			}
			uint num = 2166136261u;
			foreach (char c in identity)
			{
				num = (num ^ c) * 16777619;
			}
			num = (num ^ (uint)day) * 16777619;
			num ^= num >> 16;
			num *= 2146121005;
			return num ^ (num >> 15);
		}
	}
	internal static class DistrictAllocation
	{
		public static void allocate(int total, ReadOnlySpan<bool> eligible, ReadOnlySpan<int> weights, Span<int> targets)
		{
			if (eligible.Length != 6 || weights.Length != 6 || targets.Length != 6)
			{
				throw new ArgumentException("Exactly six districts are required.");
			}
			if (total < 0 || total > 64)
			{
				throw new ArgumentOutOfRangeException("total");
			}
			targets.Clear();
			int num = 0;
			for (int i = 0; i < 6; i++)
			{
				if (weights[i] < 0 || weights[i] > 10)
				{
					throw new ArgumentOutOfRangeException("weights");
				}
				if (eligible[i])
				{
					num += weights[i];
				}
			}
			if (num == 0)
			{
				return;
			}
			Span<int> span = stackalloc int[6];
			int num2 = 0;
			for (int j = 0; j < 6; j++)
			{
				int num3 = (eligible[j] ? (total * weights[j]) : 0);
				targets[j] = num3 / num;
				span[j] = num3 % num;
				num2 += targets[j];
			}
			for (int k = num2; k < total; k++)
			{
				int num4 = -1;
				for (int l = 0; l < 6; l++)
				{
					if (eligible[l] && weights[l] > 0 && (num4 < 0 || span[l] > span[num4]))
					{
						num4 = l;
					}
				}
				if (num4 < 0)
				{
					throw new InvalidOperationException("No eligible district for remaining patrol allocation.");
				}
				targets[num4]++;
				span[num4] = -1;
			}
		}

		public static int choose_route(ReadOnlySpan<int> districts, ReadOnlySpan<int> members, ReadOnlySpan<bool> available, ReadOnlySpan<int> actual, ReadOnlySpan<int> targets, int cursor)
		{
			if (districts.Length > 64 || districts.Length != members.Length || districts.Length != available.Length || actual.Length != 6 || targets.Length != 6)
			{
				throw new ArgumentException("Invalid district route data.");
			}
			int num = -1;
			int num2 = 0;
			for (int i = 0; i < districts.Length; i++)
			{
				int num3 = (Math.Max(cursor, 0) % districts.Length + i) % districts.Length;
				int num4 = districts[num3];
				if (available[num3] && num4 >= 0 && num4 < 6)
				{
					int num5 = targets[num4] - actual[num4];
					if (num5 > 0 && (num < 0 || (num5 >= num2 && (num5 != num2 || members[num3] < members[num]))))
					{
						num = num3;
						num2 = num5;
					}
				}
			}
			return num;
		}
	}
	internal readonly record struct PursuitAssignment(OfficerRole role, Vector2 offset, bool redirect);
	internal static class Interception
	{
		public static PursuitAssignment assign(ReadOnlySpan<Vector2> offsets, int index, Vector2 velocity)
		{
			if (offsets.Length > 128 || index < 0 || index >= offsets.Length)
			{
				throw new ArgumentOutOfRangeException("index");
			}
			Vector2 vector = offsets[index];
			float num = vector.LengthSquared();
			if (!float.IsFinite(num))
			{
				return new PursuitAssignment(OfficerRole.Chaser, default(Vector2), redirect: false);
			}
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			int num5 = 0;
			float num6 = velocity.Length();
			int num7;
			Vector2 vector2;
			if (float.IsFinite(num6) && num6 >= 0.75f)
			{
				num7 = ((num6 <= 12f) ? 1 : 0);
				if (num7 != 0)
				{
					vector2 = velocity / num6;
					goto IL_009d;
				}
			}
			else
			{
				num7 = 0;
			}
			vector2 = Vector2.UnitX;
			goto IL_009d;
			IL_009d:
			Vector2 vector3 = vector2;
			Vector2 vector4 = new Vector2(vector3.Y, 0f - vector3.X);
			float num8 = Vector2.Dot(vector, vector4);
			bool flag = num7 != 0 && eligible(vector, vector3, vector4);
			int num9 = approach_sector(vector);
			for (int i = 0; i < offsets.Length; i++)
			{
				float num10 = offsets[i].LengthSquared();
				if (!float.IsFinite(num10))
				{
					continue;
				}
				num2++;
				if (num10 < num || (num10 == num && i < index))
				{
					num3++;
					if (approach_sector(offsets[i]) == num9 && num10 >= 144f)
					{
						num4++;
					}
					if (flag && eligible(offsets[i], vector3, vector4) && Vector2.Dot(offsets[i], vector4) < 0f == num8 < 0f)
					{
						num5++;
					}
				}
			}
			if (num3 < ((num2 < 5) ? 1 : 2))
			{
				return new PursuitAssignment(OfficerRole.Chaser, default(Vector2), redirect: false);
			}
			if (num < 144f)
			{
				return new PursuitAssignment(OfficerRole.Support, default(Vector2), redirect: false);
			}
			if (flag && num5 < 4)
			{
				float num11 = Math.Clamp(num6 * 1.5f, 3f, 10f);
				Vector2 offset = vector3 * num11 + vector4 * ((num8 < 0f) ? (-1f) : 1f) * (4f + (float)num5 * 3f);
				return new PursuitAssignment(OfficerRole.Interceptor, offset, redirect: true);
			}
			if (num4 >= 16)
			{
				return new PursuitAssignment(OfficerRole.Support, default(Vector2), redirect: false);
			}
			float x = (float)num9 * (float)Math.PI / 4f + (float)(num4 % 3 - 1) * (float)Math.PI / 12f;
			float num12 = 6f + (float)(num4 / 3) * 3f;
			Vector2 offset2 = new Vector2(MathF.Cos(x), MathF.Sin(x)) * num12;
			return new PursuitAssignment(OfficerRole.Support, offset2, MathF.Sqrt(num) > num12 + 3f);
		}

		private static bool eligible(Vector2 relative, Vector2 direction, Vector2 side)
		{
			if (relative.LengthSquared() < 144f)
			{
				return false;
			}
			float num = Vector2.Dot(relative, direction);
			if (!(num >= 0f))
			{
				if (num >= -4f)
				{
					return Math.Abs(Vector2.Dot(relative, side)) >= 6f;
				}
				return false;
			}
			return true;
		}

		private static int approach_sector(Vector2 relative)
		{
			return ((int)MathF.Round(MathF.Atan2(relative.Y, relative.X) * 4f / (float)Math.PI) + 8) % 8;
		}
	}
	public sealed class Main : MelonMod
	{
		private struct SearchCache
		{
			public string? player_code;

			public int slot;

			public Vector3 origin;

			public Vector3 destination;

			public float expires_seconds;

			public bool valid;
		}

		private struct ResponseLog
		{
			public string? player_code;

			public ResponsePhase phase;

			public ResponseSeverity severity;

			public int active_limit;

			public float next_seconds;
		}

		private const int player_limit = 16;

		private const int officer_limit = 128;

		private static readonly ResponseState[] responses = new ResponseState[16];

		private static readonly PoliceOfficer?[] officers = (PoliceOfficer?[])(object)new PoliceOfficer[128];

		private static readonly float[] redirects_resume_seconds = new float[128];

		private static readonly float[] intercept_seconds = new float[128];

		private static readonly Vector3[] intercept_origins = (Vector3[])(object)new Vector3[128];

		private static readonly Vector3[] intercept_positions = (Vector3[])(object)new Vector3[128];

		private static readonly OfficerRole[] pursuit_roles = new OfficerRole[128];

		private static readonly bool[] intercept_valid = new bool[128];

		private static readonly string?[] intercept_targets = new string[128];

		private static readonly StuckRecovery[] recoveries = new StuckRecovery[128];

		private static readonly SearchCache[] searches = new SearchCache[128];

		private static readonly ResponseLog[] response_logs = new ResponseLog[16];

		private bool? logged_server;

		private int logged_players = -1;

		private static int officer_count;

		private static bool running;

		private float next_tick;

		private static int search_queries_count;

		private static float search_queries_reset_seconds;

		internal static bool authoritative
		{
			get
			{
				if (running)
				{
					return InstanceFinder.IsServer;
				}
				return false;
			}
		}

		public override void OnInitializeMelon()
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Expected O, but got Unknown
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Expected O, but got Unknown
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Expected O, but got Unknown
			PoliceConfiguration.load();
			for (int i = 0; i < responses.Length; i++)
			{
				responses[i] = new ResponseState();
			}
			for (int j = 0; j < recoveries.Length; j++)
			{
				recoveries[j] = new StuckRecovery();
			}
			((MelonBase)this).HarmonyInstance.Patch((MethodBase)AccessTools.Method(typeof(NPCMovement), "SetDestination", new Type[1] { typeof(Vector3) }, (Type[])null), new HarmonyMethod(typeof(Main), "redirect_destination", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			((MelonBase)this).HarmonyInstance.Patch((MethodBase)AccessTools.Method(typeof(PoliceStation), "Dispatch", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Main), "coordinate_dispatch", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			((MelonBase)this).HarmonyInstance.Patch((MethodBase)AccessTools.Method(typeof(CombatBehaviour), "GetNextSearchLocation", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Main), "redirect_search", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			PoliceTactics.install(((MelonBase)this).HarmonyInstance);
		}

		public override void OnSceneWasInitialized(int buildIndex, string sceneName)
		{
			if (sceneName != "Main")
			{
				return;
			}
			running = true;
			foreach (MelonMod registeredMelon in MelonTypeBase<MelonMod>.RegisteredMelons)
			{
				if (((MelonBase)registeredMelon).Info.Name.Contains("PoliceResponseOverhaul", StringComparison.OrdinalIgnoreCase) || ((MelonBase)registeredMelon).Info.Name.Contains("Police Response Overhaul", StringComparison.OrdinalIgnoreCase) || ((MelonBase)registeredMelon).Info.Name.Contains("HardcorePolice", StringComparison.OrdinalIgnoreCase))
				{
					running = false;
					((MelonBase)this).LoggerInstance.Error("Police: Disable other police overhauls before testing Coordinated Police.");
				}
			}
		}

		public override void OnSceneWasUnloaded(int buildIndex, string sceneName)
		{
			if (!(sceneName != "Main"))
			{
				running = false;
				officer_count = 0;
				logged_server = null;
				logged_players = -1;
				Array.Clear(response_logs, 0, response_logs.Length);
				ConeCop.reset();
				PoliceTactics.reset();
				PolicePopulation.reset();
				PoliceNavigation.reset();
				Array.Clear(redirects_resume_seconds, 0, redirects_resume_seconds.Length);
				Array.Clear(officers, 0, officers.Length);
				Array.Clear(intercept_seconds, 0, intercept_seconds.Length);
				Array.Clear(intercept_targets, 0, intercept_targets.Length);
				Array.Clear(searches, 0, searches.Length);
				ResponseState[] array = responses;
				for (int i = 0; i < array.Length; i++)
				{
					array[i].reset();
				}
				StuckRecovery[] array2 = recoveries;
				for (int i = 0; i < array2.Length; i++)
				{
					array2[i].reset();
				}
				next_tick = 0f;
				search_queries_count = 0;
				search_queries_reset_seconds = 0f;
			}
		}

		public override void OnUpdate()
		{
			//IL_0268: Unknown result type (might be due to invalid IL or missing references)
			//IL_026e: Invalid comparison between Unknown and I4
			if (!running || Time.time < next_tick)
			{
				return;
			}
			next_tick = Time.time + 1f;
			try
			{
				bool isServer = InstanceFinder.IsServer;
				int count = Player.PlayerList.Count;
				if (logged_server != isServer || logged_players != count)
				{
					((MelonBase)this).LoggerInstance.Msg($"Police: Network authority={(isServer ? "host" : "client/waiting")}, players={count}, scaling_players={Math.Clamp(count, 1, 4)}. {(isServer ? "Response decisions run here." : "Response decisions require the host; this peer does not dispatch police.")}");
					logged_server = isServer;
					logged_players = count;
				}
				ConeCop.tick();
				PolicePopulation.tick();
				if (!isServer)
				{
					return;
				}
				officer_count = Math.Min(PoliceOfficer.Officers.Count, 128);
				for (int i = 0; i < officer_count; i++)
				{
					PoliceOfficer val = PoliceOfficer.Officers[i];
					if ((Object)(object)officers[i] != (Object)(object)val)
					{
						redirects_resume_seconds[i] = 0f;
						intercept_seconds[i] = 0f;
						intercept_valid[i] = false;
						recoveries[i].reset();
						searches[i] = default(SearchCache);
					}
					officers[i] = val;
				}
				recover_stuck_officers();
				int num = Math.Min(Player.PlayerList.Count, 16);
				for (int j = 0; j < responses.Length; j++)
				{
					bool flag = false;
					for (int k = 0; k < num; k++)
					{
						Player val2 = Player.PlayerList[k];
						if ((Object)(object)val2 != (Object)null && val2.PlayerCode == responses[j].player_code)
						{
							flag = true;
						}
					}
					if (!flag)
					{
						responses[j].reset();
					}
				}
				for (int l = 0; l < num; l++)
				{
					Player val3 = Player.PlayerList[l];
					if (!((Object)(object)val3 == (Object)null))
					{
						bool create = (Object)(object)val3.CrimeData != (Object)null && (int)val3.CrimeData.CurrentPursuitLevel > 0;
						ResponseState responseState = get_response(val3.PlayerCode, create);
						if (responseState != null)
						{
							update_response(val3, responseState);
						}
					}
				}
				log_responses();
			}
			catch (Exception value)
			{
				running = false;
				((MelonBase)this).LoggerInstance.Error($"Police: Disabled after a game API failure: {value}");
			}
		}

		private static void log_responses()
		{
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < responses.Length; i++)
			{
				ResponseState responseState = responses[i];
				ref ResponseLog reference = ref response_logs[i];
				if (reference.player_code != responseState.player_code && !string.IsNullOrEmpty(reference.player_code))
				{
					MelonLogger.Msg("Police: Response ended player=" + reference.player_code + ".");
				}
				if (responseState.phase == ResponsePhase.Inactive)
				{
					reference = default(ResponseLog);
				}
				else
				{
					if (!(reference.player_code != responseState.player_code) && reference.phase == responseState.phase && reference.severity == responseState.severity && reference.active_limit == responseState.rules.active_limit && Time.time < reference.next_seconds)
					{
						continue;
					}
					Vector3 last_known_position = responseState.last_known_position;
					PoliceStation closestPoliceStation = PoliceStation.GetClosestPoliceStation(new Vector3(last_known_position.X, last_known_position.Y, last_known_position.Z));
					int value = ((!((Object)(object)closestPoliceStation == (Object)null)) ? closestPoliceStation.OfficerPool.Count : 0);
					int value2 = 0;
					bool value3 = false;
					int num = Math.Min(Player.PlayerList.Count, 16);
					for (int j = 0; j < num; j++)
					{
						Player val = Player.PlayerList[j];
						if (!((Object)(object)val == (Object)null) && !(val.PlayerCode != responseState.player_code))
						{
							(value2, value3, _, _) = read_pursuit(val);
							break;
						}
					}
					MelonLogger.Msg($"Police: Response player={responseState.player_code}, tier={responseState.severity}, phase={responseState.phase}, visible={value3}, active={value2}/{responseState.rules.active_limit}, allowance_used={responseState.dispatch_count}/{responseState.rules.dispatch_limit}, next_call_seconds={Math.Max(0f, responseState.dispatch_seconds - Time.time):F1}, station_pool={value}, search_wave_eligible={responseState.can_send_search_wave(Time.time)}.");
					reference = new ResponseLog
					{
						player_code = responseState.player_code,
						phase = responseState.phase,
						severity = responseState.severity,
						active_limit = responseState.rules.active_limit,
						next_seconds = Time.time + 10f
					};
				}
			}
		}

		private static void recover_stuck_officers()
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: 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)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: 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_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			for (int i = 0; i < officer_count; i++)
			{
				PoliceOfficer val = officers[i];
				if (!available(val) || ((!((Behaviour)val.PursuitBehaviour).Active || !((Object)(object)val.PursuitBehaviour.TargetPlayer != (Object)null)) && (!((Object)(object)val.FootPatrolBehaviour != (Object)null) || !((Behaviour)val.FootPatrolBehaviour).Active)))
				{
					recoveries[i].reset();
					continue;
				}
				NPCMovement movement = ((NPC)val).Movement;
				NavMeshAgent agent = movement.Agent;
				Vector3 footPosition = movement.FootPosition;
				Vector3 val2 = movement.CurrentDestination;
				if ((Object)(object)agent != (Object)null && ((Behaviour)agent).enabled && agent.isOnNavMesh && agent.pathPending)
				{
					continue;
				}
				int num2;
				if (movement.HasDestination && !movement.IsPaused && !movement.IsOnLadder && !movement.Disoriented && movement.CanMove() && (Object)(object)agent != (Object)null && ((Behaviour)agent).enabled && agent.isOnNavMesh && !agent.isStopped && !agent.isOnOffMeshLink)
				{
					Vector3 val3 = val2 - footPosition;
					num2 = ((((Vector3)(ref val3)).sqrMagnitude > 4f) ? 1 : 0);
				}
				else
				{
					num2 = 0;
				}
				bool flag = (byte)num2 != 0;
				if ((num >= 2 && flag) || !recoveries[i].try_repath(Time.time, new Vector3(footPosition.x, footPosition.y, footPosition.z), flag))
				{
					continue;
				}
				num++;
				redirects_resume_seconds[i] = Time.time + 12f;
				intercept_seconds[i] = 0f;
				intercept_valid[i] = false;
				searches[i] = default(SearchCache);
				PursuitBehaviour pursuitBehaviour = val.PursuitBehaviour;
				if (((Behaviour)pursuitBehaviour).Active && (Object)(object)pursuitBehaviour.TargetPlayer != (Object)null)
				{
					if (((CombatBehaviour)pursuitBehaviour).IsTargetImmediatelyVisible)
					{
						val2 = ((Component)pursuitBehaviour.TargetPlayer).transform.position;
					}
					else
					{
						ResponseState responseState = get_response(pursuitBehaviour.TargetPlayer.PlayerCode, create: false);
						if (responseState != null && responseState.phase != ResponsePhase.Inactive)
						{
							Vector3 last_known_position = responseState.last_known_position;
							((Vector3)(ref val2))..ctor(last_known_position.X, last_known_position.Y, last_known_position.Z);
						}
					}
				}
				if (PoliceNavigation.try_destination(movement, val2, out var destination))
				{
					movement.SetDestination(destination);
					MelonLogger.Msg($"Police: Navigation recovery requested officer={((NPC)val).ID}, duty={(((Behaviour)pursuitBehaviour).Active ? "pursuit" : "patrol")}, custom_offsets_paused_seconds=12.");
				}
				else
				{
					MelonLogger.Warning($"Police: Navigation recovery has no complete safe path officer={((NPC)val).ID}, path_status={agent.pathStatus}; custom offsets paused for 12 seconds.");
				}
			}
		}

		private static void update_response(Player player, ResponseState response)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_0264: 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_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player.CrimeData == (Object)null || !player.IsCurrentlyTargetable || player.IsArrested || (Object)(object)player.Health == (Object)null || !player.Health.IsAlive)
			{
				response.reset();
				return;
			}
			if ((int)player.CrimeData.CurrentPursuitLevel == 0)
			{
				if (response.phase != ResponsePhase.Responding || !(Time.time - response.started_seconds < 30f))
				{
					response.reset();
				}
				return;
			}
			(int, bool, bool, Vector3) snapshot = read_pursuit(player);
			observe_response(player, response, snapshot);
			if ((!snapshot.Item2 && !response.can_send_search_wave(Time.time)) || Time.time < response.dispatch_seconds || response.dispatch_count >= response.rules.dispatch_limit)
			{
				return;
			}
			if (!snapshot.Item2)
			{
				Vector3 last_known_position = response.last_known_position;
				PoliceStation closestPoliceStation = PoliceStation.GetClosestPoliceStation(new Vector3(last_known_position.X, last_known_position.Y, last_known_position.Z));
				if ((Object)(object)closestPoliceStation != (Object)null && closestPoliceStation.OfficerPool.Count > 0)
				{
					closestPoliceStation.Dispatch(response.rules.burst_limit, player, (EDispatchType)2, false);
				}
				return;
			}
			PoliceOfficer val = null;
			float num = 3600f;
			for (int i = 0; i < officer_count; i++)
			{
				PoliceOfficer val2 = officers[i];
				if (available(val2) && !((Behaviour)val2.PursuitBehaviour).Active && (!((Object)(object)val2.VehiclePursuitBehaviour != (Object)null) || !((Behaviour)val2.VehiclePursuitBehaviour).Active) && !((Object)(object)val2.PursuitTarget != (Object)null))
				{
					Vector3 val3 = ((Component)val2).transform.position - snapshot.Item4;
					float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude;
					if (!(sqrMagnitude >= num))
					{
						val = val2;
						num = sqrMagnitude;
					}
				}
			}
			if ((Object)(object)val != (Object)null)
			{
				if (response.request_dispatch(Time.time, snapshot.Item1, 1, DispatchSource.Nearby) > 0)
				{
					val.BeginFootPursuit_Networked(player.PlayerCode, true);
					MelonLogger.Msg($"Police: Nearby pursuit requested player={player.PlayerCode}, officers=1, allowance_used={response.dispatch_count}/{response.rules.dispatch_limit}.");
				}
			}
			else
			{
				PoliceStation closestPoliceStation2 = PoliceStation.GetClosestPoliceStation(snapshot.Item4);
				if ((Object)(object)closestPoliceStation2 != (Object)null && closestPoliceStation2.OfficerPool.Count > 0)
				{
					closestPoliceStation2.Dispatch(response.rules.burst_limit, player, (EDispatchType)0, true);
				}
			}
		}

		private static (int count, bool visible, bool searching, Vector3 witness) read_pursuit(Player player)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			bool item = false;
			bool item2 = false;
			Vector3 item3 = default(Vector3);
			int num2 = Math.Min(PoliceOfficer.Officers.Count, 128);
			for (int i = 0; i < num2; i++)
			{
				PoliceOfficer val = PoliceOfficer.Officers[i];
				if ((Object)(object)val == (Object)null || !((Component)val).gameObject.activeInHierarchy || !((NPC)val).IsConscious)
				{
					continue;
				}
				PursuitBehaviour pursuitBehaviour = val.PursuitBehaviour;
				VehiclePursuitBehaviour vehiclePursuitBehaviour = val.VehiclePursuitBehaviour;
				bool flag = (Object)(object)pursuitBehaviour != (Object)null && ((Behaviour)pursuitBehaviour).Active && (Object)(object)pursuitBehaviour.TargetPlayer != (Object)null && pursuitBehaviour.TargetPlayer.PlayerCode == player.PlayerCode;
				bool flag2 = (Object)(object)vehiclePursuitBehaviour != (Object)null && ((Behaviour)vehiclePursuitBehaviour).Active && (Object)(object)vehiclePursuitBehaviour.Target != (Object)null && vehiclePursuitBehaviour.Target.PlayerCode == player.PlayerCode;
				if (flag || flag2)
				{
					num++;
					if (flag && ((CombatBehaviour)pursuitBehaviour).IsSearching)
					{
						item2 = true;
					}
					if ((flag && ((CombatBehaviour)pursuitBehaviour).IsTargetImmediatelyVisible) || (flag2 && vehiclePursuitBehaviour.IsTargetImmediatelyVisible))
					{
						item = true;
						item3 = ((Component)val).transform.position;
					}
				}
			}
			return (count: num, visible: item, searching: item2, witness: item3);
		}

		private static void observe_response(Player player, ResponseState response, (int count, bool visible, bool searching, Vector3 witness) snapshot)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected I4, but got Unknown
			//IL_0043: 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_0048: 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_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			response.set_player_count(Math.Min(Player.PlayerList.Count, 16));
			response.observe_level((int)player.CrimeData.CurrentPursuitLevel);
			Vector3 val = (snapshot.visible ? ((Component)player).transform.position : player.CrimeData.LastKnownPosition);
			response.observe(snapshot.visible, snapshot.searching, new Vector3(val.x, val.y, val.z), Time.time);
		}

		internal static ResponseState? get_response(string player_code, bool create)
		{
			if (string.IsNullOrEmpty(player_code))
			{
				return null;
			}
			ResponseState responseState = null;
			for (int i = 0; i < responses.Length; i++)
			{
				ResponseState responseState2 = responses[i];
				if (responseState2.player_code == player_code)
				{
					return responseState2;
				}
				if (responseState == null && responseState2.player_code.Length == 0)
				{
					responseState = responseState2;
				}
			}
			if (!create || responseState == null)
			{
				return null;
			}
			responseState.begin(player_code, Time.time);
			return responseState;
		}

		private static bool coordinate_dispatch(PoliceStation __instance, ref int __0, Player __1, ref EDispatchType __2, ref bool __3)
		{
			if (!running || !InstanceFinder.IsServer || (Object)(object)__1 == (Object)null || __0 <= 0)
			{
				return true;
			}
			try
			{
				if ((Object)(object)__1.CrimeData == (Object)null || __1.IsArrested || (Object)(object)__1.Health == (Object)null || !__1.Health.IsAlive)
				{
					return false;
				}
				ResponseState responseState = get_response(__1.PlayerCode, create: true);
				if (responseState == null)
				{
					return false;
				}
				(int, bool, bool, Vector3) snapshot = read_pursuit(__1);
				observe_response(__1, responseState, snapshot);
				if (responseState.phase == ResponsePhase.Searching)
				{
					__2 = (EDispatchType)2;
					__3 = false;
				}
				int requested_count = Math.Min(__0, __instance.OfficerPool.Count);
				__0 = responseState.request_dispatch(Time.time, snapshot.Item1, requested_count, DispatchSource.Station);
				if (__0 == 0)
				{
					return false;
				}
				if (__0 % 2 != 0)
				{
					__2 = (EDispatchType)2;
				}
				MelonLogger.Msg($"Police: Station dispatch approved player={__1.PlayerCode}, officers={__0}, type={__2}, sighted={__3}, phase={responseState.phase}, pool_before={__instance.OfficerPool.Count}, allowance_used={responseState.dispatch_count}/{responseState.rules.dispatch_limit}. Arrival is not confirmed.");
				return true;
			}
			catch (Exception value)
			{
				running = false;
				MelonLogger.Error($"Police: Disabled dispatch coordination after a game API failure: {value}");
				return false;
			}
		}

		private static bool redirect_search(CombatBehaviour __instance, ref Vector3 __result)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_027a: Unknown result type (might be due to invalid IL or missing references)
			//IL_037d: Unknown result type (might be due to invalid IL or missing references)
			//IL_037f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0346: Unknown result type (might be due to invalid IL or missing references)
			//IL_034b: Unknown result type (might be due to invalid IL or missing references)
			//IL_034d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0352: Unknown result type (might be due to invalid IL or missing references)
			if (!running || !InstanceFinder.IsServer)
			{
				return true;
			}
			try
			{
				PursuitBehaviour val = ((Il2CppObjectBase)__instance).TryCast<PursuitBehaviour>();
				if ((Object)(object)val == (Object)null || !((Behaviour)val).Active)
				{
					return true;
				}
				Player targetPlayer = val.TargetPlayer;
				if ((Object)(object)targetPlayer == (Object)null || (Object)(object)targetPlayer.CrimeData == (Object)null || (int)targetPlayer.CrimeData.CurrentPursuitLevel == 0)
				{
					return true;
				}
				ResponseState responseState = get_response(targetPlayer.PlayerCode, create: true);
				if (responseState == null)
				{
					return true;
				}
				(int, bool, bool, Vector3) snapshot = read_pursuit(targetPlayer);
				observe_response(targetPlayer, responseState, snapshot);
				if (responseState.phase != ResponsePhase.Searching)
				{
					return true;
				}
				int num = 0;
				int num2 = -1;
				for (int i = 0; i < officer_count; i++)
				{
					PoliceOfficer val2 = officers[i];
					if (!available(val2))
					{
						continue;
					}
					PursuitBehaviour pursuitBehaviour = val2.PursuitBehaviour;
					if (((Behaviour)pursuitBehaviour).Active && !((Object)(object)pursuitBehaviour.TargetPlayer == (Object)null) && !(pursuitBehaviour.TargetPlayer.PlayerCode != targetPlayer.PlayerCode))
					{
						if (((Il2CppObjectBase)pursuitBehaviour).Pointer == ((Il2CppObjectBase)val).Pointer)
						{
							num2 = i;
							break;
						}
						num++;
					}
				}
				if (num2 < 0)
				{
					return true;
				}
				Vector3 last_known_position = responseState.last_known_position;
				Vector3 val3 = default(Vector3);
				((Vector3)(ref val3))..ctor(last_known_position.X, last_known_position.Y, last_known_position.Z);
				ref SearchCache reference = ref searches[num2];
				Vector3 val4;
				if (!(reference.player_code != targetPlayer.PlayerCode) && reference.slot == num)
				{
					val4 = reference.origin - val3;
					if (!(((Vector3)(ref val4)).sqrMagnitude > 0.01f) && !(Time.time >= reference.expires_seconds))
					{
						goto IL_03a4;
					}
				}
				if (Time.time >= search_queries_reset_seconds)
				{
					search_queries_count = 0;
					search_queries_reset_seconds = Time.time + 1f;
				}
				if (search_queries_count >= 8)
				{
					return true;
				}
				reference = new SearchCache
				{
					player_code = targetPlayer.PlayerCode,
					slot = num,
					origin = val3,
					expires_seconds = Time.time + 6f
				};
				Vector3 candidate = default(Vector3);
				for (int j = 0; j < 4; j++)
				{
					if (search_queries_count >= 8)
					{
						break;
					}
					search_queries_count++;
					Vector3 vector = SearchPattern.destination(last_known_position, num, Time.time - responseState.search_started_seconds, j);
					((Vector3)(ref candidate))..ctor(vector.X, vector.Y, vector.Z);
					if (!PoliceNavigation.try_destination(((NPC)officers[num2]).Movement, candidate, out var destination))
					{
						continue;
					}
					bool flag = false;
					for (int k = 0; k < officer_count; k++)
					{
						if (k == num2 || !searches[k].valid || searches[k].player_code != targetPlayer.PlayerCode || Time.time >= searches[k].expires_seconds || !available(officers[k]))
						{
							continue;
						}
						PursuitBehaviour pursuitBehaviour2 = officers[k].PursuitBehaviour;
						if (((Behaviour)pursuitBehaviour2).Active && !((Object)(object)pursuitBehaviour2.TargetPlayer == (Object)null) && !(pursuitBehaviour2.TargetPlayer.PlayerCode != targetPlayer.PlayerCode))
						{
							val4 = searches[k].destination - destination;
							if (((Vector3)(ref val4)).sqrMagnitude < 2.25f)
							{
								flag = true;
							}
						}
					}
					if (!flag)
					{
						reference.destination = destination;
						reference.valid = true;
						break;
					}
				}
				goto IL_03a4;
				IL_03a4:
				if (!reference.valid)
				{
					return true;
				}
				__result = reference.destination;
				return false;
			}
			catch (Exception value)
			{
				running = false;
				MelonLogger.Error($"Police: Disabled search coordination after a game API failure: {value}");
				return true;
			}
		}

		internal static void disable(string message)
		{
			running = false;
			MelonLogger.Error(message);
		}

		private static bool available(PoliceOfficer? officer)
		{
			if ((Object)(object)officer != (Object)null && ((Component)officer).gameObject.activeInHierarchy && ((NPC)officer).IsConscious && !((NPC)officer).IsInVehicle && (Object)(object)officer.PursuitBehaviour != (Object)null)
			{
				return (Object)(object)((NPC)officer).Movement != (Object)null;
			}
			return false;
		}

		internal static OfficerRole pursuit_role(PursuitBehaviour pursuit, int index)
		{
			if (index < 0 || index >= officer_count || !available(officers[index]) || ((Il2CppObjectBase)officers[index].PursuitBehaviour).Pointer != ((Il2CppObjectBase)pursuit).Pointer || (Object)(object)pursuit.TargetPlayer == (Object)null || intercept_targets[index] != pursuit.TargetPlayer.PlayerCode || Time.time >= intercept_seconds[index])
			{
				return OfficerRole.Chaser;
			}
			return pursuit_roles[index];
		}

		private static void redirect_destination(NPCMovement __instance, ref Vector3 __0)
		{
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0308: Unknown result type (might be due to invalid IL or missing references)
			//IL_0301: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_030d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0317: Unknown result type (might be due to invalid IL or missing references)
			//IL_031e: Unknown result type (might be due to invalid IL or missing references)
			//IL_035a: Unknown result type (might be due to invalid IL or missing references)
			//IL_035c: Unknown result type (might be due to invalid IL or missing references)
			//IL_035e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0363: Unknown result type (might be due to invalid IL or missing references)
			//IL_0366: Unknown result type (might be due to invalid IL or missing references)
			//IL_036b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			//IL_0265: Unknown result type (might be due to invalid IL or missing references)
			//IL_0267: Unknown result type (might be due to invalid IL or missing references)
			//IL_026c: Unknown result type (might be due to invalid IL or missing references)
			//IL_026e: Unknown result type (might be due to invalid IL or missing references)
			//IL_03cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_028a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0291: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0410: Unknown result type (might be due to invalid IL or missing references)
			//IL_0415: Unknown result type (might be due to invalid IL or missing references)
			//IL_041a: Unknown result type (might be due to invalid IL or missing references)
			//IL_039d: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0381: Unknown result type (might be due to invalid IL or missing references)
			//IL_0386: Unknown result type (might be due to invalid IL or missing references)
			//IL_0388: Unknown result type (might be due to invalid IL or missing references)
			//IL_038d: Unknown result type (might be due to invalid IL or missing references)
			//IL_041e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0423: Unknown result type (might be due to invalid IL or missing references)
			//IL_0433: Unknown result type (might be due to invalid IL or missing references)
			//IL_043a: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_04db: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0492: Unknown result type (might be due to invalid IL or missing references)
			//IL_0497: Unknown result type (might be due to invalid IL or missing references)
			//IL_0499: Unknown result type (might be due to invalid IL or missing references)
			//IL_049e: Unknown result type (might be due to invalid IL or missing references)
			if (!running || !InstanceFinder.IsServer)
			{
				return;
			}
			try
			{
				int num = -1;
				PoliceOfficer val = null;
				for (int i = 0; i < officer_count; i++)
				{
					PoliceOfficer val2 = officers[i];
					if (available(val2) && !(((Il2CppObjectBase)((NPC)val2).Movement).Pointer != ((Il2CppObjectBase)__instance).Pointer))
					{
						val = val2;
						num = i;
						break;
					}
				}
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				PursuitBehaviour pursuitBehaviour = val.PursuitBehaviour;
				Player targetPlayer = pursuitBehaviour.TargetPlayer;
				if (!((Behaviour)pursuitBehaviour).Active || (Object)(object)targetPlayer == (Object)null || (Object)(object)targetPlayer.CrimeData == (Object)null || (int)targetPlayer.CrimeData.CurrentPursuitLevel == 0)
				{
					return;
				}
				if (!((CombatBehaviour)pursuitBehaviour).IsTargetImmediatelyVisible && !read_pursuit(targetPlayer).visible)
				{
					intercept_valid[num] = false;
					intercept_seconds[num] = 0f;
					redirect_search((CombatBehaviour)(object)pursuitBehaviour, ref __0);
					return;
				}
				if (targetPlayer.IsInVehicle || Time.time < redirects_resume_seconds[num])
				{
					return;
				}
				Vector3 position = ((Component)targetPlayer).transform.position;
				Vector3 val3 = __0 - position;
				if (((Vector3)(ref val3)).sqrMagnitude > 100f)
				{
					return;
				}
				if (Time.time < intercept_seconds[num])
				{
					if (intercept_valid[num] && intercept_targets[num] == targetPlayer.PlayerCode)
					{
						val3 = intercept_origins[num] - __0;
						if (((Vector3)(ref val3)).sqrMagnitude <= 4f)
						{
							__0 = intercept_positions[num];
						}
					}
					return;
				}
				intercept_valid[num] = false;
				intercept_seconds[num] = Time.time + 1f;
				Span<Vector2> span = stackalloc Vector2[officer_count];
				span.Fill(new Vector2(float.NaN, float.NaN));
				Vector2 zero = Vector2.Zero;
				Vector3 position2 = ((Component)val).transform.position;
				for (int j = 0; j < officer_count; j++)
				{
					PoliceOfficer val4 = officers[j];
					if (!available(val4))
					{
						continue;
					}
					PursuitBehaviour pursuitBehaviour2 = val4.PursuitBehaviour;
					if (((Behaviour)pursuitBehaviour2).Active && !((Object)(object)pursuitBehaviour2.TargetPlayer == (Object)null) && !(pursuitBehaviour2.TargetPlayer.PlayerCode != targetPlayer.PlayerCode))
					{
						Vector3 val5 = ((Component)val4).transform.position - position;
						if (!(Math.Abs(val5.y) > 2f))
						{
							span[j] = new Vector2(val5.x, val5.z);
							Vector3 val6 = position2 - ((Component)val4).transform.position;
							zero += PursuitSpacing.separation(new Vector2(val6.x, val6.z), num, j);
						}
					}
				}
				Vector3 val7 = (((Object)(object)targetPlayer.VelocityCalculator == (Object)null) ? Vector3.zero : targetPlayer.VelocityCalculator.Velocity);
				PursuitAssignment pursuitAssignment = Interception.assign(span, num, new Vector2(val7.x, val7.z));
				pursuit_roles[num] = pursuitAssignment.role;
				intercept_targets[num] = targetPlayer.PlayerCode;
				if (pursuitAssignment.role == OfficerRole.Chaser)
				{
					return;
				}
				Vector3 val8 = position2 - position;
				Vector3 val9 = __0;
				if (!pursuitAssignment.redirect)
				{
					goto IL_03ca;
				}
				if (pursuitAssignment.role == OfficerRole.Interceptor)
				{
					val3 = __0 - position;
					if (!(((Vector3)(ref val3)).sqrMagnitude <= 16f))
					{
						goto IL_03ca;
					}
				}
				val9 = position + new Vector3(pursuitAssignment.offset.X, 0f, pursuitAssignment.offset.Y);
				goto IL_041c;
				IL_03ca:
				Vector2 vector = PursuitSpacing.lateral_offset(zero, new Vector2(0f - val8.x, 0f - val8.z));
				if (vector.LengthSquared() < 0.0625f)
				{
					return;
				}
				val9 += new Vector3(vector.X, 0f, vector.Y);
				goto IL_041c;
				IL_041c:
				if (!PoliceNavigation.try_redirect(__instance, __0, val9, out var destination) || Math.Abs(destination.y - position.y) > 2f)
				{
					return;
				}
				for (int k = 0; k < officer_count; k++)
				{
					if (k != num && intercept_valid[k] && !(Time.time >= intercept_seconds[k]) && !(intercept_targets[k] != targetPlayer.PlayerCode))
					{
						val3 = intercept_positions[k] - destination;
						if (((Vector3)(ref val3)).sqrMagnitude < 2.25f)
						{
							return;
						}
					}
				}
				intercept_targets[num] = targetPlayer.PlayerCode;
				intercept_origins[num] = __0;
				intercept_positions[num] = destination;
				intercept_valid[num] = true;
				__0 = destination;
			}
			catch (Exception value)
			{
				running = false;
				MelonLogger.Error($"Police: Disabled interception after a game API failure: {value}");
			}
		}
	}
	internal static class PoliceConfiguration
	{
		public static void load()
		{
			MelonPreferences_Category obj = MelonPreferences.CreateCategory("CoordinatedPolice");
			MelonPreferences_Entry<int> val = obj.CreateEntry<int>("patrols_per_player", 5, "Foot patrols per connected player (0-16, up to four players)", (string)null, false, false, (ValueValidator)null, (string)null);
			MelonPreferences_Entry<int> val2 = obj.CreateEntry<int>("reserve_base", 8, "Solo ready reserve target (0-64)", (string)null, false, false, (ValueValidator)null, (string)null);
			MelonPreferences_Entry<int> val3 = obj.CreateEntry<int>("reserves_per_extra_player", 4, "Extra reserves per additional player (0-16)", (string)null, false, false, (ValueValidator)null, (string)null);
			MelonPreferences_Entry<float> val4 = obj.CreateEntry<float>("response_size_multiplier", 1f, "Response limits and burst size multiplier (0.25-2)", (string)null, false, false, (ValueValidator)null, (string)null);
			MelonPreferences_Entry<float> val5 = obj.CreateEntry<float>("reinforcement_time_multiplier", 1f, "Dispatch delays and breaks multiplier (0.25-4; lower is faster)", (string)null, false, false, (ValueValidator)null, (string)null);
			MelonPreferences_Entry<int> val6 = obj.CreateEntry<int>("northtown_weight", 1, "Northtown patrol share (0-10)", (string)null, false, false, (ValueValidator)null, (string)null);
			MelonPreferences_Entry<int> val7 = obj.CreateEntry<int>("westville_weight", 1, "Westville patrol share (0-10)", (string)null, false, false, (ValueValidator)null, (string)null);
			MelonPreferences_Entry<int> val8 = obj.CreateEntry<int>("downtown_weight", 1, "Downtown patrol share (0-10)", (string)null, false, false, (ValueValidator)null, (string)null);
			MelonPreferences_Entry<int> val9 = obj.CreateEntry<int>("docks_weight", 1, "Docks patrol share (0-10)", (string)null, false, false, (ValueValidator)null, (string)null);
			MelonPreferences_Entry<int> val10 = obj.CreateEntry<int>("suburbia_weight", 1, "Suburbia patrol share (0-10)", (string)null, false, false, (ValueValidator)null, (string)null);
			MelonPreferences_Entry<int> val11 = obj.CreateEntry<int>("uptown_weight", 1, "Uptown patrol share (0-10)", (string)null, false, false, (ValueValidator)null, (string)null);
			PoliceSettings obj2 = new PoliceSettings
			{
				patrols_per_player = val.Value,
				reserve_base = val2.Value,
				reserves_per_extra_player = val3.Value,
				response_size = val4.Value,
				reinforcement_time = val5.Value,
				northtown_weight = val6.Value,
				westville_weight = val7.Value,
				downtown_weight = val8.Value,
				docks_weight = val9.Value,
				suburbia_weight = val10.Value,
				uptown_weight = val11.Value
			};
			PoliceSettings policeSettings = (PoliceSettings.current = obj2.validated());
			if (obj2 != policeSettings)
			{
				MelonLogger.Warning("Police: Out-of-range settings were clamped; non-finite multipliers use 1.");
			}
			val.Value = policeSettings.patrols_per_player;
			val2.Value = policeSettings.reserve_base;
			val3.Value = policeSettings.reserves_per_extra_player;
			val4.Value = policeSettings.response_size;
			val5.Value = policeSettings.reinforcement_time;
			val6.Value = policeSettings.northtown_weight;
			val7.Value = policeSettings.westville_weight;
			val8.Value = policeSettings.downtown_weight;
			val9.Value = policeSettings.docks_weight;
			val10.Value = policeSettings.suburbia_weight;
			val11.Value = policeSettings.uptown_weight;
			obj.SaveToFile(false);
			MelonLogger.Msg($"Police: Settings patrols_per_player={policeSettings.patrols_per_player}, reserves={policeSettings.reserve_base}+{policeSettings.reserves_per_extra_player}/extra player, response_size={policeSettings.response_size}, reinforcement_time={policeSettings.reinforcement_time}. Host settings govern gameplay; restart to apply edits.");
		}
	}
	internal static class PoliceDistricts
	{
		public static readonly int[] actual = new int[6];

		public static readonly int[] targets = new int[6];

		private static readonly FootPatrolRoute?[] routes = (FootPatrolRoute?[])(object)new FootPatrolRoute[64];

		private static readonly int[] route_districts = new int[64];

		private static readonly int[] route_members = new int[64];

		private static readonly bool[] route_available = new bool[64];

		private static readonly FootPatrolRoute?[] cached_routes = (FootPatrolRoute?[])(object)new FootPatrolRoute[64];

		private static readonly int[] cached_districts = new int[64];

		private static int route_count;

		private static int cursor;

		public static void reset()
		{
			Array.Clear(routes, 0, routes.Length);
			Array.Clear(cached_routes, 0, cached_routes.Length);
			Array.Clear(actual, 0, actual.Length);
			Array.Clear(targets, 0, targets.Length);
			route_count = 0;
			cursor = 0;
		}

		public static bool prepare(int patrol_target)
		{
			//IL_02b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
			route_count = 0;
			Array.Clear(routes, 0, routes.Length);
			Array.Clear(actual, 0, actual.Length);
			Array.Clear(targets, 0, targets.Length);
			Array.Clear(route_members, 0, route_members.Length);
			if (!Singleton<Map>.InstanceExists || !Singleton<LawController>.InstanceExists)
			{
				return false;
			}
			LawActivitySettings settings = Singleton<LawController>.Instance.GetSettings();
			if (settings == null || settings.Patrols == null)
			{
				return false;
			}
			Span<bool> span = stackalloc bool[6];
			span.Clear();
			Span<int> span2 = stackalloc int[6];
			for (int i = 0; i < 6; i++)
			{
				span2[i] = PoliceSettings.current.weight(i);
			}
			int num = Math.Min(((Il2CppArrayBase<PatrolInstance>)(object)settings.Patrols).Length, routes.Length);
			for (int j = 0; j < num; j++)
			{
				PatrolInstance val = ((Il2CppArrayBase<PatrolInstance>)(object)settings.Patrols)[j];
				if (val == null || (Object)(object)val.Route == (Object)null || val.Route.Waypoints == null || ((Il2CppArrayBase<Transform>)(object)val.Route.Waypoints).Length == 0)
				{
					continue;
				}
				bool flag = false;
				for (int k = 0; k < route_count; k++)
				{
					if ((Object)(object)routes[k] == (Object)(object)val.Route)
					{
						flag = true;
					}
				}
				if (flag)
				{
					continue;
				}
				int num2 = region_for_route(val.Route);
				if (num2 >= 0)
				{
					MapRegionData regionData = Singleton<Map>.Instance.GetRegionData((EMapRegion)num2);
					bool flag2 = regionData != null && regionData.IsUnlocked && span2[num2] > 0;
					routes[route_count] = val.Route;
					route_districts[route_count] = num2;
					route_available[route_count++] = flag2;
					if (flag2)
					{
						span[num2] = true;
					}
				}
			}
			DistrictAllocation.allocate(patrol_target, span, span2, targets);
			int num3 = Math.Min(PoliceOfficer.Officers.Count, 128);
			for (int l = 0; l < num3; l++)
			{
				PoliceOfficer val2 = PoliceOfficer.Officers[l];
				if ((Object)(object)val2 == (Object)null || !((NPC)val2).IsConscious || ((NPC)val2).isInBuilding || (Object)(object)val2.FootPatrolBehaviour == (Object)null || !((Behaviour)val2.FootPatrolBehaviour).Active)
				{
					continue;
				}
				PatrolGroup val3 = val2.FootPatrolBehaviour.Group;
				FootPatrolRoute val4 = ((val3 != null) ? val3.Route : null);
				int num4 = (((Object)(object)val4 != (Object)null) ? region_for_route(val4) : ((int)Singleton<Map>.Instance.GetRegionFromPosition(((Component)val2).transform.position)));
				if (num4 >= 0 && num4 < 6)
				{
					actual[num4]++;
				}
				for (int m = 0; m < route_count; m++)
				{
					if ((Object)(object)routes[m] == (Object)(object)val4)
					{
						route_members[m]++;
					}
				}
			}
			return true;
		}

		public static FootPatrolRoute? next_route(out int district, out int members)
		{
			int num = DistrictAllocation.choose_route(route_districts.AsSpan(0, route_count), route_members.AsSpan(0, route_count), route_available.AsSpan(0, route_count), actual, targets, cursor);
			district = -1;
			members = 0;
			if (num < 0)
			{
				return null;
			}
			cursor = (num + 1) % route_count;
			route_available[num] = false;
			district = route_districts[num];
			members = route_members[num];
			return routes[num];
		}

		public static int region_for_route(FootPatrolRoute route)
		{
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Expected I4, but got Unknown
			int num = -1;
			for (int i = 0; i < cached_routes.Length; i++)
			{
				if ((Object)(object)cached_routes[i] == (Object)(object)route)
				{
					return cached_districts[i];
				}
				if (num < 0 && (Object)(object)cached_routes[i] == (Object)null)
				{
					num = i;
				}
			}
			if (!Singleton<Map>.InstanceExists || route.Waypoints == null || ((Il2CppArrayBase<Transform>)(object)route.Waypoints).Length == 0)
			{
				return -1;
			}
			Span<int> span = stackalloc int[6];
			span.Clear();
			int num2 = Math.Min(((Il2CppArrayBase<Transform>)(object)route.Waypoints).Length, 128);
			int num3 = Math.Min(num2, 8);
			int num4 = -1;
			for (int j = 0; j < num3; j++)
			{
				Transform val = ((Il2CppArrayBase<Transform>)(object)route.Waypoints)[j * num2 / num3];
				if (!((Object)(object)val == (Object)null))
				{
					int num5 = (int)Singleton<Map>.Instance.GetRegionFromPosition(val.position);
					if (num5 >= 0 && num5 < 6)
					{
						span[num5]++;
					}
				}
			}
			for (int k = 0; k < 6; k++)
			{
				if (span[k] > 0 && (num4 < 0 || span[k] > span[num4]))
				{
					num4 = k;
				}
			}
			if (num >= 0 && num4 >= 0)
			{
				cached_routes[num] = route;
				cached_districts[num] = num4;
			}
			return num4;
		}

		public static void log()
		{
			MelonLogger.Msg($"Police: District patrols assigned/target Northtown={actual[0]}/{targets[0]}, Westville={actual[1]}/{targets[1]}, Downtown={actual[2]}/{targets[2]}, Docks={actual[3]}/{targets[3]}, Suburbia={actual[4]}/{targets[4]}, Uptown={actual[5]}/{targets[5]}. Targets require unlocked districts with native routes; counts follow assigned routes.");
		}
	}
	internal static class PoliceNavigation
	{
		private static NavMeshPath? path;

		public static void reset()
		{
			path = null;
		}

		public static bool try_destination(NPCMovement movement, Vector3 candidate, out Vector3 destination)
		{
			//IL_0001: 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)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: 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_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Expected O, but got Unknown
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Expected O, but got Unknown
			//IL_00e0: 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_00f1: Unknown result type (might be due to invalid IL or missing references)
			destination = default(Vector3);
			NavMeshAgent agent = movement.Agent;
			if ((Object)(object)agent == (Object)null || !((Behaviour)agent).enabled || !agent.isOnNavMesh || agent.isOnOffMeshLink || !float.IsFinite(((Vector3)(ref candidate)).sqrMagnitude))
			{
				return false;
			}
			NavMeshHit val = default(NavMeshHit);
			if (!NavMesh.SamplePosition(candidate, ref val, 0.75f, agent.areaMask))
			{
				return false;
			}
			if (Math.Abs(((NavMeshHit)(ref val)).position.y - candidate.y) > 1f)
			{
				return false;
			}
			NavMeshQueryFilter val2 = new NavMeshQueryFilter
			{
				agentTypeID = agent.agentTypeID,
				areaMask = agent.areaMask
			};
			NavMeshHit val3 = default(NavMeshHit);
			if (!NavMesh.FindClosestEdge(((NavMeshHit)(ref val)).position, ref val3, val2) || ((NavMeshHit)(ref val3)).distance < 0.25f)
			{
				return false;
			}
			if (path == null)
			{
				path = new NavMeshPath();
			}
			if (!agent.CalculatePath(((NavMeshHit)(ref val)).position, path) || (int)path.status != 0)
			{
				return false;
			}
			destination = ((NavMeshHit)(ref val)).position;
			return true;
		}

		public static bool try_redirect(NPCMovement movement, Vector3 original, Vector3 candidate, out Vector3 destination)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			//IL_007a: 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)
			destination = default(Vector3);
			NavMeshAgent agent = movement.Agent;
			if ((Object)(object)agent == (Object)null || !((Behaviour)agent).enabled || !agent.isOnNavMesh || !float.IsFinite(((Vector3)(ref original)).sqrMagnitude))
			{
				return false;
			}
			NavMeshHit val = default(NavMeshHit);
			if (!NavMesh.SamplePosition(original, ref val, 0.75f, agent.areaMask))
			{
				return false;
			}
			if (!try_destination(movement, candidate, out destination))
			{
				return false;
			}
			NavMeshQueryFilter val2 = new NavMeshQueryFilter
			{
				agentTypeID = agent.agentTypeID,
				areaMask = agent.areaMask
			};
			NavMeshHit val3 = default(NavMeshHit);
			return !NavMesh.Raycast(((NavMeshHit)(ref val)).position, destination, ref val3, val2);
		}
	}
	internal static class PolicePopulation
	{
		private struct OfficerState
		{
			public PoliceOfficer? officer;

			public float defeated_seconds;

			public bool defeated;

			public bool owned_patrol;

			public bool auto_deactivate;

			public PatrolGroup? group;

			public PoliceStation? return_station;

			public float return_seconds;

			public int return_attempts;
		}

		private static readonly OfficerState[] states = new OfficerState[128];

		private static float next_seconds;

		private static float log_seconds;

		private static bool failed;

		public static void reset()
		{
			for (int i = 0; i < states.Length; i++)
			{
				ref OfficerState reference = ref states[i];
				if ((Object)(object)reference.officer != (Object)null && reference.owned_patrol)
				{
					reference.officer.AutoDeactivate = reference.auto_deactivate;
				}
			}
			Array.Clear(states, 0, states.Length);
			next_seconds = 0f;
			log_seconds = 0f;
			PoliceDistricts.reset();
			failed = false;
		}

		public static void tick()
		{
			//IL_066f: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_040e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0247: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e1: Unknown result type (might be due to invalid IL or missing references)
			if (failed || !Singleton<LoadManager>.InstanceExists || !Singleton<LoadManager>.Instance.IsGameLoaded)
			{
				return;
			}
			try
			{
				if (!InstanceFinder.IsServer)
				{
					reconcile_client();
				}
				else
				{
					if (Time.time < next_seconds)
					{
						return;
					}
					next_seconds = Time.time + 5f;
					int num = Math.Min(Player.PlayerList.Count, 16);
					if (num == 0)
					{
						return;
					}
					PopulationRules rules = PopulationRules.for_players(num);
					int num2 = Math.Min(PoliceOfficer.Officers.Count, states.Length);
					int num3 = 0;
					int num4 = 0;
					for (int i = 0; i < num2; i++)
					{
						PoliceOfficer val = PoliceOfficer.Officers[i];
						if ((Object)(object)val == (Object)null || (Object)(object)((NPC)val).Health == (Object)null)
						{
							continue;
						}
						ref OfficerState reference = ref get_state(val);
						bool flag = ((NPC)val).Health.IsDead || ((NPC)val).Health.IsKnockedOut;
						if (flag && !reference.defeated)
						{
							reference.defeated_seconds = Time.time;
						}
						reference.defeated = flag;
						if ((Object)(object)reference.return_station != (Object)null)
						{
							if (reference.return_station.OfficerPool.Contains(val))
							{
								MelonLogger.Msg("Police: Reserve return confirmed officer=" + ((NPC)val).ID + ".");
								reference.return_station = null;
							}
							else if (flag)
							{
								reference.return_station = null;
							}
							else if ((Object)(object)val.PursuitTarget != (Object)null || ((Object)(object)val.PursuitBehaviour != (Object)null && ((Behaviour)val.PursuitBehaviour).Active) || ((Object)(object)val.VehiclePursuitBehaviour != (Object)null && ((Behaviour)val.VehiclePursuitBehaviour).Active) || ((Object)(object)val.FootPatrolBehaviour != (Object)null && ((Behaviour)val.FootPatrolBehaviour).Active) || ((Object)(object)val.CheckpointBehaviour != (Object)null && ((Behaviour)val.CheckpointBehaviour).Active) || ((Object)(object)val.SentryBehaviour != (Object)null && ((Behaviour)val.SentryBehaviour).Active))
							{
								reference.return_station = null;
								MelonLogger.Msg("Police: Returned officer already reassigned officer=" + ((NPC)val).ID + ".");
							}
							else if (Time.time >= reference.return_seconds && player_distance_squared(((Component)val).transform.position) >= 3600f)
							{
								if (reference.return_attempts >= 3)
								{
									throw new InvalidOperationException("Officer " + ((NPC)val).ID + " did not return to the station pool after three attempts.");
								}
								val.Deactivate();
								reference.return_attempts++;
								reference.return_seconds = Time.time + 5f;
							}
						}
						if (reference.owned_patrol && !flag && reference.group != null && (Object)(object)val.FootPatrolBehaviour != (Object)null && ((Behaviour)val.FootPatrolBehaviour).Active && reference.group.IsGroupReadyToAdvance())
						{
							reference.group.AdvanceGroup();
						}
						if (flag)
						{
							num4++;
						}
						else if ((Object)(object)val.FootPatrolBehaviour != (Object)null && ((Behaviour)val.FootPatrolBehaviour).Active && !((NPC)val).isInBuilding)
						{
							num3++;
						}
					}
					int num5 = count_reserves();
					int num6 = 0;
					for (int j = 0; j < states.Length; j++)
					{
						if (num6 >= 2)
						{
							break;
						}
						if (num5 >= rules.reserve_target)
						{
							break;
						}
						ref OfficerState reference2 = ref states[j];
						PoliceOfficer officer = reference2.officer;
						if ((Object)(object)officer == (Object)null || !reference2.defeated || (Object)(object)reference2.return_station != (Object)null || (Object)(object)((NPC)officer).Movement == (Object)null || ((NPC)officer).IsInVehicle || (Object)(object)officer.AssignedVehicle != (Object)null)
						{
							continue;
						}
						PoliceStation closestPoliceStation = PoliceStation.GetClosestPoliceStation(((Component)officer).transform.position);
						if (!((Object)(object)closestPoliceStation == (Object)null) && !((Object)(object)closestPoliceStation.SpawnPoint == (Object)null) && PopulationRules.can_replenish(reference2.defeated_seconds, Time.time, player_distance_squared(((Component)officer).transform.position), player_distance_squared(closestPoliceStation.SpawnPoint.position)))
						{
							if ((Object)(object)officer.FootPatrolBehaviour != (Object)null)
							{
								((Behaviour)officer.FootPatrolBehaviour).Disable_Networked((NetworkConnection)null);
							}
							if ((Object)(object)officer.PursuitBehaviour != (Object)null)
							{
								((Behaviour)officer.PursuitBehaviour).Disable_Networked((NetworkConnection)null);
							}
							if ((Object)(object)officer.VehiclePatrolBehaviour != (Object)null)
							{
								((Behaviour)officer.VehiclePatrolBehaviour).Disable_Networked((NetworkConnection)null);
							}
							if ((Object)(object)officer.BodySearchBehaviour != (Object)null)
							{
								((Behaviour)officer.BodySearchBehaviour).Disable_Networked((NetworkConnection)null);
							}
							if ((Object)(object)officer.CheckpointBehaviour != (Object)null)
							{
								((Behaviour)officer.CheckpointBehaviour).Disable_Networked((NetworkConnection)null);
							}
							if ((Object)(object)officer.SentryBehaviour != (Object)null)
							{
								((Behaviour)officer.SentryBehaviour).Disable_Networked((NetworkConnection)null);
							}
							((NPC)officer).SetVisible(false, true);
							((NPC)officer).Movement.Warp(closestPoliceStation.SpawnPoint.position);
							((NPC)officer).Health.SetAfflictedWithLethalEffect(false);
							((NPC)officer).Health.Revive();
							((NPC)officer).Health.RestoreHealth();
							if (reference2.owned_patrol)
							{
								officer.AutoDeactivate = reference2.auto_deactivate;
							}
							officer.Deactivate();
							reference2.return_station = (closestPoliceStation.OfficerPool.Contains(officer) ? null : closestPoliceStation);
							reference2.return_seconds = Time.time + 5f;
							reference2.return_attempts = 1;
							reference2.owned_patrol = false;
							reference2.group = null;
							reference2.defeated = false;
							num6++;
							num5 = count_reserves();
							MelonLogger.Msg($"Police: Reserve replacement requested officer={((NPC)officer).ID}, pooled={closestPoliceStation.OfficerPool.Contains(officer)}, reserve={num5}/{rules.reserve_target}.");
						}
					}
					bool flag2 = false;
					for (int k = 0; k < num; k++)
					{
						Player val2 = Player.PlayerList[k];
						if ((Object)(object)val2 != (Object)null && (Object)(object)val2.CrimeData != (Object)null && (int)val2.CrimeData.CurrentPursuitLevel != 0)
						{
							flag2 = true;
						}
					}
					bool flag3 = PoliceDistricts.prepare(rules.patrol_target);
					if (!flag2 && flag3 && num3 < rules.patrol_target)
					{
						deploy_patrols(rules, num3, num5);
					}
					else if (!flag2 && flag3 && num3 == rules.patrol_target)
					{
						rebalance_patrol();
					}
					if (!flag2 && num3 > rules.patrol_target)
					{
						return_patrols(num3 - rules.patrol_target);
					}
					if (Time.time < log_seconds)
					{
						return;
					}
					log_seconds = Time.time + 30f;
					num5 = count_reserves();
					num3 = 0;
					num4 = 0;
					for (int l = 0; l < num2; l++)
					{
						PoliceOfficer val3 = PoliceOfficer.Officers[l];
						if (!((Object)(object)val3 == (Object)null) && !((Object)(object)((NPC)val3).Health == (Object)null))
						{
							if (((NPC)val3).Health.IsDead || ((NPC)val3).Health.IsKnockedOut)
							{
								num4++;
							}
							else if ((Object)(object)val3.FootPatrolBehaviour != (Object)null && ((Behaviour)val3.FootPatrolBehaviour).Active && !((NPC)val3).isInBuilding)
							{
								num3++;
							}
						}
					}
					if (flag3)
					{
						PoliceDistricts.log();
					}
					MelonLogger.Msg($"Police: Population players={num}, foot_patrols={num3}/{rules.patrol_target}, reserves={num5}/{rules.reserve_target}, defeated={num4}, registered={num2}, patrol_staffing_paused={flag2}. Targets depend on native officer availability and safe replacement locations.");
				}
			}
			catch (Exception value)
			{
				failed = true;
				MelonLogger.Error($"Police: Population management disabled for this scene: {value}");
			}
		}

		private static ref OfficerState get_state(PoliceOfficer officer)
		{
			int num = -1;
			for (int i = 0; i < states.Length; i++)
			{
				if ((Object)(object)states[i].officer == (Object)(object)officer)
				{
					return ref states[i];
				}
				if ((Object)(object)states[i].officer == (Object)null && num < 0)
				{
					num = i;
				}
			}
			if (num < 0)
			{
				throw new InvalidOperationException("Police population tracking capacity exhausted.");
			}
			states[num] = new OfficerState
			{
				officer = officer
			};
			return ref states[num];
		}

		private static int count_reserves()
		{
			int num = 0;
			int num2 = Math.Min(PoliceStation.PoliceStations.Count, 16);
			for (int i = 0; i < num2; i++)
			{
				PoliceStation val = PoliceStation.PoliceStations[i];
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				int num3 = Math.Min(val.OfficerPool.Count, 128);
				for (int j = 0; j < num3; j++)
				{
					PoliceOfficer val2 = val.OfficerPool[j];
					if ((Object)(object)val2 != (Object)null && ((NPC)val2).IsConscious)
					{
						num++;
					}
				}
			}
			return num;
		}

		private static float player_distance_squared(Vector3 position)
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			float num = float.MaxValue;
			int num2 = Math.Min(Player.PlayerList.Count, 16);
			if (num2 == 0 || num2 != Player.PlayerList.Count)
			{
				return 0f;
			}
			for (int i = 0; i < num2; i++)
			{
				Player val = Player.PlayerList[i];
				if ((Object)(object)val == (Object)null)
				{
					return 0f;
				}
				Vector3 val2 = ((Component)val).transform.position - position;
				float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
				if (!float.IsFinite(sqrMagnitude))
				{
					return 0f;
				}
				num = Math.Min(num, sqrMagnitude);
			}
			return num;
		}

		private static void deploy_patrols(PopulationRules rules, int patrol_count, int reserve_count)
		{
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: 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_0154: Expected O, but got Unknown
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			int num = Math.Max(4, rules.reserve_target / 2);
			if (reserve_count <= num || patrol_count >= rules.patrol_target)
			{
				return;
			}
			for (int i = 0; i < 64; i++)
			{
				int district;
				int members;
				FootPatrolRoute val = PoliceDistricts.next_route(out district, out members);
				if ((Object)(object)val == (Object)null)
				{
					break;
				}
				int num2 = Math.Min(((Il2CppArrayBase<Transform>)(object)val.Waypoints).Length, 128);
				int num3 = (Math.Clamp(val.StartWaypointIndex, 0, num2 - 1) + members) % num2;
				Transform val2 = ((Il2CppArrayBase<Transform>)(object)val.Waypoints)[num3];
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				PoliceStation closestPoliceStation = PoliceStation.GetClosestPoliceStation(val2.position);
				if ((Object)(object)closestPoliceStation == (Object)null || (Object)(object)closestPoliceStation.SpawnPoint == (Object)null || closestPoliceStation.OfficerPool.Count == 0 || player_distance_squared(closestPoliceStation.SpawnPoint.position) < 3600f)
				{
					continue;
				}
				PoliceOfficer val3 = closestPoliceStation.PullOfficer();
				if ((Object)(object)val3 == (Object)null)
				{
					continue;
				}
				if (!((NPC)val3).IsConscious || (Object)(object)((NPC)val3).Movement == (Object)null)
				{
					throw new InvalidOperationException("Station supplied an unavailable patrol officer.");
				}
				ref OfficerState reference = ref get_state(val3);
				if (!reference.owned_patrol)
				{
					reference.auto_deactivate = val3.AutoDeactivate;
				}
				reference.owned_patrol = true;
				reference.group = new PatrolGroup(val)
				{
					CurrentWaypoint = num3
				};
				val3.AutoDeactivate = false;
				if ((Object)(object)((NPC)val3).CurrentBuilding != (Object)null)
				{
					((NPC)val3).ExitBuilding(((NPC)val3).CurrentBuilding);
				}
				val3.Activate();
				((NPC)val3).Movement.Warp(closestPoliceStation.SpawnPoint.position);
				val3.StartFootPatrol(reference.group, false);
				patrol_count++;
				PoliceDistricts.actual[district]++;
				MelonLogger.Msg($"Police: Patrol assigned officer={((NPC)val3).ID}, district={district}, route={val.RouteName}, waypoint={reference.group.CurrentWaypoint}, foot_patrols={patrol_count}/{rules.patrol_target}. Departing station without route-start warp.");
				break;
			}
		}

		private static void rebalance_patrol()
		{
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_024e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0253: Unknown result type (might be due to invalid IL or missing references)
			//IL_0260: Expected O, but got Unknown
			int district;
			int members;
			FootPatrolRoute val = PoliceDistricts.next_route(out district, out members);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			int num = Math.Min(((Il2CppArrayBase<Transform>)(object)val.Waypoints).Length, 128);
			int num2 = (Math.Clamp(val.StartWaypointIndex, 0, num - 1) + members) % num;
			Transform val2 = ((Il2CppArrayBase<Transform>)(object)val.Waypoints)[num2];
			if ((Object)(object)val2 == (Object)null)
			{
				return;
			}
			int num3 = 0;
			for (int i = 0; i < states.Length; i++)
			{
				if (num3 >= 2)
				{
					break;
				}
				ref OfficerState reference = ref states[i];
				PoliceOfficer officer = reference.officer;
				if (!reference.owned_patrol || (Object)(object)officer == (Object)null || !((NPC)officer).IsConscious || (Object)(object)officer.PursuitTarget != (Object)null || ((NPC)officer).IsInVehicle || (Object)(object)officer.AssignedVehicle != (Object)null || (Object)(object)((NPC)officer).Movement == (Object)null || (Object)(object)officer.FootPatrolBehaviour == (Object)null || !((Behaviour)officer.FootPatrolBehaviour).Active || ((NPC)officer).isInBuilding || player_distance_squared(((Component)officer).transform.position) < 3600f || ((Object)(object)officer.PursuitBehaviour != (Object)null && ((Behaviour)officer.PursuitBehaviour).Active) || ((Object)(object)officer.VehiclePursuitBehaviour != (Object)null && ((Behaviour)officer.VehiclePursuitBehaviour).Active) || ((Object)(object)officer.BodySearchBehaviour != (Object)null && ((Behaviour)officer.BodySearchBehaviour).Active) || ((Object)(object)officer.CheckpointBehaviour != (Object)null && ((Behaviour)officer.CheckpointBehaviour).Active) || ((Object)(object)officer.SentryBehaviour != (Object)null && ((Behaviour)officer.SentryBehaviour).Active))
				{
					continue;
				}
				PatrolGroup val3 = officer.FootPatrolBehaviour.Group;
				if (val3 == null || (Object)(object)val3.Route == (Object)null)
				{
					continue;
				}
				int num4 = PoliceDistricts.region_for_route(val3.Route);
				if (num4 >= 0 && PoliceDistricts.actual[num4] > PoliceDistricts.targets[num4])
				{
					num3++;
					if (PoliceNavigation.try_destination(((NPC)officer).Movement, val2.position, out var _))
					{
						((Behaviour)officer.FootPatrolBehaviour).Disable_Networked((NetworkConnection)null);
						reference.group = new PatrolGroup(val)
						{
							CurrentWaypoint = num2
						};
						officer.StartFootPatrol(reference.group, false);
						PoliceDistricts.actual[num4]--;
						PoliceDistricts.actual[district]++;
						MelonLogger.Msg($"Police: Patrol reassigned officer={((NPC)officer).ID}, from={num4}, to={district}, route={val.RouteName}. Walking to new route.");
						break;
					}
				}
			}
		}

		private static void return_patrols(int excess_count)
		{
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < states.Length; i++)
			{
				if (excess_count <= 0)
				{
					break;
				}
				ref OfficerState reference = ref states[i];
				PoliceOfficer officer = reference.officer;
				if (reference.owned_patrol && !((Object)(object)officer == (Object)null) && ((NPC)officer).IsConscious && !((Object)(object)officer.PursuitTarget != (Object)null) && !((NPC)officer).IsInVehicle && !((Object)(object)officer.AssignedVehicle != (Object)null) && !((Object)(object)officer.FootPatrolBehaviour == (Object)null) && ((Behaviour)officer.FootPatrolBehaviour).Active && !(player_distance_squared(((Component)officer).transform.position) < 3600f))
				{
					((Behaviour)officer.FootPatrolBehaviour).Disable_Networked((NetworkConnection)null);
					officer.AutoDeactivate = reference.auto_deactivate;
					officer.Deactivate();
					reference.owned_patrol = false;
					reference.group = null;
					excess_count--;
					MelonLogger.Msg("Police: Extra patrol returned officer=" + ((NPC)officer).ID + ".");
				}
			}
		}

		private static void reconcile_client()
		{
			int num = Math.Min(PoliceOfficer.Officers.Count, 128);
			for (int i = 0; i < num; i++)
			{
				PoliceOfficer val = PoliceOfficer.Officers[i];
				if (!((Object)(object)val == (Object)null) && !((Object)(object)((NPC)val).Health == (Object)null) && !((Object)(object)((NPC)val).Behaviour == (Object)null))
				{
					NPCHealth health = ((NPC)val).Health;
					if ((health.IsDead || health.IsKnockedOut) && float.IsFinite(health.Health) && float.IsFinite(health.MaxHealth) && !(health.Health < health.MaxHealth) && !(health.MaxHealth <= 0f) && !((Object)(object)((NPC)val).Behaviour.DeadBehaviour == (Object)null) && !((Object)(object)((NPC)val).Behaviour.UnconsciousBehaviour == (Object)null) && !((Behaviour)((NPC)val).Behaviour.DeadBehaviour).Enabled && !((Behaviour)((NPC)val).Behaviour.UnconsciousBehaviour).Enabled)
					{
						health.Revive();
						MelonLogger.Msg("Police: Client reconciled restored officer=" + ((NPC)val).ID + " from host health and behaviour state.");
					}
				}
			}
		}
	}
	internal sealed record PoliceSettings
	{
		public static PoliceSettings current { get; set; } = new PoliceSettings();

		public int patrols_per_player { get; init; } = 5;

		public int reserve_base { get; init; } = 8;

		public int reserves_per_extra_player { get; init; } = 4;

		public float response_size { get; init; } = 1f;

		public float reinforcement_time { get; init; } = 1f;

		public int northtown_weight { get; init; } = 1;

		public int westville_weight { get; init; } = 1;

		public int downtown_weight { get; init; } = 1;

		public int docks_weight { get; init; } = 1;

		public int suburbia_weight { get; init; } = 1;

		public int uptown_weight { get; init; } = 1;

		public PoliceSettings validated()
		{
			return this with
			{
				patrols_per_player = Math.Clamp(patrols_per_player, 0, 16),
				reserve_base = Math.Clamp(reserve_base, 0, 64),
				reserves_per_extra_player = Math.Clamp(reserves_per_extra_player, 0, 16),
				response_size = (float.IsFinite(response_size) ? Math.Clamp(response_size, 0.25f, 2f) : 1f),
				reinforcement_time = (float.IsFinite(reinforcement_time) ? Math.Clamp(reinforcement_time, 0.25f, 4f) : 1f),
				northtown_weight = Math.Clamp(northtown_weight, 0, 10),
				westville_weight = Math.Clamp(westville_weight, 0, 10),
				downtown_weight = Math.Clamp(downtown_weight, 0, 10),
				docks_weight = Math.Clamp(docks_weight, 0, 10),
				suburbia_weight = Math.Clamp(suburbia_weight, 0, 10),
				uptown_weight = Math.Clamp(uptown_weight, 0, 10)
			};
		}

		public int weight(int district)
		{
			return district switch
			{
				0 => northtown_weight, 
				1 => westville_weight, 
				2 => downtown_weight, 
				3 => docks_weight, 
				4 => suburbia_weight, 
				5 => uptown_weight, 
				_ => throw new ArgumentOutOfRangeException("district"), 
			};
		}

		[CompilerGenerated]
		private PoliceSettings(PoliceSettings original)
		{
			patrols_per_player = original.patrols_per_player;
			reserve_base = original.reserve_base;
			reserves_per_extra_player = original.reserves_per_extra_player;
			response_size = original.response_size;
			reinforcement_time = original.reinforcement_time;
			northtown_weight = original.northtown_weight;
			westville_weight = original.westville_weight;
			downtown_weight = original.downtown_weight;
			docks_weight = original.docks_weight;
			suburbia_weight = original.suburbia_weight;
			uptown_weight = original.uptown_weight;
		}

		public PoliceSettings()
		{
		}
	}
	internal static class PoliceTactics
	{
		private readonly record struct ImpactEvent(ResponseState? response, int attack_id, bool lethal, float health_before);

		private const string shotgun_path = "Avatar/Equippables/PumpShotgun";

		private static bool? shotgun_available;

		public static void install(Harmony harmony)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Expected O, but got Unknown
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Expected O, but got Unknown
			//IL_00eb: Expected O, but got Unknown
			harmony.Patch((MethodBase)AccessTools.Method(typeof(CombatBehaviour), "SetWeapon", (Type[])null, (Type[])null), new HarmonyMethod(typeof(PoliceTactics), "vary_weapon", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)AccessTools.Method(typeof(PursuitBehaviour), "GetIdealRangedWeaponDistance", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(PoliceTactics), "set_firing_distance", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)AccessTools.Method(typeof(PlayerCrimeData), "AddCrime", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(PoliceTactics), "record_crime", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)AccessTools.Method(typeof(NPC), "RpcLogic___ReceiveImpact_427288424", (Type[])null, (Type[])null), new HarmonyMethod(typeof(PoliceTactics), "record_impact_before", (Type[])null), new HarmonyMethod(typeof(PoliceTactics), "record_impact_after", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		public static void reset()
		{
			shotgun_available = null;
		}

		private static bool try_context(CombatBehaviour combat, out PursuitBehaviour? pursuit, out ResponseState? response, out int officer_index)
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Expected I4, but got Unknown
			pursuit = null;
			response = null;
			officer_index = -1;
			if (!Main.authoritative)
			{
				return false;
			}
			pursuit = ((Il2CppObjectBase)combat).TryCast<PursuitBehaviour>();
			if ((Object)(object)pursuit == (Object)null || !((Behaviour)pursuit).Active || (Object)(object)pursuit.officer == (Object)null || (Object)(object)pursuit.TargetPlayer == (Object)null)
			{
				return false;
			}
			Player targetPlayer = pursuit.TargetPlayer;
			if ((Object)(object)targetPlayer.CrimeData == (Object)null || (int)targetPlayer.CrimeData.CurrentPursuitLevel == 0)
			{
				return false;
			}
			officer_index = PoliceOfficer.Officers.IndexOf(pursuit.officer);
			if (officer_index < 0 || officer_index >= 128)
			{
				return false;
			}
			response = Main.get_response(targetPlayer.PlayerCode, create: true);
			if (response == null)
			{
				return false;
			}
			response.observe_level((int)targetPlayer.CrimeData.CurrentPursuitLevel);
			return true;
		}

		private static void vary_weapon(CombatBehaviour __instance, ref string __0)
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Expected I4, but got Unknown
			try
			{
				if (!try_context(__instance, out PursuitBehaviour pursuit, out ResponseState response, out int officer_index))
				{
					return;
				}
				AvatarWeapon weapon_Gun = pursuit.Weapon_Gun;
				string text = ((weapon_Gun != null) ? ((AvatarEquippable)weapon_Gun).AssetPath : null);
				AvatarWeapon weapon_Taser = pursuit.Weapon_Taser;
				string text2 = ((weapon_Taser != null) ? ((AvatarEquippable)weapon_Taser).AssetPath : null);
				AvatarWeapon weapon_Baton = pursuit.Weapon_Baton;
				string text3 = ((weapon_Baton != null) ? ((AvatarEquippable)weapon_Baton).AssetPath : null);
				if (__0 != text && __0 != text2 && __0 != text3)
				{
					return;
				}
				OfficerWeapon officerWeapon = ResponseRules.weapon(response.severity, (int)pursuit.TargetPlayer.CrimeData.CurrentPursuitLevel, officer_index);
				if (officerWeapon == OfficerWeapon.Shotgun && !shotgun_available.HasValue)
				{
					shotgun_available = Resources.Load<Object>("Avatar/Equippables/PumpShotgun") != (Object)null;
					if (shotgun_available == false)
					{
						MelonLogger.Warning("Police: Shotgun asset unavailable; using standard police guns.");
					}
				}
				string text4 = officerWeapon switch
				{
					OfficerWeapon.Baton => text3, 
					OfficerWeapon.Taser => text2, 
					OfficerWeapon.Pistol => text, 
					OfficerWeapon.Shotgun => (shotgun_available == true) ? "Avatar/Equippables/PumpShotgun" : text, 
					_ => null, 
				};
				if (!string.IsNullOrEmpty(text4))
				{
					__0 = text4;
				}
			}
			catch (Exception value)
			{
				Main.disable($"Police: Weapon selection failed: {value}");
			}
		}

		private static void set_firing_distance(PursuitBehaviour __instance, ref float __result)
		{
			try
			{
				if (!try_context((CombatBehaviour)(object)__instance, out PursuitBehaviour pursuit, out ResponseState _, out int officer_index))
				{
					return;
				}
				AvatarWeapon currentWeapon = ((CombatBehaviour)pursuit).currentWeapon;
				if (!((Object)(object)currentWeapon == (Object)null) && !string.IsNullOrEmpty(((AvatarEquippable)currentWeapon).AssetPath))
				{
					int num;
					if (!(((AvatarEquippable)currentWeapon).AssetPath == "Avatar/Equippables/PumpShotgun"))
					{
						string assetPath = ((AvatarEquippable)currentWeapon).AssetPath;
						AvatarWeapon weapon_Gun = pursuit.Weapon_Gun;
						num = ((assetPath == ((weapon_Gun != null) ? ((AvatarEquippable)weapon_Gun).AssetPath : null)) ? 3 : 0);
					}
					else
					{
						num = 4;
					}
					OfficerWeapon weapon = (OfficerWeapon)num;
					float num2 = ResponseRules.firing_distance(Main.pursuit_role(pursuit, officer_index), weapon);
					float num3 = currentWeapon.MaxUseRange * 0.8f;
					float num4 = Math.Max(0f, currentWeapon.MinUseRange);
					if (!(num2 <= 0f) && float.IsFinite(num4) && float.IsFinite(num3) && !(num3 <= num4))
					{
						__result = Math.Clamp(num2, num4, num3);
					}
				}
			}
			catch (Exception value)
			{
				Main.disable($"Police: Combat spacing failed: {value}");
			}
		}

		private static void record_crime(PlayerCrimeData __instance, Crime __0, int __1)
		{
			if (!Main.authoritative || __0 == null || __1 <= 0 || (Object)(object)__instance.Player == (Object)null)
			{
				return;
			}
			try
			{
				Main.get_response(__instance.Player.PlayerCode, create: true)?.observe_crime(((MemberInfo)((Object)__0).GetIl2CppType()).Name);
			}
			catch (Exception value)
			{
				Main.disable($"Police: Crime severity update failed: {value}");
			}
		}

		private static void record_impact_before(NPC __instance, Impact __0, out ImpactEvent __state)
		{
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Invalid comparison between Unknown and I4
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Invalid comparison between Unknown and I4
			__state = default(ImpactEvent);
			if (!Main.authoritative)
			{
				return;
			}
			try
			{
				PoliceOfficer val = ((Il2CppObjectBase)__instance).TryCast<PoliceOfficer>();
				if (!((Object)(object)val == (Object)null) && !((Object)(object)((NPC)val).Health == (Object)null) && !((NPC)val).Health.IsDead && !(__0.ImpactDamage <= 0f) && !((Object)(object)__0.ImpactSource == (Object)null))
				{
					Player component = ((Component)__0.ImpactSource).GetComponent<Player>();
					if (!((Object)(object)component == (Object)null))
					{
						ResponseState response = Main.get_response(component.PlayerCode, create: true);
						EImpactType impactType = __0.ImpactType;
						bool flag = ((impactType - 2 <= 1 || (int)impactType == 5) ? true : false);
						bool lethal = flag;
						__state = new ImpactEvent(response, __0.ImpactID, lethal, ((NPC)val).Health.Health);
					}
				}
			}
			catch (Exception value)
			{
				Main.disable($"Police: Attack attribution failed: {value}");
			}
		}

		private static void record_impact_after(NPC __instance, ImpactEvent __state)
		{
			if (!Main.authoritative || __state.response == null)
			{
				return;
			}
			try
			{
				NPCHealth health = __instance.Health;
				if (!((Object)(object)health == (Object)null) && (health.IsDead || !(health.Health >= __state.health_before)) && __state.response.record_attack(__state.attack_id, __state.lethal) && health.IsDead)
				{
					__state.response.record_kill();
				}
			}
			catch (Exception value)
			{
				Main.disable($"Police: Attack severity update failed: {value}");
			}
		}
	}
	internal readonly record struct PopulationRules(int patrol_target, int reserve_target)
	{
		public static PopulationRules for_players(int player_count)
		{
			int num = Math.Clamp(player_co