Decompiled source of K9Patrol Mono v1.1.0

K9_Patrol_Mono.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
using FishNet;
using FishNet.Managing;
using FishNet.Object;
using HarmonyLib;
using K9_Patrol.Source;
using K9_Patrol.Source.Controllers;
using K9_Patrol.Source.Managers;
using K9_Patrol.Source.NPCs;
using K9_Patrol.Source.Networking;
using K9_Patrol.Source.Utils;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using ScheduleOne;
using ScheduleOne.Combat;
using ScheduleOne.Core.Items.Framework;
using ScheduleOne.DevUtilities;
using ScheduleOne.ItemFramework;
using ScheduleOne.Law;
using ScheduleOne.Map;
using ScheduleOne.NPCs;
using ScheduleOne.NPCs.Behaviour;
using ScheduleOne.NPCs.Responses;
using ScheduleOne.PlayerScripts;
using ScheduleOne.Police;
using ScheduleOne.Product;
using ScheduleOne.Product.Packaging;
using ScheduleOne.Storage;
using ScheduleOne.Vision;
using SteamNetworkLib;
using SteamNetworkLib.Core;
using SteamNetworkLib.Events;
using SteamNetworkLib.Models;
using SteamNetworkLib.Utilities;
using Steamworks;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Events;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: MelonInfo(typeof(Core), "K9 Patrol", "1.1.0", "DropDaDeuce", null)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: AssemblyCompany("K9_Patrol_Mono")]
[assembly: AssemblyConfiguration("MONO")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0+0086fe93cf972ddc8e38e9eb58156e1642441df3")]
[assembly: AssemblyProduct("K9_Patrol_Mono")]
[assembly: AssemblyTitle("K9_Patrol_Mono")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.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 K9_Patrol
{
	internal static class BuildVersion
	{
		internal const string Value = "1.1.0";
	}
}
namespace K9_Patrol.Source
{
	public sealed class Core : MelonMod
	{
		public static string cfgDir;

		internal static AssetBundle bundle;

		private static bool _pumpFaulted;

		public override void OnInitializeMelon()
		{
			K9Config.Init();
			LoadAssets();
			K9DogHit.Install(((MelonBase)this).HarmonyInstance);
			K9Audio.Preload();
			try
			{
				K9NetSync.InitializeIfNeeded();
			}
			catch (Exception ex)
			{
				Logger.Error("Could not initialise K9 networking: " + ex.Message + ". Single-player is unaffected.");
			}
			Logger.Msg("K9 Patrol mod initialized.");
		}

		public override void OnUpdate()
		{
			try
			{
				K9NetSync.Pump();
				K9NetSync.PostPump();
			}
			catch (Exception arg)
			{
				if (!_pumpFaulted)
				{
					_pumpFaulted = true;
					Logger.Error($"K9 pump threw and has been reported once: {arg}");
				}
			}
		}

		public override void OnDeinitializeMelon()
		{
			try
			{
				K9SteamLink.Shutdown();
			}
			catch
			{
			}
		}

		public override void OnSceneWasInitialized(int buildIndex, string sceneName)
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			Logger.Debug($"Scene initialized: {sceneName} (Build Index: {buildIndex})");
			try
			{
				K9NetSync.ServerClearAll();
				K9NetSync.ClientClearAll();
				K9ContrabandRegistry.Clear();
				K9ContrabandPublisher.Reset();
				K9ScentTable.Rearm();
			}
			catch (Exception ex)
			{
				Logger.Debug("K9 scene reset: " + ex.Message);
			}
			if ((Object)(object)Object.FindObjectOfType<K9Manager>() == (Object)null)
			{
				GameObject val = new GameObject("K9UnitManager");
				Object.DontDestroyOnLoad((Object)(object)val);
				val.AddComponent<K9Manager>();
				Logger.Debug("K9Manager created and added to the scene.");
			}
		}

		private static void LoadAssets()
		{
			try
			{
				bundle = AssetBundleUtils.LoadAssetBundle("K9_Patrol.Assets.k9model.bundle");
				if ((Object)(object)bundle != (Object)null)
				{
					Logger.Msg("AssetBundle loaded successfully. Contents:");
					string[] allAssetNames = bundle.GetAllAssetNames();
					if (allAssetNames != null && allAssetNames.Length != 0)
					{
						string[] array = allAssetNames;
						foreach (string text in array)
						{
							Logger.Msg("  - " + text);
						}
					}
					else
					{
						Logger.Warning("Bundle appears to be empty.");
					}
				}
				else
				{
					Logger.Error("Failed to load AssetBundle - returned null");
				}
			}
			catch (Exception arg)
			{
				Logger.Error($"Failed to load AssetBundle: {arg}");
			}
		}
	}
	internal static class K9Config
	{
		internal static MelonPreferences_Category Cat;

		internal static MelonPreferences_Entry<int> UnitCount;

		internal static MelonPreferences_Entry<float> SniffRadius;

		internal static MelonPreferences_Entry<float> ScentFullRadiusScore;

		internal static MelonPreferences_Entry<float> ScentMaxRadiusMultiplier;

		internal static MelonPreferences_Entry<float> RecheckInterval;

		internal static MelonPreferences_Entry<float> BodySearchStartDistance;

		internal static MelonPreferences_Entry<float> SearchCooldown;

		internal static MelonPreferences_Entry<float> PursuitSpeedMultiplier;

		internal static MelonPreferences_Entry<float> DogBaseSpeed;

		internal static MelonPreferences_Entry<float> DogAcceleration;

		internal static MelonPreferences_Entry<float> DogAngularSpeed;

		internal static MelonPreferences_Entry<float> CatchupStartDistance;

		internal static MelonPreferences_Entry<float> CatchupMaxMultiplier;

		internal static MelonPreferences_Entry<float> HardWarpDistance;

		internal static MelonPreferences_Entry<float> HardWarpVerticalDelta;

		internal static MelonPreferences_Entry<float> WarpCooldown;

		internal static MelonPreferences_Entry<float> DogAnimationSpeedScale;

		internal static MelonPreferences_Entry<bool> AmbientBarkEnabled;

		internal static MelonPreferences_Entry<float> AmbientBarkRadius;

		internal static MelonPreferences_Entry<float> AmbientBarkMinInterval;

		internal static MelonPreferences_Entry<float> AmbientBarkMaxInterval;

		internal static MelonPreferences_Entry<bool> DebugLogging;

		private const string HostNote = "MULTIPLAYER: the host decides this one for everybody. Your copy is only used when you are hosting.";

		private const string YoursNote = "MULTIPLAYER: this one is always yours, host or not.";

		public static int MaxUnitCount => UnitCount.Value;

		internal static void Init()
		{
			string text = Path.Combine(MelonEnvironment.UserDataDirectory, "DropDaDeuce-K9Patrol");
			string text2 = Path.Combine(text, "K9Patrol.cfg");
			Directory.CreateDirectory(text);
			Cat = MelonPreferences.CreateCategory("K9Unit");
			Cat.SetFilePath(text2, File.Exists(text2), true);
			UnitCount = Cat.CreateEntry<int>("UnitCount", 2, "Maximum K9 Units", "How many officer-and-dog pairs are out on patrol at once. They are put onto the game's own police foot-patrol routes, so more of them means you run into one more often, not that they hunt you harder. MULTIPLAYER: the host decides this one for everybody. Your copy is only used when you are hosting.", false, false, (ValueValidator)null, (string)null);
			SniffRadius = Cat.CreateEntry<float>("SniffRadius", 10f, "Sniff Radius (m)", "How far a dog can smell contraband, in metres, as a starting point rather than a fixed distance. What you are carrying stretches or shrinks it: see Scent Score For Full Sniff Radius below, and K9Scents.cfg for the per-drug and per-packaging numbers. A wall, floor or door between you and the dog cuts whatever is left to about a third. In multiplayer the host's K9Scents.cfg is used for everybody, the same as this setting - and that file is also where the backpack number lives, under [containers]. MULTIPLAYER: the host decides this one for everybody. Your copy is only used when you are hosting.", false, false, (ValueValidator)null, (string)null);
			ScentFullRadiusScore = Cat.CreateEntry<float>("ScentFullRadiusScore", 20f, "Scent Score For Full Sniff Radius", "How much contraband counts as \"a normal amount\". Carry that much and the dog smells you at the full Sniff Radius; carry less and it has to get closer; carry more and it reaches further, up to the cap below. The scores come from K9Scents.cfg, where one unit of any loose drug is worth 1. At 20, a full stack of loose product is smelled at the full radius, ten of it at about half that, and it takes thirty to reach the cap. LOWER THIS NUMBER to make the dogs sharper; raise it to let yourself carry more before you are noticed. MULTIPLAYER: the host decides this one for everybody. Your copy is only used when you are hosting.", false, false, (ValueValidator)null, (string)null);
			ScentMaxRadiusMultiplier = Cat.CreateEntry<float>("ScentMaxRadiusMultiplier", 1.5f, "Maximum Sniff Radius Multiplier", "A hard limit on how far a huge stash can push the sniff radius, written as a multiple of Sniff Radius. At 1.5 with the default 10 m radius, no amount of contraband will ever let a dog smell you from beyond 15 m. MULTIPLAYER: the host decides this one for everybody. Your copy is only used when you are hosting.", false, false, (ValueValidator)null, (string)null);
			RecheckInterval = Cat.CreateEntry<float>("RecheckInterval", 0.2f, "Detection Poll Interval (s)", "How often, in seconds, each dog checks whether anyone nearby is carrying. Lower makes them react faster and costs a little more performance; there is no reason to go below 0.1 or above about 1. MULTIPLAYER: the host decides this one for everybody. Your copy is only used when you are hosting.", false, false, (ValueValidator)null, (string)null);
			BodySearchStartDistance = Cat.CreateEntry<float>("BodySearchStartDistance", 1.25f, "Body Search Start Distance (m)", "Metres. How close the officer has to actually get before the body search begins. That is the game's own search, with all the consequences that normally come with it. Raise it and you get searched from further away; lower it and the officer has to walk right up to you. MULTIPLAYER: the host decides this one for everybody. Your copy is only used when you are hosting.", false, false, (ValueValidator)null, (string)null);
			SearchCooldown = Cat.CreateEntry<float>("SearchCooldown", 30f, "Search Cooldown (s)", "Seconds of peace after a search before any K9 will start on the same person again. It is shared between all the dogs, so being cleared by one officer does not hand you straight to the next. MULTIPLAYER: the host decides this one for everybody. Your copy is only used when you are hosting.", false, false, (ValueValidator)null, (string)null);
			PursuitSpeedMultiplier = Cat.CreateEntry<float>("PursuitSpeedMultiplier", 1.25f, "Officer Pursuit Speed Multiplier", "How much faster the officer moves while chasing you, compared to their normal patrol speed. 1 is no faster at all; 1.25 is a quarter faster. The boost is given back the moment the chase ends. MULTIPLAYER: the host decides this one for everybody. Your copy is only used when you are hosting.", false, false, (ValueValidator)null, (string)null);
			DogBaseSpeed = Cat.CreateEntry<float>("DogBaseSpeed", 3f, "Dog Base Speed", "The dog's ordinary speed in metres per second, while it is trotting along beside its handler. It goes faster than this only when it has fallen behind. See the two catch-up settings below. MULTIPLAYER: the host decides this one for everybody. Your copy is only used when you are hosting.", false, false, (ValueValidator)null, (string)null);
			DogAcceleration = Cat.CreateEntry<float>("DogAcceleration", 14f, "Dog Acceleration", "How quickly the dog gets up to speed and slows down again. High numbers look eager and a bit twitchy; low numbers make it drift into a stop like it is on ice. MULTIPLAYER: the host decides this one for everybody. Your copy is only used when you are hosting.", false, false, (ValueValidator)null, (string)null);
			DogAngularSpeed = Cat.CreateEntry<float>("DogAngularSpeed", 720f, "Dog Angular Speed", "How fast the dog can swing round WHILE IT IS MOVING, in degrees per second, so how tightly it corners. 720 is two full turns a second, brisk enough that it never looks like it is steering a bus. How fast it spins on the spot to face someone is fixed, and is not this setting. MULTIPLAYER: the host decides this one for everybody. Your copy is only used when you are hosting.", false, false, (ValueValidator)null, (string)null);
			CatchupStartDistance = Cat.CreateEntry<float>("CatchupStartDistance", 6f, "Catch-up Start Distance (m)", "Metres. How far behind its handler the dog is allowed to drift before it breaks into a run to catch up. MULTIPLAYER: the host decides this one for everybody. Your copy is only used when you are hosting.", false, false, (ValueValidator)null, (string)null);
			CatchupMaxMultiplier = Cat.CreateEntry<float>("CatchupMaxMultiplier", 2.2f, "Catch-up Max Speed Multiplier", "The fastest the dog is allowed to go while catching up, as a multiple of Dog Base Speed. 2.2 means it can sprint at a little over twice its walking pace. It eases back down as it closes the gap rather than stopping dead. MULTIPLAYER: the host decides this one for everybody. Your copy is only used when you are hosting.", false, false, (ValueValidator)null, (string)null);
			HardWarpDistance = Cat.CreateEntry<float>("HardWarpDistance", 30f, "Hard Warp Distance (m)", "Metres. A dog that ends up further than this from its handler gives up walking and teleports back to them. Stairs, doorways and lifts strand dogs, and this is what rescues them. Seeing dogs blink around? Raise it. Seeing dogs stuck far behind? Lower it. MULTIPLAYER: the host decides this one for everybody. Your copy is only used when you are hosting.", false, false, (ValueValidator)null, (string)null);
			HardWarpVerticalDelta = Cat.CreateEntry<float>("HardWarpVerticalDelta", 5f, "Hard Warp Vertical Delta (m)", "Metres of height difference that also triggers the teleport above, even when the dog is not far away on the map. This is the one that fetches a dog left on the floor below when its handler goes upstairs. MULTIPLAYER: the host decides this one for everybody. Your copy is only used when you are hosting.", false, false, (ValueValidator)null, (string)null);
			WarpCooldown = Cat.CreateEntry<float>("WarpCooldown", 4f, "Warp Cooldown (s)", "The shortest gap allowed between two teleports, so a dog that cannot find its way cannot strobe in and out of existence. MULTIPLAYER: the host decides this one for everybody. Your copy is only used when you are hosting.", false, false, (ValueValidator)null, (string)null);
			DogAnimationSpeedScale = Cat.CreateEntry<float>("DogAnimationSpeedScale", 4.5f, "Dog Animation Speed", "How fast the dog's legs move, as a multiplier, when it is running at full speed. Raise it if the dog looks like it is skating; lower it if its legs look like they are spinning. It scales with how fast the dog is actually going, so changing Dog Base Speed does not require changing this too. MULTIPLAYER: this one is always yours, host or not.", false, false, (ValueValidator)null, (string)null);
			AmbientBarkEnabled = Cat.CreateEntry<bool>("AmbientBarkEnabled", true, "Dogs Bark While Idle", "true (default): a K9 woofs to itself now and then while patrolling, as long as somebody is close enough to hear it. That is just ambience. The urgent bark it makes when it has actually smelled something on you is a different sound and is not affected by this. Set false for silent dogs. MULTIPLAYER: this one is always yours, host or not.", false, false, (ValueValidator)null, (string)null);
			AmbientBarkRadius = Cat.CreateEntry<float>("AmbientBarkRadius", 15f, "Idle Bark Audible Radius (m)", "Metres. A patrolling dog only bothers woofing if somebody is within this far of it, so the map is not full of barking you cannot hear. MULTIPLAYER: this one is always yours, host or not.", false, false, (ValueValidator)null, (string)null);
			AmbientBarkMinInterval = Cat.CreateEntry<float>("AmbientBarkMinInterval", 14f, "Idle Bark Minimum Gap (s)", "Seconds. The shortest gap there can be between two woofs from the same dog. MULTIPLAYER: this one is always yours, host or not.", false, false, (ValueValidator)null, (string)null);
			AmbientBarkMaxInterval = Cat.CreateEntry<float>("AmbientBarkMaxInterval", 45f, "Idle Bark Maximum Gap (s)", "Seconds. The longest gap there can be between two woofs. Each dog rolls a new gap at random between the minimum and this one, so they do not all bark in time with each other. MULTIPLAYER: this one is always yours, host or not.", false, false, (ValueValidator)null, (string)null);
			DebugLogging = Cat.CreateEntry<bool>("DebugLogging", false, "Enable Debug Logging", "Writes a lot of extra detail to MelonLoader\\Latest.log. TURN THIS ON BEFORE REPORTING A PROBLEM and attach the log: how the dog worked out what it could smell, and how it decided where to walk, are both completely silent without it. If the problem is in a multiplayer game, turn it on for everyone in the lobby and send every log. One machine on its own almost never shows what went wrong. MULTIPLAYER: this one is always yours, host or not.", false, false, (ValueValidator)null, (string)null);
			MelonPreferences.Save();
		}
	}
	public static class Logger
	{
		internal static void Debug(string msg)
		{
			if (K9Config.DebugLogging.Value)
			{
				MelonLogger.Msg("[K9 Debug] " + msg);
			}
		}

		internal static void Warning(string msg)
		{
			MelonLogger.Warning("[K9] " + msg);
		}

		internal static void Error(string msg)
		{
			MelonLogger.Error("[K9] " + msg);
		}

		internal static void Msg(string msg)
		{
			MelonLogger.Msg("[K9] " + msg);
		}
	}
}
namespace K9_Patrol.Source.Managers
{
	public sealed class K9Manager : MonoBehaviour
	{
		private static readonly List<K9UnitController> _units = new List<K9UnitController>();

		private float _checkTimer = 0f;

		private const float CHECK_INTERVAL = 5f;

		private const float maxDistance = 100f;

		public static readonly List<Player> PlayerCache = new List<Player>();

		private float _playerCacheTimer = 0f;

		private const float PLAYER_CACHE_INTERVAL = 1f;

		private void Update()
		{
			_playerCacheTimer += Time.deltaTime;
			if (_playerCacheTimer >= 1f)
			{
				_playerCacheTimer = 0f;
				RefreshPlayerCache();
			}
			PruneUnits();
			_checkTimer += Time.deltaTime;
			if (_checkTimer >= 5f)
			{
				PeriodicOfficerCheck();
				_checkTimer = 0f;
			}
		}

		private static void RefreshPlayerCache()
		{
			PlayerCache.Clear();
			try
			{
				List<Player> playerList = Player.PlayerList;
				if (playerList != null)
				{
					foreach (Player item in playerList)
					{
						if ((Object)(object)item != (Object)null)
						{
							PlayerCache.Add(item);
						}
					}
					return;
				}
			}
			catch
			{
			}
			PlayerCache.AddRange(Object.FindObjectsOfType<Player>(true));
		}

		private static void PruneUnits()
		{
			for (int num = _units.Count - 1; num >= 0; num--)
			{
				K9UnitController k9UnitController = _units[num];
				if ((Object)(object)k9UnitController == (Object)null || (Object)(object)k9UnitController.Officer == (Object)null || (Object)(object)((NPC)k9UnitController.Officer).Health == (Object)null || ((NPC)k9UnitController.Officer).Health.IsDead)
				{
					if ((Object)(object)k9UnitController != (Object)null)
					{
						string text = (((Object)(object)k9UnitController.Officer == (Object)null) ? "its officer no longer exists" : (((Object)(object)((NPC)k9UnitController.Officer).Health == (Object)null) ? "its officer has no Health component" : "its officer is dead"));
						Logger.Debug("K9 unit pruned: " + text + ".");
						Object.Destroy((Object)(object)((Component)k9UnitController).gameObject);
					}
					_units.RemoveAt(num);
				}
			}
		}

		public static PoliceOfficer FindClosestOfficer(Vector3 pos)
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			float num = 10000f;
			PoliceOfficer result = null;
			HashSet<PoliceOfficer> hashSet = new HashSet<PoliceOfficer>(_units.Select((K9UnitController u) => u.Officer));
			foreach (PoliceOfficer officer in PoliceOfficer.Officers)
			{
				if (!((Object)(object)officer == (Object)null) && !((Object)(object)((Component)officer).transform == (Object)null) && ((Behaviour)officer).isActiveAndEnabled && !hashSet.Contains(officer) && !IsOfficerInStation(officer))
				{
					Vector3 val = pos - ((Component)officer).transform.position;
					float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
					if (sqrMagnitude < num)
					{
						num = sqrMagnitude;
						result = officer;
					}
				}
			}
			return result;
		}

		private static void PeriodicOfficerCheck()
		{
			if (!K9Net.IsServer())
			{
				return;
			}
			foreach (K9UnitController unit in _units)
			{
				if ((Object)(object)unit != (Object)null && (Object)(object)unit.Officer != (Object)null)
				{
					K9NetSync.ServerEnsureRegistered(unit.Officer);
				}
			}
			int count = _units.Count;
			if (count < K9Config.MaxUnitCount && (Object)(object)Singleton<Map>.Instance != (Object)null && Singleton<Map>.Instance.Regions != null)
			{
				int count2 = K9Config.MaxUnitCount - count;
				SpawnOfficersWithK9InRandomRegions(count2);
			}
		}

		public static bool IsOfficerAssigned(PoliceOfficer officer)
		{
			if ((Object)(object)officer == (Object)null)
			{
				return true;
			}
			foreach (K9UnitController unit in _units)
			{
				if ((Object)(object)unit != (Object)null && (Object)(object)unit.Officer == (Object)(object)officer)
				{
					return true;
				}
			}
			return false;
		}

		public static bool IsOfficerInStation(PoliceOfficer officer)
		{
			if ((Object)(object)officer == (Object)null)
			{
				return false;
			}
			foreach (PoliceStation policeStation in PoliceStation.PoliceStations)
			{
				if ((Object)(object)policeStation != (Object)null && policeStation.OfficerPool.Contains(officer))
				{
					return true;
				}
			}
			return false;
		}

		private static void SpawnOfficersWithK9InRandomRegions(int count)
		{
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Expected O, but got Unknown
			//IL_0227: Unknown result type (might be due to invalid IL or missing references)
			Logger.Debug($"Spawning up to {count} K9 officer(s)...");
			int num = 0;
			int num2 = 0;
			List<PoliceStation> policeStations = PoliceStation.PoliceStations;
			if (policeStations == null || policeStations.Count == 0)
			{
				Logger.Warning("No police stations found - cannot spawn K9 officers.");
				return;
			}
			FootPatrolRoute[] array = Object.FindObjectsOfType<FootPatrolRoute>();
			List<FootPatrolRoute> list = new List<FootPatrolRoute>();
			if (array != null)
			{
				FootPatrolRoute[] array2 = array;
				foreach (FootPatrolRoute val in array2)
				{
					if ((Object)(object)val != (Object)null && val.Waypoints != null && val.StartWaypointIndex >= 0 && val.StartWaypointIndex < val.Waypoints.Length)
					{
						list.Add(val);
					}
				}
			}
			while (num < count && num2 < count * 3)
			{
				num2++;
				int index = Random.Range(0, policeStations.Count);
				PoliceStation val2 = policeStations[index];
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				Vector3 position = ((Component)val2).transform.position;
				try
				{
					FootPatrolRoute val3 = null;
					if (list.Count > 0)
					{
						val3 = list[Random.Range(0, list.Count)];
					}
					PoliceOfficer val4 = null;
					if ((Object)(object)val3 != (Object)null)
					{
						PatrolGroup val5 = Singleton<LawManager>.Instance.StartFootpatrol(val3, 1);
						if (val5?.Members != null)
						{
							foreach (NPC member in val5.Members)
							{
								PoliceOfficer val6 = K9Cast.AsOfficer(member);
								if ((Object)(object)val6 != (Object)null && !IsOfficerAssigned(val6))
								{
									val4 = val6;
									break;
								}
							}
						}
					}
					if (val4 == null)
					{
						val4 = FindClosestOfficer(position);
					}
					if ((Object)(object)val4 == (Object)null)
					{
						Logger.Warning("Failed to acquire officer for K9 unit - retrying.");
						continue;
					}
					GameObject val7 = new GameObject($"K9Unit_{((Object)val4).GetInstanceID()}");
					val7.transform.position = ((Component)val4).transform.position;
					K9UnitController k9UnitController = val7.AddComponent<K9UnitController>();
					k9UnitController.Initialize(val4);
					_units.Add(k9UnitController);
					K9NetSync.ServerSpawn(val4);
					num++;
					Logger.Debug($"Spawned K9 unit for officer {((Object)val4).GetInstanceID()} at station {((Object)val2).name}");
				}
				catch (Exception arg)
				{
					Logger.Error($"SpawnOfficersWithK9InRandomRegions exception: {arg}");
				}
			}
			if (num < count)
			{
				Logger.Warning($"Only {num} of {count} K9 officer(s) were spawned.");
			}
		}
	}
}
namespace K9_Patrol.Source.NPCs
{
	public class K9NPC : MonoBehaviour
	{
		public enum K9State
		{
			Idle,
			Following,
			Investigating
		}

