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 QuickStackDeposit v1.1.1
QuickStackDeposit.dll
Decompiled 10 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Microsoft.CodeAnalysis; using UnityEngine; [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("QuickStackDeposit")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("QuickStackDeposit")] [assembly: AssemblyTitle("QuickStackDeposit")] [assembly: AssemblyVersion("1.0.0.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 QuickStackDeposit { [BepInPlugin("dreamscapist.valheim.quickstackdeposit", "QuickStackDeposit", "1.1.1")] public class Plugin : BaseUnityPlugin { private class ConfigurationManagerAttributes { public Action<ConfigEntryBase> CustomDrawer; } public const string PluginGUID = "dreamscapist.valheim.quickstackdeposit"; public const string PluginName = "QuickStackDeposit"; public const string PluginVersion = "1.1.1"; private static ConfigEntry<KeyboardShortcut> _depositKey; private static ConfigEntry<KeyboardShortcut> _lockToggleKey; private static ConfigEntry<float> _depositRange; private static ConfigEntry<bool> _includeEquipped; private const int HotbarRow = 0; private static readonly Dictionary<long, HashSet<Vector2i>> _allPlayerLocks = new Dictionary<long, HashSet<Vector2i>>(); private static ManualLogSource _log; private static ConfigEntry<bool> _clearLocksEntry; private static ConfigEntry<bool> _debugLogging; private static bool _wasInventoryOpenForOverlay; private static float _overlayCacheRefreshUntil; private static List<(Vector2i pos, RectTransform rect)> _cachedSlotRects = new List<(Vector2i, RectTransform)>(); private static Texture2D _borderTexture; private static string LocksFilePath => Path.Combine(Paths.ConfigPath, "dreamscapist.valheim.quickstackdeposit.locks.txt"); private static Texture2D BorderTexture { get { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown //IL_0020: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_borderTexture == (Object)null) { _borderTexture = new Texture2D(1, 1); _borderTexture.SetPixel(0, 0, Color.white); _borderTexture.Apply(); } return _borderTexture; } } private void Awake() { //IL_0027: 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_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Expected O, but got Unknown //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Expected O, but got Unknown //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) _log = ((BaseUnityPlugin)this).Logger; LoadLocksFromDisk(); _depositKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "DepositKey", new KeyboardShortcut((KeyCode)114, Array.Empty<KeyCode>()), "Key to press while your inventory screen is open to deposit matching items into nearby storages."); _lockToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "LockToggleKey", new KeyboardShortcut((KeyCode)120, Array.Empty<KeyCode>()), "Key to press while hovering over an inventory slot to lock/unlock it. Locked slots are never deposited."); _depositRange = ((BaseUnityPlugin)this).Config.Bind<float>("General", "DepositRange", 20f, new ConfigDescription("How far away (in meters) to look for storages to deposit into.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 100f), Array.Empty<object>())); _includeEquipped = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "IncludeEquippedItems", false, "If true, currently equipped items (weapons, armor, etc.) can also be deposited. Off by default so you don't accidentally strip your gear."); _debugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "EnableDebugLogging", false, "If true, logs detailed diagnostic messages (slot resolution, deposit decisions) to the BepInEx console/log. Off by default to keep the console clean."); _clearLocksEntry = ((BaseUnityPlugin)this).Config.Bind<bool>("Locking", "ClearAllLocks", false, new ConfigDescription("Button (in Configuration Manager): unlocks every locked slot for your current character.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { CustomDrawer = ClearLocksButtonDrawer } })); ((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} loaded. Deposit key: {2}, lock key: {3}, range: {4}", "QuickStackDeposit", "1.1.1", _depositKey.Value, _lockToggleKey.Value, _depositRange.Value)); } private static void ClearLocksButtonDrawer(ConfigEntryBase entry) { if (GUILayout.Button("Clear all locked slots for current character", Array.Empty<GUILayoutOption>())) { ClearLocksForCurrentPlayer(); } } private static void ClearLocksForCurrentPlayer() { if ((Object)(object)Player.m_localPlayer == (Object)null) { DebugLog("[Locks] Clear requested but no character is loaded right now."); return; } HashSet<Vector2i> lockedSlotsForPlayer = GetLockedSlotsForPlayer(Player.m_localPlayer); int count = lockedSlotsForPlayer.Count; lockedSlotsForPlayer.Clear(); SaveLocksToDisk(); DebugLog($"[Locks] Cleared {count} locked slot(s) for current character."); ((Character)Player.m_localPlayer).Message((MessageType)2, $"Cleared {count} locked slot(s)", 0, (Sprite)null, false); } private static HashSet<Vector2i> GetLockedSlotsForPlayer(Player player) { long playerID = player.GetPlayerID(); if (!_allPlayerLocks.TryGetValue(playerID, out var value)) { value = new HashSet<Vector2i>(); _allPlayerLocks[playerID] = value; } return value; } private static void LoadLocksFromDisk() { //IL_00b3: Unknown result type (might be due to invalid IL or missing references) _allPlayerLocks.Clear(); try { if (!File.Exists(LocksFilePath)) { return; } string[] array = File.ReadAllLines(LocksFilePath); foreach (string text in array) { if (string.IsNullOrWhiteSpace(text)) { continue; } string[] array2 = text.Split('|'); if (array2.Length != 2 || !long.TryParse(array2[0], out var result)) { continue; } HashSet<Vector2i> hashSet = new HashSet<Vector2i>(); string[] array3 = array2[1].Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); for (int j = 0; j < array3.Length; j++) { string[] array4 = array3[j].Split(','); if (array4.Length == 2 && int.TryParse(array4[0], out var result2) && int.TryParse(array4[1], out var result3)) { hashSet.Add(new Vector2i(result2, result3)); } } _allPlayerLocks[result] = hashSet; } DebugLog($"[Locks] Loaded locks for {_allPlayerLocks.Count} character(s) from disk."); } catch (Exception arg) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)$"[Locks] Failed to load locks file: {arg}"); } } } private static void SaveLocksToDisk() { try { List<string> list = new List<string>(); foreach (KeyValuePair<long, HashSet<Vector2i>> allPlayerLock in _allPlayerLocks) { if (allPlayerLock.Value.Count != 0) { string arg = string.Join(";", allPlayerLock.Value.Select((Vector2i p) => $"{p.x},{p.y}")); list.Add($"{allPlayerLock.Key}|{arg}"); } } File.WriteAllLines(LocksFilePath, list); } catch (Exception arg2) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)$"[Locks] Failed to save locks file: {arg2}"); } } } private void Update() { //IL_004d: 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_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) if (!((Object)(object)Player.m_localPlayer == (Object)null) && (Object)(object)InventoryGui.instance != (Object)null && InventoryGui.IsVisible() && (!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()) && !Console.IsVisible()) { KeyboardShortcut value = _lockToggleKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { ToggleLockOnHoveredSlot(Player.m_localPlayer); } value = _depositKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { List<Container> containers = FindNearbyContainers(Player.m_localPlayer, _depositRange.Value); DepositNearbyMatchingItems(Player.m_localPlayer, containers); } } } private void OnGUI() { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Player.m_localPlayer == (Object)null) { _wasInventoryOpenForOverlay = false; return; } if (!((Object)(object)InventoryGui.instance != (Object)null) || !InventoryGui.IsVisible()) { _wasInventoryOpenForOverlay = false; return; } if (!_wasInventoryOpenForOverlay) { _wasInventoryOpenForOverlay = true; _overlayCacheRefreshUntil = Time.unscaledTime + 0.3f; } if (Time.unscaledTime <= _overlayCacheRefreshUntil) { _cachedSlotRects = FindAllSlotRects(); } HashSet<Vector2i> lockedSlotsForPlayer = GetLockedSlotsForPlayer(Player.m_localPlayer); if (lockedSlotsForPlayer.Count == 0) { return; } foreach (var (item, val) in _cachedSlotRects) { if (!((Object)(object)val == (Object)null) && lockedSlotsForPlayer.Contains(item)) { DrawBorder(RectTransformToScreenRect(val), Color.gray, 4f); } } } private static List<(Vector2i pos, RectTransform rect)> FindAllSlotRects() { List<(Vector2i, RectTransform)> list = new List<(Vector2i, RectTransform)>(); InventoryGrid val = (((Object)(object)InventoryGui.instance != (Object)null) ? InventoryGui.instance.m_playerGrid : null); if ((Object)(object)val == (Object)null) { return list; } HashSet<Vector2i> seen = new HashSet<Vector2i>(); CollectSlotRectsRecursive(((Component)val).transform, list, seen, 0); DebugLog($"[Overlay] Found {list.Count} slot rects under player grid."); return list; } private static void CollectSlotRectsRecursive(Transform t, List<(Vector2i, RectTransform)> results, HashSet<Vector2i> seen, int depth) { //IL_003a: 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) if ((Object)(object)t == (Object)null || depth > 10) { return; } Component[] components = ((Component)t).GetComponents<Component>(); foreach (Component val in components) { if ((Object)(object)val == (Object)null) { continue; } Vector2i? val2 = FindVector2iField(val); if (val2.HasValue && seen.Add(val2.Value)) { RectTransform val3 = (RectTransform)(object)((t is RectTransform) ? t : null); if (val3 != null) { results.Add((val2.Value, val3)); } } } for (int j = 0; j < t.childCount; j++) { CollectSlotRectsRecursive(t.GetChild(j), results, seen, depth + 1); } } private static Rect RectTransformToScreenRect(RectTransform rect) { //IL_0098: Unknown result type (might be due to invalid IL or missing references) Vector3[] array = (Vector3[])(object)new Vector3[4]; rect.GetWorldCorners(array); float num = Mathf.Min(array[0].x, array[2].x); float num2 = Mathf.Max(array[0].x, array[2].x); float num3 = Mathf.Min(array[0].y, array[2].y); float num4 = Mathf.Max(array[0].y, array[2].y); return new Rect(num, (float)Screen.height - num4, num2 - num, num4 - num3); } private static void DrawBorder(Rect area, Color color, float thickness) { //IL_0000: 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_0021: 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_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) Color color2 = GUI.color; GUI.color = color; GUI.DrawTexture(new Rect(((Rect)(ref area)).x, ((Rect)(ref area)).y, ((Rect)(ref area)).width, thickness), (Texture)(object)BorderTexture); GUI.DrawTexture(new Rect(((Rect)(ref area)).x, ((Rect)(ref area)).yMax - thickness, ((Rect)(ref area)).width, thickness), (Texture)(object)BorderTexture); GUI.DrawTexture(new Rect(((Rect)(ref area)).x, ((Rect)(ref area)).y, thickness, ((Rect)(ref area)).height), (Texture)(object)BorderTexture); GUI.DrawTexture(new Rect(((Rect)(ref area)).xMax - thickness, ((Rect)(ref area)).y, thickness, ((Rect)(ref area)).height), (Texture)(object)BorderTexture); GUI.color = color2; } private void ToggleLockOnHoveredSlot(Player player) { //IL_0034: 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) //IL_0055: 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_007d: Unknown result type (might be due to invalid IL or missing references) Vector2i? val = FindHoveredSlotPos(player); if (!val.HasValue) { DebugLog($"[LockToggle] No slot resolved under mouse at {Input.mousePosition}."); return; } HashSet<Vector2i> lockedSlotsForPlayer = GetLockedSlotsForPlayer(player); bool flag; if (lockedSlotsForPlayer.Contains(val.Value)) { lockedSlotsForPlayer.Remove(val.Value); flag = false; } else { lockedSlotsForPlayer.Add(val.Value); flag = true; } SaveLocksToDisk(); DebugLog(string.Format("[LockToggle] {0} slot {1}. Currently locked slots: {2}", flag ? "Locking" : "Unlocking", val.Value, string.Join(", ", lockedSlotsForPlayer))); ((Character)player).Message((MessageType)2, flag ? "Inventory slot locked" : "Inventory slot unlocked", 0, (Sprite)null, false); } private static Vector2i? FindHoveredSlotPos(Player player) { //IL_0000: 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_009f: Unknown result type (might be due to invalid IL or missing references) //IL_0124: 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) GameObject val = RaycastTopmostUIElement(Vector2.op_Implicit(Input.mousePosition)); if ((Object)(object)val == (Object)null) { DebugLog("[LockToggle] UI raycast found nothing under the mouse."); return null; } DebugLog("[LockToggle] UI raycast hit: '" + GetHierarchyPath(val.transform) + "'"); Transform val2 = val.transform; int num = 0; while ((Object)(object)val2 != (Object)null && num < 12) { Component[] components = ((Component)val2).GetComponents<Component>(); foreach (Component val3 in components) { if (!((Object)(object)val3 == (Object)null)) { Vector2i? result = FindVector2iField(val3); if (result.HasValue) { DebugLog($"[LockToggle] Found Vector2i {result.Value} on {((object)val3).GetType().Name} at '{((Object)val2).name}' (depth {num})."); return result; } ItemData val4 = FindItemDataField(val3); if (val4 != null) { DebugLog($"[LockToggle] Found ItemData field on {((object)val3).GetType().Name} at '{((Object)val2).name}' (depth {num}): '{val4.m_shared.m_name}' at {val4.m_gridPos}."); return val4.m_gridPos; } } } val2 = val2.parent; num++; } DebugLog("[LockToggle] Walked up the hit hierarchy but found no ItemData/Vector2i field on any component."); return null; } private static GameObject RaycastTopmostUIElement(Vector2 screenPos) { //IL_0097: Unknown result type (might be due to invalid IL or missing references) Type type = FindRuntimeType("UnityEngine.EventSystems.EventSystem"); Type type2 = FindRuntimeType("UnityEngine.EventSystems.PointerEventData"); Type type3 = FindRuntimeType("UnityEngine.EventSystems.RaycastResult"); if (type == null || type2 == null || type3 == null) { DebugLog("[LockToggle] Could not find UnityEngine.EventSystems types at runtime."); return null; } object obj = type.GetProperty("current", BindingFlags.Static | BindingFlags.Public)?.GetValue(null); if (obj == null) { DebugLog("[LockToggle] EventSystem.current is null."); return null; } object obj2 = Activator.CreateInstance(type2, obj); type2.GetProperty("position")?.SetValue(obj2, screenPos); Type type4 = typeof(List<>).MakeGenericType(type3); object obj3 = Activator.CreateInstance(type4); MethodInfo method = type.GetMethod("RaycastAll", new Type[2] { type2, type4 }); if (method == null) { DebugLog("[LockToggle] EventSystem.RaycastAll method not found."); return null; } method.Invoke(obj, new object[2] { obj2, obj3 }); IList list = (IList)obj3; if (list.Count == 0) { return null; } object? obj4 = type3.GetProperty("gameObject")?.GetValue(list[0]); return (GameObject)((obj4 is GameObject) ? obj4 : null); } private static Type FindRuntimeType(string fullName) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly asm in assemblies) { Type type = SafeGet(() => asm.GetType(fullName)) as Type; if (type != null) { return type; } } return null; } private static string GetHierarchyPath(Transform t) { List<string> list = new List<string>(); while ((Object)(object)t != (Object)null) { list.Add(((Object)t).name); t = t.parent; } list.Reverse(); return string.Join("/", list); } private static ItemData FindItemDataField(object element) { FieldInfo[] fields = element.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo field in fields) { if (field.FieldType == typeof(ItemData)) { object obj = SafeGet(() => field.GetValue(element)); return (ItemData)((obj is ItemData) ? obj : null); } } return null; } private static Vector2i? FindVector2iField(object element) { //IL_006f: Unknown result type (might be due to invalid IL or missing references) FieldInfo[] fields = element.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo field in fields) { if (field.FieldType == typeof(Vector2i)) { object obj = SafeGet(() => field.GetValue(element)); if (obj != null) { return (Vector2i)obj; } } } return null; } private static object SafeGet(Func<object> getter) { try { return getter(); } catch { return null; } } private static void DebugLog(string message) { if (_debugLogging != null && _debugLogging.Value) { _log.LogInfo((object)message); } } private static bool IsSlotLocked(Player player, Vector2i pos) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return GetLockedSlotsForPlayer(player).Contains(pos); } private void DepositNearbyMatchingItems(Player player, List<Container> containers) { //IL_0094: 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) //IL_014a: Unknown result type (might be due to invalid IL or missing references) containers.RemoveAll((Container c) => (Object)(object)c == (Object)null); if (containers.Count == 0) { ((Character)player).Message((MessageType)2, "No nearby storages in range", 0, (Sprite)null, false); return; } Inventory inventory = ((Humanoid)player).GetInventory(); List<ItemData> list = new List<ItemData>(inventory.GetAllItems()); int num = 0; foreach (ItemData item in list) { if (item == null || (item.m_equipped && !_includeEquipped.Value) || item.m_gridPos.y == 0) { continue; } if (IsSlotLocked(player, item.m_gridPos)) { DebugLog($"[Deposit] Skipping item '{item.m_shared.m_name}' - slot {item.m_gridPos} is locked."); continue; } foreach (Container container in containers) { if ((Object)(object)container == (Object)null) { continue; } Inventory inventory2 = container.GetInventory(); if (inventory2 != null && inventory2.HaveItem(item.m_shared.m_name, true)) { int stack = item.m_stack; inventory2.MoveItemToThis(inventory, item); if (!inventory.ContainsItem(item) || item.m_stack < stack) { num++; DebugLog($"[Deposit] Moved '{item.m_shared.m_name}' (was at {item.m_gridPos}) into a nearby storage."); } if (!inventory.ContainsItem(item)) { break; } } } } if (num > 0) { ((Character)player).Message((MessageType)2, "Deposited items into nearby storage(s)", 0, (Sprite)null, false); } else { ((Character)player).Message((MessageType)2, "Nothing to deposit", 0, (Sprite)null, false); } } private List<Container> FindNearbyContainers(Player player, float range) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) List<Container> list = new List<Container>(); Collider[] array = Physics.OverlapSphere(((Component)player).transform.position, range); for (int i = 0; i < array.Length; i++) { Container componentInParent = ((Component)array[i]).GetComponentInParent<Container>(); if (!((Object)(object)componentInParent == (Object)null) && !list.Contains(componentInParent) && !componentInParent.IsInUse()) { list.Add(componentInParent); } } return list; } } }