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 Valuable Rebalance v1.0.3
ValuableRebalance.dll
Decompiled 4 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 System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using REPOLib; 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+c73d19f8c137519f22d551651df63f27ce7ae1d6")] [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.3.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { internal sealed class ItemChoice { public PrefabRef Prefab; public int Size; } 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 ConfigEntry<string> NextItemPack; internal static ConfigEntry<string> NextItemInitial; internal static ConfigEntry<string> NextItemPage; internal static ConfigEntry<string> NextItem; internal static ConfigEntry<string> NextTheme; internal static ConfigEntry<float> TIBaseBoxChance; internal static bool TIBoxPending; internal static Plugin Instance; private static bool _overridesBound; internal static readonly Dictionary<string, List<PrefabRef>[]> AllByCategory = new Dictionary<string, List<PrefabRef>[]>(StringComparer.OrdinalIgnoreCase); internal static readonly Dictionary<string, ItemChoice> ItemChoices = new Dictionary<string, ItemChoice>(StringComparer.OrdinalIgnoreCase); internal static ItemChoice ForcedItem; internal static bool ManualThemeActive; 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_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Expected O, but got Unknown //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Expected O, but got Unknown //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Expected O, but got Unknown //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Expected O, but got Unknown //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Expected O, but got Unknown //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Expected O, but got Unknown Instance = this; 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."); TIBaseBoxChance = ((BaseUnityPlugin)this).Config.Bind<float>("Balance", "TIBaseBoxChancePerLevel", 0.1f, new ConfigDescription("Chance (0-1) per level that the level's first Wide valuable spot holds the base Twilight Imperium box. It is the only Wide item in its pack, and modded items get only a few percent of spots split across every pack, so without this it almost never spawns.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); 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."); } BundleLoader.OnAllBundlesLoaded += delegate { try { Pools.OnDrain(); BindOverrideOptions(); } catch (Exception ex2) { Log.LogError((object)("ValuableBalance: pool rebuild (post-bundle-load) failed: " + ex2)); } }; Log.LogInfo((object)"ValuableBalance: armed (level-aware native pools + global sprinkle + rare re-theme)."); } catch (Exception ex) { Log.LogError((object)("ValuableBalance: setup failed: " + ex)); } } private static void BindOverrideOptions() { //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Expected O, but got Unknown //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Expected O, but got Unknown //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Expected O, but got Unknown //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Expected O, but got Unknown if (_overridesBound) { return; } string[] array = AllByCategory.Keys.OrderBy<string, string>((string x) => x, StringComparer.OrdinalIgnoreCase).ToArray(); if (array.Length == 0) { Log.LogWarning((object)"ValuableBalance: no valuable packs available for overrides."); return; } string[] array2 = new string[1] { "None" }.Concat(array).ToArray(); NextItemPack = ((BaseUnityPlugin)Instance).Config.Bind<string>("Next level overrides", "Item pack", "None", new ConfigDescription("Choose a pack and item initial, save, then reopen this menu to select its item.", (AcceptableValueBase)(object)new AcceptableValueList<string>(array2), Array.Empty<object>())); string[] array3 = new string[1] { "All" }.Concat("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".Select((char c) => c.ToString())).ToArray(); NextItemInitial = ((BaseUnityPlugin)Instance).Config.Bind<string>("Next level overrides", "Item initial", "All", new ConfigDescription("Filter the item selector by its first letter. Use All for small packs.", (AcceptableValueBase)(object)new AcceptableValueList<string>(array3), Array.Empty<object>())); NextItemPage = ((BaseUnityPlugin)Instance).Config.Bind<string>("Next level overrides", "Item page", "1", new ConfigDescription("Item choices are alphabetical, 50 per page. Use page 1 for small packs.", (AcceptableValueBase)(object)new AcceptableValueList<string>((from n in Enumerable.Range(1, 40) select n.ToString()).ToArray()), Array.Empty<object>())); NextTheme = ((BaseUnityPlugin)Instance).Config.Bind<string>("Next level overrides", "Full theme", "None", new ConfigDescription("One-shot: all next-level valuable slots use this pack. Smaller items fill large slots when needed.", (AcceptableValueBase)(object)new AcceptableValueList<string>(array2), Array.Empty<object>())); BindItemDropdown(); NextItemPack.SettingChanged += delegate { if (NextItem != null) { NextItem.Value = "None"; } BindItemDropdown(); }; NextItemInitial.SettingChanged += delegate { if (NextItem != null) { NextItem.Value = "None"; } BindItemDropdown(); }; NextItemPage.SettingChanged += delegate { if (NextItem != null) { NextItem.Value = "None"; } BindItemDropdown(); }; _overridesBound = true; Log.LogInfo((object)$"ValuableBalance: next-level override selectors ready ({array.Length} packs). Save after choosing a pack, then reopen Mods to choose its item."); } private static void BindItemDropdown() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0288: Unknown result type (might be due to invalid IL or missing references) //IL_0292: Expected O, but got Unknown ConfigDefinition val = new ConfigDefinition("Next level overrides", "Specific item"); if (NextItem != null) { ((BaseUnityPlugin)Instance).Config.Remove(val); } ItemChoices.Clear(); string text = NextItemPack?.Value; if (!string.IsNullOrEmpty(text) && AllByCategory.TryGetValue(text, out var value)) { HashSet<PrefabRef> hashSet = new HashSet<PrefabRef>(new RefComparer()); for (int i = 0; i < value.Length; i++) { foreach (PrefabRef item in value[i]) { if (item == null || !hashSet.Add(item)) { continue; } string text2 = Pools.ReadableName(item); if (string.IsNullOrEmpty(text2)) { continue; } string text3 = NextItemInitial?.Value; if (string.IsNullOrEmpty(text3) || !(text3 != "All") || text2.StartsWith(text3, StringComparison.OrdinalIgnoreCase)) { string key = text2; int num = 2; while (ItemChoices.ContainsKey(key)) { key = text2 + " #" + num; num++; } ItemChoices[key] = new ItemChoice { Prefab = item, Size = i }; } } } } int result; int num2 = ((!int.TryParse(NextItemPage?.Value, out result)) ? 1 : Math.Max(1, result)); KeyValuePair<string, ItemChoice>[] array = ItemChoices.OrderBy<KeyValuePair<string, ItemChoice>, string>((KeyValuePair<string, ItemChoice> kv) => kv.Key, StringComparer.OrdinalIgnoreCase).Skip((num2 - 1) * 50).Take(50) .ToArray(); ItemChoices.Clear(); KeyValuePair<string, ItemChoice>[] array2 = array; for (int num3 = 0; num3 < array2.Length; num3++) { KeyValuePair<string, ItemChoice> keyValuePair = array2[num3]; ItemChoices[keyValuePair.Key] = keyValuePair.Value; } string[] array3 = new string[1] { "None" }.Concat(ItemChoices.Keys.OrderBy<string, string>((string x) => x, StringComparer.OrdinalIgnoreCase)).ToArray(); NextItem = ((BaseUnityPlugin)Instance).Config.Bind<string>(val, "None", new ConfigDescription("One-shot: replace the first valuable in the next level with this item.", (AcceptableValueBase)(object)new AcceptableValueList<string>(array3), Array.Empty<object>())); } } 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(); Plugin.AllByCategory.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()); HashSet<PrefabRef> hashSet2 = 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 (hashSet2.Add(item6)) { string key = (string.IsNullOrEmpty(text3) ? "Vanilla" : text3); if (!Plugin.AllByCategory.TryGetValue(key, out var value)) { value = Plugin.NewPools(); Plugin.AllByCategory[key] = value; } value[num2].Add(item6); } if (!string.IsNullOrEmpty(text3) && Plugin.SprinkleCats.Contains(text3)) { if (hashSet.Add(item6)) { if (!Plugin.SprinkleBySize.TryGetValue(text3, out var value2)) { value2 = Plugin.NewPools(); Plugin.SprinkleBySize[text3] = value2; } value2[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."); foreach (string item7 in Plugin.SprinkleCats.OrderBy((string x) => x)) { List<PrefabRef>[] value3; int num4 = (Plugin.SprinkleBySize.TryGetValue(item7, out value3) ? value3.Sum((List<PrefabRef> l) => l.Count) : 0); Plugin.Log.LogInfo((object)$"ValuableBalance: [sprinkle] '{item7}': {num4} item(s)"); } } catch (Exception ex) { Plugin.Log.LogError((object)("ValuableBalance: pool build failed: " + ex)); } } public static void OnLevelStart() { Plugin.RethemeActive = false; Plugin.RethemeCategory = null; Plugin.ManualThemeActive = false; Plugin.ForcedItem = null; Plugin.CurrentLevel = null; Plugin.TIBoxPending = false; if (!Plugin.Enabled.Value || !Plugin.PoolsBuilt) { return; } try { if (SemiFunc.IsMasterClientOrSingleplayer() && SemiFunc.RunIsLevel() && TIBaseBox() != null && Random.value < Plugin.TIBaseBoxChance.Value) { Plugin.TIBoxPending = true; Plugin.Log.LogInfo((object)"ValuableBalance: [TI-BOX] this level's first Wide valuable spot will hold the base Twilight Imperium box."); } } catch { } if (!_healthDone) { _healthDone = true; try { HealthCheck(); } catch (Exception ex) { Plugin.Log.LogWarning((object)("ValuableBalance: health check failed: " + ex.Message)); } } try { if (SemiFunc.IsMasterClientOrSingleplayer() && Plugin.NextItem != null && Plugin.ItemChoices.TryGetValue(Plugin.NextItem.Value, out var value)) { GameObject val = null; try { val = ((PrefabRef<GameObject>)(object)value.Prefab).Prefab; } catch { } if ((Object)(object)val != (Object)null && !string.IsNullOrEmpty(((PrefabRef<GameObject>)(object)value.Prefab).ResourcePath)) { Plugin.ForcedItem = value; Plugin.Log.LogInfo((object)("ValuableBalance: next level will contain '" + Plugin.NextItem.Value + "'.")); } else { Plugin.Log.LogWarning((object)("ValuableBalance: selected item '" + Plugin.NextItem.Value + "' has no spawnable prefab; override cleared.")); Plugin.NextItem.Value = "None"; } } if (SemiFunc.IsMasterClientOrSingleplayer() && Plugin.NextTheme != null && Plugin.AllByCategory.TryGetValue(Plugin.NextTheme.Value, out var value2) && value2.Any((List<PrefabRef> pool) => pool.Count > 0)) { Plugin.RethemeCategory = Plugin.NextTheme.Value; Plugin.RethemeActive = true; Plugin.ManualThemeActive = true; Plugin.Log.LogInfo((object)("ValuableBalance: next level forced to '" + Plugin.RethemeCategory + "' loot.")); } else 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_0259: 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_003a: Expected I4, but got Unknown //IL_029c: Unknown result type (might be due to invalid IL or missing references) try { if (!Plugin.Enabled.Value || !Plugin.PoolsBuilt || (Object)(object)_volume == (Object)null || !SemiFunc.IsMasterClientOrSingleplayer()) { 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; bool flag = Plugin.ForcedItem != null; bool flag2 = false; List<PrefabRef>[] value; if (flag) { val = Plugin.ForcedItem.Prefab; if (size != Plugin.ForcedItem.Size) { Plugin.Log.LogWarning((object)$"ValuableBalance: forced item uses size {Plugin.ForcedItem.Size} in first available size {size} slot."); } } else if (Plugin.ManualThemeActive && Plugin.RethemeCategory != null && Plugin.AllByCategory.TryGetValue(Plugin.RethemeCategory, out value)) { val = RandomFromTheme(value, size); } else { if (Plugin.TIBoxPending && size == 4) { PrefabRef val2 = TIBaseBox(); if (val2 != null) { val = val2; flag2 = true; goto IL_0219; } } if (Plugin.RethemeActive && Plugin.RethemeCategory != null && Plugin.SprinkleBySize.TryGetValue(Plugin.RethemeCategory, out var value2)) { val = RandomFrom(value2[size]) ?? RandomFrom(Plugin.NativeNow[size]); } else if (Random.value < Plugin.ModdedChancePerSlot.Value && Plugin.SprinkleCats.Count > 0) { List<PrefabRef>[] value3; List<string> list = Plugin.SprinkleCats.Where((string c) => Plugin.SprinkleBySize.TryGetValue(c, out value3) && value3[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]); } } goto IL_0219; IL_0219: if (val != null) { GameObject val3 = null; try { val3 = ((PrefabRef<GameObject>)(object)val).Prefab; } catch { } if ((Object)(object)val3 != (Object)null) { _valuable = val; if (flag) { Plugin.Log.LogInfo((object)$"ValuableBalance: guaranteed '{Plugin.NextItem.Value}' at {((Component)_volume).transform.position}."); Plugin.ForcedItem = null; Plugin.NextItem.Value = "None"; } if (flag2) { Plugin.TIBoxPending = false; Plugin.Log.LogInfo((object)$"ValuableBalance: [TI-BOX] placed the base Twilight Imperium box in a Wide spot at {((Component)_volume).transform.position}."); } } } if (Plugin.ManualThemeActive && Plugin.NextTheme != null && Plugin.NextTheme.Value != "None") { Plugin.NextTheme.Value = "None"; } } 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; } internal static PrefabRef TIBaseBox() { int num = 4; if (Plugin.SprinkleBySize.TryGetValue("twilightimperiumvaluables", out var value) && value[num].Count > 0) { return value[num][0]; } if (Plugin.AllByCategory.TryGetValue("twilightimperiumvaluables", out var value2) && value2[num].Count > 0) { return value2[num][0]; } return null; } 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 PrefabRef RandomFromTheme(List<PrefabRef>[] pools, int size) { PrefabRef val = RandomFrom(pools[size]); if (val != null) { return val; } for (int num = size - 1; num >= 0; num--) { PrefabRef val2 = RandomFrom(pools[num]); if (val2 != null) { return val2; } } for (int i = size + 1; i < pools.Length; i++) { PrefabRef val3 = RandomFrom(pools[i]); if (val3 != null) { return val3; } } return null; } internal static string ReadableName(PrefabRef pr) { try { string prefabName = ((PrefabRef<GameObject>)(object)pr).PrefabName; if (string.IsNullOrEmpty(prefabName)) { return ""; } int num = prefabName.LastIndexOfAny(new char[2] { '/', '\\' }); string text = ((num >= 0) ? prefabName.Substring(num + 1) : prefabName).Trim(); if (text.StartsWith("Valuable", StringComparison.OrdinalIgnoreCase) && text.Length > 8) { text = text.Substring(8).Trim(' ', '-', '_'); } return text; } catch { return ""; } } 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) { } } }