diff --git a/src/main/java/net/citizensnpcs/api/gui/InventoryMenu.java b/src/main/java/net/citizensnpcs/api/gui/InventoryMenu.java index 39f8174e..fdf85461 100644 --- a/src/main/java/net/citizensnpcs/api/gui/InventoryMenu.java +++ b/src/main/java/net/citizensnpcs/api/gui/InventoryMenu.java @@ -9,6 +9,7 @@ import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collection; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Queue; @@ -62,6 +63,7 @@ */ public class InventoryMenu implements Listener, Runnable { private final List closeCallbacks = Lists.newArrayList(); + private boolean manualClose; private PageContext page; private int pickupAmount = -1; private final Queue stack = Queues.newArrayDeque(); @@ -93,22 +95,29 @@ private void addCloseCallback(Runnable run) { */ public void close() { HandlerList.unregisterAll(this); + manualClose = true; for (InventoryView view : views) { page.page.onClose(view.getPlayer()); view.close(); } + views.clear(); + manualClose = false; } /** * Closes the GUI for just a specific Player. */ public void close(Player player) { - for (InventoryView view : views) { + Iterator itr = views.iterator(); + manualClose = true; + while (itr.hasNext()) { + InventoryView view = itr.next(); if (view.getPlayer() == player) { - page.page.onClose(player); view.close(); + itr.remove(); } } + manualClose = false; } private InventoryMenuSlot createSlot(int pos, MenuSlot slotInfo) { @@ -186,19 +195,55 @@ private int getInventorySize(InventoryType type, int[] dim) { } } - public void transitionBack() { - if (page == null) - return; - Map data = page.ctx.data(); - page = stack.poll(); - if (page != null) { - page.ctx.data().putAll(data); - } - data.clear(); - transitionViewersToInventory(page == null ? null : page.ctx.getInventory()); - if (page == null) { - for (Runnable callback : closeCallbacks) { - callback.run(); + private void handleShiftClick(InventoryClickEvent event, Inventory dest, boolean toNPC) { + int amount = event.getCurrentItem().getAmount(); + ItemStack merging = new ItemStack(event.getCurrentItem().clone()); + ItemStack[] contents = dest.getContents(); + for (int i = 0; i < contents.length; i++) { + if (contents[i] == null || contents[i].getType() == Material.AIR) { + merging.setAmount(amount); + if (toNPC) { + event.getView().setCursor(merging); + } + InventoryClickEvent e = new InventoryClickEvent(event.getView(), event.getSlotType(), + toNPC ? i : event.getRawSlot(), event.getClick(), + toNPC ? InventoryAction.PLACE_ALL : InventoryAction.PICKUP_ALL); + onInventoryClick(e); + if (toNPC) { + event.getView().setCursor(null); + } + if (!e.isCancelled() && e.getResult() != Result.DENY) { + dest.setItem(i, merging); + event.setCurrentItem(null); + break; + } + } else if (contents[i].getType() == event.getCurrentItem().getType()) { + ItemStack stack = contents[i].clone(); + merging.setAmount(Math.min(amount, stack.getType().getMaxStackSize() - stack.getAmount())); + InventoryAction action; + if (toNPC) { + event.getView().setCursor(merging); + action = amount - merging.getAmount() <= 0 ? InventoryAction.PLACE_ALL : InventoryAction.PLACE_SOME; + } else { + action = amount - merging.getAmount() <= 0 ? InventoryAction.PICKUP_ALL + : InventoryAction.PICKUP_SOME; + pickupAmount = merging.getAmount(); + } + InventoryClickEvent e = new InventoryClickEvent(event.getView(), event.getSlotType(), + toNPC ? i : event.getRawSlot(), event.getClick(), action); + onInventoryClick(e); + if (toNPC) { + event.getView().setCursor(null); + } + if (!e.isCancelled() && e.getResult() != Result.DENY) { + stack.setAmount(stack.getAmount() + merging.getAmount()); + dest.setItem(i, stack); + amount -= merging.getAmount(); + event.getCurrentItem().setAmount(amount); + if (amount <= 0) { + break; + } + } } } } @@ -215,57 +260,7 @@ public void onInventoryClick(InventoryClickEvent event) { : event.getInventory(); boolean toNPC = dest == page.ctx.getInventory(); if ((event.getCursor() == null || event.getCursor().getType() == Material.AIR)) { - int amount = event.getCurrentItem().getAmount(); - ItemStack merging = new ItemStack(event.getCurrentItem().clone()); - ItemStack[] contents = dest.getContents(); - for (int i = 0; i < contents.length; i++) { - if (contents[i] == null || contents[i].getType() == Material.AIR) { - merging.setAmount(amount); - if (toNPC) { - event.getView().setCursor(merging); - } - InventoryClickEvent e = new InventoryClickEvent(event.getView(), event.getSlotType(), - toNPC ? i : event.getRawSlot(), event.getClick(), - toNPC ? InventoryAction.PLACE_ALL : InventoryAction.PICKUP_ALL); - onInventoryClick(e); - if (toNPC) { - event.getView().setCursor(null); - } - if (!e.isCancelled() && e.getResult() != Result.DENY) { - dest.setItem(i, merging); - event.setCurrentItem(null); - break; - } - } else if (contents[i].getType() == event.getCurrentItem().getType()) { - ItemStack stack = contents[i].clone(); - merging.setAmount(Math.min(amount, stack.getType().getMaxStackSize() - stack.getAmount())); - InventoryAction action; - if (toNPC) { - event.getView().setCursor(merging); - action = amount - merging.getAmount() <= 0 ? InventoryAction.PLACE_ALL - : InventoryAction.PLACE_SOME; - } else { - action = amount - merging.getAmount() <= 0 ? InventoryAction.PICKUP_ALL - : InventoryAction.PICKUP_SOME; - pickupAmount = merging.getAmount(); - } - InventoryClickEvent e = new InventoryClickEvent(event.getView(), event.getSlotType(), - toNPC ? i : event.getRawSlot(), event.getClick(), action); - onInventoryClick(e); - if (toNPC) { - event.getView().setCursor(null); - } - if (!e.isCancelled() && e.getResult() != Result.DENY) { - stack.setAmount(stack.getAmount() + merging.getAmount()); - dest.setItem(i, stack); - amount -= merging.getAmount(); - event.getCurrentItem().setAmount(amount); - if (amount <= 0) { - break; - } - } - } - } + handleShiftClick(event, dest, toNPC); return; } } @@ -318,7 +313,7 @@ public void onInventoryClick(InventoryClickEvent event) { @EventHandler(ignoreCancelled = true) public void onInventoryClose(InventoryCloseEvent event) { - if (page == null || !event.getInventory().equals(page.ctx.getInventory())) + if (page == null || !event.getInventory().equals(page.ctx.getInventory()) || manualClose) return; page.page.onClose(event.getPlayer()); transitionBack(); @@ -490,6 +485,23 @@ public void transition(InventoryMenuPage instance, Map context) transition(info, instance, context); } + public void transitionBack() { + if (page == null) + return; + Map data = page.ctx.data(); + page = stack.poll(); + if (page != null) { + page.ctx.data().putAll(data); + } + data.clear(); + transitionViewersToInventory(page == null ? null : page.ctx.getInventory()); + if (page == null) { + for (Runnable callback : closeCallbacks) { + callback.run(); + } + } + } + private void transitionViewersToInventory(Inventory inventory) { Collection old = views; views = Lists.newArrayListWithExpectedSize(old.size()); diff --git a/src/main/java/net/citizensnpcs/api/gui/InventoryMenuSlot.java b/src/main/java/net/citizensnpcs/api/gui/InventoryMenuSlot.java index 1646fed0..9ee24ee1 100644 --- a/src/main/java/net/citizensnpcs/api/gui/InventoryMenuSlot.java +++ b/src/main/java/net/citizensnpcs/api/gui/InventoryMenuSlot.java @@ -123,6 +123,15 @@ void onClick(CitizensInventoryClickEvent event) { } } + public void setDescription(String description) { + ItemStack item = inventory.getItem(index); + ItemMeta meta = item.getItemMeta(); + meta.setLore(Arrays.asList(Colorizer.parseColors(description).split("\n"))); + meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES); + item.setItemMeta(meta); + inventory.setItem(index, item); + } + /** * Sets a new {@link ClickType} filter that will only accept clicks with the given type. An empty set is equivalent * to allowing all click types. diff --git a/src/main/java/net/citizensnpcs/api/gui/ModalMenuInput.java b/src/main/java/net/citizensnpcs/api/gui/ModalMenuInput.java new file mode 100644 index 00000000..eed034e7 --- /dev/null +++ b/src/main/java/net/citizensnpcs/api/gui/ModalMenuInput.java @@ -0,0 +1,37 @@ +package net.citizensnpcs.api.gui; + +import java.util.function.Consumer; + +import org.bukkit.conversations.ConversationContext; +import org.bukkit.conversations.ConversationFactory; +import org.bukkit.conversations.Prompt; +import org.bukkit.conversations.StringPrompt; +import org.bukkit.entity.Player; + +import net.citizensnpcs.api.CitizensAPI; + +public class ModalMenuInput { + private ModalMenuInput() { + } + + public static void captureInput(Player player, InventoryMenu menu, Consumer input) { + menu.close(player); + player.beginConversation( + new ConversationFactory(CitizensAPI.getPlugin()).addConversationAbandonedListener((evt) -> { + menu.present(player); + }).withLocalEcho(false).withEscapeSequence("exit").withModality(false).withTimeout(60) + .withFirstPrompt(new StringPrompt() { + @Override + public Prompt acceptInput(ConversationContext ctx, String text) { + input.accept(text); + menu.present(player); + return null; + } + + @Override + public String getPromptText(ConversationContext ctx) { + return ""; + } + }).buildConversation(player)); + } +}