Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of NACops MONO v2.1.0
Mods/NACops.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using FishNet.Connection; using FishNet.Managing; using FishNet.Object; using HarmonyLib; using MelonLoader; using MelonLoader.Preferences; using MelonLoader.Utils; using Microsoft.CodeAnalysis; using NACops; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using Pathfinding; using ScheduleOne; using ScheduleOne.Audio; using ScheduleOne.AvatarFramework; using ScheduleOne.AvatarFramework.Equipping; using ScheduleOne.Building.Doors; using ScheduleOne.Combat; using ScheduleOne.Core.Items.Framework; using ScheduleOne.DevUtilities; using ScheduleOne.Dialogue; using ScheduleOne.Doors; using ScheduleOne.Economy; using ScheduleOne.Employees; using ScheduleOne.EntityFramework; using ScheduleOne.GameTime; using ScheduleOne.ItemFramework; using ScheduleOne.Law; using ScheduleOne.Levelling; using ScheduleOne.Management; using ScheduleOne.Map; using ScheduleOne.Money; using ScheduleOne.NPCs; using ScheduleOne.NPCs.Behaviour; using ScheduleOne.NPCs.Framework; using ScheduleOne.ObjectScripts; using ScheduleOne.Persistence; using ScheduleOne.PlayerScripts; using ScheduleOne.Police; using ScheduleOne.Product; using ScheduleOne.Property; using ScheduleOne.Quests; using ScheduleOne.Storage; using ScheduleOne.Tools; using ScheduleOne.UI; using ScheduleOne.UI.Handover; using ScheduleOne.UI.MainMenu; using ScheduleOne.Vehicles; using ScheduleOne.Vehicles.AI; using ScheduleOne.Vision; using ScheduleOne.VoiceOver; using TMPro; using UnityEngine; using UnityEngine.AI; using UnityEngine.Events; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using UnityEngine.UI; using VLB; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(global::NACops.NACops), "NACops", "2.1.0", "XOWithSauce", null)] [assembly: MelonColor] [assembly: MelonOptionalDependencies(new string[] { "FishNet.Runtime" })] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: MelonPlatformDomain(/*Could not decode attribute arguments.*/)] [assembly: VerifyLoaderVersion("0.7.2", true)] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("XOWithSauce")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright XOWithSauce 2026 Source MIT")] [assembly: AssemblyDescription("Schedule I NACops Mod")] [assembly: AssemblyFileVersion("2.1.0.0")] [assembly: AssemblyInformationalVersion("2.1.0")] [assembly: AssemblyProduct("NACops")] [assembly: AssemblyTitle("NACops")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/XOWithSauce/schedule-nacops")] [assembly: NeutralResourcesLanguage("en-US")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace NACops { public class FootPatrolGenerator { public static Dictionary<PatrolInstance, List<string>> generatedPatrolInstances = new Dictionary<PatrolInstance, List<string>>(); public static ConfigLoader.FootPatrolsSerialized serPatrols; public static PatrolInstance[] GeneratePatrol(LawActivitySettings template, string day = "") { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Expected O, but got Unknown //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Expected O, but got Unknown //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Expected O, but got Unknown //IL_0142: Unknown result type (might be due to invalid IL or missing references) if (generatedPatrolInstances.Count == 0) { DebugModule.Log("Generating new patrol routes", "GeneratePatrol"); Transform parent = ((Component)Singleton<LawController>.Instance).transform.Find("PatrolRoutes"); if (serPatrols == null) { serPatrols = ConfigLoader.LoadPatrolsConfig(); } foreach (SerializedFootPatrol loadedPatrol in serPatrols.loadedPatrols) { GameObject val = new GameObject(loadedPatrol.name); DebugModule.Log("Generate object for patrol: " + loadedPatrol.name, "GeneratePatrol"); DebugModule.Log("- Days: " + string.Join(" ", loadedPatrol.days), "GeneratePatrol"); FootPatrolRoute val2 = val.AddComponent<FootPatrolRoute>(); ((Object)val2).name = loadedPatrol.name; val2.RouteName = loadedPatrol.name; val2.StartWaypointIndex = 0; Transform[] array = (Transform[])(object)new Transform[loadedPatrol.waypoints.Count]; for (int i = 0; i < loadedPatrol.waypoints.Count; i++) { GameObject val3 = new GameObject((i == 0) ? "Waypoint" : $"Waypoint ({i})"); val3.transform.position = loadedPatrol.waypoints[i]; val3.transform.parent = val.transform; array[i] = val3.transform; } val2.Waypoints = array; val.transform.parent = parent; PatrolInstance val4 = new PatrolInstance(); val4.StartTime = loadedPatrol.startTime; val4.EndTime = loadedPatrol.endTime; val4.MaxMembers = loadedPatrol.members; val4.MinMembers = 1; val4.Route = val2; val4.OnlyIfCurfewEnabled = loadedPatrol.onlyIfCurfew; val4.IntensityRequirement = loadedPatrol.intensityRequirement; val.transform.parent = parent; val.SetActive(true); generatedPatrolInstances.Add(val4, loadedPatrol.days); } } if (day == "") { int num = template.Patrols.Length; int count = generatedPatrolInstances.Count; int num2 = num + count; PatrolInstance[] array2 = (PatrolInstance[])(object)new PatrolInstance[num2]; Array.Copy(template.Patrols, array2, num); int num3 = num; foreach (KeyValuePair<PatrolInstance, List<string>> generatedPatrolInstance in generatedPatrolInstances) { if (num3 >= num2) { break; } array2[num3] = generatedPatrolInstance.Key; num3++; } return array2; } int num4 = template.Patrols.Length; int num5 = 0; foreach (KeyValuePair<PatrolInstance, List<string>> generatedPatrolInstance2 in generatedPatrolInstances) { if (generatedPatrolInstance2.Value.Contains(day)) { num5++; } } if (num5 == 0) { return template.Patrols; } int num6 = num4 + num5; PatrolInstance[] array3 = (PatrolInstance[])(object)new PatrolInstance[num6]; Array.Copy(template.Patrols, array3, num4); int num7 = num4; foreach (KeyValuePair<PatrolInstance, List<string>> generatedPatrolInstance3 in generatedPatrolInstances) { if (num7 >= num6) { break; } if (generatedPatrolInstance3.Value.Contains(day)) { array3[num7] = generatedPatrolInstance3.Key; num7++; } } DebugModule.Log($" {day}: Added {num5} patrols ({num4} -> {num6})", "GeneratePatrol"); return array3; } } [Serializable] public class SerializedFootPatrol { public int startTime = 1900; public int endTime = 500; public int members = 2; public int intensityRequirement = 1; public bool onlyIfCurfew; public string name = "NACops Extra Loop"; public List<string> days; public List<Vector3> waypoints = new List<Vector3>(); } public class SentryGenerator { public static Dictionary<SentryInstance, List<string>> generatedSentryInstances = new Dictionary<SentryInstance, List<string>>(); public static ConfigLoader.SentrysSerialized serSentries; public static SentryInstance[] GenerateSentry(LawActivitySettings template, string day = "") { //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Expected O, but got Unknown //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Expected O, but got Unknown //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Expected O, but got Unknown //IL_0141: 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_014c: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Expected O, but got Unknown //IL_017d: 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_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Expected O, but got Unknown if (generatedSentryInstances.Count == 0) { DebugModule.Log("Generating new sentry spots", "GenerateSentry"); Transform val = ((Component)Singleton<LawController>.Instance).transform.Find("Sentry Locations"); if ((Object)(object)val == (Object)null) { DebugModule.Log(" Sentry Locations transform is null", "GenerateSentry"); } DebugModule.Log(" Load Sentry Config", "GenerateSentry"); if (serSentries == null) { serSentries = ConfigLoader.LoadSentryConfig(); } DebugModule.Log(" Loaded Patrols config count: " + serSentries.loadedSentrys.Count, "GenerateSentry"); foreach (SerializedSentry loadedSentry in serSentries.loadedSentrys) { GameObject val2 = new GameObject(loadedSentry.name); DebugModule.Log("Generate object for patrol: " + loadedSentry.name, "GenerateSentry"); DebugModule.Log("- Days: " + string.Join(" ", loadedSentry.days), "GenerateSentry"); SentryLocation val3 = val2.AddComponent<SentryLocation>(); val3.Routes = new List<SentryRoute>(); SentryRoute val4 = new SentryRoute(); GameObject val5 = new GameObject("Stand point"); val5.transform.parent = val2.transform; val5.transform.SetPositionAndRotation(loadedSentry.standPosition1, Quaternion.Euler(loadedSentry.pos1Rotation)); GameObject val6 = new GameObject("Stand point (1)"); val6.transform.parent = val2.transform; val6.transform.SetPositionAndRotation(loadedSentry.standPosition2, Quaternion.Euler(loadedSentry.pos2Rotation)); val4.RoutePoints = (Transform[])(object)new Transform[2] { val5.transform, val6.transform }; val4.MinutesPerPoint = loadedSentry.minutesPerPoint; val3.Routes.Add(val4); ((Component)val3).gameObject.SetActive(true); SentryInstance val7 = new SentryInstance(); val7.StartTime = loadedSentry.startTime; val7.EndTime = loadedSentry.endTime; val7.MaxMembers = loadedSentry.members; val7.MinMembers = 1; val7._potentialLocations = (SentryLocation[])(object)new SentryLocation[1] { val3 }; val7.OnlyIfCurfewEnabled = loadedSentry.onlyIfCurfew; val7.IntensityRequirement = loadedSentry.intensityRequirement; val2.transform.parent = val; val2.SetActive(true); generatedSentryInstances.Add(val7, loadedSentry.days); } } if (day == "") { int num = template.Sentries.Length; int count = generatedSentryInstances.Count; int num2 = num + count; SentryInstance[] array = (SentryInstance[])(object)new SentryInstance[num2]; Array.Copy(template.Sentries, array, num); int num3 = num; foreach (KeyValuePair<SentryInstance, List<string>> generatedSentryInstance in generatedSentryInstances) { if (num3 >= num2) { break; } array[num3] = generatedSentryInstance.Key; num3++; } return array; } int num4 = template.Sentries.Length; int num5 = 0; foreach (KeyValuePair<SentryInstance, List<string>> generatedSentryInstance2 in generatedSentryInstances) { if (generatedSentryInstance2.Value.Contains(day)) { num5++; } } if (num5 == 0) { return template.Sentries; } int num6 = num4 + num5; SentryInstance[] array2 = (SentryInstance[])(object)new SentryInstance[num6]; Array.Copy(template.Sentries, array2, num4); int num7 = num4; foreach (KeyValuePair<SentryInstance, List<string>> generatedSentryInstance3 in generatedSentryInstances) { if (num7 >= num6) { break; } if (generatedSentryInstance3.Value.Contains(day)) { array2[num7] = generatedSentryInstance3.Key; num7++; } } DebugModule.Log($" {day}: Added {num5} sentries ({num4} -> {num6})", "GenerateSentry"); return array2; } } [Serializable] public class SerializedSentry { public int startTime = 1900; public int endTime = 500; public int members = 1; public int minutesPerPoint = 60; public int intensityRequirement = 1; public bool onlyIfCurfew; public string name; public List<string> days; public Vector3 standPosition1; public Vector3 pos1Rotation; public Vector3 standPosition2; public Vector3 pos2Rotation; } public class VehiclePatrolGenerator { public static Dictionary<VehiclePatrolInstance, List<string>> generatedVehiclePatrolInstances = new Dictionary<VehiclePatrolInstance, List<string>>(); public static ConfigLoader.VehiclePatrolsSerialized serVehiclePatrols; public static VehiclePatrolInstance[] GenerateVehiclePatrol(LawActivitySettings template, string day = "") { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Expected O, but got Unknown //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Expected O, but got Unknown //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Expected O, but got Unknown //IL_0142: Unknown result type (might be due to invalid IL or missing references) if (generatedVehiclePatrolInstances.Count == 0) { DebugModule.Log("Generating new vehicle patrol routes", "GenerateVehiclePatrol"); Transform parent = ((Component)Singleton<LawController>.Instance).transform.Find("VehiclePatrolRoutes"); if (serVehiclePatrols == null) { serVehiclePatrols = ConfigLoader.LoadVehiclePatrolsConfig(); } foreach (SerializedVehiclePatrol loadedVehiclePatrol in serVehiclePatrols.loadedVehiclePatrols) { GameObject val = new GameObject(loadedVehiclePatrol.name); DebugModule.Log("Generate object for patrol: " + loadedVehiclePatrol.name, "GenerateVehiclePatrol"); DebugModule.Log("- Days: " + string.Join(" ", loadedVehiclePatrol.days), "GenerateVehiclePatrol"); VehiclePatrolRoute val2 = val.AddComponent<VehiclePatrolRoute>(); ((Object)val2).name = loadedVehiclePatrol.name; val2.RouteName = loadedVehiclePatrol.name; val2.StartWaypointIndex = 0; Transform[] array = (Transform[])(object)new Transform[loadedVehiclePatrol.waypoints.Count]; for (int i = 0; i < loadedVehiclePatrol.waypoints.Count; i++) { GameObject val3 = new GameObject((i == 0) ? "Waypoint" : $"Waypoint ({i})"); val3.transform.position = loadedVehiclePatrol.waypoints[i]; val3.transform.parent = val.transform; array[i] = val3.transform; } val2.Waypoints = array; val.transform.parent = parent; VehiclePatrolInstance val4 = new VehiclePatrolInstance(); val4.StartTime = loadedVehiclePatrol.startTime; val4.Route = val2; val4.IntensityRequirement = loadedVehiclePatrol.intensityRequirement; val4.OnlyIfCurfewEnabled = loadedVehiclePatrol.onlyIfCurfew; val.transform.parent = parent; val.SetActive(true); generatedVehiclePatrolInstances.Add(val4, loadedVehiclePatrol.days); } } if (day == "") { int num = template.VehiclePatrols.Length; int count = generatedVehiclePatrolInstances.Count; int num2 = num + count; VehiclePatrolInstance[] array2 = (VehiclePatrolInstance[])(object)new VehiclePatrolInstance[num2]; Array.Copy(template.VehiclePatrols, array2, num); int num3 = num; foreach (KeyValuePair<VehiclePatrolInstance, List<string>> generatedVehiclePatrolInstance in generatedVehiclePatrolInstances) { if (num3 >= num2) { break; } array2[num3] = generatedVehiclePatrolInstance.Key; num3++; } return array2; } int num4 = template.VehiclePatrols.Length; int num5 = 0; foreach (KeyValuePair<VehiclePatrolInstance, List<string>> generatedVehiclePatrolInstance2 in generatedVehiclePatrolInstances) { if (generatedVehiclePatrolInstance2.Value.Contains(day)) { num5++; } } if (num5 == 0) { return template.VehiclePatrols; } int num6 = num4 + num5; VehiclePatrolInstance[] array3 = (VehiclePatrolInstance[])(object)new VehiclePatrolInstance[num6]; Array.Copy(template.VehiclePatrols, array3, num4); int num7 = num4; foreach (KeyValuePair<VehiclePatrolInstance, List<string>> generatedVehiclePatrolInstance3 in generatedVehiclePatrolInstances) { if (num7 >= num6) { break; } if (generatedVehiclePatrolInstance3.Value.Contains(day)) { array3[num7] = generatedVehiclePatrolInstance3.Key; num7++; } } DebugModule.Log($" {day}: Added {num5} vehicle patrols ({num4} -> {num6})", "GenerateVehiclePatrol"); return array3; } } [Serializable] public class SerializedVehiclePatrol { public int startTime = 2300; public int intensityRequirement = 1; public bool onlyIfCurfew; public string name = "NACops Vehicle Extra Loop"; public List<string> days; public List<Vector3> waypoints = new List<Vector3>(); } [HarmonyPatch(typeof(Customer), "ProcessHandover")] public static class Customer_ProcessHandover_Patch { public static int cooldownHours = 3; [HarmonyPrefix] public static bool Prefix(Customer __instance, EHandoverOutcome outcome, Contract contract, List<ItemInstance> items, bool handoverByPlayer, bool giveBonuses = true) { MelonCoroutines.Start(PreProcessHandover(__instance, handoverByPlayer)); return true; } public static IEnumerator PreProcessHandover(Customer __instance, bool handoverByPlayer) { if (!handoverByPlayer) { yield break; } if (cooldownHours > 0) { DebugModule.Log($"Cant run buy bust, on cooldown: {cooldownHours}", "PreProcessHandover"); yield break; } if (NACops.currentConfig.BuyBusts) { MelonCoroutines.Start(SummonBustCop(__instance)); } yield return null; } public static IEnumerator SummonBustCop(Customer customer) { int value = Mathf.RoundToInt(customer.NPC.RelationData.RelationDelta * 10f); var (num, num2) = ThresholdUtils.Evaluate(NACops.thresholdConfig.BuyBustProbability, value); if (NACops.currentConfig.DebugMode || !(Random.Range(num, num2) < 0.5f)) { DebugModule.Log("Spawn buy bust", "SummonBustCop"); cooldownHours = 3; ((Component)CopInitHelper.buyBustCop).gameObject.SetActive(true); ((Component)((Component)CopInitHelper.buyBustCop).transform.Find("Avatar")).gameObject.SetActive(true); ((Behaviour)((Component)CopInitHelper.buyBustCop).GetComponent<NavMeshAgent>()).enabled = true; if (!((NPC)CopInitHelper.buyBustCop).Movement.IsPaused) { ((NPC)CopInitHelper.buyBustCop).Movement.PauseMovement(); } ((NPC)CopInitHelper.buyBustCop).Awareness.SetAwarenessActive(true); Player local = Player.Local; Vector3 val = ((Component)customer).transform.position + ((Component)customer).transform.forward * 3f; Vector3 val2 = default(Vector3); bool closestReachablePoint = ((NPC)CopInitHelper.buyBustCop).Movement.GetClosestReachablePoint(val, ref val2); bool instant = false; if (closestReachablePoint && val2 != Vector3.zero) { ((NPC)CopInitHelper.buyBustCop).Movement.Warp(val2); ((NPC)CopInitHelper.buyBustCop).Movement.ResumeMovement(); DebugModule.Log("Drug bust officer spawned now at " + ((object)((NPC)CopInitHelper.buyBustCop).CenterPoint/*cast due to .constrained prefix*/).ToString(), "SummonBustCop"); ((VOEmitter)CopInitHelper.buyBustCop.ChatterVO).Play((EVOLineType)2); ((NPC)CopInitHelper.buyBustCop).Movement.FacePoint(((Component)customer).transform.position, 0.5f); local.CrimeData.SetPursuitLevel((EPursuitLevel)3); CopInitHelper.buyBustCop.BeginFootPursuit(local.PlayerCode); ((Behaviour)CopInitHelper.buyBustCop.PursuitBehaviour).Enable_Networked(); NACops.coros.Add(MelonCoroutines.Start(SetTaser(CopInitHelper.buyBustCop))); NACops.coros.Add(MelonCoroutines.Start(LateEnableArrest(CopInitHelper.buyBustCop))); local.CrimeData.AddCrime((Crime)new AttemptingToSell(), 10); } else { DebugModule.Log("Failed to Get closest reachable position for drug bust", "SummonBustCop"); instant = true; } NACops.coros.Add(MelonCoroutines.Start(DisposeSummoned(instant, local))); } yield break; } public static IEnumerator LateEnableArrest(PoliceOfficer offc) { float maxWait = 8f; float current = 0f; while (true) { if (!NACops.registered) { yield break; } if (current >= maxWait) { break; } yield return NACops.Wait01; if (offc.PursuitBehaviour.arrestingEnabled) { offc.PursuitBehaviour.arrestingEnabled = false; } current += 0.1f; } offc.PursuitBehaviour.arrestingEnabled = true; } public static IEnumerator SetTaser(PoliceOfficer offc) { ((NPC)offc).Behaviour.CombatBehaviour.SetWeapon(((Object)(object)offc.TaserPrefab != (Object)null) ? offc.TaserPrefab.AssetPath : string.Empty); if (!((Object)(object)((NPC)offc).Behaviour.CombatBehaviour.currentWeapon == (Object)null)) { AvatarWeapon currentWeapon = ((NPC)offc).Behaviour.CombatBehaviour.currentWeapon; AvatarRangedWeapon val = (AvatarRangedWeapon)(object)((currentWeapon is AvatarRangedWeapon) ? currentWeapon : null); if ((Object)(object)val != (Object)null) { val.CanShootWhileMoving = true; val.MagazineSize = 20; val.MaxFireRate = 0.3f; ((AvatarWeapon)val).MaxUseRange = 24f; val.ReloadTime = 0.2f; val.RaiseTime = 0.1f; val.HitChance_MaxRange = 0.6f; val.HitChance_MinRange = 0.9f; ((AvatarWeapon)val).CooldownDuration = 0.3f; } } yield break; } public static IEnumerator DisposeSummoned(bool instant, Player target) { yield return NACops.Wait1; if (!NACops.registered) { yield break; } int lifeTime = 0; int maxTime = 30; if (!instant && (Object)(object)target != (Object)null && (Object)(object)CopInitHelper.buyBustCop != (Object)null) { while (lifeTime <= maxTime && !target.IsArrested && ((NPC)CopInitHelper.buyBustCop).IsConscious) { lifeTime++; yield return NACops.Wait1; if (!NACops.registered) { yield break; } } } if (!((NPC)CopInitHelper.buyBustCop).IsConscious) { yield return NACops.Wait30; ((NPC)CopInitHelper.buyBustCop).Health.Revive(); } ((NPC)CopInitHelper.buyBustCop).Awareness.SetAwarenessActive(false); ((Component)CopInitHelper.buyBustCop).gameObject.SetActive(false); ((Component)((Component)CopInitHelper.buyBustCop).transform.Find("Avatar")).gameObject.SetActive(false); if (!((NPC)CopInitHelper.buyBustCop).Movement.IsPaused) { ((NPC)CopInitHelper.buyBustCop).Movement.PauseMovement(); } ((Behaviour)((Component)CopInitHelper.buyBustCop).GetComponent<NavMeshAgent>()).enabled = false; DebugModule.Log("Disposed summoned bustcop", "DisposeSummoned"); } public static void ReduceBuyBustHours() { if (cooldownHours > 0) { cooldownHours--; } DebugModule.Log($"Reduce buy bust hours now: {cooldownHours}", "ReduceBuyBustHours"); } } public class UnityContractResolver : DefaultContractResolver { protected override JsonObjectContract CreateObjectContract(Type objectType) { JsonObjectContract val = ((DefaultContractResolver)this).CreateObjectContract(objectType); if (objectType == typeof(Vector3)) { for (int num = ((Collection<JsonProperty>)(object)val.Properties).Count - 1; num >= 0; num--) { JsonProperty val2 = ((Collection<JsonProperty>)(object)val.Properties)[num]; if (val2.PropertyName == "normalized" || val2.PropertyName == "magnitude" || val2.PropertyName == "sqrMagnitude") { ((Collection<JsonProperty>)(object)val.Properties).RemoveAt(num); } } } return val; } } public static class ConfigLoader { [Serializable] public class ModConfig { public bool DebugMode; public bool RaidsEnabled = true; public bool ExtraOfficerPatrols = true; public bool ExtraVehiclePatrols = true; public bool ExtraOfficerSentries = true; public bool CheckpointsEnabled = true; public bool NoOpenCarryWeapons = true; public bool PrivateInvestigator = true; public bool WeedInvestigator = true; public bool CorruptCops = true; public bool SnitchingSamples = true; public bool BuyBusts = true; public bool MassSurveillance = true; public bool NearbyCrazyCops = true; public bool LethalCops; public bool RacistCops; } [Serializable] public class NAOfficerConfig { public int ModAddedOfficersCount = 8; public bool CanEnterBuildings = true; public bool ShowNoticeIcons = true; public bool OverrideArresting = true; public float ArrestTime = 1.25f; public float ArrestRange = 3.5f; public bool OverrideMovement = true; public float MovementSpeedMultiplier = 1.45f; public bool OverrideWeapon = true; public string RangedWeapon = "m1911"; public float WeaponDamage = 46f; public float WeaponAimTimeMax = 1f; public float WeaponAimTimeMin = 0.5f; public int WeaponMagSize = 20; public float WeaponFireRate = 0.33f; public float WeaponMaxRange = 25f; public float WeaponReloadTime = 0.5f; public float WeaponRaiseTime = 0.2f; public float WeaponHitChanceMax = 0.3f; public float WeaponHitChanceMin = 0.8f; public bool OverrideTaser = true; public float TaserDamage = 5f; public float TaserAimTimeMax = 1f; public float TaserAimTimeMin = 0.5f; public float TaserFireRate = 3f; public float TaserMaxRange = 15f; public float TaserReloadTime = 1f; public float TaserRaiseTime = 0.7f; public float TaserHitChanceMax = 0.3f; public float TaserHitChanceMin = 0.8f; public bool OverrideMaxHealth = true; public float OfficerMaxHealth = 175f; public bool OverrideBodySearch = true; public float BodySearchDuration = 6f; public float BodySearchChance = 1f; public bool OverrideCombatBeh = true; public float CombatGiveUpRange = 9999f; public float CombatSearchTime = 9999f; public float CombatMoveSpeed = 1.3f; public int CombatEndAfterHits; public bool OverrideVision = true; public float VisionRangeMultiplier = 2f; public Dictionary<string, float> VisionSpeed = new Dictionary<string, float> { { "Suspicious", 0.3f }, { "DisobeyingCurfew", 0.3f }, { "Vandalizing", 0.3f }, { "PettyCrime", 0.2f }, { "DrugDealing", 0.4f }, { "Wanted", 0.1f }, { "Pickpocketing", 0.3f }, { "DischargingWeapon", 0.1f }, { "Brandishing", 0.1f } }; } [Serializable] public class RaidConfig { public float TraverseToPropertySpeed = 0.47f; public float ClearPropertySpeed = 0.38f; public int MaxDestroyIters = 4; public int RaidCopsCount = 3; public int DaysUntilCanRaid = 8; public int PropertyHeatThreshold = 14; public float RaiderMaxHealth = 240f; public float RaiderWeaponDmg = 65f; } [Serializable] public class MassSurveillanceConfig { public bool UseUnidirectionalCameras = true; public bool UseOmnidirectionalCameras = true; public bool SurveilCrimeStatus = true; public bool SurveilBaseCrimes = true; public int ActiveCamerasPerDay = 5; public int CameraActivationRange = 20; public int CameraNoticeSpeed = 2; public int CameraNoticeCooldown = 30; public bool PayFinesFromBank = true; public bool GrowPaymentsWithProgression = true; public int CrimePaymentMultiplier = 1; } [Serializable] public class FootPatrolsSerialized { public List<SerializedFootPatrol> loadedPatrols = new List<SerializedFootPatrol>(); } [Serializable] public class VehiclePatrolsSerialized { public List<SerializedVehiclePatrol> loadedVehiclePatrols = new List<SerializedVehiclePatrol>(); } [Serializable] public class SentrysSerialized { public List<SerializedSentry> loadedSentrys = new List<SerializedSentry>(); } public static ModConfig LoadModConfig() { string pathTo = ModDataPaths.GetPathTo(ModDataPaths.pathModConfig); ModConfig modConfig; if (File.Exists(pathTo)) { try { modConfig = JsonConvert.DeserializeObject<ModConfig>(File.ReadAllText(pathTo)); } catch (Exception ex) { modConfig = new ModConfig(); MelonLogger.Warning("Failed to read NACops Mod config: " + ex); } } else { MelonLogger.Warning("Missing NACops Mod config, creating directory and template."); modConfig = new ModConfig(); Save(modConfig); } return modConfig; } public static void Save(ModConfig config, bool logConfirm = true) { try { string pathTo = ModDataPaths.GetPathTo(ModDataPaths.pathModConfig); string contents = JsonConvert.SerializeObject((object)config, (Formatting)1); Directory.CreateDirectory(Path.GetDirectoryName(pathTo)); File.WriteAllText(pathTo, contents); if (logConfirm) { MelonLogger.Warning("NACops Mod config, written to: " + pathTo); } } catch (Exception ex) { if (logConfirm) { MelonLogger.Warning("Failed to save NACops Mod config: " + ex); } } } public static NAOfficerConfig LoadOfficerConfig() { string pathTo = ModDataPaths.GetPathTo(ModDataPaths.pathOfficerConfig); NAOfficerConfig nAOfficerConfig; if (File.Exists(pathTo)) { try { nAOfficerConfig = JsonConvert.DeserializeObject<NAOfficerConfig>(File.ReadAllText(pathTo)); nAOfficerConfig.ModAddedOfficersCount = Mathf.Clamp(nAOfficerConfig.ModAddedOfficersCount, 0, 20); if (!new List<string> { "m1911", "goldenm1911", "shotgun", "revolver" }.Contains(nAOfficerConfig.RangedWeapon)) { nAOfficerConfig.RangedWeapon = "m1911"; } foreach (string item in nAOfficerConfig.VisionSpeed.Keys.ToList()) { nAOfficerConfig.VisionSpeed[item] = Mathf.Clamp(nAOfficerConfig.VisionSpeed[item], 0.01f, 10f); } } catch (Exception ex) { nAOfficerConfig = new NAOfficerConfig(); MelonLogger.Warning("Failed to read NACops config: " + ex); } } else { MelonLogger.Warning("Missing NACops Officers config, creating directory and template."); nAOfficerConfig = new NAOfficerConfig(); Save(nAOfficerConfig); } return nAOfficerConfig; } public static void Save(NAOfficerConfig config) { try { string pathTo = ModDataPaths.GetPathTo(ModDataPaths.pathOfficerConfig); string contents = JsonConvert.SerializeObject((object)config); Directory.CreateDirectory(Path.GetDirectoryName(pathTo)); File.WriteAllText(pathTo, contents); MelonLogger.Warning("NACops Officers config, written to: " + pathTo); } catch (Exception ex) { MelonLogger.Warning("Failed to save NACops Officers config: " + ex); } } public static FootPatrolsSerialized LoadPatrolsConfig() { string pathTo = ModDataPaths.GetPathTo(ModDataPaths.pathPatrolsConfig); FootPatrolsSerialized footPatrolsSerialized; if (File.Exists(pathTo)) { try { footPatrolsSerialized = JsonConvert.DeserializeObject<FootPatrolsSerialized>(File.ReadAllText(pathTo)); List<string> list = new List<string> { "mon", "tue", "wed", "thu", "fri", "sat", "sun" }; foreach (SerializedFootPatrol loadedPatrol in footPatrolsSerialized.loadedPatrols) { loadedPatrol.members = Mathf.Clamp(loadedPatrol.members, 1, 4); loadedPatrol.name = (string.IsNullOrEmpty(loadedPatrol.name) ? "NACopsPatrol " : loadedPatrol.name); loadedPatrol.intensityRequirement = Mathf.Clamp(loadedPatrol.intensityRequirement, 0, 10); if (!TimeManager.IsValid24HourTime(loadedPatrol.startTime.ToString())) { MelonLogger.Warning("FootPatrolsConfig '" + loadedPatrol.name + "' has invalid start time"); loadedPatrol.startTime = 1900; } if (!TimeManager.IsValid24HourTime(loadedPatrol.endTime.ToString())) { MelonLogger.Warning("FootPatrolsConfig '" + loadedPatrol.name + "' has invalid end time"); loadedPatrol.endTime = 2330; } if (loadedPatrol.waypoints.Count == 0) { MelonLogger.Warning("FootPatrolsConfig is missing Waypoints for " + loadedPatrol.name); } for (int num = loadedPatrol.days.Count - 1; num != -1; num--) { if (loadedPatrol.days[num] != string.Empty) { loadedPatrol.days[num] = loadedPatrol.days[num].ToLower(); if (!list.Contains(loadedPatrol.days[num])) { MelonLogger.Warning($"FootPatrolsConfig '{loadedPatrol.name}' has invalid weekday: '{loadedPatrol.days[num]}'"); loadedPatrol.days.RemoveAt(num); } } else { loadedPatrol.days.RemoveAt(num); } } } } catch (Exception ex) { footPatrolsSerialized = new FootPatrolsSerialized(); MelonLogger.Warning("Failed to read FootPatrolsSerialized config: " + ex); } } else { footPatrolsSerialized = new FootPatrolsSerialized(); footPatrolsSerialized.loadedPatrols = new List<SerializedFootPatrol>(); Save(footPatrolsSerialized); } return footPatrolsSerialized; } public static void Save(FootPatrolsSerialized config) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown try { string pathTo = ModDataPaths.GetPathTo(ModDataPaths.pathPatrolsConfig); JsonSerializerSettings val = new JsonSerializerSettings { ContractResolver = (IContractResolver)(object)new UnityContractResolver() }; string contents = JsonConvert.SerializeObject((object)config, (Formatting)1, val); Directory.CreateDirectory(Path.GetDirectoryName(pathTo)); File.WriteAllText(pathTo, contents); MelonLogger.Warning("Foot Patrols Config has been saved!"); } catch (Exception ex) { MelonLogger.Warning("Failed to save NACops Foot Patrols config: " + ex); } } public static VehiclePatrolsSerialized LoadVehiclePatrolsConfig() { string pathTo = ModDataPaths.GetPathTo(ModDataPaths.pathVehiclePatrolsConfig); VehiclePatrolsSerialized vehiclePatrolsSerialized; if (File.Exists(pathTo)) { try { vehiclePatrolsSerialized = JsonConvert.DeserializeObject<VehiclePatrolsSerialized>(File.ReadAllText(pathTo)); List<string> list = new List<string> { "mon", "tue", "wed", "thu", "fri", "sat", "sun" }; foreach (SerializedVehiclePatrol loadedVehiclePatrol in vehiclePatrolsSerialized.loadedVehiclePatrols) { loadedVehiclePatrol.name = (string.IsNullOrEmpty(loadedVehiclePatrol.name) ? "NaCopsVehiclePatrol " : loadedVehiclePatrol.name); loadedVehiclePatrol.intensityRequirement = Mathf.Clamp(loadedVehiclePatrol.intensityRequirement, 0, 10); if (!TimeManager.IsValid24HourTime(loadedVehiclePatrol.startTime.ToString())) { MelonLogger.Warning("Vehicle Patrol Config '" + loadedVehiclePatrol.name + "' has invalid start time"); loadedVehiclePatrol.startTime = 1900; } if (loadedVehiclePatrol.waypoints.Count == 0) { MelonLogger.Warning("Vehicle Patrol Config is missing Waypoints for " + loadedVehiclePatrol.name); } for (int num = loadedVehiclePatrol.days.Count - 1; num != -1; num--) { if (loadedVehiclePatrol.days[num] != string.Empty) { loadedVehiclePatrol.days[num] = loadedVehiclePatrol.days[num].ToLower(); if (!list.Contains(loadedVehiclePatrol.days[num])) { MelonLogger.Warning($"Vehicle Patrol Config '{loadedVehiclePatrol.name}' has invalid weekday: '{loadedVehiclePatrol.days[num]}'"); loadedVehiclePatrol.days.RemoveAt(num); } } else { loadedVehiclePatrol.days.RemoveAt(num); } } } } catch (Exception ex) { vehiclePatrolsSerialized = new VehiclePatrolsSerialized(); MelonLogger.Warning("Failed to read Vehicle Patrol config: " + ex); } } else { vehiclePatrolsSerialized = new VehiclePatrolsSerialized(); vehiclePatrolsSerialized.loadedVehiclePatrols = new List<SerializedVehiclePatrol>(); Save(vehiclePatrolsSerialized); } return vehiclePatrolsSerialized; } public static void Save(VehiclePatrolsSerialized config) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown try { string pathTo = ModDataPaths.GetPathTo(ModDataPaths.pathVehiclePatrolsConfig); JsonSerializerSettings val = new JsonSerializerSettings { ContractResolver = (IContractResolver)(object)new UnityContractResolver() }; string contents = JsonConvert.SerializeObject((object)config, (Formatting)1, val); Directory.CreateDirectory(Path.GetDirectoryName(pathTo)); File.WriteAllText(pathTo, contents); MelonLogger.Warning("Vehicle Patrols config has been saved!"); } catch (Exception ex) { MelonLogger.Warning("Failed to save NACops Vehicle Patrols config: " + ex); } } public static SentrysSerialized LoadSentryConfig() { string pathTo = ModDataPaths.GetPathTo(ModDataPaths.pathSentrysConfig); SentrysSerialized sentrysSerialized; if (File.Exists(pathTo)) { try { sentrysSerialized = JsonConvert.DeserializeObject<SentrysSerialized>(File.ReadAllText(pathTo)); List<string> list = new List<string> { "mon", "tue", "wed", "thu", "fri", "sat", "sun" }; foreach (SerializedSentry loadedSentry in sentrysSerialized.loadedSentrys) { loadedSentry.members = Mathf.Clamp(loadedSentry.members, 1, 2); loadedSentry.name = (string.IsNullOrEmpty(loadedSentry.name) ? "NACopsSentry " : loadedSentry.name); loadedSentry.intensityRequirement = Mathf.Clamp(loadedSentry.intensityRequirement, 0, 10); if (!TimeManager.IsValid24HourTime(loadedSentry.startTime.ToString())) { MelonLogger.Warning("Sentry Config '" + loadedSentry.name + "' has invalid start time"); loadedSentry.startTime = 1900; } if (!TimeManager.IsValid24HourTime(loadedSentry.endTime.ToString())) { MelonLogger.Warning("Sentry Config '" + loadedSentry.name + "' has invalid end time"); loadedSentry.endTime = 2330; } if (loadedSentry.minutesPerPoint <= 0 || loadedSentry.minutesPerPoint > 480) { MelonLogger.Warning("Sentry Config '" + loadedSentry.name + "' has invalid minutes per point value. Range 1-480"); loadedSentry.minutesPerPoint = 60; } for (int num = loadedSentry.days.Count - 1; num != -1; num--) { if (loadedSentry.days[num] != string.Empty) { loadedSentry.days[num] = loadedSentry.days[num].ToLower(); if (!list.Contains(loadedSentry.days[num])) { MelonLogger.Warning($"Sentry Config '{loadedSentry.name}' has invalid weekday: '{loadedSentry.days[num]}'"); loadedSentry.days.RemoveAt(num); } } else { loadedSentry.days.RemoveAt(num); } } } } catch (Exception ex) { sentrysSerialized = new SentrysSerialized(); MelonLogger.Warning("Failed to read SentrysSerialized config: " + ex); } } else { sentrysSerialized = new SentrysSerialized(); sentrysSerialized.loadedSentrys = new List<SerializedSentry>(); Save(sentrysSerialized); } return sentrysSerialized; } public static void Save(SentrysSerialized config) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown try { string pathTo = ModDataPaths.GetPathTo(ModDataPaths.pathSentrysConfig); JsonSerializerSettings val = new JsonSerializerSettings { ContractResolver = (IContractResolver)(object)new UnityContractResolver() }; string contents = JsonConvert.SerializeObject((object)config, (Formatting)1, val); Directory.CreateDirectory(Path.GetDirectoryName(pathTo)); File.WriteAllText(pathTo, contents); MelonLogger.Warning("Sentry config has been saved!"); } catch (Exception ex) { MelonLogger.Warning("Failed to save NACops Sentry config: " + ex); } } public static string SanitizeAndFormatName(string orgName) { string text = orgName; if (text != null) { text = text.Replace(" ", "_").ToLower(); text = text.Replace(",", ""); text = text.Replace(".", ""); text = text.Replace("<", ""); text = text.Replace(">", ""); text = text.Replace(":", ""); text = text.Replace("\"", ""); text = text.Replace("/", ""); text = text.Replace("\\", ""); text = text.Replace("|", ""); text = text.Replace("?", ""); text = text.Replace("*", ""); } return text + ".json"; } public static PropertiesHeatSerialized LoadPropertyHeats() { string pathTo = ModDataPaths.GetPathTo(ModDataPaths.pathPropertyHeatConfig); string organisationName = Singleton<LoadManager>.Instance.ActiveSaveInfo.OrganisationName; int saveSlotNumber = Singleton<LoadManager>.Instance.ActiveSaveInfo.SaveSlotNumber; string path = $"{saveSlotNumber}_{SanitizeAndFormatName(organisationName)}"; PropertiesHeatSerialized propertiesHeatSerialized; if (File.Exists(Path.Combine(pathTo, path))) { try { propertiesHeatSerialized = JsonConvert.DeserializeObject<PropertiesHeatSerialized>(File.ReadAllText(Path.Combine(pathTo, path))); } catch (Exception ex) { propertiesHeatSerialized = new PropertiesHeatSerialized(); propertiesHeatSerialized.loadedPropertyHeats = new List<PropertyHeat>(); string[] array = new string[6] { "sweatshop", "bungalow", "storageunit", "dockswarehouse", "barn", "manor" }; foreach (string propertyCode in array) { PropertyHeat propertyHeat = new PropertyHeat(); propertyHeat.propertyCode = propertyCode; propertiesHeatSerialized.loadedPropertyHeats.Add(propertyHeat); } MelonLogger.Warning("Failed to read NACops Property Heat config: " + ex); } } else { MelonLogger.Warning("Missing NACops Property Heat config, creating directory and template."); propertiesHeatSerialized = new PropertiesHeatSerialized(); Save(propertiesHeatSerialized, generateTemplate: true); } return propertiesHeatSerialized; } public static void Save(PropertiesHeatSerialized config, bool generateTemplate = false) { string pathTo = ModDataPaths.GetPathTo(ModDataPaths.pathPropertyHeatConfig); if (generateTemplate) { config.loadedPropertyHeats = new List<PropertyHeat>(); string[] array = new string[6] { "sweatshop", "bungalow", "storageunit", "dockswarehouse", "barn", "manor" }; foreach (string propertyCode in array) { PropertyHeat propertyHeat = new PropertyHeat(); propertyHeat.propertyCode = propertyCode; config.loadedPropertyHeats.Add(propertyHeat); } } try { string organisationName = Singleton<LoadManager>.Instance.ActiveSaveInfo.OrganisationName; int saveSlotNumber = Singleton<LoadManager>.Instance.ActiveSaveInfo.SaveSlotNumber; string path = $"{saveSlotNumber}_{SanitizeAndFormatName(organisationName)}"; string text = Path.Combine(pathTo, path); string contents = JsonConvert.SerializeObject((object)config, (Formatting)1); Directory.CreateDirectory(Path.GetDirectoryName(text)); File.WriteAllText(text, contents); if (generateTemplate) { MelonLogger.Warning("NACops Property Heat config, written to: " + text); } } catch (Exception ex) { MelonLogger.Warning("Failed to save NACops Property Heat config: " + ex); } } public static ThresholdMappings LoadFrequencyConfig() { string pathTo = ModDataPaths.GetPathTo(ModDataPaths.pathEventFrequencyConfig); ThresholdMappings thresholdMappings; if (File.Exists(pathTo)) { try { thresholdMappings = JsonConvert.DeserializeObject<ThresholdMappings>(File.ReadAllText(pathTo)); foreach (MinMaxThreshold item in thresholdMappings.LethalCopFrequency) { if (item.MinOf < 0) { item.MinOf = 0; } if (item.Min >= item.Max) { MelonLogger.Warning("Found invalid value in progression.json at LethalCopFreq Min value, must be smaller than Max value"); if (item.Max > 0f) { item.Min = item.Max * 0.5f; } } } foreach (MinMaxThreshold item2 in thresholdMappings.LethalCopRange) { if (item2.MinOf < 0) { item2.MinOf = 0; } if (item2.Min >= item2.Max) { MelonLogger.Warning("Found invalid value in progression.json at LethalCopRange Min value, must be smaller than Max value"); if (item2.Max > 0f) { item2.Min = item2.Max * 0.5f; } } } foreach (MinMaxThreshold item3 in thresholdMappings.NearbyCrazyFrequency) { if (item3.MinOf < 0) { item3.MinOf = 0; } if (item3.Min >= item3.Max) { MelonLogger.Warning("Found invalid value in progression.json at NearbyCrazFreq Min value, must be smaller than Max value"); if (item3.Max > 0f) { item3.Min = item3.Max * 0.5f; } } } foreach (MinMaxThreshold item4 in thresholdMappings.NearbyCrazyRange) { if (item4.MinOf < 0) { item4.MinOf = 0; } if (item4.Min >= item4.Max) { MelonLogger.Warning("Found invalid value in progression.json at NearbyCrazRange Min value, must be smaller than Max value"); if (item4.Max > 0f) { item4.Min = item4.Max * 0.5f; } } } foreach (MinMaxThreshold item5 in thresholdMappings.PIFrequency) { if (item5.MinOf < 0) { item5.MinOf = 0; } if (item5.Min >= item5.Max) { MelonLogger.Warning("Found invalid value in progression.json at PIFreq Min value, must be smaller than Max value"); if (item5.Max > 0f) { item5.Min = item5.Max * 0.5f; } } } foreach (MinMaxThreshold item6 in thresholdMappings.SnitchProbability) { if (item6.MinOf < 0) { item6.MinOf = 0; } if (item6.Min >= item6.Max) { MelonLogger.Warning("Found invalid value in progression.json at SnitchProbability Min value, must be smaller than Max value"); if (item6.Max > 0f) { item6.Min = item6.Max * 0.5f; } } } foreach (MinMaxThreshold item7 in thresholdMappings.BuyBustProbability) { if (item7.MinOf < 0) { item7.MinOf = 0; } if (item7.Min >= item7.Max) { MelonLogger.Warning("Found invalid value in progression.json at BuyBustProbability Min value, must be smaller than Max value"); if (item7.Max > 0f) { item7.Min = item7.Max * 0.5f; } } } } catch (Exception ex) { thresholdMappings = new ThresholdMappings(); MelonLogger.Warning("Failed to read NACops Event Frequency config: " + ex); } } else { MelonLogger.Warning("Missing NACops Event Frequency config, creating directory and template."); thresholdMappings = new ThresholdMappings(); Save(thresholdMappings); } return thresholdMappings; } public static void Save(ThresholdMappings config) { try { string pathTo = ModDataPaths.GetPathTo(ModDataPaths.pathEventFrequencyConfig); string contents = JsonConvert.SerializeObject((object)config, (Formatting)1); Directory.CreateDirectory(Path.GetDirectoryName(pathTo)); File.WriteAllText(pathTo, contents); MelonLogger.Warning("NACops Event Frequency config, written to: " + pathTo); } catch (Exception ex) { MelonLogger.Warning("Failed to save NACops Event Frequency config: " + ex); } } public static RaidConfig LoadRaidConfig() { string pathTo = ModDataPaths.GetPathTo(ModDataPaths.pathRaidConfig); RaidConfig raidConfig; if (File.Exists(pathTo)) { try { raidConfig = JsonConvert.DeserializeObject<RaidConfig>(File.ReadAllText(pathTo)); raidConfig.TraverseToPropertySpeed = Mathf.Clamp(raidConfig.TraverseToPropertySpeed, 0.1f, 1f); raidConfig.ClearPropertySpeed = Mathf.Clamp(raidConfig.ClearPropertySpeed, 0.1f, 1f); raidConfig.MaxDestroyIters = Mathf.Clamp(raidConfig.MaxDestroyIters, 1, 10); raidConfig.RaidCopsCount = Mathf.Clamp(raidConfig.RaidCopsCount, 1, 10); raidConfig.DaysUntilCanRaid = Mathf.Clamp(raidConfig.DaysUntilCanRaid, 1, 20); raidConfig.PropertyHeatThreshold = Mathf.Clamp(raidConfig.PropertyHeatThreshold, 1, 100); raidConfig.RaiderMaxHealth = Mathf.Clamp(raidConfig.RaiderMaxHealth, 1f, 300f); raidConfig.RaiderWeaponDmg = Mathf.Clamp(raidConfig.RaiderWeaponDmg, 1f, 100f); } catch (Exception ex) { raidConfig = new RaidConfig(); MelonLogger.Warning("Failed to read NACops Raid config: " + ex); } } else { MelonLogger.Warning("Missing NACops Raid config, creating directory and template."); raidConfig = new RaidConfig(); Save(raidConfig); } return raidConfig; } public static void Save(RaidConfig config) { try { string pathTo = ModDataPaths.GetPathTo(ModDataPaths.pathRaidConfig); string contents = JsonConvert.SerializeObject((object)config, (Formatting)1); Directory.CreateDirectory(Path.GetDirectoryName(pathTo)); File.WriteAllText(pathTo, contents); MelonLogger.Warning("NACops Raid config, written to: " + pathTo); } catch (Exception ex) { MelonLogger.Warning("Failed to save NACops Raid config: " + ex); } } public static MassSurveillanceConfig LoadSurveillanceConfig() { string pathTo = ModDataPaths.GetPathTo(ModDataPaths.pathSurveillanceConfig); MassSurveillanceConfig massSurveillanceConfig; if (File.Exists(pathTo)) { try { massSurveillanceConfig = JsonConvert.DeserializeObject<MassSurveillanceConfig>(File.ReadAllText(pathTo)); massSurveillanceConfig.ActiveCamerasPerDay = Mathf.Clamp(massSurveillanceConfig.ActiveCamerasPerDay, 1, 10); massSurveillanceConfig.CameraNoticeCooldown = Mathf.Clamp(massSurveillanceConfig.CameraNoticeCooldown, 1, 60); massSurveillanceConfig.CameraActivationRange = Mathf.Clamp(massSurveillanceConfig.CameraActivationRange, 1, 50); massSurveillanceConfig.CameraNoticeSpeed = Mathf.Clamp(massSurveillanceConfig.CameraNoticeSpeed, 1, 10); } catch (Exception ex) { massSurveillanceConfig = new MassSurveillanceConfig(); MelonLogger.Warning("Failed to read NACops Mass Surveillance config: " + ex); } } else { MelonLogger.Warning("Missing NACops Mass Surveillance config, creating directory and template."); massSurveillanceConfig = new MassSurveillanceConfig(); Save(massSurveillanceConfig); } return massSurveillanceConfig; } public static void Save(MassSurveillanceConfig config) { try { string pathTo = ModDataPaths.GetPathTo(ModDataPaths.pathSurveillanceConfig); string contents = JsonConvert.SerializeObject((object)config, (Formatting)1); Directory.CreateDirectory(Path.GetDirectoryName(pathTo)); File.WriteAllText(pathTo, contents); MelonLogger.Warning("NACops Mass Surveillance config, written to: " + pathTo); } catch (Exception ex) { MelonLogger.Warning("Failed to save NACops Mass Surveillance config: " + ex); } } } public class ModPrefsHandler { public MelonPreferences_Category modConfigCategory; public void SetupMelonPreferences() { string text = "NACops XOWithSauce"; modConfigCategory = MelonPreferences.CreateCategory(text, "NACops"); modConfigCategory.CreateEntry<bool>("DebugMode", NACops.currentConfig.DebugMode, "Debug Mode Enabled", "Enable debug mode to test features", false, false, (ValueValidator)null, (string)null); modConfigCategory.CreateEntry<bool>("RaidsEnabled", NACops.currentConfig.RaidsEnabled, "Raids Enabled", "Enable raid events", false, false, (ValueValidator)null, (string)null); modConfigCategory.CreateEntry<bool>("ExtraOfficerPatrols", NACops.currentConfig.ExtraOfficerPatrols, "Extra officer foot patrols", "Adds new officer foot patrols from 'Spawn/patrols.json' file", false, false, (ValueValidator)null, (string)null); modConfigCategory.CreateEntry<bool>("ExtraVehiclePatrols", NACops.currentConfig.ExtraVehiclePatrols, "Extra officer vehicle patrols", "Adds new officer vehicle patrols from 'Spawn/vehiclepatrols.json' file", false, false, (ValueValidator)null, (string)null); modConfigCategory.CreateEntry<bool>("ExtraOfficerSentries", NACops.currentConfig.ExtraOfficerSentries, "Extra officer sentries", "Adds new officer stationary sentries from 'Spawn/sentries.json' file", false, false, (ValueValidator)null, (string)null); modConfigCategory.CreateEntry<bool>("CheckpointsEnabled", NACops.currentConfig.CheckpointsEnabled, "Checkpoints Enabled", "Enable the usage of road block checkpoints", false, false, (ValueValidator)null, (string)null); modConfigCategory.CreateEntry<bool>("NoOpenCarryWeapons", NACops.currentConfig.NoOpenCarryWeapons, "No open carry weapons", "Makes holding weapons in hand and in inventory illegal", false, false, (ValueValidator)null, (string)null); modConfigCategory.CreateEntry<bool>("PrivateInvestigator", NACops.currentConfig.PrivateInvestigator, "Private investigator", "Enable the private investigator who spies on the player and gathers evidence for property heat system", false, false, (ValueValidator)null, (string)null); modConfigCategory.CreateEntry<bool>("WeedInvestigator", NACops.currentConfig.WeedInvestigator, "Weed investigator", "Enable a feature where using drugs will cause nearby cops to search for the player", false, false, (ValueValidator)null, (string)null); modConfigCategory.CreateEntry<bool>("CorruptCops", NACops.currentConfig.CorruptCops, "Corrupt cops", "Enable a feature where cops give false charges that cause the players arrest to be more expensive", false, false, (ValueValidator)null, (string)null); modConfigCategory.CreateEntry<bool>("SnitchingSamples", NACops.currentConfig.SnitchingSamples, "Snitching samples", "Enable a feature where giving free samples can result in Investigation crime status", false, false, (ValueValidator)null, (string)null); modConfigCategory.CreateEntry<bool>("BuyBusts", NACops.currentConfig.BuyBusts, "Buy busts", "Enable a feature where after completing a deal an officer can spawn behind the player and attempt to arrest", false, false, (ValueValidator)null, (string)null); modConfigCategory.CreateEntry<bool>("MassSurveillance", NACops.currentConfig.MassSurveillance, "Mass Surveillance", "Enable the usage of Cameras across Hyland Point to monitor the player and report any crimes.", false, false, (ValueValidator)null, (string)null); modConfigCategory.CreateEntry<bool>("NearbyCrazyCops", NACops.currentConfig.NearbyCrazyCops, "Nearby crazy cops", "Enable a feature where cops will randomly find the player nearby and body search", false, false, (ValueValidator)null, (string)null); modConfigCategory.CreateEntry<bool>("LethalCops", NACops.currentConfig.LethalCops, "Lethal cops", "Enable a feature where cops will randomly start lethally hunting the player when nearby", false, false, (ValueValidator)null, (string)null); modConfigCategory.CreateEntry<bool>("RacistCops", NACops.currentConfig.RacistCops, "Racist cops", "Enable a feature where cops will hunt down black skin coloured players on sight", false, false, (ValueValidator)null, (string)null); for (int i = 0; i < modConfigCategory.Entries.Count; i++) { string id = modConfigCategory.Entries[i].Identifier; ((MelonEventBase<LemonAction<object, object>>)(object)modConfigCategory.Entries[i].OnEntryValueChangedUntyped).Subscribe((LemonAction<object, object>)ThisEntryChanged, 0, false); void ThisEntryChanged(object objOld, object objNew) { OnEntryChange(id, objOld, objNew); } } MelonPreferences.SaveCategory<MelonPreferences_Category>(text, false); DebugModule.Log("Melon preferences created", "SetupMelonPreferences"); } public static void OnEntryChange(string identifier, object objOld, object objNew) { FieldInfo[] fields = NACops.currentConfig.GetType().GetFields(); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo.Name.Contains(identifier)) { fieldInfo.SetValue(NACops.currentConfig, (bool)objNew); } } ConfigLoader.Save(NACops.currentConfig, logConfirm: false); } } public static class ModDataPaths { private static readonly string BASE_USERDATA_NAME = "XO_WithSauce-NACops"; private static readonly string TS_PACKAGE_NAME = "XO_WithSauce-NACops_"; private static readonly string packagePathUserData = Path.Combine(MelonEnvironment.UserDataDirectory, TS_PACKAGE_NAME + "MONO", BASE_USERDATA_NAME); private static readonly string manualPathUserData = Path.Combine(MelonEnvironment.UserDataDirectory, BASE_USERDATA_NAME); public static readonly string pathModConfig = "config.json"; public static readonly string pathOfficerConfig = "officer.json"; public static readonly string pathRaidConfig = "raid.json"; public static readonly string pathEventFrequencyConfig = "progression.json"; public static readonly string pathSurveillanceConfig = "surveillance.json"; public static readonly string pathPatrolsConfig = Path.Combine("Spawn", "patrols.json"); public static readonly string pathVehiclePatrolsConfig = Path.Combine("Spawn", "vehiclepatrols.json"); public static readonly string pathSentrysConfig = Path.Combine("Spawn", "sentrys.json"); public static readonly string pathPropertyHeatConfig = "HeatData"; private static bool hasCheckedInstallationPath = false; private static bool isModManagerInstallation = false; public static string GetPathTo(string modDataDestination) { if (!hasCheckedInstallationPath) { if (Directory.Exists(packagePathUserData)) { isModManagerInstallation = true; } if (Directory.Exists(manualPathUserData)) { isModManagerInstallation = false; } hasCheckedInstallationPath = true; } return Path.Combine(isModManagerInstallation ? packagePathUserData : manualPathUserData, modDataDestination); } } public static class ConsoleModule { [Flags] public enum CommandSupport { None = 0, List = 1, Spawn = 2, SpawnNoIndex = 4, Visualize = 8, Build = 0x10 } public abstract class ConsoleCommandBase { public virtual string Name { get; } public virtual CommandSupport SupportedMethods { get; } public virtual void List() { DebugModule.Log("Not implemented", "List"); } public virtual void Spawn(int index) { DebugModule.Log("Not implemented", "Spawn"); } public virtual void Visualize(int index) { DebugModule.Log("Not implemented", "Visualize"); } public virtual void Build(string arg) { DebugModule.Log("Build Argument: " + arg + " Not implemented", "Build"); } protected static void CleanVisual() { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown if (DebugModule.pathVisualizer != null && DebugModule.pathVisualizer.Count > 0) { foreach (GameObject item in DebugModule.pathVisualizer) { Object.Destroy((Object)(object)item); } } DebugModule.pathVisualizer.Clear(); if ((Object)(object)DebugModule.lineRenderMat == (Object)null) { DebugModule.lineRenderMat = new Material(Shader.Find("Sprites/Default")); } if ((Object)(object)DebugModule.cameraBeamMat == (Object)null) { DebugModule.cameraBeamMat = new Material(Shader.Find("Universal Render Pipeline/Lit")); } } protected static void DrawPath(string name, Vector3[] points) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Path_" + name); DebugModule.pathVisualizer.Add(val); LineRenderer obj = val.AddComponent<LineRenderer>(); ((Renderer)obj).material = DebugModule.lineRenderMat; obj.widthMultiplier = 0.5f; obj.startColor = Color.blue; obj.endColor = Color.red; obj.positionCount = points.Length; obj.SetPositions(points); } } public class FootPatrolTarget : ConsoleCommandBase { public static List<Vector3> recordedPathNodes = new List<Vector3>(); public static string currentPathName; public override string Name => "footpatrol"; public override CommandSupport SupportedMethods => CommandSupport.List | CommandSupport.Spawn | CommandSupport.Visualize | CommandSupport.Build; public override void List() { string text = ""; int num = 0; text += "\nIndex: Name"; foreach (PatrolInstance key in FootPatrolGenerator.generatedPatrolInstances.Keys) { text += $"\n{num}: {((Object)key.Route).name}"; num++; } text += "\n-------"; DebugModule.Log(text, "List"); } public override void Spawn(int index) { List<PatrolInstance> list = FootPatrolGenerator.generatedPatrolInstances.Keys.ToList(); PatrolInstance instance; int originalStart; int originalEnd; if (index < list.Count) { instance = list[index]; if (instance.ActiveGroup != null) { DebugModule.Log("Foot patrol group is already active", "Spawn"); return; } originalStart = instance.StartTime; originalEnd = instance.EndTime; instance.StartTime = NetworkSingleton<TimeManager>.Instance.CurrentTime; instance.EndTime = TimeManager.AddMinutesTo24HourTime(originalStart, 240); instance.StartPatrol(); DebugModule.Log("Patrol " + ((Object)instance.Route).name + " Spawned", "Spawn"); NACops.coros.Add(MelonCoroutines.Start(EndSoon())); } IEnumerator EndSoon() { yield return (object)new WaitForSeconds(240f); instance.EndPatrol(); instance.StartTime = originalStart; instance.EndTime = originalEnd; } } public override void Visualize(int index) { ConsoleCommandBase.CleanVisual(); List<PatrolInstance> list = FootPatrolGenerator.generatedPatrolInstances.Keys.ToList(); if (index >= 0 && index < list.Count) { FootPatrolRoute route = list[index].Route; Vector3[] points = route.Waypoints.Select((Transform waypoint) => waypoint.position + Vector3.up * 8f).ToArray(); ConsoleCommandBase.DrawPath(((Object)route).name, points); DebugModule.Log("Patrol " + ((Object)route).name + " Visualized", "Visualize"); } } public override void Build(string arg) { if (arg.ToLower() == "start") { BuildStart(); } else if (isBuilding) { BuildEnd(); } } public void BuildStart() { if (isBuilding) { DebugModule.Log("Already building a path or a sentry!\n Use: nacops build " + Name + " stop\n to stop building", "BuildStart"); return; } isBuilding = true; currentPathName = $"{Name}_{Guid.NewGuid()}"; DebugModule.Log("Started building path with name " + currentPathName + "\nWalk around to create new path nodes!", "BuildStart"); NACops.coros.Add(MelonCoroutines.Start(FollowPlayer())); } public IEnumerator FollowPlayer() { Transform centerPointTransform = Player.Local.CenterPointTransform; GameObject val = new GameObject("Path"); DebugModule.pathVisualizer.Add(val); recordedPathNodes.Add(Player.Local.CenterPointTransform.position); LineRenderer val2 = val.AddComponent<LineRenderer>(); ((Renderer)val2).material = DebugModule.lineRenderMat; val2.widthMultiplier = 0.5f; val2.startColor = Color.blue; val2.endColor = Color.red; val2.positionCount = recordedPathNodes.Count; val2.SetPositions(recordedPathNodes.ToArray()); while (NACops.registered && isBuilding) { if (Vector3.Distance(centerPointTransform.position, recordedPathNodes[recordedPathNodes.Count - 1]) > 6f) { BuildNode(val2); } } yield return null; } public void BuildNode(LineRenderer lineRenderer) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) recordedPathNodes.Add(Player.Local.CenterPointTransform.position); lineRenderer.positionCount = recordedPathNodes.Count; lineRenderer.SetPositions(recordedPathNodes.ToArray()); } public void BuildEnd() { //IL_00ef: 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_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) isBuilding = false; ConsoleCommandBase.CleanVisual(); if (recordedPathNodes.Count == 0) { DebugModule.Log("No recorded nodes found.", "BuildEnd"); return; } if (recordedPathNodes.Count < 4) { DebugModule.Log("Build more path nodes to save.", "BuildEnd"); recordedPathNodes.Clear(); return; } SerializedFootPatrol serializedFootPatrol = new SerializedFootPatrol(); serializedFootPatrol.startTime = 1900; serializedFootPatrol.endTime = 500; serializedFootPatrol.members = 2; serializedFootPatrol.intensityRequirement = 1; serializedFootPatrol.onlyIfCurfew = false; serializedFootPatrol.name = currentPathName; serializedFootPatrol.days = new List<string> { "mon", "tue", "wed", "thu", "fri", "sat", "sun" }; List<Vector3> list = new List<Vector3>(); list.Add(recordedPathNodes[0]); foreach (Vector3 recordedPathNode in recordedPathNodes) { if (Vector3.Distance(recordedPathNode, list[list.Count - 1]) > 24f) { list.Add(recordedPathNode); } } serializedFootPatrol.waypoints = new List<Vector3>(list); FootPatrolGenerator.serPatrols.loadedPatrols.Add(serializedFootPatrol); DebugModule.Log("Finished building: " + currentPathName, "BuildEnd"); DebugModule.Log($"Recorded path nodes: {recordedPathNodes.Count}\n Reload the game to apply changes.", "BuildEnd"); ConfigLoader.Save(FootPatrolGenerator.serPatrols); recordedPathNodes.Clear(); } } public class VehiclePatrolTarget : ConsoleCommandBase { public static List<Vector3> recordedPathNodes = new List<Vector3>(); public static string currentPathName; public override string Name => "vehiclepatrol"; public override CommandSupport SupportedMethods => CommandSupport.List | CommandSupport.Spawn | CommandSupport.Visualize | CommandSupport.Build; public override void List() { string text = ""; int num = 0; text += "\nIndex: Name"; foreach (VehiclePatrolInstance key in VehiclePatrolGenerator.generatedVehiclePatrolInstances.Keys) { text += $"\n{num}: {((Object)key.Route).name}"; num++; } text += "\n-------"; DebugModule.Log(text, "List"); } public override void Spawn(int index) { List<VehiclePatrolInstance> list = VehiclePatrolGenerator.generatedVehiclePatrolInstances.Keys.ToList(); VehiclePatrolInstance instance; int originalStart; if (index < list.Count) { instance = list[index]; if ((Object)(object)instance.activeOfficer != (Object)null) { DebugModule.Log("Vehicle patrol is already active", "Spawn"); return; } originalStart = instance.StartTime; instance.StartTime = NetworkSingleton<TimeManager>.Instance.CurrentTime; instance.StartPatrol(); DebugModule.Log("Vehicle Patrol " + ((Object)instance.Route).name + " Spawned", "Spawn"); NACops.coros.Add(MelonCoroutines.Start(EndSoon())); } IEnumerator EndSoon() { yield return (object)new WaitForSeconds(240f); instance.StartTime = originalStart; } } public override void Visualize(int index) { ConsoleCommandBase.CleanVisual(); List<VehiclePatrolInstance> list = VehiclePatrolGenerator.generatedVehiclePatrolInstances.Keys.ToList(); if (index >= 0 && index < list.Count) { VehiclePatrolRoute route = list[index].Route; Vector3[] points = route.Waypoints.Select((Transform waypoint) => waypoint.position + Vector3.up * 8f).ToArray(); ConsoleCommandBase.DrawPath(((Object)route).name, points); DebugModule.Log("Veicle Patrol " + ((Object)route).name + " Visualized", "Visualize"); } } public override void Build(string arg) { if (arg.ToLower() == "start") { BuildStart(); } else if (isBuilding) { BuildEnd(); } } public void BuildStart() { if (isBuilding) { DebugModule.Log("Already building a path or a sentry!\n Use: nacops build " + Name + " stop\n to stop building", "BuildStart"); return; } isBuilding = true; currentPathName = $"{Name}_{Guid.NewGuid()}"; DebugModule.Log("Started building path with name " + currentPathName + "\nWalk on the road to create new path nodes!", "BuildStart"); NACops.coros.Add(MelonCoroutines.Start(FollowPlayer())); } public IEnumerator FollowPlayer() { Transform tr = Player.Local.CenterPointTransform; GameObject val = new GameObject("Path"); DebugModule.pathVisualizer.Add(val); recordedPathNodes.Add(Player.Local.CenterPointTransform.position); LineRenderer lineRenderer = val.AddComponent<LineRenderer>(); ((Renderer)lineRenderer).material = DebugModule.lineRenderMat; lineRenderer.widthMultiplier = 0.5f; lineRenderer.startColor = Color.blue; lineRenderer.endColor = Color.red; lineRenderer.positionCount = recordedPathNodes.Count; lineRenderer.SetPositions(recordedPathNodes.ToArray()); while (NACops.registered && isBuilding) { yield return NACops.Wait1; if (Vector3.Distance(tr.position, recordedPathNodes[recordedPathNodes.Count - 1]) > 6f) { BuildNode(lineRenderer); } } yield return null; } public void BuildNode(LineRenderer lineRenderer) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) recordedPathNodes.Add(Player.Local.CenterPointTransform.position); lineRenderer.positionCount = recordedPathNodes.Count; lineRenderer.SetPositions(recordedPathNodes.ToArray()); } public void BuildEnd() { //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: 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_0118: Unknown result type (might be due to invalid IL or missing references) isBuilding = false; ConsoleCommandBase.CleanVisual(); if (recordedPathNodes.Count == 0) { DebugModule.Log("No recorded nodes found.", "BuildEnd"); return; } if (recordedPathNodes.Count < 4) { DebugModule.Log("Build more path nodes to save.", "BuildEnd"); recordedPathNodes.Clear(); return; } SerializedVehiclePatrol serializedVehiclePatrol = new SerializedVehiclePatrol(); serializedVehiclePatrol.startTime = 1900; serializedVehiclePatrol.intensityRequirement = 1; serializedVehiclePatrol.onlyIfCurfew = false; serializedVehiclePatrol.name = currentPathName; serializedVehiclePatrol.days = new List<string> { "mon", "tue", "wed", "thu", "fri", "sat", "sun" }; List<Vector3> list = new List<Vector3>(); list.Add(recordedPathNodes[0]); foreach (Vector3 recordedPathNode in recordedPathNodes) { if (Vector3.Distance(recordedPathNode, list[list.Count - 1]) > 24f) { list.Add(recordedPathNode); } } serializedVehiclePatrol.waypoints = new List<Vector3>(list); VehiclePatrolGenerator.serVehiclePatrols.loadedVehiclePatrols.Add(serializedVehiclePatrol); DebugModule.Log("Finished building: " + currentPathName, "BuildEnd"); DebugModule.Log($"Recorded path nodes: {recordedPathNodes.Count}\n Reload the game to apply changes.", "BuildEnd"); ConfigLoader.Save(VehiclePatrolGenerator.serVehiclePatrols); recordedPathNodes.Clear(); } } public class SentryTarget : ConsoleCommandBase { public static List<Vector3> recordedPathNodes = new List<Vector3>(); public static string currentPathName; public override string Name => "sentry"; public override CommandSupport SupportedMethods => CommandSupport.List | CommandSupport.Spawn | CommandSupport.Visualize | CommandSupport.Build; public override void List() { string text = ""; int num = 0; text += "\nIndex: Name"; foreach (SentryInstance key in SentryGenerator.generatedSentryInstances.Keys) { text += $"\n{num}: {((Object)((Component)key._potentialLocations[0]).gameObject).name}"; num++; } text += "\n-------"; DebugModule.Log(text, "List"); } public override void Spawn(int index) { List<SentryInstance> list = SentryGenerator.generatedSentryInstances.Keys.ToList(); SentryInstance instance; int originalStart; int originalEnd; if (index < list.Count) { instance = list[index]; if (instance._potentialLocations[0].AssignedOfficers.Count > 0) { DebugModule.Log("Sentry is already active", "Spawn"); return; } originalStart = instance.StartTime; originalEnd = instance.EndTime; instance.StartTime = NetworkSingleton<TimeManager>.Instance.CurrentTime; instance.EndTime = TimeManager.AddMinutesTo24HourTime(originalStart, 240); instance.StartEntry(); DebugModule.Log("Sentry " + ((Object)((Component)instance._potentialLocations[0]).gameObject).name + " Spawned", "Spawn"); NACops.coros.Add(MelonCoroutines.Start(EndSoon())); } IEnumerator EndSoon() { yield return (object)new WaitForSeconds(240f); instance.EndSentry(); instance.StartTime = originalStart; instance.EndTime = originalEnd; } } public override void Visualize(int index) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005b: 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_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: 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) ConsoleCommandBase.CleanVisual(); List<SentryInstance> list = SentryGenerator.generatedSentryInstances.Keys.ToList(); if (index >= 0 && index < list.Count) { SentryInstance val = list[index]; for (int i = 0; i < val._potentialLocations[0].Routes.Count; i++) { Vector3 position = val._potentialLocations[0].Routes[0].RoutePoints[i].position; Vector3[] points = (Vector3[])(object)new Vector3[2] { position, position + Vector3.up * 8f }; ConsoleCommandBase.DrawPath($"{((Object)((Component)val._potentialLocations[0]).gameObject).name}_{i}", points); } DebugModule.Log("Sentry " + ((Object)((Component)val._potentialLocations[0]).gameObject).name + " Visualized", "Visualize"); } } public override void Build(string arg) { if (arg.ToLower() == "start") { BuildStart(); } else if (isBuilding) { NACops.coros.Add(MelonCoroutines.Start(BuildEnd())); } } public void BuildStart() { if (isBuilding) { DebugModule.Log("Already building a path or sentry!\n Use: nacops build " + Name + " stop\n to stop building", "BuildStart"); return; } isBuilding = true; currentPathName = $"{Name}_{Guid.NewGuid()}"; DebugModule.Log(currentPathName + ": Set 1st Sentry Point\n Walk to 2nd sentry point and type:\nnacops build " + Name + " stop", "BuildStart"); MakeVertBeam(); } public void MakeVertBeam() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown //IL_0030: 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_0063: 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_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) Transform centerPointTransform = Player.Local.CenterPointTransform; GameObject val = new GameObject("Path"); DebugModule.pathVisualizer.Add(val); recordedPathNodes.Add(Player.Local.CenterPointTransform.position); LineRenderer val2 = val.AddComponent<LineRenderer>(); ((Renderer)val2).material = DebugModule.lineRenderMat; val2.widthMultiplier = 0.5f; val2.startColor = Color.blue; val2.endColor = Color.red; val2.positionCount = 2; Vector3[] positions = (Vector3[])(object)new Vector3[2] { centerPointTransform.position, centerPointTransform.position + Vector3.up * 5f }; val2.SetPositions(positions); } public IEnumerator BuildEnd() { recordedPathNodes.Add(Player.Local.CenterPointTransform.position); isBuilding = false; if (recordedPathNodes.Count == 0) { DebugModule.Log("No recorded nodes found.", "BuildEnd"); yield break; } if (recordedPathNodes.Count != 2) { DebugModule.Log("Build more sentry nodes to save.", "BuildEnd"); recordedPathNodes.Clear(); yield break; } SerializedSentry serializedSentry = new SerializedSentry(); serializedSentry.startTime = 1900; serializedSentry.endTime = 500; serializedSentry.members = 1; serializedSentry.intensityRequirement = 1; serializedSentry.onlyIfCurfew = false; serializedSentry.name = currentPathName; serializedSentry.days = new List<string> { "mon", "tue", "wed", "thu", "fri", "sat", "sun" }; serializedSentry.standPosition1 = recordedPathNodes[0]; serializedSentry.standPosition2 = recordedPathNodes[1]; SentryGenerator.serSentries.loadedSentrys.Add(serializedSentry); DebugModule.Log("Finished building: " + currentPathName, "BuildEnd"); DebugModule.Log($"Recorded path nodes: {recordedPathNodes.Count}\n Reload the game to apply changes.", "BuildEnd"); ConfigLoader.Save(SentryGenerator.serSentries); recordedPathNodes.Clear(); yield return NACops.Wait5; ConsoleCommandBase.CleanVisual(); } } public class RaidTarget : ConsoleCommandBase { public override string Name => "raid"; public override CommandSupport SupportedMethods => CommandSupport.List | CommandSupport.Spawn; public override void List() { lock (NACops.heatConfigLock) { List<PropertyHeat> list = new List<PropertyHeat>(NACops.heatConfig); string text = ""; int num = 0; text += "\nIndex: Name"; foreach (PropertyHeat item in list) { text += $"\n{num}: {item.propertyCode}\n DaysSinceRaid: {item.daysSinceLastRaid}\n Heat: {item.propertyHeat}"; num++; } text += "\n-------"; DebugModule.Log(text, "List"); } } public override void Spawn(int index) { if (index < 0 || index >= NACops.heatConfig.Count) { return; } Property val = null; foreach (Property property in Property.Properties) { if (property.PropertyCode == NACops.heatConfig[index].propertyCode) { val = property; } } if (Object.op_Implicit((Object)(object)val)) { if ((Object)(object)val.NPCSpawnPoint == (Object)null) { DebugModule.Log("No valid destination for property: " + val.propertyName, "Spawn"); } else if (val is Business) { DebugModule.Log("Cant start raid on a business", "Spawn"); } else { NACops.coros.Add(MelonCoroutines.Start(RaidPropertyEvent.BeginRaidEvent(val))); } } } public override void Visualize(int index) { DebugModule.Log("Not supported", "Visualize"); } } public class InvestigatorTarget : ConsoleCommandBase { public override string Name => "investigator"; public override CommandSupport SupportedMethods => CommandSupport.SpawnNoIndex; public override void List() { DebugModule.Log("Not supported", "List"); } public override void Spawn(int index) { if (PrivateInvestigator.investigatorActive) { DebugModule.Log("Investigator is already active!", "Spawn"); return; } DebugModule.Log("Spawning Private Investigator", "Spawn"); NACops.coros.Add(MelonCoroutines.Start(PrivateInvestigator.HandlePIMonitor())); } public override void Visualize(int index) { DebugModule.Log("Not supported", "Visualize"); } } public class CopAnalyticsTarget : ConsoleCommandBase { public static TextMeshProUGUI AnalyticsTextPanel; public override string Name => "analytics"; public override CommandSupport SupportedMethods => CommandSupport.Visualize; public override void Visualize(int index) { if ((Object)(object)AnalyticsTextPanel == (Object)null) { MelonCoroutines.Start(MakeUI()); } else if ((Object)(object)AnalyticsTextPanel != (Object)null && ((Behaviour)AnalyticsTextPanel).enabled) { DebugModule.Log("Disabling Analytics text", "Visualize"); ((Behaviour)AnalyticsTextPanel).enabled = false; } else if ((Object)(object)AnalyticsTextPanel != (Object)null) { DebugModule.Log("Enabling Analytics text", "Visualize"); ((Behaviour)AnalyticsTextPanel).enabled = true; } } public IEnumerator MakeUI() { AnalyticsTextPanel = new GameObject("CurrentLawIntensity").AddComponent<TextMeshProUGUI>(); SetupAnalyticsUI(AnalyticsTextPanel); DebugModule.Log("Finished instantiating UI", "MakeUI"); NACops.coros.Add(MelonCoroutines.Start(UpdateUI())); yield break; } public IEnumerator UpdateUI() { SetAnalyticsString(); while (true) { yield return NACops.Wait30; if (!NACops.registered) { break; } if (((Behaviour)AnalyticsTextPanel).enabled) { SetAnalyticsString(); } } } public void SetAnalyticsString() { string text = ""; text += $"LAW INTENSITY: {Singleton<LawController>.Instance.internalLawIntensity}\n"; text += $"IN POOL: {PoliceStation.PoliceStations[0].OfficerPool.Count}\n"; int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; int num5 = 0; foreach (PoliceOfficer officer in PoliceOfficer.Officers) { if (!((NPC)officer).isInBuilding) { num++; } if ((Object)(object)((NPC)officer).Behaviour.activeBehaviour != (Object)null) { if ((Object)(object)((NPC)officer).Behaviour.activeBehaviour == (Object)(object)officer.CheckpointBehaviour) { num2++; } if ((Object)(object)((NPC)officer).Behaviour.activeBehaviour == (Object)(object)officer.FootPatrolBehaviour) { num3++; } if ((Object)(object)((NPC)officer).Behaviour.activeBehaviour == (Object)(object)officer.SentryBehaviour) { num4++; } if ((Object)(object)((NPC)officer).Behaviour.activeBehaviour == (Object)(object)officer.VehiclePatrolBehaviour) { num5++; } } } text += $"ACTIVE: {num}/{PoliceOfficer.Officers.Count}\n"; int num6 = 0; int num7 = 0; int num8 = 0; int num9 = 0; int num10 = 0; int num11 = 0; int num12 = 0; int num13 = 0; int num14 = 0; int num15 = 0; int num16 = 0; LawActivitySettings settings = Singleton<LawController>.Instance.GetSettings(); int currentTime = NetworkSingleton<TimeManager>.Instance.CurrentTime; List<string> list = new List<string>(); CheckpointInstance[] checkpoints = settings.Checkpoints; foreach (CheckpointInstance val in checkpoints) { if (TimeManager.IsGivenTimeWithinRange(currentTime, val.StartTime, val.EndTime)) { num7 += val.MinMembers; num8 += val.MaxMembers; num6++; } } list.Add($"Checkpoints: {num6} | static members: {num7}-{num8} | actual performing: {num2}\n"); PatrolInstance[] patrols = settings.Patrols; foreach (PatrolInstance val2 in patrols) { if (TimeManager.IsGivenTimeWithinRange(currentTime, val2.StartTime, val2.EndTime)) { num10 += val2.MinMembers; num11 += val2.MaxMembers; num9++; } } list.Add($"FootPatrols: {num9} | static members: {num10}-{num11} | actual performing: {num3}\n"); SentryInstance[] sentries = settings.Sentries; foreach (SentryInstance val3 in sentries) { if (TimeManager.IsGivenTimeWithinRange(currentTime, val3.StartTime, val3.EndTime)) { num13 += val3.MinMembers; num14 += val3.MaxMembers; num12++; } } list.Add($"Sentries: {num12} | static members: {num13}-{num14} | actual performing: {num4}\n"); VehiclePatrolInstance[] vehiclePatrols = settings.VehiclePatrols; foreach (VehiclePatrolInstance val4 in vehiclePatrols) { if (TimeManager.IsGivenTimeWithinRange(currentTime, val4.StartTime, TimeManager.AddMinutesTo24HourTime(val4.latestStartTime, 60))) { num16++; num15++; } } list.Add($"VehiclePatrols: {num15} | static members: {num16} | actual performing: {num5}\n"); int num17 = num7 + num10 + num13 + num16; int num18 = num8 + num11 + num14 + num16; int value = Mathf.Abs(PoliceOfficer.Officers.Count - num17); string value2 = ((PoliceOfficer.Officers.Count > num17) ? $"Surplus {value}" : $"Missing {value}"); text += $"OFFICERS REQUIRED NOW: {num17}-{num18} | {value2}\n"; int value3 = num2 + num3 + num4 + num5; int value4 = Mathf.RoundToInt((float)(num17 + num18) / 2f); int value5 = num6 + num9 + num12 + num15; text += $"ACTIVITIES TOTAL: {value5} | STATIC MEDIAN: {value4} | BEHACTIVE: {value3}\n"; foreach (string item in list) { text += item; } ((TMP_Text)AnalyticsTextPanel).text = text; } public void SetupAnalyticsUI(TextMeshProUGUI comp) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0066: 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) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) ((TMP_Text)comp).transform.SetParent(((Component)Singleton<HUD>.Instance.canvas).transform, false); ((TMP_Text)comp).alignment = (TextAlignmentOptions)257; ((TMP_Text)comp).fontSize = 16f; ((Graphic)comp).color = Color.red; ((TMP_Text)comp).rectTransform.anchorMin = new Vector2(0f, 1f); ((TMP_Text)comp).rectTransform.anchorMax = new Vector2(0f, 1f); ((TMP_Text)comp).rectTransform.pivot = new Vector2(0f, 1f); ((TMP_Text)comp).rectTransform.anchoredPosition = new Vector2(40f, -40f); ((TMP_Text)comp).rectTransform.sizeDelta = new Vector2(600f, 500f); } } public class SurveillanceTarget : ConsoleCommandBase { public static bool hasDrawnVisuals; public override string Name => "surveillance"; public override CommandSupport SupportedMethods => CommandSupport.SpawnNoIndex | CommandSupport.Visualize; public override void Spawn(int index) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) DebugModule.Log("Enabling nearest Flock instance", "Spawn"); Vector3 position = Player.Local.CenterPointTransform.position; HylandFlockInstance hylandFlockInstance = null; float num = 100f; foreach (HylandFlockInstance allCamera in MassSurveillance.allCameras) { if (!allCamera.activeToday) { float num2 = Vector3.Distance(position, ((Component)allCamera).transform.position); if (num2 < num) { num = num2; hylandFlockInstance = allCamera; } } } hylandFlockInstance.ActivateInstance(); MassSurveillance.activeCameras.Add(hylandFlockInstance); DebugModule.Log("Enabled", "Spawn"); } public override void Visualize(int index) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0035: 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_003d: Unknown result type (might be due to invalid IL or missing references) //IL_003e: 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_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) ConsoleCommandBase.CleanVisual(); if (hasDrawnVisuals) { hasDrawnVisuals = false; return; } for (int i = 0; i < MassSurveillance.activeCameras.Count; i++) { Vector3 position = ((Component)MassSurveillance.activeCameras[i]).transform.position; Vector3[] points = (Vector3[])(object)new Vector3[2] { position, position + Vector3.up * 40f }; ConsoleCommandBase.DrawPath($"ActiveFlock_{i}", points); } hasDrawnVisuals = true; DebugModule.Log("Active cameras visualized", "Visualize"); } } public static bool isBuilding = false; public static bool isLoggingEnabled = false; public static readonly HashSet<string> ConsoleMethodNames = new HashSet<string> { "Help", "List", "Spawn", "Visualize", "BuildStart", "BuildEnd", "RunCommand" }; } public static class DebugModule { public static Material lineRenderMat; public static List<GameObject> pathVisualizer = new List<GameObject>(); public static Material cameraBeamMat; public static Dictionary<string, ConsoleModule.ConsoleCommandBase> consoleTargets = new Dictionary<string, ConsoleModule.ConsoleCommandBase> { { "footpatrol", new ConsoleModule.FootPatrolTarget() }, { "vehiclepatrol", new ConsoleModule.VehiclePatrolTarget() }, { "sentry", new ConsoleModule.SentryTarget() }, { "raid", new ConsoleModule.RaidTarget() }, { "investigator", new ConsoleModule.InvestigatorTarget() }, { "surveillance", new ConsoleModule.SurveillanceTarget() }, { "analytics", new ConsoleModule.CopAnalyticsTarget() } }; public static void Log(string msg, [CallerMemberName] string memberName = "") { if (ConsoleModule.isLoggingEnabled || ConsoleModule.ConsoleMethodNames.Contains(memberName)) { MelonLogger.Msg("[" + memberName + "] " + msg); } } public static void RunCommand(List<string> args) { if (args.Count == 2 && args[1].ToLower() == "help") { Help(); return; } if (args.Count == 3 && args[1].ToLower() == "enable" && args[2].ToLower() == "logs") { ConsoleModule.isLoggingEnabled = true; return; } if (args.Count < 3) { Log("Usage: nacops (action) (target) (index or argument)\n Try: nacops help", "RunCommand"); return; } string text = args[1].ToLower(); string text2 = args[2].ToLower(); int num = ((args.Count > 3 && int.TryParse(args[3], out num)) ? num : (-1)); bool flag = false; if (num == -1 && args.Count > 3 && (args[3].ToLower() == "start" || args[3].ToLower() == "stop")) { flag = true; } if (!consoleTargets.TryGetValue(text2, out var value)) { Log("Unknown command target '" + text2 + "'", "RunCommand"); return; } ConsoleModule.CommandSupport commandSupport = text switch { "list" => ConsoleModule.CommandSupport.List, "spawn" => ConsoleModule.CommandSupport.Spawn | ConsoleModule.CommandSupport.SpawnNoIndex, "visualize" => ConsoleModule.CommandSupport.Visualize, "build" => ConsoleModule.CommandSupport.Build, _ => ConsoleModule.CommandSupport.None, }; if ((value.SupportedMethods & commandSupport) == 0) { Log($"Command target '{text2}' does not support requested method '{commandSupport}'", "RunCommand"); return; } if (commandSupport == ConsoleModule.CommandSupport.Build && !flag) { Log("Command requested method 'build " + text2 + "' only supports arguments 'start' and 'stop'", "RunCommand"); return; } switch (commandSupport) { case ConsoleModule.CommandSupport.List: value.List(); break; case ConsoleModule.CommandSupport.Spawn | ConsoleModule.CommandSupport.SpawnNoIndex: value.Spawn(num); break; case ConsoleModule.CommandSupport.Visualize: value.Visualize(num); break; case ConsoleModule.CommandSupport.Build: value.Build(args[3]); break; } } public static void Help() { string text = ""; text += "\nSupported Commands:"; text += "\n\n# ENABLE FULL LOGGING"; text += "\nnacops enable logs"; foreach (ConsoleModule.ConsoleCommandBase value in consoleTargets.Values) { text = text + "\n\n# " + value.Name.ToUpper(); if (value.SupportedMethods.HasFlag(ConsoleModule.CommandSupport.List)) { text = text + "\nnacops list " + value.Name; } if (value.SupportedMethods.HasFlag(ConsoleModule.CommandSupport.Spawn)) { text = text + "\nnacops spawn " + value.Name + " (index)"; } if (value.SupportedMethods.HasFlag(ConsoleModule.CommandSupport.SpawnNoIndex)) { text = text + "\nnacops spawn " + value.Name; } if (value.SupportedMethods.HasFlag(ConsoleModule.CommandSupport.Visualize)) { text = ((!(value is ConsoleModule.CopAnalyticsTarget) && !(value is ConsoleModule.SurveillanceTarget)) ? (text + "\nnacops visualize " + value.Name + " (index)") : (text + "\nnacops visualize " + value.Name)); } if (value.SupportedMethods.HasFlag(ConsoleModule.CommandSupport.Build)) { text = text + "\nnacops build " + value.Name + " start"; text = text + "\nnacops build " + value.Name + " stop"; } } Log(text, "Help"); } } [HarmonyPatch(typeof(Console), "SubmitCommand", new Type[] { typeof(List<string>) })] public static class Console_SubmitCommand_ListString_Patch { public static bool Prefix(Console __instance, List<string> args) { if (args.Count == 0) { return true; } if (args[0].ToLower() == "nacops") { DebugModule.RunCommand(args); return true; } return true; } } [HarmonyPatch(typeof(Console), "SubmitCommand", new Type[] { typeof(string) })] public static class Console_SubmitCommand_String_Patch { public static bool Prefix(Console __instance, string args) { return true; } } [HarmonyPatch(typeof(Player), "ConsumeProduct")] public static class Player_ConsumeProduct_Patch { public static bool evaluating; public static bool Prefix(Player __instance, ProductItemInstance product) { DebugModule.Log("ConsumePrefix", "Prefix"); if (!evaluating && NACops.currentDrugApprehender.Count < 1) { evaluating = true; DebugModule.Log("CorosBegin", "Prefix"); NACops.coros.Add(MelonCoroutines.Start(DrugConsumedCoro(__instance, product))); } return true; } public static IEnumerator DrugConsumedCoro(Player player, ProductItemInstance product) { if (!NACops.currentConfig.WeedInvestigator) { yield break; } bool num = product is WeedInstance; bool flag = product is MethInstance; bool flag2 = product is CocaineInstance; bool flag3 = product is ShroomInstance; bool flag4 = num || flag || flag2 || flag3; DebugModule.Log("Is Supported Instance for Apprehender: " + flag4, "DrugConsumedCoro"); if (flag4) { DebugModule.Log("Instance casted, check officers count: " + NACops.allActiveOfficers.Count, "DrugConsumedCoro"); PoliceOfficer noticeOfficer = null; float smallestDistance = 49f; bool direct = false; foreach (PoliceOfficer offc in NACops.allActiveOfficers) { yield return NACops.Wait01; if (!BaseUtility.GUIDInUse.Contains(((NPC)offc).BakedGUID) && !NACops.currentDrugApprehender.Contains(offc) && !(Vector3.Distance(((Component)offc).transform.position, ((Component)player).transform.position) > 50f) && !((NPC)offc).Health.IsDead && !((NPC)offc).Health.IsKnockedOut) { if (((NPC)offc).Awareness.VisionCone.IsPlayerVisible(player) && ((NPC)offc).Movement.CanMove() && !((NPC)offc).IsInVehicle && !((NPC)offc).isInBuilding) { offc.BeginFootPursuit(player.PlayerCode); NACops.coros.Add(MelonCoroutines.Start(BaseUtility.GiveFalseCharges(3, player))); direct = true; DebugModule.Log("Apprehend immediate direct", "DrugConsumedCoro"); break; } float num2 = Vector3.Distance(((Component)offc).transform.position, ((Component)player).transform.position); if (num2 < smallestDistance && !((NPC)offc).IsInVehicle && !((NPC)offc).isInBuilding) { smallestDistance = num2; noticeOfficer = offc; } } } if ((Object)(object)noticeOfficer == (Object)null || direct) { DebugModule.Log("No apprehender candidate found", "DrugConsumedCoro"); evaluating = false; yield break; } NACops.currentDrugApprehender.Add(noticeOfficer); DebugModule.Log("Proceed apprehender candidate", "DrugConsumedCoro"); NACops.coros.Add(MelonCoroutines.Start(ApprehenderOfficerClear(noticeOfficer))); bool apprehending = false; ((NPC)noticeOfficer).Movement.FacePoint(((Component)player).transform.position, 0.4f); yield return NACops.Wait05; if (((NPC)noticeOfficer).Awareness.VisionCone.IsPlayerVisible(player)) { DebugModule.Log("Apprehend immediate candidate", "DrugConsumedCoro"); noticeOfficer.BeginBodySearch(player.PlayerCode); NACops.coros.Add(MelonCoroutines.Start(BaseUtility.GiveFalseCharges(3, player))); apprehending = true; } if ((Object)(object)noticeOfficer != (Object)null && !apprehending) { for (int i = 0; i <= 6; i++) { DebugModule.Log("Apprehend Search suspect", "DrugConsumedCoro"); if (!NACops.registered) { yi