Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of SafeQuickStack v1.0.1
Dupo_SafeQuickStack.dll
Decompiled a day agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Dupo_SafeQuickStack")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyInformationalVersion("1.0.1+e88e032ed8c63866f512c923cec834943bfc538c")] [assembly: AssemblyProduct("Dupo_SafeQuickStack")] [assembly: AssemblyTitle("Dupo_SafeQuickStack")] [assembly: AssemblyVersion("1.0.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Dupo.SafeQuickStack { [BepInPlugin("dupo.SafeQuickStack", "Dupo_SafeQuickStack", "1.0.1")] public class Plugin : BaseUnityPlugin { internal static class QuickStack { [HarmonyPatch(typeof(Container), "RPC_StackResponse")] private static class StackResponsePatch { private static bool Prefix(Container __instance, bool granted) { if (!Pending.Contains(__instance)) { return true; } OnResponse(__instance, granted); return false; } } internal static ConfigEntry<KeyboardShortcut> Hotkey; internal static ConfigEntry<float> Range; internal static ConfigEntry<bool> KeepHotbar; internal static ConfigEntry<bool> IncludeNonStackable; internal static ConfigEntry<bool> PlayerBuiltOnly; internal static ConfigEntry<bool> IncludeCartsAndShips; private const float GrantSettleDelay = 0.25f; private const float RequestTimeout = 3f; private static readonly FieldRef<Container, ZNetView> NViewRef = AccessTools.FieldRefAccess<Container, ZNetView>("m_nview"); private static readonly MethodInfo LoadMethod = AccessTools.Method(typeof(Container), "Load", (Type[])null, (Type[])null); private static readonly HashSet<Container> Pending = new HashSet<Container>(); private static float _requestTime; private static int _movedItems; private static int _filledChests; private static int _refusedChests; internal static void Bind(ConfigFile config) { //IL_001b: 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_0062: Expected O, but got Unknown Hotkey = config.Bind<KeyboardShortcut>("2 - Quick Stack", "Hotkey", new KeyboardShortcut((KeyCode)103, (KeyCode[])(object)new KeyCode[1] { (KeyCode)306 }), "Key that moves the items from your inventory into nearby chests that already contain the same item."); Range = config.Bind<float>("2 - Quick Stack", "Range", 10f, new ConfigDescription("Maximum distance from you to a chest, in meters.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 50f), Array.Empty<object>())); KeepHotbar = config.Bind<bool>("2 - Quick Stack", "Keep Hotbar Items", true, "Never move the items in your hotbar (the first inventory row, keys 1-8)."); IncludeNonStackable = config.Bind<bool>("2 - Quick Stack", "Include Non-Stackable Items", false, "Also move items that do not stack, such as weapons, tools and armor, when a chest has the same one. Equipped items are never moved."); PlayerBuiltOnly = config.Bind<bool>("2 - Quick Stack", "Player-Built Chests Only", true, "Only use chests built by a player. Skips chests found in dungeons and villages."); IncludeCartsAndShips = config.Bind<bool>("2 - Quick Stack", "Include Carts and Ships", false, "Also use the storage of carts and ships."); } internal static void Update() { //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) if (Pending.Count > 0 && Time.time - _requestTime > 3f) { Debug($"QuickStack: {Pending.Count} chest(s) did not answer, giving up"); Pending.Clear(); ShowSummary(); } KeyboardShortcut value = Hotkey.Value; if (((KeyboardShortcut)(ref value)).IsDown() && CanUseHotkeys() && Pending.Count <= 0) { Start(Player.m_localPlayer); } } private static void Start(Player player) { //IL_0029: 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_0057: Unknown result type (might be due to invalid IL or missing references) _movedItems = 0; _filledChests = 0; _refusedChests = 0; _requestTime = Time.time; Inventory inventory = ((Humanoid)player).GetInventory(); Vector3 position = ((Component)player).transform.position; float value = Range.Value; List<Container> list = new List<Container>(); Container[] array = Object.FindObjectsByType<Container>((FindObjectsSortMode)0); foreach (Container val in array) { if (IsCandidate(val, inventory, position, value)) { list.Add(val); } } Debug($"QuickStack: requesting {list.Count} chest(s)"); if (list.Count == 0) { ShowSummary(); return; } foreach (Container item in list) { Pending.Add(item); } foreach (Container item2 in list) { item2.StackAll(); } } private static bool IsCandidate(Container container, Inventory playerInventory, Vector3 position, float range) { //IL_003e: 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_00d0: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)container == (Object)null || container.GetInventory() == null) { return false; } ZNetView val = NViewRef.Invoke(container); if ((Object)(object)val == (Object)null || !val.IsValid()) { return false; } if (container.GetInventory() == playerInventory) { return false; } if (Vector3.Distance(position, ((Component)container).transform.position) > range) { return false; } if ((Object)(object)((Component)container).GetComponentInParent<TombStone>() != (Object)null) { return false; } if (!IncludeCartsAndShips.Value && ((Object)(object)container.m_wagon != (Object)null || (Object)(object)((Component)container).GetComponentInParent<Vagon>() != (Object)null || (Object)(object)((Component)container).GetComponentInParent<Ship>() != (Object)null)) { return false; } if (PlayerBuiltOnly.Value) { Piece componentInParent = ((Component)container).GetComponentInParent<Piece>(); if ((Object)(object)componentInParent == (Object)null || !componentInParent.IsPlacedByPlayer()) { return false; } } if (container.m_checkGuardStone && !PrivateArea.CheckAccess(((Component)container).transform.position, 0f, false, false)) { return false; } foreach (ItemData allItem in playerInventory.GetAllItems()) { if (CanMove(allItem) && container.GetInventory().ContainsItemByName(allItem.m_shared.m_name)) { return true; } } return false; } private static bool CanMove(ItemData item) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ((Humanoid)localPlayer).IsItemEquiped(item)) { return false; } if (KeepHotbar.Value && item.m_gridPos.y == 0) { return false; } if (SlotLocks.IsLocked(item.m_gridPos)) { return false; } if (!IncludeNonStackable.Value && item.m_shared.m_maxStackSize <= 1) { return false; } return true; } private static void OnResponse(Container container, bool granted) { if (!granted) { Debug("QuickStack: " + ((Object)container).name + " refused (in use or private)"); _refusedChests++; Finish(container); } else { ((MonoBehaviour)Instance).StartCoroutine(StackWhenSettled(container)); } } private static IEnumerator StackWhenSettled(Container container) { yield return (object)new WaitForSeconds(0.25f); Player localPlayer = Player.m_localPlayer; ZNetView val = (((Object)(object)container != (Object)null) ? NViewRef.Invoke(container) : null); if ((Object)(object)localPlayer == (Object)null || (Object)(object)val == (Object)null || !val.IsValid() || !val.IsOwner()) { Debug("QuickStack: chest lost or no longer ours, skipped"); if ((Object)(object)container != (Object)null) { _refusedChests++; } Finish(container); yield break; } LoadMethod.Invoke(container, null); int num = MoveItems(((Humanoid)localPlayer).GetInventory(), container.GetInventory()); if (num > 0) { _movedItems += num; _filledChests++; InventoryGui instance = InventoryGui.instance; if (instance != null) { instance.m_moveItemEffects.Create(((Component)container).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)); } } Debug($"QuickStack: moved {num} item(s) into {((Object)container).name}"); Finish(container); } private static int MoveItems(Inventory from, Inventory to) { int num = 0; foreach (ItemData item in new List<ItemData>(from.GetAllItems())) { if (CanMove(item) && to.ContainsItemByName(item.m_shared.m_name)) { int stack = item.m_stack; if (to.AddItem(item)) { from.RemoveItem(item); num += stack; } else { num += stack - item.m_stack; } } } return num; } private static void Finish(Container container) { if (Pending.Remove(container) && Pending.Count == 0) { ShowSummary(); } } private static void ShowSummary() { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { string text = ((_movedItems > 0) ? $"Quick Stack: {_movedItems} item(s) into {_filledChests} chest(s)" : "Quick Stack: nothing to stack"); if (_refusedChests > 0) { string text2 = ((_refusedChests == 1) ? "1 chest" : $"{_refusedChests} chests"); text = text + "\n<color=#FF9933><b>" + text2 + " in use by another player: skipped</b></color>"; } ((Character)localPlayer).Message((MessageType)2, text, 0, (Sprite)null, false); } } } internal static class SlotLocks { internal enum IconPosition { Center, Left, Right, TopLeft, TopRight, BottomLeft, BottomRight } [HarmonyPatch(typeof(InventoryGui), "OnSelectedItem")] private static class SelectItemPatch { private static bool Prefix(InventoryGui __instance, InventoryGrid grid, Vector2i pos) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0039: 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_004d: Unknown result type (might be due to invalid IL or missing references) if (!Active() || (Object)(object)grid != (Object)(object)__instance.m_playerGrid || !ZInput.GetKey(LockKey.Value, true)) { return true; } if (DragItemRef.Invoke(__instance) != null) { return true; } if (pos.x < 0 || pos.y < 0) { return true; } Toggle(pos); return false; } } [HarmonyPatch(typeof(InventoryGrid), "UpdateGui")] private static class UpdateGuiPatch { private static void Postfix(InventoryGrid __instance, Player player) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Expected O, but got Unknown if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || __instance.GetInventory() != ((Humanoid)player).GetInventory()) { return; } bool flag = Active(); foreach (InventoryElement item in ElementsRef.Invoke(__instance)) { if ((Object)(object)item == (Object)null) { continue; } bool flag2 = flag && IsLocked(item.Position); Transform val = ((Component)item).transform.Find("SafeQuickStack_Lock"); if ((Object)(object)val == (Object)null) { if (!flag2) { continue; } val = CreateIcon(((Component)item).transform); } if (((Component)val).gameObject.activeSelf != flag2) { ((Component)val).gameObject.SetActive(flag2); } if (flag2) { PlaceIcon((RectTransform)val); } } } } internal static ConfigEntry<KeyCode> LockKey; internal static ConfigEntry<IconPosition> LockIconPosition; private const string CustomDataKey = "dupo.SafeQuickStack.LockedSlots"; private const string IconName = "SafeQuickStack_Lock"; private static readonly HashSet<int> Locked = new HashSet<int>(); private static Player _loadedFor; private static Sprite _lockSprite; private static readonly FieldRef<InventoryGui, ItemData> DragItemRef = AccessTools.FieldRefAccess<InventoryGui, ItemData>("m_dragItem"); private static readonly FieldRef<InventoryGrid, List<InventoryElement>> ElementsRef = AccessTools.FieldRefAccess<InventoryGrid, List<InventoryElement>>("m_elements"); internal static void Bind(ConfigFile config) { LockKey = config.Bind<KeyCode>("3 - Slot Locks", "Lock Key", (KeyCode)308, "Hold this key and left-click a slot of your inventory to lock or unlock it. Quick Stack never moves the items in a locked slot."); LockIconPosition = config.Bind<IconPosition>("3 - Slot Locks", "Lock Icon Position", IconPosition.Center, "Where the padlock is drawn on a locked slot. Center = over the middle of the item icon, Left/Right = middle of the left/right edge. TopRight covers the quality level of upgraded items, TopLeft covers the hotbar numbers."); } private static int Key(int x, int y) { return y * 1000 + x; } private static void EnsureLoaded() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)(object)_loadedFor) { return; } _loadedFor = localPlayer; Locked.Clear(); if ((Object)(object)localPlayer == (Object)null || !localPlayer.m_customData.TryGetValue("dupo.SafeQuickStack.LockedSlots", out var value)) { return; } string[] array = value.Split(';'); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(','); if (array2.Length == 2 && int.TryParse(array2[0], out var result) && int.TryParse(array2[1], out var result2)) { Locked.Add(Key(result, result2)); } } } private static void Save() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } List<string> list = new List<string>(); foreach (int item in Locked) { list.Add($"{item % 1000},{item / 1000}"); } if (list.Count == 0) { localPlayer.m_customData.Remove("dupo.SafeQuickStack.LockedSlots"); } else { localPlayer.m_customData["dupo.SafeQuickStack.LockedSlots"] = string.Join(";", list); } } internal static bool IsLocked(Vector2i pos) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) EnsureLoaded(); return Locked.Contains(Key(pos.x, pos.y)); } private static void Toggle(Vector2i pos) { //IL_0005: 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_003a: 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) EnsureLoaded(); int item = Key(pos.x, pos.y); if (!Locked.Remove(item)) { Locked.Add(item); } Save(); Debug(string.Format("SlotLocks: slot {0},{1} {2}", pos.x, pos.y, Locked.Contains(item) ? "locked" : "unlocked")); } private static bool Active() { return Enabled.Value; } private static Transform CreateIcon(Transform slot) { //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_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: 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_0052: 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) //IL_006c: Expected O, but got Unknown //IL_006c: 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) GameObject val = new GameObject("SafeQuickStack_Lock", new Type[2] { typeof(RectTransform), typeof(Image) }); RectTransform val2 = (RectTransform)val.transform; ((Transform)val2).SetParent(slot, false); val2.pivot = new Vector2(0.5f, 0.5f); val2.sizeDelta = new Vector2(18f, 18f); PlaceIcon(val2); Image component = val.GetComponent<Image>(); component.sprite = GetLockSprite(); ((Graphic)component).raycastTarget = false; val.transform.SetAsLastSibling(); return val.transform; } private static void PlaceIcon(RectTransform rect) { //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_012b: 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_0149: 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_013a: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_013c: 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_0157: Unknown result type (might be due to invalid IL or missing references) Vector2 val = default(Vector2); Vector2 zero = default(Vector2); switch (LockIconPosition.Value) { case IconPosition.Right: ((Vector2)(ref val))..ctor(1f, 0.5f); ((Vector2)(ref zero))..ctor(-6f, 0f); break; case IconPosition.TopLeft: ((Vector2)(ref val))..ctor(0f, 1f); ((Vector2)(ref zero))..ctor(6f, -6f); break; case IconPosition.TopRight: ((Vector2)(ref val))..ctor(1f, 1f); ((Vector2)(ref zero))..ctor(-6f, -6f); break; case IconPosition.BottomLeft: ((Vector2)(ref val))..ctor(0f, 0f); ((Vector2)(ref zero))..ctor(6f, 6f); break; case IconPosition.BottomRight: ((Vector2)(ref val))..ctor(1f, 0f); ((Vector2)(ref zero))..ctor(-6f, 6f); break; case IconPosition.Center: ((Vector2)(ref val))..ctor(0.5f, 0.5f); zero = Vector2.zero; break; default: ((Vector2)(ref val))..ctor(0f, 0.5f); ((Vector2)(ref zero))..ctor(6f, 0f); break; } if (rect.anchorMin != val) { Vector2 anchorMin = (rect.anchorMax = val); rect.anchorMin = anchorMin; } if (rect.anchoredPosition != zero) { rect.anchoredPosition = zero; } } private static Sprite GetLockSprite() { //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Expected O, but got Unknown //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0081: 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_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: 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_00b0: 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 ((Object)(object)_lockSprite != (Object)null) { return _lockSprite; } Color val = default(Color); ((Color)(ref val))..ctor(1f, 0.82f, 0.25f, 1f); Color val2 = default(Color); ((Color)(ref val2))..ctor(0.08f, 0.06f, 0.03f, 1f); Color[] array = (Color[])(object)new Color[1024]; for (int i = 0; i < 32; i++) { for (int j = 0; j < 32; j++) { Color val3 = Color.clear; if (InShape(j, i)) { val3 = (InKeyhole(j, i) ? val2 : val); } else { for (int k = -2; k <= 2; k++) { if (val3.a != 0f) { break; } for (int l = -2; l <= 2; l++) { if (l * l + k * k <= 5 && InShape(j + l, i + k)) { val3 = val2; break; } } } } array[i * 32 + j] = val3; } } Texture2D val4 = new Texture2D(32, 32, (TextureFormat)4, false) { filterMode = (FilterMode)1, wrapMode = (TextureWrapMode)1 }; val4.SetPixels(array); val4.Apply(); _lockSprite = Sprite.Create(val4, new Rect(0f, 0f, 32f, 32f), new Vector2(0.5f, 0.5f)); return _lockSprite; static bool InKeyhole(int x, int y) { if (x < 15 || x > 16 || y < 6 || y > 11) { return Mathf.Pow((float)x - 15.5f, 2f) + Mathf.Pow((float)y - 11f, 2f) <= 3.5f; } return true; } static bool InShape(int x, int y) { if (x >= 7 && x <= 24 && y >= 3 && y <= 16) { if (x == 7 || x == 24) { if (y != 3) { return y != 16; } return false; } return true; } if (y > 16) { float num = (float)x - 15.5f; float num2 = (float)y - 16f; float num3 = Mathf.Sqrt(num * num + num2 * num2); if (num3 >= 5f) { return num3 <= 8.5f; } return false; } return false; } } } public const string ModGuid = "dupo.SafeQuickStack"; public const string ModName = "Dupo_SafeQuickStack"; public const string ModVersion = "1.0.1"; internal static ManualLogSource Log; internal static Plugin Instance; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> DebugLog; private readonly Harmony _harmony = new Harmony("dupo.SafeQuickStack"); private void Awake() { Log = ((BaseUnityPlugin)this).Logger; Instance = this; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Enabled", true, "Turns the mod on or off (Quick Stack, slot locks and the padlock icons)."); DebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Debug Log", false, "Write extra diagnostic lines to BepInEx/LogOutput.log."); QuickStack.Bind(((BaseUnityPlugin)this).Config); SlotLocks.Bind(((BaseUnityPlugin)this).Config); _harmony.PatchAll(typeof(Plugin).Assembly); Log.LogInfo((object)"Dupo_SafeQuickStack 1.0.1 loaded"); } private void Update() { if (Enabled.Value) { QuickStack.Update(); } } internal static bool CanUseHotkeys() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || ((Character)localPlayer).IsTeleporting()) { return false; } if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()) { return false; } if (Console.IsVisible() || TextInput.IsVisible() || Menu.IsVisible() || Minimap.InTextInput()) { return false; } if ((Object)(object)Hud.instance != (Object)null && (Object)(object)Hud.instance.m_buildUi != (Object)null && Hud.instance.m_buildUi.SearchFieldFocused) { return false; } return true; } private void OnDestroy() { _harmony.UnpatchSelf(); } internal static void Debug(string message) { if (DebugLog != null && DebugLog.Value) { Log.LogInfo((object)message); } } } }