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 EnemySpawnControl v1.0.0
BepInEx/plugins/EnemySpawnControl-1.0.0.dll
Decompiled 2 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("EnemySpawnControl")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+930ccf738288baaceab0ae7e541eae9cdbb446b7")] [assembly: AssemblyProduct("EnemySpawnControl")] [assembly: AssemblyTitle("EnemySpawnControl")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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 EnemySpawnControl { [BepInPlugin("com.out_pu22led.EnemySpawnControl", "Enemy Spawn Control", "1.0.0")] public class Plugin : BaseUnityPlugin { private const string PLUGIN_GUID = "com.out_pu22led.EnemySpawnControl"; public static ConfigEntry<bool> UseWhitelist = null; public static ConfigEntry<string> AllowedEnemies = null; public static ConfigEntry<string> BlockedEnemies = null; public static ConfigEntry<bool> EnableLogging = null; public static ManualLogSource Log = null; private static HashSet<string> _allowedCache = null; private static HashSet<string> _blockedCache = null; private static readonly Dictionary<string, string> _aliases = new Dictionary<string, string> { { "animal", "animal" }, { "banger", "bangdirector" }, { "clown", "beamer" }, { "birthdayboy", "birthdayboy" }, { "cleanupcrew", "bombthrower" }, { "bowtie", "bowtie" }, { "peeper", "ceilingeye" }, { "apexpredator", "duck" }, { "elsa", "elsa" }, { "mentalist", "floater" }, { "gnome", "gnomedirector" }, { "headman", "head" }, { "headgrab", "headgrabber" }, { "hearthugger", "hearthugger" }, { "hidden", "hidden" }, { "huntsman", "hunter" }, { "oogly", "oogly" }, { "robe", "robe" }, { "reaper", "runner" }, { "loom", "shadow" }, { "spewer", "slowmouth" }, { "trudge", "slowwalker" }, { "gambit", "spinny" }, { "shadowchild", "thinman" }, { "tick", "tick" }, { "bella", "tricycle" }, { "chef", "tumbler" }, { "upscream", "upscream" }, { "rugrat", "valuablethrower" } }; private void Awake() { Log = ((BaseUnityPlugin)this).Logger; UseWhitelist = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "UseWhitelist", false, "If true, only spawn enemies in AllowedEnemies list. If false, block enemies in BlockedEnemies list."); AllowedEnemies = ((BaseUnityPlugin)this).Config.Bind<string>("Whitelist", "AllowedEnemies", "Gnome,Runner,Duck", "Comma-separated list of allowed enemy names (case-insensitive). Press F7 in-game to see all enemy names in log."); BlockedEnemies = ((BaseUnityPlugin)this).Config.Bind<string>("Blacklist", "BlockedEnemies", "Shadow,Hunter,ThinMan", "Comma-separated list of blocked enemy names (case-insensitive). Press F7 in-game to see all enemy names in log."); EnableLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "EnableLogging", true, "Enable detailed logging of enemy spawn filtering"); RebuildCaches(); Harmony.CreateAndPatchAll(typeof(EnemyDirectorPatches), (string)null); ((BaseUnityPlugin)this).Logger.LogInfo((object)"================================="); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Enemy Spawn Control Loaded!"); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Mode: " + (UseWhitelist.Value ? "WHITELIST" : "BLACKLIST"))); if (UseWhitelist.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Allowed: " + AllowedEnemies.Value)); } else { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Blocked: " + BlockedEnemies.Value)); } ((BaseUnityPlugin)this).Logger.LogInfo((object)"Press F7 to list all available enemies"); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Press Ctrl+R to reload config"); ((BaseUnityPlugin)this).Logger.LogInfo((object)"================================="); } private void Update() { if (Input.GetKeyDown((KeyCode)288)) { ListAllEnemyTypes(); } if (Input.GetKey((KeyCode)306) && Input.GetKeyDown((KeyCode)114)) { ((BaseUnityPlugin)this).Config.Reload(); RebuildCaches(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"================================="); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Config Reloaded!"); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Mode: " + (UseWhitelist.Value ? "WHITELIST" : "BLACKLIST"))); if (UseWhitelist.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Allowed: " + AllowedEnemies.Value)); } else { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Blocked: " + BlockedEnemies.Value)); } ((BaseUnityPlugin)this).Logger.LogInfo((object)"================================="); } } public static void RebuildCaches() { _allowedCache = new HashSet<string>(from s in AllowedEnemies.Value.Split(',') select ResolveAlias(s.ToLower().Trim().Replace(" ", "")), StringComparer.OrdinalIgnoreCase); _blockedCache = new HashSet<string>(from s in BlockedEnemies.Value.Split(',') select ResolveAlias(s.ToLower().Trim().Replace(" ", "")), StringComparer.OrdinalIgnoreCase); } private static string ResolveAlias(string name) { if (!_aliases.TryGetValue(name, out string value)) { return name; } return value; } public static string? GetSpawnName(EnemySetup? setup) { List<PrefabRef> list = setup?.spawnObjects; if (list == null || list.Count <= 0) { return null; } return list[0].PrefabName; } public static string NormalizeName(string rawName) { string text = rawName.Replace("(Clone)", "").Trim(); if (text.StartsWith("Enemy", StringComparison.OrdinalIgnoreCase)) { string text2 = text; text = text2.Substring(5, text2.Length - 5).Trim(); } if (text.StartsWith('-')) { string text2 = text; text = text2.Substring(1, text2.Length - 1).Trim(); } return text.ToLower().Replace(" ", ""); } private void ListAllEnemyTypes() { if (!Object.op_Implicit((Object)(object)EnemyDirector.instance)) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"EnemyDirector not found! Start a game first."); return; } ((BaseUnityPlugin)this).Logger.LogInfo((object)"================================="); ((BaseUnityPlugin)this).Logger.LogInfo((object)"ALL ENEMY TYPES (use these names in config):"); ((BaseUnityPlugin)this).Logger.LogInfo((object)"================================="); HashSet<string> hashSet = new HashSet<string>(); List<EnemySetup>[] array = new List<EnemySetup>[3] { EnemyDirector.instance.enemiesDifficulty1, EnemyDirector.instance.enemiesDifficulty2, EnemyDirector.instance.enemiesDifficulty3 }; for (int i = 0; i < array.Length; i++) { foreach (EnemySetup item in array[i]) { string spawnName = GetSpawnName(item); if (spawnName != null) { hashSet.Add(NormalizeName(spawnName)); } } } List<string> list = hashSet.OrderBy((string x) => x).ToList(); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Found {list.Count} unique enemy types:"); foreach (string item2 in list) { ((BaseUnityPlugin)this).Logger.LogInfo((object)(" - " + item2)); } ((BaseUnityPlugin)this).Logger.LogInfo((object)"================================="); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Copy these names to your config file!"); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Config location: BepInEx\\config\\com.out_pu22led.EnemySpawnControl.cfg"); ((BaseUnityPlugin)this).Logger.LogInfo((object)"================================="); } public static bool IsEnemyAllowed(string enemyName) { if (string.IsNullOrEmpty(enemyName)) { return true; } string text = NormalizeName(enemyName); if (UseWhitelist.Value) { bool flag = _allowedCache.Contains(text); if (EnableLogging.Value && !flag) { Log.LogInfo((object)("[BLOCKED] " + text + " (not in whitelist)")); } return flag; } bool flag2 = _blockedCache.Contains(text); if (EnableLogging.Value && flag2) { Log.LogInfo((object)("[BLOCKED] " + text + " (in blacklist)")); } return !flag2; } } [HarmonyPatch] public class EnemyDirectorPatches { [HarmonyPrefix] [HarmonyPatch(typeof(EnemyDirector), "AmountSetup")] private static void AmountSetup_Prefix(EnemyDirector __instance) { Plugin.Log.LogInfo((object)"================================="); Plugin.Log.LogInfo((object)"Filtering enemy spawn lists..."); int count = __instance.enemiesDifficulty1.Count; int count2 = __instance.enemiesDifficulty2.Count; int count3 = __instance.enemiesDifficulty3.Count; __instance.enemiesDifficulty1 = __instance.enemiesDifficulty1.Where(delegate(EnemySetup setup) { string spawnName2 = Plugin.GetSpawnName(setup); return spawnName2 == null || Plugin.IsEnemyAllowed(spawnName2); }).ToList(); __instance.enemiesDifficulty2 = __instance.enemiesDifficulty2.Where(delegate(EnemySetup setup) { string spawnName2 = Plugin.GetSpawnName(setup); return spawnName2 == null || Plugin.IsEnemyAllowed(spawnName2); }).ToList(); __instance.enemiesDifficulty3 = __instance.enemiesDifficulty3.Where(delegate(EnemySetup setup) { string spawnName2 = Plugin.GetSpawnName(setup); return spawnName2 == null || Plugin.IsEnemyAllowed(spawnName2); }).ToList(); Plugin.Log.LogInfo((object)$"Difficulty 1: {count} -> {__instance.enemiesDifficulty1.Count}"); Plugin.Log.LogInfo((object)$"Difficulty 2: {count2} -> {__instance.enemiesDifficulty2.Count}"); Plugin.Log.LogInfo((object)$"Difficulty 3: {count3} -> {__instance.enemiesDifficulty3.Count}"); if (Plugin.EnableLogging.Value) { HashSet<string> hashSet = new HashSet<string>(); List<EnemySetup>[] array = new List<EnemySetup>[3] { __instance.enemiesDifficulty1, __instance.enemiesDifficulty2, __instance.enemiesDifficulty3 }; for (int num = 0; num < array.Length; num++) { foreach (EnemySetup item in array[num]) { string spawnName = Plugin.GetSpawnName(item); if (spawnName != null) { hashSet.Add(Plugin.NormalizeName(spawnName)); } } } Plugin.Log.LogInfo((object)"Remaining enemies:"); foreach (string item2 in hashSet.OrderBy((string x) => x)) { Plugin.Log.LogInfo((object)(" - " + item2)); } } Plugin.Log.LogInfo((object)"================================="); } [HarmonyPostfix] [HarmonyPatch(typeof(EnemyDirector), "AmountSetup")] private static void AmountSetup_Postfix(EnemyDirector __instance) { List<EnemySetup> list = (List<EnemySetup>)AccessTools.Field(typeof(EnemyDirector), "enemyList").GetValue(__instance); if (list != null) { int count = list.Count; list.RemoveAll((EnemySetup e) => (Object)(object)e == (Object)null); int num = count - list.Count; if (num > 0) { FieldInfo fieldInfo = AccessTools.Field(typeof(EnemyDirector), "totalAmount"); int num2 = (int)fieldInfo.GetValue(__instance); num2 -= num; fieldInfo.SetValue(__instance, num2); Plugin.Log.LogInfo((object)$"Removed {num} null slot(s) from enemyList. Adjusted totalAmount: {num2}"); } } } } }