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 Loot Topup v1.2.0
LootTopup.dll
Decompiled an hour agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using REPOLib.Modules; 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: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: AssemblyCompany("LootTopup")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("LootTopup")] [assembly: AssemblyTitle("LootTopup")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [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 LootTopup { [BepInPlugin("themorningstar.loottopup", "Loot Topup", "1.2.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { internal struct TemplateInfo { public GameObject prefab; public string resourceId; public PrefabRef repoLibRef; public float value; } public const string Guid = "themorningstar.loottopup"; internal static ManualLogSource Log = null; internal static ConfigEntry<float> TargetMultiplier; internal static ConfigEntry<int> MinValuables; internal static ConfigEntry<bool> WatchdogEnabled; internal static ConfigEntry<float> WatchdogInterval; internal static ConfigEntry<bool> RespectHuntingSeason; internal static ConfigEntry<string> SkipMutatorNames; internal static ConfigEntry<int> MaxSpawnsPerLevel; internal static bool ToppedUpThisLevel; internal static int SpawnedThisLevel; internal static readonly List<TemplateInfo> Templates = new List<TemplateInfo>(); internal static bool Busy; internal static readonly HashSet<string> SpawnBlacklist = new HashSet<string>(); private void Awake() { //IL_00fd: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; TargetMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Topup", "TargetMultiplier", 1.3f, "Top up until achievable value is at least haulGoal * this. 1.0 = exactly completable; 1.3 = 30% buffer."); MinValuables = ((BaseUnityPlugin)this).Config.Bind<int>("Topup", "MinValuables", 8, "Guarantee at least this many valuables on the map at level start (anti-fragility for sparse maps). 0 disables."); WatchdogEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Watchdog", "Enabled", true, "Detect mid-level softlocks (too much value destroyed to finish) and top up so the run stays winnable."); WatchdogInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Watchdog", "IntervalSeconds", 4f, "How often the watchdog re-checks completability during a level."); RespectHuntingSeason = ((BaseUnityPlugin)this).Config.Bind<bool>("Mutators", "RespectNoLootMutators", true, "Skip all topup when a mutator that intentionally removes loot (e.g. Hunting Season) is active."); SkipMutatorNames = ((BaseUnityPlugin)this).Config.Bind<string>("Mutators", "SkipMutatorNames", "Hunting Season", "Comma-separated mutator display names during which topup is disabled."); MaxSpawnsPerLevel = ((BaseUnityPlugin)this).Config.Bind<int>("Topup", "MaxSpawnsPerLevel", 60, "Hard cap on how many valuables this mod will spawn in a single level (runaway guard). Failed spawns also count toward this cap."); try { new Harmony("themorningstar.loottopup").PatchAll(); Log.LogInfo((object)"LootTopup: armed (min-count floor + mid-level softlock watchdog; respects no-loot mutators)."); } catch (Exception ex) { Log.LogError((object)("LootTopup: patch failed: " + ex)); } } } [HarmonyPatch(typeof(LevelGenerator), "Start")] internal static class ResetGuardPatch { [HarmonyPrefix] private static void Prefix() { Plugin.ToppedUpThisLevel = false; Plugin.SpawnedThisLevel = 0; Plugin.Templates.Clear(); Plugin.SpawnBlacklist.Clear(); Plugin.Busy = false; } } [HarmonyPatch(typeof(RoundDirector), "StartRoundLogic")] internal static class StartPatch { [HarmonyPostfix] private static void Postfix(RoundDirector __instance) { if (!Plugin.ToppedUpThisLevel && SemiFunc.IsMasterClientOrSingleplayer() && SemiFunc.RunIsLevel()) { Plugin.ToppedUpThisLevel = true; ((MonoBehaviour)__instance).StartCoroutine(Driver(__instance)); } } private static IEnumerator Driver(RoundDirector rd) { yield return (object)new WaitForSeconds(2.5f); TopupLogic.CaptureTemplates(); TopupLogic.Evaluate(rd, "level start"); if (!Plugin.WatchdogEnabled.Value) { yield break; } WaitForSeconds wait = new WaitForSeconds(Mathf.Max(1f, Plugin.WatchdogInterval.Value)); while ((Object)(object)rd != (Object)null && SemiFunc.RunIsLevel()) { yield return wait; if ((Object)(object)rd == (Object)null || !SemiFunc.RunIsLevel()) { break; } TopupLogic.Evaluate(rd, "watchdog"); } } } [HarmonyPatch(typeof(PhysGrabObjectImpactDetector), "BreakRPC")] internal static class BreakTriggerPatch { [HarmonyPostfix] private static void Postfix() { if (Plugin.WatchdogEnabled.Value && SemiFunc.IsMasterClientOrSingleplayer() && SemiFunc.RunIsLevel()) { RoundDirector instance = RoundDirector.instance; if ((Object)(object)instance != (Object)null) { TopupLogic.Evaluate(instance, "item damaged"); } } } } internal static class TopupLogic { private static Type _mutatorManagerType; private static bool _mutatorLookupDone; public static void CaptureTemplates() { Plugin.Templates.Clear(); ValuableObject[] array = Object.FindObjectsOfType<ValuableObject>(); foreach (ValuableObject val in array) { if (!((Object)(object)val == (Object)null)) { GameObject gameObject = ((Component)val).gameObject; if (!((Object)(object)gameObject == (Object)null)) { Plugin.Templates.Add(MakeTemplate(gameObject, val.dollarValueCurrent)); } } } } private static Plugin.TemplateInfo MakeTemplate(GameObject go, float value) { string text = "Valuables/" + ((Object)go).name.Replace("(Clone)", "").Trim(); PrefabRef repoLibRef = default(PrefabRef); NetworkPrefabs.TryGetNetworkPrefabRef(text, ref repoLibRef); return new Plugin.TemplateInfo { prefab = go, resourceId = text, repoLibRef = repoLibRef, value = value }; } private static GameObject TrySpawn(Plugin.TemplateInfo tmpl, bool mp, Vector3 pos, Quaternion rot) { //IL_0009: 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_002f: 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_001f: 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 (!mp) { return Object.Instantiate<GameObject>(tmpl.prefab, pos, rot); } if (tmpl.repoLibRef != null) { return NetworkPrefabs.SpawnNetworkPrefab(tmpl.repoLibRef, pos, rot, (byte)0, (object[])null); } return PhotonNetwork.InstantiateRoomObject(tmpl.resourceId, pos, rot, (byte)0, (object[])null); } public static void Evaluate(RoundDirector rd, string reason) { //IL_030d: Unknown result type (might be due to invalid IL or missing references) //IL_0335: Unknown result type (might be due to invalid IL or missing references) //IL_033a: Unknown result type (might be due to invalid IL or missing references) //IL_033f: Unknown result type (might be due to invalid IL or missing references) //IL_0341: Unknown result type (might be due to invalid IL or missing references) //IL_0346: Unknown result type (might be due to invalid IL or missing references) //IL_034c: Unknown result type (might be due to invalid IL or missing references) //IL_034e: Unknown result type (might be due to invalid IL or missing references) if (Plugin.Busy || (Object)(object)rd == (Object)null) { return; } int haulGoal = rd.haulGoal; if (haulGoal <= 0 || Plugin.SpawnedThisLevel >= Plugin.MaxSpawnsPerLevel.Value || (Plugin.RespectHuntingSeason.Value && NoLootMutatorActive())) { return; } List<ValuableObject> list = (from v in Object.FindObjectsOfType<ValuableObject>() where (Object)(object)v != (Object)null select v).ToList(); int count = list.Count; float num = 0f; for (int num2 = 0; num2 < list.Count; num2++) { num += list[num2].dollarValueCurrent; } int num3 = Mathf.Max(1, rd.extractionPoints); float num4 = (float)haulGoal / (float)num3; float num5 = (float)rd.extractionPointsCompleted * num4; float num6 = num + num5; float num7 = (float)haulGoal * Plugin.TargetMultiplier.Value; int value = Plugin.MinValuables.Value; bool num8 = num6 < num7; bool flag = count < value; if (!num8 && !flag) { return; } List<Plugin.TemplateInfo> source = ((Plugin.Templates.Count > 0) ? Plugin.Templates : list.Select((ValuableObject v) => MakeTemplate(((Component)v).gameObject, v.dollarValueCurrent)).ToList()); source = source.Where((Plugin.TemplateInfo t) => (Object)(object)t.prefab != (Object)null).ToList(); if (source.Count == 0) { Plugin.Log.LogWarning((object)$"LootTopup ({reason}): level short (goal={haulGoal}, achievable={num6:0}, items={count}) but no templates to spawn from."); return; } float avgVal = source.Average((Plugin.TemplateInfo t) => t.value); List<Plugin.TemplateInfo> list2 = source.Where((Plugin.TemplateInfo t) => t.value <= avgVal).ToList(); if (list2.Count == 0) { list2 = source; } bool mp = SemiFunc.IsMultiplayer(); Plugin.Busy = true; int num9 = 0; int num10 = 0; int num11 = 0; Plugin.Log.LogInfo((object)$"LootTopup ({reason}): topping up. goal={haulGoal} achievable={num6:0} (target {num7:0}), items={count} (min {value})."); try { while ((num6 < num7 || count + num9 < value) && num11 < 500 && Plugin.SpawnedThisLevel + num10 < Plugin.MaxSpawnsPerLevel.Value) { num11++; List<Plugin.TemplateInfo> list3 = ((num6 < num7) ? source : list2).Where((Plugin.TemplateInfo x) => !Plugin.SpawnBlacklist.Contains(x.resourceId)).ToList(); if (list3.Count == 0) { Plugin.Log.LogWarning((object)("LootTopup (" + reason + "): all viable templates are blacklisted; stopping topup.")); break; } Plugin.TemplateInfo tmpl = list3[Random.Range(0, list3.Count)]; if ((Object)(object)tmpl.prefab == (Object)null) { continue; } Vector3 pos = tmpl.prefab.transform.position + new Vector3(Random.Range(-1.2f, 1.2f), 0.7f, Random.Range(-1.2f, 1.2f)); Quaternion rotation = Random.rotation; GameObject val; try { val = TrySpawn(tmpl, mp, pos, rotation); } catch (Exception ex) { if (Plugin.SpawnBlacklist.Add(tmpl.resourceId)) { Plugin.Log.LogWarning((object)("LootTopup: spawn exception for '" + tmpl.resourceId + "' (" + ex.Message + "); blacklisting for this level.")); } num10++; continue; } if ((Object)(object)val == (Object)null) { if (Plugin.SpawnBlacklist.Add(tmpl.resourceId)) { Plugin.Log.LogWarning((object)("LootTopup: spawn returned null for '" + tmpl.resourceId + "'; blacklisting for this level.")); } num10++; continue; } ValuableObject component = val.GetComponent<ValuableObject>(); if (!((Object)(object)component == (Object)null)) { try { component.DollarValueSetLogic(); } catch { } num6 += component.dollarValueCurrent; num9++; num10++; } } } finally { Plugin.Busy = false; } Plugin.SpawnedThisLevel += num10; int num12 = num10 - num9; string text = ((num12 > 0) ? $" ({num12} failed/blacklisted)" : ""); Plugin.Log.LogInfo((object)$"LootTopup ({reason}): spawned {num9} valuable(s){text}; achievable now ~{num6:0} vs goal {haulGoal}, level total spawned {Plugin.SpawnedThisLevel}."); } private static bool NoLootMutatorActive() { try { string[] array = (from s in (Plugin.SkipMutatorNames.Value ?? "").Split(',') select s.Trim() into s where s.Length > 0 select s).ToArray(); if (array.Length == 0) { return false; } if (!_mutatorLookupDone) { _mutatorLookupDone = true; _mutatorManagerType = AppDomain.CurrentDomain.GetAssemblies().SelectMany(delegate(Assembly a) { try { return a.GetTypes(); } catch { return Array.Empty<Type>(); } }).FirstOrDefault((Type t) => t.Name == "MutatorManager"); } if (_mutatorManagerType == null) { return false; } object obj = (_mutatorManagerType.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public) ?? _mutatorManagerType.GetProperty("instance", BindingFlags.Static | BindingFlags.Public))?.GetValue(null); if (obj == null) { return false; } if (!(obj.GetType().GetProperty("RegisteredMutators")?.GetValue(obj) is IEnumerable enumerable)) { return false; } foreach (object item in enumerable) { object obj2 = item.GetType().GetProperty("Value")?.GetValue(item); if (obj2 == null) { continue; } PropertyInfo property = obj2.GetType().GetProperty("Active"); if (property == null) { continue; } object value = property.GetValue(obj2); if (value is bool && (bool)value) { string mname = (obj2.GetType().GetProperty("MutatorName")?.GetValue(obj2) as string) ?? ""; if (array.Any((string s) => string.Equals(s, mname, StringComparison.OrdinalIgnoreCase))) { Plugin.Log.LogInfo((object)("LootTopup: '" + mname + "' mutator active - skipping topup (intentional no-loot).")); return true; } } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("LootTopup: mutator check failed (proceeding): " + ex.Message)); } return false; } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { internal IgnoresAccessChecksToAttribute(string assemblyName) { } } }