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 LootTopup v1.1.0
LootTopup.dll
Decompiled 21 hours 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 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.1.0")] public class Plugin : BaseUnityPlugin { internal struct TemplateInfo { public GameObject prefab; public string resourceId; 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; 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)."); 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.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(new Plugin.TemplateInfo { prefab = gameObject, resourceId = "Valuables/" + ((Object)gameObject).name.Replace("(Clone)", "").Trim(), value = val.dollarValueCurrent }); } } } } public static void Evaluate(RoundDirector rd, string reason) { //IL_02b9: Unknown result type (might be due to invalid IL or missing references) //IL_02e1: Unknown result type (might be due to invalid IL or missing references) //IL_02e6: Unknown result type (might be due to invalid IL or missing references) //IL_02eb: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Unknown result type (might be due to invalid IL or missing references) //IL_02f2: 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_0313: Unknown result type (might be due to invalid IL or missing references) //IL_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_0301: 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) => new Plugin.TemplateInfo { prefab = ((Component)v).gameObject, resourceId = "Valuables/" + ((Object)((Component)v).gameObject).name.Replace("(Clone)", "").Trim(), value = 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 flag2 = SemiFunc.IsMultiplayer(); Plugin.Busy = true; int num9 = 0; int num10 = 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) && num10 < 500 && Plugin.SpawnedThisLevel + num9 < Plugin.MaxSpawnsPerLevel.Value) { num10++; List<Plugin.TemplateInfo> list3 = ((num6 < num7) ? source : list2); Plugin.TemplateInfo templateInfo = list3[Random.Range(0, list3.Count)]; if ((Object)(object)templateInfo.prefab == (Object)null) { continue; } Vector3 val = templateInfo.prefab.transform.position + new Vector3(Random.Range(-1.2f, 1.2f), 0.7f, Random.Range(-1.2f, 1.2f)); Quaternion rotation = Random.rotation; GameObject val2; try { val2 = (flag2 ? PhotonNetwork.InstantiateRoomObject(templateInfo.resourceId, val, rotation, (byte)0, (object[])null) : Object.Instantiate<GameObject>(templateInfo.prefab, val, rotation)); } catch (Exception ex) { Plugin.Log.LogWarning((object)("LootTopup: spawn failed (" + ex.Message + "); trying another.")); continue; } if ((Object)(object)val2 == (Object)null) { continue; } ValuableObject component = val2.GetComponent<ValuableObject>(); if (!((Object)(object)component == (Object)null)) { try { component.DollarValueSetLogic(); } catch { } num6 += component.dollarValueCurrent; num9++; } } } finally { Plugin.Busy = false; } Plugin.SpawnedThisLevel += num9; Plugin.Log.LogInfo((object)$"LootTopup ({reason}): spawned {num9} valuable(s); 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) { } } }