		internal static readonly List<K9NPC> Live = new List<K9NPC>();

		private NavMeshAgent _agent;

		private Animator _anim;

		public PoliceOfficer _officer;

		public Player _pursuitTarget;

		private K9State _state;

		private float _lastWarpTime;

		private float _desiredSpeed;

		private const string SpeedParamName = "speed";

		private const string IsWalkingParamName = "isWalking";

		private const string IsTrackingParamName = "isTracking";

		private const string BarkParamName = "bark";

		private const string SitParamName = "sit";

		private const string WagParamName = "wag";

		private const string AlertParamName = "alert";

		private const string PlayRateParamName = "playRate";

		private const string OverlayLayerName = "Overlay";

		private static readonly int SpeedHash = Animator.StringToHash("speed");

		private static readonly int IsWalkingHash = Animator.StringToHash("isWalking");

		private static readonly int IsTrackingHash = Animator.StringToHash("isTracking");

		private static readonly int BarkHash = Animator.StringToHash("bark");

		private static readonly int SitHash = Animator.StringToHash("sit");

		private static readonly int WagHash = Animator.StringToHash("wag");

		private static readonly int AlertHash = Animator.StringToHash("alert");

		private static readonly int PlayRateHash = Animator.StringToHash("playRate");

		private bool _animParamValidated;

