Decompiled source of Shadows Of Midgard v1.0.0

ShadowsOfMidgard/plugins/ShadowsOfMidgard.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("ShadowsOfMidgard")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ShadowsOfMidgard")]
[assembly: AssemblyTitle("ShadowsOfMidgard")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 ShadowsOfMidgard
{
	[BepInPlugin("wubarrk.ShadowsOfMidgard", "Shadows of Midgard", "1.0.0")]
	public class ShadowsOfMidgard : BaseUnityPlugin
	{
		public const string ModGUID = "wubarrk.ShadowsOfMidgard";

		public const string ModName = "Shadows of Midgard";

		public const string ModVersion = "1.0.0";

		private object _harmonyInstance;

		public static ManualLogSource Log;

		private List<string> PatchSuccess;

		private List<string> PatchFail;

		private static readonly Dictionary<string, string> PatchDescriptions = new Dictionary<string, string>
		{
			{ "BaseAI_StealthBrain_Patch", "BaseAI.CanSenseTarget — stealth detection gate" },
			{ "BaseAI_StealthBrain_IsAlerted_Patch", "BaseAI.IsAlerted — unified alertness state" },
			{ "BaseAI_SetMoveDir_Patch", "Character.SetMoveDir — state-based movement control" },
			{ "Character_Damage_Patch", "Character.Damage — damage awareness boost" },
			{ "CharacterOnDamagedPatch", "Character.OnDamaged — hit reaction awareness" },
			{ "Humanoid_Attack_Patch", "Humanoid.StartAttack — combat authority gate" },
			{ "MonsterAI_StealthBrain_UpdateAI_Patch", "MonsterAI.UpdateAI — behavior execution" },
			{ "MonsterAI_StealthBrain_UpdateTarget_Patch", "MonsterAI.UpdateTarget — target acquisition" }
		};

		private void Awake()
		{
			try
			{
				Log = ((BaseUnityPlugin)this).Logger;
				Log.LogInfo((object)"Shadows of Midgard v1.0.0 awakening from the mists of Ginnungagap...");
				try
				{
					ConfigManager.Load(((BaseUnityPlugin)this).Config);
				}
				catch (Exception arg)
				{
					Log.LogWarning((object)$"ConfigManager.Load threw an exception: {arg}");
				}
				try
				{
					StealthUIRoot.Init();
					StealthUIController.Init();
					try
					{
						ConfigSync.Init();
					}
					catch (Exception arg2)
					{
						Log.LogWarning((object)$"ConfigSync.Init threw an exception: {arg2}");
					}
				}
				catch (Exception arg3)
				{
					Log.LogWarning((object)$"Stealth UI initialization threw an exception: {arg3}");
				}
				PatchSuccess = new List<string>();
				PatchFail = new List<string>();
				Type type = null;
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				foreach (Assembly assembly in assemblies)
				{
					try
					{
						type = assembly.GetType("HarmonyLib.Harmony");
						if (type != null)
						{
							break;
						}
					}
					catch
					{
					}
				}
				if (type == null)
				{
					Log.LogWarning((object)"Harmony not found in loaded assemblies; skipping runtime patching.");
				}
				else
				{
					try
					{
						ConstructorInfo constructor = type.GetConstructor(new Type[1] { typeof(string) });
						if (constructor != null)
						{
							_harmonyInstance = constructor.Invoke(new object[1] { "wubarrk.ShadowsOfMidgard" });
						}
						else
						{
							Log.LogWarning((object)"Harmony constructor(string) not found; skipping patching.");
						}
						ApplyPatchesWithSaga();
					}
					catch (Exception arg4)
					{
						Log.LogWarning((object)$"Failed to initialize Harmony via reflection: {arg4}");
					}
				}
				Log.LogInfo((object)"Shadows of Midgard loaded successfully. May your steps be silent.");
			}
			catch (Exception arg5)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)string.Format("{0} failed to initialize: {1}", "Shadows of Midgard", arg5));
			}
		}

		private void OnDestroy()
		{
			try
			{
				try
				{
					ConfigSync.Shutdown();
				}
				catch (Exception arg)
				{
					Log.LogWarning((object)$"ConfigSync.Shutdown error: {arg}");
				}
				if (_harmonyInstance != null)
				{
					MethodInfo method = _harmonyInstance.GetType().GetMethod("UnpatchSelf", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (method != null)
					{
						method.Invoke(_harmonyInstance, null);
					}
				}
			}
			catch (Exception arg2)
			{
				Log.LogWarning((object)$"Error while unpatching Harmony: {arg2}");
			}
			Log.LogInfo((object)"Shadows of Midgard unpatched and returned to the halls of Valhalla.");
		}

		private void ApplyPatchesWithSaga()
		{
			if (_harmonyInstance == null)
			{
				PrintPatchSaga();
				return;
			}
			PatchSuccess = new List<string>();
			PatchFail = new List<string>();
			try
			{
				Type type = _harmonyInstance.GetType();
				List<Type> list = (from t in typeof(ShadowsOfMidgard).Assembly.GetTypes()
					where t.Namespace != null && t.Namespace.StartsWith("ShadowsOfMidgard.Patches") && HasHarmonyAttributes(t)
					select t).ToList();
				Log.LogInfo((object)$"Discovered {list.Count} Harmony patches. Forging...");
				foreach (Type item in list)
				{
					try
					{
						ApplyPatchType(_harmonyInstance, type, item);
						PatchSuccess.Add(item.Name);
					}
					catch (Exception ex)
					{
						PatchFail.Add(item.Name);
						Log.LogWarning((object)("✗ " + item.Name + ": " + ex.Message));
						for (Exception innerException = ex.InnerException; innerException != null; innerException = innerException.InnerException)
						{
							Log.LogWarning((object)("    → " + innerException.Message));
						}
					}
				}
			}
			catch (Exception ex2)
			{
				Log.LogWarning((object)$"Error while scanning patches: {ex2}");
				for (Exception innerException2 = ex2.InnerException; innerException2 != null; innerException2 = innerException2.InnerException)
				{
					Log.LogWarning((object)("  Inner: " + innerException2.Message));
				}
			}
			PrintPatchSaga();
		}

		private void ApplyPatchType(object harmonyInstance, Type harmonyType, Type patchType)
		{
			MethodInfo? method = harmonyType.GetMethod("CreateClassProcessor", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(Type) }, null);
			if (method == null)
			{
				throw new MethodAccessException("CreateClassProcessor method not found on Harmony type");
			}
			object obj = method.Invoke(harmonyInstance, new object[1] { patchType });
			if (obj == null)
			{
				throw new InvalidOperationException("CreateClassProcessor returned null for " + patchType.Name);
			}
			MethodInfo? method2 = obj.GetType().GetMethod("Patch", BindingFlags.Instance | BindingFlags.Public, null, Type.EmptyTypes, null);
			if (method2 == null)
			{
				throw new MethodAccessException("Patch method not found on PatchClassProcessor");
			}
			method2.Invoke(obj, null);
		}

		private bool HasHarmonyAttributes(Type t)
		{
			try
			{
				foreach (CustomAttributeData customAttributesDatum in t.GetCustomAttributesData())
				{
					if (customAttributesDatum.AttributeType != null && customAttributesDatum.AttributeType.Name != null && customAttributesDatum.AttributeType.Name.IndexOf("HarmonyPatch", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						return true;
					}
				}
			}
			catch
			{
			}
			return false;
		}

		private void PrintPatchSaga()
		{
			if (ShadowsOfMidgardConfig.ShowPatchSaga != null && !ShadowsOfMidgardConfig.ShowPatchSaga.Value)
			{
				Log.LogInfo((object)$"Patch results: {PatchSuccess.Count} succeeded, {PatchFail.Count} failed");
				return;
			}
			Log.LogInfo((object)"══════════════════════════════════════════════════");
			Log.LogInfo((object)"⚔\ufe0f  Saga of the Patches — As Told by the Skalds  ⚔\ufe0f");
			Log.LogInfo((object)"══════════════════════════════════════════════════");
			foreach (string item in PatchSuccess)
			{
				string value;
				string text = (PatchDescriptions.TryGetValue(item, out value) ? value : item);
				Log.LogInfo((object)("  \ud83d\udfe2 " + text));
			}
			foreach (string item2 in PatchFail)
			{
				string value2;
				string text2 = (PatchDescriptions.TryGetValue(item2, out value2) ? value2 : item2);
				Log.LogWarning((object)("  \ud83d\udd34 " + text2));
			}
			Log.LogInfo((object)"──────────────────────────────────────────────────");
			Log.LogInfo((object)$"  Forged: {PatchSuccess.Count}  |  Shattered: {PatchFail.Count}  |  Total: {PatchSuccess.Count + PatchFail.Count}");
			if (PatchFail.Count == 0)
			{
				Log.LogInfo((object)"  All patches stand firm like the roots of Yggdrasil.");
			}
			else
			{
				Log.LogWarning((object)"  Some patches faltered. The ravens whisper of trouble...");
				Log.LogWarning((object)"  Consult the runes (the log above) to uncover the cause.");
			}
			Log.LogInfo((object)"══════════════════════════════════════════════════");
		}
	}
	public class ArmorProfile
	{
		public ArmorWeight Weight;

		public ArmorMaterial Material;

		public bool HasCamoTag;
	}
	public enum ArmorWeight
	{
		Light,
		Medium,
		Heavy
	}
	public enum ArmorMaterial
	{
		Cloth,
		Leather,
		Metal,
		Bone,
		Bark,
		Magic,
		Unknown
	}
	public static class ArmorProfileSystem
	{
		private static readonly Dictionary<string, ArmorProfile> Cache = new Dictionary<string, ArmorProfile>();

		public static ArmorProfile GetProfile(ItemData item)
		{
			if (item == null || item.m_shared == null)
			{
				return null;
			}
			string key = item.m_shared.m_name ?? "";
			if (Cache.TryGetValue(key, out var value))
			{
				return value;
			}
			ArmorProfile armorProfile = new ArmorProfile();
			DetectMaterial(item, armorProfile);
			DetectWeight(item, armorProfile);
			DetectCamo(item, armorProfile);
			Cache[key] = armorProfile;
			return armorProfile;
		}

		private static void DetectMaterial(ItemData item, ArmorProfile profile)
		{
			string text = item.m_shared.m_name.ToLowerInvariant();
			if (text.Contains("iron") || text.Contains("bronze") || text.Contains("metal"))
			{
				profile.Material = ArmorMaterial.Metal;
			}
			else if (text.Contains("leather") || text.Contains("hide"))
			{
				profile.Material = ArmorMaterial.Leather;
			}
			else
			{
				profile.Material = ArmorMaterial.Cloth;
			}
		}

		private static void DetectWeight(ItemData item, ArmorProfile profile)
		{
			float armor = item.m_shared.m_armor;
			if (armor <= 12f)
			{
				profile.Weight = ArmorWeight.Light;
			}
			else if (armor <= 28f)
			{
				profile.Weight = ArmorWeight.Medium;
			}
			else
			{
				profile.Weight = ArmorWeight.Heavy;
			}
		}

		private static void DetectCamo(ItemData item, ArmorProfile profile)
		{
			string text = item.m_shared.m_name.ToLowerInvariant();
			profile.HasCamoTag = text.Contains("forest") || text.Contains("swamp") || text.Contains("camo") || text.Contains("ghillie");
		}
	}
	public static class ArmorUtils
	{
		public static ArmorProfile GetArmorProfile(ItemData item)
		{
			return ArmorProfileSystem.GetProfile(item);
		}

		public static float GetTotalStealthPenalty(Player player)
		{
			float num = 0f;
			foreach (ItemData equippedItem in ((Humanoid)player).GetInventory().GetEquippedItems())
			{
				ArmorProfile armorProfile = GetArmorProfile(equippedItem);
				if (armorProfile != null)
				{
					switch (armorProfile.Weight)
					{
					case ArmorWeight.Light:
						num += 0.1f;
						break;
					case ArmorWeight.Medium:
						num += 0.25f;
						break;
					case ArmorWeight.Heavy:
						num += 0.45f;
						break;
					}
				}
			}
			return Mathf.Clamp01(num);
		}

		public static float GetTotalArmorNoise(Player player)
		{
			float num = 0f;
			foreach (ItemData equippedItem in ((Humanoid)player).GetInventory().GetEquippedItems())
			{
				ArmorProfile armorProfile = GetArmorProfile(equippedItem);
				if (armorProfile != null)
				{
					num = armorProfile.Material switch
					{
						ArmorMaterial.Metal => num + 0.5f, 
						ArmorMaterial.Leather => num + 0.25f, 
						ArmorMaterial.Cloth => num + 0.1f, 
						_ => num + 0.2f, 
					};
				}
			}
			return Mathf.Clamp01(num);
		}
	}
	public static class ConfigManager
	{
		public static void Load(ConfigFile file)
		{
			ShadowsOfMidgardConfig.LoadUIConfig(file);
			if (!((Object)(object)ZNet.instance != (Object)null) || ZNet.instance.IsServer())
			{
				ShadowsOfMidgardConfig.LoadGameplayConfig(file);
			}
		}
	}
	public static class ShadowsOfMidgardConfig
	{
		public static StealthConfigModel Active;

		public static ConfigEntry<float> UI_EyePosX;

		public static ConfigEntry<float> UI_EyePosY;

		public static ConfigEntry<float> UI_NoisePosX;

		public static ConfigEntry<float> UI_NoisePosY;

		public static ConfigEntry<bool> DebugVision;

		public static ConfigEntry<bool> DebugAI;

		public static ConfigEntry<bool> ShowPatchSaga;

		public static StealthConfigModel LoadGameplayConfig(ConfigFile file)
		{
			StealthConfigModel stealthConfigModel = new StealthConfigModel();
			stealthConfigModel.EnableVisibilitySystem = file.Bind<bool>("Systems", "EnableVisibilitySystem", true, "Enable visibility calculations.").Value;
			stealthConfigModel.EnableNoiseSystem = file.Bind<bool>("Systems", "EnableNoiseSystem", true, "Enable noise calculations.").Value;
			stealthConfigModel.EnableHidingSystem = file.Bind<bool>("Systems", "EnableHidingSystem", true, "Enable hiding calculations.").Value;
			stealthConfigModel.EnableCamoSystem = file.Bind<bool>("Systems", "EnableCamoSystem", true, "Enable camouflage calculations.").Value;
			LoadStealthBrainConfig(file, stealthConfigModel);
			LoadVisibilityConfig(file, stealthConfigModel);
			LoadNoiseConfig(file, stealthConfigModel);
			LoadHidingConfig(file, stealthConfigModel);
			LoadCamoConfig(file, stealthConfigModel);
			LoadAwarenessConfig(file, stealthConfigModel);
			LoadDetectionRangeConfig(file, stealthConfigModel);
			LoadDebugConfig(file);
			Active = stealthConfigModel;
			return stealthConfigModel;
		}

		public static void LoadUIConfig(ConfigFile file)
		{
			UI_EyePosX = file.Bind<float>("UI", "EyePosX", 80f, "Horizontal position of the stealth gem.");
			UI_EyePosY = file.Bind<float>("UI", "EyePosY", 80f, "Vertical position of the stealth gem.");
			UI_NoisePosX = file.Bind<float>("UI", "NoisePosX", 80f, "Horizontal position of the noise meter.");
			UI_NoisePosY = file.Bind<float>("UI", "NoisePosY", 40f, "Vertical position of the noise meter.");
		}

		public static void LoadDebugConfig(ConfigFile file)
		{
			DebugVision = file.Bind<bool>("Debug", "EnableVisionDebug", false, "Enable detailed debug logs and overlays for stealth systems.");
			DebugAI = file.Bind<bool>("Debug", "EnableAIDebug", false, "Enable detailed AI debug logs (sensing, decisions, movement).");
			ShowPatchSaga = file.Bind<bool>("Debug", "ShowPatchSaga", true, "Show the epic patching saga dialog at startup (patch success/fail summary).");
		}

		private static void LoadStealthBrainConfig(ConfigFile file, StealthConfigModel cfg)
		{
			cfg.VisionThreshold = Clamp(file.Bind<float>("StealthBrain", "VisionThreshold", 0.25f, "Minimum visibility required for AI to see the player. Higher = harder to see.").Value, 0f, 1f);
			cfg.HearingThreshold = Clamp(file.Bind<float>("StealthBrain", "HearingThreshold", 0.15f, "Minimum noise required for AI to hear the player. Higher = harder to hear.").Value, 0f, 1f);
			cfg.SuspicionGain = Clamp(file.Bind<float>("StealthBrain", "SuspicionGain", 2.5f, "Rate at which suspicion increases when partially detected (heard). Higher = faster detection.").Value, 0f, 5f);
			cfg.AlertGain = Clamp(file.Bind<float>("StealthBrain", "AlertGain", 3f, "Rate at which alertness increases when clearly detected (seen). Higher = faster engagement.").Value, 0f, 5f);
			cfg.DetectionDecay = Clamp(file.Bind<float>("StealthBrain", "DetectionDecay", 0.5f, "Rate at which detection decays when the player is not sensed. Higher = faster cool-down.").Value, 0f, 5f);
			cfg.SearchDuration = Clamp(file.Bind<float>("StealthBrain", "SearchDuration", 4f, "How long AI will search after losing sight (seconds). Default: 4s.").Value, 1f, 30f);
			cfg.GiveUpTime = Clamp(file.Bind<float>("StealthBrain", "GiveUpTime", 8f, "How long after losing all signals the AI gives up (seconds). Default: 8s.").Value, 1f, 60f);
		}

		private static void LoadVisibilityConfig(ConfigFile file, StealthConfigModel cfg)
		{
			cfg.LightPenalty = Clamp(file.Bind<float>("Visibility", "LightPenalty", 0.35f, "Visibility modifier in bright light (0-1). Lower = darker overall.").Value, 0f, 2f);
			cfg.ShadowBonus = Clamp(file.Bind<float>("Visibility", "ShadowBonus", 0.25f, "Visibility reduction when in shadow. Higher = harder to see in shade.").Value, 0f, 1f);
			cfg.GrassBonus = Clamp(file.Bind<float>("Visibility", "GrassBonus", 0.2f, "Visibility reduction when crouching in grass/terrain.").Value, 0f, 1f);
			cfg.MovementPenalty = Clamp(file.Bind<float>("Visibility", "MovementPenalty", 0.8f, "Visibility increase from movement speed. Higher = more visible when moving.").Value, 0f, 3f);
			cfg.ArmorVisibility = Clamp(file.Bind<float>("Visibility", "ArmorVisibility", 0.6f, "How visible heavy armor makes the player.").Value, 0f, 3f);
			cfg.WeatherVisReduction = Clamp(file.Bind<float>("Visibility", "WeatherVisReduction", 0.2f, "Visibility reduction during rain or snow. Higher = harder to see in bad weather.").Value, 0f, 1f);
			cfg.FogVisReduction = Clamp(file.Bind<float>("Visibility", "FogVisReduction", 0.3f, "Visibility reduction from fog and mist. Higher = harder to see in fog.").Value, 0f, 1f);
		}

		private static void LoadNoiseConfig(ConfigFile file, StealthConfigModel cfg)
		{
			cfg.MovementNoise = Clamp(file.Bind<float>("Noise", "MovementNoise", 0.7f, "Noise generated by movement. Higher = louder when moving.").Value, 0f, 3f);
			cfg.ArmorNoise = Clamp(file.Bind<float>("Noise", "ArmorNoise", 0.6f, "Noise generated by heavy armor. Higher = louder with heavy gear.").Value, 0f, 3f);
			cfg.CrouchNoiseReduction = Clamp(file.Bind<float>("Noise", "CrouchNoiseReduction", 0.6f, "Noise reduction when crouching. Higher = quieter when crouched.").Value, 0f, 1f);
			cfg.WeatherNoiseReduction = Clamp(file.Bind<float>("Noise", "WeatherNoiseReduction", 0.4f, "Noise reduction during rain/snow. Higher = louder weather masks noise better.").Value, 0f, 1f);
		}

		private static void LoadHidingConfig(ConfigFile file, StealthConfigModel cfg)
		{
			cfg.BushBonus = Clamp(file.Bind<float>("Hiding", "BushBonus", 0.4f, "Hiding bonus when inside bushes.").Value, 0f, 1f);
			cfg.GrassBonus_Hiding = Clamp(file.Bind<float>("Hiding", "GrassBonus", 0.25f, "Hiding bonus when crouched in grass.").Value, 0f, 1f);
			cfg.CrouchBonus = Clamp(file.Bind<float>("Hiding", "CrouchBonus", 0.15f, "Hiding bonus when crouching (without other cover).").Value, 0f, 1f);
		}

		private static void LoadCamoConfig(ConfigFile file, StealthConfigModel cfg)
		{
			cfg.BiomeCamo = Clamp(file.Bind<float>("Camo", "BiomeCamo", 0.15f, "General camouflage bonus from matching biome.").Value, 0f, 1f);
			cfg.ArmorCamo = Clamp(file.Bind<float>("Camo", "ArmorCamo", 0.1f, "Camouflage bonus from armor matching biome.").Value, 0f, 1f);
		}

		private static void LoadAwarenessConfig(ConfigFile file, StealthConfigModel cfg)
		{
			cfg.SuspiciousThreshold = Clamp(file.Bind<float>("Awareness", "SuspiciousThreshold", 0.15f, "Detection level at which AI becomes Suspicious (starts investigating).").Value, 0f, 1f);
			cfg.AlertedThreshold = Clamp(file.Bind<float>("Awareness", "AlertedThreshold", 0.4f, "Detection level at which AI becomes Alerted (pursues with caution).").Value, 0f, 1f);
			cfg.EngagedThreshold = Clamp(file.Bind<float>("Awareness", "EngagedThreshold", 0.75f, "Detection level at which AI becomes Engaged (full combat mode).").Value, 0f, 1f);
		}

		private static void LoadDetectionRangeConfig(ConfigFile file, StealthConfigModel cfg)
		{
			cfg.MaxVisualRange = Clamp(file.Bind<float>("DetectionRange", "MaxVisualRange", 40f, "Maximum range (meters) at which AI can see the player. Default: 40m.").Value, 5f, 100f);
			cfg.MaxHearingRange = Clamp(file.Bind<float>("DetectionRange", "MaxHearingRange", 25f, "Maximum range (meters) at which AI can hear the player. Default: 25m.").Value, 5f, 80f);
			cfg.VisionConeHalfAngle = Clamp(file.Bind<float>("DetectionRange", "VisionConeHalfAngle", 60f, "Half-angle (degrees) of AI vision cone. 60 = 120° total FOV. Hearing is omnidirectional.").Value, 15f, 180f);
		}

		private static float Clamp(float v, float min, float max)
		{
			if (float.IsNaN(v) || float.IsInfinity(v))
			{
				return min;
			}
			return Mathf.Clamp(v, min, max);
		}
	}
	public class StealthConfigModel
	{
		public float VisionThreshold = 0.25f;

		public float HearingThreshold = 0.15f;

		public float SuspicionGain = 2.5f;

		public float AlertGain = 3f;

		public float DetectionDecay = 0.5f;

		public float SearchDuration = 4f;

		public float GiveUpTime = 8f;

		public float LightPenalty = 0.35f;

		public float ShadowBonus = 0.25f;

		public float GrassBonus = 0.2f;

		public float MovementPenalty = 0.8f;

		public float ArmorVisibility = 0.6f;

		public float WeatherVisReduction = 0.2f;

		public float FogVisReduction = 0.3f;

		public float MovementNoise = 0.7f;

		public float ArmorNoise = 0.6f;

		public float CrouchNoiseReduction = 0.6f;

		public float WeatherNoiseReduction = 0.4f;

		public float BushBonus = 0.4f;

		public float GrassBonus_Hiding = 0.25f;

		public float CrouchBonus = 0.15f;

		public float BiomeCamo = 0.15f;

		public float ArmorCamo = 0.1f;

		public float SuspiciousThreshold = 0.15f;

		public float AlertedThreshold = 0.4f;

		public float EngagedThreshold = 0.75f;

		public float MaxVisualRange = 40f;

		public float MaxHearingRange = 25f;

		public float VisionConeHalfAngle = 60f;

		public bool EnableVisibilitySystem = true;

		public bool EnableNoiseSystem = true;

		public bool EnableHidingSystem = true;

		public bool EnableCamoSystem = true;
	}
	public static class AIAuthority
	{
		public static bool IsAuthoritative(BaseAI ai)
		{
			if ((Object)(object)ai == (Object)null)
			{
				return false;
			}
			ZNetView component = ((Component)ai).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null)
			{
				if ((Object)(object)ZNet.instance != (Object)null)
				{
					return ZNet.instance.IsServer();
				}
				return false;
			}
			return component.IsOwner();
		}
	}
	public static class AwarenessSystem
	{
		private static readonly Dictionary<Character, AwarenessData> Data = new Dictionary<Character, AwarenessData>();

		private static int _cleanupCounter = 0;

		private const int CLEANUP_INTERVAL = 300;

		public static AwarenessData GetData(Character c)
		{
			if (!Data.TryGetValue(c, out var value))
			{
				value = new AwarenessData();
				Data[c] = value;
			}
			_cleanupCounter++;
			if (_cleanupCounter >= 300)
			{
				_cleanupCounter = 0;
				Cleanup();
			}
			return value;
		}

		public static void Clear(Character c)
		{
			if ((Object)(object)c != (Object)null)
			{
				Data.Remove(c);
			}
		}

		public static IEnumerable<KeyValuePair<Character, AwarenessData>> GetAllData()
		{
			return Data;
		}

		public static bool HasLineOfSight(Character c, Player target)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)c == (Object)null || (Object)(object)target == (Object)null)
			{
				return false;
			}
			return RaycastUtils.HasLineOfSight(c.GetEyePoint(), ((Component)target).transform.position);
		}

		public static void Cleanup()
		{
			foreach (Character item in Data.Keys.Where((Character c) => (Object)(object)c == (Object)null || c.IsDead()).ToList())
			{
				Data.Remove(item);
			}
		}

		public static void ClearAll()
		{
			Data.Clear();
		}
	}
	public static class BehaviorSystem
	{
		private enum CombatStance
		{
			Defensive,
			Balanced,
			Aggressive
		}

		private static Action<BaseAI, ZDOID> SetTargetInfoDelegate;

		static BehaviorSystem()
		{
			try
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(BaseAI), "SetTargetInfo", new Type[1] { typeof(ZDOID) }, (Type[])null);
				if (methodInfo != null)
				{
					SetTargetInfoDelegate = (Action<BaseAI, ZDOID>)Delegate.CreateDelegate(typeof(Action<BaseAI, ZDOID>), methodInfo);
					return;
				}
				ManualLogSource log = ShadowsOfMidgard.Log;
				if (log != null)
				{
					log.LogWarning((object)"[BehaviorSystem] Could not find SetTargetInfo method via reflection");
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log2 = ShadowsOfMidgard.Log;
				if (log2 != null)
				{
					log2.LogError((object)("[BehaviorSystem] Failed to initialize SetTargetInfo delegate: " + ex.Message));
				}
			}
		}

		private static void SetAITarget(BaseAI ai, ZDOID targetId)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ai == (Object)null || SetTargetInfoDelegate == null)
			{
				return;
			}
			try
			{
				SetTargetInfoDelegate(ai, targetId);
				if (ShadowsOfMidgardConfig.DebugVision != null && ShadowsOfMidgardConfig.DebugVision.Value && ShadowsOfMidgardConfig.DebugAI != null && ShadowsOfMidgardConfig.DebugAI.Value)
				{
					Character component = ((Component)ai).GetComponent<Character>();
					string text = (((Object)(object)component != (Object)null) ? ((Object)component).name : ((Object)ai).name);
					int frameCount = Time.frameCount;
					int num = (((Object)(object)ai != (Object)null) ? ((Object)ai).GetInstanceID() : 0);
					ManualLogSource log = ShadowsOfMidgard.Log;
					if (log != null)
					{
						log.LogInfo((object)$"[BehaviorSystem] frame={frameCount} ai={num} SetAITarget: {text} -> {targetId}");
					}
				}
			}
			catch (Exception ex)
			{
				if (ShadowsOfMidgardConfig.DebugVision != null && ShadowsOfMidgardConfig.DebugVision.Value)
				{
					ManualLogSource log2 = ShadowsOfMidgard.Log;
					if (log2 != null)
					{
						log2.LogWarning((object)("[BehaviorSystem] Failed to set AI target: " + ex.Message));
					}
				}
			}
		}

		public static void CallNearbyAllies(Character caller, Vector3 position, float radius, Player target)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: 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)
			if ((Object)(object)caller == (Object)null || caller.IsDead() || (Object)(object)target == (Object)null)
			{
				return;
			}
			Collider[] array = Physics.OverlapSphere(position, radius);
			int num = ((array != null) ? array.Length : 0);
			int num2 = 0;
			List<string> list = new List<string>();
			Collider[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				Character component = ((Component)array2[i]).GetComponent<Character>();
				if (!((Object)(object)component != (Object)null) || !((Object)(object)component != (Object)(object)caller) || component.IsDead())
				{
					continue;
				}
				MonsterAI component2 = ((Component)component).GetComponent<MonsterAI>();
				if (!((Object)(object)component2 != (Object)null) || ((BaseAI)component2).IsSleeping() || !AIAuthority.IsAuthoritative((BaseAI)(object)component2))
				{
					continue;
				}
				ZNetView component3 = ((Component)target).GetComponent<ZNetView>();
				ZDO val = (((Object)(object)component3 != (Object)null) ? component3.GetZDO() : null);
				if (val != null)
				{
					SetAITarget((BaseAI)(object)component2, val.m_uid);
				}
				AwarenessData data = AwarenessSystem.GetData(component);
				if (data != null)
				{
					float num3 = ShadowsOfMidgardConfig.Active?.AlertedThreshold ?? 0.4f;
					data.DetectionLevel = Mathf.Max(data.DetectionLevel, num3);
					if (data.CurrentState < VanillaAlertness.Alerted)
					{
						data.CurrentState = VanillaAlertness.Alerted;
					}
					data.LastKnownPosition = ((Component)target).transform.position;
					data.TimeSinceSeen = 0f;
					num2++;
					try
					{
						list.Add(((Object)component).name);
					}
					catch
					{
					}
				}
			}
			if (ShadowsOfMidgardConfig.DebugVision != null && ShadowsOfMidgardConfig.DebugVision.Value && ShadowsOfMidgardConfig.DebugAI != null && ShadowsOfMidgardConfig.DebugAI.Value)
			{
				int frameCount = Time.frameCount;
				int num4 = (((Object)(object)caller != (Object)null) ? ((Object)caller).GetInstanceID() : 0);
				ManualLogSource log = ShadowsOfMidgard.Log;
				if (log != null)
				{
					log.LogInfo((object)string.Format("[CallNearbyAllies] frame={0} caller={1} callerId={2} target={3} radius={4} nearbyChecked={5} affected={6} names={7}", frameCount, (caller != null) ? ((Object)caller).name : null, num4, (target != null) ? ((Object)target).name : null, radius, num, num2, string.Join(",", list)));
				}
			}
		}

		public static void ResetAI(MonsterAI ai)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			if (AIAuthority.IsAuthoritative((BaseAI)(object)ai))
			{
				SetAITarget((BaseAI)(object)ai, ZDOID.None);
			}
		}

		public static void ExecuteAction(MonsterAI ai, AIBehaviorAction action, Player target, StealthDecision decision)
		{
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: 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_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: 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_014e: 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_01ae: 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_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			if (!AIAuthority.IsAuthoritative((BaseAI)(object)ai))
			{
				return;
			}
			Character component = ((Component)ai).GetComponent<Character>();
			if ((Object)(object)component == (Object)null || component.IsDead())
			{
				return;
			}
			Vector3 val3;
			switch (action)
			{
			case AIBehaviorAction.Idle:
				ResetAI(ai);
				break;
			case AIBehaviorAction.Flee:
			case AIBehaviorAction.StrategicRetreat:
				ResetAI(ai);
				if ((Object)(object)target != (Object)null)
				{
					ApplyMovement(ai, component, decision.Movement);
				}
				break;
			case AIBehaviorAction.Pursue:
				if ((Object)(object)target != (Object)null)
				{
					ZNetView component3 = ((Component)target).GetComponent<ZNetView>();
					ZDO val2 = (((Object)(object)component3 != (Object)null) ? component3.GetZDO() : null);
					if (val2 != null)
					{
						SetAITarget((BaseAI)(object)ai, val2.m_uid);
					}
					ApplyMovement(ai, component, decision.Movement);
				}
				break;
			case AIBehaviorAction.Attack:
				if ((Object)(object)target != (Object)null && !((Character)target).IsDead())
				{
					ZNetView component2 = ((Component)target).GetComponent<ZNetView>();
					ZDO val = (((Object)(object)component2 != (Object)null) ? component2.GetZDO() : null);
					if (val != null)
					{
						SetAITarget((BaseAI)(object)ai, val.m_uid);
					}
					ApplyMovement(ai, component, decision.Movement);
				}
				break;
			case AIBehaviorAction.Search:
				if (decision.SearchTarget.HasValue)
				{
					Vector3 value2 = decision.SearchTarget.Value;
					Vector3 position2 = ((Component)component).transform.position;
					val3 = value2 - position2;
					Vector3 normalized2 = ((Vector3)(ref val3)).normalized;
					MovementDirective movementDirective = default(MovementDirective);
					movementDirective.Direction = normalized2;
					movementDirective.Speed = decision.Movement.Speed;
					movementDirective.ShouldRun = decision.Movement.ShouldRun;
					movementDirective.Strategy = MovementStrategy.TowardLastSeen;
					MovementDirective movement2 = movementDirective;
					ApplyMovement(ai, component, movement2);
				}
				break;
			case AIBehaviorAction.Investigate:
				if (decision.SearchTarget.HasValue)
				{
					Vector3 value = decision.SearchTarget.Value;
					Vector3 position = ((Component)component).transform.position;
					val3 = value - position;
					Vector3 normalized = ((Vector3)(ref val3)).normalized;
					MovementDirective movementDirective = default(MovementDirective);
					movementDirective.Direction = normalized;
					movementDirective.Speed = 0.3f;
					movementDirective.ShouldRun = false;
					movementDirective.Strategy = MovementStrategy.TowardLastSeen;
					MovementDirective movement = movementDirective;
					ApplyMovement(ai, component, movement);
				}
				break;
			case AIBehaviorAction.CallForHelp:
				if ((Object)(object)target != (Object)null && decision.GroupBehavior.CallForHelp)
				{
					CallNearbyAllies(component, ((Component)component).transform.position, decision.GroupBehavior.AlertRadius, target);
				}
				break;
			case AIBehaviorAction.Patrol:
				break;
			}
		}

		public static void ApplyMovement(MonsterAI ai, Character c, MovementDirective movement)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//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_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			if (movement.Direction == Vector3.zero)
			{
				c.SetMoveDir(Vector3.zero);
				return;
			}
			Vector3 val = movement.Direction * movement.Speed;
			c.SetMoveDir(val);
			if (movement.ShouldRun)
			{
				c.m_running = true;
			}
			else
			{
				c.m_running = false;
			}
			try
			{
				if (ShadowsOfMidgardConfig.DebugVision != null && ShadowsOfMidgardConfig.DebugVision.Value && ShadowsOfMidgardConfig.DebugAI != null && ShadowsOfMidgardConfig.DebugAI.Value)
				{
					int frameCount = Time.frameCount;
					int num = (((Object)(object)ai != (Object)null) ? ((Object)ai).GetInstanceID() : 0);
					ManualLogSource log = ShadowsOfMidgard.Log;
					if (log != null)
					{
						log.LogInfo((object)$"[BehaviorSystem] frame={frameCount} ai={num} ApplyMovement: Dir={val} Speed={movement.Speed:F2} Run={movement.ShouldRun}");
					}
				}
			}
			catch
			{
			}
		}

		public static void ExecuteCombat(MonsterAI ai, Character c, StealthDecision decision)
		{
			if (AIAuthority.IsAuthoritative((BaseAI)(object)ai) && !((Object)(object)c == (Object)null) && !c.IsDead() && decision.Combat.ShouldAttack)
			{
				if (decision.Combat.AggressionLevel < 0.3f)
				{
					SetCombatStance(c, CombatStance.Defensive);
				}
				else if (decision.Combat.AggressionLevel > 0.7f)
				{
					SetCombatStance(c, CombatStance.Aggressive);
				}
				else
				{
					SetCombatStance(c, CombatStance.Balanced);
				}
				if (decision.Combat.FavorDefense)
				{
					SetCombatStance(c, CombatStance.Defensive);
				}
				if (ShadowsOfMidgardConfig.DebugVision != null && ShadowsOfMidgardConfig.DebugVision.Value)
				{
					ShadowsOfMidgard.Log.LogInfo((object)($"[Combat Execute] {((Object)c).name}: Aggression={decision.Combat.AggressionLevel:F2} " + $"Defense={decision.Combat.FavorDefense} OptimalRange={decision.Combat.OptimalAttackRange:F2}m"));
				}
			}
		}

		private static void SetCombatStance(Character c, CombatStance stance)
		{
			if (!((Object)(object)((c is Humanoid) ? c : null) == (Object)null))
			{
				switch (stance)
				{
				}
			}
		}

		public static float GetCombatCooldown(float baseCooldown, float aggressionLevel)
		{
			return baseCooldown * (2f - aggressionLevel);
		}
	}
	public static class CamoSystem
	{
		private static readonly HashSet<Biome> CamoFriendlyBiomes = new HashSet<Biome>
		{
			(Biome)1,
			(Biome)8,
			(Biome)2,
			(Biome)16,
			(Biome)512
		};

		private static readonly HashSet<Biome> PartialCamoBiomes = new HashSet<Biome> { (Biome)4 };

		private static readonly Dictionary<string, HashSet<Biome>> ArmorBiomeMap = new Dictionary<string, HashSet<Biome>>
		{
			{
				"forest",
				new HashSet<Biome>
				{
					(Biome)8,
					(Biome)1
				}
			},
			{
				"swamp",
				new HashSet<Biome> { (Biome)2 }
			},
			{
				"snow",
				new HashSet<Biome>
				{
					(Biome)4,
					(Biome)64
				}
			},
			{
				"plains",
				new HashSet<Biome> { (Biome)16 }
			},
			{
				"mist",
				new HashSet<Biome> { (Biome)512 }
			}
		};

		public static float GetCamoFactor(Player p)
		{
			StealthConfigModel active = ShadowsOfMidgardConfig.Active;
			if ((Object)(object)p == (Object)null || active == null || !active.EnableCamoSystem)
			{
				return 0f;
			}
			float num = 0f;
			num += BiomeMatch(p, active);
			num += ArmorMatch(p, active);
			num = Mathf.Clamp01(num);
			if (ShadowsOfMidgardConfig.DebugVision.Value)
			{
				Debug.Log((object)$"[Camo] {((Object)p).name}: camo={num:F2}");
			}
			return num;
		}

		private static Biome GetPlayerBiome(Player p)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			Heightmap val = Heightmap.FindHeightmap(((Component)p).transform.position);
			if ((Object)(object)val != (Object)null)
			{
				return val.GetBiome(((Component)p).transform.position, 0.02f, false);
			}
			return (Biome)0;
		}

		private static float BiomeMatch(Player p, StealthConfigModel cfg)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			Biome playerBiome = GetPlayerBiome(p);
			if (CamoFriendlyBiomes.Contains(playerBiome))
			{
				return cfg.BiomeCamo;
			}
			if (PartialCamoBiomes.Contains(playerBiome))
			{
				return cfg.BiomeCamo * 0.5f;
			}
			return 0f;
		}

		private static float ArmorMatch(Player p, StealthConfigModel cfg)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			Biome playerBiome = GetPlayerBiome(p);
			float result = 0f;
			int num = 0;
			foreach (ItemData equippedItem in ((Humanoid)p).GetInventory().GetEquippedItems())
			{
				ArmorProfile profile = ArmorProfileSystem.GetProfile(equippedItem);
				if (profile == null || !profile.HasCamoTag)
				{
					continue;
				}
				string text = equippedItem.m_shared?.m_name?.ToLowerInvariant() ?? "";
				bool flag = false;
				foreach (KeyValuePair<string, HashSet<Biome>> item in ArmorBiomeMap)
				{
					if (text.Contains(item.Key) && item.Value.Contains(playerBiome))
					{
						flag = true;
						break;
					}
				}
				if (flag)
				{
					num++;
				}
			}
			if (num > 0)
			{
				result = cfg.ArmorCamo * Mathf.Min(1f, (float)num / 4f);
			}
			return result;
		}
	}
	public static class ConfigSync
	{
		private class ConfigSyncBehaviour : MonoBehaviour
		{
			private float _timer;

			private void Update()
			{
				try
				{
					_timer += Time.deltaTime;
					if (_timer >= 30f)
					{
						_timer = 0f;
						if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
						{
							BroadcastConfig();
						}
					}
				}
				catch (Exception arg)
				{
					ShadowsOfMidgard.Log.LogWarning((object)$"ConfigSyncBehaviour.Update exception: {arg}");
				}
			}

			private void OnDestroy()
			{
			}
		}

		private const string RPC_Request = "SOM_RequestConfig";

		private const string RPC_Response = "SOM_ConfigResponse";

		private const float ReSyncInterval = 30f;

		private static GameObject s_syncGO;

		private static readonly object s_lock = new object();

		private static bool s_registered = false;

		public static bool IsRegistered
		{
			get
			{
				lock (s_lock)
				{
					return s_registered;
				}
			}
		}

		public static void Init()
		{
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Expected O, but got Unknown
			try
			{
				lock (s_lock)
				{
					if (s_registered)
					{
						ShadowsOfMidgard.Log.LogInfo((object)"ConfigSync: already registered; skipping.");
						return;
					}
				}
				if (ZRoutedRpc.instance == null)
				{
					ShadowsOfMidgard.Log.LogInfo((object)"ConfigSync: ZRoutedRpc not ready; skipping config sync registration.");
					return;
				}
				ZRoutedRpc.instance.Register<string>("SOM_ConfigResponse", (Action<long, string>)OnConfigResponse);
				ZRoutedRpc.instance.Register<string>("SOM_RequestConfig", (Action<long, string>)OnRequestConfig);
				lock (s_lock)
				{
					s_registered = true;
				}
				if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
				{
					BroadcastConfig();
					try
					{
						if ((Object)(object)s_syncGO == (Object)null)
						{
							s_syncGO = new GameObject("SOM_ConfigSync");
							Object.DontDestroyOnLoad((Object)(object)s_syncGO);
							((Object)s_syncGO).hideFlags = (HideFlags)61;
							s_syncGO.AddComponent<ConfigSyncBehaviour>();
						}
					}
					catch (Exception arg)
					{
						ShadowsOfMidgard.Log.LogWarning((object)$"ConfigSync: failed to create periodic re-sync behaviour: {arg}");
					}
				}
				else
				{
					try
					{
						ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "SOM_RequestConfig", new object[1] { string.Empty });
					}
					catch (Exception arg2)
					{
						ShadowsOfMidgard.Log.LogWarning((object)$"ConfigSync: failed to invoke request RPC: {arg2}");
					}
				}
				ShadowsOfMidgard.Log.LogInfo((object)"ConfigSync initialized.");
			}
			catch (Exception arg3)
			{
				ShadowsOfMidgard.Log.LogWarning((object)$"ConfigSync.Init exception: {arg3}");
			}
		}

		private static void OnRequestConfig(long sender, string dummy)
		{
			try
			{
				if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
				{
					BroadcastConfig();
				}
			}
			catch (Exception arg)
			{
				ShadowsOfMidgard.Log.LogWarning((object)$"ConfigSync.OnRequestConfig exception: {arg}");
			}
		}

		internal static void BroadcastConfig()
		{
			try
			{
				if (ShadowsOfMidgardConfig.Active == null)
				{
					ShadowsOfMidgard.Log.LogWarning((object)"ConfigSync: Active gameplay config is null; nothing to broadcast.");
					return;
				}
				string text = SerializeConfig(ShadowsOfMidgardConfig.Active);
				if (ZRoutedRpc.instance == null)
				{
					ShadowsOfMidgard.Log.LogWarning((object)"ConfigSync: ZRoutedRpc.instance is null; cannot broadcast.");
					return;
				}
				ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "SOM_ConfigResponse", new object[1] { text });
				ShadowsOfMidgard.Log.LogInfo((object)"ConfigSync: broadcasted authoritative gameplay config to peers.");
			}
			catch (Exception arg)
			{
				ShadowsOfMidgard.Log.LogWarning((object)$"ConfigSync.BroadcastConfig exception: {arg}");
			}
		}

		private static void OnConfigResponse(long sender, string payload)
		{
			try
			{
				if (!((Object)(object)ZNet.instance != (Object)null) || !ZNet.instance.IsServer())
				{
					if (string.IsNullOrEmpty(payload))
					{
						ShadowsOfMidgard.Log.LogWarning((object)"ConfigSync: received empty config payload; ignoring.");
						return;
					}
					if (ShadowsOfMidgardConfig.Active == null)
					{
						ShadowsOfMidgard.Log.LogWarning((object)"ConfigSync: Active gameplay config not initialized on client; cannot apply server config.");
						return;
					}
					DeserializeAndApplyConfig(payload, ShadowsOfMidgardConfig.Active);
					ShadowsOfMidgard.Log.LogInfo((object)"ConfigSync: applied server authoritative gameplay config (in-memory).");
				}
			}
			catch (Exception arg)
			{
				ShadowsOfMidgard.Log.LogWarning((object)$"ConfigSync.OnConfigResponse exception: {arg}");
			}
		}

		private static string SerializeConfig(StealthConfigModel model)
		{
			try
			{
				FieldInfo[] fields = typeof(StealthConfigModel).GetFields(BindingFlags.Instance | BindingFlags.Public);
				StringBuilder stringBuilder = new StringBuilder();
				FieldInfo[] array = fields;
				foreach (FieldInfo fieldInfo in array)
				{
					object value = fieldInfo.GetValue(model);
					if (value != null)
					{
						string value2 = ((!(value is float num)) ? ((!(value is double num2)) ? ((!(value is bool)) ? value.ToString() : (((bool)value) ? "1" : "0")) : num2.ToString(CultureInfo.InvariantCulture)) : num.ToString(CultureInfo.InvariantCulture));
						stringBuilder.Append(fieldInfo.Name).Append("=").Append(value2)
							.Append(";");
					}
				}
				return stringBuilder.ToString();
			}
			catch (Exception arg)
			{
				ShadowsOfMidgard.Log.LogWarning((object)$"ConfigSync.SerializeConfig exception: {arg}");
				return string.Empty;
			}
		}

		private static void DeserializeAndApplyConfig(string payload, StealthConfigModel target)
		{
			try
			{
				FieldInfo[] fields = typeof(StealthConfigModel).GetFields(BindingFlags.Instance | BindingFlags.Public);
				Dictionary<string, FieldInfo> dictionary = new Dictionary<string, FieldInfo>(StringComparer.OrdinalIgnoreCase);
				FieldInfo[] array = fields;
				foreach (FieldInfo fieldInfo in array)
				{
					dictionary[fieldInfo.Name] = fieldInfo;
				}
				string[] array2 = payload.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries);
				for (int i = 0; i < array2.Length; i++)
				{
					string[] array3 = array2[i].Split(new char[1] { '=' }, 2);
					if (array3.Length != 2)
					{
						continue;
					}
					string key = array3[0];
					string text = array3[1];
					if (!dictionary.TryGetValue(key, out var value))
					{
						continue;
					}
					int result3;
					if (value.FieldType == typeof(float))
					{
						if (float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
						{
							value.SetValue(target, result);
						}
					}
					else if (value.FieldType == typeof(double))
					{
						if (double.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2))
						{
							value.SetValue(target, result2);
						}
					}
					else if (value.FieldType == typeof(bool))
					{
						if (text == "1" || text.Equals("true", StringComparison.OrdinalIgnoreCase))
						{
							value.SetValue(target, true);
						}
						else
						{
							value.SetValue(target, false);
						}
					}
					else if (value.FieldType == typeof(int) && int.TryParse(text, out result3))
					{
						value.SetValue(target, result3);
					}
				}
			}
			catch (Exception arg)
			{
				ShadowsOfMidgard.Log.LogWarning((object)$"ConfigSync.DeserializeAndApplyConfig exception: {arg}");
			}
		}

		public static void Shutdown()
		{
			try
			{
				lock (s_lock)
				{
					if (!s_registered)
					{
						return;
					}
					s_registered = false;
				}
				try
				{
					if ((Object)(object)s_syncGO != (Object)null)
					{
						Object.Destroy((Object)(object)s_syncGO);
						s_syncGO = null;
					}
				}
				catch (Exception arg)
				{
					ShadowsOfMidgard.Log.LogWarning((object)$"ConfigSync.Shutdown: failed to destroy sync GameObject: {arg}");
				}
				ShadowsOfMidgard.Log.LogInfo((object)"ConfigSync: shutdown complete.");
			}
			catch (Exception arg2)
			{
				ShadowsOfMidgard.Log.LogWarning((object)$"ConfigSync.Shutdown exception: {arg2}");
			}
		}
	}
	public static class HidingSystem
	{
		private static int _vegetationMask = -1;

		private static int VegetationMask
		{
			get
			{
				if (_vegetationMask == -1)
				{
					_vegetationMask = LayerMask.GetMask(new string[4] { "Default", "piece", "piece_nonsolid", "static_solid" });
				}
				return _vegetationMask;
			}
		}

		public static float GetHidingFactor(Player p)
		{
			StealthConfigModel active = ShadowsOfMidgardConfig.Active;
			if ((Object)(object)p == (Object)null || active == null || !active.EnableHidingSystem)
			{
				return 0f;
			}
			float num = 0f;
			num += BushBonus(p, active);
			num += GrassBonus(p, active);
			num += CrouchBonus(p, active);
			num = Mathf.Clamp01(num);
			if (ShadowsOfMidgardConfig.DebugVision.Value)
			{
				Debug.Log((object)$"[Hiding] {((Object)p).name}: hiding={num:F2}");
			}
			return num;
		}

		private static float BushBonus(Player p, StealthConfigModel cfg)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			Collider[] array = Physics.OverlapSphere(((Component)p).transform.position, 1f, VegetationMask);
			for (int i = 0; i < array.Length; i++)
			{
				if (((Object)array[i]).name.ToLowerInvariant().Contains("bush"))
				{
					return cfg.BushBonus;
				}
			}
			return 0f;
		}

		private static float GrassBonus(Player p, StealthConfigModel cfg)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			Collider[] array = Physics.OverlapSphere(((Component)p).transform.position, 1f, VegetationMask);
			for (int i = 0; i < array.Length; i++)
			{
				if (((Object)array[i]).name.ToLowerInvariant().Contains("grass"))
				{
					return cfg.GrassBonus_Hiding;
				}
			}
			return 0f;
		}

		private static float CrouchBonus(Player p, StealthConfigModel cfg)
		{
			if (!((Character)p).IsCrouching())
			{
				return 0f;
			}
			return cfg.CrouchBonus;
		}
	}
	public static class NoiseSystem
	{
		public static float GetNoise(Player p)
		{
			StealthConfigModel active = ShadowsOfMidgardConfig.Active;
			if ((Object)(object)p == (Object)null || active == null || !active.EnableNoiseSystem)
			{
				return 0f;
			}
			float num = 0f;
			num += MovementNoise(p, active);
			num += ArmorNoise(p, active);
			num -= CrouchReduction(p, active);
			num -= WeatherReduction(active);
			num = Mathf.Clamp01(num);
			if (ShadowsOfMidgardConfig.DebugVision.Value)
			{
				Debug.Log((object)$"[Noise] {((Object)p).name}: noise={num:F2}");
			}
			return num;
		}

		private static float MovementNoise(Player p, StealthConfigModel cfg)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			Vector3 velocity = ((Character)p).GetVelocity();
			return Mathf.Clamp01(((Vector3)(ref velocity)).magnitude / 7f) * cfg.MovementNoise;
		}

		private static float ArmorNoise(Player p, StealthConfigModel cfg)
		{
			return ArmorUtils.GetTotalArmorNoise(p) * cfg.ArmorNoise;
		}

		private static float CrouchReduction(Player p, StealthConfigModel cfg)
		{
			if (!((Character)p).IsCrouching())
			{
				return 0f;
			}
			return cfg.CrouchNoiseReduction;
		}

		private static float WeatherReduction(StealthConfigModel cfg)
		{
			EnvMan instance = EnvMan.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return 0f;
			}
			string text = instance.GetCurrentEnvironment()?.m_name?.ToLowerInvariant() ?? "";
			if (text.Contains("rain") || text.Contains("snow") || text.Contains("mist"))
			{
				return cfg.WeatherNoiseReduction;
			}
			return 0f;
		}
	}
	public static class StealthBrain
	{
		private struct CachedEvaluation
		{
			public int LastFullEvalFrame;

			public int LastCacheFrame;

			public StealthDecision Decision;
		}

		private struct StableAIKey
		{
			public readonly ulong NetworkUid;

			public readonly int InstanceId;

			public StableAIKey(ulong networkUid, int instanceId)
			{
				NetworkUid = networkUid;
				InstanceId = instanceId;
			}

			public override bool Equals(object obj)
			{
				if (obj is StableAIKey stableAIKey)
				{
					if (NetworkUid == stableAIKey.NetworkUid)
					{
						return InstanceId == stableAIKey.InstanceId;
					}
					return false;
				}
				return false;
			}

			public override int GetHashCode()
			{
				ulong networkUid = NetworkUid;
				return (networkUid.GetHashCode() * 397) ^ InstanceId;
			}

			public override string ToString()
			{
				if (NetworkUid == 0L)
				{
					return $"inst:{InstanceId}";
				}
				return $"net:{NetworkUid}";
			}
		}

		private const float NEARBY_ALLY_RADIUS = 30f;

		private const float STRATEGIC_RETREAT_DISTANCE = 50f;

		private const float HEALTH_CRITICAL_THRESHOLD = 0.25f;

		private const float HEALTH_LOW_THRESHOLD = 0.5f;

		private const float STRATEGIC_RETREAT_COOLDOWN = 10f;

		private const float ALLY_CALL_COOLDOWN = 3f;

		private const int EVALUATION_SKIP = 5;

		private static readonly Dictionary<StableAIKey, CachedEvaluation> _evalCache = new Dictionary<StableAIKey, CachedEvaluation>();

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

		private static int _cacheCleanupFrame = 0;

		private const int CACHE_CLEANUP_INTERVAL = 600;

		public static StealthDecision Evaluate(MonsterAI ai, Player p, float dt)
		{
			StealthDecision stealthDecision = new StealthDecision();
			StealthConfigModel active = ShadowsOfMidgardConfig.Active;
			if ((Object)(object)ai == (Object)null || (Object)(object)p == (Object)null || active == null)
			{
				return stealthDecision;
			}
			Character component = ((Component)ai).GetComponent<Character>();
			if ((Object)(object)component == (Object)null || component.IsDead())
			{
				return stealthDecision;
			}
			if (((BaseAI)ai).IsSleeping())
			{
				return stealthDecision;
			}
			AwarenessData data = AwarenessSystem.GetData(component);
			if (data == null)
			{
				return stealthDecision;
			}
			StableAIKey stableAIKey = GetStableAIKey(ai);
			int instanceID = ((Object)ai).GetInstanceID();
			int frameCount = Time.frameCount;
			if (frameCount - _cacheCleanupFrame > 600)
			{
				_cacheCleanupFrame = frameCount;
				CleanupStaleCache(frameCount);
			}
			if (_evalCache.TryGetValue(stableAIKey, out var value) && value.LastFullEvalFrame == frameCount)
			{
				try
				{
					if (ShadowsOfMidgardConfig.DebugVision != null && ShadowsOfMidgardConfig.DebugVision.Value && ShadowsOfMidgardConfig.DebugAI != null && ShadowsOfMidgardConfig.DebugAI.Value)
					{
						ManualLogSource log = ShadowsOfMidgard.Log;
						if (log != null)
						{
							log.LogInfo((object)$"[CACHE-HIT] frame={frameCount} ai={stableAIKey} inst={instanceID} CanSense={value.Decision.CanSense} Det={value.Decision.DetectionLevel:F2}");
						}
						foreach (StableAIKey key2 in _evalCache.Keys)
						{
							if (key2.InstanceId == stableAIKey.InstanceId && !key2.Equals(stableAIKey))
							{
								ManualLogSource log2 = ShadowsOfMidgard.Log;
								if (log2 != null)
								{
									log2.LogWarning((object)$"[CACHE-ANOMALY] frame={frameCount} inst={instanceID} duplicate-keys: current={stableAIKey} other={key2}");
								}
							}
						}
					}
				}
				catch
				{
				}
				return value.Decision;
			}
			try
			{
				if (ShadowsOfMidgardConfig.DebugVision != null && ShadowsOfMidgardConfig.DebugVision.Value && ShadowsOfMidgardConfig.DebugAI != null && ShadowsOfMidgardConfig.DebugAI.Value)
				{
					string text = (((Object)(object)component != (Object)null) ? ((Object)component).name : (((Object)(object)ai != (Object)null) ? ((Object)ai).name : "<unknown>"));
					string text2 = (((Object)(object)p != (Object)null) ? ((Object)p).name : "<unknown>");
					ManualLogSource log3 = ShadowsOfMidgard.Log;
					if (log3 != null)
					{
						log3.LogInfo((object)$"[StealthBrain] frame={frameCount} ai={instanceID} START: {text} -> {text2} State={data.CurrentState} Det={data.DetectionLevel:F2}");
					}
				}
			}
			catch
			{
			}
			EvaluateSensing(ai, p, component, data, active, dt, stealthDecision);
			EvaluateStateTransitions(data, active, stealthDecision, dt);
			AssessHealth(ai, p, data, active);
			EvaluateFleeing(ai, p, component, data, active, stealthDecision, dt);
			EvaluateNearbyAllies(ai, component, data, active);
			EvaluateMovement(ai, p, component, data, active, stealthDecision, dt);
			EvaluateCombat(ai, p, component, data, active, stealthDecision);
			EvaluateGroupBehavior(ai, p, component, data, active, stealthDecision, dt);
			DetermineAction(ai, p, component, data, active, stealthDecision);
			FinalizeDecision(data, stealthDecision);
			stealthDecision.FrameCounter = frameCount;
			try
			{
				if (ShadowsOfMidgardConfig.DebugVision != null && ShadowsOfMidgardConfig.DebugVision.Value && ShadowsOfMidgardConfig.DebugAI != null && ShadowsOfMidgardConfig.DebugAI.Value)
				{
					ManualLogSource log4 = ShadowsOfMidgard.Log;
					if (log4 != null)
					{
						log4.LogInfo((object)($"[StealthBrain] frame={frameCount} ai={stableAIKey} inst={instanceID} CanSense={stealthDecision.CanSense} State={stealthDecision.State} " + $"DetLevel={stealthDecision.DetectionLevel:F2} Action={stealthDecision.RecommendedAction} " + "Reason=" + stealthDecision.DecisionReason));
					}
				}
			}
			catch
			{
			}
			_evalCache[stableAIKey] = new CachedEvaluation
			{
				LastFullEvalFrame = frameCount,
				LastCacheFrame = frameCount,
				Decision = stealthDecision
			};
			try
			{
				if (ShadowsOfMidgardConfig.DebugVision != null && ShadowsOfMidgardConfig.DebugVision.Value && ShadowsOfMidgardConfig.DebugAI != null && ShadowsOfMidgardConfig.DebugAI.Value)
				{
					ManualLogSource log5 = ShadowsOfMidgard.Log;
					if (log5 != null)
					{
						log5.LogInfo((object)$"[CACHE-WRITE] frame={frameCount} ai={stableAIKey} inst={instanceID} CanSense={stealthDecision.CanSense} Det={stealthDecision.DetectionLevel:F2}");
					}
					foreach (KeyValuePair<StableAIKey, CachedEvaluation> item in _evalCache)
					{
						StableAIKey key = item.Key;
						CachedEvaluation value2 = item.Value;
						if (key.InstanceId == stableAIKey.InstanceId && !key.Equals(stableAIKey) && value2.LastFullEvalFrame == frameCount)
						{
							ManualLogSource log6 = ShadowsOfMidgard.Log;
							if (log6 != null)
							{
								log6.LogWarning((object)$"[CACHE-ANOMALY] frame={frameCount} inst={instanceID} conflicting-cache-entry: new={stableAIKey} existing={key}");
							}
						}
					}
				}
			}
			catch
			{
			}
			if (stableAIKey.NetworkUid == 0L)
			{
				int instanceID2 = ((Object)ai).GetInstanceID();
				if (!_pendingNetworkMigrations.ContainsKey(instanceID2))
				{
					_pendingNetworkMigrations[instanceID2] = ai;
					try
					{
						if (ShadowsOfMidgardConfig.DebugVision != null && ShadowsOfMidgardConfig.DebugVision.Value && ShadowsOfMidgardConfig.DebugAI != null && ShadowsOfMidgardConfig.DebugAI.Value)
						{
							ManualLogSource log7 = ShadowsOfMidgard.Log;
							if (log7 != null)
							{
								log7.LogInfo((object)$"[CACHE-PENDING] frame={frameCount} inst={instanceID2} registered for migration (no network UID yet)");
							}
						}
					}
					catch
					{
					}
				}
			}
			TryProcessPendingNetworkMigrations(frameCount);
			return stealthDecision;
		}

		private static void TryProcessPendingNetworkMigrations(int currentFrame)
		{
			if (_pendingNetworkMigrations.Count == 0 || currentFrame % 30 != 0)
			{
				return;
			}
			try
			{
				bool flag = (Object)(object)ZNet.instance != (Object)null;
				bool flag2 = false;
				try
				{
					flag2 = ZRoutedRpc.instance != null;
				}
				catch
				{
					flag2 = false;
				}
				if (!flag || !flag2)
				{
					try
					{
						if (ShadowsOfMidgardConfig.DebugVision != null && ShadowsOfMidgardConfig.DebugVision.Value && ShadowsOfMidgardConfig.DebugAI != null && ShadowsOfMidgardConfig.DebugAI.Value)
						{
							ManualLogSource log = ShadowsOfMidgard.Log;
							if (log != null)
							{
								log.LogInfo((object)$"[CACHE-MIGRATE] frame={currentFrame} network not ready: ZNet={(Object)(object)ZNet.instance != (Object)null} ZRouted={ZRoutedRpc.instance != null} pending={_pendingNetworkMigrations.Count}");
							}
						}
						return;
					}
					catch
					{
						return;
					}
				}
			}
			catch
			{
			}
			List<int> list = new List<int>();
			foreach (KeyValuePair<int, MonsterAI> pendingNetworkMigration in _pendingNetworkMigrations)
			{
				int key = pendingNetworkMigration.Key;
				MonsterAI value = pendingNetworkMigration.Value;
				if ((Object)(object)value == (Object)null)
				{
					list.Add(key);
					continue;
				}
				ulong num = 0uL;
				try
				{
					ZNetView component = ((Component)value).GetComponent<ZNetView>();
					if ((Object)(object)component != (Object)null)
					{
						ZDO zDO = component.GetZDO();
						if (zDO != null)
						{
							num = (ulong)((ZDOID)(ref zDO.m_uid)).UserID;
						}
					}
				}
				catch
				{
				}
				if (num == 0L)
				{
					continue;
				}
				StableAIKey key2 = new StableAIKey(0uL, key);
				StableAIKey key3 = new StableAIKey(num, key);
				if (_evalCache.TryGetValue(key2, out var value2))
				{
					_evalCache.Remove(key2);
					_evalCache[key3] = value2;
					try
					{
						if (ShadowsOfMidgardConfig.DebugVision != null && ShadowsOfMidgardConfig.DebugVision.Value && ShadowsOfMidgardConfig.DebugAI != null && ShadowsOfMidgardConfig.DebugAI.Value)
						{
							ManualLogSource log2 = ShadowsOfMidgard.Log;
							if (log2 != null)
							{
								log2.LogInfo((object)$"[CACHE-MIGRATE] frame={currentFrame} inst={key} -> net={num}");
							}
						}
					}
					catch
					{
					}
				}
				list.Add(key);
			}
			foreach (int item in list)
			{
				_pendingNetworkMigrations.Remove(item);
			}
		}

		private static StableAIKey GetStableAIKey(MonsterAI ai)
		{
			if ((Object)(object)ai == (Object)null)
			{
				return new StableAIKey(0uL, 0);
			}
			int instanceID = ((Object)ai).GetInstanceID();
			try
			{
				ZNetView component = ((Component)ai).GetComponent<ZNetView>();
				if ((Object)(object)component != (Object)null)
				{
					ZDO zDO = component.GetZDO();
					if (zDO != null)
					{
						return new StableAIKey((ulong)((ZDOID)(ref zDO.m_uid)).UserID, instanceID);
					}
				}
			}
			catch
			{
			}
			return new StableAIKey(0uL, instanceID);
		}

		private static void EvaluateSensing(MonsterAI ai, Player p, Character c, AwarenessData data, StealthConfigModel cfg, float dt, StealthDecision decision)
		{
			//IL_007e: 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_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: 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_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Unknown result type (might be due to invalid IL or missing references)
			//IL_0267: Unknown result type (might be due to invalid IL or missing references)
			float num = (cfg.EnableVisibilitySystem ? VisibilitySystem.GetVisibility(p) : 0f);
			float num2 = (cfg.EnableNoiseSystem ? NoiseSystem.GetNoise(p) : 0f);
			float num3 = (cfg.EnableHidingSystem ? HidingSystem.GetHidingFactor(p) : 0f);
			float num4 = (cfg.EnableCamoSystem ? CamoSystem.GetCamoFactor(p) : 0f);
			float num5 = num * (1f - num3) * (1f - num4);
			bool flag = AwarenessSystem.HasLineOfSight(c, p);
			float num6 = Vector3.Distance(((Component)c).transform.position, ((Component)p).transform.position);
			float num7 = 1f - Mathf.Clamp01(num6 / cfg.MaxVisualRange);
			float num8 = 1f - Mathf.Clamp01(num6 / cfg.MaxHearingRange);
			num5 *= num7;
			float num9 = num2 * num8;
			Vector3 val = ((Component)p).transform.position - ((Component)c).transform.position;
			Vector3 normalized = ((Vector3)(ref val)).normalized;
			float num10 = Vector3.Dot(((Component)c).transform.forward, normalized);
			float num11 = Mathf.Cos(cfg.VisionConeHalfAngle * ((float)Math.PI / 180f));
			if (num10 < num11)
			{
				float num12 = Mathf.Clamp01((num10 - -1f) / (num11 - -1f)) * 0.1f;
				num5 *= num12;
			}
			bool flag2 = data.CurrentState >= VanillaAlertness.Alerted;
			decision.CanSee = (flag || flag2) && num5 > cfg.VisionThreshold;
			decision.CanHear = num9 > cfg.HearingThreshold;
			decision.CanSense = decision.CanSee || decision.CanHear;
			if (decision.CanSee)
			{
				decision.SensingConfidence = Mathf.Clamp01(num5 / (cfg.VisionThreshold * 2f));
			}
			else if (decision.CanHear)
			{
				decision.SensingConfidence = Mathf.Clamp01(num9 / (cfg.HearingThreshold * 2f));
			}
			else
			{
				decision.SensingConfidence = 0f;
			}
			float num13 = 0f;
			num13 = (decision.CanSee ? cfg.AlertGain : ((!decision.CanHear) ? (0f - cfg.DetectionDecay) : cfg.SuspicionGain));
			data.DetectionLevel = Mathf.Clamp01(data.DetectionLevel + num13 * dt);
			if (decision.CanSee || decision.CanHear)
			{
				data.LastKnownPosition = ((Component)p).transform.position;
				data.TimeSinceSeen = 0f;
			}
			else
			{
				data.TimeSinceSeen += dt;
			}
			decision.DetectionLevel = data.DetectionLevel;
			decision.Debug.Visibility = num;
			decision.Debug.Noise = num2;
			decision.Debug.Hiding = num3;
			decision.Debug.Camo = num4;
			decision.Debug.CanSee = decision.CanSee;
			decision.Debug.CanHear = decision.CanHear;
			decision.Debug.CanSense = decision.CanSense;
			try
			{
				if (ShadowsOfMidgardConfig.DebugVision != null && ShadowsOfMidgardConfig.DebugVision.Value && ShadowsOfMidgardConfig.DebugAI != null && ShadowsOfMidgardConfig.DebugAI.Value)
				{
					int frameCount = Time.frameCount;
					int num14 = (((Object)(object)ai != (Object)null) ? ((Object)ai).GetInstanceID() : 0);
					StableAIKey stableAIKey = GetStableAIKey(ai);
					string text = (((Object)(object)c != (Object)null) ? ((Object)c).name : (((Object)(object)ai != (Object)null) ? ((Object)ai).name : "<unknown>"));
					ManualLogSource log = ShadowsOfMidgard.Log;
					if (log != null)
					{
						log.LogInfo((object)($"[StealthSense] frame={frameCount} ai={stableAIKey} inst={num14} {text}: vis={num:F2} adjVis={num5:F2} noise={num2:F2} adjNoise={num9:F2} " + $"hiding={num3:F2} camo={num4:F2} dist={num6:F1} dot={num10:F2} canSee={decision.CanSee} " + $"canHear={decision.CanHear} canSense={decision.CanSense} det={data.DetectionLevel:F2}"));
					}
				}
			}
			catch
			{
			}
		}

		private static void EvaluateStateTransitions(AwarenessData data, StealthConfigModel cfg, StealthDecision decision, float dt)
		{
			VanillaAlertness currentState = data.CurrentState;
			float detectionLevel = data.DetectionLevel;
			if (detectionLevel >= cfg.EngagedThreshold)
			{
				data.CurrentState = VanillaAlertness.Engaged;
			}
			else if (detectionLevel >= cfg.AlertedThreshold)
			{
				if (data.CurrentState < VanillaAlertness.Alerted)
				{
					data.CurrentState = VanillaAlertness.Alerted;
				}
			}
			else if (detectionLevel >= cfg.SuspiciousThreshold && data.CurrentState < VanillaAlertness.Suspicious)
			{
				data.CurrentState = VanillaAlertness.Suspicious;
			}
			if (data.CurrentState == VanillaAlertness.Engaged && detectionLevel < cfg.AlertedThreshold * 0.7f)
			{
				data.CurrentState = VanillaAlertness.Alerted;
			}
			if (data.CurrentState == VanillaAlertness.Alerted && detectionLevel < cfg.SuspiciousThreshold * 0.5f)
			{
				data.CurrentState = VanillaAlertness.Suspicious;
			}
			if (data.CurrentState == VanillaAlertness.Suspicious && detectionLevel < 0.02f)
			{
				data.CurrentState = VanillaAlertness.Unaware;
			}
			if (data.CurrentState != currentState)
			{
				data.TimeInCurrentState = 0f;
			}
			else
			{
				data.TimeInCurrentState += dt;
			}
			decision.State = data.CurrentState;
			decision.Debug.State = data.CurrentState;
			decision.Debug.DetectionLevel = data.DetectionLevel;
		}

		private static void AssessHealth(MonsterAI ai, Player p, AwarenessData data, StealthConfigModel cfg)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			Character component = ((Component)ai).GetComponent<Character>();
			if (!((Object)(object)component == (Object)null))
			{
				data.SelfHealthPercent = component.GetHealth() / component.GetMaxHealth();
				data.PlayerHealthPercent = ((Character)p).GetHealth() / ((Character)p).GetMaxHealth();
				data.TargetDistance = Vector3.Distance(((Component)component).transform.position, ((Component)p).transform.position);
			}
		}

		private static void EvaluateFleeing(MonsterAI ai, Player p, Character c, AwarenessData data, StealthConfigModel cfg, StealthDecision decision, float dt)
		{
			//IL_0176: 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_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: 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_01a6: 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_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			decision.Flee = default(FleeDirective);
			if (data.SelfHealthPercent < 0.25f && data.CurrentState >= VanillaAlertness.Alerted)
			{
				decision.Flee.ShouldFlee = true;
				decision.Flee.Reason = FleeReason.HealthCritical;
				decision.Flee.HealthThreshold = 0.25f;
				decision.Flee.IsStrategicRetreat = true;
				decision.Flee.ReturnWaitTime = 15f;
				decision.FleeConfidence = 0.95f;
				data.CurrentFleeReason = FleeReason.HealthCritical;
				data.FleeStartHealth = data.SelfHealthPercent;
			}
			else if (data.SelfHealthPercent < 0.5f && data.CurrentState >= VanillaAlertness.Alerted)
			{
				float num = data.SelfHealthPercent / 0.5f;
				float num2 = Mathf.Lerp(0.7f, 0f, num);
				if (num2 > Random.value)
				{
					decision.Flee.ShouldFlee = true;
					decision.Flee.Reason = FleeReason.StrategicRetreat;
					decision.Flee.HealthThreshold = 0.5f;
					decision.Flee.IsStrategicRetreat = true;
					decision.Flee.ReturnWaitTime = 8f;
					decision.FleeConfidence = num2;
					data.CurrentFleeReason = FleeReason.StrategicRetreat;
					data.FleeStartHealth = data.SelfHealthPercent;
				}
				else
				{
					decision.Flee.ShouldFlee = false;
					data.CurrentFleeReason = FleeReason.None;
				}
			}
			else
			{
				decision.Flee.ShouldFlee = false;
				data.CurrentFleeReason = FleeReason.None;
			}
			if (decision.Flee.ShouldFlee)
			{
				Vector3 val = ((Component)c).transform.position - ((Component)p).transform.position;
				Vector3 normalized = ((Vector3)(ref val)).normalized;
				decision.Flee.FleeTarget = ((Component)c).transform.position + normalized * 50f;
				data.FleeDirection = normalized;
				data.TimeSpentFleeing += dt;
			}
			else
			{
				data.TimeSpentFleeing = 0f;
			}
			decision.Debug.FleeReason = data.CurrentFleeReason;
			decision.Debug.FleeThreshold = decision.Flee.HealthThreshold;
			decision.Debug.IsStrategicRetreat = decision.Flee.IsStrategicRetreat;
			decision.Debug.SelfHealthPercent = data.SelfHealthPercent;
			decision.Debug.PlayerHealthPercent = data.PlayerHealthPercent;
		}

		private static void EvaluateNearbyAllies(MonsterAI ai, Character c, AwarenessData data, StealthConfigModel cfg)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			Collider[] array = Physics.OverlapSphere(((Component)c).transform.position, 30f);
			int num = 0;
			Collider[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				Character component = ((Component)array2[i]).GetComponent<Character>();
				if ((Object)(object)component != (Object)null && (Object)(object)component != (Object)(object)c && !component.IsDead() && (Object)(object)((Component)component).GetComponent<MonsterAI>() != (Object)null)
				{
					MonsterAI component2 = ((Component)component).GetComponent<MonsterAI>();
					if ((Object)(object)component2 != (Object)null && !((BaseAI)component2).IsSleeping())
					{
						num++;
					}
				}
			}
			data.NearbyAlliesCount = num;
			data.IsPartOfGroup = num > 0;
		}

		private static void EvaluateMovement(MonsterAI ai, Player p, Character c, AwarenessData data, StealthConfigModel cfg, StealthDecision decision, float dt)
		{
			//IL_0013: 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_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0393: Unknown result type (might be due to invalid IL or missing references)
			//IL_0398: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: 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)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_035a: Unknown result type (might be due to invalid IL or missing references)
			//IL_035b: Unknown result type (might be due to invalid IL or missing references)
			//IL_030f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0310: Unknown result type (might be due to invalid IL or missing references)
			//IL_0311: Unknown result type (might be due to invalid IL or missing references)
			//IL_0316: Unknown result type (might be due to invalid IL or missing references)
			//IL_031f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0321: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0242: Unknown result type (might be due to invalid IL or missing references)
			//IL_0247: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_025a: Unknown result type (might be due to invalid IL or missing references)
			//IL_025f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0263: 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_0271: Unknown result type (might be due to invalid IL or missing references)
			//IL_0273: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0420: Unknown result type (might be due to invalid IL or missing references)
			decision.Movement = default(MovementDirective);
			Vector3 position = ((Component)c).transform.position;
			Vector3 position2 = ((Component)p).transform.position;
			Vector3 val = position2 - position;
			Vector3 normalized = ((Vector3)(ref val)).normalized;
			float num = Vector3.Distance(position, position2);
			if (decision.Flee.ShouldFlee)
			{
				decision.Movement.Direction = data.FleeDirection;
				decision.Movement.Speed = 1f;
				decision.Movement.ShouldRun = true;
				decision.Movement.Strategy = MovementStrategy.AwayFromPlayer;
				data.DesiredDirection = data.FleeDirection;
				data.DesiredSpeed = 1f;
				data.ShouldRun = true;
			}
			else
			{
				switch (data.CurrentState)
				{
				case VanillaAlertness.Unaware:
					decision.Movement.Direction = Vector3.zero;
					decision.Movement.Speed = 0f;
					decision.Movement.ShouldRun = false;
					decision.Movement.Strategy = MovementStrategy.None;
					break;
				case VanillaAlertness.Suspicious:
					if (data.TimeSinceSeen < cfg.SearchDuration && data.LastKnownPosition != Vector3.zero)
					{
						val = data.LastKnownPosition - position;
						Vector3 normalized2 = ((Vector3)(ref val)).normalized;
						decision.Movement.Direction = normalized2;
						decision.Movement.Speed = 0.5f;
						decision.Movement.ShouldRun = false;
						decision.Movement.Strategy = MovementStrategy.TowardLastSeen;
						decision.SearchTarget = data.LastKnownPosition;
						decision.SearchRadius = 10f;
					}
					else
					{
						decision.Movement.Direction = Vector3.zero;
						decision.Movement.Speed = 0f;
						decision.Movement.ShouldRun = false;
						decision.Movement.Strategy = MovementStrategy.None;
					}
					break;
				case VanillaAlertness.Alerted:
					if (decision.CanSense)
					{
						decision.Movement.Direction = normalized;
						decision.Movement.Speed = 1f;
						decision.Movement.ShouldRun = true;
						decision.Movement.Strategy = MovementStrategy.TowardPlayer;
					}
					else if (data.TimeSinceSeen < cfg.SearchDuration && data.LastKnownPosition != Vector3.zero)
					{
						val = data.LastKnownPosition - position;
						Vector3 normalized3 = ((Vector3)(ref val)).normalized;
						decision.Movement.Direction = normalized3;
						decision.Movement.Speed = 1f;
						decision.Movement.ShouldRun = true;
						decision.Movement.Strategy = MovementStrategy.TowardLastSeen;
						decision.SearchTarget = data.LastKnownPosition;
						decision.SearchRadius = 15f;
					}
					else
					{
						decision.Movement.Direction = Vector3.zero;
						decision.Movement.Speed = 0f;
						decision.Movement.ShouldRun = false;
						decision.Movement.Strategy = MovementStrategy.None;
					}
					break;
				case VanillaAlertness.Engaged:
					if (num < 5f)
					{
						Vector3 flankingDirection = GetFlankingDirection(position, position2);
						decision.Movement.Direction = flankingDirection;
						decision.Movement.Speed = 0.7f;
						decision.Movement.ShouldRun = true;
						decision.Movement.Strategy = MovementStrategy.Flanking;
					}
					else
					{
						decision.Movement.Direction = normalized;
						decision.Movement.Speed = 1f;
						decision.Movement.ShouldRun = true;
						decision.Movement.Strategy = MovementStrategy.TowardPlayer;
					}
					break;
				}
				data.DesiredDirection = decision.Movement.Direction;
				data.DesiredSpeed = decision.Movement.Speed;
				data.ShouldRun = decision.Movement.ShouldRun;
			}
			decision.ShouldSearch = (data.CurrentState == VanillaAlertness.Suspicious || data.CurrentState == VanillaAlertness.Alerted) && data.TimeSinceSeen < cfg.SearchDuration && data.LastKnownPosition != Vector3.zero;
			decision.ShouldGiveUp = data.TimeSinceSeen > cfg.GiveUpTime;
			decision.Debug.SuggestedDirection = decision.Movement.Direction;
			decision.Debug.SuggestedSpeed = decision.Movement.Speed;
			decision.Debug.Strategy = decision.Movement.Strategy;
		}

		private static void EvaluateCombat(MonsterAI ai, Player p, Character c, AwarenessData data, StealthConfigModel cfg, StealthDecision decision)
		{
			decision.Combat = default(CombatDirective);
			if (data.CurrentState == VanillaAlertness.Engaged && !decision.Flee.ShouldFlee)
			{
				decision.Combat.ShouldAttack = true;
				decision.Combat.OptimalAttackRange = 2.5f;
				decision.Combat.ShouldKeepDistance = false;
				decision.Combat.AggressionLevel = 1f;
				decision.Combat.FavorDefense = data.SelfHealthPercent < 0.5f;
				decision.Combat.DamageThreshold = 0.25f;
				data.TargetPriority = TargetPriority.Immediate;
				decision.ShouldPursue = true;
			}
			else if (data.CurrentState == VanillaAlertness.Alerted && !decision.Flee.ShouldFlee)
			{
				decision.Combat.ShouldAttack = true;
				decision.Combat.OptimalAttackRange = 3f;
				decision.Combat.ShouldKeepDistance = true;
				decision.Combat.AggressionLevel = 0.7f;
				decision.Combat.FavorDefense = true;
				decision.Combat.DamageThreshold = 0.5f;
				data.TargetPriority = TargetPriority.Confirmed;
				decision.ShouldPursue = true;
			}
			else if (data.CurrentState == VanillaAlertness.Suspicious)
			{
				decision.Combat.ShouldAttack = false;
				decision.Combat.AggressionLevel = 0.3f;
				decision.Combat.FavorDefense = false;
				data.TargetPriority = TargetPriority.Secondary;
				decision.ShouldPursue = false;
			}
			else
			{
				decision.Combat.ShouldAttack = false;
				decision.Combat.AggressionLevel = 0f;
				decision.Combat.FavorDefense = false;
				data.TargetPriority = TargetPriority.None;
				decision.ShouldPursue = false;
			}
			decision.TargetPriority = data.TargetPriority;
			decision.Debug.AggressionLevel = decision.Combat.AggressionLevel;
		}

		private static void EvaluateGroupBehavior(MonsterAI ai, Player p, Character c, AwarenessData data, StealthConfigModel cfg, StealthDecision decision, float dt)
		{
			decision.GroupBehavior = default(GroupBehavior);
			if (data.CurrentState == VanillaAlertness.Engaged && data.IsPartOfGroup)
			{
				data.TimeUntilNextAllyCall -= dt;
				if (data.TimeUntilNextAllyCall <= 0f)
				{
					decision.GroupBehavior.CallForHelp = true;
					decision.GroupBehavior.AlertRadius = 30f;
					decision.GroupBehavior.CoordinatedAttack = data.NearbyAlliesCount >= 2;
					decision.GroupBehavior.MinAlliesForGroupAction = 2;
					data.TimeUntilNextAllyCall = 3f;
				}
			}
			else if (data.CurrentState == VanillaAlertness.Alerted && data.IsPartOfGroup)
			{
				data.TimeUntilNextAllyCall -= dt;
				if (data.TimeUntilNextAllyCall <= 0f && data.NearbyAlliesCount >= 1)
				{
					decision.GroupBehavior.CallForHelp = true;
					decision.GroupBehavior.AlertRadius = 30f;
					decision.GroupBehavior.CoordinatedAttack = false;
					decision.GroupBehavior.MinAlliesForGroupAction = 1;
					data.TimeUntilNextAllyCall = 4.5f;
				}
			}
			else
			{
				decision.GroupBehavior.CallForHelp = false;
			}
			decision.Debug.NearbyAlliesCount = data.NearbyAlliesCount;
			decision.Debug.ShouldCallForHelp = decision.GroupBehavior.CallForHelp;
			decision.Debug.AlertRadius = decision.GroupBehavior.AlertRadius;
		}

		private static void DetermineAction(MonsterAI ai, Player p, Character c, AwarenessData data, StealthConfigModel cfg, StealthDecision decision)
		{
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			if (decision.Flee.ShouldFlee)
			{
				data.CurrentAction = (decision.Flee.IsStrategicRetreat ? AIBehaviorAction.StrategicRetreat : AIBehaviorAction.Flee);
			}
			else if (decision.ShouldGiveUp)
			{
				data.CurrentAction = AIBehaviorAction.Idle;
			}
			else if (data.CurrentState == VanillaAlertness.Unaware)
			{
				data.CurrentAction = AIBehaviorAction.Idle;
			}
			else if (data.CurrentState == VanillaAlertness.Suspicious)
			{
				data.CurrentAction = (decision.ShouldSearch ? AIBehaviorAction.Search : AIBehaviorAction.Investigate);
			}
			else if (data.CurrentState == VanillaAlertness.Alerted)
			{
				data.CurrentAction = (decision.CanSense ? AIBehaviorAction.Pursue : AIBehaviorAction.Search);
			}
			else if (data.CurrentState == VanillaAlertness.Engaged)
			{
				data.CurrentAction = AIBehaviorAction.Attack;
			}
			decision.RecommendedAction = data.CurrentAction;
			data.TimeInCurrentAction += Time.deltaTime;
			if (decision.GroupBehavior.CallForHelp && data.CurrentState >= VanillaAlertness.Alerted)
			{
				Character component = ((Component)ai).GetComponent<Character>();
				if ((Object)(object)component != (Object)null)
				{
					BehaviorSystem.CallNearbyAllies(component, ((Component)component).transform.position, decision.GroupBehavior.AlertRadius, p);
				}
			}
		}

		private static void FinalizeDecision(AwarenessData data, StealthDecision decision)
		{
			decision.Confidence = data.LastConfidence;
			decision.Confidence = Mathf.Clamp01(decision.Confidence);
			decision.DecisionReason = GenerateDecisionReason(data, decision);
			data.LastDecisionReason = decision.DecisionReason;
			data.LastConfidence = decision.Confidence;
			decision.Debug.DecisionReason = decision.DecisionReason;
			decision.Debug.FullDecisionTrace = BuildDecisionTrace(data, decision);
		}

		private static Vector3 GetFlankingDirection(Vector3 aiPos, Vector3 playerPos)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//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_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = playerPos - aiPos;
			Vector3 normalized = ((Vector3)(ref val)).normalized;
			Vector3 val2 = default(Vector3);
			((Vector3)(ref val2))..ctor(0f - normalized.z, 0f, normalized.x);
			if (!(Random.value > 0.5f))
			{
				return -val2;
			}
			return val2;
		}

		private static string GenerateDecisionReason(AwarenessData data, StealthDecision decision)
		{
			if (decision.Flee.ShouldFlee)
			{
				if (!decision.Flee.IsStrategicRetreat)
				{
					return $"Fleeing (Health: {data.SelfHealthPercent:P})";
				}
				return $"Strategic retreat (Health: {data.SelfHealthPercent:P})";
			}
			if (decision.ShouldGiveUp)
			{
				return "Giving up - lost target";
			}
			return data.CurrentState switch
			{
				VanillaAlertness.Unaware => "Unaware - idle", 
				VanillaAlertness.Suspicious => decision.CanHear ? "Suspicious - heard sound" : "Suspicious - investigating", 
				VanillaAlertness.Alerted => decision.CanSee ? "Alerted - pursuing" : "Alerted - searching", 
				VanillaAlertness.Engaged => decision.GroupBehavior.CallForHelp ? "Engaged - calling allies" : "Engaged - attacking", 
				_ => "Unknown state", 
			};
		}

		private static string BuildDecisionTrace(AwarenessData data, StealthDecision decision)
		{
			return $"[State:{data.CurrentState}|Det:{decision.DetectionLevel:F2}|" + $"Act:{data.CurrentAction}|HP:{data.SelfHealthPercent:P}|" + $"Flee:{decision.Flee.ShouldFlee}|Allies:{data.NearbyAlliesCount}]";
		}

		private static void CleanupStaleCache(int currentFrame)
		{
			List<StableAIKey> list = new List<StableAIKey>();
			foreach (KeyValuePair<StableAIKey, CachedEvaluation> item in _evalCache)
			{
				if (currentFrame - item.Value.LastFullEvalFrame > 600)
				{
					list.Add(item.Key);
				}
			}
			foreach (StableAIKey item2 in list)
			{
				_evalCache.Remove(item2);
			}
		}

		public static void ClearCache(MonsterAI ai)
		{
			if (!((Object)(object)ai != (Object)null))
			{
				return;
			}
			int instanceID = ((Object)ai).GetInstanceID();
			StableAIKey key = new StableAIKey(0uL, instanceID);
			if (_evalCache.ContainsKey(key))
			{
				_evalCache.Remove(key);
			}
			try
			{
				ZNetView component = ((Component)ai).GetComponent<ZNetView>();
				if (!((Object)(object)component != (Object)null))
				{
					return;
				}
				ZDO zDO = component.GetZDO();
				if (zDO != null)
				{
					StableAIKey key2 = new StableAIKey((ulong)((ZDOID)(ref zDO.m_uid)).UserID, instanceID);
					if (_evalCache.ContainsKey(key2))
					{
						_evalCache.Remove(key2);
					}
				}
			}
			catch
			{
			}
		}

		public static void ClearAllCaches()
		{
			_evalCache.Clear();
		}
	}
	public class NoiseMeter : MonoBehaviour, IBeginDragHandler, IEventSystemHandler, IDragHandler, IEndDragHandler
	{
		private Image _background;

		private Image _fill;

		private RectTransform _rect;

		private RectTransform _fillRect;

		private float _smoothNoise;

		private const float BAR_WIDTH = 60f;

		private const float BAR_HEIGHT = 8f;

		public static NoiseMeter Create()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("NoiseMeter");
			val.transform.SetParent(((Component)StealthUIRoot.Instance.canvas).transform, false);
			NoiseMeter noiseMeter = val.AddComponent<NoiseMeter>();
			noiseMeter.Build();
			return noiseMeter;
		}

		private void Build()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected O, but got Unknown
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: 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_0178: Unknown result type (might be due to invalid IL or missing references)
			_background = ((Component)this).gameObject.AddComponent<Image>();
			((Graphic)_background).color = new Color(0.1f, 0.1f, 0.1f, 0.7f);
			((Graphic)_background).raycastTarget = true;
			_rect = ((Graphic)_background).rectTransform;
			_rect.sizeDelta = new Vector2(60f, 8f);
			_rect.anchoredPosition = new Vector2(ShadowsOfMidgardConfig.UI_NoisePosX.Value, ShadowsOfMidgardConfig.UI_NoisePosY.Value);
			GameObject val = new GameObject("NoiseFill");
			val.transform.SetParent(((Component)this).transform, false);
			_fill = val.AddComponent<Image>();
			((Graphic)_fill).color = Color.green;
			((Graphic)_fill).raycastTarget = false;
			_fillRect = ((Graphic)_fill).rectTransform;
			_fillRect.anchorMin = new Vector2(0f, 0f);
			_fillRect.anchorMax = new Vector2(0f, 1f);
			_fillRect.pivot = new Vector2(0f, 0.5f);
			_fillRect.offsetMin = new Vector2(1f, 1f);
			_fillRect.offsetMax = new Vector2(1f, -1f);
			_fillRect.sizeDelta = new Vector2(0f, 0f);
		}

		public void UpdateNoise(float noise)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: 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_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: 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)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			_smoothNoise = Mathf.Lerp(_smoothNoise, noise, Time.deltaTime * 8f);
			float num = Mathf.Clamp01(_smoothNoise) * 58f;
			_fillRect.sizeDelta = new Vector2(num, 0f);
			if (_smoothNoise < 0.33f)
			{
				((Graphic)_fill).color = Color.Lerp(Color.green, Color.yellow, _smoothNoise / 0.33f);
			}
			else if (_smoothNoise < 0.66f)
			{
				((Graphic)_fill).color = Color.Lerp(Color.yellow, new Color(1f, 0.5f, 0f), (_smoothNoise - 0.33f) / 0.33f);
			}
			else
			{
				((Graphic)_fill).color = Color.Lerp(new Color(1f, 0.5f, 0f), Color.red, (_smoothNoise - 0.66f) / 0.34f);
			}
		}

		public void SetVisible(bool visible)
		{
			((Component)this).gameObject.SetActive(visible);
		}

		public void OnBeginDrag(PointerEventData eventData)
		{
		}

		public void OnDrag(PointerEventData eventData)
		{
			//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)
			RectTransform rect = _rect;
			rect.anchoredPosition += eventData.delta;
		}

		public void OnEndDrag(PointerEventData eventData)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			ShadowsOfMidgardConfig.UI_NoisePosX.Value = _rect.anchoredPosition.x;
			ShadowsOfMidgardConfig.UI_NoisePosY.Value = _rect.anchoredPosition.y;
			((ConfigEntryBase)ShadowsOfMidgardConfig.UI_NoisePosX).ConfigFile.Save();
		}
	}
	public static class SpriteLoader
	{
		private static readonly Dictionary<string, Sprite> Cache = new Dictionary<string, Sprite>();

		private static MethodInfo _loadImageMethod;

		public static Sprite LoadSprite(string fileName)
		{
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			if (Cache.TryGetValue(fileName, out var value))
			{
				return value;
			}
			string text = Path.Combine(Paths.PluginPath, "ShadowsOfMidgard", "Assets", fileName);
			if (File.Exists(text))
			{
				try
				{
					Texture2D val = DecodePNG(File.ReadAllBytes(text));
					if ((Object)(object)val != (Object)null)
					{
						Sprite val2 = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
						Cache[fileName] = val2;
						return val2;
					}
					Debug.LogWarning((object)("SpriteLoader: Failed to decode PNG from disk: " + fileName + " - will attempt embedded resource."));
				}
				catch (Exception arg)
				{
					Debug.LogWarning((object)$"SpriteLoader: Error reading disk file {text}: {arg}");
				}
			}
			byte[] embeddedResourceBytes = GetEmbeddedResourceBytes(fileName);
			if (embeddedResourceBytes != null)
			{
				Texture2D val3 = DecodePNG(embeddedResourceBytes);
				if ((Object)(object)val3 == (Object)null)
				{
					Debug.LogError((object)("SpriteLoader: Failed to decode embedded PNG: " + fileName));
					return null;
				}
				try
				{
					Directory.CreateDirectory(Path.GetDirectoryName(text));
					File.WriteAllBytes(text, embeddedResourceBytes);
				}
				catch (Exception arg2)
				{
					Debug.LogWarning((object)$"SpriteLoader: Could not write embedded asset to disk {text}: {arg2}");
				}
				Sprite val4 = Sprite.Create(val3, new Rect(0f, 0f, (float)((Texture)val3).width, (float)((Texture)val3).height), new Vector2(0.5f, 0.5f));
				Cache[fileName] = val4;
				return val4;
			}
			Debug.LogError((object)("SpriteLoader: File not found and embedded resource missing: " + text));
			return null;
		}

		public static Sprite LoadEmbeddedPNG(string resourceName)
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			if (Cache.TryGetValue(resourceName, out var value))
			{
				return value;
			}
			byte[] embeddedResourceBytes = GetEmbeddedResourceBytes(resourceName);
			if (embeddedResourceBytes == null)
			{
				Debug.LogError((object)("SpriteLoader: Embedded resource not found: " + resourceName));
				return null;
			}
			Texture2D val = DecodePNG(embeddedResourceBytes);
			if ((Object)(object)val == (Object)null)
			{
				Debug.LogError((object)(