mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-10-14 21:22:49 +00:00
Shift click support
This commit is contained in:
parent
09b1d06bd1
commit
65bcd83330
8 changed files with 631 additions and 155 deletions
|
|
@ -116,6 +116,8 @@ namespace MinecraftClient.Commands
|
||||||
return String.Join("\n", response.ToArray());
|
return String.Join("\n", response.ToArray());
|
||||||
case "click":
|
case "click":
|
||||||
if (args.Length >= 3)
|
if (args.Length >= 3)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
int slot = int.Parse(args[2]);
|
int slot = int.Parse(args[2]);
|
||||||
WindowActionType actionType = WindowActionType.LeftClick;
|
WindowActionType actionType = WindowActionType.LeftClick;
|
||||||
|
|
@ -137,9 +139,25 @@ namespace MinecraftClient.Commands
|
||||||
handler.DoWindowAction(inventoryId, slot, actionType);
|
handler.DoWindowAction(inventoryId, slot, actionType);
|
||||||
return Translations.Get("cmd.inventory.clicking", Translations.Get(keyName), slot, inventoryId);
|
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;
|
else return CmdUsage;
|
||||||
case "drop":
|
case "drop":
|
||||||
if (args.Length >= 3)
|
if (args.Length >= 3)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
int slot = int.Parse(args[2]);
|
int slot = int.Parse(args[2]);
|
||||||
// check item exist
|
// check item exist
|
||||||
|
|
@ -164,6 +182,8 @@ namespace MinecraftClient.Commands
|
||||||
return "Failed";
|
return "Failed";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (FormatException) { return GetCmdDescTranslated(); }
|
||||||
|
}
|
||||||
else return GetCmdDescTranslated();
|
else return GetCmdDescTranslated();
|
||||||
default:
|
default:
|
||||||
return GetCmdDescTranslated();
|
return GetCmdDescTranslated();
|
||||||
|
|
|
||||||
|
|
@ -1027,7 +1027,7 @@ namespace MinecraftClient
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="inventoryID">Window ID of the requested inventory</param>
|
/// <param name="inventoryID">Window ID of the requested inventory</param>
|
||||||
/// <returns> Item Dictionary indexed by Slot ID (Check wiki.vg for slot ID)</returns>
|
/// <returns> Item Dictionary indexed by Slot ID (Check wiki.vg for slot ID)</returns>
|
||||||
public Container GetInventory(int inventoryID)
|
public Container? GetInventory(int inventoryID)
|
||||||
{
|
{
|
||||||
if (InvokeRequired)
|
if (InvokeRequired)
|
||||||
return InvokeOnMainThread(() => GetInventory(inventoryID));
|
return InvokeOnMainThread(() => GetInventory(inventoryID));
|
||||||
|
|
@ -1043,7 +1043,7 @@ namespace MinecraftClient
|
||||||
/// <returns> Item Dictionary indexed by Slot ID (Check wiki.vg for slot ID)</returns>
|
/// <returns> Item Dictionary indexed by Slot ID (Check wiki.vg for slot ID)</returns>
|
||||||
public Container GetPlayerInventory()
|
public Container GetPlayerInventory()
|
||||||
{
|
{
|
||||||
return GetInventory(0);
|
return GetInventory(0)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -1277,6 +1277,63 @@ namespace MinecraftClient
|
||||||
return InvokeOnMainThread(() => handler.SendUseItem(0, this.sequenceId));
|
return InvokeOnMainThread(() => handler.SendUseItem(0, this.sequenceId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Try to merge a slot
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="inventory">The container where the item is located</param>
|
||||||
|
/// <param name="item">Items to be processed</param>
|
||||||
|
/// <param name="slotId">The ID of the slot of the item to be processed</param>
|
||||||
|
/// <param name="curItem">The slot that was put down</param>
|
||||||
|
/// <param name="curId">The ID of the slot being put down</param>
|
||||||
|
/// <param name="changedSlots">Record changes</param>
|
||||||
|
/// <returns>Whether to fully merge</returns>
|
||||||
|
private static bool TryMergeSlot(Container inventory, Item item, int slotId, Item curItem, int curId, List<Tuple<short, Item?>> 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, Item?>((short)curId, curItem));
|
||||||
|
changedSlots.Add(new Tuple<short, Item?>((short)slotId, null));
|
||||||
|
|
||||||
|
inventory.Items.Remove(slotId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item.Count -= spaceLeft;
|
||||||
|
curItem.Count += spaceLeft;
|
||||||
|
|
||||||
|
changedSlots.Add(new Tuple<short, Item?>((short)curId, curItem));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Store items in a new slot
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="inventory">The container where the item is located</param>
|
||||||
|
/// <param name="item">Items to be processed</param>
|
||||||
|
/// <param name="slotId">The ID of the slot of the item to be processed</param>
|
||||||
|
/// <param name="newSlotId">ID of the new slot</param>
|
||||||
|
/// <param name="changedSlots">Record changes</param>
|
||||||
|
private static void StoreInNewSlot(Container inventory, Item item, int slotId, int newSlotId, List<Tuple<short, Item?>> changedSlots)
|
||||||
|
{
|
||||||
|
Item newItem = new(item.Type, item.Count, item.NBT);
|
||||||
|
inventory.Items[newSlotId] = newItem;
|
||||||
|
inventory.Items.Remove(slotId);
|
||||||
|
|
||||||
|
changedSlots.Add(new Tuple<short, Item?>((short)newSlotId, newItem));
|
||||||
|
changedSlots.Add(new Tuple<short, Item?>((short)slotId, null));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Click a slot in the specified window
|
/// Click a slot in the specified window
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -1286,15 +1343,15 @@ namespace MinecraftClient
|
||||||
if (InvokeRequired)
|
if (InvokeRequired)
|
||||||
return InvokeOnMainThread(() => DoWindowAction(windowId, slotId, action));
|
return InvokeOnMainThread(() => DoWindowAction(windowId, slotId, action));
|
||||||
|
|
||||||
Item item = null;
|
Item? item = null;
|
||||||
if (inventories.ContainsKey(windowId) && inventories[windowId].Items.ContainsKey(slotId))
|
if (inventories.ContainsKey(windowId) && inventories[windowId].Items.ContainsKey(slotId))
|
||||||
item = inventories[windowId].Items[slotId];
|
item = inventories[windowId].Items[slotId];
|
||||||
|
|
||||||
List<Tuple<short, Item>> changedSlots = new List<Tuple<short, Item>>(); // List<Slot ID, Changed Items>
|
List<Tuple<short, Item?>> changedSlots = new(); // List<Slot ID, Changed Items>
|
||||||
|
|
||||||
// Update our inventory base on action type
|
// Update our inventory base on action type
|
||||||
var inventory = GetInventory(windowId);
|
Container inventory = GetInventory(windowId)!;
|
||||||
var playerInventory = GetInventory(0);
|
Container playerInventory = GetInventory(0)!;
|
||||||
if (inventory != null)
|
if (inventory != null)
|
||||||
{
|
{
|
||||||
switch (action)
|
switch (action)
|
||||||
|
|
@ -1343,9 +1400,9 @@ namespace MinecraftClient
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inventory.Items.ContainsKey(slotId))
|
if (inventory.Items.ContainsKey(slotId))
|
||||||
changedSlots.Add(new Tuple<short, Item>((short)slotId, inventory.Items[slotId]));
|
changedSlots.Add(new Tuple<short, Item?>((short)slotId, inventory.Items[slotId]));
|
||||||
else
|
else
|
||||||
changedSlots.Add(new Tuple<short, Item>((short)slotId, null));
|
changedSlots.Add(new Tuple<short, Item?>((short)slotId, null));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -1359,7 +1416,7 @@ namespace MinecraftClient
|
||||||
playerInventory.Items[-1] = inventory.Items[slotId];
|
playerInventory.Items[-1] = inventory.Items[slotId];
|
||||||
inventory.Items.Remove(slotId);
|
inventory.Items.Remove(slotId);
|
||||||
|
|
||||||
changedSlots.Add(new Tuple<short, Item>((short)slotId, null));
|
changedSlots.Add(new Tuple<short, Item?>((short)slotId, null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -1439,30 +1496,454 @@ namespace MinecraftClient
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (inventory.Items.ContainsKey(slotId))
|
if (inventory.Items.ContainsKey(slotId))
|
||||||
changedSlots.Add(new Tuple<short, Item>((short)slotId, inventory.Items[slotId]));
|
changedSlots.Add(new Tuple<short, Item?>((short)slotId, inventory.Items[slotId]));
|
||||||
else
|
else
|
||||||
changedSlots.Add(new Tuple<short, Item>((short)slotId, null));
|
changedSlots.Add(new Tuple<short, Item?>((short)slotId, null));
|
||||||
break;
|
break;
|
||||||
case WindowActionType.ShiftClick:
|
case WindowActionType.ShiftClick:
|
||||||
if (slotId == 0) break;
|
if (slotId == 0) break;
|
||||||
if (inventory.Items.ContainsKey(slotId))
|
if (item != null)
|
||||||
{
|
{
|
||||||
/* Target slot have item */
|
/* Target slot have item */
|
||||||
|
|
||||||
|
bool lower2upper = false, upper2backpack = false, backpack2hotbar = false; // mutual exclusion
|
||||||
|
bool hotbarFirst = true; // Used when upper2backpack = true
|
||||||
int upperStartSlot = 9;
|
int upperStartSlot = 9;
|
||||||
int upperEndSlot = 35;
|
int upperEndSlot = 35;
|
||||||
|
int lowerStartSlot = 36;
|
||||||
|
|
||||||
switch (inventory.Type)
|
switch (inventory.Type)
|
||||||
{
|
{
|
||||||
case ContainerType.PlayerInventory:
|
case ContainerType.PlayerInventory:
|
||||||
|
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;
|
upperStartSlot = 9;
|
||||||
upperEndSlot = 35;
|
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;
|
break;
|
||||||
case ContainerType.Crafting:
|
case ContainerType.Crafting:
|
||||||
|
if (slotId >= 0 && slotId <= 9)
|
||||||
|
{
|
||||||
|
if (slotId >= 1 && slotId <= 9)
|
||||||
|
hotbarFirst = false;
|
||||||
|
upper2backpack = true;
|
||||||
|
lowerStartSlot = 10;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lower2upper = true;
|
||||||
upperStartSlot = 1;
|
upperStartSlot = 1;
|
||||||
upperEndSlot = 9;
|
upperEndSlot = 9;
|
||||||
|
}
|
||||||
break;
|
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
|
// TODO: Define more container type here
|
||||||
|
goto case ContainerType.Generic_9x3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cursor have item or not doesn't matter
|
// 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 no more same item , will put on the first empty slot (smaller slot id)
|
||||||
// If inventory full, item will not move
|
// If inventory full, item will not move
|
||||||
int itemCount = inventory.Items[slotId].Count;
|
int itemCount = inventory.Items[slotId].Count;
|
||||||
if (slotId <= upperEndSlot)
|
if (lower2upper)
|
||||||
{
|
{
|
||||||
// Clicked slot is on upper side inventory, put it to hotbar
|
int firstEmptySlot = -1;
|
||||||
// Now try to find same item and put on them
|
for (int i = upperStartSlot; i <= upperEndSlot; ++i)
|
||||||
var itemsClone = playerInventory.Items.ToDictionary(entry => entry.Key, entry => entry.Value);
|
|
||||||
foreach (KeyValuePair<int, Item> _item in itemsClone)
|
|
||||||
{
|
{
|
||||||
if (_item.Key <= upperEndSlot) continue;
|
if (inventory.Items.TryGetValue(i, out Item? curItem))
|
||||||
|
|
||||||
int maxCount = _item.Value.Type.StackCount();
|
|
||||||
if (_item.Value.Type == inventory.Items[slotId].Type && _item.Value.Count < maxCount)
|
|
||||||
{
|
{
|
||||||
// Put item on that stack
|
if (TryMergeSlot(inventory, item!, slotId, curItem, i, changedSlots))
|
||||||
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>((short)_item.Key, inventory.Items[_item.Key]));
|
|
||||||
changedSlots.Add(new Tuple<short, Item>((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>((short)_item.Key, inventory.Items[_item.Key]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (inventory.Items[slotId].Count > 0)
|
|
||||||
{
|
|
||||||
int[] emptySlots = inventory.GetEmpytSlots();
|
|
||||||
int emptySlot = -2;
|
|
||||||
foreach (int slot in emptySlots)
|
|
||||||
{
|
|
||||||
if (slot <= upperEndSlot) continue;
|
|
||||||
emptySlot = slot;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (emptySlot != -2)
|
else if (firstEmptySlot == -1)
|
||||||
{
|
firstEmptySlot = i;
|
||||||
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, Item>((short)emptySlot, inventory.Items[emptySlot]));
|
|
||||||
changedSlots.Add(new Tuple<short, Item>((short)slotId, null));
|
|
||||||
}
|
}
|
||||||
else if (inventory.Items[slotId].Count != itemCount)
|
if (item!.Count > 0)
|
||||||
{
|
{
|
||||||
changedSlots.Add(new Tuple<short, Item>((short)slotId, inventory.Items[slotId]));
|
if (firstEmptySlot != -1)
|
||||||
|
StoreInNewSlot(inventory, item, slotId, firstEmptySlot, changedSlots);
|
||||||
|
else if (item.Count != itemCount)
|
||||||
|
changedSlots.Add(new Tuple<short, Item?>((short)slotId, inventory.Items[slotId]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
else if (upper2backpack)
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// Clicked slot is on hotbar, put it to upper inventory
|
int hotbarEnd = lowerStartSlot + 4 * 9 - 1;
|
||||||
// Now try to find same item and put on them
|
if (hotbarFirst)
|
||||||
var itemsClone = playerInventory.Items.ToDictionary(entry => entry.Key, entry => entry.Value);
|
|
||||||
foreach (KeyValuePair<int, Item> _item in itemsClone)
|
|
||||||
{
|
{
|
||||||
if (_item.Key < upperStartSlot) continue;
|
int lastEmptySlot = -1;
|
||||||
if (_item.Key >= upperEndSlot) break;
|
for (int i = hotbarEnd; i >= lowerStartSlot; --i)
|
||||||
|
|
||||||
int maxCount = _item.Value.Type.StackCount();
|
|
||||||
if (_item.Value.Type == inventory.Items[slotId].Type && _item.Value.Count < maxCount)
|
|
||||||
{
|
{
|
||||||
// Put item on that stack
|
if (inventory.Items.TryGetValue(i, out Item? curItem))
|
||||||
int spaceLeft = maxCount - _item.Value.Count;
|
|
||||||
if (inventory.Items[slotId].Count <= spaceLeft)
|
|
||||||
{
|
{
|
||||||
// Can fit into the stack
|
if (TryMergeSlot(inventory, item!, slotId, curItem, i, changedSlots))
|
||||||
inventory.Items[_item.Key].Count += inventory.Items[slotId].Count;
|
|
||||||
inventory.Items.Remove(slotId);
|
|
||||||
|
|
||||||
changedSlots.Add(new Tuple<short, Item>((short)_item.Key, inventory.Items[_item.Key]));
|
|
||||||
changedSlots.Add(new Tuple<short, Item>((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>((short)_item.Key, inventory.Items[_item.Key]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (inventory.Items[slotId].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;
|
break;
|
||||||
}
|
}
|
||||||
if (emptySlot != -2)
|
else if (lastEmptySlot == -1)
|
||||||
|
lastEmptySlot = i;
|
||||||
|
}
|
||||||
|
if (item!.Count > 0)
|
||||||
{
|
{
|
||||||
var itemTmp = inventory.Items[slotId];
|
if (lastEmptySlot != -1)
|
||||||
inventory.Items[emptySlot] = new Item(itemTmp.Type, itemTmp.Count, itemTmp.NBT);
|
StoreInNewSlot(inventory, item, slotId, lastEmptySlot, changedSlots);
|
||||||
inventory.Items.Remove(slotId);
|
else if (item.Count != itemCount)
|
||||||
|
changedSlots.Add(new Tuple<short, Item?>((short)slotId, inventory.Items[slotId]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int firstEmptySlot = -1;
|
||||||
|
for (int i = lowerStartSlot; i <= hotbarEnd; ++i)
|
||||||
|
{
|
||||||
|
if (inventory.Items.TryGetValue(i, out Item? curItem))
|
||||||
|
{
|
||||||
|
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, Item?>((short)slotId, inventory.Items[slotId]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (backpack2hotbar)
|
||||||
|
{
|
||||||
|
int hotbarEnd = lowerStartSlot + 1 * 9 - 1;
|
||||||
|
|
||||||
changedSlots.Add(new Tuple<short, Item>((short)emptySlot, inventory.Items[emptySlot]));
|
int firstEmptySlot = -1;
|
||||||
changedSlots.Add(new Tuple<short, Item>((short)slotId, null));
|
for (int i = lowerStartSlot; i <= hotbarEnd; ++i)
|
||||||
}
|
|
||||||
else if (inventory.Items[slotId].Count != itemCount)
|
|
||||||
{
|
{
|
||||||
changedSlots.Add(new Tuple<short, Item>((short)slotId, inventory.Items[slotId]));
|
if (inventory.Items.TryGetValue(i, out Item? curItem))
|
||||||
|
{
|
||||||
|
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, Item?>((short)slotId, inventory.Items[slotId]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1592,19 +2047,19 @@ namespace MinecraftClient
|
||||||
if (inventory.Items.ContainsKey(slotId))
|
if (inventory.Items.ContainsKey(slotId))
|
||||||
{
|
{
|
||||||
inventory.Items[slotId].Count--;
|
inventory.Items[slotId].Count--;
|
||||||
changedSlots.Add(new Tuple<short, Item>((short)slotId, inventory.Items[slotId]));
|
changedSlots.Add(new Tuple<short, Item?>((short)slotId, inventory.Items[slotId]));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inventory.Items[slotId].Count <= 0)
|
if (inventory.Items[slotId].Count <= 0)
|
||||||
{
|
{
|
||||||
inventory.Items.Remove(slotId);
|
inventory.Items.Remove(slotId);
|
||||||
changedSlots.Add(new Tuple<short, Item>((short)slotId, null));
|
changedSlots.Add(new Tuple<short, Item?>((short)slotId, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case WindowActionType.DropItemStack:
|
case WindowActionType.DropItemStack:
|
||||||
inventory.Items.Remove(slotId);
|
inventory.Items.Remove(slotId);
|
||||||
changedSlots.Add(new Tuple<short, Item>((short)slotId, null));
|
changedSlots.Add(new Tuple<short, Item?>((short)slotId, null));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -741,7 +741,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="nbt">Dictionary to encode as Nbt</param>
|
/// <param name="nbt">Dictionary to encode as Nbt</param>
|
||||||
/// <returns>Byte array for this NBT tag</returns>
|
/// <returns>Byte array for this NBT tag</returns>
|
||||||
public byte[] GetNbt(Dictionary<string, object> nbt)
|
public byte[] GetNbt(Dictionary<string, object>? nbt)
|
||||||
{
|
{
|
||||||
return GetNbt(nbt, true);
|
return GetNbt(nbt, true);
|
||||||
}
|
}
|
||||||
|
|
@ -752,7 +752,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// <param name="nbt">Dictionary to encode as Nbt</param>
|
/// <param name="nbt">Dictionary to encode as Nbt</param>
|
||||||
/// <param name="root">TRUE if starting a new NBT tag, FALSE if processing a nested NBT tag</param>
|
/// <param name="root">TRUE if starting a new NBT tag, FALSE if processing a nested NBT tag</param>
|
||||||
/// <returns>Byte array for this NBT tag</returns>
|
/// <returns>Byte array for this NBT tag</returns>
|
||||||
private byte[] GetNbt(Dictionary<string, object> nbt, bool root)
|
private byte[] GetNbt(Dictionary<string, object>? nbt, bool root)
|
||||||
{
|
{
|
||||||
if (nbt == null || nbt.Count == 0)
|
if (nbt == null || nbt.Count == 0)
|
||||||
return new byte[] { 0 }; // TAG_End
|
return new byte[] { 0 }; // TAG_End
|
||||||
|
|
@ -1065,9 +1065,9 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// <param name="item">Item</param>
|
/// <param name="item">Item</param>
|
||||||
/// <param name="itemPalette">Item Palette</param>
|
/// <param name="itemPalette">Item Palette</param>
|
||||||
/// <returns>Item slot representation</returns>
|
/// <returns>Item slot representation</returns>
|
||||||
public byte[] GetItemSlot(Item item, ItemPalette itemPalette)
|
public byte[] GetItemSlot(Item? item, ItemPalette itemPalette)
|
||||||
{
|
{
|
||||||
List<byte> slotData = new List<byte>();
|
List<byte> slotData = new();
|
||||||
if (protocolversion > Protocol18Handler.MC_1_13_Version)
|
if (protocolversion > Protocol18Handler.MC_1_13_Version)
|
||||||
{
|
{
|
||||||
// MC 1.13 and greater
|
// MC 1.13 and greater
|
||||||
|
|
|
||||||
|
|
@ -752,7 +752,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return false; //Currently not implemented
|
return false; //Currently not implemented
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool SendWindowAction(int windowId, int slotId, WindowActionType action, Item item, List<Tuple<short, Item>> changedSlots, int stateId)
|
public bool SendWindowAction(int windowId, int slotId, WindowActionType action, Item? item, List<Tuple<short, Item?>> changedSlots, int stateId)
|
||||||
{
|
{
|
||||||
return false; //Currently not implemented
|
return false; //Currently not implemented
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2634,7 +2634,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
catch (ObjectDisposedException) { return false; }
|
catch (ObjectDisposedException) { return false; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool SendWindowAction(int windowId, int slotId, WindowActionType action, Item item, List<Tuple<short, Item>> changedSlots, int stateId)
|
public bool SendWindowAction(int windowId, int slotId, WindowActionType action, Item? item, List<Tuple<short, Item?>> changedSlots, int stateId)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -2669,7 +2669,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
case WindowActionType.AddDragMiddle: button = 9; mode = 5; item = new Item(ItemType.Null, 0, null); break;
|
case WindowActionType.AddDragMiddle: button = 9; mode = 5; item = new Item(ItemType.Null, 0, null); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<byte> packet = new List<byte>();
|
List<byte> packet = new();
|
||||||
packet.Add((byte)windowId); // Window ID
|
packet.Add((byte)windowId); // Window ID
|
||||||
|
|
||||||
// 1.18+
|
// 1.18+
|
||||||
|
|
|
||||||
|
|
@ -169,7 +169,7 @@ namespace MinecraftClient.Protocol
|
||||||
/// <param name="changedSlots">Slots that have been changed in this event: List<SlotID, Changed Items> </param>
|
/// <param name="changedSlots">Slots that have been changed in this event: List<SlotID, Changed Items> </param>
|
||||||
/// <param name="stateId">Inventory's stateId</param>
|
/// <param name="stateId">Inventory's stateId</param>
|
||||||
/// <returns>True if packet was successfully sent</returns>
|
/// <returns>True if packet was successfully sent</returns>
|
||||||
bool SendWindowAction(int windowId, int slotId, WindowActionType action, Item item, List<Tuple<short, Item>> changedSlots, int stateId);
|
bool SendWindowAction(int windowId, int slotId, WindowActionType action, Item? item, List<Tuple<short, Item?>> changedSlots, int stateId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Request Creative Mode item creation into regular/survival Player Inventory
|
/// Request Creative Mode item creation into regular/survival Player Inventory
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ namespace MinecraftClient.Protocol
|
||||||
bool GetNetworkPacketCaptureEnabled();
|
bool GetNetworkPacketCaptureEnabled();
|
||||||
void SetNetworkPacketCaptureEnabled(bool enabled);
|
void SetNetworkPacketCaptureEnabled(bool enabled);
|
||||||
int GetProtocolVersion();
|
int GetProtocolVersion();
|
||||||
Container GetInventory(int inventoryID);
|
Container? GetInventory(int inventoryID);
|
||||||
ILogger GetLogger();
|
ILogger GetLogger();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -296,6 +296,7 @@ cmd.inventory.left=Left
|
||||||
cmd.inventory.right=Right
|
cmd.inventory.right=Right
|
||||||
cmd.inventory.middle=Middle
|
cmd.inventory.middle=Middle
|
||||||
cmd.inventory.clicking={0} clicking slot {1} in window #{2}
|
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.no_item=No item in slot #{0}
|
||||||
cmd.inventory.drop=Dropped 1 item from slot #{0}
|
cmd.inventory.drop=Dropped 1 item from slot #{0}
|
||||||
cmd.inventory.drop_stack=Dropped whole item stack from slot #{0}
|
cmd.inventory.drop_stack=Dropped whole item stack from slot #{0}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue