Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Shadows Of Midgard v1.0.0
ShadowsOfMidgard/plugins/ShadowsOfMidgard.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
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)(