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 DebugLogs; public ConfigEntry ShowPauseMenuButton; public ConfigEntry MenuKey; public ConfigEntry 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("General", "DebugLogs", false, "Verbose loot apply logging. Leave off unless you are diagnosing a problem."); ShowPauseMenuButton = ((BaseUnityPlugin)this).Config.Bind("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("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("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 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; } 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 GetSpawnSpotList(Spawner spawner) { return GetFieldValue>(SpawnerSpots, spawner); } public static IList GetWeightedSpotLists(Spawner spawner) { return GetFieldValue(SpawnerWeighted, spawner); } public static List GetEntrySpawnSpots(object entry) { if (entry == null) { return null; } try { return AccessTools.Field(entry.GetType(), "spawnSpots")?.GetValue(entry) as List; } 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(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 _items = new List(); private static readonly Dictionary _byId = new Dictionary(); private static readonly Dictionary _byName = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary _byFold = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly HashSet _seenInstances = new HashSet(); private static string[] _dropdownNames = new string[1] { "(empty)" }; public static bool IsReady { get; private set; } public static int Count => _items.Count; public static IReadOnlyList Items => _items; public static string[] DropdownNames => _dropdownNames; public static bool TryRefresh() { ItemDatabase val = null; try { val = SingletonAsset.Instance; } catch { return false; } if ((Object)(object)val == (Object)null) { return false; } List list = new List(); HashSet seen = new HashSet(); if (val.itemLookup != null) { foreach (KeyValuePair item in val.itemLookup) { ConsiderItem(list, seen, item.Value); } } if (((DatabaseAsset)(object)val).Objects != null) { for (int i = 0; i < ((DatabaseAsset)(object)val).Objects.Count; i++) { ConsiderItem(list, seen, ((DatabaseAsset)(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 found, HashSet 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 dictionary = new Dictionary(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 list = new List(_items.Count); foreach (CatalogItem item in _items.OrderBy((CatalogItem catalogItem) => catalogItem.DisplayName, StringComparer.OrdinalIgnoreCase).ThenBy((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 _entries = new List(); private static readonly Dictionary _byPool = new Dictionary(); public static IReadOnlyList 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 pools = new List(); public List vanilla = new List(); public List spots = new List(); } [Serializable] internal sealed class SavedSpotCount { public string pool; public int count; } [Serializable] internal sealed class SavedPool { public string pool; public List weights = new List(); public List removed = new List(); 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> _vanilla = new Dictionary>(); private static readonly Dictionary> _overrides = new Dictionary>(); private static readonly Dictionary> _removed = new Dictionary>(); private static readonly Dictionary _spawn = new Dictionary(); private static readonly Dictionary _vanillaSpots = new Dictionary(); 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 value; HashSet 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 PickPrefabsNoDuplicates(SpawnPool pool, int count, HashSet used) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) List list = new List(); if (count <= 0) { return list; } if (used == null) { used = new HashSet(); } List list2 = new List(); List 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 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(), removed = new List(), 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 hashSet = new HashSet(); List 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 hashSet = new HashSet(); 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 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 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 dictionary = new Dictionary(); if (_vanilla.TryGetValue(pool, out var value)) { foreach (KeyValuePair 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 item3 in value3) { if (item3.Value <= 0) { dictionary.Remove(item3.Key); } else { dictionary[item3.Key] = item3.Value; } } } List list = new List(dictionary.Count); foreach (KeyValuePair 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 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 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 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 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 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 hashSet = new HashSet(); IReadOnlyList 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 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 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(); 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 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 live, List effective) { HashSet hashSet = new HashSet(); for (int i = 0; i < effective.Count; i++) { LootEntry lootEntry = effective[i]; live[lootEntry.Id] = lootEntry.Weight; hashSet.Add(lootEntry.Id); } List list = new List(); 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> 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.Instance; } catch { return false; } if (val?.itemLookup == null || val.itemLookup.Count == 0) { return false; } Dictionary> dictionary = new Dictionary>(); foreach (KeyValuePair item in val.itemLookup) { Item value = item.Value; if ((Object)(object)value == (Object)null) { continue; } LootData component = ((Component)value).GetComponent(); 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()); } value4[value.itemID] = value2; } } } if (dictionary.Count == 0) { return false; } _vanilla.Clear(); foreach (KeyValuePair> 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> allSpawnWeightDatum in LootData.AllSpawnWeightData) { if (!LuggageCatalog.IsLuggagePool(allSpawnWeightDatum.Key) || allSpawnWeightDatum.Value == null) { continue; } Dictionary dictionary = new Dictionary(allSpawnWeightDatum.Value.Count); foreach (KeyValuePair 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 dictionary = new Dictionary(value.Count); foreach (KeyValuePair item in value) { dictionary[item.Key] = item.Value; } LootData.AllSpawnWeightData[pool] = dictionary; } private static void InvalidateLootCaches() { GameAccess.ClearLootCaches(); } private static Dictionary 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(); _overrides[pool] = value; } return value; } private static HashSet 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(); _removed[pool] = value; } return value; } private static bool TryGetRemoved(SpawnPool pool, out HashSet 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 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(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 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 list = new List(); foreach (KeyValuePair 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 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 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 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(savedPool.pool, out SpawnPool result) || !LuggageCatalog.IsLuggagePool(result)) { continue; } Dictionary dictionary = new Dictionary(); 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 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 list = new List(); foreach (KeyValuePair> 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(), removed = new List() }; foreach (KeyValuePair 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 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(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(), vanilla = SerializeVanilla(), spots = SerializeVanillaSpots() }; HashSet hashSet = new HashSet(); 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(), removed = new List(), 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 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(), vanilla = new List(), spots = new List() }; 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 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 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 ReadPoolList(Reader reader) { List list = new List(); 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(), removed = new List() }; 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.TryConsume(','); } return savedPool; } private static List ReadWeightList(Reader reader) { List list = new List(); reader.Expect('['); while (true) { reader.SkipWs(); if (reader.TryConsume(']')) { break; } SavedWeight savedWeight = new SavedWeight(); reader.Expect('{'); while (true) { reader.SkipWs(); if (reader.TryConsume('}')) { break; } string text = reader.ReadString(); reader.SkipWs(); reader.Expect(':'); reader.SkipWs(); if (text == "id") { savedWeight.id = reader.ReadInt(); } else if (text == "weight") { savedWeight.weight = reader.ReadInt(); } else { reader.SkipValue(); } reader.SkipWs(); reader.TryConsume(','); } list.Add(savedWeight); reader.SkipWs(); reader.TryConsume(','); } return list; } private static List ReadSpotList(Reader reader) { List list = new List(); reader.Expect('['); while (true) { reader.SkipWs(); if (reader.TryConsume(']')) { break; } SavedSpotCount savedSpotCount = new SavedSpotCount(); reader.Expect('{'); while (true) { reader.SkipWs(); if (reader.TryConsume('}')) { break; } string text = reader.ReadString(); reader.SkipWs(); reader.Expect(':'); reader.SkipWs(); if (text == "pool") { savedSpotCount.pool = reader.ReadString(); } else if (text == "count") { savedSpotCount.count = reader.ReadInt(); } else { reader.SkipValue(); } reader.SkipWs(); reader.TryConsume(','); } list.Add(savedSpotCount); reader.SkipWs(); reader.TryConsume(','); } return list; } private static List ReadIntList(Reader reader) { List list = new List(); reader.Expect('['); while (true) { reader.SkipWs(); if (reader.TryConsume(']')) { break; } list.Add(reader.ReadInt()); reader.SkipWs(); reader.TryConsume(','); } return list; } private static string Quote(string value) { if (value == null) { value = string.Empty; } StringBuilder stringBuilder = new StringBuilder(value.Length + 8); stringBuilder.Append('"'); foreach (char c in value) { if (c == '"' || c == '\\') { stringBuilder.Append('\\').Append(c); continue; } switch (c) { case '\n': stringBuilder.Append("\\n"); break; case '\r': stringBuilder.Append("\\r"); break; case '\t': stringBuilder.Append("\\t"); break; default: stringBuilder.Append(c); break; } } stringBuilder.Append('"'); return stringBuilder.ToString(); } } internal static class PresetStore { public const string DefaultName = "default"; private static string _folder; public static string FolderPath { get { EnsureFolder(); return _folder; } } public static void Initialize() { EnsureFolder(); } public static List ListNames() { List list = new List(); EnsureFolder(); if (string.IsNullOrEmpty(_folder) || !Directory.Exists(_folder)) { return list; } try { string[] files = Directory.GetFiles(_folder, "*.json"); for (int i = 0; i < files.Length; i++) { list.Add(Path.GetFileNameWithoutExtension(files[i])); } list.Sort(StringComparer.OrdinalIgnoreCase); int num = list.FindIndex((string n) => IsDefaultName(n)); if (num > 0) { string item = list[num]; list.RemoveAt(num); list.Insert(0, item); } } catch (Exception ex) { Plugin.LogError("Failed to list presets: " + ex.Message); } return list; } public static bool Save(string name, out string error) { error = null; string text = Sanitize(name); if (string.IsNullOrEmpty(text)) { error = "Type a name for this preset first."; return false; } EnsureFolder(); if (string.IsNullOrEmpty(_folder)) { error = "Could not create the Presets folder next to the plugin."; return false; } try { string text2 = Path.Combine(_folder, text + ".json"); SavedLootFile savedLootFile = LootStore.ExportUserFile(); File.WriteAllText(text2, LootJson.ToJson(savedLootFile)); Plugin.LogInfo("Saved preset " + text + " (" + ((savedLootFile.pools != null) ? savedLootFile.pools.Count : 0) + " luggage) to " + text2); return true; } catch (Exception ex) { error = "Failed to save preset: " + ex.Message; Plugin.LogError(error); return false; } } public static bool Load(string name, out string error) { error = null; string text = Sanitize(name); if (string.IsNullOrEmpty(text)) { error = "Pick a preset to load."; return false; } EnsureFolder(); string text2 = Path.Combine(_folder ?? string.Empty, text + ".json"); if (!File.Exists(text2)) { error = "No preset named \"" + text + "\"."; return false; } try { SavedLootFile file = LootJson.FromJson(File.ReadAllText(text2)); if (!LootJson.HasData(file)) { error = "Preset \"" + text + "\" is empty."; return false; } LootStore.ApplyUserFile(file); Plugin.LogDebug("Loaded preset " + text + " from " + text2); return true; } catch (Exception ex) { error = "Failed to load preset: " + ex.Message; Plugin.LogError(error); return false; } } public static bool SaveDefault() { string error; return Save("default", out error); } public static void SaveDefaultQuiet() { try { EnsureFolder(); if (!string.IsNullOrEmpty(_folder)) { SavedLootFile file = LootStore.ExportUserFile(); if (LootJson.HasData(file)) { File.WriteAllText(Path.Combine(_folder, "default.json"), LootJson.ToJson(file)); } } } catch (Exception ex) { Plugin.LogError("Failed to save default preset: " + ex.Message); } } public static bool TryLoadDefault() { EnsureFolder(); string path = Path.Combine(_folder ?? string.Empty, "default.json"); if (!File.Exists(path)) { return false; } try { SavedLootFile file = LootJson.FromJson(File.ReadAllText(path)); if (!LootJson.HasData(file)) { return false; } LootStore.ApplyUserFile(file); Plugin.LogInfo("Loaded last-used luggage settings from the default preset."); return true; } catch (Exception ex) { Plugin.LogError("Failed to load the default preset: " + ex.Message); return false; } } public static bool IsDefaultName(string name) { return string.Equals(Sanitize(name), "default", StringComparison.OrdinalIgnoreCase); } public static bool Delete(string name, out string error) { error = null; string text = Sanitize(name); if (string.IsNullOrEmpty(text)) { error = "Pick a preset to delete."; return false; } if (IsDefaultName(text)) { error = "The default preset is updated automatically when you close the menu."; return false; } EnsureFolder(); string path = Path.Combine(_folder ?? string.Empty, text + ".json"); if (!File.Exists(path)) { error = "No preset named \"" + text + "\"."; return false; } try { File.Delete(path); return true; } catch (Exception ex) { error = "Failed to delete preset: " + ex.Message; Plugin.LogError(error); return false; } } public static string Sanitize(string name) { if (string.IsNullOrWhiteSpace(name)) { return string.Empty; } char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); char[] array = name.Trim().ToCharArray(); for (int i = 0; i < array.Length; i++) { char c = array[i]; bool flag = c < ' '; if (!flag) { for (int j = 0; j < invalidFileNameChars.Length; j++) { if (c == invalidFileNameChars[j]) { flag = true; break; } } } if (flag) { array[i] = '_'; } } string text = new string(array).Trim(); if (text.Length > 40) { text = text.Substring(0, 40).Trim(); } return text; } private static void EnsureFolder() { if (!string.IsNullOrEmpty(_folder) && Directory.Exists(_folder)) { return; } try { string location = typeof(Plugin).Assembly.Location; string text = (string.IsNullOrEmpty(location) ? null : Path.GetDirectoryName(location)); if (string.IsNullOrEmpty(text)) { text = Paths.PluginPath; } _folder = Path.Combine(text, "Presets"); Directory.CreateDirectory(_folder); } catch (Exception ex) { Plugin.LogError("Failed to create Presets folder: " + ex.Message); } } } internal static class LuggageSpawnPatches { [HarmonyPatch(typeof(LootData), "GetRandomItems")] private static class Patch_GetRandomItems { private static void Postfix(SpawnPool spawnPool, int count, ref List __result) { //IL_0001: 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_0026: Unknown result type (might be due to invalid IL or missing references) if ((int)spawnPool != 0 && LootStore.GetDisableDuplicates(spawnPool)) { HashSet used = _activeUniqueUsed ?? new HashSet(); __result = ReplaceWithUniquePrefabs(spawnPool, count, used); _replacedThisGetObjectsCall = true; } } } [HarmonyPatch(typeof(Spawner), "GetObjectsToSpawn")] private static class Patch_GetObjectsToSpawn { private unsafe static void Prefix(Spawner __instance, int spawnCount, ref bool canRepeat) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: 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_002a: Invalid comparison between Unknown and I4 //IL_0033: 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) _replacedThisGetObjectsCall = false; Luggage val = (Luggage)(object)((__instance is Luggage) ? __instance : null); if (val == null) { return; } SpawnPool val2 = ResolvePool(val); bool flag = canRepeat; if ((int)val2 > 0) { if (LootStore.GetDisableDuplicates(val2)) { canRepeat = false; _activeUniqueUsed = GetUsedItemIds(val); } else if (LootStore.ShouldAllowRepeats(val2, spawnCount)) { canRepeat = true; } } Plugin.LogTrace("GetObjectsToSpawn Prefix count=" + spawnCount + " canRepeat " + flag + " -> " + canRepeat + " pool=" + ((object)(*(SpawnPool*)(&val2))/*cast due to .constrained prefix*/).ToString()); } private static void Postfix(Spawner __instance, int spawnCount, ref List __result) { //IL_0016: 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_001c: 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_004e: Unknown result type (might be due to invalid IL or missing references) Luggage val = (Luggage)(object)((__instance is Luggage) ? __instance : null); if (val != null) { SpawnPool val2 = ResolvePool(val); if ((int)val2 != 0 && LootStore.GetDisableDuplicates(val2) && (!_replacedThisGetObjectsCall || HasDuplicateItemIds(__result))) { __result = ReplaceWithUniquePrefabs(val2, spawnCount, GetUsedItemIds(val)); } } } } [HarmonyPatch(typeof(Spawner), "GetSpawnSpots")] private static class Patch_GetSpawnSpots { private unsafe static void Postfix(Spawner __instance, ref List __result) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) Luggage val = (Luggage)(object)((__instance is Luggage) ? __instance : null); if (val != null) { SpawnPool pool = ResolvePool(val); int num = ((__result != null) ? __result.Count : 0); int wanted = GetWanted(val, __result); Plugin.LogTrace("GetSpawnSpots have=" + num + " wanted=" + wanted + " override=" + LootStore.HasSpawnCountOverride(pool) + " pool=" + ((object)(*(SpawnPool*)(&pool))/*cast due to .constrained prefix*/).ToString()); } } } [HarmonyPatch(typeof(Spawner), "SpawnItems")] private static class Patch_SpawnItems { private static void Prefix(Spawner __instance, ref List spawnSpots) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) Luggage val = (Luggage)(object)((__instance is Luggage) ? __instance : null); if (val != null) { SpawnPool pool = ResolvePool(val); BeginUniquePicks(val); _activeUniqueUsed = GetUsedItemIds(val); int num = ((spawnSpots != null) ? spawnSpots.Count : 0); int wanted = GetWanted(val, spawnSpots); Plugin.LogTrace("SpawnItems Prefix have=" + num + " wanted=" + wanted + " override=" + LootStore.HasSpawnCountOverride(pool) + " " + DebugDump.HostLine()); } } private static void Postfix(Spawner __instance, List spawnSpots, ref List __result) { Luggage val = (Luggage)(object)((__instance is Luggage) ? __instance : null); if (val != null) { if (__result == null) { __result = new List(); } int wanted = GetWanted(val, spawnSpots); Plugin.LogTrace("SpawnItems Postfix vanillaResult=" + __result.Count + " wanted=" + wanted); FillToWanted(val, __result, wanted); ApplyPartyShare(val, __result); RememberFilled(val, __result.Count); Plugin.LogTrace("SpawnItems Postfix final=" + __result.Count); } } } [HarmonyPatch(typeof(SpawnedItemTracker), "SpawnAndTrackFromItemHistory")] private static class Patch_SpawnFromHistory { private static void Prefix(SpawnedItemTracker __instance) { if (!((Object)(object)__instance == (Object)null)) { BeginUniquePicks(((Component)__instance).GetComponent()); } } private unsafe static void Postfix(SpawnedItemTracker __instance, ref List __result) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: 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_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null) { return; } Luggage component = ((Component)__instance).GetComponent(); Plugin.LogTrace("SpawnAndTrackFromItemHistory result=" + ((__result != null) ? __result.Count : 0) + " luggage=" + (((Object)(object)component != (Object)null) ? ((Object)component).name : "null")); if ((Object)(object)component == (Object)null) { return; } SpawnPool val = ResolvePool(component); Plugin.LogTrace("history pool=" + ((object)(*(SpawnPool*)(&val))/*cast due to .constrained prefix*/).ToString() + " override=" + LootStore.HasSpawnCountOverride(val)); if ((int)val != 0 && LootStore.HasSpawnCountOverride(val)) { if (__result == null) { __result = new List(); } FillToWanted(component, __result, GetWanted(component, GameAccess.GetSpawnSpotList((Spawner)(object)component))); ApplyPartyShare(component, __result); RememberFilled(component, __result.Count); Plugin.LogTrace("history final=" + __result.Count); } } } private const string AnchorRootName = "PLI_ExtraSpots"; private const float NearbyItemRadius = 1.6f; private static readonly Dictionary _filledCount = new Dictionary(); private static readonly Dictionary> _usedItemIds = new Dictionary>(); private static HashSet _activeUniqueUsed; private static bool _replacedThisGetObjectsCall; internal static void ClearOpenState() { _filledCount.Clear(); _usedItemIds.Clear(); _activeUniqueUsed = null; _replacedThisGetObjectsCall = false; } private static void BeginUniquePicks(Luggage luggage) { if (!((Object)(object)luggage == (Object)null)) { _usedItemIds[((Object)luggage).GetInstanceID()] = new HashSet(); } } private static HashSet GetUsedItemIds(Luggage luggage) { if ((Object)(object)luggage == (Object)null) { return new HashSet(); } int instanceID = ((Object)luggage).GetInstanceID(); if (!_usedItemIds.TryGetValue(instanceID, out var value) || value == null) { value = new HashSet(); _usedItemIds[instanceID] = value; } return value; } internal static void HandleOpened(Luggage luggage, Character interactor) { if (!((Object)(object)luggage == (Object)null) && !((Object)(object)Plugin.Instance == (Object)null)) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(FillAfterOpen(luggage)); } } private unsafe static IEnumerator FillAfterOpen(Luggage luggage) { yield return (object)new WaitForSeconds(0.2f); if ((Object)(object)luggage == (Object)null) { yield break; } if (!IsSpawnHost()) { Plugin.LogTrace("FillAfterOpen skip not host. " + DebugDump.HostLine()); yield break; } SpawnPool pool = ResolvePool(luggage); if ((int)pool == 0 || !LootStore.HasSpawnCountOverride(pool)) { Plugin.LogTrace("FillAfterOpen no count override pool=" + ((object)(*(SpawnPool*)(&pool))/*cast due to .constrained prefix*/).ToString()); yield break; } int maxSpots = CountVanillaSpots(luggage); int wanted = LootStore.GetWantedSpawnCount(pool, maxSpots, maxSpots); int have = CountExistingSpawns(luggage); Plugin.LogTrace("FillAfterOpen " + LuggageCatalog.GetDisplayName(pool) + " have=" + have + " wanted=" + wanted + " " + DebugDump.HostLine()); if (wanted > have) { List seeded = new List(wanted); for (int i = 0; i < have; i++) { seeded.Add(null); } FillToWanted(luggage, seeded, wanted); ApplyPartyShare(luggage, seeded); RememberFilled(luggage, have + CountNonNull(seeded)); } } private static void RememberFilled(Luggage luggage, int count) { if (!((Object)(object)luggage == (Object)null) && count > 0) { int instanceID = ((Object)luggage).GetInstanceID(); if (!_filledCount.TryGetValue(instanceID, out var value) || value < count) { _filledCount[instanceID] = count; } } } private static int CountExistingSpawns(Luggage luggage) { int num = 0; try { num = GameAccess.GetTrackedSpawnCount(((Component)luggage).GetComponent()); } catch { } int value = 0; if ((Object)(object)luggage != (Object)null) { _filledCount.TryGetValue(((Object)luggage).GetInstanceID(), out value); } int num2 = CountNearbyItems(luggage); int num3 = num; if (value > num3) { num3 = value; } if (num2 > num3) { num3 = num2; } Plugin.LogTrace("CountExisting tracked=" + num + " remembered=" + value + " nearby=" + num2); return num3; } private static int CountNearbyItems(Luggage luggage) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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_0074: 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) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)luggage == (Object)null) { return 0; } Vector3 val; try { val = luggage.Center(); } catch { val = ((Component)luggage).transform.position; } Item[] array = Object.FindObjectsByType((FindObjectsSortMode)0); if (array == null) { return 0; } float num = 2.5600002f; int num2 = 0; foreach (Item val2 in array) { if (!((Object)(object)val2 == (Object)null)) { Vector3 val3 = ((Component)val2).transform.position - val; if (((Vector3)(ref val3)).sqrMagnitude <= num) { num2++; } } } return num2; } private static int CountNonNull(List views) { if (views == null) { return 0; } int num = 0; for (int i = 0; i < views.Count; i++) { if ((Object)(object)views[i] != (Object)null) { num++; } } return num; } internal unsafe static SpawnPool ResolvePool(Luggage luggage) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: 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_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0080: 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_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_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_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Invalid comparison between Unknown and I4 //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0044: 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_0180: 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_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)luggage == (Object)null) { return (SpawnPool)0; } SpawnPool val = GameAccess.InvokeGetSpawnPool((Spawner)(object)luggage); if (LuggageCatalog.IsLuggagePool(val)) { return val; } SpawnPool spawnerPool = GameAccess.GetSpawnerPool((Spawner)(object)luggage); if (LuggageCatalog.IsLuggagePool(spawnerPool)) { return spawnerPool; } string text = string.Empty; bool flag = false; try { Scene activeScene = SceneManager.GetActiveScene(); text = ((Scene)(ref activeScene)).name; flag = ((Component)luggage).gameObject.scene == activeScene; } catch { } SpawnPool val2 = (SpawnPool)0; if (flag) { val2 = LuggageCatalog.GuessPoolFromWorld(((Object)luggage).name, text); } if ((int)val2 > 0) { Plugin.LogTrace("ResolvePool " + ((Object)luggage).name + " scene=" + text + " invoke=" + ((object)(*(SpawnPool*)(&val))/*cast due to .constrained prefix*/).ToString() + " field=" + ((object)(*(SpawnPool*)(&spawnerPool))/*cast due to .constrained prefix*/).ToString() + " -> " + ((object)(*(SpawnPool*)(&val2))/*cast due to .constrained prefix*/).ToString()); return val2; } Plugin.LogTrace("ResolvePool failed " + ((Object)luggage).name + " scene=" + text + " invoke=" + ((object)(*(SpawnPool*)(&val))/*cast due to .constrained prefix*/).ToString() + " field=" + ((object)(*(SpawnPool*)(&spawnerPool))/*cast due to .constrained prefix*/).ToString()); return (SpawnPool)0; } internal static void DiscoverVanillaSpotCounts() { //IL_002b: 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_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Invalid comparison between Unknown and I4 //IL_0052: Unknown result type (might be due to invalid IL or missing references) try { Luggage[] array = Resources.FindObjectsOfTypeAll(); if (array == null) { return; } foreach (Luggage val in array) { if ((Object)(object)val == (Object)null) { continue; } SpawnPool val2 = ResolvePool(val); if ((int)val2 != 0) { int num = CountVanillaSpots(val); if (num > 0) { LootStore.RememberVanillaSpots(val2, num); } } } } catch { } } internal static int CountVanillaSpots(Luggage luggage) { int num = CountSpotList(GameAccess.GetSpawnSpotList((Spawner)(object)luggage)); try { IList weightedSpotLists = GameAccess.GetWeightedSpotLists((Spawner)(object)luggage); if (weightedSpotLists != null) { for (int i = 0; i < weightedSpotLists.Count; i++) { object obj = weightedSpotLists[i]; if (obj != null) { num = Mathf.Max(num, CountSpotList(GameAccess.GetEntrySpawnSpots(obj))); } } } } catch { } return num; } private static int CountSpotList(List spots) { if (spots == null) { return 0; } int num = 0; for (int i = 0; i < spots.Count; i++) { Transform val = spots[i]; if (!((Object)(object)val == (Object)null) && (!((Object)(object)val.parent != (Object)null) || !(((Object)val.parent).name == "PLI_ExtraSpots"))) { num++; } } return num; } private static List GetLongestSpotList(Luggage luggage, List fallback) { List list = fallback; int num = CountSpotList(fallback); List spawnSpotList = GameAccess.GetSpawnSpotList((Spawner)(object)luggage); int num2 = CountSpotList(spawnSpotList); if (num2 > num) { list = spawnSpotList; num = num2; } try { IList weightedSpotLists = GameAccess.GetWeightedSpotLists((Spawner)(object)luggage); if (weightedSpotLists != null) { for (int i = 0; i < weightedSpotLists.Count; i++) { object obj = weightedSpotLists[i]; if (obj != null) { List entrySpawnSpots = GameAccess.GetEntrySpawnSpots(obj); int num3 = CountSpotList(entrySpawnSpots); if (num3 > num) { list = entrySpawnSpots; num = num3; } } } } } catch { } return list ?? fallback ?? new List(); } internal static int GetRoomPlayerCount() { try { if (PhotonNetwork.InRoom && PhotonNetwork.CurrentRoom != null) { return Mathf.Max(1, PhotonNetwork.CurrentRoom.PlayerCount); } if (PhotonNetwork.PlayerList != null && PhotonNetwork.PlayerList.Length != 0) { return PhotonNetwork.PlayerList.Length; } } catch { } return 1; } internal static List BuildSpots(Luggage luggage, List original, int wanted) { List list = new List(Mathf.Max(0, wanted)); if (wanted <= 0) { return list; } int num = original?.Count ?? 0; int num2 = Mathf.Min(num, wanted); for (int i = 0; i < num2; i++) { if ((Object)(object)original[i] != (Object)null) { list.Add(original[i]); } } int num3 = wanted - list.Count; if (num3 <= 0 || (Object)(object)luggage == (Object)null) { return list; } List list2 = new List(list.Count); for (int j = 0; j < list.Count; j++) { if ((Object)(object)list[j] != (Object)null) { list2.Add(list[j]); } } Transform orCreateAnchorRoot = GetOrCreateAnchorRoot(luggage); for (int k = 0; k < num3; k++) { Transform orCreateAnchor = GetOrCreateAnchor(orCreateAnchorRoot, k); PlaceOnVanillaSpot(luggage, orCreateAnchor, k, list2); list.Add(orCreateAnchor); } return list; } private static Transform GetOrCreateAnchorRoot(Luggage luggage) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_0045: 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) Transform val = ((Component)luggage).transform.Find("PLI_ExtraSpots"); if ((Object)(object)val != (Object)null) { return val; } GameObject val2 = new GameObject("PLI_ExtraSpots"); val2.transform.SetParent(((Component)luggage).transform, false); val2.transform.localPosition = Vector3.zero; val2.transform.localRotation = Quaternion.identity; return val2.transform; } private static Transform GetOrCreateAnchor(Transform root, int index) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown string text = "Spot_" + index; Transform val = root.Find(text); if ((Object)(object)val != (Object)null) { return val; } GameObject val2 = new GameObject(text); val2.transform.SetParent(root, false); return val2.transform; } private static void PlaceOnVanillaSpot(Luggage luggage, Transform anchor, int extraIndex, List vanillaSpots) { //IL_004f: 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_0033: 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) Transform val = null; if (vanillaSpots != null && vanillaSpots.Count > 0) { val = vanillaSpots[extraIndex % vanillaSpots.Count]; } if ((Object)(object)val != (Object)null) { anchor.position = val.position; anchor.rotation = val.rotation; } else { anchor.position = luggage.Center(); anchor.rotation = ((Component)luggage).transform.rotation; } } private static Transform GetVanillaSpot(Luggage luggage, int index) { List longestSpotList = GetLongestSpotList(luggage, GameAccess.GetSpawnSpotList((Spawner)(object)luggage)); if (longestSpotList == null || longestSpotList.Count == 0) { return null; } return longestSpotList[index % longestSpotList.Count]; } private static PhotonView SpawnCopy(Luggage luggage, Item source, int copyIndex) { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0074: 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) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)source == (Object)null) { return null; } Item val = null; try { ItemDatabase.TryGetItem(source.itemID, ref val); } catch { } if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).gameObject == (Object)null) { return null; } Transform vanillaSpot = GetVanillaSpot(luggage, copyIndex); Vector3 val2 = (((Object)(object)vanillaSpot != (Object)null) ? vanillaSpot.position : luggage.Center()); Quaternion val3 = (((Object)(object)vanillaSpot != (Object)null) ? vanillaSpot.rotation : ((Component)luggage).transform.rotation); GameObject val4 = null; try { val4 = PhotonNetwork.InstantiateItemRoom(((Object)((Component)val).gameObject).name, val2, val3); } catch (Exception ex) { Plugin.LogDebug("Party-share spawn failed: " + ex.Message); return null; } if ((Object)(object)val4 == (Object)null) { return null; } Item component = val4.GetComponent(); if ((Object)(object)component == (Object)null) { return null; } GameAccess.InvokeOffsetSpawn((Spawner)(object)luggage, component); GameAccess.InvokeInitializePhysics((Spawner)(object)luggage, component); return ((Component)component).GetComponent(); } private static bool IsSpawnHost() { try { if (NetCode.Session.IsHost) { return true; } } catch { } try { if (PhotonNetwork.IsMasterClient) { return true; } } catch { } return false; } private static int GetWanted(Luggage luggage, List spots) { //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_0050: 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) SpawnPool val = ResolvePool(luggage); if ((int)val == 0) { return spots?.Count ?? 0; } int num = CountVanillaSpots(luggage); if (num <= 0 && spots != null) { num = CountSpotList(spots); } if (num > 0) { LootStore.RememberVanillaSpots(val, num); } return LootStore.GetWantedSpawnCount(val, num, num); } private static PhotonView SpawnOne(Luggage luggage, GameObject prefab, Transform spot, int index, int total) { //IL_0043: 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_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) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: 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_0148: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_015e: 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) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)prefab == (Object)null || (Object)(object)luggage == (Object)null) { return null; } Vector3 val; Quaternion rotation; if ((Object)(object)spot != (Object)null) { val = spot.position; rotation = spot.rotation; } else { val = luggage.Center(); rotation = ((Component)luggage).transform.rotation; } GameObject val2 = null; try { string text = (((Object)(object)prefab.gameObject != (Object)null) ? ((Object)prefab.gameObject).name : ((Object)prefab).name); val2 = PhotonNetwork.InstantiateItemRoom(text, val, rotation); } catch (Exception ex) { Plugin.LogDebug("Spawn failed: " + ex.Message); return null; } if ((Object)(object)val2 == (Object)null) { return null; } Item component = val2.GetComponent(); if ((Object)(object)component == (Object)null) { return null; } try { if (Object.op_Implicit((Object)(object)((Spawner)luggage).spawnUpTowardsTarget)) { Transform transform = ((Component)component).transform; Vector3 val3 = ((Spawner)luggage).spawnUpTowardsTarget.position - ((Component)component).transform.position; transform.up = ((Vector3)(ref val3)).normalized; } } catch { } try { if (((Spawner)luggage).centerItemsVisually && (Object)(object)spot != (Object)null) { Transform transform2 = ((Component)component).transform; transform2.position += spot.position - component.Center(); } } catch { } GameAccess.InvokeOffsetSpawn((Spawner)(object)luggage, component); GameAccess.InvokeInitializePhysics((Spawner)(object)luggage, component); return ((Component)component).GetComponent(); } private static void FillToWanted(Luggage luggage, List result, int wanted) { //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_02c1: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)luggage == (Object)null) { Plugin.LogTrace("FillToWanted abort luggage=null"); return; } if (result == null) { Plugin.LogTrace("FillToWanted abort result=null"); return; } if (!IsSpawnHost()) { Plugin.LogTrace("FillToWanted abort not host. " + DebugDump.HostLine() + " have=" + result.Count + " wanted=" + wanted); return; } if (wanted <= result.Count) { Plugin.LogTrace("FillToWanted skip already have " + result.Count + " >= wanted " + wanted); return; } Plugin.LogTrace("FillToWanted start have=" + result.Count + " wanted=" + wanted + " " + DebugDump.HostLine()); SpawnPool val = ResolvePool(luggage); List list = BuildSpots(luggage, GetLongestSpotList(luggage, GameAccess.GetSpawnSpotList((Spawner)(object)luggage)), wanted); bool canRepeat = (int)val != 0 && LootStore.ShouldAllowRepeats(val, wanted); int count = result.Count; if (result.Count == 0) { List list2 = null; list2 = GameAccess.InvokeGetObjectsToSpawn((Spawner)(object)luggage, wanted, canRepeat); if (list2 != null) { for (int i = 0; i < list2.Count; i++) { if (result.Count >= wanted) { break; } Transform spot = ((result.Count < list.Count) ? list[result.Count] : null); PhotonView val2 = SpawnOne(luggage, list2[i], spot, result.Count, wanted); if ((Object)(object)val2 != (Object)null) { result.Add(val2); } } } } int num = 0; while (result.Count < wanted && num++ < wanted) { List list3 = null; list3 = GameAccess.InvokeGetObjectsToSpawn((Spawner)(object)luggage, 1, canRepeat); if (list3 == null || list3.Count == 0 || (Object)(object)list3[0] == (Object)null) { break; } Transform spot2 = ((result.Count < list.Count) ? list[result.Count] : null); PhotonView val3 = SpawnOne(luggage, list3[0], spot2, result.Count, wanted); if ((Object)(object)val3 == (Object)null) { break; } result.Add(val3); } Plugin.LogTrace("FillToWanted done " + LuggageCatalog.GetDisplayName(val) + " " + count + " -> " + result.Count + " / wanted " + wanted + " repeats=" + canRepeat); } private static void ApplyPartyShare(Luggage luggage, List result) { //IL_0025: 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_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)luggage == (Object)null || result == null || result.Count == 0) { return; } SpawnPool val = ResolvePool(luggage); if ((int)val == 0 || !LootStore.GetShareWithParty(val)) { return; } int num = GetRoomPlayerCount() - 1; if (num <= 0) { return; } int count = result.Count; if (num <= 0) { return; } List list = new List(count * num); for (int i = 0; i < count; i++) { PhotonView val2 = result[i]; Item val3 = (((Object)(object)val2 != (Object)null) ? ((Component)val2).GetComponent() : null); if ((Object)(object)val3 == (Object)null) { continue; } for (int j = 0; j < num; j++) { PhotonView val4 = SpawnCopy(luggage, val3, i * num + j); if ((Object)(object)val4 != (Object)null) { list.Add(val4); } } } for (int k = 0; k < list.Count; k++) { result.Add(list[k]); } Plugin.LogDebug(LuggageCatalog.GetDisplayName(val) + " party share: " + count + " item(s) x" + (num + 1) + " players = " + result.Count + " on the ground."); } private static bool TryGetPrefabItemId(GameObject prefab, out ushort id) { id = 0; if ((Object)(object)prefab == (Object)null) { return false; } Item component = prefab.GetComponent(); if ((Object)(object)component == (Object)null) { return false; } id = component.itemID; return true; } private static bool HasDuplicateItemIds(List objects) { if (objects == null || objects.Count < 2) { return false; } HashSet hashSet = new HashSet(); for (int i = 0; i < objects.Count; i++) { if (TryGetPrefabItemId(objects[i], out var id) && !hashSet.Add(id)) { return true; } } return false; } private unsafe static List ReplaceWithUniquePrefabs(SpawnPool pool, int count, HashSet used) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) List list = LootStore.PickPrefabsNoDuplicates(pool, count, used); Plugin.LogTrace("no-dupe pick count=" + (list?.Count ?? 0) + " pool=" + ((object)(*(SpawnPool*)(&pool))/*cast due to .constrained prefix*/).ToString()); return list; } } internal static class DebugDump { [HarmonyPatch(typeof(Luggage), "OpenLuggageRPC")] private static class Patch_OpenLuggageRPC { private static void Prefix(Luggage __instance, bool spawnItems) { try { LogLuggage("OpenLuggageRPC Prefix spawnItems=" + spawnItems, __instance); } catch (Exception ex) { Plugin.LogTrace("OpenLuggageRPC Prefix log failed: " + ex.Message); } } private static void Postfix(Luggage __instance, bool spawnItems) { try { LogLuggage("OpenLuggageRPC Postfix spawnItems=" + spawnItems, __instance); } catch (Exception ex) { Plugin.LogTrace("OpenLuggageRPC Postfix log failed: " + ex.Message); } } } private static bool _hookedOpen; internal static void Initialize() { if (_hookedOpen) { return; } try { GlobalEvents.OnLuggageOpened = (Action)Delegate.Combine(GlobalEvents.OnLuggageOpened, new Action(LuggageSpawnPatches.HandleOpened)); GlobalEvents.OnLuggageOpened = (Action)Delegate.Combine(GlobalEvents.OnLuggageOpened, new Action(OnLuggageOpened)); _hookedOpen = true; } catch (Exception ex) { Plugin.LogError("Failed to hook GlobalEvents.OnLuggageOpened: " + ex.Message); } } internal static void Shutdown() { if (_hookedOpen) { try { GlobalEvents.OnLuggageOpened = (Action)Delegate.Remove(GlobalEvents.OnLuggageOpened, new Action(OnLuggageOpened)); } catch { } try { GlobalEvents.OnLuggageOpened = (Action)Delegate.Remove(GlobalEvents.OnLuggageOpened, new Action(LuggageSpawnPatches.HandleOpened)); } catch { } _hookedOpen = false; } } internal static void DumpEverything(string reason) { //IL_0048: 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) if (!Plugin.IsDebug) { return; } try { StringBuilder stringBuilder = new StringBuilder(2048); stringBuilder.AppendLine("======== PLI DEBUG DUMP (" + reason + ") ========"); stringBuilder.AppendLine("version=3.0.0"); Scene activeScene = SceneManager.GetActiveScene(); stringBuilder.AppendLine("scene=" + ((Scene)(ref activeScene)).name + " time=" + Time.time.ToString("0.00")); stringBuilder.AppendLine("menuOpen=" + LuggageMenu.IsOpen); stringBuilder.AppendLine(HostLine()); stringBuilder.AppendLine("items=" + ItemCatalog.Count + " ready=" + ItemCatalog.IsReady + " luggagePools=" + LuggageCatalog.Count + " vanillaSnapshot=" + LootStore.HasVanillaSnapshot); stringBuilder.AppendLine(LootStore.DebugDescribeAllPools()); Plugin.LogInfo(stringBuilder.ToString()); } catch (Exception ex) { Plugin.LogError("Debug dump failed: " + ex); } } internal static string HostLine() { bool flag = false; bool flag2 = false; bool flag3 = false; int num = 1; string text = string.Empty; try { flag = PhotonNetwork.InRoom; } catch (Exception ex) { text = text + " inRoom=" + ex.Message; } try { flag2 = PhotonNetwork.IsMasterClient; } catch (Exception ex2) { text = text + " master=" + ex2.Message; } try { flag3 = NetCode.Session.IsHost; } catch (Exception ex3) { text = text + " isHost=" + ex3.Message; } try { num = LuggageSpawnPatches.GetRoomPlayerCount(); } catch { } return "inRoom=" + flag + " IsMasterClient=" + flag2 + " NetCode.IsHost=" + flag3 + " players=" + num + text; } internal unsafe static void LogLuggage(string where, Luggage luggage, string extra = null) { //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_025a: 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_003f: 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) if (!Plugin.IsDebug) { return; } try { if ((Object)(object)luggage == (Object)null) { Plugin.LogTrace(where + " luggage=null " + extra); return; } SpawnPool pool = LuggageSpawnPatches.ResolvePool(luggage); PoolSpawnSettings spawnSettings = LootStore.GetSpawnSettings(pool); int thisOpenSpots = 0; int instanceMax = 0; bool flag = false; int num = 0; try { thisOpenSpots = GameAccess.GetSpawnSpotList((Spawner)(object)luggage)?.Count ?? 0; } catch { } try { instanceMax = LuggageSpawnPatches.CountVanillaSpots(luggage); } catch { } try { SpawnedItemTracker component = ((Component)luggage).GetComponent(); if ((Object)(object)component != (Object)null) { flag = component.HasSpawnHistory; num = GameAccess.GetHistoryCount(component); } } catch (Exception ex) { Plugin.LogTrace(where + " tracker read failed: " + ex.Message); } int wantedSpawnCount = LootStore.GetWantedSpawnCount(pool, thisOpenSpots, instanceMax); Plugin.LogTrace(where + " go=" + ((Object)luggage).name + " type=" + ((object)luggage).GetType().Name + " pool=" + ((object)(*(SpawnPool*)(&pool))/*cast due to .constrained prefix*/).ToString() + " (" + LuggageCatalog.GetDisplayName(pool) + ") state=" + GameAccess.GetLuggageState(luggage) + " spotsNow=" + thisOpenSpots + " instanceMax=" + instanceMax + " force=" + spawnSettings.ForceCount + " random=" + spawnSettings.RandomCount + " countValue=" + spawnSettings.CountValue + " storedCount=" + LootStore.GetCountValue(pool) + " mult=" + spawnSettings.Multiplier + " wanted=" + wantedSpawnCount + " override=" + LootStore.HasSpawnCountOverride(pool) + " share=" + spawnSettings.ShareWithParty + " noDupes=" + spawnSettings.DisableDuplicates + " enabledItems=" + LootStore.CountEnabledItems(pool) + " history=" + flag + " historyRecords=" + num + " " + HostLine() + (string.IsNullOrEmpty(extra) ? string.Empty : (" " + extra))); } catch (Exception ex2) { Plugin.LogTrace(where + " log failed: " + ex2); } } private static void OnLuggageOpened(Luggage luggage, Character interactor) { try { if (Plugin.IsDebug) { string text = (((Object)(object)interactor != (Object)null) ? ((Object)interactor).name : "null"); LogLuggage("GlobalEvents.OnLuggageOpened opener=" + text, luggage); if ((Object)(object)Plugin.Instance != (Object)null) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(DumpAfterOpen(luggage)); } } } catch (Exception ex) { Plugin.LogTrace("OnLuggageOpened log failed: " + ex.Message); } } private static IEnumerator DumpAfterOpen(Luggage luggage) { yield return (object)new WaitForSeconds(0.35f); LogLuggage("after-open +0.35s", luggage); } } internal static class LuggageUi { internal static readonly Color PanelColor = new Color(0.08f, 0.06f, 0.05f, 0.94f); internal static readonly Color ButtonColor = new Color(0.1792453f, 0.1253449f, 0.09046815f, 62f / 85f); internal static readonly Color ButtonHover = new Color(0.26f, 0.18f, 0.13f, 0.92f); internal static readonly Color Cream = new Color(0.95f, 0.93f, 0.88f, 1f); internal static readonly Color CreamDim = new Color(0.82f, 0.78f, 0.72f, 0.92f); internal static readonly Color AccentOn = new Color(0.55f, 0.42f, 0.22f, 0.95f); internal static readonly Color EditedColor = new Color(0.85f, 0.72f, 0.38f, 1f); internal static readonly Color FieldWellColor = new Color(0.22f, 0.16f, 0.11f, 1f); private static readonly Color CheckboxWellColor = new Color(0.12f, 0.09f, 0.07f, 1f); private static readonly Color CheckboxOnColor = new Color(0.92f, 0.78f, 0.38f, 1f); private static Sprite _whiteSprite; private static GameObject _buttonDonor; private static TMP_FontAsset _cachedFont; private static int _cachedSceneHandle = -1; internal static Canvas CreateOverlayCanvas(GameObject root, int sortingOrder) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) Canvas val = root.GetComponent() ?? root.AddComponent(); val.renderMode = (RenderMode)0; val.overrideSorting = true; val.sortingOrder = sortingOrder; val.additionalShaderChannels = (AdditionalCanvasShaderChannels)25; CanvasScaler val2 = root.GetComponent() ?? root.AddComponent(); val2.uiScaleMode = (ScaleMode)1; val2.referenceResolution = new Vector2(1920f, 1080f); val2.matchWidthOrHeight = 1f; if ((Object)(object)root.GetComponent() == (Object)null) { root.AddComponent(); } return val; } internal static RectTransform CreatePanel(Transform parent, string name, Vector2 size, Color color) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown //IL_005d: 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_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(Image) }); val.transform.SetParent(parent, false); RectTransform component = val.GetComponent(); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(0.5f, 0.5f); component.anchorMax = val2; component.anchorMin = val2; component.pivot = new Vector2(0.5f, 0.5f); component.sizeDelta = size; Image component2 = val.GetComponent(); component2.sprite = WhiteSprite(); component2.type = (Type)1; ((Graphic)component2).color = color; ((Graphic)component2).raycastTarget = true; return component; } internal static TextMeshProUGUI CreateLabel(Transform parent, string name, string text, float fontSize, FontStyles style = (FontStyles)0, TextAlignmentOptions align = (TextAlignmentOptions)513) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[2] { typeof(RectTransform), typeof(CanvasRenderer) }); val.transform.SetParent(parent, false); TextMeshProUGUI val2 = val.AddComponent(); ((TMP_Text)val2).text = text ?? string.Empty; ((TMP_Text)val2).fontSize = fontSize; ((TMP_Text)val2).fontStyle = style; ((TMP_Text)val2).alignment = align; ((Graphic)val2).color = Cream; ((Graphic)val2).raycastTarget = false; ((TMP_Text)val2).enableWordWrapping = false; ((TMP_Text)val2).overflowMode = (TextOverflowModes)0; ((MaskableGraphic)val2).maskable = true; ApplyFont(val2); return val2; } internal static LayoutElement MakeNonCollapsing(GameObject go, float height, float minWidth = 0f) { LayoutElement val = go.GetComponent() ?? go.AddComponent(); val.minHeight = height; val.preferredHeight = height; val.flexibleHeight = 0f; if (minWidth > 0f) { val.minWidth = minWidth; if (val.preferredWidth < minWidth) { val.preferredWidth = minWidth; } } return val; } internal static Button CreateButton(Transform parent, string label, UnityAction onClick, Vector2 preferredSize) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) return CreatePlainButton(parent, label, onClick, preferredSize); } internal static Button CreatePlainButton(Transform parent, string label, UnityAction onClick, Vector2 preferredSize) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) GameObject val = CreatePlainButtonObject(parent, label); ApplyButtonSize(val, preferredSize); TextMeshProUGUI componentInChildren = val.GetComponentInChildren(true); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).text = label ?? string.Empty; ((TMP_Text)componentInChildren).overflowMode = (TextOverflowModes)0; ((MaskableGraphic)componentInChildren).maskable = true; ApplyFont(componentInChildren); } Button component = val.GetComponent