diff --git a/MinecraftClient/Inventory/Container.cs b/MinecraftClient/Inventory/Container.cs index 4547c50c..113e862d 100644 --- a/MinecraftClient/Inventory/Container.cs +++ b/MinecraftClient/Inventory/Container.cs @@ -172,5 +172,19 @@ namespace MinecraftClient.Inventory } return result.ToArray(); } + + public int[] GetEmpytSlot() + { + List result = new List(); + for (int i = 0; i < Type.SlotCount(); i++) + { + result.Add(i); + } + foreach (var item in Items) + { + result.Remove(item.Key); + } + return result.ToArray(); + } } } diff --git a/MinecraftClient/Inventory/ContainerTypeExtensions.cs b/MinecraftClient/Inventory/ContainerTypeExtensions.cs new file mode 100644 index 00000000..95f84e64 --- /dev/null +++ b/MinecraftClient/Inventory/ContainerTypeExtensions.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MinecraftClient.Inventory +{ + public static class ContainerTypeExtensions + { + public static int SlotCount(this ContainerType c) + { + switch (c) + { + case ContainerType.PlayerInventory: return 44; + case ContainerType.Generic_9x3: return 62; + case ContainerType.Generic_9x6: return 89; + case ContainerType.Generic_3x3: return 44; + case ContainerType.Crafting: return 45; + case ContainerType.BlastFurnace: return 38; + case ContainerType.Furnace: return 38; + case ContainerType.Smoker: return 38; + case ContainerType.Enchantment: return 37; + case ContainerType.BrewingStand: return 40; + case ContainerType.Merchant: return 38; + case ContainerType.Beacon: return 36; + case ContainerType.Anvil: return 38; + case ContainerType.Hopper: return 40; + case ContainerType.ShulkerBox: return 62; + case ContainerType.Loom: return 39; + case ContainerType.Stonecutter: return 37; + case ContainerType.Lectern: return 36; + case ContainerType.Cartography: return 38; + case ContainerType.Grindstone: return 38; + case ContainerType.Unknown: return 0; + default: return 0; + } + } + } +} diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index 1ef9053a..33afc900 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -1006,9 +1006,20 @@ namespace MinecraftClient // Check if both item are the same? if (inventory.Items[slotId].Type == playerInventory.Items[-1].Type) { - // Put cursor item to target - inventory.Items[slotId].Count += playerInventory.Items[-1].Count; - playerInventory.Items.Remove(-1); + int maxCount = inventory.Items[slotId].Type.StackCount(); + // Check item stacking + if ((inventory.Items[slotId].Count + playerInventory.Items[-1].Count) <= maxCount) + { + // Put cursor item to target + inventory.Items[slotId].Count += playerInventory.Items[-1].Count; + playerInventory.Items.Remove(-1); + } + else + { + // Leave some item on cursor + playerInventory.Items[-1].Count -= (maxCount - inventory.Items[slotId].Count); + inventory.Items[slotId].Count = maxCount; + } } else { @@ -1052,9 +1063,13 @@ namespace MinecraftClient // Check if both item are the same? if (inventory.Items[slotId].Type == playerInventory.Items[-1].Type) { - // Drop 1 item count from cursor - playerInventory.Items[-1].Count--; - inventory.Items[slotId].Count++; + // Check item stacking + if (inventory.Items[slotId].Count < inventory.Items[slotId].Type.StackCount()) + { + // Drop 1 item count from cursor + playerInventory.Items[-1].Count--; + inventory.Items[slotId].Count++; + } } else { @@ -1117,33 +1132,117 @@ namespace MinecraftClient { /* Target slot have item */ - // Cursor have item or not doesn't matter + int upperStartSlot = 9; + int upperEndSlot = 35; + switch (inventory.Type) { - case ContainerType.PlayerInventory: // Shift click within player inventory - // If hotbar already have same item, will put on it first until every stack are full - // If no more same item , will put on the first empty slot (smaller slot id) - // If inventory full, item will not move - if (slotId < 36) - { - // Clicked slot is on upper side inventory, put it to hotbar - foreach(KeyValuePair _item in playerInventory.Items) - { - if (_item.Key < 35) continue; + case ContainerType.PlayerInventory: + upperStartSlot = 9; + upperEndSlot = 35; + break; + case ContainerType.Crafting: + upperStartSlot = 1; + upperEndSlot = 9; + break; + // TODO: Define more container type here + } - if (_item.Value.Type == inventory.Items[slotId].Type && _item.Value.Count < 64) - { - // - } + // Cursor have item or not doesn't matter + // If hotbar already have same item, will put on it first until every stack are full + // If no more same item , will put on the first empty slot (smaller slot id) + // If inventory full, item will not move + if (slotId <= upperEndSlot) + { + // Clicked slot is on upper side inventory, put it to hotbar + // Now try to find same item and put on them + var itemsClone = playerInventory.Items.ToDictionary(entry => entry.Key, entry => entry.Value); + foreach (KeyValuePair _item in itemsClone) + { + if (_item.Key <= upperEndSlot) continue; + + int maxCount = _item.Value.Type.StackCount(); + if (_item.Value.Type == inventory.Items[slotId].Type && _item.Value.Count < maxCount) + { + // Put item on that stack + int spaceLeft = maxCount - _item.Value.Count; + if (inventory.Items[slotId].Count <= spaceLeft) + { + // Can fit into the stack + inventory.Items[_item.Key].Count += inventory.Items[slotId].Count; + inventory.Items.Remove(slotId); + } + else + { + inventory.Items[slotId].Count -= spaceLeft; + inventory.Items[_item.Key].Count = inventory.Items[_item.Key].Type.StackCount(); } } - else + } + if (inventory.Items[slotId].Count > 0) + { + int[] emptySlots = inventory.GetEmpytSlot(); + int emptySlot = -2; + foreach (int slot in emptySlots) { - // Clicked slot is on hotbar, put it to upper inventory - - + if (slot <= upperEndSlot) continue; + emptySlot = slot; + break; } - break; + if (emptySlot != -2) + { + var itemTmp = inventory.Items[slotId]; + inventory.Items[emptySlot] = new Item((int)itemTmp.Type, itemTmp.Count, itemTmp.NBT); + inventory.Items.Remove(slotId); + } + } + } + else + { + // Clicked slot is on hotbar, put it to upper inventory + // Now try to find same item and put on them + var itemsClone = playerInventory.Items.ToDictionary(entry => entry.Key, entry => entry.Value); + foreach (KeyValuePair _item in itemsClone) + { + if (_item.Key < upperStartSlot) continue; + if (_item.Key >= upperEndSlot) break; + + int maxCount = _item.Value.Type.StackCount(); + if (_item.Value.Type == inventory.Items[slotId].Type && _item.Value.Count < maxCount) + { + // Put item on that stack + int spaceLeft = maxCount - _item.Value.Count; + if (inventory.Items[slotId].Count <= spaceLeft) + { + // Can fit into the stack + inventory.Items[_item.Key].Count += inventory.Items[slotId].Count; + inventory.Items.Remove(slotId); + } + else + { + inventory.Items[slotId].Count -= spaceLeft; + inventory.Items[_item.Key].Count = inventory.Items[_item.Key].Type.StackCount(); + } + } + } + if (inventory.Items[slotId].Count > 0) + { + int[] emptySlots = inventory.GetEmpytSlot(); + int emptySlot = -2; + foreach (int slot in emptySlots) + { + if (slot < upperStartSlot) continue; + if (slot >= upperEndSlot) break; + emptySlot = slot; + break; + } + if (emptySlot != -2) + { + var itemTmp = inventory.Items[slotId]; + inventory.Items[emptySlot] = new Item((int)itemTmp.Type, itemTmp.Count, itemTmp.NBT); + inventory.Items.Remove(slotId); + } + } } } break; diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj index 9838ac10..792e67e4 100644 --- a/MinecraftClient/MinecraftClient.csproj +++ b/MinecraftClient/MinecraftClient.csproj @@ -112,6 +112,7 @@ + diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 02a138c4..e7a1901c 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -1573,7 +1573,7 @@ namespace MinecraftClient.Protocol.Handlers case WindowActionType.LeftClick: button = 0; break; case WindowActionType.RightClick: button = 1; break; case WindowActionType.MiddleClick: button = 2; mode = 3; break; - case WindowActionType.ShiftClick: button = 0; mode = 1; break; + case WindowActionType.ShiftClick: button = 0; mode = 1; item = new Item(-1, 0, null); break; case WindowActionType.DropItem: button = 0; mode = 4;