From cd1badb9d6f5c1416ece0ebaa4910aa8c654b64a Mon Sep 17 00:00:00 2001 From: ReinforceZwei <39955851+ReinforceZwei@users.noreply.github.com> Date: Sat, 29 Aug 2020 23:53:29 +0800 Subject: [PATCH] Add item moving helper class (#1243) * Create item moving helper class * Change the method calling order for joined game Dispatch bot after things are initialized such as inventories * Add GetItemMovingHelper ChatBot method --- MinecraftClient/ChatBot.cs | 10 + MinecraftClient/Inventory/ItemMovingHelper.cs | 173 ++++++++++++++++++ MinecraftClient/McClient.cs | 3 +- MinecraftClient/MinecraftClient.csproj | 1 + 4 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 MinecraftClient/Inventory/ItemMovingHelper.cs diff --git a/MinecraftClient/ChatBot.cs b/MinecraftClient/ChatBot.cs index c288c814..d4604310 100644 --- a/MinecraftClient/ChatBot.cs +++ b/MinecraftClient/ChatBot.cs @@ -1182,6 +1182,16 @@ namespace MinecraftClient return Handler.DoWindowAction(inventoryId, slot, actionType); } + /// + /// Get inventory action helper + /// + /// Inventory Container + /// ItemMovingHelper instance + protected ItemMovingHelper GetItemMovingHelper(Container container) + { + return new ItemMovingHelper(container, Handler); + } + /// /// Change player selected hotbar slot /// diff --git a/MinecraftClient/Inventory/ItemMovingHelper.cs b/MinecraftClient/Inventory/ItemMovingHelper.cs new file mode 100644 index 00000000..08c9d5bd --- /dev/null +++ b/MinecraftClient/Inventory/ItemMovingHelper.cs @@ -0,0 +1,173 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; + +namespace MinecraftClient.Inventory +{ + /// + /// Class that contains useful methods to move item around in a container + /// + public class ItemMovingHelper + { + private Container c; + private McClient mc; + + /// + /// Create a helper that contains useful methods to move item around in container + /// + /// Source container to use. All method will use this container for handling first slot parameter + /// McClient handler. Needed for sending WindowAction packet to the server + /// + /// If you are using ChatBot API and cannot have direct access to McClient handler, use as second parameter + /// + public ItemMovingHelper(Container c, McClient mc) + { + this.c = c; + this.mc = mc; + } + + /// + /// Move an item fron source to dest. Source should contain an item and dest slot should be empty + /// + /// Source slot + /// Dest slot + /// Dest slot's container (only if dest slot is in other container) + /// True if success or false if Failed + public bool MoveTo(int source, int dest, Container destContainer = null) + { + // Condition: source has item and dest has no item + if (ValidateSlots(source, dest, destContainer) && + HasItem(source) && + ((destContainer != null && !HasItem(dest, destContainer)) || (destContainer == null && !HasItem(dest)))) + return mc.DoWindowAction(c.ID, source, WindowActionType.LeftClick) + && mc.DoWindowAction(destContainer == null ? c.ID : destContainer.ID, dest, WindowActionType.LeftClick); + else return false; + } + + /// + /// Swap two item. Both slots should contain item + /// + /// Slot 1 + /// Slot 2 + /// Slot2 container (only if slot2 is in other container) + /// True if success or False if Failed + public bool Swap(int slot1, int slot2, Container destContainer = null) + { + // Condition: Both slot1 and slot2 has item + if (ValidateSlots(slot1, slot2, destContainer) && + HasItem(slot1) && + (destContainer != null && HasItem(slot2, destContainer) || (destContainer == null && HasItem(slot2)))) + return mc.DoWindowAction(c.ID, slot1, WindowActionType.LeftClick) + && mc.DoWindowAction(destContainer == null ? c.ID : destContainer.ID, slot2, WindowActionType.LeftClick) + && mc.DoWindowAction(c.ID, slot1, WindowActionType.LeftClick); + else return false; + } + + /// + /// Drag a stack of item over a set of slots. Those slots should be empty or have the same item as source item + /// + /// Source item + /// Slots to be drag over + /// Which mouse key to use: StartDragLeft, StartDragRight or StartDragMiddle + /// True if success or false if Failed + /// + /// After calling this method, you will need to wait until the server have updated the player inventory before doing next inventory action + /// + public bool DragOverSlots(int source, IEnumerable slots, WindowActionType mouseKey = WindowActionType.StartDragLeft) + { + if (!HasItem(source)) + return false; + List availableSlots = new List(slots.Count()); + // filter out different item type or non-empty slots (they will be ignored silently) + foreach (var slot in slots) + if (ItemTypeEqual(source, slot) || !HasItem(slot)) + availableSlots.Add(slot); + if (availableSlots.Count > 0) + { + WindowActionType startDragging = WindowActionType.StartDragLeft; + WindowActionType addDragging = WindowActionType.AddDragLeft; + WindowActionType endDragging = WindowActionType.EndDragLeft; + switch (mouseKey) + { + case WindowActionType.StartDragRight: + { + startDragging = WindowActionType.StartDragRight; + addDragging = WindowActionType.AddDragRight; + endDragging = WindowActionType.EndDragRight; + break; + } + case WindowActionType.StartDragMiddle: + { + startDragging = WindowActionType.StartDragMiddle; + addDragging = WindowActionType.AddDragMiddle; + endDragging = WindowActionType.EndDragMiddle; + break; + } + } + mc.DoWindowAction(c.ID, source, WindowActionType.LeftClick); // grab item + mc.DoWindowAction(c.ID, -999, startDragging); + foreach (var slot in availableSlots) + { + mc.DoWindowAction(c.ID, slot, addDragging); + } + mc.DoWindowAction(c.ID, -999, endDragging); + mc.DoWindowAction(c.ID, source, WindowActionType.LeftClick); // put down item left (if any) + return true; + } + else return false; + } + + /// + /// Validate two slot by comparing they are different and within the maximum slot count of the container + /// + /// Slot 1 + /// Slot 2 + /// Second container (only if slot2 is in other container) + /// The compare result + private bool ValidateSlots(int s1, int s2, Container s2Container = null) + { + if (s2Container == null) + return (s1 != s2 && s1 < c.Type.SlotCount() && s2 < c.Type.SlotCount()); + else + return (s1 < c.Type.SlotCount() && s2 < s2Container.Type.SlotCount()); + } + + /// + /// Check if the slot has an item inside + /// + /// Slot ID + /// Specify another contianer (only if the slot is in other container) + /// True if has item + private bool HasItem(int slot, Container c = null) + { + if (c == null) + c = this.c; + return c.Items.ContainsKey(slot); + } + + /// + /// Check both slots item type are the same + /// + /// + /// + /// Second container (only if slot2 is in other container) + /// True if they are equal + private bool ItemTypeEqual(int slot1, int slot2, Container s2Container = null) + { + if (s2Container == null) + { + if (HasItem(slot1) && HasItem(slot2)) + return c.Items[slot1].Type == c.Items[slot2].Type; + else return false; + } + else + { + if (HasItem(slot1) && HasItem(slot2, s2Container)) + return c.Items[slot1].Type == s2Container.Items[slot2].Type; + else return false; + } + } + } +} diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index 3db0c5a2..06e5bcb6 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -1510,7 +1510,6 @@ namespace MinecraftClient Settings.MCSettings_Skin_All, Settings.MCSettings_MainHand); - DispatchBotEvent(bot => bot.AfterGameJoined()); if (inventoryHandlingRequested) { @@ -1520,6 +1519,8 @@ namespace MinecraftClient } ClearInventories(); + + DispatchBotEvent(bot => bot.AfterGameJoined()); } /// diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj index 49f1e6b4..930e27ac 100644 --- a/MinecraftClient/MinecraftClient.csproj +++ b/MinecraftClient/MinecraftClient.csproj @@ -120,6 +120,7 @@ +