Search in sources :

Example 1 with DragTracker

use of net.glowstone.inventory.DragTracker in project Glowstone by GlowstoneMC.

the class WindowClickHandler method process.

private boolean process(GlowPlayer player, WindowClickMessage message) {
    int viewSlot = message.getSlot();
    InventoryView view = player.getOpenInventory();
    GlowInventory top = (GlowInventory) view.getTopInventory();
    GlowInventory bottom = (GlowInventory) view.getBottomInventory();
    ItemStack slotItem = InventoryUtil.itemOrEmpty(view.getItem(viewSlot));
    ItemStack cursor = player.getItemOnCursor();
    // check that the player has a correct view of the item
    if (!Objects.equals(message.getItem(), slotItem) && (message.getMode() == 0 || message.getMode() == 1)) {
        // in mode 3 (get) and 4 (drop), client does not send item in slot under cursor
        if (message.getMode() == 0 || !InventoryUtil.isEmpty(message.getItem())) {
            // recipe slot is not synced by design
            if (view.getTopInventory().getType() != InventoryType.CRAFTING || viewSlot >= view.getTopInventory().getSize() || ((GlowInventory) view.getTopInventory()).getSlot(viewSlot).getType() != SlotType.RESULT) {
                player.sendItemChange(viewSlot, slotItem);
                return false;
            }
        }
    }
    // determine inventory and slot clicked, used in some places
    // todo: attempt to allow for users to implement their own inventory?
    // CraftBukkit does not allow this but it may be worth the trouble for
    // the extensibility.
    GlowInventory inv;
    if (viewSlot < top.getSize()) {
        inv = top;
    } else {
        inv = bottom;
    }
    int invSlot = view.convertSlot(viewSlot);
    if (invSlot == -1 || viewSlot == -1) {
        return true;
    }
    SlotType slotType = inv.getSlotType(invSlot);
    // handle dragging
    if (message.getMode() == 5) {
        // 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
        DragTracker drag = player.getInventory().getDragTracker();
        boolean right = message.getButton() >= 4;
        switch(message.getButton()) {
            // start left drag
            case 0:
            case // start right drag
            4:
                return drag.start(right);
            // add slot left
            case 1:
            case // add slot right
            5:
                return drag.addSlot(right, message.getSlot());
            // end left drag
            case 2:
            case // end right drag
            6:
                List<Integer> slots = drag.finish(right);
                if (slots == null || InventoryUtil.isEmpty(cursor)) {
                    return false;
                }
                ItemStack newCursor = cursor.clone();
                Map<Integer, ItemStack> newSlots = new HashMap<>();
                int perSlot = right ? 1 : cursor.getAmount() / slots.size();
                for (int dragSlot : slots) {
                    ItemStack oldItem = view.getItem(dragSlot);
                    if (InventoryUtil.isEmpty(oldItem) || cursor.isSimilar(oldItem)) {
                        Inventory dragInv = dragSlot < top.getSize() ? top : bottom;
                        int oldItemAmount = InventoryUtil.itemOrEmpty(oldItem).getAmount();
                        int transfer = Math.min(Math.min(perSlot, cursor.getAmount()), maxStack(dragInv, cursor.getType()) - oldItemAmount);
                        ItemStack newItem = combine(oldItem, cursor, transfer);
                        newSlots.put(dragSlot, newItem);
                        newCursor = amountOrEmpty(newCursor, newCursor.getAmount() - transfer);
                        if (InventoryUtil.isEmpty(newCursor)) {
                            break;
                        }
                    }
                }
                InventoryDragEvent event = new InventoryDragEvent(view, newCursor, cursor, right, newSlots);
                EventFactory.getInstance().callEvent(event);
                if (event.isCancelled()) {
                    return false;
                }
                for (Entry<Integer, ItemStack> entry : newSlots.entrySet()) {
                    view.setItem(entry.getKey(), entry.getValue());
                }
                player.setItemOnCursor(newCursor);
                return true;
            default:
                return false;
        }
    }
    // determine what action will be taken and fire event
    ClickType clickType = WindowClickLogic.getClickType(message.getMode(), message.getButton(), viewSlot);
    InventoryAction action = WindowClickLogic.getAction(clickType, slotType, cursor, slotItem);
    if (clickType == ClickType.UNKNOWN || action == InventoryAction.UNKNOWN) {
        // show a warning for unknown click type
        GlowServer.logger.warning(player.getName() + ": mystery window click " + clickType + "/" + action + ": " + message);
    }
    // deny CLONE_STACK for non-creative mode players
    if (action == InventoryAction.CLONE_STACK && player.getGameMode() != GameMode.CREATIVE) {
        action = InventoryAction.NOTHING;
    }
    // determine whether NO_AI, HOTBAR_MOVE_AND_READD or HOTBAR_SWAP should be executed
    if (clickType == ClickType.NUMBER_KEY) {
        ItemStack destItem = bottom.getItem(message.getButton());
        if (InventoryUtil.isEmpty(slotItem)) {
            if (InventoryUtil.isEmpty(destItem) || !inv.itemPlaceAllowed(invSlot, destItem)) {
                // both items are empty, do nothing
                // or, current item is empty and destItem cannot be moved into current slot
                action = InventoryAction.NOTHING;
            }
        } else if (inv != bottom || !inv.itemPlaceAllowed(invSlot, destItem)) {
            // target and source inventory are different or destItem cannot be placed in
            // current slot
            action = InventoryAction.HOTBAR_MOVE_AND_READD;
        }
    }
    if (WindowClickLogic.isPlaceAction(action)) {
        // check whether item can be dropped into the clicked slot
        if (!inv.itemPlaceAllowed(invSlot, cursor)) {
            // placement not allowed
            if (!InventoryUtil.isEmpty(slotItem) && slotItem.isSimilar(cursor)) {
                // item in slot is the same as item on cursor
                if (cursor.getAmount() + 1 == cursor.getMaxStackSize()) {
                    // There is still space under the cursor for one item
                    action = InventoryAction.PICKUP_ONE;
                } else if (cursor.getAmount() < cursor.getMaxStackSize()) {
                    // There is still some space under the cursor
                    action = InventoryAction.PICKUP_SOME;
                }
            } else {
                action = InventoryAction.NOTHING;
            }
        }
    }
    InventoryClickEvent event = null;
    if (top == inv && top instanceof GlowCraftingInventory && top.getSlotType(invSlot) == SlotType.RESULT) {
        // Clicked on output slot of crafting inventory
        if (InventoryUtil.isEmpty(slotItem)) {
            // No crafting recipe result, don't do anything
            action = InventoryAction.NOTHING;
        }
        int cursorAmount = InventoryUtil.itemOrEmpty(cursor).getAmount();
        if (message.getMode() == 0 && slotItem.isSimilar(cursor)) {
            if (!InventoryUtil.isEmpty(slotItem) && cursorAmount + slotItem.getAmount() <= slotItem.getMaxStackSize()) {
                // if the player can take the whole result
                if (WindowClickLogic.isPickupAction(action) || WindowClickLogic.isPlaceAction(action)) {
                    // always take the whole crafting result out of the crafting inventories
                    action = InventoryAction.PICKUP_ALL;
                } else if (action == InventoryAction.DROP_ONE_SLOT) {
                    // always drop the whole stack, not just single items
                    action = InventoryAction.DROP_ALL_SLOT;
                }
            } else {
                // if their cursor is full, do nothing
                action = InventoryAction.NOTHING;
            }
        }
        // this ignores whether the crafting process actually happens (full inventory, etc.)
        if (action != InventoryAction.NOTHING) {
            Recipe recipe = ((CraftingInventory) inv).getRecipe();
            if (clickType == ClickType.NUMBER_KEY) {
                event = new CraftItemEvent(recipe, view, slotType, viewSlot, clickType, action, message.getButton());
            } else {
                event = new CraftItemEvent(recipe, view, slotType, viewSlot, clickType, action);
            }
        }
    }
    if (event == null) {
        if (clickType == ClickType.NUMBER_KEY) {
            event = new InventoryClickEvent(view, slotType, viewSlot, clickType, action, message.getButton());
        } else {
            event = new InventoryClickEvent(view, slotType, viewSlot, clickType, action);
        }
    }
    EventFactory.getInstance().callEvent(event);
    if (event.isCancelled()) {
        int slot = event.getSlot();
        player.getSession().send(new SetWindowSlotMessage(player.getOpenWindowId(), slot, event.getInventory().getItem(slot)));
        player.getSession().send(new SetWindowSlotMessage(-1, -1, player.getItemOnCursor()));
        return true;
    }
    boolean handled = true;
    switch(action) {
        case NOTHING:
            break;
        case UNKNOWN:
            // any action the client tried to take should be denied
            return false;
        // PICKUP_*
        case PICKUP_ALL:
            view.setItem(viewSlot, InventoryUtil.createEmptyStack());
            int cursorAmount = InventoryUtil.itemOrEmpty(cursor).getAmount();
            player.setItemOnCursor(amountOrEmpty(slotItem, cursorAmount + slotItem.getAmount()));
            break;
        case PICKUP_HALF:
            // pick up half (favor picking up)
            int keepAmount = slotItem.getAmount() / 2;
            ItemStack newCursor = slotItem.clone();
            newCursor.setAmount(slotItem.getAmount() - keepAmount);
            inv.setItem(invSlot, amountOrEmpty(slotItem, keepAmount));
            player.setItemOnCursor(newCursor);
            break;
        case PICKUP_SOME:
            // pick up as many items as possible
            int pickUp = Math.min(cursor.getMaxStackSize() - cursor.getAmount(), slotItem.getAmount());
            view.setItem(viewSlot, amountOrEmpty(slotItem, slotItem.getAmount() - pickUp));
            player.setItemOnCursor(amountOrEmpty(cursor, cursor.getAmount() + pickUp));
            break;
        case PICKUP_ONE:
            view.setItem(invSlot, amountOrEmpty(slotItem, slotItem.getAmount() - 1));
            player.setItemOnCursor(amountOrEmpty(cursor, cursor.getAmount() + 1));
            break;
        // PLACE_*
        case PLACE_ALL:
            view.setItem(viewSlot, combine(slotItem, cursor, cursor.getAmount()));
            player.setItemOnCursor(InventoryUtil.createEmptyStack());
            break;
        case PLACE_SOME:
            {
                // slotItem *should* never be empty in this situation?
                int transfer = Math.min(cursor.getAmount(), maxStack(inv, slotItem.getType()) - slotItem.getAmount());
                view.setItem(viewSlot, combine(slotItem, cursor, transfer));
                player.setItemOnCursor(amountOrEmpty(cursor, cursor.getAmount() - transfer));
                break;
            }
        case PLACE_ONE:
            view.setItem(viewSlot, combine(slotItem, cursor, 1));
            player.setItemOnCursor(amountOrEmpty(cursor, cursor.getAmount() - 1));
            break;
        case SWAP_WITH_CURSOR:
            view.setItem(viewSlot, cursor);
            player.setItemOnCursor(slotItem);
            break;
        // DROP_*
        case DROP_ALL_CURSOR:
            if (!InventoryUtil.isEmpty(cursor)) {
                drop(player, cursor);
                player.setItemOnCursor(InventoryUtil.createEmptyStack());
            }
            break;
        case DROP_ONE_CURSOR:
            if (!InventoryUtil.isEmpty(cursor)) {
                drop(player, amountOrEmpty(cursor.clone(), 1));
                player.setItemOnCursor(amountOrEmpty(cursor, cursor.getAmount() - 1));
            }
            break;
        case DROP_ALL_SLOT:
            if (!InventoryUtil.isEmpty(slotItem)) {
                drop(player, slotItem);
                view.setItem(viewSlot, InventoryUtil.createEmptyStack());
            }
            break;
        case DROP_ONE_SLOT:
            if (InventoryUtil.isEmpty(slotItem)) {
                drop(player, amountOrEmpty(slotItem.clone(), 1));
                view.setItem(viewSlot, amountOrEmpty(slotItem, slotItem.getAmount() - 1));
            }
            break;
        // shift-click
        case MOVE_TO_OTHER_INVENTORY:
            if (!InventoryUtil.isEmpty(slotItem)) {
                inv.handleShiftClick(player, view, viewSlot, slotItem);
            }
            break;
        case HOTBAR_MOVE_AND_READD:
        case HOTBAR_SWAP:
            GlowPlayerInventory playerInv = player.getInventory();
            int hotbarSlot = message.getButton();
            ItemStack destItem = playerInv.getItem(hotbarSlot);
            if (InventoryUtil.isEmpty(slotItem)) {
                // nothing in current slot
                if (InventoryUtil.isEmpty(destItem)) {
                    // no action
                    return false;
                } else {
                    // do nothing if current slots does not accept the item
                    if (action == InventoryAction.HOTBAR_SWAP) {
                        inv.setItem(invSlot, destItem);
                        playerInv.setItem(hotbarSlot, InventoryUtil.createEmptyStack());
                    }
                    return true;
                }
            } else {
                if (InventoryUtil.isEmpty(destItem)) {
                    // move from current slot to hotbar
                    playerInv.setItem(hotbarSlot, slotItem);
                    inv.setItem(invSlot, InventoryUtil.createEmptyStack());
                    return true;
                } else {
                    // both are non-empty, swap them
                    playerInv.setItem(hotbarSlot, slotItem);
                    if (action == InventoryAction.HOTBAR_SWAP) {
                        inv.setItem(invSlot, destItem);
                    } else {
                        inv.setItem(invSlot, InventoryUtil.createEmptyStack());
                        playerInv.addItem(destItem);
                    }
                    return true;
                }
            }
        case CLONE_STACK:
            // only in creative and with no item on cursor handled earlier
            // copy and maximize item
            ItemStack stack = slotItem.clone();
            stack.setAmount(stack.getType().getMaxStackSize());
            player.setItemOnCursor(stack);
            break;
        case COLLECT_TO_CURSOR:
            if (InventoryUtil.isEmpty(cursor)) {
                return false;
            }
            int slotCount = view.countSlots();
            for (int i = 0; i < slotCount && cursor.getAmount() < maxStack(inv, cursor.getType()); ++i) {
                ItemStack item = view.getItem(i);
                SlotType type = (i < top.getSize() ? top : bottom).getSlotType(view.convertSlot(i));
                if (InventoryUtil.isEmpty(item) || !cursor.isSimilar(item) || type == SlotType.RESULT) {
                    continue;
                }
                int transfer = Math.min(item.getAmount(), maxStack(inv, cursor.getType()) - cursor.getAmount());
                cursor.setAmount(cursor.getAmount() + transfer);
                view.setItem(i, amountOrEmpty(item, item.getAmount() - transfer));
            }
            break;
        default:
            handled = false;
    }
    if (handled && top == inv && top instanceof GlowCraftingInventory && top.getSlotType(invSlot) == SlotType.RESULT && action != MOVE_TO_OTHER_INVENTORY && action != NOTHING) {
        // If we are crafting (but not using shift click because no more items can be crafted
        // for the given pattern. If a new item can be crafted with another pattern, a new
        // click is required).
        final GlowCraftingInventory glowCraftingInventory = (GlowCraftingInventory) top;
        glowCraftingInventory.craft();
        // Notify the player the result slot changed
        player.sendItemChange(viewSlot, glowCraftingInventory.getResult());
    }
    if (!handled) {
        GlowServer.logger.warning(player.getName() + ": unhandled click action " + action + " for " + message);
    }
    return handled;
}
Also used : CraftingInventory(org.bukkit.inventory.CraftingInventory) GlowCraftingInventory(net.glowstone.inventory.GlowCraftingInventory) HashMap(java.util.HashMap) Recipe(org.bukkit.inventory.Recipe) SlotType(org.bukkit.event.inventory.InventoryType.SlotType) InventoryAction(org.bukkit.event.inventory.InventoryAction) DragTracker(net.glowstone.inventory.DragTracker) InventoryDragEvent(org.bukkit.event.inventory.InventoryDragEvent) InventoryClickEvent(org.bukkit.event.inventory.InventoryClickEvent) GlowCraftingInventory(net.glowstone.inventory.GlowCraftingInventory) InventoryView(org.bukkit.inventory.InventoryView) ClickType(org.bukkit.event.inventory.ClickType) SetWindowSlotMessage(net.glowstone.net.message.play.inv.SetWindowSlotMessage) GlowInventory(net.glowstone.inventory.GlowInventory) CraftItemEvent(org.bukkit.event.inventory.CraftItemEvent) ItemStack(org.bukkit.inventory.ItemStack) CraftingInventory(org.bukkit.inventory.CraftingInventory) Inventory(org.bukkit.inventory.Inventory) GlowInventory(net.glowstone.inventory.GlowInventory) GlowPlayerInventory(net.glowstone.inventory.GlowPlayerInventory) GlowCraftingInventory(net.glowstone.inventory.GlowCraftingInventory) GlowPlayerInventory(net.glowstone.inventory.GlowPlayerInventory)

Aggregations

HashMap (java.util.HashMap)1 DragTracker (net.glowstone.inventory.DragTracker)1 GlowCraftingInventory (net.glowstone.inventory.GlowCraftingInventory)1 GlowInventory (net.glowstone.inventory.GlowInventory)1 GlowPlayerInventory (net.glowstone.inventory.GlowPlayerInventory)1 SetWindowSlotMessage (net.glowstone.net.message.play.inv.SetWindowSlotMessage)1 ClickType (org.bukkit.event.inventory.ClickType)1 CraftItemEvent (org.bukkit.event.inventory.CraftItemEvent)1 InventoryAction (org.bukkit.event.inventory.InventoryAction)1 InventoryClickEvent (org.bukkit.event.inventory.InventoryClickEvent)1 InventoryDragEvent (org.bukkit.event.inventory.InventoryDragEvent)1 SlotType (org.bukkit.event.inventory.InventoryType.SlotType)1 CraftingInventory (org.bukkit.inventory.CraftingInventory)1 Inventory (org.bukkit.inventory.Inventory)1 InventoryView (org.bukkit.inventory.InventoryView)1 ItemStack (org.bukkit.inventory.ItemStack)1 Recipe (org.bukkit.inventory.Recipe)1