		private bool _speedParamExists;

		private bool _isWalkingParamExists;

		private bool _isTrackingParamExists;

		private bool _barkParamExists;

		private bool _sitParamExists;

		private bool _wagParamExists;

		private bool _alertParamExists;

		private bool _playRateParamExists;

		private int _overlayLayer = -1;

		private float _overlayWeight;

		private const float ANIM_FULL_RUN_AT = 1.2f;

		private const float MIN_PLAY_RATE = 0.6f;

		private const float OVERLAY_BARK_HOLD = 1.2f;

		private const float OVERLAY_FADE = 6f;

		private float _overlayHoldUntil = -999f;

		private float _lastLoggedMotion;

		private const float SIT_AFTER_IDLE = 3f;

		private const float WAG_MIN_GAP = 11f;

		private const float WAG_MAX_GAP = 26f;

		private const float ALERT_ENTER_DISTANCE = 3.5f;

		private const float ALERT_EXIT_DISTANCE = 4.5f;

		private bool _closedIn;

		private float _idleSince = -1f;

		private float _nextWagAt;

		private float _lastBarkTime = -999f;

		private const float BARK_COOLDOWN = 3f;

		private float _lastAlertTime = -999f;

		private const float ALERT_COOLDOWN = 6f;

		private float _lastOwTime = -999f;

		private const float OW_COOLDOWN = 2f;

		private float _lastOwBroadcastAt = -999f;

		private AudioSource _audio;

		private float _nextAmbientBarkAt;

		private Vector3 _lastRequestedDestination = new Vector3(float.NaN, float.NaN, float.NaN);

		private float _nextRepathAt;

		private const float REPATH_INTERVAL = 0.2f;

		private const float REPATH_MOVE_THRESHOLD = 0.75f;

		private Vector3 _lastHandlerPos = new Vector3(float.NaN, float.NaN, float.NaN);

		private float _handlerSpeed;

		private const float HANDLER_MOVING_SPEED = 0.35f;

		private const float FOLLOW_ENTER_DISTANCE = 2.6f;

		private const float FOLLOW_EXIT_DISTANCE = 1.8f;

		private bool _offMeshReported;

		private const float STALL_TIMEOUT = 1.5f;

		private const float STALL_SPEED = 0.05f;

		private float _stalledSince = -1f;

		private static float AnimSpeedScale => Mathf.Max(0.05f, K9Config.DogAnimationSpeedScale.Value);

		private float CloseDistance => Mathf.Min(4f, K9DogTuning.CatchupStart * 0.65f);

		private bool HandlerIsMoving(Vector3 officerPos)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: 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_0043: Unknown result type (might be due to invalid IL or missing references)
			if (float.IsNaN(_lastHandlerPos.x))
			{
				_lastHandlerPos = officerPos;
				return false;
			}
			float deltaTime = Time.deltaTime;
			if (deltaTime > 0.0001f)
			{
				Vector3 val = officerPos - _lastHandlerPos;
				val.y = 0f;
				float num = ((Vector3)(ref val)).magnitude / deltaTime;
				if (num < 20f)
				{
					_handlerSpeed = Mathf.Lerp(_handlerSpeed, num, 1f - Mathf.Exp(-6f * deltaTime));
				}
			}
			_lastHandlerPos = officerPos;
			return _handlerSpeed > 0.35f;
		}

		public void Initialize(PoliceOfficer officerGO, NavMeshAgent agent, Animator animator)
		{
			_officer = officerGO;
			_agent = agent;
			_anim = animator;
			if ((Object)(object)_agent != (Object)null)
			{
				_agent.stoppingDistance = 1.25f;
				_agent.autoBraking = true;
				_agent.updateRotation = true;
			}
			_desiredSpeed = K9DogTuning.BaseSpeed;
			ChangeState(K9State.Idle);
			EnsureAudioSource();
			ValidateAnimatorParameter();
			ScheduleNextAmbientBark();
			K9DogHit.Attach(((Component)this).gameObject);
			if (!Live.Contains(this))
			{
				Live.Add(this);
			}
		}

		private void OnDestroy()
		{
			Live.Remove(this);
		}

		private void ValidateAnimatorParameter()
		{
			_animParamValidated = true;
			if ((Object)(object)_anim == (Object)null)
			{
				Logger.Warning("K9NPC: Animator missing.");
				return;
			}
			if ((Object)(object)_anim.runtimeAnimatorController == (Object)null)
			{
				Logger.Warning("K9NPC: Animator has no runtimeAnimatorController assigned.");
				return;
			}
			for (int i = 0; i < _anim.layerCount; i++)
			{
				if (_anim.GetLayerName(i) == "Overlay")
				{
					_overlayLayer = i;
					break;
				}
			}
			if (_overlayLayer < 0)
			{
				Logger.Warning("K9NPC: no 'Overlay' animator layer. Sniff and growl will not play, and the bundle is older than this build.");
			}
			else
			{
				_anim.SetLayerWeight(_overlayLayer, 0f);
			}
			AnimatorControllerParameter[] parameters = _anim.parameters;
			AnimatorControllerParameter[] array = parameters;
			foreach (AnimatorControllerParameter val in array)
			{
				if (val != null)
				{
					if (val.name == "speed")
					{
						_speedParamExists = true;
					}
					if (val.name == "isWalking")
					{
						_isWalkingParamExists = true;
					}
					if (val.name == "isTracking")
					{
						_isTrackingParamExists = true;
					}
					if (val.name == "bark")
					{
						_barkParamExists = true;
					}
					if (val.name == "sit")
					{
						_sitParamExists = true;
					}
					if (val.name == "wag")
					{
						_wagParamExists = true;
					}
					if (val.name == "alert")
					{
						_alertParamExists = true;
					}
					if (val.name == "playRate")
					{
						_playRateParamExists = true;
					}
				}
			}
			if (!_speedParamExists)
			{
				Logger.Warning("K9NPC: Animator parameter 'speed' not found.");
			}
			if (!_isWalkingParamExists)
			{
				Logger.Warning("K9NPC: Animator parameter 'isWalking' not found.");
			}
			if (!_isTrackingParamExists)
			{
				Logger.Warning("K9NPC: Animator parameter 'isTracking' not found.");
			}
			if (!_barkParamExists)
			{
				Logger.Warning("K9NPC: Animator parameter 'bark' not found.");
			}
			if (!_sitParamExists || !_wagParamExists || !_alertParamExists)
			{
				Logger.Warning($"K9NPC: Animator is missing the b22 parameters (sit={_sitParamExists} " + $"wag={_wagParamExists} alert={_alertParamExists}). The bundle is older " + "than this build - rebuild k9model.bundle from the Unity project.");
			}
			if (!_playRateParamExists)
			{
				Logger.Warning("K9NPC: Animator parameter 'playRate' not found. The bundle is older than this build, and the dog will appear to SLIDE - its legs run at the clip's own pace while its body moves at the agent's. Rebuild k9model.bundle from the Unity project.");
			}
		}

		public void TriggerBark()
		{
			Bark("K9_Patrol.Assets.Woof.mp3", priority: false);
		}

		public void TriggerAlert()
		{
			Bark("K9_Patrol.Assets.TheyHaveDrugs.mp3", priority: true);
		}

		private void EnsureAudioSource()
		{
			if ((Object)(object)_audio != (Object)null)
			{
				return;
			}
			try
			{
				AudioSource component = ((Component)this).gameObject.GetComponent<AudioSource>();
				_audio = (((Object)(object)component != (Object)null) ? component : ((Component)this).gameObject.AddComponent<AudioSource>());
				if ((Object)(object)_audio == (Object)null)
				{
					Logger.Warning("K9NPC: could not attach an AudioSource to this dog; falling back to positional one-shots.");
				}
				else
				{
					K9Audio.Configure3D(_audio);
				}
			}
			catch (Exception ex)
			{
				Logger.Warning("K9NPC: could not set up an AudioSource (" + ex.Message + "); falling back to positional one-shots.");
				_audio = null;
			}
		}

		private void Bark(string resource, bool priority)
		{
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			float time = Time.time;
			if (priority)
			{
				if (time - _lastAlertTime < 6f)
				{
					return;
				}
			}
			else if (time - _lastBarkTime < 3f)
			{
				return;
			}
			if (!priority && (Object)(object)_audio != (Object)null && _audio.isPlaying)
			{
				return;
			}
			EnsureAudioSource();
			bool flag = K9Audio.PlayOn(_audio, resource);
			if (!flag)
			{
				flag = K9Audio.PlayAt(resource, ((Component)this).transform.position);
			}
			try
			{
				if (!_animParamValidated)
				{
					ValidateAnimatorParameter();
				}
				_overlayHoldUntil = Time.time + 1.2f;
				if ((Object)(object)_anim != (Object)null && _barkParamExists)
				{
					_anim.SetTrigger(BarkHash);
				}
			}
			catch (Exception ex)
			{
				Logger.Debug("K9NPC.Bark animator failed: " + ex.Message);
			}
			if (flag)
			{
				if (priority)
				{
					_lastAlertTime = time;
				}
				else
				{
					_lastBarkTime = time;
				}
			}
			if (K9Config.DebugLogging.Value)
			{
				Logger.Debug($"K9NPC.Bark '{resource}' priority={priority} haveSource={(Object)(object)_audio != (Object)null} " + $"sourcePlaying={(Object)(object)_audio != (Object)null && _audio.isPlaying} played={flag}");
			}
		}

