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 ValuableRebalance v3.1.0
ValuableRebalance.dll
Decompiled 9 hours agousing System; 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 REPOLib.Modules; using REPOLib.Objects.Sdk; 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("ValuableRebalance")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("ValuableRebalance")] [assembly: AssemblyTitle("ValuableRebalance")] [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 ValuableRebalance { [BepInPlugin("themorningstar.valuablerebalance", "Valuable Balance", "3.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { internal sealed class RefComparer : IEqualityComparer<PrefabRef> { public bool Equals(PrefabRef a, PrefabRef b) { return a == b; } public int GetHashCode(PrefabRef o) { return RuntimeHelpers.GetHashCode(o); } } public const string Guid = "themorningstar.valuablerebalance"; internal static ManualLogSource Log = null; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<float> ModdedChancePerSlot; internal static ConfigEntry<float> RethemeChance; internal static ConfigEntry<bool> Verbose; internal static readonly Dictionary<PrefabRef, string> PrefabToBundle = new Dictionary<PrefabRef, string>(new RefComparer()); internal static readonly HashSet<string> SprinkleCats = new HashSet<string>(); internal static readonly Dictionary<string, List<PrefabRef>[]> SprinkleBySize = new Dictionary<string, List<PrefabRef>[]>(); internal static readonly Dictionary<LevelValuables, List<PrefabRef>[]> PresetNativeBySize = new Dictionary<LevelValuables, List<PrefabRef>[]>(); internal static bool PoolsBuilt; internal static object CurrentLevel; internal static List<PrefabRef>[] NativeNow = NewPools(); internal static bool RethemeActive; internal static string RethemeCategory; internal static List<PrefabRef>[] NewPools() { List<PrefabRef>[] array = new List<PrefabRef>[7]; for (int i = 0; i < 7; i++) { array[i] = new List<PrefabRef>(); } return array; } private void Awake() { //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Expected O, but got Unknown //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Expected O, but got Unknown //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Expected O, but got Unknown //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Expected O, but got Unknown //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Balance", "Enabled", true, "Master switch."); ModdedChancePerSlot = ((BaseUnityPlugin)this).Config.Bind<float>("Balance", "ModdedChancePerSlot", 0.06f, "Chance (0-1) each valuable spawn is a global modded item instead of the map's native loot. ~0.06 => roughly 1-2 modded items per map."); RethemeChance = ((BaseUnityPlugin)this).Config.Bind<float>("Balance", "RethemeChance", 0.02f, "Chance (0-1) an entire map is re-themed so every valuable comes from a single global pack (all-Pokemon map, etc.)."); Verbose = ((BaseUnityPlugin)this).Config.Bind<bool>("Balance", "Verbose", false, "Log pool sizes, sprinkle categories, and per-map re-theme rolls."); try { Harmony val = new Harmony("themorningstar.valuablerebalance"); MethodInfo methodInfo = AccessTools.Method(typeof(Valuables), "RegisterValuable", new Type[1] { typeof(ValuableContent) }, (Type[])null); if (methodInfo != null) { val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(Track).GetMethod("Post")), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo methodInfo2 = AccessTools.Method(typeof(Valuables), "RegisterInitialValuables", (Type[])null, (Type[])null); if (methodInfo2 != null) { val.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(Pools).GetMethod("OnDrain")), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } else { Log.LogWarning((object)"ValuableBalance: RegisterInitialValuables not found."); } MethodInfo methodInfo3 = AccessTools.Method(typeof(LevelGenerator), "Start", (Type[])null, (Type[])null); if (methodInfo3 != null) { val.Patch((MethodBase)methodInfo3, new HarmonyMethod(typeof(Pools).GetMethod("OnLevelStart")), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo methodInfo4 = AccessTools.Method(typeof(ValuableDirector), "SpawnValuable", (Type[])null, (Type[])null); if (methodInfo4 != null) { val.Patch((MethodBase)methodInfo4, new HarmonyMethod(typeof(Pools).GetMethod("OnSpawn")), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } else { Log.LogWarning((object)"ValuableBalance: ValuableDirector.SpawnValuable not found; per-slot balancing disabled."); } Log.LogInfo((object)"ValuableBalance: armed (level-aware native pools + global sprinkle + rare re-theme)."); } catch (Exception ex) { Log.LogError((object)("ValuableBalance: setup failed: " + ex)); } } } internal static class Track { public static void Post(ValuableContent __0, PrefabRef __result) { try { if (__result != null && !((Object)(object)__0 == (Object)null) && !((Object)(object)((Content)__0).Bundle == (Object)null)) { string value = ((Object)((Content)__0).Bundle).name?.ToLowerInvariant(); if (!string.IsNullOrEmpty(value)) { Plugin.PrefabToBundle[__result] = value; } } } catch { } } } internal static class Pools { private static bool _healthDone; private static string Category(PrefabRef pr, Dictionary<string, string> index) { try { AssetBundle bundle = ((PrefabRef<GameObject>)(object)pr).Bundle; if ((Object)(object)bundle != (Object)null) { string text = ((Object)bundle).name?.ToLowerInvariant(); if (!string.IsNullOrEmpty(text)) { return text; } } } catch { } if (Plugin.PrefabToBundle.TryGetValue(pr, out var value)) { return value; } return SourceFromBundleIndex(pr, index); } public static void OnDrain() { if (!Plugin.Enabled.Value) { return; } try { Plugin.SprinkleCats.Clear(); Plugin.SprinkleBySize.Clear(); Plugin.PresetNativeBySize.Clear(); Dictionary<string, string> index = BuildAssetBundleIndex(); List<LevelValuables> list = (from lv in Resources.FindObjectsOfTypeAll<LevelValuables>() where (Object)(object)lv != (Object)null select lv).ToList(); foreach (LevelValuables item in list) { if (((Object)item).name == null || ((Object)item).name.IndexOf("Generic", StringComparison.OrdinalIgnoreCase) < 0) { continue; } List<PrefabRef>[] array = Lists(item); foreach (List<PrefabRef> list2 in array) { if (list2 == null) { continue; } foreach (PrefabRef item2 in list2) { if (item2 != null) { string text = Category(item2, index); if (!string.IsNullOrEmpty(text)) { Plugin.SprinkleCats.Add(text); } } } } } if (Plugin.SprinkleCats.Count == 0) { foreach (LevelValuables item3 in list) { List<PrefabRef>[] array = Lists(item3); foreach (List<PrefabRef> list3 in array) { if (list3 == null) { continue; } foreach (PrefabRef item4 in list3) { if (item4 != null) { string text2 = Category(item4, index); if (!string.IsNullOrEmpty(text2)) { Plugin.SprinkleCats.Add(text2); } } } } } if (Plugin.SprinkleCats.Count > 0) { Plugin.Log.LogWarning((object)"ValuableBalance: no 'Generic' preset found; treating all mod categories as global (theme-scoping disabled)."); } } HashSet<PrefabRef> hashSet = new HashSet<PrefabRef>(new Plugin.RefComparer()); foreach (LevelValuables item5 in list) { List<PrefabRef>[] array2 = Plugin.NewPools(); Plugin.PresetNativeBySize[item5] = array2; List<PrefabRef>[] array3 = Lists(item5); for (int num2 = 0; num2 < 7; num2++) { if (array3[num2] == null) { continue; } foreach (PrefabRef item6 in array3[num2]) { if (item6 == null) { continue; } string text3 = Category(item6, index); if (!string.IsNullOrEmpty(text3) && Plugin.SprinkleCats.Contains(text3)) { if (hashSet.Add(item6)) { if (!Plugin.SprinkleBySize.TryGetValue(text3, out var value)) { value = Plugin.NewPools(); Plugin.SprinkleBySize[text3] = value; } value[num2].Add(item6); } } else { array2[num2].Add(item6); } } } } Plugin.PoolsBuilt = true; int num3 = Plugin.SprinkleBySize.Values.Sum((List<PrefabRef>[] p) => p.Sum((List<PrefabRef> l) => l.Count)); Plugin.Log.LogInfo((object)$"ValuableBalance: pools built - {Plugin.SprinkleCats.Count} global sprinkle pack(s) ({num3} items), {Plugin.PresetNativeBySize.Count} map preset(s) kept native."); } catch (Exception ex) { Plugin.Log.LogError((object)("ValuableBalance: pool build failed: " + ex)); } } public static void OnLevelStart() { Plugin.RethemeActive = false; Plugin.RethemeCategory = null; Plugin.CurrentLevel = null; if (!Plugin.Enabled.Value || !Plugin.PoolsBuilt) { return; } if (!_healthDone) { _healthDone = true; try { HealthCheck(); } catch (Exception ex) { Plugin.Log.LogWarning((object)("ValuableBalance: health check failed: " + ex.Message)); } } try { if (Plugin.SprinkleCats.Count > 0 && Random.value < Plugin.RethemeChance.Value) { List<string> list = Plugin.SprinkleCats.ToList(); Plugin.RethemeCategory = list[Random.Range(0, list.Count)]; Plugin.RethemeActive = true; Plugin.Log.LogInfo((object)("ValuableBalance: THEMED MAP - all valuables from '" + Plugin.RethemeCategory + "'.")); } } catch { } } private static void EnsureNative(Level lvl) { if (lvl == Plugin.CurrentLevel) { return; } Plugin.CurrentLevel = lvl; List<PrefabRef>[] array = Plugin.NewPools(); try { if ((Object)(object)lvl != (Object)null && lvl.ValuablePresets != null) { HashSet<PrefabRef> hashSet = new HashSet<PrefabRef>(new Plugin.RefComparer()); foreach (LevelValuables valuablePreset in lvl.ValuablePresets) { if (!((Object)(object)valuablePreset != (Object)null) || !Plugin.PresetNativeBySize.TryGetValue(valuablePreset, out var value)) { continue; } for (int i = 0; i < 7; i++) { foreach (PrefabRef item in value[i]) { if (item != null && hashSet.Add(item)) { array[i].Add(item); } } } } } } catch { } Plugin.NativeNow = array; } public static void OnSpawn(ref PrefabRef _valuable, ValuableVolume _volume, string _path) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected I4, but got Unknown try { if (!Plugin.Enabled.Value || !Plugin.PoolsBuilt || (Object)(object)_volume == (Object)null) { return; } int size = (int)_volume.VolumeType; if (size < 0 || size > 6) { return; } Level lvl = null; try { lvl = (((Object)(object)LevelGenerator.Instance != (Object)null) ? LevelGenerator.Instance.Level : null); } catch { } EnsureNative(lvl); PrefabRef val = null; if (Plugin.RethemeActive && Plugin.RethemeCategory != null && Plugin.SprinkleBySize.TryGetValue(Plugin.RethemeCategory, out var value)) { val = RandomFrom(value[size]) ?? RandomFrom(Plugin.NativeNow[size]); } else if (Random.value < Plugin.ModdedChancePerSlot.Value && Plugin.SprinkleCats.Count > 0) { List<PrefabRef>[] value2; List<string> list = Plugin.SprinkleCats.Where((string c) => Plugin.SprinkleBySize.TryGetValue(c, out value2) && value2[size].Count > 0).ToList(); val = ((list.Count <= 0) ? RandomFrom(Plugin.NativeNow[size]) : RandomFrom(Plugin.SprinkleBySize[list[Random.Range(0, list.Count)]][size])); } else { val = RandomFrom(Plugin.NativeNow[size]); } if (val != null && val != _valuable) { GameObject val2 = null; try { val2 = ((PrefabRef<GameObject>)(object)val).Prefab; } catch { } if ((Object)(object)val2 != (Object)null) { _valuable = val; } } } catch { } } private static void HealthCheck() { int num = Plugin.PresetNativeBySize.Values.Sum((List<PrefabRef>[] p) => p.Sum((List<PrefabRef> l) => l.Count)); Plugin.Log.LogInfo((object)$"===== ValuableBalance HEALTH CHECK: {Plugin.SprinkleCats.Count} global loot pack(s), {Plugin.PresetNativeBySize.Count} map preset(s), {num} native items ====="); foreach (string item in Plugin.SprinkleCats.OrderBy((string x) => x)) { List<PrefabRef>[] value; int num2 = (Plugin.SprinkleBySize.TryGetValue(item, out value) ? value.Sum((List<PrefabRef> l) => l.Count) : 0); Plugin.Log.LogInfo((object)$" [global pack] {item}: {num2} valuable(s)"); } Plugin.Log.LogInfo((object)" (If a global loot mod you installed is NOT listed above, it registered nothing = broken.)"); HashSet<PrefabRef> seen = new HashSet<PrefabRef>(new Plugin.RefComparer()); int checkedCount = 0; int unresolved = 0; List<string> broken = new List<string>(); foreach (KeyValuePair<string, List<PrefabRef>[]> item2 in Plugin.SprinkleBySize) { Scan(item2.Value, item2.Key); } foreach (KeyValuePair<LevelValuables, List<PrefabRef>[]> item3 in Plugin.PresetNativeBySize) { Scan(item3.Value, "native:" + ((Object)item3.Key).name); } if (broken.Count > 0) { Plugin.Log.LogWarning((object)$"ValuableBalance: {broken.Count} valuable(s) render as a PLACEHOLDER box (missing model/material) - likely broken:"); { foreach (string item4 in broken) { Plugin.Log.LogWarning((object)(" [broken-render] " + item4)); } return; } } Plugin.Log.LogInfo((object)$"ValuableBalance: no placeholder-render valuables among {checkedCount} checked ({unresolved} unresolved)."); void Scan(List<PrefabRef>[] pools, string tag) { for (int i = 0; i < 7; i++) { foreach (PrefabRef item5 in pools[i]) { if (item5 != null && seen.Add(item5)) { GameObject val = null; try { val = ((PrefabRef<GameObject>)(object)item5).Prefab; } catch { } if ((Object)(object)val == (Object)null) { unresolved++; } else { checkedCount++; if (RendersAsPlaceholder(val)) { broken.Add("'" + ((Object)val).name + "' (" + tag + ")"); } } } } } } } private static bool RendersAsPlaceholder(GameObject go) { Renderer[] componentsInChildren = go.GetComponentsInChildren<Renderer>(true); if (componentsInChildren == null || componentsInChildren.Length == 0) { return true; } Renderer[] array = componentsInChildren; foreach (Renderer val in array) { if ((Object)(object)val == (Object)null) { continue; } Material[] sharedMaterials = val.sharedMaterials; foreach (Material val2 in sharedMaterials) { if (!((Object)(object)val2 == (Object)null)) { string text = ((Object)val2).name ?? ""; if (!text.StartsWith("Default-") && !text.StartsWith("Sprites-Default") && (!((Object)(object)val2.shader != (Object)null) || !(((Object)val2.shader).name == "Hidden/InternalErrorShader"))) { return false; } } } } return true; } private static List<PrefabRef>[] Lists(LevelValuables lv) { return new List<PrefabRef>[7] { lv.tiny, lv.small, lv.medium, lv.big, lv.wide, lv.tall, lv.veryTall }; } private static PrefabRef RandomFrom(List<PrefabRef> list) { if (list == null || list.Count == 0) { return null; } return list[Random.Range(0, list.Count)]; } private static Dictionary<string, string> BuildAssetBundleIndex() { Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); foreach (AssetBundle allLoadedAssetBundle in AssetBundle.GetAllLoadedAssetBundles()) { if ((Object)(object)allLoadedAssetBundle == (Object)null) { continue; } string value = ((Object)allLoadedAssetBundle).name?.ToLowerInvariant(); if (string.IsNullOrEmpty(value)) { continue; } string[] allAssetNames; try { if (allLoadedAssetBundle.isStreamedSceneAssetBundle) { continue; } allAssetNames = allLoadedAssetBundle.GetAllAssetNames(); goto IL_0061; } catch { } continue; IL_0061: string[] array = allAssetNames; foreach (string text in array) { if (text != null && text.EndsWith(".prefab", StringComparison.OrdinalIgnoreCase)) { string text2 = LeafName(text); if (text2.Length > 0 && !dictionary.ContainsKey(text2)) { dictionary[text2] = value; } } } } return dictionary; } private static string SourceFromBundleIndex(PrefabRef pr, Dictionary<string, string> index) { try { string text = LeafName(((PrefabRef<GameObject>)(object)pr).PrefabName); if (text.Length > 0 && index.TryGetValue(text, out var value)) { return value; } } catch { } return null; } private static string LeafName(string path) { if (string.IsNullOrEmpty(path)) { return ""; } string text = path; int num = text.LastIndexOfAny(new char[2] { '/', '\\' }); if (num >= 0) { text = text.Substring(num + 1); } if (text.EndsWith(".prefab", StringComparison.OrdinalIgnoreCase)) { text = text.Substring(0, text.Length - 7); } return text.Trim().ToLowerInvariant(); } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { internal IgnoresAccessChecksToAttribute(string assemblyName) { } } }