Decompiled source of PEAK Luggage Items v3.0.0
tony4twentys-PEAK Luggage Items.dll
Decompiled 5 days ago
The result has been truncated due to the large size, download it to view full contents!
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