		internal void OnHit(Impact impact)
		{
			if (impact != null)
			{
				PlayOw();
				float time = Time.time;
				if (time - _lastOwBroadcastAt >= 2f)
				{
					_lastOwBroadcastAt = time;
					K9NetSync.BroadcastDogYelp(_officer);
				}
				K9DogHit.ForwardToOfficer(_officer, impact);
			}
		}

		internal void PlayRemoteYelp()
		{
			PlayOw();
		}

		private void PlayOw()
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			float time = Time.time;
			if (!(time - _lastOwTime < 2f))
			{
				EnsureAudioSource();
				bool flag = K9Audio.PlayOn(_audio, "K9_Patrol.Assets.Ow.mp3");
				if (!flag)
				{
					flag = K9Audio.PlayAt("K9_Patrol.Assets.Ow.mp3", ((Component)this).transform.position);
				}
				if (flag)
				{
					_lastOwTime = time;
				}
				if (K9Config.DebugLogging.Value)
				{
					Logger.Debug($"K9NPC.PlayOw haveSource={(Object)(object)_audio != (Object)null} played={flag}");
				}
			}
		}

		private void ScheduleNextAmbientBark()
		{
			float num = Mathf.Max(1f, K9Config.AmbientBarkMinInterval.Value);
			float num2 = Mathf.Max(num, K9Config.AmbientBarkMaxInterval.Value);
			_nextAmbientBarkAt = Time.time + Random.Range(num, num2);
		}

		private void TryAmbientBark()
		{
			//IL_0061: 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_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			if (!K9Config.AmbientBarkEnabled.Value || (Object)(object)_pursuitTarget != (Object)null)
			{
				return;
			}
			float time = Time.time;
			if (time < _nextAmbientBarkAt)
			{
				return;
			}
			float value = K9Config.AmbientBarkRadius.Value;
			float num = value * value;
			bool flag = false;
			Vector3 position = ((Component)this).transform.position;
			foreach (Player item in K9Manager.PlayerCache)
			{
				if (!((Object)(object)item == (Object)null) && !((Object)(object)((Component)item).transform == (Object)null))
				{
					Vector3 val = position - ((Component)item).transform.position;
					if (((Vector3)(ref val)).sqrMagnitude <= num)
					{
						flag = true;
						break;
					}
				}
			}
			if (!flag)
			{
				_nextAmbientBarkAt = time + 2f;
				return;
			}
			ScheduleNextAmbientBark();
			TriggerBark();
		}

		private void RequestPath(Vector3 destination)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: 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_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_agent == (Object)null || !_agent.isOnNavMesh)
			{
				return;
			}
			if (_agent.hasPath && Time.time < _nextRepathAt)
			{
				Vector3 val = destination - _lastRequestedDestination;
				if (((Vector3)(ref val)).sqrMagnitude < 0.5625f)
				{
					return;
				}
			}
			_nextRepathAt = Time.time + 0.2f;
			_lastRequestedDestination = destination;
			_agent.SetDestination(destination);
		}

		private void ChangeState(K9State next)
		{
			if (_state != next)
			{
				_state = next;
				if (_state == K9State.Idle)
				{
					_idleSince = Time.time;
					ScheduleNextWag();
				}
				else
				{
					_idleSince = -1f;
				}
				Logger.Debug($"K9NPC state changed to {_state}");
			}
		}

		private void ScheduleNextWag()
		{
			_nextWagAt = Time.time + Random.Range(11f, 26f);
		}

		private void Update()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: 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)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_officer == (Object)null || (Object)(object)_agent == (Object)null)
			{
				return;
			}
			Vector3 position = ((Component)_officer).transform.position;
			float num = Vector3.Distance(((Component)this).transform.position, position);
			float verticalDelta = Mathf.Abs(((Component)this).transform.position.y - position.y);
			bool flag = HandlerIsMoving(position);
			if ((Object)(object)_pursuitTarget != (Object)null)
			{
				ChangeState(K9State.Investigating);
			}
			else
			{
				switch (_state)
				{
				case K9State.Idle:
					if (num > 2.6f || flag)
					{
						ChangeState(K9State.Following);
					}
					break;
				case K9State.Following:
					if (num < 1.8f && !flag)
					{
						ChangeState(K9State.Idle);
					}
					break;
				case K9State.Investigating:
					ChangeState(K9State.Idle);
					break;
				}
			}
			switch (_state)
			{
			case K9State.Investigating:
				HandleInvestigating();
				break;
			case K9State.Following:
				HandleFollowing(num, position);
				break;
			default:
				HandleIdle(position);
				break;
			}
			UpdateAnimation();
			TryAmbientBark();
			TryHardWarp(num, verticalDelta);
		}

		private void HandleIdle(Vector3 officerPos)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			if (_agent.hasPath)
			{
				_agent.ResetPath();
			}
			_desiredSpeed = Mathf.MoveTowards(_desiredSpeed, 0f, Time.deltaTime * K9DogTuning.Acceleration);
			_agent.speed = _desiredSpeed;
			Vector3 val = officerPos - ((Component)this).transform.position;
			val.y = 0f;
			if (((Vector3)(ref val)).sqrMagnitude > 0.0005f)
			{
				Quaternion val2 = Quaternion.LookRotation(val);
				((Component)this).transform.rotation = Quaternion.Slerp(((Component)this).transform.rotation, val2, Time.deltaTime * 4f);
			}
		}

		private void HandleFollowing(float dist, Vector3 officerPos)
		{
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			float baseSpeed = K9DogTuning.BaseSpeed;
			float catchupMax = K9DogTuning.CatchupMax;
			float num;
			if (dist < CloseDistance)
			{
				num = Mathf.Max(baseSpeed * 0.5f, _handlerSpeed);
			}
			else if (dist < K9DogTuning.CatchupStart)
			{
				num = baseSpeed;
			}
			else
			{
				float num2 = Mathf.Max(0.5f, K9DogTuning.HardWarpDistance - K9DogTuning.CatchupStart);
				float num3 = Mathf.Clamp01((dist - K9DogTuning.CatchupStart) / num2);
				num = baseSpeed * Mathf.Lerp(1f, catchupMax, num3);
			}
			_desiredSpeed = Mathf.MoveTowards(_desiredSpeed, num, Time.deltaTime * K9DogTuning.Acceleration);
			_agent.speed = Mathf.Max(0.01f, _desiredSpeed);
			RequestPath(officerPos);
		}

		private void HandleInvestigating()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: 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_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: 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)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_pursuitTarget == (Object)null)
			{
				ChangeState(K9State.Idle);
				return;
			}
			Vector3 position = ((Component)_pursuitTarget).transform.position;
			float num = Vector3.Distance(((Component)this).transform.position, position);
			float baseSpeed = K9DogTuning.BaseSpeed;
			_desiredSpeed = Mathf.MoveTowards(_desiredSpeed, baseSpeed, Time.deltaTime * K9DogTuning.Acceleration);
			_agent.speed = _desiredSpeed;
			RequestPath(position);
			if (num < _agent.stoppingDistance)
			{
				if (_agent.hasPath)
				{
					_agent.ResetPath();
				}
				Vector3 val = position - ((Component)this).transform.position;
				val.y = 0f;
				if (((Vector3)(ref val)).sqrMagnitude > 0.0005f)
				{
					Quaternion val2 = Quaternion.LookRotation(val);
					((Component)this).transform.rotation = Quaternion.Slerp(((Component)this).transform.rotation, val2, Time.deltaTime * 4f);
				}
			}
		}

		private bool IsStalled()
		{
			//IL_0038: 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_0196: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_agent == (Object)null)
			{
				return false;
			}
			bool flag = _state != K9State.Idle && _desiredSpeed > 0.05f;
			Vector3 velocity = _agent.velocity;
			bool flag2 = ((Vector3)(ref velocity)).sqrMagnitude > 0.0025000002f;
			bool flag3 = !_agent.pathPending && _agent.remainingDistance <= _agent.stoppingDistance + 0.15f;
			if (!flag || flag2 || flag3)
			{
				if (_stalledSince >= 0f)
				{
					_stalledSince = -1f;
					if (_offMeshReported)
					{
						_offMeshReported = false;
						Logger.Debug("K9 dog is moving again.");
					}
				}
				return false;
			}
			if (_stalledSince < 0f)
			{
				_stalledSince = Time.time;
				return false;
			}
			if (Time.time - _stalledSince < 1.5f)
			{
				return false;
			}
			if (!_offMeshReported)
			{
				_offMeshReported = true;
				Logger.Warning($"K9 dog is STUCK - it wants to move at {_desiredSpeed:0.0} m/s and is not moving at all. " + $"onNavMesh={_agent.isOnNavMesh} hasPath={_agent.hasPath} pathPending={_agent.pathPending} " + $"pathStatus={_agent.pathStatus} state={_state}. Warping it back to its handler. " + "onNavMesh=False means nothing ever set a destination; a Partial or Invalid pathStatus means the handler is somewhere the dog cannot walk to.");
			}
			return true;
		}

		private void TryHardWarp(float dist, float verticalDelta)
		{
			//IL_0068: 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_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			float hardWarpDistance = K9DogTuning.HardWarpDistance;
			bool flag = IsStalled();
			if (!flag && dist < hardWarpDistance && verticalDelta < K9DogTuning.HardWarpVertical)
			{
				return;
			}
			float time = Time.time;
			if (!(time - _lastWarpTime < K9DogTuning.WarpCooldown))
			{
				_lastWarpTime = time;
				_stalledSince = -1f;
				Vector3 position = ((Component)_officer).transform.position;
				NavMeshHit val = default(NavMeshHit);
				if (NavMesh.SamplePosition(position, ref val, 3f, -1))
				{
					_agent.Warp(((NavMeshHit)(ref val)).position);
					Logger.Debug("K9 dog hard-warped (" + (flag ? "it was stuck" : $"{dist:0.0}m behind its handler") + ").");
				}
				else
				{
					((Component)this).transform.position = position;
					Logger.Warning("K9 dog warp fallback (no navmesh sample).");
				}
			}
		}

		private void UpdateAnimation()
		{
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0282: Unknown result type (might be due to invalid IL or missing references)
			//IL_0287: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_anim == (Object)null || (Object)(object)_agent == (Object)null)
			{
				return;
			}
			if (!_animParamValidated)
			{
				ValidateAnimatorParameter();
			}
			bool flag = _state == K9State.Following || _state == K9State.Investigating;
			if (_isWalkingParamExists)
			{
				_anim.SetBool(IsWalkingHash, flag);
			}
			bool flag2 = (Object)(object)_pursuitTarget != (Object)null;
			Vector3 val;
			if (!flag2)
			{
				_closedIn = false;
			}
			else
			{
				val = ((Component)this).transform.position - ((Component)_pursuitTarget).transform.position;
				float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
				float num = (_closedIn ? 4.5f : 3.5f);
				_closedIn = sqrMagnitude <= num * num;
			}
			bool closedIn = _closedIn;
			if (_isTrackingParamExists)
			{
				_anim.SetBool(IsTrackingHash, flag2 && !closedIn);
			}
			if (_alertParamExists)
			{
				_anim.SetBool(AlertHash, closedIn);
			}
			if (_overlayLayer >= 0)
			{
				bool flag3 = flag2 || Time.time < _overlayHoldUntil;
				_overlayWeight = Mathf.MoveTowards(_overlayWeight, flag3 ? 1f : 0f, 6f * Time.deltaTime);
				_anim.SetLayerWeight(_overlayLayer, _overlayWeight);
			}
			bool flag4 = !flag2 && _state == K9State.Idle && _idleSince >= 0f;
			if (_sitParamExists)
			{
				_anim.SetBool(SitHash, flag4 && Time.time - _idleSince >= 3f);
			}
			if (_wagParamExists && flag4 && Time.time >= _nextWagAt)
			{
				if (Time.time - _idleSince < 3f)
				{
					_anim.SetTrigger(WagHash);
				}
				ScheduleNextWag();
			}
			if (_speedParamExists)
			{
				float num2 = Mathf.Max(0.01f, K9DogTuning.BaseSpeed * 1.2f);
				val = _agent.velocity;
				float num3 = Mathf.Min(((Vector3)(ref val)).magnitude, Mathf.Max(0.01f, _agent.speed));
				float num4 = num3 / num2;
				float num5 = ((num4 > 1f) ? 1f : num4);
				try
				{
					_anim.SetFloat("speed", num5, 0.12f, Time.deltaTime);
				}
				catch
				{
					_anim.SetFloat(SpeedHash, num5);
				}
				float num6 = 1f;
				if (_playRateParamExists)
				{
					num6 = Mathf.Clamp(AnimSpeedScale * num4, 0.6f, AnimSpeedScale * 2f);
					_anim.SetFloat(PlayRateHash, num6);
				}
				if (K9Config.DebugLogging.Value && Mathf.Abs(num5 - _lastLoggedMotion) > 0.05f)
				{
					Logger.Debug($"AnimSpeed motion={num5:0.00} vel={num3:0.00} agentSpeed={_agent.speed:0.00} " + $"playRate={num6:0.00} (scale {AnimSpeedScale:0.00})");
					_lastLoggedMotion = num5;
				}
			}
		}
	}
	internal static class K9DogHit
	{
		private const string HitLayerName = "NPCTrigger";

		private static readonly Vector3 MinSize = new Vector3(0.15f, 0.15f, 0.3f);

		private static readonly Vector3 MaxSize = new Vector3(2f, 2f, 3f);

		private const float Padding = 1.1f;

		private static readonly Vector3 FallbackSize = new Vector3(0.38610002f, 0.88000005f, 1.1572001f);

		private static readonly Vector3 FallbackCentre = new Vector3(0f, 0.4f, 0.097f);

		private static bool _patched;

		private static bool _patchFailed;

		private static bool _masksChecked;

		internal static void Install(Harmony harmony)
		{
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Expected O, but got Unknown
			if (_patched || _patchFailed)
			{
				return;
			}
			try
			{
				if (harmony == null)
				{
					Logger.Warning("K9DogHit: no Harmony instance; the dog will not be hittable.");
					_patchFailed = true;
					return;
				}
				MethodInfo methodInfo = AccessTools.Method(typeof(PhysicsDamageable), "ReceiveImpact", (Type[])null, (Type[])null);
				if (methodInfo == null)
				{
					Logger.Warning("K9DogHit: PhysicsDamageable.ReceiveImpact not found - the game's combat API has moved. The dog will not be hittable.");
					_patchFailed = true;
					return;
				}
				MethodInfo methodInfo2 = AccessTools.Method(typeof(K9DogHit), "ReceiveImpactPostfix", (Type[])null, (Type[])null);
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				_patched = true;
				Logger.Msg("K9DogHit: patched PhysicsDamageable.ReceiveImpact - the dog is hittable.");
			}
			catch (Exception ex)
			{
				_patchFailed = true;
				Logger.Error("K9DogHit: could not patch PhysicsDamageable.ReceiveImpact (" + ex.Message + "). The dog will not be hittable; everything else is unaffected.");
			}
		}

		internal static void Attach(GameObject dog)
		{
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)dog == (Object)null)
			{
				return;
			}
			try
			{
				int num = LayerMask.NameToLayer("NPCTrigger");
				if (num < 0)
				{
					Logger.Warning("K9DogHit: no 'NPCTrigger' layer in this build of the game; the dog will not be hittable.");
					return;
				}
				PhysicsDamageable val = dog.GetComponent<PhysicsDamageable>();
				if ((Object)(object)val == (Object)null)
				{
					val = dog.AddComponent<PhysicsDamageable>();
				}
				if ((Object)(object)val == (Object)null)
				{
					Logger.Warning("K9DogHit: could not attach PhysicsDamageable; the dog will not be hittable.");
					return;
				}
				val.Rb = null;
				BoxCollider val2 = dog.GetComponent<BoxCollider>();
				if ((Object)(object)val2 == (Object)null)
				{
					val2 = dog.AddComponent<BoxCollider>();
				}
				if ((Object)(object)val2 == (Object)null)
				{
					Logger.Warning("K9DogHit: could not attach a BoxCollider; the dog will not be hittable.");
					return;
				}
				if (!TryMeasure(dog.transform, out var centre, out var size))
				{
					centre = FallbackCentre;
					size = FallbackSize;
					Logger.Debug("K9DogHit: could not measure the dog; using the fallback hitbox.");
				}
				val2.center = centre;
				val2.size = size;
				((Collider)val2).isTrigger = true;
				dog.layer = num;
				Logger.Debug("K9DogHit: hitbox on '" + ((Object)dog).name + "' layer=NPCTrigger " + $"centre={centre} size={size} (local units, root scale {dog.transform.localScale.x:0.00}).");
				CheckCombatMasksOnce(num);
			}
			catch (Exception ex)
			{
				Logger.Warning("K9DogHit.Attach failed (" + ex.Message + "); the dog will not be hittable.");
			}
		}

		private static void CheckCombatMasksOnce(int layer)
		{
			if (_masksChecked)
			{
				return;
			}
			_masksChecked = true;
			try
			{
				if (!NetworkSingleton<CombatManager>.InstanceExists)
				{
					_masksChecked = false;
					return;
				}
				CombatManager instance = NetworkSingleton<CombatManager>.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					_masksChecked = false;
					return;
				}
				int num = 1 << layer;
				bool flag = (((LayerMask)(ref instance.MeleeLayerMask)).value & num) != 0;
				bool flag2 = (((LayerMask)(ref instance.RangedWeaponLayerMask)).value & num) != 0;
				if (flag && flag2)
				{
					Logger.Debug(string.Format("K9DogHit: '{0}' (layer {1}) is in both combat masks - ", "NPCTrigger", layer) + "punches, melee weapons and gunfire all reach the dog.");
					return;
				}
				Logger.Warning(string.Format("K9DogHit: '{0}' (layer {1}) is NOT in ", "NPCTrigger", layer) + (flag ? "" : "MeleeLayerMask") + ((flag || flag2) ? "" : " and ") + (flag2 ? "" : "RangedWeaponLayerMask") + ". The dog will not be hittable by those, and the assumption this mod makes about the game's own NPC hit layer no longer holds.");
			}
			catch (Exception ex)
			{
				Logger.Debug("K9DogHit: could not read the combat layer masks (" + ex.Message + ").");
			}
		}

		private static bool TryMeasure(Transform root, out Vector3 centre, out Vector3 size)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_0236: Unknown result type (might be due to invalid IL or missing references)
			//IL_023b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_0256: Unknown result type (might be due to invalid IL or missing references)
			//IL_0268: Unknown result type (might be due to invalid IL or missing references)
			//IL_0279: Unknown result type (might be due to invalid IL or missing references)
			//IL_028c: Unknown result type (might be due to invalid IL or missing references)
			//IL_029f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: 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_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: 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_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: 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_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			centre = Vector3.zero;
			size = Vector3.zero;
			Renderer[] componentsInChildren = ((Component)root).GetComponentsInChildren<Renderer>(true);
			if (componentsInChildren == null || componentsInChildren.Length == 0)
			{
				return false;
			}
			bool flag = false;
			float num = 0f;
			Renderer[] array = componentsInChildren;
			Vector3 val4 = default(Vector3);
			Vector3 val5 = default(Vector3);
			foreach (Renderer val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Mesh val2 = null;
				SkinnedMeshRenderer val3 = K9Cast.AsSkinnedMesh(val);
				if ((Object)(object)val3 != (Object)null)
				{
					val2 = val3.sharedMesh;
				}
				else
				{
					MeshFilter component = ((Component)val).GetComponent<MeshFilter>();
					if ((Object)(object)component != (Object)null)
					{
						val2 = component.sharedMesh;
					}
				}
				if (!((Object)(object)val2 == (Object)null))
				{
					Bounds bounds = val2.bounds;
					Vector3 extents = ((Bounds)(ref bounds)).extents;
					((Vector3)(ref val4))..ctor(float.MaxValue, float.MaxValue, float.MaxValue);
					((Vector3)(ref val5))..ctor(float.MinValue, float.MinValue, float.MinValue);
					for (int j = 0; j < 8; j++)
					{
						Vector3 val6 = ((Bounds)(ref bounds)).center + new Vector3(((j & 1) == 0) ? (0f - extents.x) : extents.x, ((j & 2) == 0) ? (0f - extents.y) : extents.y, ((j & 4) == 0) ? (0f - extents.z) : extents.z);
						Vector3 val7 = root.InverseTransformPoint(((Component)val).transform.TransformPoint(val6));
						val4 = Vector3.Min(val4, val7);
						val5 = Vector3.Max(val5, val7);
					}
					Vector3 val8 = val5 - val4;
					float num2 = val8.x * val8.y * val8.z;
					if (!(num2 <= num))
					{
						num = num2;
						centre = (val4 + val5) * 0.5f;
						size = val8;
						flag = true;
					}
				}
			}
			if (!flag)
			{
				return false;
			}
			size *= 1.1f;
			Vector3 localScale = root.localScale;
			Vector3 val9 = default(Vector3);
			((Vector3)(ref val9))..ctor(size.x * Mathf.Abs(localScale.x), size.y * Mathf.Abs(localScale.y), size.z * Mathf.Abs(localScale.z));
			if (val9.x < MinSize.x || val9.y < MinSize.y || val9.z < MinSize.z)
			{
				return false;
			}
			if (val9.x > MaxSize.x || val9.y > MaxSize.y || val9.z > MaxSize.z)
			{
				return false;
			}
			return true;
		}

		private static void ReceiveImpactPostfix(PhysicsDamageable __instance, Impact impact)
		{
			try
			{
				if ((Object)(object)__instance == (Object)null || impact == null)
				{
					return;
				}
				GameObject gameObject = ((Component)__instance).gameObject;
				if (!((Object)(object)gameObject == (Object)null))
				{
					K9NPC component = gameObject.GetComponent<K9NPC>();
					if (!((Object)(object)component == (Object)null))
					{
						component.OnHit(impact);
					}
				}
			}
			catch (Exception ex)
			{
				Logger.Debug("K9DogHit postfix threw: " + ex.Message);
			}
		}

		internal static void ForwardToOfficer(PoliceOfficer officer, Impact impact)
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)officer == (Object)null || impact == null)
			{
				return;
			}
			try
			{
				NPCResponses responses = ((NPC)officer).Responses;
				if ((Object)(object)responses == (Object)null)
				{
					Logger.Debug("K9DogHit: the handler has no NPCResponses; the hit was not reported.");
					return;
				}
				responses.ImpactReceived(impact);
				if (K9Config.DebugLogging.Value)
				{
					Player val = default(Player);
					Logger.Debug($"K9DogHit: forwarded a {impact.ImpactType} impact " + $"(dmg={impact.ImpactDamage:0.#} force={impact.ImpactForce:0.#}) to officer " + ((Object)officer).name + "; attacker=" + ((impact.IsPlayerImpact(ref val) && (Object)(object)val != (Object)null) ? val.PlayerCode : "not a player") + ".");
				}
			}
			catch (Exception ex)
			{
				Logger.Warning("K9DogHit: forwarding the hit to the officer failed (" + ex.Message + ").");
			}
		}
	}
	internal static class K9DogFactory
	{
		private static bool _bundleMissWarned;

		private static bool _prefabMissWarned;

		internal static K9NPC Build(PoliceOfficer officer, string dogName)
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: 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_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)officer == (Object)null)
			{
				return null;
			}
			try
			{
				if ((Object)(object)Core.bundle == (Object)null)
				{
					WarnOnce(ref _bundleMissWarned, "K9DogFactory: the asset bundle is not loaded; no dog can be built.");
					return null;
				}
				GameObject val = K9Assets.LoadPrefab();
				if ((Object)(object)val == (Object)null)
				{
					WarnOnce(ref _prefabMissWarned, "K9DogFactory: the dog prefab is missing from the bundle; no dog can be built.");
					return null;
				}
				Vector3 val2 = ((Component)officer).transform.position + ((Component)officer).transform.right * 0.9f;
				Vector3 val3 = val2;
				NavMeshHit val4 = default(NavMeshHit);
				if (NavMesh.SamplePosition(val2, ref val4, 3f, -1))
				{
					val3 = ((NavMeshHit)(ref val4)).position;
				}
				else
				{
					val3.y += 0.5f;
				}
				GameObject val5 = Object.Instantiate<GameObject>(val, val3, ((Component)officer).transform.rotation);
				((Object)val5).name = dogName;
				K9UnitController.ApplyDogMaterialAndVisualTweaks(val5);
				NavMeshAgent val6 = val5.GetComponent<NavMeshAgent>();
				if ((Object)(object)val6 == (Object)null)
				{
					val6 = val5.AddComponent<NavMeshAgent>();
				}
				val6.speed = K9DogTuning.BaseSpeed;
				val6.acceleration = K9DogTuning.Acceleration;
				val6.angularSpeed = K9DogTuning.AngularSpeed;
				Animator componentInChildren = val5.GetComponentInChildren<Animator>();
				K9NPC k9NPC = val5.AddComponent<K9NPC>();
				k9NPC.Initialize(officer, val6, componentInChildren);
				return k9NPC;
			}
			catch (Exception arg)
			{
				Logger.Error($"K9DogFactory.Build failed for officer '{((Object)officer).name}': {arg}");
				return null;
			}
		}

		private static void WarnOnce(ref bool flag, string message)
		{
			if (!flag)
			{
				flag = true;
				Logger.Error(message);
			}
		}
	}
}
namespace K9_Patrol.Source.Utils
{
	internal static class K9DogTuning
	{
		private const int PAYLOAD_VERSION = 1;

