Decompiled source of PEAK Luggage Items v3.0.0

tony4twentys-PEAK Luggage Items.dll

Decompiled 5 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Peak;
using Peak.Network;
using Photon.Pun;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using Zorro.Core;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: AssemblyTitle("PEAK Luggage Items")]
[assembly: AssemblyDescription("Live luggage loot editor for PEAK")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("PEAK Luggage Items")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("2ee823ee-81d0-43ba-91f7-a88355d316cf")]
[assembly: AssemblyFileVersion("3.0.0.0")]
[assembly: AssemblyInformationalVersion("3.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("3.0.0.0")]
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}
namespace PEAKLuggageItems
{
	[BepInPlugin("tony4twentys.PEAK_Luggage_Items", "PEAK Luggage Items", "3.0.0")]
	[BepInProcess("PEAK.exe")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "tony4twentys.PEAK_Luggage_Items";

		public const string PluginName = "PEAK Luggage Items";

		public const string PluginVersion = "3.0.0";

		public static Plugin Instance;

		public ConfigEntry<bool> DebugLogs;

		public ConfigEntry<bool> ShowPauseMenuButton;

		public ConfigEntry<KeyCode> MenuKey;

		public ConfigEntry<KeyCode> MenuModifier;

		private Harmony _harmony;

		private Coroutine _catalogCoroutine;

		public ManualLogSource Logger => ((BaseUnityPlugin)this).Logger;

		public static bool IsDebug => (Object)(object)Instance != (Object)null && Instance.DebugLogs != null && Instance.DebugLogs.Value;

		private void Awake()
		{
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Expected O, but got Unknown
			Instance = this;
			DebugLogs = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugLogs", false, "Verbose loot apply logging. Leave off unless you are diagnosing a problem.");
			ShowPauseMenuButton = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowPauseMenuButton", true, "Show the Luggage Items button on the pause menu. The hotkey still works if this is off.");
			MenuKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "MenuKey", (KeyCode)108, "Open the in-game luggage loot menu (used with MenuModifier). Also available from the pause menu.");
			MenuModifier = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "MenuModifier", (KeyCode)306, "Optional modifier for the loot menu. Default LeftControl (Ctrl+L). Set to None to use MenuKey alone. LeftControl also accepts RightControl.");
			LuggageCatalog.Refresh();
			LootStore.Initialize();
			PresetStore.Initialize();
			PresetStore.TryLoadDefault();
			LuggageMenu.EnsureCreated();
			DebugDump.Initialize();
			_harmony = new Harmony("tony4twentys.PEAK_Luggage_Items");
			_harmony.PatchAll(typeof(Plugin).Assembly);
			SceneManager.sceneLoaded += OnSceneLoaded;
			_catalogCoroutine = ((MonoBehaviour)this).StartCoroutine(WaitForItemCatalog());
			Logger.LogInfo((object)string.Format("{0} v{1} loaded ({2} live luggage pools). Open with Ctrl+L or the pause menu. Old Item Weights / Item Spawn Pools config keys are unused now — loot is edited in the menu.", "PEAK Luggage Items", "3.0.0", LuggageCatalog.Count));
		}

		private void Update()
		{
			if (IsMenuHotkeyDown())
			{
				LuggageMenu.Toggle();
			}
		}

		internal bool IsMenuHotkeyDown()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Invalid comparison between Unknown and I4
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Invalid comparison between Unknown and I4
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Invalid comparison between Unknown and I4
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Invalid comparison between Unknown and I4
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Invalid comparison between Unknown and I4
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Invalid comparison between Unknown and I4
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Invalid comparison between Unknown and I4
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Invalid comparison between Unknown and I4
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			if (LuggageMenu.IsListeningForHotkey)
			{
				return false;
			}
			KeyCode val = (KeyCode)((MenuKey == null) ? 108 : ((int)MenuKey.Value));
			if ((int)val == 0)
			{
				return false;
			}
			KeyCode val2 = (KeyCode)((MenuModifier == null) ? 306 : ((int)MenuModifier.Value));
			if ((int)val2 == 0)
			{
				return Input.GetKeyDown(val);
			}
			bool flag = Input.GetKey(val2);
			if ((int)val2 == 306 || (int)val2 == 305)
			{
				flag = Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305);
			}
			else if ((int)val2 == 308 || (int)val2 == 307)
			{
				flag = Input.GetKey((KeyCode)308) || Input.GetKey((KeyCode)307);
			}
			else if ((int)val2 == 304 || (int)val2 == 303)
			{
				flag = Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303);
			}
			return flag && Input.GetKeyDown(val);
		}

		private void OnApplicationQuit()
		{
			try
			{
				PresetStore.SaveDefault();
			}
			catch
			{
			}
		}

		private void OnDestroy()
		{
			try
			{
				PresetStore.SaveDefault();
			}
			catch
			{
			}
			try
			{
				SceneManager.sceneLoaded -= OnSceneLoaded;
			}
			catch
			{
			}
			try
			{
				DebugDump.Shutdown();
			}
			catch
			{
			}
			try
			{
				LuggageMenu.DestroyInstance();
			}
			catch
			{
			}
			try
			{
				Harmony harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
			}
			catch
			{
			}
			if (_catalogCoroutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(_catalogCoroutine);
			}
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		private IEnumerator WaitForItemCatalog()
		{
			if (ItemCatalog.TryRefresh())
			{
				OnCatalogReady();
				yield break;
			}
			float elapsed = 0f;
			while (!ItemCatalog.TryRefresh())
			{
				elapsed += 0.25f;
				if (elapsed > 120f)
				{
					Logger.LogWarning((object)"Item database never loaded. The loot picker cannot list items.");
					yield break;
				}
				yield return (object)new WaitForSeconds(0.25f);
			}
			OnCatalogReady();
		}

		private void OnCatalogReady()
		{
			LuggageMenu.OnCatalogReady();
			Logger.LogInfo((object)($"Loaded {ItemCatalog.Count} live items for the luggage loot menu." + (LootStore.HasVanillaSnapshot ? " Saved luggage defaults are ready." : " Vanilla defaults load in the Airport or after the first climb, then persist.")));
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			if ((int)mode != 1)
			{
				LuggageMenu.OnAfterSceneLoad();
				LuggageSpawnPatches.ClearOpenState();
				if (IsAirportOrLevel(((Scene)(ref scene)).name))
				{
					LootStore.TryEnsureDefaults();
				}
				int count = ItemCatalog.Count;
				try
				{
					ItemCatalog.TryRefresh();
				}
				catch
				{
				}
				if (ItemCatalog.Count > count)
				{
					Logger.LogInfo((object)("Found " + (ItemCatalog.Count - count) + " more item(s) after scene load (" + ItemCatalog.Count + " total)."));
				}
				LuggageMenu.OnCatalogReady();
			}
		}

		private static bool IsAirportOrLevel(string sceneName)
		{
			if (string.IsNullOrEmpty(sceneName))
			{
				return false;
			}
			return sceneName.Equals("Airport", StringComparison.OrdinalIgnoreCase) || sceneName.IndexOf("Level_", StringComparison.OrdinalIgnoreCase) >= 0;
		}

		public static void SetDebug(bool enabled)
		{
			if (!((Object)(object)Instance == (Object)null) && Instance.DebugLogs != null)
			{
				Instance.DebugLogs.Value = enabled;
				((BaseUnityPlugin)Instance).Config.Save();
				LogInfo("[PLI] Debug logs " + (enabled ? "ON — open a luggage, then read BepInEx/LogOutput.log" : "OFF"));
				if (enabled)
				{
					DebugDump.DumpEverything("debug enabled");
				}
			}
		}

		public static KeyCode GetMenuKey()
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: 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)
			return (KeyCode)((!((Object)(object)Instance != (Object)null) || Instance.MenuKey == null) ? 108 : ((int)Instance.MenuKey.Value));
		}

		public static KeyCode GetMenuModifier()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			return (KeyCode)((!((Object)(object)Instance != (Object)null) || Instance.MenuModifier == null) ? 306 : ((int)Instance.MenuModifier.Value));
		}

		public static bool GetShowPauseMenuButton()
		{
			return (Object)(object)Instance == (Object)null || Instance.ShowPauseMenuButton == null || Instance.ShowPauseMenuButton.Value;
		}

		public static void SetShowPauseMenuButton(bool enabled)
		{
			if (!((Object)(object)Instance == (Object)null) && Instance.ShowPauseMenuButton != null)
			{
				Instance.ShowPauseMenuButton.Value = enabled;
				((BaseUnityPlugin)Instance).Config.Save();
				LogInfo("[PLI] Pause menu button " + (enabled ? "ON" : "OFF"));
			}
		}

		public static void SetMenuKey(KeyCode key)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)Instance == (Object)null) && Instance.MenuKey != null && (int)key != 0)
			{
				Instance.MenuKey.Value = key;
				((BaseUnityPlugin)Instance).Config.Save();
				LogInfo("[PLI] Menu key set to " + FormatHotkey());
			}
		}

		public static void SetMenuModifier(KeyCode key)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)Instance == (Object)null) && Instance.MenuModifier != null)
			{
				Instance.MenuModifier.Value = key;
				((BaseUnityPlugin)Instance).Config.Save();
				LogInfo("[PLI] Menu key set to " + FormatHotkey());
			}
		}

		public static string FormatHotkey()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			KeyCode menuKey = GetMenuKey();
			KeyCode menuModifier = GetMenuModifier();
			if ((int)menuModifier == 0)
			{
				return PrettyKey(menuKey);
			}
			return PrettyKey(menuModifier) + " + " + PrettyKey(menuKey);
		}

		public unsafe static string PrettyKey(KeyCode key)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Invalid comparison between Unknown and I4
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Invalid comparison between Unknown and I4
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected I4, but got Unknown
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Invalid comparison between Unknown and I4
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Invalid comparison between Unknown and I4
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Expected I4, but got Unknown
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Invalid comparison between Unknown and I4
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Invalid comparison between Unknown and I4
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Invalid comparison between Unknown and I4
			KeyCode val = key;
			KeyCode val2 = val;
			if ((int)val2 <= 27)
			{
				if ((int)val2 <= 8)
				{
					if ((int)val2 == 0)
					{
						return "None";
					}
					if ((int)val2 == 8)
					{
						return "Backspace";
					}
				}
				else
				{
					if ((int)val2 == 13)
					{
						return "Enter";
					}
					if ((int)val2 == 27)
					{
						return "Esc";
					}
				}
			}
			else if ((int)val2 <= 127)
			{
				if ((int)val2 == 32)
				{
					return "Space";
				}
				if ((int)val2 == 127)
				{
					return "Delete";
				}
			}
			else
			{
				switch (val2 - 277)
				{
				case 0:
					return "Insert";
				case 1:
					return "Home";
				case 2:
					return "End";
				case 3:
					return "Page Up";
				case 4:
					return "Page Down";
				}
				switch (val2 - 303)
				{
				case 3:
					return "Left Ctrl";
				case 2:
					return "Right Ctrl";
				case 5:
					return "Left Alt";
				case 4:
					return "Right Alt";
				case 1:
					return "Left Shift";
				case 0:
					return "Right Shift";
				case 7:
					return "Left Cmd";
				case 6:
					return "Right Cmd";
				}
			}
			string text = ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString();
			if (text.StartsWith("Alpha", StringComparison.Ordinal) && text.Length == 6)
			{
				return text.Substring(5);
			}
			if (text.StartsWith("Keypad", StringComparison.Ordinal))
			{
				return "Num " + text.Substring("Keypad".Length);
			}
			return text;
		}

		public static void LogTrace(string message)
		{
			if (IsDebug)
			{
				LogInfo("[PLI] " + message);
			}
		}

		public static void LogInfo(string message)
		{
			if ((Object)(object)Instance != (Object)null)
			{
				Instance.Logger.LogInfo((object)message);
			}
			else
			{
				Debug.Log((object)("[PEAK Luggage Items] " + message));
			}
		}

		public static void LogDebug(string message)
		{
			if ((Object)(object)Instance != (Object)null && Instance.DebugLogs != null && Instance.DebugLogs.Value)
			{
				Instance.Logger.LogInfo((object)message);
			}
		}

		public static void LogError(string message)
		{
			if ((Object)(object)Instance != (Object)null)
			{
				Instance.Logger.LogError((object)message);
			}
			else
			{
				Debug.LogError((object)("[PEAK Luggage Items] " + message));
			}
		}
	}
	internal static class GameAccess
	{
		private static readonly PropertyInfo WindowBlockingProp = AccessTools.Property(typeof(GUIManager), "windowBlockingInput");

		private static readonly PropertyInfo WindowCursorProp = AccessTools.Property(typeof(GUIManager), "windowShowingCursor");

		private static readonly FieldInfo LootCacheCopy = AccessTools.Field(typeof(LootData), "cachedSpawnPoolCopy");

		private static readonly FieldInfo LootCacheWorking = AccessTools.Field(typeof(LootData), "cachedSpawnPoolWorking");

		private static readonly FieldInfo LootCacheInvalid = AccessTools.Field(typeof(LootData), "cachedInvalidSpawns");

		private static readonly FieldInfo TrackerHistory = AccessTools.Field(typeof(SpawnedItemTracker), "_historyFromSave");

		private static readonly FieldInfo TrackerSpawned = AccessTools.Field(typeof(SpawnedItemTracker), "_spawnedItems");

		private static readonly FieldInfo SpawnerPool = AccessTools.Field(typeof(Spawner), "spawnPool");

		private static readonly FieldInfo SpawnerSpots = AccessTools.Field(typeof(Spawner), "spawnSpots");

		private static readonly FieldInfo SpawnerWeighted = AccessTools.Field(typeof(Spawner), "weightedSpawnSpots");

		private static readonly FieldInfo LuggageState = AccessTools.Field(typeof(Luggage), "state");

		private static readonly MethodInfo GetSpawnPoolMethod = AccessTools.Method(typeof(Spawner), "GetSpawnPool", (Type[])null, (Type[])null);

		private static readonly MethodInfo GetObjectsToSpawnMethod = AccessTools.Method(typeof(Spawner), "GetObjectsToSpawn", new Type[2]
		{
			typeof(int),
			typeof(bool)
		}, (Type[])null);

		private static readonly MethodInfo OffsetSpawnMethod = AccessTools.Method(typeof(Spawner), "OffsetSpawn", new Type[1] { typeof(Item) }, (Type[])null);

		private static readonly MethodInfo InitializePhysicsMethod = AccessTools.Method(typeof(Spawner), "InitializePhysics", new Type[1] { typeof(Item) }, (Type[])null);

		public static Button GetPauseMenuButton(PauseMenuMainPage page, string fieldName)
		{
			if ((Object)(object)page == (Object)null)
			{
				return null;
			}
			object? obj = AccessTools.Field(typeof(PauseMenuMainPage), fieldName)?.GetValue(page);
			return (Button)((obj is Button) ? obj : null);
		}

		public static void SetMenuWindowFlags(GUIManager gui, bool blocking, bool showingCursor)
		{
			if (!((Object)(object)gui == (Object)null))
			{
				WindowBlockingProp?.SetValue(gui, blocking, null);
				WindowCursorProp?.SetValue(gui, showingCursor, null);
			}
		}

		public static void ClearLootCaches()
		{
			ClearFieldCollection(LootCacheCopy);
			ClearFieldCollection(LootCacheWorking);
			ClearFieldCollection(LootCacheInvalid);
		}

		public static int GetHistoryCount(SpawnedItemTracker tracker)
		{
			return CountFieldCollection(TrackerHistory, tracker);
		}

		public static int GetTrackedSpawnCount(SpawnedItemTracker tracker)
		{
			return CountFieldCollection(TrackerSpawned, tracker);
		}

		public static SpawnPool GetSpawnerPool(Spawner spawner)
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)spawner == (Object)null || SpawnerPool == null)
			{
				return (SpawnPool)0;
			}
			try
			{
				if (SpawnerPool.GetValue(spawner) is SpawnPool result)
				{
					return result;
				}
			}
			catch
			{
			}
			return (SpawnPool)0;
		}

		public static SpawnPool InvokeGetSpawnPool(Spawner spawner)
		{
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)spawner == (Object)null || GetSpawnPoolMethod == null)
			{
				return (SpawnPool)0;
			}
			try
			{
				if (GetSpawnPoolMethod.Invoke(spawner, null) is SpawnPool result)
				{
					return result;
				}
			}
			catch (Exception ex)
			{
				Plugin.LogTrace("InvokeGetSpawnPool: " + (ex.InnerException ?? ex).Message);
			}
			return (SpawnPool)0;
		}

		public static List<GameObject> InvokeGetObjectsToSpawn(Spawner spawner, int count, bool canRepeat)
		{
			if ((Object)(object)spawner == (Object)null || GetObjectsToSpawnMethod == null)
			{
				return null;
			}
			try
			{
				return GetObjectsToSpawnMethod.Invoke(spawner, new object[2] { count, canRepeat }) as List<GameObject>;
			}
			catch (Exception ex)
			{
				Plugin.LogTrace("InvokeGetObjectsToSpawn: " + (ex.InnerException ?? ex).Message);
				return null;
			}
		}

		public static void InvokeOffsetSpawn(Spawner spawner, Item item)
		{
			if ((Object)(object)spawner == (Object)null || (Object)(object)item == (Object)null || OffsetSpawnMethod == null)
			{
				return;
			}
			try
			{
				OffsetSpawnMethod.Invoke(spawner, new object[1] { item });
			}
			catch
			{
			}
		}

		public static void InvokeInitializePhysics(Spawner spawner, Item item)
		{
			if ((Object)(object)spawner == (Object)null || (Object)(object)item == (Object)null || InitializePhysicsMethod == null)
			{
				return;
			}
			try
			{
				InitializePhysicsMethod.Invoke(spawner, new object[1] { item });
			}
			catch
			{
			}
		}

		public static List<Transform> GetSpawnSpotList(Spawner spawner)
		{
			return GetFieldValue<List<Transform>>(SpawnerSpots, spawner);
		}

		public static IList GetWeightedSpotLists(Spawner spawner)
		{
			return GetFieldValue<IList>(SpawnerWeighted, spawner);
		}

		public static List<Transform> GetEntrySpawnSpots(object entry)
		{
			if (entry == null)
			{
				return null;
			}
			try
			{
				return AccessTools.Field(entry.GetType(), "spawnSpots")?.GetValue(entry) as List<Transform>;
			}
			catch
			{
				return null;
			}
		}

		public static string GetLuggageState(Luggage luggage)
		{
			if ((Object)(object)luggage == (Object)null || LuggageState == null)
			{
				return "?";
			}
			try
			{
				object value = LuggageState.GetValue(luggage);
				return (value != null) ? value.ToString() : "?";
			}
			catch
			{
				return "?";
			}
		}

		private static T GetFieldValue<T>(FieldInfo field, object instance) where T : class
		{
			if (field == null || instance == null)
			{
				return null;
			}
			try
			{
				return field.GetValue(instance) as T;
			}
			catch
			{
				return null;
			}
		}

		private static void ClearFieldCollection(FieldInfo field)
		{
			if (field == null)
			{
				return;
			}
			try
			{
				object value = field.GetValue(null);
				if (value is IDictionary dictionary)
				{
					dictionary.Clear();
				}
				else if (value is IList list)
				{
					list.Clear();
				}
			}
			catch
			{
			}
		}

		private static int CountFieldCollection(FieldInfo field, object instance)
		{
			if (field == null || instance == null)
			{
				return 0;
			}
			try
			{
				object value = field.GetValue(instance);
				if (value is ICollection { Count: var count })
				{
					return count;
				}
			}
			catch
			{
			}
			return 0;
		}
	}
	internal sealed class CatalogItem
	{
		public ushort Id;

		public string DisplayName;

		public string DropdownName;

		public string PrefabName;

		public Item Prefab;
	}
	internal static class ItemCatalog
	{
		public const string EmptyLabel = "(empty)";

		private static readonly List<CatalogItem> _items = new List<CatalogItem>();

		private static readonly Dictionary<ushort, CatalogItem> _byId = new Dictionary<ushort, CatalogItem>();

		private static readonly Dictionary<string, CatalogItem> _byName = new Dictionary<string, CatalogItem>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, CatalogItem> _byFold = new Dictionary<string, CatalogItem>(StringComparer.OrdinalIgnoreCase);

		private static readonly HashSet<int> _seenInstances = new HashSet<int>();

		private static string[] _dropdownNames = new string[1] { "(empty)" };

		public static bool IsReady { get; private set; }

		public static int Count => _items.Count;

		public static IReadOnlyList<CatalogItem> Items => _items;

		public static string[] DropdownNames => _dropdownNames;

		public static bool TryRefresh()
		{
			ItemDatabase val = null;
			try
			{
				val = SingletonAsset<ItemDatabase>.Instance;
			}
			catch
			{
				return false;
			}
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			List<Item> list = new List<Item>();
			HashSet<int> seen = new HashSet<int>();
			if (val.itemLookup != null)
			{
				foreach (KeyValuePair<ushort, Item> item in val.itemLookup)
				{
					ConsiderItem(list, seen, item.Value);
				}
			}
			if (((DatabaseAsset<ItemDatabase, Item>)(object)val).Objects != null)
			{
				for (int i = 0; i < ((DatabaseAsset<ItemDatabase, Item>)(object)val).Objects.Count; i++)
				{
					ConsiderItem(list, seen, ((DatabaseAsset<ItemDatabase, Item>)(object)val).Objects[i]);
				}
			}
			if (list.Count == 0)
			{
				return IsReady;
			}
			if (!IsReady)
			{
				_items.Clear();
				_byId.Clear();
				_byName.Clear();
				_byFold.Clear();
				_seenInstances.Clear();
			}
			int num = 0;
			for (int j = 0; j < list.Count; j++)
			{
				Item val2 = list[j];
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				int instanceID = ((Object)val2).GetInstanceID();
				if (_seenInstances.Add(instanceID))
				{
					CatalogItem catalogItem = new CatalogItem
					{
						Id = val2.itemID,
						Prefab = val2,
						PrefabName = SafePrefabName(val2),
						DisplayName = SafeDisplayName(val2)
					};
					_items.Add(catalogItem);
					if (!_byId.ContainsKey(catalogItem.Id))
					{
						_byId[catalogItem.Id] = catalogItem;
					}
					num++;
				}
			}
			if (num > 0)
			{
				BuildDropdownNames();
			}
			IsReady = _items.Count > 0;
			return IsReady;
		}

		public static bool TryGet(ushort id, out CatalogItem item)
		{
			return _byId.TryGetValue(id, out item);
		}

		public static string GetName(ushort id)
		{
			if (_byId.TryGetValue(id, out var value) && value != null)
			{
				return value.DisplayName;
			}
			Item val = default(Item);
			if (ItemDatabase.TryGetItem(id, ref val) && (Object)(object)val != (Object)null)
			{
				return SafeDisplayName(val);
			}
			return "Unknown item " + id;
		}

		public static bool TryResolveCatalog(string value, out CatalogItem item)
		{
			item = null;
			if (string.IsNullOrEmpty(value) || string.Equals(value.Trim(), "(empty)", StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			string text = value.Trim();
			if (_byName.TryGetValue(text, out item) && (Object)(object)item?.Prefab != (Object)null)
			{
				return true;
			}
			string text2 = FoldName(text);
			if (text2.Length > 0 && _byFold.TryGetValue(text2, out item) && (Object)(object)item?.Prefab != (Object)null)
			{
				return true;
			}
			if (ushort.TryParse(text, out var result) && _byId.TryGetValue(result, out item))
			{
				return (Object)(object)item?.Prefab != (Object)null;
			}
			Item val = default(Item);
			if (ItemDatabase.TryGetItem(text, ref val) && (Object)(object)val != (Object)null)
			{
				item = WrapTransient(val);
				return (Object)(object)item?.Prefab != (Object)null;
			}
			return false;
		}

		private static void ConsiderItem(List<Item> found, HashSet<int> seen, Item item)
		{
			if (!((Object)(object)item == (Object)null))
			{
				int instanceID = ((Object)item).GetInstanceID();
				if (seen.Add(instanceID))
				{
					found.Add(item);
				}
			}
		}

		private static void BuildDropdownNames()
		{
			_byName.Clear();
			_byFold.Clear();
			Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
			for (int i = 0; i < _items.Count; i++)
			{
				string key = DropdownBaseName(_items[i]);
				if (!dictionary.ContainsKey(key))
				{
					dictionary[key] = 0;
				}
				dictionary[key]++;
			}
			List<string> list = new List<string>(_items.Count);
			foreach (CatalogItem item in _items.OrderBy<CatalogItem, string>((CatalogItem catalogItem) => catalogItem.DisplayName, StringComparer.OrdinalIgnoreCase).ThenBy<CatalogItem, string>((CatalogItem catalogItem) => catalogItem.PrefabName, StringComparer.OrdinalIgnoreCase))
			{
				string text = DropdownBaseName(item);
				string text2 = (item.DropdownName = ((dictionary[text] > 1) ? (text + " (" + Sanitize(item.PrefabName) + ")") : text));
				IndexName(text2, item);
				IndexName(item.DisplayName, item);
				IndexName(item.PrefabName, item);
				list.Add(text2);
			}
			_dropdownNames = list.ToArray();
		}

		private static string DropdownBaseName(CatalogItem item)
		{
			string text = Sanitize(item.DisplayName);
			if (string.IsNullOrEmpty(text))
			{
				text = Sanitize(item.PrefabName);
			}
			if (string.IsNullOrEmpty(text))
			{
				text = ((item.Id != 0) ? ("Item " + item.Id) : "Unnamed item");
			}
			return text;
		}

		private static void IndexName(string name, CatalogItem item)
		{
			if (item == null || string.IsNullOrEmpty(name))
			{
				return;
			}
			string text = name.Trim();
			if (text.Length != 0)
			{
				if (!_byName.ContainsKey(text))
				{
					_byName[text] = item;
				}
				string text2 = FoldName(text);
				if (text2.Length > 0 && !_byFold.ContainsKey(text2))
				{
					_byFold[text2] = item;
				}
			}
		}

		private static CatalogItem WrapTransient(Item prefab)
		{
			return new CatalogItem
			{
				Id = prefab.itemID,
				Prefab = prefab,
				PrefabName = SafePrefabName(prefab),
				DisplayName = SafeDisplayName(prefab),
				DropdownName = SafeDisplayName(prefab)
			};
		}

		private static string FoldName(string value)
		{
			if (string.IsNullOrEmpty(value))
			{
				return string.Empty;
			}
			StringBuilder stringBuilder = new StringBuilder(value.Length);
			foreach (char c in value)
			{
				if (char.IsLetterOrDigit(c))
				{
					stringBuilder.Append(char.ToLowerInvariant(c));
				}
			}
			return stringBuilder.ToString();
		}

		private static string Sanitize(string value)
		{
			if (string.IsNullOrEmpty(value))
			{
				return string.Empty;
			}
			StringBuilder stringBuilder = new StringBuilder(value.Length);
			foreach (char c in value)
			{
				if (c == '=' || c == '[' || c == ']' || c == '\\' || c == '"' || c == '\n' || c == '\r' || c == '#')
				{
					stringBuilder.Append(' ');
				}
				else
				{
					stringBuilder.Append(c);
				}
			}
			return stringBuilder.ToString().Trim();
		}

		private static string SafePrefabName(Item item)
		{
			try
			{
				if ((Object)(object)item != (Object)null && (Object)(object)((Component)item).gameObject != (Object)null && !string.IsNullOrEmpty(((Object)((Component)item).gameObject).name))
				{
					string text = ((Object)((Component)item).gameObject).name;
					int num = text.IndexOf("(Clone)", StringComparison.Ordinal);
					if (num >= 0)
					{
						text = text.Substring(0, num).Trim();
					}
					return text;
				}
				if ((Object)(object)item != (Object)null && !string.IsNullOrEmpty(((Object)item).name))
				{
					return ((Object)item).name;
				}
			}
			catch
			{
			}
			return "unknown";
		}

		private static string SafeDisplayName(Item item)
		{
			try
			{
				if (item?.UIData != null && !string.IsNullOrEmpty(item.UIData.itemName))
				{
					try
					{
						string name = item.GetName();
						if (!string.IsNullOrEmpty(name) && name.IndexOf("NAME_", StringComparison.OrdinalIgnoreCase) < 0)
						{
							return name;
						}
					}
					catch
					{
					}
					return Prettify(item.UIData.itemName);
				}
			}
			catch
			{
			}
			return Prettify(SafePrefabName(item));
		}

		private static string Prettify(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return "Unknown";
			}
			StringBuilder stringBuilder = new StringBuilder(name.Length + 8);
			for (int i = 0; i < name.Length; i++)
			{
				char c = name[i];
				if (c == '_' || c == '-')
				{
					stringBuilder.Append(' ');
					continue;
				}
				if (i > 0 && char.IsUpper(c) && !char.IsUpper(name[i - 1]) && name[i - 1] != ' ')
				{
					stringBuilder.Append(' ');
				}
				stringBuilder.Append(c);
			}
			return stringBuilder.ToString().Trim();
		}
	}
	internal sealed class LuggageEntry
	{
		public SpawnPool Pool;

		public string EnumName;

		public string DisplayName;
	}
	internal static class LuggageCatalog
	{
		private static readonly List<LuggageEntry> _entries = new List<LuggageEntry>();

		private static readonly Dictionary<SpawnPool, LuggageEntry> _byPool = new Dictionary<SpawnPool, LuggageEntry>();

		public static IReadOnlyList<LuggageEntry> All => _entries;

		public static int Count => _entries.Count;

		public static void Refresh()
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			_entries.Clear();
			_byPool.Clear();
			foreach (SpawnPool value in Enum.GetValues(typeof(SpawnPool)))
			{
				if (IsLuggagePool(value))
				{
					string enumName = ((object)value/*cast due to .constrained prefix*/).ToString();
					LuggageEntry luggageEntry = new LuggageEntry
					{
						Pool = value,
						EnumName = enumName,
						DisplayName = Prettify(enumName)
					};
					_entries.Add(luggageEntry);
					_byPool[value] = luggageEntry;
				}
			}
			_entries.Sort((LuggageEntry a, LuggageEntry b) => string.Compare(a.DisplayName, b.DisplayName, StringComparison.OrdinalIgnoreCase));
		}

		public unsafe static bool IsLuggagePool(SpawnPool pool)
		{
			string text = ((object)(*(SpawnPool*)(&pool))/*cast due to .constrained prefix*/).ToString();
			return text.StartsWith("Luggage", StringComparison.Ordinal) || string.Equals(text, "RespawnCoffin", StringComparison.Ordinal);
		}

		public static bool TryGet(SpawnPool pool, out LuggageEntry entry)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			return _byPool.TryGetValue(pool, out entry);
		}

		public static SpawnPool GuessPoolFromWorld(string objectName, string sceneName)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Invalid comparison between Unknown and I4
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Invalid comparison between Unknown and I4
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			SpawnPool val = GuessPoolFromName(objectName);
			if ((int)val > 0)
			{
				return val;
			}
			if (!string.IsNullOrEmpty(objectName) && objectName.IndexOf("Trick", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				SpawnPool val2 = GuessPoolFromName("LuggageClown");
				if ((int)val2 > 0)
				{
					return val2;
				}
			}
			if (!string.IsNullOrEmpty(sceneName))
			{
				if (sceneName.IndexOf("Airport", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return GuessPoolFromName("LuggageBeach");
				}
				if (sceneName.StartsWith("Level_", StringComparison.OrdinalIgnoreCase))
				{
					return GuessPoolFromName("Luggage" + sceneName.Substring("Level_".Length));
				}
			}
			return (SpawnPool)0;
		}

		public static SpawnPool GuessPoolFromName(string objectName)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrEmpty(objectName))
			{
				return (SpawnPool)0;
			}
			for (int i = 0; i < _entries.Count; i++)
			{
				LuggageEntry luggageEntry = _entries[i];
				if (objectName.IndexOf(luggageEntry.EnumName, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return luggageEntry.Pool;
				}
			}
			for (int j = 0; j < _entries.Count; j++)
			{
				LuggageEntry luggageEntry2 = _entries[j];
				string text = luggageEntry2.DisplayName.Replace(" ", string.Empty);
				if (text.Length >= 3 && objectName.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return luggageEntry2.Pool;
				}
			}
			return (SpawnPool)0;
		}

		public unsafe static string GetDisplayName(SpawnPool pool)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			if (_byPool.TryGetValue(pool, out var value) && value != null)
			{
				return value.DisplayName;
			}
			return Prettify(((object)(*(SpawnPool*)(&pool))/*cast due to .constrained prefix*/).ToString());
		}

		private static string Prettify(string enumName)
		{
			if (string.IsNullOrEmpty(enumName))
			{
				return "Unknown";
			}
			string text = enumName;
			if (text.StartsWith("Luggage", StringComparison.Ordinal))
			{
				text = text.Substring("Luggage".Length);
			}
			if (string.IsNullOrEmpty(text))
			{
				text = enumName;
			}
			char[] array = new char[text.Length + 8];
			int length = 0;
			for (int i = 0; i < text.Length; i++)
			{
				char c = text[i];
				if (c == '_' || c == '-')
				{
					array[length++] = ' ';
					continue;
				}
				if (i > 0 && char.IsUpper(c) && !char.IsUpper(text[i - 1]) && text[i - 1] != ' ')
				{
					array[length++] = ' ';
				}
				array[length++] = c;
			}
			return new string(array, 0, length).Trim();
		}
	}
	[Serializable]
	internal sealed class SavedLootFile
	{
		public int version = 1;

		public List<SavedPool> pools = new List<SavedPool>();

		public List<SavedPool> vanilla = new List<SavedPool>();

		public List<SavedSpotCount> spots = new List<SavedSpotCount>();
	}
	[Serializable]
	internal sealed class SavedSpotCount
	{
		public string pool;

		public int count;
	}
	[Serializable]
	internal sealed class SavedPool
	{
		public string pool;

		public List<SavedWeight> weights = new List<SavedWeight>();

		public List<int> removed = new List<int>();

		public int spawnCount;

		public int countValue;

		public bool forceCount;

		public bool randomCount;

		public int multiplier;

		public bool shareWithParty;

		public bool disableDuplicates;
	}
	internal sealed class PoolSpawnSettings
	{
		public bool ForceCount;

		public bool RandomCount;

		public int CountValue;

		public int Multiplier = 1;

		public bool ShareWithParty;

		public bool DisableDuplicates;

		public bool IsDefault => !ForceCount && !RandomCount && Multiplier <= 1 && !ShareWithParty && !DisableDuplicates;

		public PoolSpawnSettings Clone()
		{
			return new PoolSpawnSettings
			{
				ForceCount = ForceCount,
				RandomCount = RandomCount,
				CountValue = CountValue,
				Multiplier = ((Multiplier < 1) ? 1 : Multiplier),
				ShareWithParty = ShareWithParty,
				DisableDuplicates = DisableDuplicates
			};
		}
	}
	[Serializable]
	internal sealed class SavedWeight
	{
		public int id;

		public int weight;
	}
	internal sealed class LootEntry
	{
		public ushort Id;

		public int Weight;

		public bool IsCustom;

		public bool IsVanilla;
	}
	internal static class LootStore
	{
		[HarmonyPatch(typeof(LootData), "PopulateLootData")]
		[HarmonyPriority(0)]
		private static class Patch_PopulateLootData
		{
			private static void Postfix()
			{
				try
				{
					CaptureVanillaAndApply();
				}
				catch (Exception ex)
				{
					Plugin.LogError("PopulateLootData overlay failed: " + ex);
				}
			}
		}

		public const int DefaultAddWeight = 15;

		public const int MaxRolledSpawns = 24;

		private static readonly Dictionary<SpawnPool, Dictionary<ushort, int>> _vanilla = new Dictionary<SpawnPool, Dictionary<ushort, int>>();

		private static readonly Dictionary<SpawnPool, Dictionary<ushort, int>> _overrides = new Dictionary<SpawnPool, Dictionary<ushort, int>>();

		private static readonly Dictionary<SpawnPool, HashSet<ushort>> _removed = new Dictionary<SpawnPool, HashSet<ushort>>();

		private static readonly Dictionary<SpawnPool, PoolSpawnSettings> _spawn = new Dictionary<SpawnPool, PoolSpawnSettings>();

		private static readonly Dictionary<SpawnPool, int> _vanillaSpots = new Dictionary<SpawnPool, int>();

		private static string _path;

		private static bool _loaded;

		private static SavedPool _clipboard;

		public static bool HasVanillaSnapshot { get; private set; }

		public static bool HasClipboard => _clipboard != null;

		public static void Initialize()
		{
			if (!_loaded)
			{
				_path = Path.Combine(Paths.ConfigPath, "tony4twentys.PEAK_Luggage_Items.loot.json");
				Load();
				_loaded = true;
			}
		}

		public static bool IsEdited(SpawnPool pool)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<ushort, int> value;
			HashSet<ushort> value2;
			return (_overrides.TryGetValue(pool, out value) && value.Count > 0) || (_removed.TryGetValue(pool, out value2) && value2.Count > 0) || HasSpawnEdits(pool);
		}

		public static bool HasSpawnEdits(SpawnPool pool)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			PoolSpawnSettings value;
			return _spawn.TryGetValue(pool, out value) && value != null && !value.IsDefault;
		}

		public static PoolSpawnSettings GetSpawnSettings(SpawnPool pool)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			if (_spawn.TryGetValue(pool, out var value) && value != null)
			{
				return value.Clone();
			}
			return new PoolSpawnSettings();
		}

		public static int GetMaxSpotCount(SpawnPool pool)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			LuggageSpawnPatches.DiscoverVanillaSpotCounts();
			int num = 0;
			if (_vanillaSpots.TryGetValue(pool, out var value) && value > 0)
			{
				num = value;
			}
			return Mathf.Clamp(Mathf.Max(num, FallbackMaxSpots(pool)), 1, 24);
		}

		public static int GetCountValue(SpawnPool pool)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			PoolSpawnSettings spawnSettings = GetSpawnSettings(pool);
			int maxSpotCount = GetMaxSpotCount(pool);
			if (spawnSettings.CountValue > 0)
			{
				return Mathf.Clamp(spawnSettings.CountValue, 1, maxSpotCount);
			}
			return maxSpotCount;
		}

		public static int GetWantedSpawnCount(SpawnPool pool, int thisOpenSpots, int instanceMax)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			PoolSpawnSettings spawnSettings = GetSpawnSettings(pool);
			int countValue = GetCountValue(pool);
			int num = ((instanceMax > 0) ? instanceMax : thisOpenSpots);
			if (num <= 0)
			{
				num = countValue;
			}
			int num2 = Mathf.Min(countValue, num);
			int num3 = (spawnSettings.ForceCount ? Mathf.Max(0, num2) : ((!spawnSettings.RandomCount) ? Mathf.Max(0, num) : ((num2 > 0) ? Random.Range(1, num2 + 1) : 0)));
			if (num3 <= 0 && spawnSettings.Multiplier > 1)
			{
				num3 = 1;
			}
			int num4 = ((spawnSettings.Multiplier < 1) ? 1 : spawnSettings.Multiplier);
			return Mathf.Max(0, num3 * num4);
		}

		public static void RememberVanillaSpots(SpawnPool pool, int count)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			if (LuggageCatalog.IsLuggagePool(pool) && count > 0 && (!_vanillaSpots.TryGetValue(pool, out var value) || value < count))
			{
				_vanillaSpots[pool] = count;
				Save();
			}
		}

		public unsafe static int FallbackMaxSpots(SpawnPool pool)
		{
			string a = ((object)(*(SpawnPool*)(&pool))/*cast due to .constrained prefix*/).ToString();
			if (string.Equals(a, "LuggageClown", StringComparison.Ordinal))
			{
				return 6;
			}
			if (string.Equals(a, "LuggageAncient", StringComparison.Ordinal) || string.Equals(a, "LuggageCursed", StringComparison.Ordinal) || string.Equals(a, "RespawnCoffin", StringComparison.Ordinal))
			{
				return 1;
			}
			if (string.Equals(a, "LuggageClimber", StringComparison.Ordinal))
			{
				return 2;
			}
			return 3;
		}

		public static bool HasSpawnCountOverride(SpawnPool pool)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			PoolSpawnSettings spawnSettings = GetSpawnSettings(pool);
			return spawnSettings.ForceCount || spawnSettings.RandomCount || spawnSettings.Multiplier > 1;
		}

		public static bool GetShareWithParty(SpawnPool pool)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			PoolSpawnSettings value;
			return _spawn.TryGetValue(pool, out value) && value != null && value.ShareWithParty;
		}

		public static bool GetDisableDuplicates(SpawnPool pool)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			PoolSpawnSettings value;
			return _spawn.TryGetValue(pool, out value) && value != null && value.DisableDuplicates;
		}

		public static bool GetAllowRepeats(SpawnPool pool)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return ShouldAllowRepeats(pool, -1);
		}

		public static List<GameObject> PickPrefabsNoDuplicates(SpawnPool pool, int count, HashSet<ushort> used)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			List<GameObject> list = new List<GameObject>();
			if (count <= 0)
			{
				return list;
			}
			if (used == null)
			{
				used = new HashSet<ushort>();
			}
			List<LootEntry> list2 = new List<LootEntry>();
			List<LootEntry> effectiveItems = GetEffectiveItems(pool);
			for (int i = 0; i < effectiveItems.Count; i++)
			{
				if (effectiveItems[i].Weight > 0)
				{
					list2.Add(effectiveItems[i]);
				}
			}
			if (list2.Count == 0)
			{
				return list;
			}
			while (list.Count < count)
			{
				int num = 0;
				for (int j = 0; j < list2.Count; j++)
				{
					if (!used.Contains(list2[j].Id))
					{
						num += list2[j].Weight;
					}
				}
				if (num <= 0)
				{
					if (used.Count == 0)
					{
						break;
					}
					used.Clear();
					continue;
				}
				int num2 = Random.Range(0, num);
				LootEntry lootEntry = null;
				for (int k = 0; k < list2.Count; k++)
				{
					if (!used.Contains(list2[k].Id))
					{
						num2 -= list2[k].Weight;
						if (num2 < 0)
						{
							lootEntry = list2[k];
							break;
						}
					}
				}
				if (lootEntry == null)
				{
					break;
				}
				Item val = null;
				try
				{
					ItemDatabase.TryGetItem(lootEntry.Id, ref val);
				}
				catch
				{
				}
				if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).gameObject == (Object)null)
				{
					used.Add(lootEntry.Id);
					continue;
				}
				used.Add(lootEntry.Id);
				list.Add(((Component)val).gameObject);
			}
			return list;
		}

		public static int CountEnabledItems(SpawnPool pool)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			List<LootEntry> effectiveItems = GetEffectiveItems(pool);
			for (int i = 0; i < effectiveItems.Count; i++)
			{
				if (effectiveItems[i].Weight > 0)
				{
					num++;
				}
			}
			return num;
		}

		public static bool ShouldAllowRepeats(SpawnPool pool, int wanted)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			int num = CountEnabledItems(pool);
			if (wanted <= 0)
			{
				PoolSpawnSettings spawnSettings = GetSpawnSettings(pool);
				int countValue = GetCountValue(pool);
				int num2 = ((spawnSettings.Multiplier < 1) ? 1 : spawnSettings.Multiplier);
				wanted = Mathf.Max(1, countValue * num2);
			}
			if (GetDisableDuplicates(pool))
			{
				return num < wanted;
			}
			if (!_spawn.TryGetValue(pool, out var value) || value == null)
			{
				return false;
			}
			return value.ForceCount || value.RandomCount || value.Multiplier > 1;
		}

		public static void SetForceCount(SpawnPool pool, bool value)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			MutateSettings(pool, delegate(PoolSpawnSettings s)
			{
				//IL_0031: Unknown result type (might be due to invalid IL or missing references)
				s.ForceCount = value;
				if (value)
				{
					s.RandomCount = false;
					if (s.CountValue <= 0)
					{
						s.CountValue = GetMaxSpotCount(pool);
					}
				}
			});
		}

		public static void SetRandomCount(SpawnPool pool, bool value)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			MutateSettings(pool, delegate(PoolSpawnSettings s)
			{
				//IL_0031: Unknown result type (might be due to invalid IL or missing references)
				s.RandomCount = value;
				if (value)
				{
					s.ForceCount = false;
					if (s.CountValue <= 0)
					{
						s.CountValue = GetMaxSpotCount(pool);
					}
				}
			});
		}

		public static void SetCountValue(SpawnPool pool, int value)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			int max = GetMaxSpotCount(pool);
			MutateSettings(pool, delegate(PoolSpawnSettings s)
			{
				s.CountValue = Mathf.Clamp(value, 1, max);
			});
		}

		public static void SetMultiplier(SpawnPool pool, int multiplier)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			MutateSettings(pool, delegate(PoolSpawnSettings s)
			{
				s.Multiplier = Mathf.Max(1, multiplier);
			});
		}

		public static void SetShareWithParty(SpawnPool pool, bool value)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			MutateSettings(pool, delegate(PoolSpawnSettings s)
			{
				s.ShareWithParty = value;
				if (value)
				{
					s.DisableDuplicates = false;
				}
			});
		}

		public static void SetDisableDuplicates(SpawnPool pool, bool value)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			MutateSettings(pool, delegate(PoolSpawnSettings s)
			{
				s.DisableDuplicates = value;
				if (value)
				{
					s.ShareWithParty = false;
				}
			});
		}

		public static void CopyPool(SpawnPool pool)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			_clipboard = ExportPool(pool);
		}

		public static bool PastePool(SpawnPool dest)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			if (_clipboard == null)
			{
				return false;
			}
			ImportPool(dest, _clipboard);
			return true;
		}

		public unsafe static SavedPool ExportPool(SpawnPool pool)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			PoolSpawnSettings spawnSettings = GetSpawnSettings(pool);
			SavedPool savedPool = new SavedPool
			{
				pool = ((object)(*(SpawnPool*)(&pool))/*cast due to .constrained prefix*/).ToString(),
				weights = new List<SavedWeight>(),
				removed = new List<int>(),
				spawnCount = spawnSettings.CountValue,
				countValue = spawnSettings.CountValue,
				forceCount = spawnSettings.ForceCount,
				randomCount = spawnSettings.RandomCount,
				multiplier = ((spawnSettings.Multiplier < 1) ? 1 : spawnSettings.Multiplier),
				shareWithParty = spawnSettings.ShareWithParty,
				disableDuplicates = spawnSettings.DisableDuplicates
			};
			HashSet<ushort> hashSet = new HashSet<ushort>();
			List<LootEntry> effectiveItems = GetEffectiveItems(pool);
			for (int i = 0; i < effectiveItems.Count; i++)
			{
				LootEntry lootEntry = effectiveItems[i];
				if (lootEntry.Weight > 0)
				{
					savedPool.weights.Add(new SavedWeight
					{
						id = lootEntry.Id,
						weight = lootEntry.Weight
					});
					hashSet.Add(lootEntry.Id);
				}
			}
			if (_vanilla.TryGetValue(pool, out var value) && value != null)
			{
				foreach (ushort key in value.Keys)
				{
					if (!hashSet.Contains(key))
					{
						savedPool.removed.Add(key);
					}
				}
			}
			return savedPool;
		}

		public static void ImportPool(SpawnPool dest, SavedPool data)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			if (data == null)
			{
				return;
			}
			_overrides.Remove(dest);
			_removed.Remove(dest);
			_spawn.Remove(dest);
			PoolSpawnSettings poolSpawnSettings = ReadSpawnSettings(data);
			if (!poolSpawnSettings.IsDefault || poolSpawnSettings.CountValue > 0)
			{
				_spawn[dest] = poolSpawnSettings;
			}
			HashSet<ushort> hashSet = new HashSet<ushort>();
			if (data.weights != null)
			{
				for (int i = 0; i < data.weights.Count; i++)
				{
					SavedWeight savedWeight = data.weights[i];
					if (savedWeight != null && savedWeight.id >= 0 && savedWeight.id <= 65535 && savedWeight.weight > 0)
					{
						ushort num = (ushort)savedWeight.id;
						WriteWeight(dest, num, savedWeight.weight);
						hashSet.Add(num);
					}
				}
			}
			if (_vanilla.TryGetValue(dest, out var value) && value != null)
			{
				foreach (ushort key in value.Keys)
				{
					if (!hashSet.Contains(key))
					{
						GetOrCreateRemoved(dest).Add(key);
					}
				}
			}
			if (data.removed != null)
			{
				for (int j = 0; j < data.removed.Count; j++)
				{
					int num2 = data.removed[j];
					if (num2 >= 0 && num2 <= 65535)
					{
						ushort item = (ushort)num2;
						if (!hashSet.Contains(item))
						{
							GetOrCreateRemoved(dest).Add(item);
						}
					}
				}
			}
			Save();
			ApplyToGame();
		}

		public static SavedLootFile ExportUserFile()
		{
			return BuildSavedFile();
		}

		public static void ApplyUserFile(SavedLootFile file)
		{
			if (!LootJson.HasData(file))
			{
				Plugin.LogDebug("Ignored empty preset/loot file so current settings were not wiped.");
				return;
			}
			_overrides.Clear();
			_removed.Clear();
			_spawn.Clear();
			ApplySavedPools(file.pools);
			Save();
			ApplyToGame();
		}

		public static bool TryGetVanillaWeight(SpawnPool pool, ushort id, out int weight)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			weight = 0;
			Dictionary<ushort, int> value;
			return _vanilla.TryGetValue(pool, out value) && value != null && value.TryGetValue(id, out weight) && weight > 0;
		}

		public static string DescribeSpawn(SpawnPool pool, int vanillaSpots)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			PoolSpawnSettings spawnSettings = GetSpawnSettings(pool);
			int countValue = GetCountValue(pool);
			string text = (spawnSettings.ForceCount ? ("force " + countValue) : (spawnSettings.RandomCount ? ("random 1-" + countValue) : ((vanillaSpots <= 0) ? "vanilla spots" : (vanillaSpots + " vanilla spot" + ((vanillaSpots == 1) ? "" : "s")))));
			if (spawnSettings.Multiplier > 1)
			{
				text = text + "  ·  " + spawnSettings.Multiplier + "×";
			}
			if (spawnSettings.ShareWithParty)
			{
				text += "  ·  party share";
			}
			return text;
		}

		public static List<LootEntry> GetEffectiveItems(SpawnPool pool)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<ushort, int> dictionary = new Dictionary<ushort, int>();
			if (_vanilla.TryGetValue(pool, out var value))
			{
				foreach (KeyValuePair<ushort, int> item in value)
				{
					dictionary[item.Key] = item.Value;
				}
			}
			if (_removed.TryGetValue(pool, out var value2))
			{
				foreach (ushort item2 in value2)
				{
					dictionary.Remove(item2);
				}
			}
			if (_overrides.TryGetValue(pool, out var value3))
			{
				foreach (KeyValuePair<ushort, int> item3 in value3)
				{
					if (item3.Value <= 0)
					{
						dictionary.Remove(item3.Key);
					}
					else
					{
						dictionary[item3.Key] = item3.Value;
					}
				}
			}
			List<LootEntry> list = new List<LootEntry>(dictionary.Count);
			foreach (KeyValuePair<ushort, int> item4 in dictionary)
			{
				bool flag = value?.ContainsKey(item4.Key) ?? false;
				int num = 0;
				if (flag)
				{
					num = value[item4.Key];
				}
				list.Add(new LootEntry
				{
					Id = item4.Key,
					Weight = item4.Value,
					IsVanilla = flag,
					IsCustom = (!flag || num != item4.Value)
				});
			}
			list.Sort((LootEntry a, LootEntry b) => string.Compare(ItemCatalog.GetName(a.Id), ItemCatalog.GetName(b.Id), StringComparison.OrdinalIgnoreCase));
			return list;
		}

		public static int GetTotalWeight(SpawnPool pool)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			List<LootEntry> effectiveItems = GetEffectiveItems(pool);
			for (int i = 0; i < effectiveItems.Count; i++)
			{
				num += Mathf.Max(0, effectiveItems[i].Weight);
			}
			return num;
		}

		public static bool IsEnabled(SpawnPool pool, ushort id)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			List<LootEntry> effectiveItems = GetEffectiveItems(pool);
			for (int i = 0; i < effectiveItems.Count; i++)
			{
				if (effectiveItems[i].Id == id)
				{
					return effectiveItems[i].Weight > 0;
				}
			}
			return false;
		}

		public static int GetSuggestedWeight(SpawnPool pool, ushort id)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			List<LootEntry> effectiveItems = GetEffectiveItems(pool);
			for (int i = 0; i < effectiveItems.Count; i++)
			{
				if (effectiveItems[i].Id == id && effectiveItems[i].Weight > 0)
				{
					return effectiveItems[i].Weight;
				}
			}
			if (_vanilla.TryGetValue(pool, out var value) && value.TryGetValue(id, out var value2) && value2 > 0)
			{
				return value2;
			}
			return 15;
		}

		public static void SetEnabled(SpawnPool pool, ushort id, bool enabled, int weightIfEnabled)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			if (enabled)
			{
				int weight = ((weightIfEnabled > 0) ? weightIfEnabled : GetSuggestedWeight(pool, id));
				SetWeight(pool, id, weight);
			}
			else
			{
				RemoveItem(pool, id);
			}
		}

		public static void AddItem(SpawnPool pool, ushort id)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: 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_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			if (TryGetRemoved(pool, out var removed) && removed.Remove(id))
			{
				if (removed.Count == 0)
				{
					_removed.Remove(pool);
				}
				Save();
				ApplyToGame();
				return;
			}
			List<LootEntry> effectiveItems = GetEffectiveItems(pool);
			for (int i = 0; i < effectiveItems.Count; i++)
			{
				if (effectiveItems[i].Id == id)
				{
					return;
				}
			}
			int weight = 15;
			if (_vanilla.TryGetValue(pool, out var value) && value.TryGetValue(id, out var value2) && value2 > 0)
			{
				weight = value2;
			}
			SetWeight(pool, id, weight);
		}

		public static void SetWeight(SpawnPool pool, ushort id, int weight)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			if (weight <= 0)
			{
				RemoveItem(pool, id);
				return;
			}
			WriteWeight(pool, id, weight);
			Save();
			ApplyToGame();
		}

		private static void WriteWeight(SpawnPool pool, ushort id, int weight)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			if (TryGetRemoved(pool, out var removed))
			{
				removed.Remove(id);
				if (removed.Count == 0)
				{
					_removed.Remove(pool);
				}
			}
			if (_vanilla.TryGetValue(pool, out var value) && value.TryGetValue(id, out var value2) && value2 == weight)
			{
				if (_overrides.TryGetValue(pool, out var value3))
				{
					value3.Remove(id);
					if (value3.Count == 0)
					{
						_overrides.Remove(pool);
					}
				}
			}
			else
			{
				GetOrCreateOverrides(pool)[id] = weight;
			}
		}

		public static void RemoveItem(SpawnPool pool, ushort id)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			if (_overrides.TryGetValue(pool, out var value))
			{
				value.Remove(id);
				if (value.Count == 0)
				{
					_overrides.Remove(pool);
				}
			}
			if (_vanilla.TryGetValue(pool, out var value2) && value2.ContainsKey(id))
			{
				GetOrCreateRemoved(pool).Add(id);
			}
			Save();
			ApplyToGame();
		}

		public static void ResetPool(SpawnPool pool)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			_overrides.Remove(pool);
			_removed.Remove(pool);
			_spawn.Remove(pool);
			Save();
			RestorePoolFromSnapshot(pool);
			ApplyToGame();
		}

		public static void ClearPool(SpawnPool pool)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			List<LootEntry> effectiveItems = GetEffectiveItems(pool);
			for (int i = 0; i < effectiveItems.Count; i++)
			{
				ushort id = effectiveItems[i].Id;
				if (_overrides.TryGetValue(pool, out var value))
				{
					value.Remove(id);
				}
				if (_vanilla.TryGetValue(pool, out var value2) && value2.ContainsKey(id))
				{
					GetOrCreateRemoved(pool).Add(id);
				}
			}
			if (_overrides.TryGetValue(pool, out var value3) && value3.Count == 0)
			{
				_overrides.Remove(pool);
			}
			Save();
			ApplyToGame();
		}

		public static void EnableAll(SpawnPool pool)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			HashSet<ushort> hashSet = new HashSet<ushort>();
			IReadOnlyList<CatalogItem> items = ItemCatalog.Items;
			for (int i = 0; i < items.Count; i++)
			{
				CatalogItem catalogItem = items[i];
				if (catalogItem != null && !hashSet.Contains(catalogItem.Id))
				{
					hashSet.Add(catalogItem.Id);
					WriteWeight(pool, catalogItem.Id, GetSuggestedWeight(pool, catalogItem.Id));
				}
			}
			List<LootEntry> effectiveItems = GetEffectiveItems(pool);
			for (int j = 0; j < effectiveItems.Count; j++)
			{
				ushort id = effectiveItems[j].Id;
				if (!hashSet.Contains(id))
				{
					WriteWeight(pool, id, (effectiveItems[j].Weight > 0) ? effectiveItems[j].Weight : GetSuggestedWeight(pool, id));
				}
			}
			Save();
			ApplyToGame();
		}

		public static void SetAllEnabledWeights(SpawnPool pool, int weight)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			weight = Mathf.Clamp(weight, 1, 9999);
			List<LootEntry> effectiveItems = GetEffectiveItems(pool);
			for (int i = 0; i < effectiveItems.Count; i++)
			{
				WriteWeight(pool, effectiveItems[i].Id, weight);
			}
			Save();
			ApplyToGame();
		}

		public static void TryEnsureDefaults()
		{
			if (HasVanillaSnapshot)
			{
				ApplyToGame();
			}
			else if (TryCaptureFromLiveTables())
			{
				Save();
				ApplyToGame();
			}
			else if (BuildVanillaFromItemLootData())
			{
				Plugin.LogDebug("Built luggage defaults from item LootData (" + _vanilla.Count + " pools).");
				Save();
				ApplyToGame();
			}
		}

		public static void CaptureVanillaAndApply()
		{
			CaptureVanilla();
			if (HasVanillaSnapshot)
			{
				Save();
			}
			ApplyToGame();
		}

		public static void ApplyToGame()
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			if (LootData.AllSpawnWeightData == null)
			{
				return;
			}
			foreach (LuggageEntry item in LuggageCatalog.All)
			{
				SpawnPool pool = item.Pool;
				bool flag = IsEdited(pool);
				if (!LootData.AllSpawnWeightData.TryGetValue(pool, out var value) || value == null)
				{
					if (!flag && !HasVanillaSnapshot)
					{
						continue;
					}
					value = new Dictionary<ushort, int>();
					LootData.AllSpawnWeightData[pool] = value;
				}
				if (HasVanillaSnapshot)
				{
					SyncLiveToEffective(value, GetEffectiveItems(pool));
				}
				else
				{
					if (!flag)
					{
						continue;
					}
					if (_removed.TryGetValue(pool, out var value2))
					{
						foreach (ushort item2 in value2)
						{
							value.Remove(item2);
						}
					}
					if (!_overrides.TryGetValue(pool, out var value3))
					{
						continue;
					}
					foreach (KeyValuePair<ushort, int> item3 in value3)
					{
						if (item3.Value <= 0)
						{
							value.Remove(item3.Key);
						}
						else
						{
							value[item3.Key] = item3.Value;
						}
					}
				}
			}
			InvalidateLootCaches();
			Plugin.LogDebug("Applied luggage loot overlays.");
		}

		private static void SyncLiveToEffective(Dictionary<ushort, int> live, List<LootEntry> effective)
		{
			HashSet<ushort> hashSet = new HashSet<ushort>();
			for (int i = 0; i < effective.Count; i++)
			{
				LootEntry lootEntry = effective[i];
				live[lootEntry.Id] = lootEntry.Weight;
				hashSet.Add(lootEntry.Id);
			}
			List<ushort> list = new List<ushort>();
			foreach (ushort key in live.Keys)
			{
				if (!hashSet.Contains(key))
				{
					list.Add(key);
				}
			}
			for (int j = 0; j < list.Count; j++)
			{
				live.Remove(list[j]);
			}
		}

		private static bool TryCaptureFromLiveTables()
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			if (LootData.AllSpawnWeightData == null || LootData.AllSpawnWeightData.Count == 0)
			{
				return false;
			}
			int num = 0;
			foreach (KeyValuePair<SpawnPool, Dictionary<ushort, int>> allSpawnWeightDatum in LootData.AllSpawnWeightData)
			{
				if (LuggageCatalog.IsLuggagePool(allSpawnWeightDatum.Key) && allSpawnWeightDatum.Value != null && allSpawnWeightDatum.Value.Count > 0)
				{
					num++;
				}
			}
			if (num == 0)
			{
				return false;
			}
			CaptureVanilla();
			return HasVanillaSnapshot;
		}

		private static bool BuildVanillaFromItemLootData()
		{
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Unknown result type (might be due to invalid IL or missing references)
			ItemDatabase val = null;
			try
			{
				val = SingletonAsset<ItemDatabase>.Instance;
			}
			catch
			{
				return false;
			}
			if (val?.itemLookup == null || val.itemLookup.Count == 0)
			{
				return false;
			}
			Dictionary<SpawnPool, Dictionary<ushort, int>> dictionary = new Dictionary<SpawnPool, Dictionary<ushort, int>>();
			foreach (KeyValuePair<ushort, Item> item in val.itemLookup)
			{
				Item value = item.Value;
				if ((Object)(object)value == (Object)null)
				{
					continue;
				}
				LootData component = ((Component)value).GetComponent<LootData>();
				if ((Object)(object)component == (Object)null)
				{
					continue;
				}
				int value2 = 15;
				try
				{
					if (LootData.RarityWeights != null && LootData.RarityWeights.TryGetValue(component.Rarity, out var value3) && value3 > 0)
					{
						value2 = value3;
					}
				}
				catch
				{
				}
				foreach (SpawnPool value5 in Enum.GetValues(typeof(SpawnPool)))
				{
					if (!LuggageCatalog.IsLuggagePool(value5))
					{
						continue;
					}
					bool flag = false;
					try
					{
						flag = ((Enum)component.spawnLocations).HasFlag((Enum)(object)value5);
					}
					catch
					{
						continue;
					}
					if (flag)
					{
						if (!dictionary.TryGetValue(value5, out var value4) || value4 == null)
						{
							value4 = (dictionary[value5] = new Dictionary<ushort, int>());
						}
						value4[value.itemID] = value2;
					}
				}
			}
			if (dictionary.Count == 0)
			{
				return false;
			}
			_vanilla.Clear();
			foreach (KeyValuePair<SpawnPool, Dictionary<ushort, int>> item2 in dictionary)
			{
				_vanilla[item2.Key] = item2.Value;
			}
			HasVanillaSnapshot = _vanilla.Count > 0;
			return HasVanillaSnapshot;
		}

		private static void CaptureVanilla()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			_vanilla.Clear();
			if (LootData.AllSpawnWeightData == null)
			{
				return;
			}
			foreach (KeyValuePair<SpawnPool, Dictionary<ushort, int>> allSpawnWeightDatum in LootData.AllSpawnWeightData)
			{
				if (!LuggageCatalog.IsLuggagePool(allSpawnWeightDatum.Key) || allSpawnWeightDatum.Value == null)
				{
					continue;
				}
				Dictionary<ushort, int> dictionary = new Dictionary<ushort, int>(allSpawnWeightDatum.Value.Count);
				foreach (KeyValuePair<ushort, int> item in allSpawnWeightDatum.Value)
				{
					dictionary[item.Key] = item.Value;
				}
				_vanilla[allSpawnWeightDatum.Key] = dictionary;
			}
			HasVanillaSnapshot = _vanilla.Count > 0;
			Plugin.LogDebug("Captured vanilla loot for " + _vanilla.Count + " luggage pools.");
		}

		private static void RestorePoolFromSnapshot(SpawnPool pool)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			if (LootData.AllSpawnWeightData == null || !_vanilla.TryGetValue(pool, out var value))
			{
				return;
			}
			Dictionary<ushort, int> dictionary = new Dictionary<ushort, int>(value.Count);
			foreach (KeyValuePair<ushort, int> item in value)
			{
				dictionary[item.Key] = item.Value;
			}
			LootData.AllSpawnWeightData[pool] = dictionary;
		}

		private static void InvalidateLootCaches()
		{
			GameAccess.ClearLootCaches();
		}

		private static Dictionary<ushort, int> GetOrCreateOverrides(SpawnPool pool)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			if (!_overrides.TryGetValue(pool, out var value) || value == null)
			{
				value = new Dictionary<ushort, int>();
				_overrides[pool] = value;
			}
			return value;
		}

		private static HashSet<ushort> GetOrCreateRemoved(SpawnPool pool)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			if (!_removed.TryGetValue(pool, out var value) || value == null)
			{
				value = new HashSet<ushort>();
				_removed[pool] = value;
			}
			return value;
		}

		private static bool TryGetRemoved(SpawnPool pool, out HashSet<ushort> removed)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			return _removed.TryGetValue(pool, out removed) && removed != null;
		}

		private static PoolSpawnSettings ReadSpawnSettings(SavedPool saved)
		{
			PoolSpawnSettings poolSpawnSettings = new PoolSpawnSettings
			{
				ForceCount = saved.forceCount,
				RandomCount = saved.randomCount,
				CountValue = ((saved.countValue > 0) ? saved.countValue : saved.spawnCount),
				Multiplier = ((saved.multiplier < 1) ? 1 : saved.multiplier),
				ShareWithParty = saved.shareWithParty,
				DisableDuplicates = saved.disableDuplicates
			};
			if (!poolSpawnSettings.ForceCount && !poolSpawnSettings.RandomCount && saved.spawnCount > 0 && saved.countValue <= 0)
			{
				poolSpawnSettings.ForceCount = true;
				poolSpawnSettings.CountValue = saved.spawnCount;
			}
			if (poolSpawnSettings.ForceCount && poolSpawnSettings.RandomCount)
			{
				poolSpawnSettings.RandomCount = false;
			}
			if (poolSpawnSettings.ShareWithParty && poolSpawnSettings.DisableDuplicates)
			{
				poolSpawnSettings.DisableDuplicates = false;
			}
			return poolSpawnSettings;
		}

		private static void LoadVanillaSpots(List<SavedSpotCount> list)
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			if (list == null)
			{
				return;
			}
			for (int i = 0; i < list.Count; i++)
			{
				SavedSpotCount savedSpotCount = list[i];
				if (savedSpotCount != null && !string.IsNullOrEmpty(savedSpotCount.pool) && savedSpotCount.count > 0 && Enum.TryParse<SpawnPool>(savedSpotCount.pool, out SpawnPool result) && LuggageCatalog.IsLuggagePool(result))
				{
					int num = Mathf.Clamp(savedSpotCount.count, 1, 24);
					if (!_vanillaSpots.TryGetValue(result, out var value) || num > value)
					{
						_vanillaSpots[result] = num;
					}
				}
			}
		}

		private static List<SavedSpotCount> SerializeVanillaSpots()
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			List<SavedSpotCount> list = new List<SavedSpotCount>();
			foreach (KeyValuePair<SpawnPool, int> vanillaSpot in _vanillaSpots)
			{
				if (vanillaSpot.Value > 0)
				{
					list.Add(new SavedSpotCount
					{
						pool = ((object)vanillaSpot.Key/*cast due to .constrained prefix*/).ToString(),
						count = vanillaSpot.Value
					});
				}
			}
			return list;
		}

		private unsafe static void MutateSettings(SpawnPool pool, Action<PoolSpawnSettings> mutate)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			if (!_spawn.TryGetValue(pool, out var value) || value == null)
			{
				value = new PoolSpawnSettings();
			}
			mutate(value);
			if (value.IsDefault && value.CountValue <= 0)
			{
				_spawn.Remove(pool);
			}
			else
			{
				_spawn[pool] = value;
			}
			Save();
			Plugin.LogTrace("saved spawn " + ((object)(*(SpawnPool*)(&pool))/*cast due to .constrained prefix*/).ToString() + " force=" + value.ForceCount + " random=" + value.RandomCount + " count=" + value.CountValue + " mult=" + value.Multiplier + " share=" + value.ShareWithParty + " noDupes=" + value.DisableDuplicates + " kept=" + _spawn.ContainsKey(pool));
		}

		internal unsafe static string DebugDescribeAllPools()
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("lootFile=" + _path);
			IReadOnlyList<LuggageEntry> all = LuggageCatalog.All;
			for (int i = 0; i < all.Count; i++)
			{
				SpawnPool pool = all[i].Pool;
				PoolSpawnSettings spawnSettings = GetSpawnSettings(pool);
				int num = CountEnabledItems(pool);
				int maxSpotCount = GetMaxSpotCount(pool);
				int wantedSpawnCount = GetWantedSpawnCount(pool, maxSpotCount, maxSpotCount);
				stringBuilder.AppendLine("  " + all[i].DisplayName + " [" + ((object)(*(SpawnPool*)(&pool))/*cast due to .constrained prefix*/).ToString() + "] force=" + spawnSettings.ForceCount + " random=" + spawnSettings.RandomCount + " count=" + spawnSettings.CountValue + " getCount=" + GetCountValue(pool) + " maxSpots=" + maxSpotCount + " mult=" + spawnSettings.Multiplier + " wanted=" + wantedSpawnCount + " override=" + HasSpawnCountOverride(pool) + " share=" + spawnSettings.ShareWithParty + " noDupes=" + spawnSettings.DisableDuplicates + " items=" + num + " edited=" + IsEdited(pool) + " inMemory=" + _spawn.ContainsKey(pool));
			}
			return stringBuilder.ToString();
		}

		private static void LoadVanillaList(List<SavedPool> list)
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			if (list == null)
			{
				return;
			}
			for (int i = 0; i < list.Count; i++)
			{
				SavedPool savedPool = list[i];
				if (savedPool == null || string.IsNullOrEmpty(savedPool.pool) || savedPool.weights == null || !Enum.TryParse<SpawnPool>(savedPool.pool, out SpawnPool result) || !LuggageCatalog.IsLuggagePool(result))
				{
					continue;
				}
				Dictionary<ushort, int> dictionary = new Dictionary<ushort, int>();
				for (int j = 0; j < savedPool.weights.Count; j++)
				{
					SavedWeight savedWeight = savedPool.weights[j];
					if (savedWeight != null && savedWeight.id >= 0 && savedWeight.id <= 65535 && savedWeight.weight > 0)
					{
						dictionary[(ushort)savedWeight.id] = savedWeight.weight;
					}
				}
				if (dictionary.Count > 0)
				{
					_vanilla[result] = dictionary;
				}
			}
			HasVanillaSnapshot = _vanilla.Count > 0;
		}

		private static List<SavedPool> SerializeVanilla()
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			List<SavedPool> list = new List<SavedPool>();
			foreach (KeyValuePair<SpawnPool, Dictionary<ushort, int>> item in _vanilla)
			{
				if (item.Value == null || item.Value.Count == 0)
				{
					continue;
				}
				SavedPool savedPool = new SavedPool
				{
					pool = ((object)item.Key/*cast due to .constrained prefix*/).ToString(),
					weights = new List<SavedWeight>(),
					removed = new List<int>()
				};
				foreach (KeyValuePair<ushort, int> item2 in item.Value)
				{
					savedPool.weights.Add(new SavedWeight
					{
						id = item2.Key,
						weight = item2.Value
					});
				}
				list.Add(savedPool);
			}
			return list;
		}

		private static void Load()
		{
			_overrides.Clear();
			_removed.Clear();
			_spawn.Clear();
			if (string.IsNullOrEmpty(_path) || !File.Exists(_path))
			{
				return;
			}
			try
			{
				string json = File.ReadAllText(_path);
				SavedLootFile savedLootFile = LootJson.FromJson(json);
				if (LootJson.HasData(savedLootFile))
				{
					LoadVanillaList(savedLootFile.vanilla);
					LoadVanillaSpots(savedLootFile.spots);
					ApplySavedPools(savedLootFile.pools);
					Plugin.LogInfo("Loaded luggage loot overlays from " + _path);
				}
			}
			catch (Exception ex)
			{
				Plugin.LogError("Failed to load loot overlays: " + ex.Message);
			}
		}

		private static void ApplySavedPools(List<SavedPool> list)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			if (list == null)
			{
				return;
			}
			for (int i = 0; i < list.Count; i++)
			{
				SavedPool savedPool = list[i];
				if (savedPool == null || string.IsNullOrEmpty(savedPool.pool) || !Enum.TryParse<SpawnPool>(savedPool.pool, out SpawnPool result) || !LuggageCatalog.IsLuggagePool(result))
				{
					continue;
				}
				if (savedPool.weights != null)
				{
					for (int j = 0; j < savedPool.weights.Count; j++)
					{
						SavedWeight savedWeight = savedPool.weights[j];
						if (savedWeight != null && savedWeight.id >= 0 && savedWeight.id <= 65535 && savedWeight.weight > 0)
						{
							GetOrCreateOverrides(result)[(ushort)savedWeight.id] = savedWeight.weight;
						}
					}
				}
				if (savedPool.removed != null)
				{
					for (int k = 0; k < savedPool.removed.Count; k++)
					{
						int num = savedPool.removed[k];
						if (num >= 0 && num <= 65535)
						{
							GetOrCreateRemoved(result).Add((ushort)num);
						}
					}
				}
				PoolSpawnSettings poolSpawnSettings = ReadSpawnSettings(savedPool);
				if (!poolSpawnSettings.IsDefault || poolSpawnSettings.CountValue > 0)
				{
					_spawn[result] = poolSpawnSettings;
				}
			}
		}

		private unsafe static SavedLootFile BuildSavedFile()
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			SavedLootFile savedLootFile = new SavedLootFile
			{
				version = 3,
				pools = new List<SavedPool>(),
				vanilla = SerializeVanilla(),
				spots = SerializeVanillaSpots()
			};
			HashSet<SpawnPool> hashSet = new HashSet<SpawnPool>();
			foreach (SpawnPool key in _overrides.Keys)
			{
				hashSet.Add(key);
			}
			foreach (SpawnPool key2 in _removed.Keys)
			{
				hashSet.Add(key2);
			}
			foreach (SpawnPool key3 in _spawn.Keys)
			{
				hashSet.Add(key3);
			}
			foreach (SpawnPool item in hashSet)
			{
				PoolSpawnSettings spawnSettings = GetSpawnSettings(item);
				SavedPool savedPool = new SavedPool
				{
					pool = ((object)(*(SpawnPool*)(&item))/*cast due to .constrained prefix*/).ToString(),
					weights = new List<SavedWeight>(),
					removed = new List<int>(),
					spawnCount = spawnSettings.CountValue,
					countValue = spawnSettings.CountValue,
					forceCount = spawnSettings.ForceCount,
					randomCount = spawnSettings.RandomCount,
					multiplier = ((spawnSettings.Multiplier < 1) ? 1 : spawnSettings.Multiplier),
					shareWithParty = spawnSettings.ShareWithParty,
					disableDuplicates = spawnSettings.DisableDuplicates
				};
				if (_overrides.TryGetValue(item, out var value))
				{
					foreach (KeyValuePair<ushort, int> item2 in value)
					{
						savedPool.weights.Add(new SavedWeight
						{
							id = item2.Key,
							weight = item2.Value
						});
					}
				}
				if (_removed.TryGetValue(item, out var value2))
				{
					foreach (ushort item3 in value2)
					{
						savedPool.removed.Add(item3);
					}
				}
				if (savedPool.weights.Count > 0 || savedPool.removed.Count > 0 || !spawnSettings.IsDefault || spawnSettings.CountValue > 0)
				{
					savedLootFile.pools.Add(savedPool);
				}
			}
			return savedLootFile;
		}

		private static void Save()
		{
			if (string.IsNullOrEmpty(_path))
			{
				return;
			}
			try
			{
				SavedLootFile file = BuildSavedFile();
				File.WriteAllText(_path, LootJson.ToJson(file));
				PresetStore.SaveDefaultQuiet();
			}
			catch (Exception ex)
			{
				Plugin.LogError("Failed to save loot overlays: " + ex.Message);
			}
		}
	}
	internal static class LootJson
	{
		private sealed class Reader
		{
			private readonly string _s;

			private int _i;

			public Reader(string json)
			{
				_s = json ?? string.Empty;
			}

			public void SkipWs()
			{
				while (_i < _s.Length && char.IsWhiteSpace(_s[_i]))
				{
					_i++;
				}
			}

			public void Expect(char c)
			{
				SkipWs();
				if (_i >= _s.Length || _s[_i] != c)
				{
					throw new FormatException("Expected '" + c + "' at " + _i);
				}
				_i++;
			}

			public bool TryConsume(char c)
			{
				if (_i < _s.Length && _s[_i] == c)
				{
					_i++;
					return true;
				}
				return false;
			}

			public string ReadString()
			{
				SkipWs();
				Expect('"');
				StringBuilder stringBuilder = new StringBuilder();
				while (_i < _s.Length)
				{
					char c = _s[_i++];
					switch (c)
					{
					case '"':
						return stringBuilder.ToString();
					case '\\':
						if (_i < _s.Length)
						{
							char c2 = _s[_i++];
							switch (c2)
							{
							case 'n':
								stringBuilder.Append('\n');
								break;
							case 'r':
								stringBuilder.Append('\r');
								break;
							case 't':
								stringBuilder.Append('\t');
								break;
							default:
								stringBuilder.Append(c2);
								break;
							}
							break;
						}
						goto default;
					default:
						stringBuilder.Append(c);
						break;
					}
				}
				throw new FormatException("Unterminated string.");
			}

			public int ReadInt()
			{
				SkipWs();
				int i = _i;
				if (_i < _s.Length && (_s[_i] == '-' || _s[_i] == '+'))
				{
					_i++;
				}
				while (_i < _s.Length && char.IsDigit(_s[_i]))
				{
					_i++;
				}
				if (i == _i)
				{
					throw new FormatException("Expected number at " + _i);
				}
				return int.Parse(_s.Substring(i, _i - i), CultureInfo.InvariantCulture);
			}

			public bool ReadBool()
			{
				SkipWs();
				if (Match("true"))
				{
					return true;
				}
				if (Match("false"))
				{
					return false;
				}
				throw new FormatException("Expected bool at " + _i);
			}

			public void SkipValue()
			{
				SkipWs();
				if (_i >= _s.Length)
				{
					return;
				}
				switch (_s[_i])
				{
				case '"':
					ReadString();
					break;
				case '{':
					_i++;
					while (true)
					{
						SkipWs();
						if (TryConsume('}'))
						{
							break;
						}
						ReadString();
						SkipWs();
						Expect(':');
						SkipValue();
						SkipWs();
						TryConsume(',');
					}
					break;
				case '[':
					_i++;
					while (true)
					{
						SkipWs();
						if (TryConsume(']'))
						{
							break;
						}
						SkipValue();
						SkipWs();
						TryConsume(',');
					}
					break;
				default:
					if (!Match("true") && !Match("false") && !Match("null"))
					{
						ReadInt();
					}
					break;
				}
			}

			private bool Match(string token)
			{
				if (_i + token.Length > _s.Length)
				{
					return false;
				}
				for (int i = 0; i < token.Length; i++)
				{
					if (_s[_i + i] != token[i])
					{
						return false;
					}
				}
				_i += token.Length;
				return true;
			}
		}

		public static bool HasData(SavedLootFile file)
		{
			return file != null && ((file.pools != null && file.pools.Count > 0) || (file.vanilla != null && file.vanilla.Count > 0) || (file.spots != null && file.spots.Count > 0));
		}

		public static string ToJson(SavedLootFile file)
		{
			if (file == null)
			{
				file = new SavedLootFile();
			}
			StringBuilder stringBuilder = new StringBuilder(2048);
			stringBuilder.Append("{\n  \"version\": ").Append(file.version).Append(",\n");
			stringBuilder.Append("  \"pools\": ");
			WritePoolList(stringBuilder, file.pools, "  ");
			stringBuilder.Append(",\n  \"vanilla\": ");
			WritePoolList(stringBuilder, file.vanilla, "  ");
			stringBuilder.Append(",\n  \"spots\": ");
			WriteSpotList(stringBuilder, file.spots, "  ");
			stringBuilder.Append("\n}");
			return stringBuilder.ToString();
		}

		public static SavedLootFile FromJson(string json)
		{
			SavedLootFile savedLootFile = new SavedLootFile
			{
				pools = new List<SavedPool>(),
				vanilla = new List<SavedPool>(),
				spots = new List<SavedSpotCount>()
			};
			if (string.IsNullOrWhiteSpace(json))
			{
				return savedLootFile;
			}
			Reader reader = new Reader(json);
			reader.SkipWs();
			reader.Expect('{');
			while (true)
			{
				reader.SkipWs();
				if (reader.TryConsume('}'))
				{
					break;
				}
				string text = reader.ReadString();
				reader.SkipWs();
				reader.Expect(':');
				reader.SkipWs();
				switch (text)
				{
				case "version":
					savedLootFile.version = reader.ReadInt();
					break;
				case "pools":
					savedLootFile.pools = ReadPoolList(reader);
					break;
				case "vanilla":
					savedLootFile.vanilla = ReadPoolList(reader);
					break;
				case "spots":
					savedLootFile.spots = ReadSpotList(reader);
					break;
				default:
					reader.SkipValue();
					break;
				}
				reader.SkipWs();
				reader.TryConsume(',');
			}
			return savedLootFile;
		}

		private static void WritePoolList(StringBuilder sb, List<SavedPool> list, string indent)
		{
			if (list == null || list.Count == 0)
			{
				sb.Append("[]");
				return;
			}
			sb.Append("[\n");
			for (int i = 0; i < list.Count; i++)
			{
				if (i > 0)
				{
					sb.Append(",\n");
				}
				WritePool(sb, list[i], indent + "  ");
			}
			sb.Append('\n').Append(indent).Append(']');
		}

		private static void WritePool(StringBuilder sb, SavedPool pool, string indent)
		{
			if (pool == null)
			{
				sb.Append(indent).Append("{}");
				return;
			}
			sb.Append(indent).Append("{\n");
			sb.Append(indent).Append("  \"pool\": ").Append(Quote(pool.pool))
				.Append(",\n");
			sb.Append(indent).Append("  \"weights\": [");
			if (pool.weights != null)
			{
				for (int i = 0; i < pool.weights.Count; i++)
				{
					SavedWeight savedWeight = pool.weights[i];
					if (savedWeight != null)
					{
						if (i > 0)
						{
							sb.Append(", ");
						}
						sb.Append("{\"id\": ").Append(savedWeight.id).Append(", \"weight\": ")
							.Append(savedWeight.weight)
							.Append('}');
					}
				}
			}
			sb.Append("],\n");
			sb.Append(indent).Append("  \"removed\": [");
			if (pool.removed != null)
			{
				for (int j = 0; j < pool.removed.Count; j++)
				{
					if (j > 0)
					{
						sb.Append(", ");
					}
					sb.Append(pool.removed[j]);
				}
			}
			sb.Append("],\n");
			sb.Append(indent).Append("  \"spawnCount\": ").Append(pool.spawnCount)
				.Append(",\n");
			sb.Append(indent).Append("  \"countValue\": ").Append(pool.countValue)
				.Append(",\n");
			sb.Append(indent).Append("  \"forceCount\": ").Append(pool.forceCount ? "true" : "false")
				.Append(",\n");
			sb.Append(indent).Append("  \"randomCount\": ").Append(pool.randomCount ? "true" : "false")
				.Append(",\n");
			sb.Append(indent).Append("  \"multiplier\": ").Append(pool.multiplier)
				.Append(",\n");
			sb.Append(indent).Append("  \"shareWithParty\": ").Append(pool.shareWithParty ? "true" : "false")
				.Append(",\n");
			sb.Append(indent).Append("  \"disableDuplicates\": ").Append(pool.disableDuplicates ? "true" : "false")
				.Append('\n');
			sb.Append(indent).Append('}');
		}

		private static void WriteSpotList(StringBuilder sb, List<SavedSpotCount> list, string indent)
		{
			if (list == null || list.Count == 0)
			{
				sb.Append("[]");
				return;
			}
			sb.Append("[\n");
			for (int i = 0; i < list.Count; i++)
			{
				SavedSpotCount savedSpotCount = list[i];
				if (i > 0)
				{
					sb.Append(",\n");
				}
				sb.Append(indent).Append("  {\"pool\": ").Append(Quote(savedSpotCount?.pool));
				sb.Append(", \"count\": ").Append(savedSpotCount?.count ?? 0).Append('}');
			}
			sb.Append('\n').Append(indent).Append(']');
		}

		private static List<SavedPool> ReadPoolList(Reader reader)
		{
			List<SavedPool> list = new List<SavedPool>();
			reader.Expect('[');
			while (true)
			{
				reader.SkipWs();
				if (reader.TryConsume(']'))
				{
					break;
				}
				list.Add(ReadPool(reader));
				reader.SkipWs();
				reader.TryConsume(',');
			}
			return list;
		}

		private static SavedPool ReadPool(Reader reader)
		{
			SavedPool savedPool = new SavedPool
			{
				weights = new List<SavedWeight>(),
				removed = new List<int>()
			};
			reader.Expect('{');
			while (true)
			{
				reader.SkipWs();
				if (reader.TryConsume('}'))
				{
					break;
				}
				string text = reader.ReadString();
				reader.SkipWs();
				reader.Expect(':');
				reader.SkipWs();
				switch (text)
				{
				case "pool":
					savedPool.pool = reader.ReadString();
					break;
				case "weights":
					savedPool.weights = ReadWeightList(reader);
					break;
				case "removed":
					savedPool.removed = ReadIntList(reader);
					break;
				case "spawnCount":
					savedPool.spawnCount = reader.ReadInt();
					break;
				case "countValue":
					savedPool.countValue = reader.ReadInt();
					break;
				case "forceCount":
					savedPool.forceCount = reader.ReadBool();
					break;
				case "randomCount":
					savedPool.randomCount = reader.ReadBool();
					break;
				case "multiplier":
					savedPool.multiplier = reader.ReadInt();
					break;
				case "shareWithParty":
					savedPool.shareWithParty = reader.ReadBool();
					break;
				case "disableDuplicates":
					savedPool.disableDuplicates = reader.ReadBool();
					break;
				default:
					reader.SkipValue();
					break;
				}
				reader.SkipWs();
				reader.Tr