package net.glowstone.inventory;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.event.inventory.InventoryAction;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.event.inventory.InventoryType.SlotType;
import org.bukkit.inventory.ItemStack;
/**
* The complicated logic for determining how window click messages are
* interpreted.
*/
public final class WindowClickLogic {
private WindowClickLogic() {
}
/**
* Determine the ClickType of a window click message based on the raw
* mode, button, and slot values if possible.
* @param mode The raw mode number.
* @param button The raw button number.
* @param slot The raw slot number.
* @return The ClickType of the window click, or UNKNOWN.
*/
public static ClickType getClickType(final int mode, final int button, final int slot) {
// mode ; button ; slot (* = -999)
// m b s
// 0 0 lmb
// 1 rmb
// 1 0 shift+lmb
// 1 shift+rmb (same as 1/0)
// 2 * number key b+1
// 3 2 middle click / duplicate (creative only)
// 4 0 drop
// 4 1 ctrl + drop
// 4 0 * lmb with no item (no-op)
// 4 1 * rmb with no item (no-op)
// 5 0 * start left drag
// 5 1 add slot left drag
// 5 2 * end left drag
// 5 4 * start right drag
// 5 5 add slot right drag
// 5 6 * end right drag
// 6 0 double click
switch (mode) {
case 0: // normal click
if (button == 0) {
return slot == -1 ? ClickType.WINDOW_BORDER_LEFT : ClickType.LEFT;
} else if (button == 1) {
return slot == -1 ? ClickType.WINDOW_BORDER_RIGHT : ClickType.RIGHT;
}
break;
case 1: // shift click
if (button == 0) {
return ClickType.SHIFT_LEFT;
} else if (button == 1) {
return ClickType.SHIFT_RIGHT;
}
break;
case 2: // number key
return ClickType.NUMBER_KEY;
case 3: // middle click
if (button == 2) {
return ClickType.MIDDLE;
}
break;
case 4: // drop or ctrl+drop
if (button == 0) {
return ClickType.DROP;
} else if (button == 1) {
return ClickType.CONTROL_DROP;
}
break;
case 5: // drag
break;
case 6:
return ClickType.DOUBLE_CLICK;
}
return ClickType.UNKNOWN;
}
/**
* Determine the InventoryAction to be performed for a window click based
* on the click type, slot type, and items involved.
* @param clickType The click type.
* @param slot The slot clicked.
* @param cursor The item on the cursor.
* @param slotItem The item in the slot.
* @return The InventoryAction to perform, or UNKNOWN.
*/
public static InventoryAction getAction(ClickType clickType, InventoryType.SlotType slot, ItemStack cursor, ItemStack slotItem) {
final boolean outside = (slot == InventoryType.SlotType.OUTSIDE);
switch (clickType) {
case LEFT:
// "SWAP_WITH_CURSOR", "PLACE_ONE", "DROP_ALL_CURSOR", "PLACE_ALL", "PLACE_SOME", "NOTHING", "PICKUP_ALL"
if (cursor == null) {
if (outside || slotItem == null) {
return InventoryAction.NOTHING;
}
return InventoryAction.PICKUP_ALL;
}
if (outside) {
return InventoryAction.DROP_ALL_CURSOR;
}
if (slot == SlotType.ARMOR) {
return InventoryAction.PLACE_ONE;
}
if (slotItem == null) {
return InventoryAction.PLACE_ALL;
}
if (slotItem.isSimilar(cursor)) {
int transfer = Math.min(cursor.getAmount(), slotItem.getType().getMaxStackSize() - slotItem.getAmount());
if (transfer == 0) {
return InventoryAction.NOTHING;
} else if (transfer == 1) {
return InventoryAction.PLACE_ONE;
} else if (transfer == cursor.getAmount()) {
return InventoryAction.PLACE_ALL;
} else {
return InventoryAction.PLACE_SOME;
}
}
return InventoryAction.SWAP_WITH_CURSOR;
case RIGHT:
// "NOTHING", "PLACE_ONE", "PICKUP_HALF", "DROP_ONE_CURSOR", "SWAP_WITH_CURSOR"
if (cursor == null) {
if (outside || slotItem == null) {
return InventoryAction.NOTHING;
}
return InventoryAction.PICKUP_HALF;
}
if (outside) {
return InventoryAction.DROP_ONE_CURSOR;
}
if (slotItem == null) {
return InventoryAction.PLACE_ONE;
}
if (cursor.isSimilar(slotItem)) {
if (slotItem.getAmount() + 1 <= slotItem.getType().getMaxStackSize()) {
return InventoryAction.PLACE_ONE;
}
return InventoryAction.NOTHING;
}
return InventoryAction.SWAP_WITH_CURSOR;
case SHIFT_LEFT:
case SHIFT_RIGHT:
if (slotItem != null) {
return InventoryAction.MOVE_TO_OTHER_INVENTORY;
} else {
return InventoryAction.NOTHING;
}
case WINDOW_BORDER_LEFT:
case WINDOW_BORDER_RIGHT:
return InventoryAction.NOTHING;
case MIDDLE:
// not supported yet
if (cursor == null) {
return InventoryAction.CLONE_STACK;
} else {
return InventoryAction.NOTHING;
}
case NUMBER_KEY:
// {"NUMBER_KEY", "NOTHING", "HOTBAR_SWAP"},
// note: should treat as NOTHING if there is no item in the hotbar
return InventoryAction.HOTBAR_SWAP;
case DOUBLE_CLICK:
if (cursor != null) {
return InventoryAction.COLLECT_TO_CURSOR;
} else {
return InventoryAction.NOTHING;
}
case DROP:
// {"DROP", "DROP_ONE_SLOT"},
return InventoryAction.DROP_ONE_SLOT;
case CONTROL_DROP:
return InventoryAction.DROP_ALL_SLOT;
case CREATIVE:
// not supported yet
return InventoryAction.UNKNOWN;
case UNKNOWN:
default:
return InventoryAction.UNKNOWN;
}
}
/**
* Check if a given InventoryAction involves placing items into the slot.
* @param action The InventoryAction.
* @return True if the cursor is to be added to the slot.
*/
public static boolean isPlaceAction(InventoryAction action) {
switch (action) {
case SWAP_WITH_CURSOR:
case PLACE_ONE:
case PLACE_ALL:
case PLACE_SOME:
return true;
}
return false;
}
/**
* Check if a given InventoryAction involves taking items from the slot.
* @param action The InventoryAction.
* @return True if the slot is to be added to the cursor.
*/
public static boolean isPickupAction(InventoryAction action) {
switch (action) {
case PICKUP_ALL:
case PICKUP_HALF:
case PICKUP_ONE:
case PICKUP_SOME:
return true;
}
return false;
}
}