		private static bool _hasHost;

		private static float _baseSpeed;

		private static float _accel;

		private static float _angular;

		private static float _catchStart;

		private static float _catchMax;

		private static float _warpDist;

		private static float _warpVert;

		private static float _warpCooldown;

		internal static bool UsingHostValues => _hasHost;

		internal static float BaseSpeed => _hasHost ? _baseSpeed : K9Config.DogBaseSpeed.Value;

		internal static float Acceleration => _hasHost ? _accel : K9Config.DogAcceleration.Value;

		internal static float AngularSpeed => _hasHost ? _angular : K9Config.DogAngularSpeed.Value;

		internal static float CatchupStart => _hasHost ? _catchStart : K9Config.CatchupStartDistance.Value;

		internal static float CatchupMax => _hasHost ? _catchMax : K9Config.CatchupMaxMultiplier.Value;

		internal static float HardWarpDistance => _hasHost ? _warpDist : K9Config.HardWarpDistance.Value;

		internal static float HardWarpVertical => _hasHost ? _warpVert : K9Config.HardWarpVerticalDelta.Value;

		internal static float WarpCooldown => _hasHost ? _warpCooldown : K9Config.WarpCooldown.Value;

		internal static string BuildHostPayload()
		{
			CultureInfo invariantCulture = CultureInfo.InvariantCulture;
			return string.Join("|", 1.ToString(invariantCulture), K9Config.DogBaseSpeed.Value.ToString("R", invariantCulture), K9Config.DogAcceleration.Value.ToString("R", invariantCulture), K9Config.DogAngularSpeed.Value.ToString("R", invariantCulture), K9Config.CatchupStartDistance.Value.ToString("R", invariantCulture), K9Config.CatchupMaxMultiplier.Value.ToString("R", invariantCulture), K9Config.HardWarpDistance.Value.ToString("R", invariantCulture), K9Config.HardWarpVerticalDelta.Value.ToString("R", invariantCulture), K9Config.WarpCooldown.Value.ToString("R", invariantCulture));
		}

