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 AlertCreatures v1.0.1
AlertCreatures.dll
Decompiled 2 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using ConditionalConfigSync; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("AlertCreatures")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyInformationalVersion("1.1.0")] [assembly: AssemblyProduct("AlertCreatures")] [assembly: AssemblyTitle("AlertCreatures")] [assembly: AssemblyVersion("1.1.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace AlertCreatures { [BepInPlugin("com.dreich.alertcreatures", "Alert Creatures", "1.0.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class AlertCreaturesPlugin : BaseUnityPlugin { public const string Guid = "com.dreich.alertcreatures"; public const string Name = "Alert Creatures"; public const string Version = "1.0.1"; private const string ConfigSyncGuid = "_shudnal.ConditionalConfigSync"; private const string GhettoNetworkGuid = "com.Fire.FiresGhettoNetworkMod"; private static bool ConfigSyncAvailable; internal static ConfigEntry<bool> ModEnabled; internal static ConfigEntry<bool> UsePeerPositions; internal static ConfigEntry<string> ExcludedCreatures; internal static HashSet<string> Excluded = new HashSet<string>(StringComparer.OrdinalIgnoreCase); internal static int ExcludedVersion; internal static ScanSettings Animals; internal static ScanSettings Enemies; internal static ScanSettings Tamed; private void Awake() { //IL_012e: Unknown result type (might be due to invalid IL or missing references) ConfigSyncAvailable = Chainloader.PluginInfos.ContainsKey("_shudnal.ConditionalConfigSync"); if (ConfigSyncAvailable) { ConfigSyncCompat.Initialize("com.dreich.alertcreatures", "Alert Creatures", "1.0.1"); } ConfigEntry<bool> entry = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Lock Configuration", true, "If on, the configuration is locked and can be changed by server admins only. Requires ConditionalConfigSync."); if (ConfigSyncAvailable) { ConfigSyncCompat.AddLockingConfigEntry(entry); } ModEnabled = BindShared("General", "Enabled", defaultValue: true, "Turns the whole mod on or off. When off, every creature looks for targets on the vanilla schedule."); UsePeerPositions = BindShared("General", "UsePeerPositionsOnServer", defaultValue: true, "Dedicated server only. Also counts each connected player's reported position when checking whether a player is close, not just the player characters loaded on the server. Matters when the server runs creatures itself, for example with server-side creature ownership from Fire's Ghetto Network Mod."); ExcludedCreatures = BindShared("General", "ExcludedCreatures", "Hen,Chicken", "Comma-separated creature prefab names that always use the vanilla schedule, whether tamed or wild. Leave empty to exclude nothing."); ExcludedCreatures.SettingChanged += delegate { ParseExcluded(); }; ParseExcluded(); Animals = BindScanSettings("Animals", "untamed wildlife that flees, such as deer, hares and other creatures driven by AnimalAI", "every 2 seconds"); Enemies = BindScanSettings("Enemies", "untamed creatures driven by MonsterAI, such as greydwarfs, trolls, and wild boars, wolves and lox", "every 2 seconds with a player within 50 m, otherwise every 6 seconds"); Tamed = BindScanSettings("Tamed", "tamed creatures of any kind, so they spot threats to you and your base sooner", "every 2 seconds for most tames"); new Harmony("com.dreich.alertcreatures").PatchAll(); } private static void ParseExcluded() { HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); string[] array = (ExcludedCreatures.Value ?? "").Split(new char[1] { ',' }); foreach (string text in array) { if (text.Trim().Length > 0) { hashSet.Add(text.Trim()); } } Excluded = hashSet; ExcludedVersion++; } private ScanSettings BindScanSettings(string section, string who, string vanilla) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Expected O, but got Unknown ScanSettings scanSettings = new ScanSettings(); scanSettings.Enabled = BindShared(section, "Enabled", defaultValue: true, "Makes " + who + " look for targets more often while a player is close. Vanilla checks " + vanilla + "."); scanSettings.PlayerRange = BindShared(section, "PlayerRange", 25f, new ConfigDescription("Distance in metres from the nearest player within which the faster checks are used. This only decides how often the creature looks: it still has to hear or see a target with its own hearing and view ranges to react.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>())); scanSettings.ScanInterval = BindShared(section, "ScanInterval", 0.5f, new ConfigDescription("Seconds between target checks while a player is within PlayerRange. Values below 0.5 gain little in multiplayer, because a player's noise level is only shared every 0.5 seconds. The game runs creature AI 20 times a second, so 0.05 is the fastest possible.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 2f), Array.Empty<object>())); return scanSettings; } private ConfigEntry<T> BindShared<T>(string section, string key, T defaultValue, string description) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown return BindShared(section, key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>())); } private ConfigEntry<T> BindShared<T>(string section, string key, T defaultValue, ConfigDescription description) { ConfigEntry<T> val = ((BaseUnityPlugin)this).Config.Bind<T>(section, key, defaultValue, description); if (ConfigSyncAvailable) { ConfigSyncCompat.AddConfigEntry<T>(val); } return val; } } internal class ScanSettings { internal ConfigEntry<bool> Enabled; internal ConfigEntry<float> PlayerRange; internal ConfigEntry<float> ScanInterval; } internal static class ConfigSyncCompat { private static object configSync; [MethodImpl(MethodImplOptions.NoInlining)] internal static void Initialize(string guid, string name, string version) { //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_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown configSync = (object)new ConfigSync(guid) { DisplayName = name, CurrentVersion = version, MinimumRequiredVersion = version, ModRequired = true }; } [MethodImpl(MethodImplOptions.NoInlining)] internal static void AddLockingConfigEntry(ConfigEntry<bool> entry) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) ((ConditionalConfigSync)(ConfigSync)configSync).AddLockingConfigEntry<bool>(entry); } [MethodImpl(MethodImplOptions.NoInlining)] internal static void AddConfigEntry<T>(ConfigEntry<T> entry) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) ((ConditionalConfigSync)(ConfigSync)configSync).AddConfigEntry<T>(entry, (ConfigSyncMode)0); } } internal static class ScanTimer { private class State { internal bool HoldsScanWait = true; internal bool IsExcluded; internal int ExcludedVersion = -1; } private static readonly ConditionalWeakTable<BaseAI, State> States = new ConditionalWeakTable<BaseAI, State>(); private static readonly FieldRef<BaseAI, Character> CharacterRef = AccessTools.FieldRefAccess<BaseAI, Character>("m_character"); internal static void AfterUpdate(BaseAI ai, ref float timer, float before, ScanSettings untamed, float vanillaNear, float vanillaFar) { //IL_00cb: Unknown result type (might be due to invalid IL or missing references) State orCreateValue = States.GetOrCreateValue(ai); if (timer > before) { orCreateValue.HoldsScanWait = Mathf.Approximately(timer, vanillaNear) || Mathf.Approximately(timer, vanillaFar); } if (!AlertCreaturesPlugin.ModEnabled.Value || !orCreateValue.HoldsScanWait) { return; } if (orCreateValue.ExcludedVersion != AlertCreaturesPlugin.ExcludedVersion) { orCreateValue.ExcludedVersion = AlertCreaturesPlugin.ExcludedVersion; orCreateValue.IsExcluded = AlertCreaturesPlugin.Excluded.Contains(Utils.GetPrefabName(((Component)ai).gameObject)); } if (orCreateValue.IsExcluded) { return; } Character val = CharacterRef.Invoke(ai); ScanSettings scanSettings = (((Object)(object)val != (Object)null && val.IsTamed()) ? AlertCreaturesPlugin.Tamed : untamed); if (scanSettings.Enabled.Value) { float value = scanSettings.ScanInterval.Value; if (timer > value && IsPlayerNear(((Component)ai).transform.position, scanSettings.PlayerRange.Value)) { timer = value; } } } private static bool IsPlayerNear(Vector3 position, float range) { //IL_0000: 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) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) if (Player.IsPlayerInRange(position, range)) { return true; } ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null || !instance.IsDedicated() || !AlertCreaturesPlugin.UsePeerPositions.Value) { return false; } float num = range * range; foreach (ZNetPeer peer in instance.GetPeers()) { Vector3 val = peer.GetRefPos() - position; if (((Vector3)(ref val)).sqrMagnitude < num) { return true; } } return false; } } [HarmonyPatch(typeof(AnimalAI), "UpdateAI")] internal static class AnimalAIScanPatch { private static readonly FieldRef<AnimalAI, float> Timer = AccessTools.FieldRefAccess<AnimalAI, float>("m_updateTargetTimer"); private static void Prefix(AnimalAI __instance, out float __state) { __state = Timer.Invoke(__instance); } private static void Postfix(AnimalAI __instance, bool __result, float __state) { if (!__result) { return; } try { ScanTimer.AfterUpdate((BaseAI)(object)__instance, ref Timer.Invoke(__instance), __state, AlertCreaturesPlugin.Animals, 2f, 10f); } catch { } } } [HarmonyPatch(typeof(MonsterAI), "UpdateAI")] internal static class MonsterAIScanPatch { private static readonly FieldRef<MonsterAI, float> Timer = AccessTools.FieldRefAccess<MonsterAI, float>("m_updateTargetTimer"); private static void Prefix(MonsterAI __instance, out float __state) { __state = Timer.Invoke(__instance); } private static void Postfix(MonsterAI __instance, bool __result, float __state) { if (!__result || ((BaseAI)__instance).IsSleeping()) { return; } try { ScanTimer.AfterUpdate((BaseAI)(object)__instance, ref Timer.Invoke(__instance), __state, AlertCreaturesPlugin.Enemies, 2f, 6f); } catch { } } } }