using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.2.1")] [assembly: AssemblyInformationalVersion("1.0.0+57d9ddb874359d5fc5ab99d249522b80311a94b2")] [assembly: AssemblyVersion("1.2.1.0")] namespace DropSingleItem { [BepInPlugin("TechGuard.DropSingleItem", "DropSingleItem", "1.2.1")] [BepInProcess("valheim.exe")] public class DropSingleItemMod : BaseUnityPlugin { private const string NAME = "DropSingleItem"; private const string GUID = "TechGuard.DropSingleItem"; private const string VERSION = "1.2.1"; private readonly Harmony harmony = new Harmony("TechGuard.DropSingleItem"); private static ConfigEntry InventoryUseItemKeyCodeConfig; public static KeyCode InventoryUseItemKeyCode { get { //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (Enum.TryParse(InventoryUseItemKeyCodeConfig.Value, out KeyCode result)) { return result; } return (KeyCode)324; } } public void Awake() { InventoryUseItemKeyCodeConfig = ((BaseUnityPlugin)this).Config.Bind("Input", "InventoryUseItem", "Mouse1", "Keybinding for using/consuming/equipping an item in the inventory (Mouse1 = right mouse button).\nIf this is set to anything other than Mouse1, you no longer have to hold shift to split a stack when right clicking.\nAvailable KeyCodes can be found here: https://docs.unity3d.com/ScriptReference/KeyCode.html"); harmony.PatchAll(); } } } namespace DropSingleItem.Patch { [HarmonyPatch(typeof(InventoryGui), "Update")] internal class InventoryGui_Update_DropSingleOutside { public static void Postfix(InventoryGui __instance) { if (InventoryGui.IsVisible() && !((Object)(object)__instance.dragGo() == (Object)null) && ZInput.GetMouseButtonDown(1) && !Utils.PointerOverInventoryGrid(__instance)) { Utils.DropOneOutside(__instance); } } } [HarmonyPatch(typeof(InventoryGui), "OnSelectedItem")] internal class InventoryGui_OnSelectedItem_GrabFullStack { private const float DoubleClickThreshold = 0.3f; private static float LastItemClickedTime = -1f; public static bool Prefix(InventoryGui __instance, InventoryGrid grid, ItemData item, Vector2i pos, Modifier mod) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if ((int)mod != 0) { return true; } bool num = LastItemClickedTime >= 0f && Time.unscaledTime - LastItemClickedTime < 0.3f; LastItemClickedTime = Time.unscaledTime; if (!num) { return true; } ItemData val = item; Inventory val2 = (((Object)(object)grid != (Object)null) ? grid.GetInventory() : null); if (val == null && __instance.dragItem() != null) { val = __instance.dragItem(); val2 = __instance.dragInventory(); } if (val == null || val2 == null) { return true; } Utils.MergeMatchingIntoSlot(__instance, val2, val); return false; } } [HarmonyPatch(typeof(InventoryGui), "OnRightClickItem")] internal class InventoryGui_OnRightClickItem { public static bool Prefix(InventoryGui __instance, ref int ___m_dragAmount, InventoryGrid grid, ItemData item, Vector2i pos) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 //IL_008d: Unknown result type (might be due to invalid IL or missing references) bool flag = (Object)(object)__instance.dragGo() != (Object)null; bool flag2 = (int)DropSingleItemMod.InventoryUseItemKeyCode == 324; bool flag3 = flag2 && Utils.IsShiftHeld(); if (!flag2 || flag3) { if (!flag && item != null && item.m_stack > 1) { if (Utils.TrySplitStackIntoEmptySlot(grid.GetInventory(), item)) { Utils.PlayMoveEffects(__instance); } return false; } if (flag3) { return false; } } if (!flag) { return flag2; } ItemData val = __instance.dragItem(); if (!Utils.CanStackItem(val, item)) { return false; } Inventory val2 = __instance.dragInventory(); int num = __instance.dragAmount(); int stack = val.m_stack; ___m_dragAmount = 1; __instance.OnSelectedItem(grid, item, pos, (Modifier)0); num -= stack - val.m_stack; if (num > 0 && val != null && val2 != null && val2.ContainsItem(val)) { __instance.SetupDragItem(val, val2, num); } return false; } } [HarmonyPatch(typeof(InventoryGrid), "UpdateInventory")] internal class InventoryGrid_UpdateInventory { public static void Postfix(InventoryGrid __instance, Inventory inventory, Player player, ItemData dragItem) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 //IL_0012: 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_0025: Unknown result type (might be due to invalid IL or missing references) if ((int)DropSingleItemMod.InventoryUseItemKeyCode != 324 && dragItem == null && ZInput.GetKeyDown(DropSingleItemMod.InventoryUseItemKeyCode, true)) { ItemData item = __instance.GetItem(new Vector2i(ZInput.pointerPosition)); if (item != null && Object.op_Implicit((Object)(object)Player.m_localPlayer)) { ((Humanoid)Player.m_localPlayer).UseItem(inventory, item, true); } } } } [HarmonyPatch(typeof(InventoryGui), "UpdateItemDrag")] internal class InventoryGui_UpdateItemDrag { public static IEnumerable Transpiler(IEnumerable instructions) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Expected O, but got Unknown //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Expected O, but got Unknown MethodInfo method = typeof(ZInput).GetMethod("GetMouseButton", new Type[1] { typeof(int) }); List list = new List(instructions); for (int i = 1; i < list.Count; i++) { if (list[i - 1].opcode == OpCodes.Ldc_I4_1 && CodeInstructionExtensions.Calls(list[i], method)) { list[i - 1] = new CodeInstruction(OpCodes.Nop, (object)null); list[i] = new CodeInstruction(OpCodes.Ldc_I4_S, (object)0); } } return list.AsEnumerable(); } } internal class Utils { public static bool IsShiftHeld() { if (!ZInput.GetKey((KeyCode)304, true)) { return ZInput.GetKey((KeyCode)303, true); } return true; } public static bool CanStackItem(ItemData a, ItemData b) { if (a == null || b == null) { return true; } if (a == b) { return false; } if (a.m_shared.m_name != b.m_shared.m_name) { return false; } if (a.m_shared.m_maxStackSize == 1 || b.m_shared.m_maxStackSize == 1) { return false; } if ((a.m_shared.m_maxQuality > 1 || b.m_shared.m_maxQuality > 1) && a.m_quality != b.m_quality) { return false; } return true; } public static void PlayMoveEffects(InventoryGui instance) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) EffectList val = instance.moveItemEffects(); if (val != null) { val.Create(((Component)instance).transform.position, Quaternion.identity, (Transform)null, 1f, -1, ZDOID.None); } } public static bool TrySplitStackIntoEmptySlot(Inventory inventory, ItemData item) { //IL_004c: 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_0052: 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_0073: 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 (inventory == null || item == null || item.m_stack < 2 || !inventory.HaveEmptySlot()) { return false; } Vector2i value = Traverse.Create((object)inventory).Method("FindEmptySlot", new Type[1] { typeof(bool) }, (object[])null).GetValue(new object[1] { true }); if (value.x < 0 || value.y < 0) { return false; } int num = item.m_stack / 2; return inventory.MoveItemToThis(inventory, item, num, value.x, value.y); } public static void MergeMatchingIntoSlot(InventoryGui gui, Inventory targetInventory, ItemData target) { if (!((Object)(object)gui == (Object)null) && targetInventory != null && target != null && !((Object)(object)Player.m_localPlayer == (Object)null)) { int stack = target.m_stack; Container val = gui.currentContainer(); if ((Object)(object)val != (Object)null) { MergeMatchingStacksInto(targetInventory, target, val.GetInventory()); } MergeMatchingStacksInto(targetInventory, target, ((Humanoid)Player.m_localPlayer).GetInventory()); if (target.m_stack != stack) { PlayMoveEffects(gui); } gui.SetupDragItem(null, null, 1); gui.UpdateCraftingPanel(); } } private static void MergeMatchingStacksInto(Inventory targetInventory, ItemData target, Inventory source) { if (source == null || target == null || target.m_shared == null) { return; } List list = new List(); source.GetAllItems(target.m_shared.m_name, list); while (list.Any() && target.m_stack < target.m_shared.m_maxStackSize) { ItemData val = list.Last(); list.RemoveAt(list.Count - 1); if (CanStackItem(target, val)) { targetInventory.MoveItemToThis(source, val, val.m_stack, target.m_gridPos.x, target.m_gridPos.y); } } } public static bool PointerOverInventoryGrid(InventoryGui gui) { if (!ContainsPointer((Component)(object)gui.playerGrid())) { return ContainsPointer((Component)(object)gui.containerGrid()); } return true; } private static bool ContainsPointer(Component component) { //IL_0044: 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_0035: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)component == (Object)null) { return false; } Transform transform = component.transform; RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null); if ((Object)(object)val == (Object)null) { return false; } Canvas componentInParent = component.GetComponentInParent(); Camera val2 = null; if ((Object)(object)componentInParent != (Object)null && (int)componentInParent.renderMode != 0) { val2 = componentInParent.worldCamera; } return RectTransformUtility.RectangleContainsScreenPoint(val, Vector2.op_Implicit(ZInput.pointerPosition), val2); } public static void DropOneOutside(InventoryGui gui) { if ((Object)(object)gui == (Object)null || (Object)(object)gui.dragGo() == (Object)null) { return; } ItemData val = gui.dragItem(); Inventory val2 = gui.dragInventory(); if (val == null || val2 == null || (Object)(object)Player.m_localPlayer == (Object)null) { return; } int num = gui.dragAmount(); int stack = val.m_stack; if (!val2.ContainsItem(val)) { gui.SetupDragItem(null, null, 1); } else if (((Humanoid)Player.m_localPlayer).DropItem(val2, val, 1)) { PlayMoveEffects(gui); num -= stack - val.m_stack; if (val2.ContainsItem(val) && num > 0) { gui.SetupDragItem(val, val2, num); } else { gui.SetupDragItem(null, null, 1); } gui.UpdateCraftingPanel(); } } } internal static class InventoryGui_Extensions { public static void SetupDragItem(this InventoryGui instance, ItemData item, Inventory inventory, int amount) { Traverse.Create((object)instance).Method("SetupDragItem", new Type[3] { typeof(ItemData), typeof(Inventory), typeof(int) }, (object[])null).GetValue(new object[3] { item, inventory, amount }); } public static void UpdateCraftingPanel(this InventoryGui instance, bool focusView = false) { Traverse.Create((object)instance).Method("UpdateCraftingPanel", new Type[1] { typeof(bool) }, (object[])null).GetValue(new object[1] { focusView }); } public static void OnSelectedItem(this InventoryGui instance, InventoryGrid grid, ItemData item, Vector2i pos, Modifier mod) { //IL_005b: 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) Traverse.Create((object)instance).Method("OnSelectedItem", new Type[4] { typeof(InventoryGrid), typeof(ItemData), typeof(Vector2i), typeof(Modifier) }, (object[])null).GetValue(new object[4] { grid, item, pos, mod }); } public static EffectList moveItemEffects(this InventoryGui instance) { return Traverse.Create((object)instance).Field("m_moveItemEffects").GetValue(); } public static GameObject dragGo(this InventoryGui instance) { return Traverse.Create((object)instance).Field("m_dragGo").GetValue(); } public static ItemData dragItem(this InventoryGui instance) { return Traverse.Create((object)instance).Field("m_dragItem").GetValue(); } public static Inventory dragInventory(this InventoryGui instance) { return Traverse.Create((object)instance).Field("m_dragInventory").GetValue(); } public static int dragAmount(this InventoryGui instance) { return Traverse.Create((object)instance).Field("m_dragAmount").GetValue(); } public static Container currentContainer(this InventoryGui instance) { return Traverse.Create((object)instance).Field("m_currentContainer").GetValue(); } public static InventoryGrid playerGrid(this InventoryGui instance) { return Traverse.Create((object)instance).Field("m_playerGrid").GetValue(); } public static InventoryGrid containerGrid(this InventoryGui instance) { return Traverse.Create((object)instance).Field("m_containerGrid").GetValue(); } } }