		internal static void ApplyHostPayload(string payload)
		{
			if (string.IsNullOrEmpty(payload))
			{
				return;
			}
			try
			{
				string[] array = payload.Split('|');
				if (array.Length != 9)
				{
					Logger.Warning($"K9 tuning payload has {array.Length} fields, expected 9 - ignoring, staying on local config.");
					return;
				}
				CultureInfo invariantCulture = CultureInfo.InvariantCulture;
				if (!int.TryParse(array[0], NumberStyles.Integer, invariantCulture, out var result) || result != 1)
				{
					Logger.Warning($"K9 tuning payload version '{array[0]}' is not {1} - ignoring, staying on local config.");
					return;
				}
				if (!float.TryParse(array[1], NumberStyles.Float, invariantCulture, out var result2) || !float.TryParse(array[2], NumberStyles.Float, invariantCulture, out var result3) || !float.TryParse(array[3], NumberStyles.Float, invariantCulture, out var result4) || !float.TryParse(array[4], NumberStyles.Float, invariantCulture, out var result5) || !float.TryParse(array[5], NumberStyles.Float, invariantCulture, out var result6) || !float.TryParse(array[6], NumberStyles.Float, invariantCulture, out var result7) || !float.TryParse(array[7], NumberStyles.Float, invariantCulture, out var result8) || !float.TryParse(array[8], NumberStyles.Float, invariantCulture, out var result9))
				{
					Logger.Warning("K9 tuning payload did not parse - ignoring, staying on local config.");
					return;
				}
				_baseSpeed = Clamp(result2, 0.1f, 50f);
				_accel = Clamp(result3, 0.1f, 200f);
				_angular = Clamp(result4, 10f, 5000f);
				_catchStart = Clamp(result5, 0.5f, 200f);
				_catchMax = Clamp(result6, 1f, 10f);
				_warpDist = Clamp(result7, 1f, 500f);
				_warpVert = Clamp(result8, 0.5f, 200f);
				_warpCooldown = Clamp(result9, 0f, 60f);
				_hasHost = true;
				Logger.Msg($"K9 dog tuning applied from host: baseSpeed={_baseSpeed} accel={_accel} angular={_angular} " + $"catchStart={_catchStart} catchMax={_catchMax} hardWarp={_warpDist} warpVert={_warpVert} warpCd={_warpCooldown}");
			}
			catch (Exception ex)
			{
				Logger.Warning("K9 tuning payload threw while parsing (" + ex.Message + ") - staying on local config.");
			}
		}

		internal static void Clear()
		{
			if (_hasHost)
			{
				_hasHost = false;
				Logger.Debug("K9 dog tuning cleared - back to local config values.");
			}
		}

		private static float Clamp(float v, float lo, float hi)
		{
			return (v < lo) ? lo : ((v > hi) ? hi : v);
		}
	}
	public static class AssetBundleUtils
	{
		private static readonly Core mod = MelonAssembly.FindMelonInstance<Core>();

		private static readonly MelonAssembly melonAssembly = ((MelonBase)mod).MelonAssembly;

		public static AssetBundle LoadAssetBundle(string logicalName)
		{
			try
			{
				string[] manifestResourceNames = melonAssembly.Assembly.GetManifestResourceNames();
				string text = manifestResourceNames.FirstOrDefault((string n) => n.Equals(logicalName, StringComparison.OrdinalIgnoreCase));
				if (text == null)
				{
					string alt = logicalName.Replace(' ', '_');
					text = manifestResourceNames.FirstOrDefault((string n) => n.Equals(alt, StringComparison.OrdinalIgnoreCase));
				}
				if (text == null)
				{
					text = manifestResourceNames.FirstOrDefault((string n) => n.EndsWith(logicalName, StringComparison.OrdinalIgnoreCase));
				}
				if (text == null)
				{
					string fileOnly = logicalName.Split('.', StringSplitOptions.RemoveEmptyEntries).Last();
					text = manifestResourceNames.FirstOrDefault((string n) => n.EndsWith(fileOnly, StringComparison.OrdinalIgnoreCase));
				}
				if (text == null)
				{
					string text2 = string.Join("\n  - ", manifestResourceNames);
					((MelonBase)mod).Unregister("AssetBundle resource '" + logicalName + "' not found.\nAvailable embedded resources:\n  - " + text2 + "\nExpected format: <RootNamespace>.Assets.k9model.bundle (RootNamespace = " + typeof(Core).Namespace + ")", false);
					return null;
				}
				Stream manifestResourceStream = melonAssembly.Assembly.GetManifestResourceStream(text);
				if (manifestResourceStream == null)
				{
					((MelonBase)mod).Unregister("Found resource name '" + text + "' but stream was null.", false);
					return null;
				}
				return AssetBundle.LoadFromStream(manifestResourceStream);
			}
			catch (Exception arg)
			{
				((MelonBase)mod).Unregister($"Failed to load AssetBundle. Please report to dev: {arg}", false);
				return null;
			}
		}

		public static string ResolveAssetName(AssetBundle bundle, string wantedPath)
		{
			if ((Object)(object)bundle == (Object)null || string.IsNullOrEmpty(wantedPath))
			{
				return null;
			}
			try
			{
				string[] allAssetNames = bundle.GetAllAssetNames();
				if (allAssetNames == null || allAssetNames.Length == 0)
				{
					Logger.Error("ResolveAssetName: the bundle reports no assets at all.");
					return null;
				}
				string[] array = allAssetNames;
				foreach (string text in array)
				{
					if (string.Equals(text, wantedPath, StringComparison.Ordinal))
					{
						return text;
					}
				}
				string[] array2 = allAssetNames;
				foreach (string text2 in array2)
				{
					if (string.Equals(text2, wantedPath, StringComparison.OrdinalIgnoreCase))
					{
						return text2;
					}
				}
				string b = wantedPath.Substring(wantedPath.LastIndexOf('/') + 1);
				string[] array3 = allAssetNames;
				foreach (string text3 in array3)
				{
					string a = text3.Substring(text3.LastIndexOf('/') + 1);
					if (string.Equals(a, b, StringComparison.OrdinalIgnoreCase))
					{
						Logger.Warning("Asset '" + wantedPath + "' was not found at that path; matched '" + text3 + "' by file name instead. The bundle layout has changed - update the hardcoded path.");
						return text3;
					}
				}
				Logger.Error($"Asset '{wantedPath}' is not in the bundle. It holds {allAssetNames.Length} asset(s):\n  - " + string.Join("\n  - ", allAssetNames));
				return null;
			}
			catch (Exception ex)
			{
				Logger.Error("ResolveAssetName('" + wantedPath + "') threw: " + ex.Message);
				return null;
			}
		}
	}
	internal static class K9Assets
	{
		internal const string Prefab = "Assets/Bublisher/3D Stylized Animated Dogs Kit/Prefabs/K9Model.prefab";

