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 GuaranteedInteractables v0.2.1
GuaranteedInteractables.dll
Decompiled 18 hours agousing System; using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using RoR2; using UnityEngine; [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = "")] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: CompilationRelaxations(8)] [assembly: AssemblyVersion("0.0.0.0")] namespace GuaranteedInteractables; [BepInPlugin("com.goose.guaranteedinteractables", "Guaranteed Interactables", "0.2.1")] public sealed class Plugin : BaseUnityPlugin { private enum InteractableKind { WhitePrinter, GreenPrinter, RedPrinter, YellowPrinter, Scrapper, VoidCradle, GunnerDrone, HealingDrone, SmallChest, LargeChest, EquipmentChest, CategoryChest, ChanceShrine } public const string PluginGuid = "com.goose.guaranteedinteractables"; public const string PluginName = "Guaranteed Interactables"; public const string PluginVersion = "0.2.1"; private readonly Dictionary<InteractableKind, DirectorCard> cards = new Dictionary<InteractableKind, DirectorCard>(); private readonly Dictionary<InteractableKind, int> spawned = new Dictionary<InteractableKind, int>(); private readonly Dictionary<InteractableKind, ConfigEntry<int>> minimums = new Dictionary<InteractableKind, ConfigEntry<int>>(); private ConfigEntry<int> totalRandomDrones; private ConfigEntry<int> totalRandomPrinters; private ConfigEntry<int> totalRandomChests; private readonly Dictionary<InteractableKind, ConfigEntry<bool>> poolEnabled = new Dictionary<InteractableKind, ConfigEntry<bool>>(); private static readonly InteractableKind[] PrinterKinds = new InteractableKind[4] { InteractableKind.WhitePrinter, InteractableKind.GreenPrinter, InteractableKind.RedPrinter, InteractableKind.YellowPrinter }; private static readonly InteractableKind[] RandomChestKinds = new InteractableKind[4] { InteractableKind.SmallChest, InteractableKind.LargeChest, InteractableKind.EquipmentChest, InteractableKind.CategoryChest }; private static readonly InteractableKind[] DroneKinds = new InteractableKind[2] { InteractableKind.GunnerDrone, InteractableKind.HealingDrone }; private void Awake() { minimums[InteractableKind.WhitePrinter] = BindMinimum("White item printers", 1); minimums[InteractableKind.GreenPrinter] = BindMinimum("Green item printers", 1); minimums[InteractableKind.RedPrinter] = BindMinimum("Red item printers", 1); minimums[InteractableKind.YellowPrinter] = BindMinimum("Yellow item printers", 1); minimums[InteractableKind.Scrapper] = BindMinimum("Scrappers", 1); minimums[InteractableKind.VoidCradle] = BindMinimum("Void Cradles", 1); minimums[InteractableKind.GunnerDrone] = BindMinimum("Gunner Drones", 0); minimums[InteractableKind.HealingDrone] = BindMinimum("Healing Drones", 0); minimums[InteractableKind.SmallChest] = BindMinimum("Small Chests", 0); minimums[InteractableKind.LargeChest] = BindMinimum("Large Chests", 0); minimums[InteractableKind.EquipmentChest] = BindMinimum("Equipment Chests", 0); minimums[InteractableKind.CategoryChest] = BindMinimum("Category Chests", 0); minimums[InteractableKind.ChanceShrine] = BindMinimum("Shrines of Chance", 0); totalRandomDrones = BindMinimum("Total random drones", 0); totalRandomPrinters = BindMinimum("Total random printers", 0); totalRandomChests = BindMinimum("Total random chests", 0); BindPool("Random printer pool", PrinterKinds, defaultValue: true); BindPool("Random chest pool", RandomChestKinds, defaultValue: true); BindPool("Random drone pool", DroneKinds, defaultValue: true); SceneDirector.onPrePopulateSceneServer += OnPrePopulateSceneServer; SceneDirector.onGenerateInteractableCardSelection += OnGenerateInteractableCardSelection; SceneDirector.onSpawnInteractableServer += OnSpawnInteractableServer; SceneDirector.onPostPopulateSceneServer += OnPostPopulateSceneServer; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Guaranteed Interactables loaded."); } private ConfigEntry<int> BindMinimum(string name, int defaultValue) { return ((BaseUnityPlugin)this).Config.Bind<int>("Guaranteed interactables", name, defaultValue, "Minimum present after vanilla interactable generation. Set to 0 to disable."); } private void BindPool(string section, InteractableKind[] kinds, bool defaultValue) { foreach (InteractableKind interactableKind in kinds) { poolEnabled[interactableKind] = ((BaseUnityPlugin)this).Config.Bind<bool>(section, "Include " + GetDisplayName(interactableKind), defaultValue, "Allow this type to be selected by the matching Total random setting."); } } private static string GetDisplayName(InteractableKind kind) { return kind.ToString(); } private void OnDestroy() { SceneDirector.onPrePopulateSceneServer -= OnPrePopulateSceneServer; SceneDirector.onGenerateInteractableCardSelection -= OnGenerateInteractableCardSelection; SceneDirector.onSpawnInteractableServer -= OnSpawnInteractableServer; SceneDirector.onPostPopulateSceneServer -= OnPostPopulateSceneServer; } private void OnPrePopulateSceneServer(SceneDirector sceneDirector) { cards.Clear(); spawned.Clear(); foreach (InteractableKind value in Enum.GetValues(typeof(InteractableKind))) { spawned[value] = 0; } } private void OnGenerateInteractableCardSelection(SceneDirector sceneDirector, DirectorCardCategorySelection selection) { //IL_002b: 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) if (!IsNormalStage() || (Object)(object)selection == (Object)null || selection.categories == null) { return; } Category[] categories = selection.categories; for (int i = 0; i < categories.Length; i++) { Category val = categories[i]; if (val.cards != null) { DirectorCard[] array = val.cards; foreach (DirectorCard directorCard in array) { CacheCard(directorCard); } } } } private void CacheCard(DirectorCard directorCard) { InteractableSpawnCard val = (InteractableSpawnCard)((directorCard != null) ? /*isinst with value type is only supported in some contexts*/: null); if (!((Object)(object)val == (Object)null) && !((Object)(object)((SpawnCard)val).prefab == (Object)null) && TryGetKind(val, out var kind)) { cards[kind] = directorCard; } } private void OnSpawnInteractableServer(SceneDirector sceneDirector, GameObject spawnedObject, DirectorCard card) { InteractableSpawnCard val = (InteractableSpawnCard)((card != null) ? /*isinst with value type is only supported in some contexts*/: null); if (!((Object)(object)val == (Object)null) && !((Object)(object)((SpawnCard)val).prefab == (Object)null) && TryGetKind(val, out var kind)) { spawned[kind] = ((!spawned.ContainsKey(kind)) ? 1 : (spawned[kind] + 1)); } } private void OnPostPopulateSceneServer(SceneDirector sceneDirector) { if (!IsNormalStage()) { return; } foreach (KeyValuePair<InteractableKind, ConfigEntry<int>> minimum in minimums) { InteractableKind key = minimum.Key; if (IsAvailableInThisRun(key)) { int num = Math.Max(0, minimum.Value.Value); int num2 = (spawned.ContainsKey(key) ? spawned[key] : 0); for (int i = num2; i < num; i++) { TrySpawnGuaranteed(key); } } } EnsureRandomMinimum("drones", totalRandomDrones.Value, DroneKinds); EnsureRandomMinimum("printers", totalRandomPrinters.Value, PrinterKinds); EnsureRandomMinimum("chests", totalRandomChests.Value, RandomChestKinds); } private void EnsureRandomMinimum(string label, int desired, InteractableKind[] kinds) { desired = Math.Max(0, desired); int num = 0; List<InteractableKind> list = new List<InteractableKind>(); foreach (InteractableKind interactableKind in kinds) { num += (spawned.ContainsKey(interactableKind) ? spawned[interactableKind] : 0); if (IsAvailableInThisRun(interactableKind) && cards.ContainsKey(interactableKind) && IsPoolEnabled(interactableKind)) { list.Add(interactableKind); } } if (list.Count == 0) { if (num < desired) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("No eligible " + label + " spawn cards were available on this stage.")); } return; } int num2 = num; int num3 = Math.Max(20, (desired - num) * 10); while (num2 < desired && num3-- > 0) { InteractableKind kind = list[Random.Range(0, list.Count)]; if (TrySpawnGuaranteed(kind)) { num2++; } } if (num2 < desired) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could only place " + num2 + " of " + desired + " requested random " + label + ".")); } } private bool IsPoolEnabled(InteractableKind kind) { if (poolEnabled.TryGetValue(kind, out var value)) { return value.Value; } return true; } private bool TrySpawnGuaranteed(InteractableKind kind) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Expected O, but got Unknown if (!cards.TryGetValue(kind, out var value) || value == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)string.Concat("Could not find a spawn card for ", kind, " on this stage.")); return false; } SpawnCard spawnCard = value.spawnCard; InteractableSpawnCard val = (InteractableSpawnCard)(object)((spawnCard is InteractableSpawnCard) ? spawnCard : null); if ((Object)(object)val == (Object)null) { return false; } DirectorPlacementRule val2 = new DirectorPlacementRule(); val2.placementMode = (PlacementMode)4; val2.minDistance = 20f; val2.maxDistance = 120f; val2.preventOverhead = true; DirectorPlacementRule val3 = val2; DirectorSpawnRequest val4 = new DirectorSpawnRequest((SpawnCard)(object)val, val3, Run.instance.spawnRng); GameObject val5 = DirectorCore.instance.TrySpawnObject(val4); if ((Object)(object)val5 == (Object)null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)string.Concat("No valid position was found for guaranteed ", kind, ".")); return false; } ((BaseUnityPlugin)this).Logger.LogInfo((object)string.Concat("Spawned guaranteed ", kind, ".")); return true; } private static bool IsNormalStage() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Invalid comparison between Unknown and I4 SceneInfo instance = SceneInfo.instance; SceneDef val = (((Object)(object)instance != (Object)null) ? instance.sceneDef : null); if ((Object)(object)val != (Object)null && (int)val.sceneType == 1) { return val.validForRandomSelection; } return false; } private static bool IsAvailableInThisRun(InteractableKind kind) { if (kind == InteractableKind.VoidCradle) { if ((Object)(object)Run.instance != (Object)null) { return Run.instance.IsExpansionEnabled(DLC1Content.DLC1); } return false; } return true; } private static bool TryGetKind(InteractableSpawnCard card, out InteractableKind kind) { if (!TryGetKind(((Object)((SpawnCard)card).prefab).name, out kind)) { return TryGetKind(((Object)card).name, out kind); } return true; } private static bool TryGetKind(string name, out InteractableKind kind) { switch (name) { case "Duplicator": kind = InteractableKind.WhitePrinter; return true; case "DuplicatorLarge": kind = InteractableKind.GreenPrinter; return true; case "DuplicatorMilitary": kind = InteractableKind.RedPrinter; return true; case "DuplicatorWild": kind = InteractableKind.YellowPrinter; return true; case "Scrapper": kind = InteractableKind.Scrapper; return true; case "VoidChest": kind = InteractableKind.VoidCradle; return true; case "Drone1Broken": kind = InteractableKind.GunnerDrone; return true; case "Drone2Broken": kind = InteractableKind.HealingDrone; return true; case "Chest1": kind = InteractableKind.SmallChest; return true; case "Chest2": kind = InteractableKind.LargeChest; return true; case "EquipmentBarrel": kind = InteractableKind.EquipmentChest; return true; default: if (name.StartsWith("CategoryChest") || name.StartsWith("iscCategoryChest")) { kind = InteractableKind.CategoryChest; return true; } if (name.StartsWith("ShrineChance") || name.StartsWith("iscShrineChance")) { kind = InteractableKind.ChanceShrine; return true; } kind = InteractableKind.WhitePrinter; return false; } } }