using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using HexQuickStackStorage.UI; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("HexQuickStackStorage")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("HexQuickStackStorage")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("dfffd921-4f13-41b7-9dd6-fed57ea5fb5c")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace HexQuickStackStorage { [BepInPlugin("com.hex.quickstackstorage", "HexQuickStackStorage", "1.0.0")] public class Plugin : BaseUnityPlugin { private const string PluginGuid = "com.hex.quickstackstorage"; private const string PluginName = "HexQuickStackStorage"; private const string PluginVersion = "1.0.0"; private ConfigEntry _searchRadius; private ConfigEntry _quickStackShortcut; private Harmony _harmonyInstance; internal static Plugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } internal static float SearchRadius { get { if (Instance?._searchRadius == null) { return 25f; } return Instance._searchRadius.Value; } } internal static KeyboardShortcut QuickStackShortcut { get { //IL_002a: 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) if (Instance?._quickStackShortcut == null) { return new KeyboardShortcut((KeyCode)112, Array.Empty()); } return Instance._quickStackShortcut.Value; } } private void Awake() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown //IL_0067: 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_0096: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; _searchRadius = ((BaseUnityPlugin)this).Config.Bind("General", "SearchRadius", 25f, new ConfigDescription("The radius in which to search for nearby containers when quick stacking.", (AcceptableValueBase)(object)new AcceptableValueRange(5f, 150f), Array.Empty())); _quickStackShortcut = ((BaseUnityPlugin)this).Config.Bind("General", "QuickStackShortcut", new KeyboardShortcut((KeyCode)112, Array.Empty()), "Keyboard shortcut used to quick stack nearby containers."); TrashService.Initialize(); Assembly executingAssembly = Assembly.GetExecutingAssembly(); _harmonyInstance = new Harmony("com.hex.quickstackstorage"); _harmonyInstance.PatchAll(executingAssembly); Log.LogInfo((object)"HexQuickStackStorage v1.0.0 loaded."); } private void Update() { //IL_0008: 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 (IsTypingInInputField()) { return; } KeyboardShortcut quickStackShortcut = QuickStackShortcut; if (((KeyboardShortcut)(ref quickStackShortcut)).IsDown()) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { QuickStackService.QuickStack(localPlayer); } } } private void OnDestroy() { Harmony harmonyInstance = _harmonyInstance; if (harmonyInstance != null) { harmonyInstance.UnpatchSelf(); } ManualLogSource log = Log; if (log != null) { log.LogInfo((object)"HexQuickStackStorage v1.0.0 unloaded."); } Instance = null; } private static bool IsTypingInInputField() { if ((Object)(object)EventSystem.current == (Object)null || (Object)(object)EventSystem.current.currentSelectedGameObject == (Object)null) { return false; } GameObject currentSelectedGameObject = EventSystem.current.currentSelectedGameObject; if (!((Object)(object)currentSelectedGameObject.GetComponent() != (Object)null)) { return (Object)(object)currentSelectedGameObject.GetComponent() != (Object)null; } return true; } } internal static class ContainerService { internal static List GetNearbyContainers(Player player) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) List list = new List(); if ((Object)(object)player == (Object)null) { return list; } long playerID = Game.instance.GetPlayerProfile().GetPlayerID(); Collider[] array = Physics.OverlapSphere(((Component)player).transform.position, Plugin.SearchRadius); HashSet hashSet = new HashSet(); Collider[] array2 = array; for (int i = 0; i < array2.Length; i++) { Container componentInParent = ((Component)array2[i]).GetComponentInParent(); if (!((Object)(object)componentInParent == (Object)null) && hashSet.Add(componentInParent) && IsPlayerOwnedContainer(componentInParent, playerID)) { list.Add(componentInParent); } } return list; } internal static bool IsPlayerOwnedContainer(Container container, long playerId) { if ((Object)(object)container == (Object)null) { return false; } Piece componentInParent = ((Component)container).GetComponentInParent(); if ((Object)(object)componentInParent == (Object)null) { return false; } return componentInParent.GetCreator() == playerId; } } internal static class InventorySortService { private static readonly MethodInfo ChangedMethod = AccessTools.Method(typeof(Inventory), "Changed", new Type[2] { typeof(bool), typeof(bool) }, (Type[])null); private static readonly object[] ChangedArguments = new object[2] { false, false }; internal static void SortPlayerInventory() { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { Inventory inventory = ((Humanoid)localPlayer).GetInventory(); if (inventory != null) { int vanillaInventoryHeight = GetVanillaInventoryHeight(localPlayer); Sort(inventory, vanillaInventoryHeight, preserveHotbar: true); } } } internal static void SortContainer(Container container) { if ((Object)(object)container == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null) { return; } long playerID = Game.instance.GetPlayerProfile().GetPlayerID(); if (ContainerService.IsPlayerOwnedContainer(container, playerID)) { Inventory inventory = container.GetInventory(); if (inventory != null) { Sort(inventory, inventory.GetHeight(), preserveHotbar: false); } } } private static void Sort(Inventory inventory, int validHeight, bool preserveHotbar) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) List allItems = inventory.GetAllItems(); if (allItems == null || allItems.Count <= 1) { return; } int width = inventory.GetWidth(); if (width <= 0 || validHeight <= 0) { return; } List list = new List(allItems.Count); HashSet<(int, int)> hashSet = new HashSet<(int, int)>(); foreach (ItemData item in allItems) { if (item != null && IsValidSlot(item.m_gridPos, width, validHeight)) { if (item.m_equipped) { hashSet.Add((item.m_gridPos.x, item.m_gridPos.y)); } else if (preserveHotbar && item.m_gridPos.y == 0) { hashSet.Add((item.m_gridPos.x, item.m_gridPos.y)); } else { list.Add(item); } } } list.Sort(CompareItems); int num = 0; for (int i = (preserveHotbar ? 1 : 0); i < validHeight; i++) { if (num >= list.Count) { break; } for (int j = 0; j < width; j++) { if (num >= list.Count) { break; } if (!hashSet.Contains((j, i))) { list[num].m_gridPos = new Vector2i(j, i); num++; } } } ChangedMethod?.Invoke(inventory, ChangedArguments); } private static int GetVanillaInventoryHeight(Player player) { string s = default(string); if (player.TryGetUniqueKeyValue("invrows", ref s) && int.TryParse(s, out var result)) { return Mathf.Clamp(result, 0, 9); } return 4; } private static bool IsValidSlot(Vector2i position, int width, int height) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0009: 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_001b: Unknown result type (might be due to invalid IL or missing references) if (position.x >= 0 && position.x < width && position.y >= 0) { return position.y < height; } return false; } private static int CompareItems(ItemData a, ItemData b) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) ref ItemType itemType = ref a.m_shared.m_itemType; object target = b.m_shared.m_itemType; int num = ((Enum)Unsafe.As(ref itemType)/*cast due to .constrained prefix*/).CompareTo(target); if (num != 0) { return num; } num = string.Compare(a.m_shared.m_name, b.m_shared.m_name, StringComparison.Ordinal); if (num != 0) { return num; } num = b.m_quality.CompareTo(a.m_quality); if (num != 0) { return num; } return b.m_stack.CompareTo(a.m_stack); } } internal static class QuickStackService { internal static void QuickStack(Player player) { if ((Object)(object)player == (Object)null) { return; } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return; } int vanillaInventoryHeight = GetVanillaInventoryHeight(player); foreach (Container nearbyContainer in ContainerService.GetNearbyContainers(player)) { if (!((Object)(object)nearbyContainer == (Object)null)) { Inventory inventory2 = nearbyContainer.GetInventory(); if (inventory2 != null) { QuickStackIntoContainer(player, inventory, inventory2, vanillaInventoryHeight); } } } } private static void QuickStackIntoContainer(Player player, Inventory playerInventory, Inventory containerInventory, int vanillaHeight) { foreach (ItemData item in new List(playerInventory.GetAllItems())) { if (item != null && item.m_gridPos.y >= 0 && item.m_gridPos.y < vanillaHeight && item.m_gridPos.y != 0 && !((Humanoid)player).IsItemEquiped(item) && ContainerHasMatchingItem(containerInventory, item) && containerInventory.CanAddItem(item, -1) && containerInventory.AddItem(item)) { playerInventory.RemoveItem(item); } } } private static bool ContainerHasMatchingItem(Inventory containerInventory, ItemData sourceItem) { foreach (ItemData allItem in containerInventory.GetAllItems()) { if (allItem != null && allItem.m_shared.m_name == sourceItem.m_shared.m_name) { return true; } } return false; } private static int GetVanillaInventoryHeight(Player player) { string s = default(string); if (player.TryGetUniqueKeyValue("invrows", ref s) && int.TryParse(s, out var result)) { return Mathf.Clamp(result, 0, 9); } return 4; } } internal static class TrashService { private const string JunkFileName = "HexQuickStackStorage.junk.txt"; private static readonly HashSet JunkItemNames = new HashSet(StringComparer.Ordinal); private static readonly FieldInfo DragItemField = AccessTools.Field(typeof(InventoryGui), "m_dragItem"); private static readonly FieldInfo DragInventoryField = AccessTools.Field(typeof(InventoryGui), "m_dragInventory"); private static readonly FieldInfo DragAmountField = AccessTools.Field(typeof(InventoryGui), "m_dragAmount"); private static readonly MethodInfo SetupDragItemMethod = AccessTools.Method(typeof(InventoryGui), "SetupDragItem", (Type[])null, (Type[])null); private static string JunkFilePath => Path.Combine(Paths.ConfigPath, "HexQuickStackStorage.junk.txt"); internal static void Initialize() { LoadJunkItems(); } internal static bool IsMarked(ItemData item) { if (item == null || item.m_shared == null) { return false; } return JunkItemNames.Contains(item.m_shared.m_name); } internal static void ToggleMarked(Player player, ItemData item) { if ((Object)(object)player == (Object)null || item == null || item.m_shared == null || !IsVanillaInventoryItem(player, item)) { return; } if (!CanDeleteItem(item)) { ShowProtectedItemMessage(player); return; } string name = item.m_shared.m_name; if (!JunkItemNames.Add(name)) { JunkItemNames.Remove(name); } SaveJunkItems(); } internal static void DeleteDraggedItem(InventoryGui inventoryGui, Player player) { if ((Object)(object)inventoryGui == (Object)null || (Object)(object)player == (Object)null) { return; } object? obj = DragItemField?.GetValue(inventoryGui); ItemData val = (ItemData)((obj is ItemData) ? obj : null); object? obj2 = DragInventoryField?.GetValue(inventoryGui); Inventory val2 = (Inventory)((obj2 is Inventory) ? obj2 : null); if (val == null || val2 == null) { return; } Inventory inventory = ((Humanoid)player).GetInventory(); if (val2 != inventory || !IsVanillaInventoryItem(player, val)) { return; } if (!CanDeleteItem(val)) { ShowProtectedItemMessage(player); return; } int num = Mathf.Min((!(DragAmountField != null)) ? 1 : ((int)DragAmountField.GetValue(inventoryGui)), val.m_stack); if (val2.RemoveItem(val, num)) { SetupDragItemMethod?.Invoke(inventoryGui, new object[3] { null, null, 1 }); } } internal static void DeleteMarkedItems(Player player) { if ((Object)(object)player == (Object)null) { return; } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return; } List list = new List(inventory.GetAllItems()); bool flag = false; foreach (ItemData item in list) { if (item != null && item.m_shared != null && JunkItemNames.Contains(item.m_shared.m_name) && IsVanillaInventoryItem(player, item) && !CanDeleteItem(item)) { flag = true; } } if (flag) { ShowProtectedItemMessage(player); } } internal static bool IsVanillaInventoryItem(Player player, ItemData item) { if ((Object)(object)player == (Object)null || item == null) { return false; } int vanillaInventoryHeight = GetVanillaInventoryHeight(player); if (item.m_gridPos.y >= 0) { return item.m_gridPos.y < vanillaInventoryHeight; } return false; } private static bool CanDeleteItem(ItemData item) { if (item == null) { return false; } if (item.m_equipped) { return false; } if (item.m_gridPos.y == 0) { return false; } return true; } private static int GetVanillaInventoryHeight(Player player) { string s = default(string); if (player.TryGetUniqueKeyValue("invrows", ref s) && int.TryParse(s, out var result)) { return Mathf.Clamp(result, 0, 9); } return 4; } private static void LoadJunkItems() { JunkItemNames.Clear(); if (!File.Exists(JunkFilePath)) { return; } string[] array = File.ReadAllLines(JunkFilePath); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (!string.IsNullOrEmpty(text)) { JunkItemNames.Add(text); } } } private static void SaveJunkItems() { Directory.CreateDirectory(Paths.ConfigPath); List list = new List(JunkItemNames); list.Sort(StringComparer.Ordinal); File.WriteAllLines(JunkFilePath, list); } private static void ShowProtectedItemMessage(Player player) { ((Character)player).Message((MessageType)2, "Can't delete equipped or hotbar items.", 0, (Sprite)null, false); } } internal static class InventoryUiController { private const string SortButtonName = "HexSortButton"; private const string QuickStackButtonName = "HexQuickStackButton"; private const string TrashButtonName = "HexTrashButton"; private const float ButtonSize = 36f; private const float ButtonSpacing = 4f; private const float TrashIconSize = 32f; private const float TrashIconXOffset = 4f; private const float TrashIconYOffset = 0f; private static readonly FieldInfo CurrentContainerField = AccessTools.Field(typeof(InventoryGui), "m_currentContainer"); private static readonly FieldInfo DragItemField = AccessTools.Field(typeof(InventoryGui), "m_dragItem"); private static InventoryGui _inventoryGui; internal static void Initialize(InventoryGui inventoryGui) { if (!((Object)(object)inventoryGui == (Object)null)) { _inventoryGui = inventoryGui; CreateButtons(); } } private static void CreateButtons() { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected O, but got Unknown //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown if (!((Object)(object)_inventoryGui == (Object)null) && !((Object)(object)_inventoryGui.m_player == (Object)null) && !((Object)(object)_inventoryGui.m_takeAllButton == (Object)null)) { Button takeAllButton = _inventoryGui.m_takeAllButton; CreateActionButton(takeAllButton, "HexSortButton", "S", 0, new UnityAction(OnSortClicked)); CreateActionButton(takeAllButton, "HexQuickStackButton", "Q", 1, new UnityAction(OnQuickStackClicked)); CreateTrashButton(new UnityAction(OnTrashClicked)); } } private static Button CreateActionButton(Button template, string buttonName, string text, int index, UnityAction onClick) { Transform val = ((Component)_inventoryGui.m_player).transform.Find(buttonName); if ((Object)(object)val != (Object)null) { return ((Component)val).GetComponent