		internal const string AlbedoTexture = "Assets/Bublisher/3D Stylized Animated Dogs Kit/Textures/3D Stylized Animated Dogs Kit - BaseColor_Custom.png";

		internal const string NormalTexture = "Assets/Bublisher/3D Stylized Animated Dogs Kit/Textures/3D Stylized Animated Dogs Kit - Normal.png";

		internal const string BarkClip = "Assets/ModContent/K9/Audio/Woof.mp3";

		internal const string AlertClip = "Assets/ModContent/K9/Audio/TheyHaveDrugs.mp3";

		internal const string OwClip = "Assets/ModContent/K9/Audio/ow.mp3";

		internal static GameObject LoadPrefab()
		{
			if ((Object)(object)Core.bundle == (Object)null)
			{
				Logger.Error("LoadPrefab: bundle is null.");
				return null;
			}
			string text = AssetBundleUtils.ResolveAssetName(Core.bundle, "Assets/Bublisher/3D Stylized Animated Dogs Kit/Prefabs/K9Model.prefab");
			return (text == null) ? null : Core.bundle.LoadAsset<GameObject>(text);
		}

		internal static Texture2D LoadTexture(string path)
		{
			if ((Object)(object)Core.bundle == (Object)null)
			{
				return null;
			}
			string text = AssetBundleUtils.ResolveAssetName(Core.bundle, path);
			return (text == null) ? null : Core.bundle.LoadAsset<Texture2D>(text);
		}

		internal static AudioClip LoadAudio(string path)
		{
			if ((Object)(object)Core.bundle == (Object)null)
			{
				return null;
			}
			try
			{
				string[] allAssetNames = Core.bundle.GetAllAssetNames();
				if (allAssetNames == null)
				{
					return null;
				}
				string b = path.Substring(path.LastIndexOf('/') + 1);
				string[] array = allAssetNames;
				foreach (string text in array)
				{
					string a = text.Substring(text.LastIndexOf('/') + 1);
					if (string.Equals(a, b, StringComparison.OrdinalIgnoreCase))
					{
						return Core.bundle.LoadAsset<AudioClip>(text);
					}
				}
			}
			catch (Exception ex)
			{
				Logger.Debug("K9Assets.LoadAudio('" + path + "') failed: " + ex.Message);
			}
			return null;
		}
	}
	internal static class K9Audio
	{
		internal const string BarkResource = "K9_Patrol.Assets.Woof.mp3";

		internal const string AlertResource = "K9_Patrol.Assets.TheyHaveDrugs.mp3";

		internal const string OwResource = "K9_Patrol.Assets.Ow.mp3";

		internal const float GainDb = 3f;

		private static readonly Dictionary<string, AudioClip> _clips = new Dictionary<string, AudioClip>();

		private static readonly HashSet<string> _loading = new HashSet<string>();

		private static readonly HashSet<string> _failed = new HashSet<string>();

		internal static float GainLinear => Mathf.Pow(10f, 0.15f);

		private static string CacheDir => Path.Combine(MelonEnvironment.UserDataDirectory, "DropDaDeuce-K9Patrol", "cache");

		internal static void Configure3D(AudioSource source)
		{
			if (!((Object)(object)source == (Object)null))
			{
				source.playOnAwake = false;
				source.loop = false;
				source.spatialBlend = 1f;
				source.rolloffMode = (AudioRolloffMode)0;
				source.minDistance = GainLinear;
				source.maxDistance = 500f;
			}
		}

		internal static void Preload()
		{
			EnsureLoaded("K9_Patrol.Assets.Woof.mp3");
			EnsureLoaded("K9_Patrol.Assets.TheyHaveDrugs.mp3");
			EnsureLoaded("K9_Patrol.Assets.Ow.mp3");
		}

		private static bool TryLoadFromBundle(string resourceName)
		{
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Invalid comparison between Unknown and I4
			string text = resourceName switch
			{
				"K9_Patrol.Assets.Ow.mp3" => "Assets/ModContent/K9/Audio/ow.mp3", 
				"K9_Patrol.Assets.TheyHaveDrugs.mp3" => "Assets/ModContent/K9/Audio/TheyHaveDrugs.mp3", 
				"K9_Patrol.Assets.Woof.mp3" => "Assets/ModContent/K9/Audio/Woof.mp3", 
				_ => null, 
			};
			if (text == null)
			{
				return false;
			}
			AudioClip val = K9Assets.LoadAudio(text);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			try
			{
				if ((int)val.loadState != 2)
				{
					val.LoadAudioData();
				}
			}
			catch (Exception ex)
			{
				Logger.Debug("K9Audio: LoadAudioData for '" + resourceName + "' threw (" + ex.Message + "); the first play may be silent.");
			}
			try
			{
				((Object)val).hideFlags = (HideFlags)(((Object)val).hideFlags | 0x20);
				Object.DontDestroyOnLoad((Object)(object)val);
			}
			catch (Exception ex2)
			{
				Logger.Debug("K9Audio: could not pin '" + resourceName + "' against asset unloading (" + ex2.Message + ").");
			}
			_clips[resourceName] = val;
			Logger.Debug($"K9Audio: loaded '{resourceName}' from the asset bundle ({val.length:0.00}s, state={val.loadState}).");
			return true;
		}

		internal static bool PlayOn(AudioSource source, string resourceName, float volume = 1f)
		{
			if ((Object)(object)source == (Object)null)
			{
				return false;
			}
			try
			{
				AudioClip clip = GetClip(resourceName);
				if ((Object)(object)clip == (Object)null)
				{
					return false;
				}
				source.Stop();
				source.clip = clip;
				source.volume = Mathf.Clamp01(volume);
				source.Play();
				return source.isPlaying;
			}
			catch (Exception ex)
			{
				Logger.Debug("K9Audio.PlayOn('" + resourceName + "') failed: " + ex.Message);
				return false;
			}
		}

