Decompiled source of Valuable Rebalance v1.0.3

ValuableRebalance.dll

Decompiled 4 days ago
using 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)
		{
		}
	}
}