diff --git a/MinecraftClient/Commands/Inventory.cs b/MinecraftClient/Commands/Inventory.cs
index b164662a..27562075 100644
--- a/MinecraftClient/Commands/Inventory.cs
+++ b/MinecraftClient/Commands/Inventory.cs
@@ -117,52 +117,72 @@ namespace MinecraftClient.Commands
case "click":
if (args.Length >= 3)
{
- int slot = int.Parse(args[2]);
- WindowActionType actionType = WindowActionType.LeftClick;
- string keyName = "cmd.inventory.left";
- if (args.Length >= 4)
+ try
{
- string b = args[3];
- if (b.ToLower()[0] == 'r')
+ int slot = int.Parse(args[2]);
+ WindowActionType actionType = WindowActionType.LeftClick;
+ string keyName = "cmd.inventory.left";
+ if (args.Length >= 4)
{
- actionType = WindowActionType.RightClick;
- keyName = "cmd.inventory.right";
- }
- if (b.ToLower()[0] == 'm')
- {
- actionType = WindowActionType.MiddleClick;
- keyName = "cmd.inventory.middle";
+ string b = args[3];
+ if (b.ToLower()[0] == 'r')
+ {
+ actionType = WindowActionType.RightClick;
+ keyName = "cmd.inventory.right";
+ }
+ if (b.ToLower()[0] == 'm')
+ {
+ actionType = WindowActionType.MiddleClick;
+ keyName = "cmd.inventory.middle";
+ }
}
+ handler.DoWindowAction(inventoryId, slot, actionType);
+ return Translations.Get("cmd.inventory.clicking", Translations.Get(keyName), slot, inventoryId);
}
- handler.DoWindowAction(inventoryId, slot, actionType);
- return Translations.Get("cmd.inventory.clicking", Translations.Get(keyName), slot, inventoryId);
+ catch (FormatException) { return GetCmdDescTranslated(); }
+ }
+ else return CmdUsage;
+ case "shiftclick":
+ if (args.Length >= 3)
+ {
+ try
+ {
+ int slot = int.Parse(args[2]);
+ handler.DoWindowAction(inventoryId, slot, WindowActionType.ShiftClick);
+ return Translations.Get("cmd.inventory.shiftclick", slot, inventoryId);
+ }
+ catch (FormatException) { return GetCmdDescTranslated(); }
}
else return CmdUsage;
case "drop":
if (args.Length >= 3)
{
- int slot = int.Parse(args[2]);
- // check item exist
- if (!handler.GetInventory(inventoryId).Items.ContainsKey(slot))
- return Translations.Get("cmd.inventory.no_item", slot);
- WindowActionType actionType = WindowActionType.DropItem;
- if (args.Length >= 4)
+ try
{
- if (args[3].ToLower() == "all")
+ int slot = int.Parse(args[2]);
+ // check item exist
+ if (!handler.GetInventory(inventoryId).Items.ContainsKey(slot))
+ return Translations.Get("cmd.inventory.no_item", slot);
+ WindowActionType actionType = WindowActionType.DropItem;
+ if (args.Length >= 4)
{
- actionType = WindowActionType.DropItemStack;
+ if (args[3].ToLower() == "all")
+ {
+ actionType = WindowActionType.DropItemStack;
+ }
+ }
+ if (handler.DoWindowAction(inventoryId, slot, actionType))
+ {
+ if (actionType == WindowActionType.DropItemStack)
+ return Translations.Get("cmd.inventory.drop_stack", slot);
+ else return Translations.Get("cmd.inventory.drop", slot);
+ }
+ else
+ {
+ return "Failed";
}
}
- if (handler.DoWindowAction(inventoryId, slot, actionType))
- {
- if (actionType == WindowActionType.DropItemStack)
- return Translations.Get("cmd.inventory.drop_stack", slot);
- else return Translations.Get("cmd.inventory.drop", slot);
- }
- else
- {
- return "Failed";
- }
+ catch (FormatException) { return GetCmdDescTranslated(); }
}
else return GetCmdDescTranslated();
default:
diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs
index 53a92a89..65722866 100644
--- a/MinecraftClient/McClient.cs
+++ b/MinecraftClient/McClient.cs
@@ -1027,7 +1027,7 @@ namespace MinecraftClient
///
/// Window ID of the requested inventory
/// Item Dictionary indexed by Slot ID (Check wiki.vg for slot ID)
- public Container GetInventory(int inventoryID)
+ public Container? GetInventory(int inventoryID)
{
if (InvokeRequired)
return InvokeOnMainThread(() => GetInventory(inventoryID));
@@ -1043,7 +1043,7 @@ namespace MinecraftClient
/// Item Dictionary indexed by Slot ID (Check wiki.vg for slot ID)
public Container GetPlayerInventory()
{
- return GetInventory(0);
+ return GetInventory(0)!;
}
///
@@ -1277,6 +1277,63 @@ namespace MinecraftClient
return InvokeOnMainThread(() => handler.SendUseItem(0, this.sequenceId));
}
+ ///
+ /// Try to merge a slot
+ ///
+ /// The container where the item is located
+ /// Items to be processed
+ /// The ID of the slot of the item to be processed
+ /// The slot that was put down
+ /// The ID of the slot being put down
+ /// Record changes
+ /// Whether to fully merge
+ private static bool TryMergeSlot(Container inventory, Item item, int slotId, Item curItem, int curId, List> changedSlots)
+ {
+ int spaceLeft = curItem.Type.StackCount() - curItem.Count;
+ if (curItem.Type == item!.Type && spaceLeft > 0)
+ {
+ // Put item on that stack
+ if (item.Count <= spaceLeft)
+ {
+ // Can fit into the stack
+ item.Count = 0;
+ curItem.Count += item.Count;
+
+ changedSlots.Add(new Tuple((short)curId, curItem));
+ changedSlots.Add(new Tuple((short)slotId, null));
+
+ inventory.Items.Remove(slotId);
+ return true;
+ }
+ else
+ {
+ item.Count -= spaceLeft;
+ curItem.Count += spaceLeft;
+
+ changedSlots.Add(new Tuple((short)curId, curItem));
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// Store items in a new slot
+ ///
+ /// The container where the item is located
+ /// Items to be processed
+ /// The ID of the slot of the item to be processed
+ /// ID of the new slot
+ /// Record changes
+ private static void StoreInNewSlot(Container inventory, Item item, int slotId, int newSlotId, List> changedSlots)
+ {
+ Item newItem = new(item.Type, item.Count, item.NBT);
+ inventory.Items[newSlotId] = newItem;
+ inventory.Items.Remove(slotId);
+
+ changedSlots.Add(new Tuple((short)newSlotId, newItem));
+ changedSlots.Add(new Tuple((short)slotId, null));
+ }
+
///
/// Click a slot in the specified window
///
@@ -1286,15 +1343,15 @@ namespace MinecraftClient
if (InvokeRequired)
return InvokeOnMainThread(() => DoWindowAction(windowId, slotId, action));
- Item item = null;
+ Item? item = null;
if (inventories.ContainsKey(windowId) && inventories[windowId].Items.ContainsKey(slotId))
item = inventories[windowId].Items[slotId];
- List> changedSlots = new List>(); // List
+ List> changedSlots = new(); // List
// Update our inventory base on action type
- var inventory = GetInventory(windowId);
- var playerInventory = GetInventory(0);
+ Container inventory = GetInventory(windowId)!;
+ Container playerInventory = GetInventory(0)!;
if (inventory != null)
{
switch (action)
@@ -1343,9 +1400,9 @@ namespace MinecraftClient
}
if (inventory.Items.ContainsKey(slotId))
- changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId]));
+ changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId]));
else
- changedSlots.Add(new Tuple((short)slotId, null));
+ changedSlots.Add(new Tuple((short)slotId, null));
}
else
{
@@ -1359,7 +1416,7 @@ namespace MinecraftClient
playerInventory.Items[-1] = inventory.Items[slotId];
inventory.Items.Remove(slotId);
- changedSlots.Add(new Tuple((short)slotId, null));
+ changedSlots.Add(new Tuple((short)slotId, null));
}
}
break;
@@ -1439,30 +1496,454 @@ namespace MinecraftClient
}
}
if (inventory.Items.ContainsKey(slotId))
- changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId]));
+ changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId]));
else
- changedSlots.Add(new Tuple((short)slotId, null));
+ changedSlots.Add(new Tuple((short)slotId, null));
break;
case WindowActionType.ShiftClick:
if (slotId == 0) break;
- if (inventory.Items.ContainsKey(slotId))
+ if (item != null)
{
/* Target slot have item */
+ bool lower2upper = false, upper2backpack = false, backpack2hotbar = false; // mutual exclusion
+ bool hotbarFirst = true; // Used when upper2backpack = true
int upperStartSlot = 9;
int upperEndSlot = 35;
+ int lowerStartSlot = 36;
switch (inventory.Type)
{
case ContainerType.PlayerInventory:
- upperStartSlot = 9;
- upperEndSlot = 35;
+ if (slotId >= 0 && slotId <= 8 || slotId == 45)
+ {
+ if (slotId != 0)
+ hotbarFirst = false;
+ upper2backpack = true;
+ lowerStartSlot = 9;
+ }
+ else if (item != null && false /* Check if wearable */)
+ {
+ lower2upper = true;
+ // upperStartSlot = ?;
+ // upperEndSlot = ?;
+ // Todo: Distinguish the type of equipment
+ }
+ else
+ {
+ if (slotId >= 9 && slotId <= 35)
+ {
+ backpack2hotbar = true;
+ lowerStartSlot = 36;
+ }
+ else
+ {
+ lower2upper = true;
+ upperStartSlot = 9;
+ upperEndSlot = 35;
+ }
+ }
+ break;
+ case ContainerType.Generic_9x1:
+ if (slotId >= 0 && slotId <= 8)
+ {
+ upper2backpack = true;
+ lowerStartSlot = 9;
+ }
+ else
+ {
+ lower2upper = true;
+ upperStartSlot = 0;
+ upperEndSlot = 8;
+ }
+ break;
+ case ContainerType.Generic_9x2:
+ if (slotId >= 0 && slotId <= 17)
+ {
+ upper2backpack = true;
+ lowerStartSlot = 18;
+ }
+ else
+ {
+ lower2upper = true;
+ upperStartSlot = 0;
+ upperEndSlot = 17;
+ }
+ break;
+ case ContainerType.Generic_9x3:
+ case ContainerType.ShulkerBox:
+ if (slotId >= 0 && slotId <= 26)
+ {
+ upper2backpack = true;
+ lowerStartSlot = 27;
+ }
+ else
+ {
+ lower2upper = true;
+ upperStartSlot = 0;
+ upperEndSlot = 26;
+ }
+ break;
+ case ContainerType.Generic_9x4:
+ if (slotId >= 0 && slotId <= 35)
+ {
+ upper2backpack = true;
+ lowerStartSlot = 36;
+ }
+ else
+ {
+ lower2upper = true;
+ upperStartSlot = 0;
+ upperEndSlot = 35;
+ }
+ break;
+ case ContainerType.Generic_9x5:
+ if (slotId >= 0 && slotId <= 44)
+ {
+ upper2backpack = true;
+ lowerStartSlot = 45;
+ }
+ else
+ {
+ lower2upper = true;
+ upperStartSlot = 0;
+ upperEndSlot = 44;
+ }
+ break;
+ case ContainerType.Generic_9x6:
+ if (slotId >= 0 && slotId <= 53)
+ {
+ upper2backpack = true;
+ lowerStartSlot = 54;
+ }
+ else
+ {
+ lower2upper = true;
+ upperStartSlot = 0;
+ upperEndSlot = 53;
+ }
+ break;
+ case ContainerType.Generic_3x3:
+ if (slotId >= 0 && slotId <= 8)
+ {
+ upper2backpack = true;
+ lowerStartSlot = 9;
+ }
+ else
+ {
+ lower2upper = true;
+ upperStartSlot = 0;
+ upperEndSlot = 8;
+ }
+ break;
+ case ContainerType.Anvil:
+ if (slotId >= 0 && slotId <= 2)
+ {
+ if (slotId >= 0 && slotId <= 1)
+ hotbarFirst = false;
+ upper2backpack = true;
+ lowerStartSlot = 3;
+ }
+ else
+ {
+ lower2upper = true;
+ upperStartSlot = 0;
+ upperEndSlot = 1;
+ }
+ break;
+ case ContainerType.Beacon:
+ if (slotId == 0)
+ {
+ hotbarFirst = false;
+ upper2backpack = true;
+ lowerStartSlot = 1;
+ }
+ else if (item != null && item.Count == 1 && (item.Type == ItemType.NetheriteIngot ||
+ item.Type == ItemType.Emerald || item.Type == ItemType.Diamond || item.Type == ItemType.GoldIngot ||
+ item.Type == ItemType.IronIngot) && !inventory.Items.ContainsKey(0))
+ {
+ lower2upper = true;
+ upperStartSlot = 0;
+ upperEndSlot = 0;
+ }
+ else
+ {
+ if (slotId >= 1 && slotId <= 27)
+ {
+ backpack2hotbar = true;
+ lowerStartSlot = 28;
+ }
+ else
+ {
+ lower2upper = true;
+ upperStartSlot = 1;
+ upperEndSlot = 27;
+ }
+ }
+ break;
+ case ContainerType.BlastFurnace:
+ case ContainerType.Furnace:
+ case ContainerType.Smoker:
+ if (slotId >= 0 && slotId <= 2)
+ {
+ if (slotId >= 0 && slotId <= 1)
+ hotbarFirst = false;
+ upper2backpack = true;
+ lowerStartSlot = 3;
+ }
+ else if (item != null && false /* Check if it can be burned */)
+ {
+ lower2upper = true;
+ upperStartSlot = 0;
+ upperEndSlot = 0;
+ }
+ else
+ {
+ if (slotId >= 3 && slotId <= 29)
+ {
+ backpack2hotbar = true;
+ lowerStartSlot = 30;
+ }
+ else
+ {
+ lower2upper = true;
+ upperStartSlot = 3;
+ upperEndSlot = 29;
+ }
+ }
+ break;
+ case ContainerType.BrewingStand:
+ if (slotId >= 0 && slotId <= 3)
+ {
+ upper2backpack = true;
+ lowerStartSlot = 5;
+ }
+ else if (item != null && item.Type == ItemType.BlazePowder)
+ {
+ lower2upper = true;
+ if (!inventory.Items.ContainsKey(4) || inventory.Items[4].Count < 64)
+ upperStartSlot = upperEndSlot = 4;
+ else
+ upperStartSlot = upperEndSlot = 3;
+ }
+ else if (item != null && false /* Check if it can be used for alchemy */)
+ {
+ lower2upper = true;
+ upperStartSlot = upperEndSlot = 3;
+ }
+ else if (item != null && (item.Type == ItemType.Potion || item.Type == ItemType.GlassBottle))
+ {
+ lower2upper = true;
+ upperStartSlot = 0;
+ upperEndSlot = 2;
+ }
+ else
+ {
+ if (slotId >= 5 && slotId <= 31)
+ {
+ backpack2hotbar = true;
+ lowerStartSlot = 32;
+ }
+ else
+ {
+ lower2upper = true;
+ upperStartSlot = 5;
+ upperEndSlot = 31;
+ }
+ }
break;
case ContainerType.Crafting:
- upperStartSlot = 1;
- upperEndSlot = 9;
+ if (slotId >= 0 && slotId <= 9)
+ {
+ if (slotId >= 1 && slotId <= 9)
+ hotbarFirst = false;
+ upper2backpack = true;
+ lowerStartSlot = 10;
+ }
+ else
+ {
+ lower2upper = true;
+ upperStartSlot = 1;
+ upperEndSlot = 9;
+ }
break;
+ case ContainerType.Enchantment:
+ if (slotId >= 0 && slotId <= 1)
+ {
+ upper2backpack = true;
+ lowerStartSlot = 5;
+ }
+ else if (item != null && item.Type == ItemType.LapisLazuli)
+ {
+ lower2upper = true;
+ upperStartSlot = upperEndSlot = 1;
+ }
+ else
+ {
+ lower2upper = true;
+ upperStartSlot = 0;
+ upperEndSlot = 0;
+ }
+ break;
+ case ContainerType.Grindstone:
+ if (slotId >= 0 && slotId <= 2)
+ {
+ if (slotId >= 0 && slotId <= 1)
+ hotbarFirst = false;
+ upper2backpack = true;
+ lowerStartSlot = 3;
+ }
+ else if (item != null && false /* Check */)
+ {
+ lower2upper = true;
+ upperStartSlot = 0;
+ upperEndSlot = 1;
+ }
+ else
+ {
+ lower2upper = true;
+ upperStartSlot = 0;
+ upperEndSlot = 1;
+ }
+ break;
+ case ContainerType.Hopper:
+ if (slotId >= 0 && slotId <= 4)
+ {
+ upper2backpack = true;
+ lowerStartSlot = 5;
+ }
+ else
+ {
+ lower2upper = true;
+ upperStartSlot = 0;
+ upperEndSlot = 4;
+ }
+ break;
+ case ContainerType.Lectern:
+ break;
+ case ContainerType.Loom:
+ if (slotId >= 0 && slotId <= 3)
+ {
+ if (slotId >= 0 && slotId <= 5)
+ hotbarFirst = false;
+ upper2backpack = true;
+ lowerStartSlot = 4;
+ }
+ else if (item != null && false /* Check for availability for staining */)
+ {
+ lower2upper = true;
+ // upperStartSlot = ?;
+ // upperEndSlot = ?;
+ }
+ else
+ {
+ if (slotId >= 4 && slotId <= 30)
+ {
+ backpack2hotbar = true;
+ lowerStartSlot = 31;
+ }
+ else
+ {
+ lower2upper = true;
+ upperStartSlot = 4;
+ upperEndSlot = 30;
+ }
+ }
+ break;
+ case ContainerType.Merchant:
+ if (slotId >= 0 && slotId <= 2)
+ {
+ if (slotId >= 0 && slotId <= 1)
+ hotbarFirst = false;
+ upper2backpack = true;
+ lowerStartSlot = 3;
+ }
+ else if (item != null && false /* Check if it is available for trading */)
+ {
+ lower2upper = true;
+ upperStartSlot = 0;
+ upperEndSlot = 1;
+ }
+ else
+ {
+ if (slotId >= 3 && slotId <= 29)
+ {
+ backpack2hotbar = true;
+ lowerStartSlot = 30;
+ }
+ else
+ {
+ lower2upper = true;
+ upperStartSlot = 3;
+ upperEndSlot = 29;
+ }
+ }
+ break;
+ case ContainerType.Cartography:
+ if (slotId >= 0 && slotId <= 2)
+ {
+ if (slotId >= 0 && slotId <= 1)
+ hotbarFirst = false;
+ upper2backpack = true;
+ lowerStartSlot = 3;
+ }
+ else if (item != null && item.Type == ItemType.FilledMap)
+ {
+ lower2upper = true;
+ upperStartSlot = upperEndSlot = 0;
+ }
+ else if (item != null && item.Type == ItemType.Map)
+ {
+ lower2upper = true;
+ upperStartSlot = upperEndSlot = 1;
+ }
+ else
+ {
+ if (slotId >= 3 && slotId <= 29)
+ {
+ backpack2hotbar = true;
+ lowerStartSlot = 30;
+ }
+ else
+ {
+ lower2upper = true;
+ upperStartSlot = 3;
+ upperEndSlot = 29;
+ }
+ }
+ break;
+ case ContainerType.Stonecutter:
+ if (slotId >= 0 && slotId <= 1)
+ {
+ if (slotId == 0)
+ hotbarFirst = false;
+ upper2backpack = true;
+ lowerStartSlot = 2;
+ }
+ else if (item != null && false /* Check if it is available for stone cutteing */)
+ {
+ lower2upper = true;
+ upperStartSlot = 0;
+ upperEndSlot = 0;
+ }
+ else
+ {
+ if (slotId >= 2 && slotId <= 28)
+ {
+ backpack2hotbar = true;
+ lowerStartSlot = 29;
+ }
+ else
+ {
+ lower2upper = true;
+ upperStartSlot = 2;
+ upperEndSlot = 28;
+ }
+ }
+ break;
+ default:
// TODO: Define more container type here
+ goto case ContainerType.Generic_9x3;
}
// Cursor have item or not doesn't matter
@@ -1470,120 +1951,94 @@ namespace MinecraftClient
// If no more same item , will put on the first empty slot (smaller slot id)
// If inventory full, item will not move
int itemCount = inventory.Items[slotId].Count;
- if (slotId <= upperEndSlot)
+ if (lower2upper)
{
- // 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)
+ int firstEmptySlot = -1;
+ for (int i = upperStartSlot; i <= upperEndSlot; ++i)
{
- if (_item.Key <= upperEndSlot) continue;
-
- int maxCount = _item.Value.Type.StackCount();
- if (_item.Value.Type == inventory.Items[slotId].Type && _item.Value.Count < maxCount)
+ if (inventory.Items.TryGetValue(i, out Item? curItem))
{
- // Put item on that stack
- int spaceLeft = maxCount - _item.Value.Count;
- if (inventory.Items[slotId].Count <= spaceLeft)
+ if (TryMergeSlot(inventory, item!, slotId, curItem, i, changedSlots))
+ break;
+ }
+ else if (firstEmptySlot == -1)
+ firstEmptySlot = i;
+ }
+ if (item!.Count > 0)
+ {
+ if (firstEmptySlot != -1)
+ StoreInNewSlot(inventory, item, slotId, firstEmptySlot, changedSlots);
+ else if (item.Count != itemCount)
+ changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId]));
+ }
+ }
+ else if (upper2backpack)
+ {
+ int hotbarEnd = lowerStartSlot + 4 * 9 - 1;
+ if (hotbarFirst)
+ {
+ int lastEmptySlot = -1;
+ for (int i = hotbarEnd; i >= lowerStartSlot; --i)
+ {
+ if (inventory.Items.TryGetValue(i, out Item? curItem))
{
- // Can fit into the stack
- inventory.Items[_item.Key].Count += inventory.Items[slotId].Count;
- inventory.Items.Remove(slotId);
-
- changedSlots.Add(new Tuple((short)_item.Key, inventory.Items[_item.Key]));
- changedSlots.Add(new Tuple((short)slotId, null));
- }
- else
- {
- inventory.Items[slotId].Count -= spaceLeft;
- inventory.Items[_item.Key].Count = inventory.Items[_item.Key].Type.StackCount();
-
- changedSlots.Add(new Tuple((short)_item.Key, inventory.Items[_item.Key]));
+ if (TryMergeSlot(inventory, item!, slotId, curItem, i, changedSlots))
+ break;
}
+ else if (lastEmptySlot == -1)
+ lastEmptySlot = i;
+ }
+ if (item!.Count > 0)
+ {
+ if (lastEmptySlot != -1)
+ StoreInNewSlot(inventory, item, slotId, lastEmptySlot, changedSlots);
+ else if (item.Count != itemCount)
+ changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId]));
}
}
- if (inventory.Items[slotId].Count > 0)
+ else
{
- int[] emptySlots = inventory.GetEmpytSlots();
- int emptySlot = -2;
- foreach (int slot in emptySlots)
+ int firstEmptySlot = -1;
+ for (int i = lowerStartSlot; i <= hotbarEnd; ++i)
{
- if (slot <= upperEndSlot) continue;
- emptySlot = slot;
- break;
+ if (inventory.Items.TryGetValue(i, out Item? curItem))
+ {
+ if (TryMergeSlot(inventory, item!, slotId, curItem, i, changedSlots))
+ break;
+ }
+ else if (firstEmptySlot == -1)
+ firstEmptySlot = i;
}
- if (emptySlot != -2)
+ if (item!.Count > 0)
{
- var itemTmp = inventory.Items[slotId];
- inventory.Items[emptySlot] = new Item(itemTmp.Type, itemTmp.Count, itemTmp.NBT);
- inventory.Items.Remove(slotId);
-
- changedSlots.Add(new Tuple((short)emptySlot, inventory.Items[emptySlot]));
- changedSlots.Add(new Tuple((short)slotId, null));
- }
- else if (inventory.Items[slotId].Count != itemCount)
- {
- changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId]));
+ if (firstEmptySlot != -1)
+ StoreInNewSlot(inventory, item, slotId, firstEmptySlot, changedSlots);
+ else if (item.Count != itemCount)
+ changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId]));
}
}
}
- else
+ else if (backpack2hotbar)
{
- // 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)
+ int hotbarEnd = lowerStartSlot + 1 * 9 - 1;
+
+ int firstEmptySlot = -1;
+ for (int i = lowerStartSlot; i <= hotbarEnd; ++i)
{
- 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)
+ if (inventory.Items.TryGetValue(i, out Item? curItem))
{
- // 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);
-
- changedSlots.Add(new Tuple((short)_item.Key, inventory.Items[_item.Key]));
- changedSlots.Add(new Tuple((short)slotId, null));
- }
- else
- {
- inventory.Items[slotId].Count -= spaceLeft;
- inventory.Items[_item.Key].Count = inventory.Items[_item.Key].Type.StackCount();
-
- changedSlots.Add(new Tuple((short)_item.Key, inventory.Items[_item.Key]));
- }
+ if (TryMergeSlot(inventory, item!, slotId, curItem, i, changedSlots))
+ break;
}
+ else if (firstEmptySlot == -1)
+ firstEmptySlot = i;
}
- if (inventory.Items[slotId].Count > 0)
+ if (item!.Count > 0)
{
- int[] emptySlots = inventory.GetEmpytSlots();
- 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(itemTmp.Type, itemTmp.Count, itemTmp.NBT);
- inventory.Items.Remove(slotId);
-
- changedSlots.Add(new Tuple((short)emptySlot, inventory.Items[emptySlot]));
- changedSlots.Add(new Tuple((short)slotId, null));
- }
- else if (inventory.Items[slotId].Count != itemCount)
- {
- changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId]));
- }
+ if (firstEmptySlot != -1)
+ StoreInNewSlot(inventory, item, slotId, firstEmptySlot, changedSlots);
+ else if (item.Count != itemCount)
+ changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId]));
}
}
}
@@ -1592,19 +2047,19 @@ namespace MinecraftClient
if (inventory.Items.ContainsKey(slotId))
{
inventory.Items[slotId].Count--;
- changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId]));
+ changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId]));
}
if (inventory.Items[slotId].Count <= 0)
{
inventory.Items.Remove(slotId);
- changedSlots.Add(new Tuple((short)slotId, null));
+ changedSlots.Add(new Tuple((short)slotId, null));
}
break;
case WindowActionType.DropItemStack:
inventory.Items.Remove(slotId);
- changedSlots.Add(new Tuple((short)slotId, null));
+ changedSlots.Add(new Tuple((short)slotId, null));
break;
}
}
diff --git a/MinecraftClient/Protocol/Handlers/DataTypes.cs b/MinecraftClient/Protocol/Handlers/DataTypes.cs
index f9a75213..d026c919 100644
--- a/MinecraftClient/Protocol/Handlers/DataTypes.cs
+++ b/MinecraftClient/Protocol/Handlers/DataTypes.cs
@@ -741,7 +741,7 @@ namespace MinecraftClient.Protocol.Handlers
///
/// Dictionary to encode as Nbt
/// Byte array for this NBT tag
- public byte[] GetNbt(Dictionary nbt)
+ public byte[] GetNbt(Dictionary? nbt)
{
return GetNbt(nbt, true);
}
@@ -752,7 +752,7 @@ namespace MinecraftClient.Protocol.Handlers
/// Dictionary to encode as Nbt
/// TRUE if starting a new NBT tag, FALSE if processing a nested NBT tag
/// Byte array for this NBT tag
- private byte[] GetNbt(Dictionary nbt, bool root)
+ private byte[] GetNbt(Dictionary? nbt, bool root)
{
if (nbt == null || nbt.Count == 0)
return new byte[] { 0 }; // TAG_End
@@ -1065,9 +1065,9 @@ namespace MinecraftClient.Protocol.Handlers
/// Item
/// Item Palette
/// Item slot representation
- public byte[] GetItemSlot(Item item, ItemPalette itemPalette)
+ public byte[] GetItemSlot(Item? item, ItemPalette itemPalette)
{
- List slotData = new List();
+ List slotData = new();
if (protocolversion > Protocol18Handler.MC_1_13_Version)
{
// MC 1.13 and greater
diff --git a/MinecraftClient/Protocol/Handlers/Protocol16.cs b/MinecraftClient/Protocol/Handlers/Protocol16.cs
index b0ff6546..4c8542e7 100644
--- a/MinecraftClient/Protocol/Handlers/Protocol16.cs
+++ b/MinecraftClient/Protocol/Handlers/Protocol16.cs
@@ -752,7 +752,7 @@ namespace MinecraftClient.Protocol.Handlers
return false; //Currently not implemented
}
- public bool SendWindowAction(int windowId, int slotId, WindowActionType action, Item item, List> changedSlots, int stateId)
+ public bool SendWindowAction(int windowId, int slotId, WindowActionType action, Item? item, List> changedSlots, int stateId)
{
return false; //Currently not implemented
}
diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs
index a00011ce..fe60c690 100644
--- a/MinecraftClient/Protocol/Handlers/Protocol18.cs
+++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs
@@ -2634,7 +2634,7 @@ namespace MinecraftClient.Protocol.Handlers
catch (ObjectDisposedException) { return false; }
}
- public bool SendWindowAction(int windowId, int slotId, WindowActionType action, Item item, List> changedSlots, int stateId)
+ public bool SendWindowAction(int windowId, int slotId, WindowActionType action, Item? item, List> changedSlots, int stateId)
{
try
{
@@ -2669,7 +2669,7 @@ namespace MinecraftClient.Protocol.Handlers
case WindowActionType.AddDragMiddle: button = 9; mode = 5; item = new Item(ItemType.Null, 0, null); break;
}
- List packet = new List();
+ List packet = new();
packet.Add((byte)windowId); // Window ID
// 1.18+
diff --git a/MinecraftClient/Protocol/IMinecraftCom.cs b/MinecraftClient/Protocol/IMinecraftCom.cs
index 52bfcac7..d584d13b 100644
--- a/MinecraftClient/Protocol/IMinecraftCom.cs
+++ b/MinecraftClient/Protocol/IMinecraftCom.cs
@@ -169,7 +169,7 @@ namespace MinecraftClient.Protocol
/// Slots that have been changed in this event: List
/// Inventory's stateId
/// True if packet was successfully sent
- bool SendWindowAction(int windowId, int slotId, WindowActionType action, Item item, List> changedSlots, int stateId);
+ bool SendWindowAction(int windowId, int slotId, WindowActionType action, Item? item, List> changedSlots, int stateId);
///
/// Request Creative Mode item creation into regular/survival Player Inventory
diff --git a/MinecraftClient/Protocol/IMinecraftComHandler.cs b/MinecraftClient/Protocol/IMinecraftComHandler.cs
index 4e0de511..5ef147dd 100644
--- a/MinecraftClient/Protocol/IMinecraftComHandler.cs
+++ b/MinecraftClient/Protocol/IMinecraftComHandler.cs
@@ -41,7 +41,7 @@ namespace MinecraftClient.Protocol
bool GetNetworkPacketCaptureEnabled();
void SetNetworkPacketCaptureEnabled(bool enabled);
int GetProtocolVersion();
- Container GetInventory(int inventoryID);
+ Container? GetInventory(int inventoryID);
ILogger GetLogger();
///
diff --git a/MinecraftClient/Resources/lang/en.ini b/MinecraftClient/Resources/lang/en.ini
index ba86f955..fc5b5c81 100644
--- a/MinecraftClient/Resources/lang/en.ini
+++ b/MinecraftClient/Resources/lang/en.ini
@@ -296,6 +296,7 @@ cmd.inventory.left=Left
cmd.inventory.right=Right
cmd.inventory.middle=Middle
cmd.inventory.clicking={0} clicking slot {1} in window #{2}
+cmd.inventory.shiftclick=Shift clicking slot {0} in window #{1}
cmd.inventory.no_item=No item in slot #{0}
cmd.inventory.drop=Dropped 1 item from slot #{0}
cmd.inventory.drop_stack=Dropped whole item stack from slot #{0}