		internal static bool PlayAt(string resourceName, Vector3 position, float volume = 1f)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				AudioClip clip = GetClip(resourceName);
				if ((Object)(object)clip == (Object)null)
				{
					return false;
				}
				AudioSource.PlayClipAtPoint(clip, position, Mathf.Clamp01(volume));
				return true;
			}
			catch (Exception ex)
			{
				Logger.Debug("K9Audio.PlayAt('" + resourceName + "') failed: " + ex.Message);
				return false;
			}
		}

		private static AudioClip GetClip(string resourceName)
		{
			if (_clips.TryGetValue(resourceName, out var value))
			{
				if ((Object)(object)value != (Object)null)
				{
					return value;
				}
				_clips.Remove(resourceName);
				_failed.Remove(resourceName);
				Logger.Debug("K9Audio: '" + resourceName + "' was unloaded by the engine; reloading it.");
			}
			EnsureLoaded(resourceName);
			return (_clips.TryGetValue(resourceName, out value) && (Object)(object)value != (Object)null) ? value : null;
		}

		private static void EnsureLoaded(string resourceName)
		{
			if (!_clips.ContainsKey(resourceName) && !_loading.Contains(resourceName) && !_failed.Contains(resourceName) && !TryLoadFromBundle(resourceName))
			{
				_loading.Add(resourceName);
				MelonCoroutines.Start(LoadRoutine(resourceName));
			}
		}

		private static IEnumerator LoadRoutine(string resourceName)
		{
			string path;
			try
			{
				Directory.CreateDirectory(CacheDir);
				path = Path.Combine(CacheDir, resourceName);
				Core core = MelonAssembly.FindMelonInstance<Core>();
				object obj;
				if (core == null)
				{
					obj = null;
				}
				else
				{
					MelonAssembly melonAssembly = ((MelonBase)core).MelonAssembly;
					obj = ((melonAssembly != null) ? melonAssembly.Assembly : null);
				}
				using Stream src = ((Assembly)obj)?.GetManifestResourceStream(resourceName);
				if (src == null)
				{
					Logger.Warning("K9Audio: embedded resource '" + resourceName + "' not found; that sound will be silent.");
					_failed.Add(resourceName);
					_loading.Remove(resourceName);
					yield break;
				}
				using FileStream dst = File.Create(path);
				src.CopyTo(dst);
			}
			catch (Exception ex)
			{
				Exception ex2 = ex;
				Logger.Warning("K9Audio: could not stage '" + resourceName + "' to disk (" + ex2.Message + "); that sound will be silent.");
				_failed.Add(resourceName);
				_loading.Remove(resourceName);
				yield break;
			}
			string url = new Uri(path).AbsoluteUri;
			UnityWebRequest req = UnityWebRequestMultimedia.GetAudioClip(url, (AudioType)13);
			yield return req.SendWebRequest();
			try
			{
				if ((int)req.result != 1)
				{
					Logger.Warning("K9Audio: failed to decode '" + resourceName + "' (" + req.error + "); that sound will be silent.");
					_failed.Add(resourceName);
					yield break;
				}
				AudioClip clip = DownloadHandlerAudioClip.GetContent(req);
				if ((Object)(object)clip == (Object)null)
				{
					Logger.Warning("K9Audio: '" + resourceName + "' decoded to a null clip; that sound will be silent.");
					_failed.Add(resourceName);
					yield break;
				}
				Object.DontDestroyOnLoad((Object)(object)clip);
				((Object)clip).hideFlags = (HideFlags)61;
				_clips[resourceName] = clip;
				Logger.Debug($"K9Audio: loaded '{resourceName}' ({clip.length:0.00}s)");
			}
			catch (Exception ex)
			{
				Exception ex3 = ex;
				Logger.Warning("K9Audio: error finishing '" + resourceName + "' (" + ex3.Message + "); that sound will be silent.");
				_failed.Add(resourceName);
			}
			finally
			{
				_loading.Remove(resourceName);
				try
				{
					req.Dispose();
				}
				catch
				{
				}
			}
		}
	}
	internal static class K9Net
	{
		private static NetworkManager _nm;

		private static float _nextResolveAt;

		private const float RESOLVE_INTERVAL = 1f;

		internal static NetworkManager Manager
		{
			get
			{
				if ((Object)(object)_nm != (Object)null)
				{
					return _nm;
				}
				if (Time.unscaledTime < _nextResolveAt)
				{
					return null;
				}
				_nextResolveAt = Time.unscaledTime + 1f;
				try
				{
					_nm = InstanceFinder.NetworkManager;
				}
				catch
				{
				}
				if ((Object)(object)_nm == (Object)null)
				{
					try
					{
						_nm = Object.FindObjectOfType<NetworkManager>();
					}
					catch
					{
					}
				}
				return _nm;
			}
		}

		internal static bool IsServer()
		{
			try
			{
				NetworkManager manager = Manager;
				return (Object)(object)manager == (Object)null || manager.IsServer;
			}
			catch
			{
				return true;
			}
		}
	}
	internal static class K9ScentTable
	{
		private const string FileName = "K9Scents.cfg";

		private const string SecSmell = "[smell]";

		private const string SecPackaging = "[packaging]";

		private const string SecItems = "[items]";

		private const string SecContainers = "[containers]";

		internal const string BackpackKey = "backpack";

		private static readonly Dictionary<string, float> _smell = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, float> _through = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, float> _containers = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, float> _overrides = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, string> _kind = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

		private const int FileFormat = 4;

		private const int SMELL_REKEY_FORMAT = 3;

		private static bool _formatUpgraded;

		private static bool _readFailed;

		private static bool _loaded;

		private static bool _discovered;

		private static bool _everDiscovered;

		private static float _nextRescanAt;

		private static string _path;

		private const float RESCAN_INTERVAL = 60f;

		private const int PAYLOAD_VERSION = 1;

		private const int PAYLOAD_MAX_BYTES = 8192;

		private static bool _hasHost;

		private static readonly Dictionary<string, float> _hostSmell = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, float> _hostThrough = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, float> _hostOverrides = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);

		private static float _hostDefaultProduct = 1f;

		private static float _hostDefaultOther = 0.5f;

		private static float _hostBagWeight = 1f;

		internal const string OtherKey = "Other";

		private const float DefaultSmellProduct = 1f;

		private const float DefaultSmellOther = 0.5f;

		internal const string UnpackagedKey = "(none)";

		internal static bool UsingHostValues => _hasHost;

		internal static bool HasDiscovered => _everDiscovered;

		internal static float BackpackWeight => Mathf.Clamp01(_hasHost ? _hostBagWeight : LocalBackpackWeight);

		internal static float LocalBackpackWeight
		{
			get
			{
				EnsureLoaded();
				float value;
				return _containers.TryGetValue("backpack", out value) ? Mathf.Clamp01(value) : 1f;
			}
		}

		private static string Path
		{
			get
			{
				if (string.IsNullOrEmpty(_path))
				{
					string path = System.IO.Path.Combine(MelonEnvironment.UserDataDirectory, "DropDaDeuce-K9Patrol");
					_path = System.IO.Path.Combine(path, "K9Scents.cfg");
				}
				return _path;
			}
		}

		internal static string BuildHostPayload()
		{
			if (!_everDiscovered)
			{
				return string.Empty;
			}
			EnsureLoaded();
			CultureInfo invariantCulture = CultureInfo.InvariantCulture;
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(1.ToString(invariantCulture)).Append('|').Append(LocalBackpackWeight.ToString("0.###", invariantCulture))
				.Append('|')
				.Append(1f.ToString("0.###", invariantCulture))
				.Append('|')
				.Append(0.5f.ToString("0.###", invariantCulture));
			int dropped = 0;
			AppendSection(stringBuilder, 'S', _smell, invariantCulture, ref dropped);
			AppendSection(stringBuilder, 'P', _through, invariantCulture, ref dropped);
			AppendSection(stringBuilder, 'I', _overrides, invariantCulture, ref dropped);
			if (dropped > 0)
			{
				Logger.Warning($"K9 scent payload skipped {dropped} row(s) whose name contains '|' or '~'. " + "Those rows cannot cross the wire and will read as the host's default on every client.");
			}
			string text = stringBuilder.ToString();
			if (Encoding.UTF8.GetByteCount(text) > 8192)
			{
				StringBuilder stringBuilder2 = new StringBuilder();
				stringBuilder2.Append(1.ToString(invariantCulture)).Append('|').Append(LocalBackpackWeight.ToString("0.###", invariantCulture))
					.Append('|')
					.Append(1f.ToString("0.###", invariantCulture))
					.Append('|')
					.Append(0.5f.ToString("0.###", invariantCulture));
				int dropped2 = 0;
				AppendSection(stringBuilder2, 'S', _smell, invariantCulture, ref dropped2);
				AppendSection(stringBuilder2, 'P', _through, invariantCulture, ref dropped2);
				stringBuilder2.Append("|I");
				Logger.Warning($"K9 scent payload is too large with {_overrides.Count} [items] override(s); " + "publishing without them. Every overridden item will score on its KIND row for every player in this lobby, including you.");
				text = stringBuilder2.ToString();
				if (Encoding.UTF8.GetByteCount(text) > 8192)
				{
					Logger.Warning("K9 scent table still will not fit in lobby data; publishing nothing. Every player, including you, scores from their own K9Scents.cfg this session.");
					return string.Empty;
				}
			}
			return text;
		}

		private static void AppendSection(StringBuilder sb, char tag, Dictionary<string, float> rows, CultureInfo c, ref int dropped)
		{
			sb.Append('|').Append(tag);
			foreach (KeyValuePair<string, float> item in Sorted(rows))
			{
				if (item.Key.IndexOf('|') >= 0 || item.Key.IndexOf('~') >= 0 || item.Key.IndexOf('=') >= 0)
				{
					dropped++;
				}
				else
				{
					sb.Append('~').Append(item.Key).Append('=')
						.Append(item.Value.ToString("0.###", c));
				}
			}
		}

		internal static void ApplyHostPayload(string payload)
		{
			if (string.IsNullOrEmpty(payload))
			{
				return;
			}
			try
			{
				string[] array = payload.Split('|');
				if (array.Length != 7)
				{
					Logger.Warning($"K9 scent payload has {array.Length} fields, expected 7 - ignoring, " + "staying on local values.");
					return;
				}
				CultureInfo invariantCulture = CultureInfo.InvariantCulture;
				if (!int.TryParse(array[0], NumberStyles.Integer, invariantCulture, out var result) || result != 1)
				{
					Logger.Warning($"K9 scent payload version '{array[0]}' is not {1} - ignoring, " + "staying on local values.");
					return;
				}
				if (!float.TryParse(array[1], NumberStyles.Float, invariantCulture, out var result2) || !float.TryParse(array[2], NumberStyles.Float, invariantCulture, out var result3) || !float.TryParse(array[3], NumberStyles.Float, invariantCulture, out var result4))
				{
					Logger.Warning("K9 scent payload header did not parse - ignoring, staying on local values.");
					return;
				}
				Dictionary<string, float> dictionary = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);
				Dictionary<string, float> dictionary2 = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);
				Dictionary<string, float> dictionary3 = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);
				if (!ParseSection(array[4], 'S', dictionary, invariantCulture) || !ParseSection(array[5], 'P', dictionary2, invariantCulture) || !ParseSection(array[6], 'I', dictionary3, invariantCulture))
				{
					Logger.Warning("K9 scent payload body did not parse - ignoring, staying on local values.");
					return;
				}
				_hostSmell.Clear();
				_hostThrough.Clear();
				_hostOverrides.Clear();
				foreach (KeyValuePair<string, float> item in dictionary)
				{
					_hostSmell[item.Key] = Mathf.Clamp(item.Value, 0f, 100f);
				}
				foreach (KeyValuePair<string, float> item2 in dictionary2)
				{
					_hostThrough[item2.Key] = Mathf.Clamp01(item2.Value);
				}
				foreach (KeyValuePair<string, float> item3 in dictionary3)
				{
					_hostOverrides[item3.Key] = Mathf.Clamp(item3.Value, 0f, 100f);
				}
				_hostBagWeight = Mathf.Clamp01(result2);
				_hostDefaultProduct = Mathf.Clamp(result3, 0f, 100f);
				_hostDefaultOther = Mathf.Clamp(result4, 0f, 100f);
				_hasHost = true;
				Logger.Msg($"K9 scent table applied from host: {_hostSmell.Count} kind(s), " + $"{_hostThrough.Count} packaging, {_hostOverrides.Count} override(s), " + "backpack weight " + _hostBagWeight.ToString("0.###", invariantCulture) + ". Your own K9Scents.cfg is not being used in this lobby.");
			}
			catch (Exception ex)
			{
				Logger.Warning("K9 scent payload threw while parsing (" + ex.Message + ") - staying on local values.");
			}
		}

		private static bool ParseSection(string field, char tag, Dictionary<string, float> into, CultureInfo c)
		{
			if (string.IsNullOrEmpty(field) || field[0] != tag)
			{
				return false;
			}
			if (field.Length == 1)
			{
				return true;
			}
			string[] array = field.Substring(1).Split('~');
			foreach (string text in array)
			{
				if (text.Length != 0)
				{
					int num = text.IndexOf('=');
					if (num <= 0)
					{
						return false;
					}
					string key = text.Substring(0, num);
					if (!float.TryParse(text.Substring(num + 1), NumberStyles.Float, c, out var result))
					{
						return false;
					}
					into[key] = result;
				}
			}
			return true;
		}

		internal static void ClearHostTable()
		{
			if (_hasHost)
			{
				_hasHost = false;
				_hostSmell.Clear();
				_hostThrough.Clear();
				_hostOverrides.Clear();
				_hostDefaultProduct = 1f;
				_hostDefaultOther = 0.5f;
				_hostBagWeight = 1f;
				Logger.Msg("K9 scent table cleared - back to your own K9Scents.cfg.");
			}
		}

		internal static void Rearm()
		{
			_nextRescanAt = 0f;
		}

		internal static bool Reload()
		{
			_smell.Clear();
			_through.Clear();
			_overrides.Clear();
			_containers.Clear();
			_kind.Clear();
			_loaded = false;
			_discovered = false;
			_readFailed = false;
			_nextRescanAt = 0f;
			Logger.Msg("K9 scent table dropped; K9Scents.cfg will be re-read on the next contraband scan.");
			return true;
		}

		private static string KindOf(ItemDefinition def, out bool isDrug)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			isDrug = false;
			ProductDefinition val = K9Cast.AsProductDefinition(def);
			if ((Object)(object)val != (Object)null)
			{
				EDrugType? val2 = SafeDrugType(val);
				if (val2.HasValue)
				{
					isDrug = true;
					return DrugKey(val2.Value);
				}
			}
			return SafeCategoryKey(def);
		}

		private static string DrugKey(EDrugType drug)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected I4, but got Unknown
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Expected I4, but got Unknown
			if (1 == 0)
			{
			}
			string result = (int)drug switch
			{
				0 => "Weed", 
				1 => "Meth", 
				2 => "Cocaine", 
				3 => "MDMA", 
				4 => "Shrooms", 
				5 => "Heroin", 
				_ => $"DrugType{(int)drug}", 
			};
			if (1 == 0)
			{
			}
			return result;
		}

		private static string SafeCategoryKey(ItemDefinition def)
		{
			try
			{
				string text = ((object)Unsafe.As<EItemCategory, EItemCategory>(ref ((BaseItemDefinition)def).Category)/*cast due to .constrained prefix*/).ToString();
				return string.IsNullOrEmpty(text) ? "Other" : text;
			}
			catch
			{
				return "Other";
			}
		}

		private static float DefaultThroughputFor(string packagingId, EStealthLevel level)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected I4, but got Unknown
			if (!string.IsNullOrEmpty(packagingId))
			{
				switch (packagingId.ToLowerInvariant())
				{
				case "baggie":
					return 0.6f;
				case "brick":
					return 0.6f;
				case "jar":
					return 0.4f;
				}
			}
			if (1 == 0)
			{
			}
			float result = (int)level switch
			{
				0 => 0.6f, 
				1 => 0.4f, 
				2 => 0.25f, 
				_ => 0.6f, 
			};
			if (1 == 0)
			{
			}
			return result;
		}

		internal static float SmellOf(string itemId, bool isProduct)
		{
			EnsureLoaded();
			if (_hasHost)
			{
				if (!string.IsNullOrEmpty(itemId))
				{
					if (_hostOverrides.TryGetValue(itemId, out var value))
					{
						return Mathf.Max(0f, value);
					}
					if (_kind.TryGetValue(itemId, out var value2) && _hostSmell.TryGetValue(value2, out var value3))
					{
						return Mathf.Max(0f, value3);
					}
				}
				return isProduct ? _hostDefaultProduct : _hostDefaultOther;
			}
			if (!string.IsNullOrEmpty(itemId))
			{
				if (_overrides.TryGetValue(itemId, out var value4))
				{
					return Mathf.Max(0f, value4);
				}
				if (_kind.TryGetValue(itemId, out var value5) && _smell.TryGetValue(value5, out var value6))
				{
					return Mathf.Max(0f, value6);
				}
			}
			return isProduct ? 1f : 0.5f;
		}

		internal static string KindOfId(string itemId)
		{
			EnsureLoaded();
			if (string.IsNullOrEmpty(itemId))
			{
				return null;
			}
			if (_overrides.ContainsKey(itemId))
			{
				return "[items] override";
			}
			string value;
			return _kind.TryGetValue(itemId, out value) ? value : null;
		}

		internal static float PackagingMultiplier(string packagi