diff --git a/api/build.gradle.kts b/api/build.gradle.kts index bb54151d..0ad329bb 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -2,4 +2,10 @@ dependencies { compileOnly("io.papermc.paper:paper-api:1.20.1-R0.1-SNAPSHOT") compileOnly("com.comphenix.protocol:ProtocolLib:5.0.0") implementation("de.tr7zw:item-nbt-api:2.11.3") -} \ No newline at end of file +} + +tasks { + shadowJar { + relocate ("de.tr7zw", "net.momirealms.customfishing.libraries") + } +} diff --git a/api/src/main/java/net/momirealms/customfishing/api/manager/FishingManager.java b/api/src/main/java/net/momirealms/customfishing/api/manager/FishingManager.java index 0c0b5197..9ae01995 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/manager/FishingManager.java +++ b/api/src/main/java/net/momirealms/customfishing/api/manager/FishingManager.java @@ -6,8 +6,10 @@ import net.momirealms.customfishing.api.mechanic.game.Game; import net.momirealms.customfishing.api.mechanic.game.GameSettings; import net.momirealms.customfishing.api.mechanic.game.GamingPlayer; import net.momirealms.customfishing.api.mechanic.loot.Loot; +import org.bukkit.entity.FishHook; import org.bukkit.entity.Player; +import java.util.Optional; import java.util.UUID; public interface FishingManager { @@ -17,6 +19,8 @@ public interface FishingManager { void removeHookCheckTask(Player player); + Optional getHook(UUID uuid); + void removeTempFishingState(Player player); void processGameResult(GamingPlayer gamingPlayer); diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/AbstractGamingPlayer.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/AbstractGamingPlayer.java index ea191a54..535f867f 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/AbstractGamingPlayer.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/AbstractGamingPlayer.java @@ -54,6 +54,11 @@ public abstract class AbstractGamingPlayer implements GamingPlayer, Runnable { return false; } + @Override + public boolean onChat(String message) { + return false; + } + @Override public boolean onSwapHand() { return false; diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GamingPlayer.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GamingPlayer.java index ec145138..f100ccc3 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GamingPlayer.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/game/GamingPlayer.java @@ -1,5 +1,6 @@ package net.momirealms.customfishing.api.mechanic.game; +import net.kyori.adventure.text.Component; import net.momirealms.customfishing.api.mechanic.effect.Effect; import org.bukkit.entity.Player; @@ -20,4 +21,6 @@ public interface GamingPlayer { Player getPlayer(); Effect getEffectReward(); + + boolean onChat(String message); } diff --git a/api/src/main/java/net/momirealms/customfishing/api/mechanic/market/MarketGUI.java b/api/src/main/java/net/momirealms/customfishing/api/mechanic/market/MarketGUIHolder.java similarity index 87% rename from api/src/main/java/net/momirealms/customfishing/api/mechanic/market/MarketGUI.java rename to api/src/main/java/net/momirealms/customfishing/api/mechanic/market/MarketGUIHolder.java index c45a70a2..a4ed8dc8 100644 --- a/api/src/main/java/net/momirealms/customfishing/api/mechanic/market/MarketGUI.java +++ b/api/src/main/java/net/momirealms/customfishing/api/mechanic/market/MarketGUIHolder.java @@ -4,7 +4,7 @@ import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; import org.jetbrains.annotations.NotNull; -public class MarketGUI implements InventoryHolder { +public class MarketGUIHolder implements InventoryHolder { private Inventory inventory; diff --git a/plugin/src/main/java/net/momirealms/customfishing/libraries/inventorygui/DynamicGuiElement.java b/plugin/src/main/java/net/momirealms/customfishing/libraries/inventorygui/DynamicGuiElement.java deleted file mode 100644 index 2f2e613a..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/libraries/inventorygui/DynamicGuiElement.java +++ /dev/null @@ -1,189 +0,0 @@ -package net.momirealms.customfishing.libraries.inventorygui; - -/* - * Copyright 2017 Max Lee (https://github.com/Phoenix616) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -import org.bukkit.entity.HumanEntity; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; - -import java.util.ArrayList; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; -import java.util.function.Supplier; - -/** - * Represents an element in a gui that will query all it's data when drawn. - */ -public class DynamicGuiElement extends GuiElement { - private Function query; - - private Map cachedElements = new ConcurrentHashMap<>(); - - /** - * Represents an element in a gui that will query all it's data when drawn. - * @param slotChar The character to replace in the gui setup string - * @param query Query the element data, this should return an element with the information - */ - public DynamicGuiElement(char slotChar, Supplier query) { - this(slotChar, (h) -> query.get()); - } - - /** - * Represents an element in a gui that will query all it's data when drawn. - * @param slotChar The character to replace in the gui setup string - * @param query Query the element data, this should return an element with the information and handle null players properly - */ - public DynamicGuiElement(char slotChar, Function query) { - super(slotChar); - this.query = query; - } - - /** - * Query this element's state for every player who had it cached - */ - public void update() { - for (UUID playerId : new ArrayList<>(cachedElements.keySet())) { - Player p = gui.getPlugin().getServer().getPlayer(playerId); - if (p != null && p.isOnline()) { - update(p); - } else { - cachedElements.remove(playerId); - } - } - } - - /** - * Query this element's state for a certain player - * @param player The player for whom to update the element - */ - public CacheEntry update(HumanEntity player) { - CacheEntry cacheEntry = new CacheEntry(queryElement(player)); - if (cacheEntry.element instanceof DynamicGuiElement) { - ((DynamicGuiElement) cacheEntry.element).update(player); - } else if (cacheEntry.element instanceof GuiElementGroup) { - InventoryGui.updateElements(player, ((GuiElementGroup) cacheEntry.element).getElements()); - } - cachedElements.put(player.getUniqueId(), cacheEntry); - return cacheEntry; - } - - @Override - public void setGui(InventoryGui gui) { - super.setGui(gui); - } - - @Override - public ItemStack getItem(HumanEntity who, int slot) { - GuiElement element = getCachedElement(who); - return element != null ? element.getItem(who, slot) : null; - } - - @Override - public Action getAction(HumanEntity who) { - GuiElement element = getCachedElement(who); - return element != null ? element.getAction(who) : null; - } - - /** - * Get the supplier for this element's content - * @return The supplier query - */ - public Function getQuery() { - return query; - } - - /** - * Set the supplier for this element's content - * @param query The supplier query to set - */ - public void setQuery(Function query) { - this.query = query; - } - - /** - * Query the element for a player - * @param who The player - * @return The GuiElement or null - */ - public GuiElement queryElement(HumanEntity who) { - GuiElement element = getQuery().apply(who); - if (element != null) { - element.setGui(gui); - element.setSlots(slots); - } - return element; - } - - /** - * Get the cached element, creates a new one if there is none for that player. - * Use {@link #getLastCached(HumanEntity)} to check if a player has something cached. - * @param who The player to get the element for - * @return The element that is currently cached - */ - public GuiElement getCachedElement(HumanEntity who) { - CacheEntry cached = cachedElements.get(who.getUniqueId()); - if (cached == null) { - cached = update(who); - } - return cached.getElement(); - } - - /** - * Remove the cached element if the player has one. - * @param who The player to remove the cached element for - * @return The element that was cached or null if none was cached - */ - public GuiElement removeCachedElement(HumanEntity who) { - CacheEntry cached = cachedElements.remove(who.getUniqueId()); - return cached != null ? cached.getElement() : null; - } - - /** - * Get the time at which this element was last cached for a certain player - * @param who The player to get the last cache time for - * @return The timestamp from when it was last cached or -1 if it wasn't cached - */ - public long getLastCached(HumanEntity who) { - CacheEntry cached = cachedElements.get(who.getUniqueId()); - return cached != null ? cached.getCreated() : -1; - } - - public class CacheEntry { - private final GuiElement element; - private final long created = System.currentTimeMillis(); - - CacheEntry(GuiElement element) { - this.element = element; - } - - public GuiElement getElement() { - return element; - } - - public long getCreated() { - return created; - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/libraries/inventorygui/GuiBackElement.java b/plugin/src/main/java/net/momirealms/customfishing/libraries/inventorygui/GuiBackElement.java deleted file mode 100644 index b73ccb46..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/libraries/inventorygui/GuiBackElement.java +++ /dev/null @@ -1,77 +0,0 @@ -package net.momirealms.customfishing.libraries.inventorygui; - -import org.bukkit.entity.HumanEntity; -import org.bukkit.inventory.ItemStack; - -/** - * An element that will not appear if there is no previous history, - * but will go back one step if there is - */ -public class GuiBackElement extends StaticGuiElement { - - private boolean close; - - /** - * An element used to go back in history of the gui if there is something to go back to. - * Will not display when there is nothing to go back to. - * - * @param slotChar The character to replace in the gui setup string - * @param item The {@link ItemStack} representing this element - * @param text The text to display on this element, placeholders are automatically - * replaced, see {@link InventoryGui#replaceVars} for a list of the - * placeholder variables. Empty text strings are also filter out, use - * a single space if you want to add an empty line!
- * If it's not set/empty the item's default name will be used - */ - public GuiBackElement(char slotChar, ItemStack item, String... text) { - this(slotChar, item, false, text); - } - - /** - * An element used to go back in history of the gui - * - * @param slotChar The character to replace in the gui setup string - * @param item The {@link ItemStack} representing this element - * @param close Whether to close the GUI if there is nothing to go back to. - * Will not display item if set to false and nothing to go back to. - * @param text The text to display on this element, placeholders are automatically - * replaced, see {@link InventoryGui#replaceVars} for a list of the - * placeholder variables. Empty text strings are also filter out, use - * a single space if you want to add an empty line!
- * If it's not set/empty the item's default name will be used - */ - public GuiBackElement(char slotChar, ItemStack item, boolean close, String... text) { - super(slotChar, item, text); - this.close = close; - - setAction(click -> { - if (canGoBack(click.getWhoClicked())) { - InventoryGui.goBack(click.getWhoClicked()); - } else if (close) { - click.getGui().close(); - } - return true; - }); - } - - @Override - public ItemStack getItem(HumanEntity who, int slot) { - if (!canGoBack(who) && !close) { - return gui.getFiller() != null ? gui.getFiller().getItem(who, slot) : null; - } - - return super.getItem(who, slot); - } - - /** - * Whether this element can close the GUI when nothing to go back to - * @return Close the GUI when nothing to go back - */ - public boolean canClose() { - return close; - } - - private boolean canGoBack(HumanEntity who) { - return InventoryGui.getHistory(who).size() > 1 || (InventoryGui.getHistory(who).size() == 1 && InventoryGui.getHistory(who).peekLast() != gui); - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/libraries/inventorygui/GuiElement.java b/plugin/src/main/java/net/momirealms/customfishing/libraries/inventorygui/GuiElement.java deleted file mode 100644 index 33b50954..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/libraries/inventorygui/GuiElement.java +++ /dev/null @@ -1,235 +0,0 @@ -package net.momirealms.customfishing.libraries.inventorygui; - -/* - * Copyright 2017 Max Lee (https://github.com/Phoenix616) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -import org.bukkit.entity.HumanEntity; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryInteractEvent; -import org.bukkit.inventory.ItemStack; - -/** - * Represents an element in a gui - */ -public abstract class GuiElement { - private final char slotChar; - private Action action; - protected int[] slots = new int[0]; - protected InventoryGui gui; - - /** - * Represents an element in a gui - * @param slotChar The character to replace in the gui setup string - * @param action The action to run when the player clicks on this element - */ - public GuiElement(char slotChar, Action action) { - this.slotChar = slotChar; - setAction(action); - } - - /** - * Represents an element in a gui that doesn't have any action when clicked - * @param slotChar The character to replace in the gui setup string - */ - public GuiElement(char slotChar) { - this(slotChar, null); - } - - /** - * Get the character in the gui setup that corresponds with this element - * @return The character - */ - public char getSlotChar() { - return slotChar; - } - - /** - * Get the item that is displayed by this element on a certain page - * @param who The player who views the page - * @param slot The slot to get the item for - * @return The ItemStack that is displayed as this element - */ - public abstract ItemStack getItem(HumanEntity who, int slot); - - /** - * Get the action that is executed when clicking on this element - * @param who The player who views the page - * @return The action to run - */ - public Action getAction(HumanEntity who) { - return action; - } - - /** - * Set the action that is executed when clicking on this element - * @param action The action to run. The {@link Action#onClick} method should - * return whether or not the click event should be cancelled - */ - public void setAction(Action action) { - this.action = action; - } - - /** - * Get the indexes of the lots that this element is displayed in - * @return An array of the lost indexes - */ - public int[] getSlots() { - return slots; - } - - /** - * Set the ids of the slots where this element is assigned to - * @param slots An array of the slot ids where this element is displayed - */ - public void setSlots(int[] slots) { - this.slots = slots; - } - - /** - * Get the index that this slot has in the list of slots that this element is displayed in - * @param slot The id of the slot - * @return The index in the list of slots that this id has or -1 if it isn't in that list - */ - public int getSlotIndex(int slot) { - return getSlotIndex(slot, 0); - } - - /** - * Get the index that this slot has in the list of slots that this element is displayed in - * @param slot The id of the slot - * @param pageNumber The number of the page that the gui is on - * @return The index in the list of slots that this id has or -1 if it isn't in that list - */ - public int getSlotIndex(int slot, int pageNumber) { - for (int i = 0; i < slots.length; i++) { - if (slots[i] == slot) { - return i + slots.length * pageNumber; - } - } - return -1; - } - - /** - * Set the gui this element belongs to - * @param gui The GUI that this element is in - */ - public void setGui(InventoryGui gui) { - this.gui = gui; - } - - /** - * Get the gui this element belongs to - * @return The GUI that this element is in - */ - public InventoryGui getGui() { - return gui; - } - - public static interface Action { - - /** - * Executed when a player clicks on an element - * @param click The Click class containing information about the click - * @return Whether or not the click event should be cancelled - */ - boolean onClick(Click click); - - } - - public static class Click { - private final InventoryGui gui; - private final int slot; - private final ClickType clickType; - private ItemStack cursor; - private final GuiElement element; - private final InventoryInteractEvent event; - - public Click(InventoryGui gui, int slot, ClickType clickType, ItemStack cursor, GuiElement element, InventoryInteractEvent event) { - this.gui = gui; - this.slot = slot; - this.clickType = clickType; - this.cursor = cursor; - this.element = element; - this.event = event; - } - - /** - * Get the slot of the GUI that was clicked - * @return The clicked slot - */ - public int getSlot() { - return slot; - } - - /** - * Get the element that was clicked - * @return The clicked GuiElement - */ - public GuiElement getElement() { - return element; - } - - /** - * Get the type of the click - * @return The type of the click - */ - public ClickType getType() { - return clickType; - } - - /** - * Get the item on the cursor - * @return The item on the cursor when this click occurred - */ - public ItemStack getCursor() { - return cursor; - } - - /** - * Set the item on the cursor after the click - * @param cursor The new item on the cursor - */ - public void setCursor(ItemStack cursor) { - this.cursor = cursor; - } - - /** - * Get who clicked the element - * @return The player that clicked - */ - public HumanEntity getWhoClicked() { - return event.getWhoClicked(); - } - - /** - * Get the event of the inventory interaction - * @return The InventoryInteractEvent associated with this Click - */ - public InventoryInteractEvent getRawEvent() { - return event; - } - - public InventoryGui getGui() { - return gui; - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/libraries/inventorygui/GuiElementGroup.java b/plugin/src/main/java/net/momirealms/customfishing/libraries/inventorygui/GuiElementGroup.java deleted file mode 100644 index 8077b5a7..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/libraries/inventorygui/GuiElementGroup.java +++ /dev/null @@ -1,276 +0,0 @@ -package net.momirealms.customfishing.libraries.inventorygui; - -/* - * Copyright 2017 Max Lee (https://github.com/Phoenix616) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -import org.bukkit.entity.HumanEntity; -import org.bukkit.inventory.ItemStack; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -/** - * Represents a group of multiple elements. Will be left-aligned by default. - */ -public class GuiElementGroup extends GuiElement { - private List elements = new ArrayList<>(); - private GuiElement filler = null; - private Alignment alignment = Alignment.LEFT; - - /** - * A group of elements - * @param slotChar The character to replace in the gui setup string - * @param elements The elements in this group - */ - public GuiElementGroup(char slotChar, GuiElement... elements) { - super(slotChar, null); - setAction(click -> { - GuiElement element = getElement(click.getSlot(), click.getGui().getPageNumber(click.getWhoClicked())); - if (element != null && element.getAction(click.getRawEvent().getWhoClicked()) != null) { - return element.getAction(click.getWhoClicked()).onClick(click); - } - return true; - }); - Collections.addAll(this.elements, elements); - } - - @Override - public ItemStack getItem(HumanEntity who, int slot) { - GuiElement element = getElement(slot, gui.getPageNumber(who)); - if (element != null) { - return element.getItem(who, slot); - } - return null; - } - - @Override - public void setGui(InventoryGui gui) { - super.setGui(gui); - for (GuiElement element : elements) { - if (element != null) { - element.setGui(gui); - } - } - if (filler != null) { - filler.setGui(gui); - } - } - - @Override - public void setSlots(int[] slots) { - super.setSlots(slots); - for (GuiElement element : elements) { - if (element != null) { - element.setSlots(slots); - } - } - } - - /** - * Add an element to this group - * @param element The element to add - */ - public void addElement(GuiElement element){ - elements.add(element); - if (element != null) { - element.setGui(gui); - element.setSlots(slots); - } - } - - /** - * Add elements to this group - * @param elements The elements to add - */ - public void addElements(GuiElement... elements){ - for (GuiElement element : elements) { - addElement(element); - } - } - - /** - * Add elements to this group - * @param elements The elements to add - */ - public void addElements(Collection elements){ - for (GuiElement element : elements) { - addElement(element); - } - } - - /** - * Get the element in a certain slot - * @param slot The slot to get the element for - * @return The GuiElement in that slot or null - */ - public GuiElement getElement(int slot) { - return getElement(slot, 0); - } - - /** - * Get the element in a certain slot on a certain page - * @param slot The slot to get the element for - * @param pageNumber The number of the page that the gui is on - * @return The GuiElement in that slot or null - */ - public GuiElement getElement(int slot, int pageNumber) { - if (elements.isEmpty()) { - return null; - } - int index = getSlotIndex(slot, slots.length < elements.size() ? pageNumber : 0); - if (index > -1) { - if (alignment == Alignment.LEFT) { - if (index < elements.size()) { - return elements.get(index); - } - } else { - int lineWidth = getLineWidth(slot); - int linePosition = getLinePosition(slot); - if (elements.size() - index > lineWidth - linePosition) { - return elements.get(index); - } - int rest = elements.size() - (index - linePosition); - int blankBefore = alignment == Alignment.CENTER ? (lineWidth - rest) / 2 : lineWidth - rest; - if (linePosition < blankBefore || index - blankBefore >= elements.size()) { - return filler; - } - return elements.get(index - blankBefore); - } - } - return filler; - } - - /** - * Get the width of the line the slot is in - * @param slot The slot - * @return The width of the line in the GUI setup of this group - */ - private int getLineWidth(int slot) { - int width = gui.getWidth(); - int row = slot / width; - - int amount = 0; - for (int s : slots) { - if (s >= row * width && s < (row + 1) * width) { - amount++; - } - } - return amount; - } - - /** - * Get the position of the slot in its line - * @param slot The slot ID - * @return The line position or -1 if not in its line. wat - */ - private int getLinePosition(int slot) { - int width = gui.getWidth(); - int row = slot / width; - - int position = -1; - for (int s : slots) { - if (s >= row * width && s < (row + 1) * width) { - position++; - if (s == slot) { - return position; - } - } - } - return position; - } - - /** - * Get all elements of this group. This list is immutable, use {@link #addElement(GuiElement)} - * and {@link #clearElements()} to modify the elements in this group. - * @return An immutable list of all elements in this group - */ - public List getElements() { - return Collections.unmodifiableList(elements); - } - - /** - * Removes all elements in the group - */ - public void clearElements() { - elements.clear(); - } - - /** - * Set the filler element for empty slots - * @param item The item for the filler element - */ - public void setFiller(ItemStack item) { - filler = new StaticGuiElement(' ', item, " "); - filler.setGui(gui); - } - - /** - * Set the filler element for empty slots - * @param filler The item for the filler element - */ - public void setFiller(GuiElement filler) { - this.filler = filler; - if (filler != null) { - filler.setGui(gui); - } - } - - /** - * Get the filler element - * @return The filler element - */ - public GuiElement getFiller() { - return filler; - } - - /** - * Get the size of this group - * @return The amount of elements that this group has - */ - public int size() { - return elements.size(); - } - - /** - * Set the alignment of the elements in this group - * @param alignment The alignment - */ - public void setAlignment(Alignment alignment) { - this.alignment = alignment; - } - - /** - * Get the alignment of the elements in this group - * @return The alignment - */ - public Alignment getAlignment() { - return alignment; - } - - public enum Alignment { - LEFT, - CENTER, - RIGHT; - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/libraries/inventorygui/GuiPageElement.java b/plugin/src/main/java/net/momirealms/customfishing/libraries/inventorygui/GuiPageElement.java deleted file mode 100644 index 8abf4330..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/libraries/inventorygui/GuiPageElement.java +++ /dev/null @@ -1,124 +0,0 @@ -package net.momirealms.customfishing.libraries.inventorygui; - -/* - * Copyright 2017 Max Lee (https://github.com/Phoenix616) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -import org.bukkit.entity.HumanEntity; -import org.bukkit.inventory.ItemStack; - -/** - * This is an element that allows for controlling the pagination of the gui. - * Untested und potentially unfinished. - */ -public class GuiPageElement extends StaticGuiElement { - private PageAction pageAction; - private boolean silent = false; - - /** - * An element that allows for controlling the pagination of the gui. - * @param slotChar The character to replace in the gui setup string - * @param item The {@link ItemStack} representing this element - * @param pageAction What kind of page action you want to happen when interacting with the element. - * @param text The text to display on this element, placeholders are automatically - * replaced, see {@link InventoryGui#replaceVars} for a list of the - * placeholder variables. Empty text strings are also filter out, use - * a single space if you want to add an empty line!
- * If it's not set/empty the item's default name will be used - */ - public GuiPageElement(char slotChar, ItemStack item, PageAction pageAction, String... text) { - super(slotChar, item, text); - setAction(click -> { - switch (pageAction) { - case NEXT: - if (click.getGui().getPageNumber(click.getWhoClicked()) + 1 < click.getGui().getPageAmount(click.getWhoClicked())) { - if (!isSilent()) { - click.getGui().playClickSound(); - } - click.getGui().setPageNumber(click.getWhoClicked(), click.getGui().getPageNumber(click.getWhoClicked()) + 1); - } - break; - case PREVIOUS: - if (click.getGui().getPageNumber(click.getWhoClicked()) > 0) { - if (!isSilent()) { - click.getGui().playClickSound(); - } - click.getGui().setPageNumber(click.getWhoClicked(), click.getGui().getPageNumber(click.getWhoClicked()) - 1); - } - break; - case FIRST: - if (!isSilent()) { - click.getGui().playClickSound(); - } - click.getGui().setPageNumber(click.getWhoClicked(), 0); - break; - case LAST: - if (!isSilent()) { - click.getGui().playClickSound(); - } - click.getGui().setPageNumber(click.getWhoClicked(), click.getGui().getPageAmount(click.getWhoClicked()) - 1); - break; - } - return true; - }); - this.pageAction = pageAction; - } - - /** - * Get whether or not this element should make a sound when interacted with - * @return Whether or not to make a sound when interacted with - */ - public boolean isSilent() { - return silent; - } - - /** - * Set whether or not this element should make a sound when interacted with - * @param silent Whether or not to make a sound when interacted with - */ - public void setSilent(boolean silent) { - this.silent = silent; - } - - @Override - public ItemStack getItem(HumanEntity who, int slot) { - if (((pageAction == PageAction.FIRST || pageAction == PageAction.LAST) && gui.getPageAmount(who) < 3) - || (pageAction == PageAction.NEXT && gui.getPageNumber(who) + 1 >= gui.getPageAmount(who)) - || (pageAction == PageAction.PREVIOUS && gui.getPageNumber(who) == 0)) { - return gui.getFiller() != null ? gui.getFiller().getItem(who, slot) : null; - } - if (pageAction == PageAction.PREVIOUS) { - setNumber(gui.getPageNumber(who)); - } else if (pageAction == PageAction.NEXT) { - setNumber(gui.getPageNumber(who) + 2); - } else if (pageAction == PageAction.LAST) { - setNumber(gui.getPageAmount(who)); - } - return super.getItem(who, slot).clone(); - } - - public enum PageAction { - NEXT, - PREVIOUS, - FIRST, - LAST; - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/libraries/inventorygui/GuiStateElement.java b/plugin/src/main/java/net/momirealms/customfishing/libraries/inventorygui/GuiStateElement.java deleted file mode 100644 index 448c66ae..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/libraries/inventorygui/GuiStateElement.java +++ /dev/null @@ -1,280 +0,0 @@ -package net.momirealms.customfishing.libraries.inventorygui; - -/* - * Copyright 2017 Max Lee (https://github.com/Phoenix616) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -import org.bukkit.entity.HumanEntity; -import org.bukkit.inventory.ItemStack; - -import java.util.function.Supplier; - -/** - * An element that can switch between certain states. It automatically handles the switching - * of the item in the slot that corresponds to the state that the element is in. - */ -public class GuiStateElement extends GuiElement { - private Supplier queryState = null; - private boolean silent = false; - private int currentState; - private final State[] states; - - /** - * An element that can switch between certain states. - * @param slotChar The character to replace in the gui setup string. - * @param defaultState The index of the default state. - * @param states The list of different {@link State}s that this element can have. - */ - public GuiStateElement(char slotChar, int defaultState, State... states) { - super(slotChar, null); - if (states.length == 0) { - throw new IllegalArgumentException("You need to add at least one State!"); - } - this.currentState = defaultState; - this.states = states; - - setAction(click -> { - State next = nextState(); - next.change.onChange(click); - if (!isSilent()) { - click.getGui().playClickSound(); - } - gui.draw(click.getWhoClicked(), false); - return true; - }); - } - - /** - * An element that can switch between certain states. - * @param slotChar The character to replace in the gui setup string. - * @param defaultState The key of the default state. - * @param states The list of different {@link State}s that this element can have. - */ - public GuiStateElement(char slotChar, String defaultState, State... states) { - this(slotChar, getStateIndex(defaultState, states), states); - } - - /** - * An element that can switch between certain states. - * @param slotChar The character to replace in the gui setup string. - * @param queryState Supplier for the current state. - * @param states The list of different {@link State}s that this element can have. - */ - public GuiStateElement(char slotChar, Supplier queryState, State... states) { - this(slotChar, queryState.get(), states); - this.queryState = () -> getStateIndex(queryState.get(), states); - } - - /** - * An element that can switch between certain states. The first state will be the default one. - * @param slotChar The character to replace in the gui setup string. - * @param states The list of different {@link State}s that this element can have. - */ - public GuiStateElement(char slotChar, State... states) { - this(slotChar, 0, states); - } - - /** - * Loop through the states of this element - * @return The new state (next one to the old) - */ - public State nextState() { - queryCurrentState(); - currentState = states.length > currentState + 1 ? currentState + 1 : 0; - return states[currentState]; - } - - /** - * Loop through the states of this element backwards - * @return The new state (previous one to the old) - */ - public State previousState() { - queryCurrentState(); - currentState = currentState > 0 ? currentState - 1 : states.length - 1; - return states[currentState]; - } - - @Override - public ItemStack getItem(HumanEntity who, int slot) { - return getState().getItem(who); - } - - @Override - public void setGui(InventoryGui gui) { - super.setGui(gui); - for (State state : states) { - state.setGui(gui); - } - } - - /** - * Get the current state of this element - * @return The current state of this element - */ - public State getState() { - queryCurrentState(); - return states[currentState]; - } - - /** - * Get whether or not this element should make a sound when interacted with - * @return Whether or not to make a sound when interacted with - */ - public boolean isSilent() { - return silent; - } - - /** - * Set whether or not this element should make a sound when interacted with - * @param silent Whether or not to make a sound when interacted with - */ - public void setSilent(boolean silent) { - this.silent = silent; - } - - /** - * Try to query the current state if there is a query - */ - private void queryCurrentState() { - if (queryState != null) { - currentState = queryState.get(); - } - } - - /** - * Set the current state with the state's key. Does not trigger the state's change. - * @param key The key to search for. - * @throws IllegalArgumentException Thrown if there is no state with the provided key. - */ - public void setState(String key) throws IllegalArgumentException { - currentState = getStateIndex(key, states); - } - - /** - * Get the index of a state from a key - * @param key The key to search for. - * @param states The states to search in. - * @return The index of that key in the state array. - * @throws IllegalArgumentException Thrown if there is no state with the provided key. - */ - private static int getStateIndex(String key, State[] states) throws IllegalArgumentException { - for (int i = 0; i < states.length; i++) { - if (states[i].getKey().equals(key)) { - return i; - } - } - throw new IllegalArgumentException("This element does not have the state " + key); - } - - /** - * A state that the {@link GuiStateElement} can have. - */ - public static class State { - private final Change change; - private final String key; - private final ItemStack item; - private String[] text; - private InventoryGui gui; - - /** - * A state that the {@link GuiStateElement} can have. - * @param change What to do when the state changes - * @param key The state's string key - * @param item The {@link ItemStack} to represent this state - * @param text The text to display on this element, placeholders are automatically - * replaced, see {@link InventoryGui#replaceVars} for a list of the - * placeholder variables. Empty text strings are also filter out, use - * a single space if you want to add an empty line!
- * If it's not set/empty the item's default name will be used - */ - public State(Change change, String key, ItemStack item, String... text) { - this.change = change; - this.key = key; - this.item = item; - this.text = text; - } - - /** - * Set this element's display text. If this is an empty array the item's name will be displayed - * @param text The text to display on this element, placeholders are automatically - * replaced, see {@link InventoryGui#replaceVars} for a list of the - * placeholder variables. Empty text strings are also filter out, use - * a single space if you want to add an empty line!
- * If it's not set/empty the item's default name will be used - */ - public void setText(String... text) { - this.text = text; - } - - /** - * Get the {@link ItemStack} that represents this state. - * @return The {@link ItemStack} that represents this state - * @deprecated Use {@link #getItem(HumanEntity)} - */ - @Deprecated - public ItemStack getItem() { - return getItem(null); - } - - /** - * Get the {@link ItemStack} that represents this state. - * @param who The player viewing the GUI - * @return The {@link ItemStack} that represents this state - */ - public ItemStack getItem(HumanEntity who) { - ItemStack clone = item.clone(); - gui.setItemText(who, clone, getText()); - return clone; - } - - /** - * Get the string key of the state. - * @return The state's string key - */ - public String getKey() { - return key; - } - - /** - * Get the text lines that describe this state. - * @return The text lines for this state - */ - public String[] getText() { - return text; - } - - private void setGui(InventoryGui gui) { - this.gui = gui; - } - - /** - * Define what should happen when the state of the element' state changes to this state - */ - public interface Change { - - /** - * What should happen when the element's state changes to this state - * @param click The click that triggered this change - */ - void onChange(Click click); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/libraries/inventorygui/GuiStorageElement.java b/plugin/src/main/java/net/momirealms/customfishing/libraries/inventorygui/GuiStorageElement.java deleted file mode 100644 index b6204d07..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/libraries/inventorygui/GuiStorageElement.java +++ /dev/null @@ -1,368 +0,0 @@ -package net.momirealms.customfishing.libraries.inventorygui; - -/* - * Copyright 2017 Max Lee (https://github.com/Phoenix616) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.entity.HumanEntity; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; - -import java.util.function.Function; - -/** - * This element is used to access an {@link Inventory}. The slots in the inventory are selected - * by searching through the whole gui the element is in and getting the number of the spot - * in the character group that this element is in.
- * E.g. if you have five characters called "s" in the gui setup and the second element is - * accessed by the player then it will translate to the second slot in the inventory. - */ -public class GuiStorageElement extends GuiElement { - private final Inventory storage; - private final int invSlot; - private Runnable applyStorage; - private Function itemValidator; - - /** - * An element used to access an {@link Inventory}. - * @param slotChar The character to replace in the gui setup string. - * @param storage The {@link Inventory} that this element is linked to. - */ - public GuiStorageElement(char slotChar, Inventory storage) { - this(slotChar, storage, -1); - } - - /** - * An element used to access a specific slot in an {@link Inventory}. - * @param slotChar The character to replace in the gui setup string. - * @param storage The {@link Inventory} that this element is linked to. - * @param invSlot The index of the slot to access in the {@link Inventory}. - */ - public GuiStorageElement(char slotChar, Inventory storage, int invSlot) { - this(slotChar, storage, invSlot, null, null); - } - - /** - * An element used to access a specific slot in an {@link Inventory}. - * @param slotChar The character to replace in the gui setup string. - * @param storage The {@link Inventory} that this element is linked to. - * @param invSlot The index of the slot to access in the {@link Inventory}. - * @param applyStorage Apply the storage that this element represents. - * @param itemValidator Should return false for items that should not work in that slot - * Can be null if the storage is directly linked. - */ - public GuiStorageElement(char slotChar, Inventory storage, int invSlot, Runnable applyStorage, Function itemValidator) { - super(slotChar, null); - this.invSlot = invSlot; - this.applyStorage = applyStorage; - this.itemValidator = itemValidator; - setAction(click -> { - if (getStorageSlot(click.getWhoClicked(), click.getSlot()) < 0) { - return true; - } - ItemStack storageItem = getStorageItem(click.getWhoClicked(), click.getSlot()); - ItemStack slotItem = click.getRawEvent().getView().getTopInventory().getItem(click.getSlot()); - if (slotItem == null && storageItem != null && storageItem.getType() != Material.AIR - || storageItem == null && slotItem != null && slotItem.getType() != Material.AIR - || storageItem != null && !storageItem.equals(slotItem)) { - gui.draw(click.getWhoClicked(), false); - return true; - } - - if (!(click.getRawEvent() instanceof InventoryClickEvent)) { - // Only the click event will be handled here, drag event is handled separately - return true; - } - - InventoryClickEvent event = (InventoryClickEvent) click.getRawEvent(); - - ItemStack movedItem = null; - switch (event.getAction()) { - case NOTHING: - case CLONE_STACK: - return false; - case MOVE_TO_OTHER_INVENTORY: - if (event.getRawSlot() < click.getRawEvent().getView().getTopInventory().getSize()) { - // Moved from storage - - // Check if there is actually space (more advanced checks can unfortunately not be supported right now) - if (click.getRawEvent().getView().getBottomInventory().firstEmpty() == -1) { - // No empty slot, cancel - return true; - } - movedItem = null; - } else { - // Moved to storage - - // Check if there is actually space (more advanced checks can unfortunately not be supported right now) - if (click.getRawEvent().getView().getTopInventory().firstEmpty() == -1) { - // No empty slot, cancel - return true; - } - movedItem = event.getCurrentItem(); - } - // Update GUI to avoid display glitches - gui.runTask(gui::draw); - break; - case HOTBAR_MOVE_AND_READD: - case HOTBAR_SWAP: - int button = event.getHotbarButton(); - if (button < 0) { - return true; - } - ItemStack hotbarItem = click.getRawEvent().getView().getBottomInventory().getItem(button); - if (hotbarItem != null) { - movedItem = hotbarItem.clone(); - } - break; - case PICKUP_ONE: - case DROP_ONE_SLOT: - if (event.getCurrentItem() != null) { - movedItem = event.getCurrentItem().clone(); - movedItem.setAmount(movedItem.getAmount() - 1); - } - break; - case DROP_ALL_SLOT: - movedItem = null; - break; - case PICKUP_HALF: - if (event.getCurrentItem() != null) { - movedItem = event.getCurrentItem().clone(); - movedItem.setAmount(movedItem.getAmount() / 2); - } - break; - case PLACE_SOME: - if (event.getCurrentItem() == null) { - if (event.getCursor() != null) { - movedItem = event.getCursor().clone(); - } - } else { - movedItem = event.getCurrentItem().clone(); - int newAmount = movedItem.getAmount() + (event.getCursor() != null ? event.getCursor().getAmount() : 0); - if (newAmount < movedItem.getMaxStackSize()) { - movedItem.setAmount(newAmount); - } else { - movedItem.setAmount(movedItem.getMaxStackSize()); - } - } - break; - case PLACE_ONE: - if (event.getCursor() != null) { - if (event.getCurrentItem() == null) { - movedItem = event.getCursor().clone(); - movedItem.setAmount(1); - } else { - movedItem = event.getCursor().clone(); - movedItem.setAmount(event.getCurrentItem().getAmount() + 1); - } - } - break; - case PLACE_ALL: - if (event.getCursor() != null) { - movedItem = event.getCursor().clone(); - if (event.getCurrentItem() != null && event.getCurrentItem().getAmount() > 0) { - movedItem.setAmount(event.getCurrentItem().getAmount() + movedItem.getAmount()); - } - } - break; - case PICKUP_ALL: - case SWAP_WITH_CURSOR: - if (event.getCursor() != null) { - movedItem = event.getCursor().clone(); - }; - break; - case COLLECT_TO_CURSOR: - if (event.getCursor() == null - || event.getCurrentItem() != null && event.getCurrentItem().getType() != Material.AIR) { - return true; - } - gui.simulateCollectToCursor(click); - return false; - default: - click.getRawEvent().getWhoClicked().sendMessage(ChatColor.RED + "The action " + event.getAction() + " is not supported! Sorry about that :("); - return true; - } - return !setStorageItem(click.getWhoClicked(), click.getSlot(), movedItem); - }); - this.storage = storage; - } - - @Override - public ItemStack getItem(HumanEntity who, int slot) { - int index = getStorageSlot(who, slot); - if (index > -1 && index < storage.getSize()) { - return storage.getItem(index); - } - return null; - } - - /** - * Get the {@link Inventory} that this element is linked to. - * @return The {@link Inventory} that this element is linked to. - */ - public Inventory getStorage() { - return storage; - } - - /** - * Get the storage slot index that corresponds to the InventoryGui slot - * @param player The player which is using the GUI view - * @param slot The slot in the GUI - * @return The index of the storage slot or -1 if it's outside the storage - */ - private int getStorageSlot(HumanEntity player, int slot) { - int index = invSlot != -1 ? invSlot : getSlotIndex(slot, gui.getPageNumber(player)); - if (index < 0 || index >= storage.getSize()) { - return -1; - } - return index; - } - - /** - * Get the item in the storage that corresponds to the InventoryGui slot - * @param slot The slot in the GUI - * @return The {@link ItemStack} or null if the slot is outside of the item's size - * @deprecated Use {@link #getStorageItem(HumanEntity, int)} - */ - @Deprecated - public ItemStack getStorageItem(int slot) { - return getStorageItem(null, slot); - } - - /** - * Get the item in the storage that corresponds to the InventoryGui slot - * @param player The player which is using the GUI view - * @param slot The slot in the GUI - * @return The {@link ItemStack} or null if the slot is outside of the item's size - */ - public ItemStack getStorageItem(HumanEntity player, int slot) { - int index = getStorageSlot(player, slot); - if (index == -1) { - return null; - } - return storage.getItem(index); - } - - /** - * Set the item in the storage that corresponds to the InventoryGui slot. - * @param slot The slot in the GUI - * @param item The {@link ItemStack} to set - * @return true if the item was set; false if the slot was outside of this storage - * @deprecated Use {@link #setStorageItem(HumanEntity, int, ItemStack)} - */ - @Deprecated - public boolean setStorageItem(int slot, ItemStack item) { - return setStorageItem(null, slot, item); - } - - /** - * Set the item in the storage that corresponds to the InventoryGui slot. - * @param player The player using the GUI view - * @param slot The slot in the GUI - * @param item The {@link ItemStack} to set - * @return true if the item was set; false if the slot was outside of this storage - */ - public boolean setStorageItem(HumanEntity player, int slot, ItemStack item) { - int index = getStorageSlot(player, slot); - if (index == -1) { - return false; - } - if (!validateItem(slot, item)) { - return false; - } - storage.setItem(index, item); - if (applyStorage != null) { - applyStorage.run(); - } - return true; - } - - /** - * Get the runnable that applies the storage - * @return The storage applying runnable; might be null - */ - public Runnable getApplyStorage() { - return applyStorage; - } - - /** - * Set what should be done to apply the storage. - * Not necessary if the storage is directly backed by a real inventory. - * @param applyStorage How to apply the storage; can be null if nothing should be done - */ - public void setApplyStorage(Runnable applyStorage) { - this.applyStorage = applyStorage; - } - - /** - * Get the item validator - * @return The item validator - */ - public Function getItemValidator() { - return itemValidator; - } - - /** - * Set a function that can validate whether or not an item can fit in the slot - * @param itemValidator The item validator that takes a {@link ValidatorInfo} and returns true for items that - * should and false for items that should not work in that slot - */ - public void setItemValidator(Function itemValidator) { - this.itemValidator = itemValidator; - } - - /** - * Validate whether or not an item can be put in a slot with the item validator set in {@link #setItemValidator(Function)} - * @param slot The slot the item should be tested for - * @param item The item to test - * @return true for items that should and false for items that should not work in that slot - */ - public boolean validateItem(int slot, ItemStack item) { - return itemValidator == null || itemValidator.apply(new ValidatorInfo(this, slot, item)); - } - - public static class ValidatorInfo { - private final GuiElement element; - private final int slot; - private final ItemStack item; - - public ValidatorInfo(GuiElement element, int slot, ItemStack item) { - this.item = item; - this.slot = slot; - this.element = element; - } - - public GuiElement getElement() { - return element; - } - - public int getSlot() { - return slot; - } - - public ItemStack getItem() { - return item; - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/libraries/inventorygui/InventoryGui.java b/plugin/src/main/java/net/momirealms/customfishing/libraries/inventorygui/InventoryGui.java deleted file mode 100644 index bcc6209d..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/libraries/inventorygui/InventoryGui.java +++ /dev/null @@ -1,1690 +0,0 @@ -package net.momirealms.customfishing.libraries.inventorygui; - -/* - * Copyright 2017 Max Lee (https://github.com/Phoenix616) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -import net.kyori.adventure.text.Component; -import net.momirealms.customfishing.api.util.InventoryUtils; -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.Nameable; -import org.bukkit.Sound; -import org.bukkit.block.BlockState; -import org.bukkit.entity.Entity; -import org.bukkit.entity.HumanEntity; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockBreakEvent; -import org.bukkit.event.block.BlockDispenseEvent; -import org.bukkit.event.entity.EntityDeathEvent; -import org.bukkit.event.inventory.*; -import org.bukkit.event.player.PlayerSwapHandItemsEvent; -import org.bukkit.event.server.PluginDisableEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryHolder; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.material.MaterialData; -import org.bukkit.plugin.java.JavaPlugin; -import org.jetbrains.annotations.NotNull; - -import java.lang.reflect.InvocationTargetException; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -/** - * The main library class that lets you create and manage your GUIs - */ -public class InventoryGui implements Listener { - - private final static int[] ROW_WIDTHS = {3, 5, 9}; - private final static InventoryType[] INVENTORY_TYPES = { - InventoryType.DISPENSER, // 3*3 - InventoryType.HOPPER, // 5*1 - InventoryType.CHEST // 9*x - }; - - private final static Map GUI_MAP = new ConcurrentHashMap<>(); - private final static Map> GUI_HISTORY = new ConcurrentHashMap<>(); - - private final static Map PATTERN_CACHE = new HashMap<>(); - - private final static boolean FOLIA; - - private static String DEFAULT_CLICK_SOUND; - - private final JavaPlugin plugin; - private final GuiListener listener; - private InventoryCreator creator; - private String title; - private boolean titleUpdated = false; - private final char[] slots; - private int width; - private final GuiElement[] elementSlots; - private final Map elements = new ConcurrentHashMap<>(); - private InventoryType inventoryType; - private final Map inventories = new ConcurrentHashMap<>(); - private InventoryHolder owner; - private final Map pageNumbers = new ConcurrentHashMap<>(); - private final Map pageAmounts = new ConcurrentHashMap<>(); - private GuiElement.Action outsideAction = click -> false; - private CloseAction closeAction = close -> true; - private String clickSound = getDefaultClickSound(); - private boolean silent = false; - - /** - * Added by CustomFishing - */ - public InventoryGui(JavaPlugin plugin, InventoryHolder owner, Component title, String[] rows, GuiElement... elements) { - this(plugin, new InventoryCreator( - (gui, who, type) -> InventoryUtils.createInventory(new Holder(gui), type, title), - (gui, who, size) -> InventoryUtils.createInventory(new Holder(gui), size, title)), - owner, "", rows, elements); - } - - static { - boolean folia; - try { - Class.forName("io.papermc.paper.threadedregions.scheduler.AsyncScheduler"); - folia = true; - } catch (ClassNotFoundException e) { - folia = false; - } - FOLIA = folia; - - // Sound names changed, make it compatible with both versions - String clickSound = null; - Map clickSounds = new LinkedHashMap<>(); - clickSounds.put("UI_BUTTON_CLICK", "ui.button.click"); - clickSounds.put("CLICK", "random.click"); - for (Map.Entry entry : clickSounds.entrySet()) { - try { - // Try to get sound enum to see if it exists - Sound.valueOf(entry.getKey().toUpperCase(Locale.ROOT)); - // If it does use the sound key - clickSound = entry.getValue(); - break; - } catch (IllegalArgumentException ignored) {} - } - if (clickSound == null) { - for (Sound sound : Sound.values()) { - if (sound.name().contains("CLICK")) { - // Convert to sound key under the assumption that the enum name is just using underscores in the place of dots - clickSound = sound.name().toLowerCase(Locale.ROOT).replace('_', '.'); - break; - } - } - } - setDefaultClickSound(clickSound); - } - - /** - * Create a new gui with a certain setup and some elements - * @param plugin Your plugin - * @param creator A creator for the backing inventory - * @param owner The holder that owns this gui to retrieve it with {@link #get(InventoryHolder)}. - * Can be null. - * @param title The name of the GUI. This will be the title of the inventory. - * @param rows How your rows are setup. Each element is getting assigned to a character. - * Empty/missing ones get filled with the Filler. - * @param elements The {@link GuiElement}s that the gui should have. You can also use {@link #addElement(GuiElement)} later. - * @throws IllegalArgumentException Thrown when the provided rows cannot be matched to an InventoryType - */ - public InventoryGui(JavaPlugin plugin, InventoryCreator creator, InventoryHolder owner, String title, String[] rows, GuiElement... elements) { - this.plugin = plugin; - this.creator = creator; - this.owner = owner; - this.title = title; - this.listener = new GuiListener(); - - width = ROW_WIDTHS[0]; - for (String row : rows) { - if (row.length() > width) { - width = row.length(); - } - } - for (int i = 0; i < ROW_WIDTHS.length && i < INVENTORY_TYPES.length; i++) { - if (width < ROW_WIDTHS[i]) { - width = ROW_WIDTHS[i]; - } - if (width == ROW_WIDTHS[i]) { - inventoryType = INVENTORY_TYPES[i]; - break; - } - } - if (inventoryType == null) { - throw new IllegalArgumentException("Could not match row setup to an inventory type!"); - } - - StringBuilder slotsBuilder = new StringBuilder(); - for (String row : rows) { - if (row.length() < width) { - double side = (width - row.length()) / 2.0; - for (int i = 0; i < Math.floor(side); i++) { - slotsBuilder.append(" "); - } - slotsBuilder.append(row); - for (int i = 0; i < Math.ceil(side); i++) { - slotsBuilder.append(" "); - } - } else if (row.length() == width) { - slotsBuilder.append(row); - } else { - slotsBuilder.append(row, 0, width); - } - } - slots = slotsBuilder.toString().toCharArray(); - elementSlots = new GuiElement[slots.length]; - - addElements(elements); - } - - /** - * Create a new gui with a certain setup and some elements - * @param plugin Your plugin - * @param owner The holder that owns this gui to retrieve it with {@link #get(InventoryHolder)}. - * Can be null. - * @param title The name of the GUI. This will be the title of the inventory. - * @param rows How your rows are setup. Each element is getting assigned to a character. - * Empty/missing ones get filled with the Filler. - * @param elements The {@link GuiElement}s that the gui should have. You can also use {@link #addElement(GuiElement)} later. - * @throws IllegalArgumentException Thrown when the provided rows cannot be matched to an InventoryType - */ - @Deprecated - public InventoryGui(JavaPlugin plugin, InventoryHolder owner, String title, String[] rows, GuiElement... elements) { - this(plugin, new InventoryCreator( - (gui, who, type) -> plugin.getServer().createInventory(new Holder(gui), type, gui.replaceVars(who, gui.getTitle())), - (gui, who, size) -> plugin.getServer().createInventory(new Holder(gui), size, gui.replaceVars(who, gui.getTitle()))), - owner, title, rows, elements); - } - - /** - * The simplest way to create a new gui. It has no owner and elements are optional. - * @param plugin Your plugin - * @param title The name of the GUI. This will be the title of the inventory. - * @param rows How your rows are setup. Each element is getting assigned to a character. - * Empty/missing ones get filled with the Filler. - * @param elements The {@link GuiElement}s that the gui should have. You can also use {@link #addElement(GuiElement)} later. - * @throws IllegalArgumentException Thrown when the provided rows cannot be matched to an InventoryType - */ - @Deprecated - public InventoryGui(JavaPlugin plugin, String title, String[] rows, GuiElement... elements) { - this(plugin, null, title, rows, elements); - } - - /** - * Create a new gui that has no owner with a certain setup and some elements - * @param plugin Your plugin - * @param owner The holder that owns this gui to retrieve it with {@link #get(InventoryHolder)}. - * Can be null. - * @param title The name of the GUI. This will be the title of the inventory. - * @param rows How your rows are setup. Each element is getting assigned to a character. - * Empty/missing ones get filled with the Filler. - * @param elements The {@link GuiElement}s that the gui should have. You can also use {@link #addElement(GuiElement)} later. - * @throws IllegalArgumentException Thrown when the provided rows cannot be matched to an InventoryType - */ - @Deprecated - public InventoryGui(JavaPlugin plugin, InventoryHolder owner, String title, String[] rows, Collection elements) { - this(plugin, owner, title, rows); - addElements(elements); - } - - /** - * Directly set the element in a specific slot - * @param element The {@link GuiElement} to add - * @throws IllegalArgumentException Thrown if the provided slot is below 0 or equal/above the available slot count - * @throws IllegalStateException Thrown if the element was already added to a gui - */ - public void setElement(int slot, GuiElement element) { - if (slot < 0 || slot >= elementSlots.length) { - throw new IllegalArgumentException("Provided slots is outside available slots! (" + elementSlots.length + ")"); - } - if (element.getSlots().length > 0 || element.getGui() != null) { - throw new IllegalStateException("Element was already added to a gui!"); - } - element.setSlots(new int[] {slot}); - element.setGui(this); - elementSlots[slot] = element; - } - - /** - * Add an element to the gui with its position directly based on the elements slot char and the gui setup string - * @param element The {@link GuiElement} to add - */ - public void addElement(GuiElement element) { - if (element.getSlots().length > 0 || element.getGui() != null) { - throw new IllegalStateException("Element was already added to a gui!"); - } - elements.put(element.getSlotChar(), element); - element.setGui(this); - int[] slots = getSlots(element.getSlotChar()); - element.setSlots(slots); - for (int slot : slots) { - elementSlots[slot] = element; - } - } - - private int[] getSlots(char slotChar) { - ArrayList slotList = new ArrayList<>(); - for (int i = 0; i < slots.length; i++) { - if (slots[i] == slotChar) { - slotList.add(i); - } - } - return slotList.stream().mapToInt(Integer::intValue).toArray(); - } - - /** - * Create and add a {@link StaticGuiElement} in one quick method. - * @param slotChar The character to specify the elements position based on the gui setup string - * @param item The item that should be displayed - * @param action The {@link net.momirealms.customfishing.libraries.inventorygui.GuiElement.Action} to run when the player clicks on this element - * @param text The text to display on this element, placeholders are automatically - * replaced, see {@link InventoryGui#replaceVars} for a list of the - * placeholder variables. Empty text strings are also filter out, use - * a single space if you want to add an empty line!
- * If it's not set/empty the item's default name will be used - */ - public void addElement(char slotChar, ItemStack item, GuiElement.Action action, String... text) { - addElement(new StaticGuiElement(slotChar, item, action, text)); - } - - /** - * Create and add a {@link StaticGuiElement} that has no action. - * @param slotChar The character to specify the elements position based on the gui setup string - * @param item The item that should be displayed - * @param text The text to display on this element, placeholders are automatically - * replaced, see {@link InventoryGui#replaceVars} for a list of the - * placeholder variables. Empty text strings are also filter out, use - * a single space if you want to add an empty line!
- * If it's not set/empty the item's default name will be used - */ - public void addElement(char slotChar, ItemStack item, String... text) { - addElement(new StaticGuiElement(slotChar, item, null, text)); - } - - /** - * Create and add a {@link StaticGuiElement} in one quick method. - * @param slotChar The character to specify the elements position based on the gui setup string - * @param materialData The {@link MaterialData} of the item of tihs element - * @param action The {@link net.momirealms.customfishing.libraries.inventorygui.GuiElement.Action} to run when the player clicks on this element - * @param text The text to display on this element, placeholders are automatically - * replaced, see {@link InventoryGui#replaceVars} for a list of the - * placeholder variables. Empty text strings are also filter out, use - * a single space if you want to add an empty line!
- * If it's not set/empty the item's default name will be used - */ - public void addElement(char slotChar, MaterialData materialData, GuiElement.Action action, String... text) { - addElement(slotChar, materialData.toItemStack(1), action, text); - } - - /** - * Create and add a {@link StaticGuiElement} - * @param slotChar The character to specify the elements position based on the gui setup string - * @param material The {@link Material} that the item should have - * @param data The byte representation of the material data of this element - * @param action The {@link GuiElement.Action} to run when the player clicks on this element - * @param text The text to display on this element, placeholders are automatically - * replaced, see {@link InventoryGui#replaceVars} for a list of the - * placeholder variables. Empty text strings are also filter out, use - * a single space if you want to add an empty line!
- * If it's not set/empty the item's default name will be used - */ - public void addElement(char slotChar, Material material, byte data, GuiElement.Action action, String... text) { - addElement(slotChar, new MaterialData(material, data), action, text); - } - - /** - * Create and add a {@link StaticGuiElement} - * @param slotChar The character to specify the elements position based on the gui setup string - * @param material The {@link Material} that the item should have - * @param action The {@link GuiElement.Action} to run when the player clicks on this element - * @param text The text to display on this element, placeholders are automatically - * replaced, see {@link InventoryGui#replaceVars} for a list of the - * placeholder variables. Empty text strings are also filter out, use - * a single space if you want to add an empty line!
- * If it's not set/empty the item's default name will be used - */ - public void addElement(char slotChar, Material material, GuiElement.Action action, String... text) { - addElement(slotChar, material, (byte) 0, action, text); - } - - /** - * Add multiple elements to the gui with their position based on their slot character - * @param elements The {@link GuiElement}s to add - */ - public void addElements(GuiElement... elements) { - for (GuiElement element : elements) { - addElement(element); - } - } - - /** - * Add multiple elements to the gui with their position based on their slot character - * @param elements The {@link GuiElement}s to add - */ - public void addElements(Collection elements) { - for (GuiElement element : elements) { - addElement(element); - } - } - - /** - * Remove a specific element from this gui. - * @param element The element to remove - * @return Whether or not the gui contained this element and if it was removed - */ - public boolean removeElement(GuiElement element) { - boolean removed = elements.remove(element.getSlotChar(), element); - for (int slot : element.getSlots()) { - if (elementSlots[slot] == element) { - elementSlots[slot] = null; - removed = true; - } - } - return removed; - } - - /** - * Remove the element that is currently assigned to a specific slot char from all slots in the gui - * @param slotChar The char of the slot - * @return The element which was in that slot or null if there was none - */ - public GuiElement removeElement(char slotChar) { - GuiElement element = getElement(slotChar); - if (element != null) { - removeElement(element); - } - return element; - } - - /** - * Remove the element that is currently in a specific slot. Will not remove that element from other slots - * @param slot The slot - * @return The element which was in that slot or null if there was none - */ - public GuiElement removeElement(int slot) { - if (slot < 0 || slot >= elementSlots.length) { - return null; - } - GuiElement element = elementSlots[slot]; - elementSlots[slot] = null; - return element; - } - - /** - * Set the filler element for empty slots - * @param item The item for the filler element - */ - public void setFiller(ItemStack item) { - addElement(new StaticGuiElement(' ', item, " ")); - } - - /** - * Get the filler element - * @return The filler element for empty slots - */ - public GuiElement getFiller() { - return elements.get(' '); - } - - /** - * Get the number of the page that this gui is on. zero indexed. Only affects group elements. - * @param player The Player to query the page number for - * @return The page number - */ - public int getPageNumber(@NotNull HumanEntity player) { - return pageNumbers.getOrDefault(player.getUniqueId(), 0); - } - - /** - * Set the number of the page that this gui is on for all players. zero indexed. Only affects group elements. - * @param pageNumber The page number to set - */ - public void setPageNumber(int pageNumber) { - for (UUID playerId : inventories.keySet()) { - Player player = plugin.getServer().getPlayer(playerId); - if (player != null) { - setPageNumber(player, pageNumber); - } - } - } - - /** - * Set the number of the page that this gui is on for a player. zero indexed. Only affects group elements. - * @param player The player to set the page number for - * @param pageNumber The page number to set - */ - public void setPageNumber(HumanEntity player, int pageNumber) { - setPageNumberInternal(player, pageNumber); - draw(player, false); - } - - private void setPageNumberInternal(HumanEntity player, int pageNumber) { - pageNumbers.put(player.getUniqueId(), Math.max(pageNumber, 0)); - } - - /** - * Get the amount of pages that this GUI has for a certain player - * @param player The Player to query the page amount for - * @return The amount of pages - */ - public int getPageAmount(@NotNull HumanEntity player) { - return pageAmounts.getOrDefault(player.getUniqueId(), 1); - } - - /** - * Set the amount of pages that this GUI has for a certain player - * @param player The Player to query the page amount for - * @param pageAmount The page amount - */ - private void setPageAmount(HumanEntity player, int pageAmount) { - pageAmounts.put(player.getUniqueId(), pageAmount); - } - - private void calculatePageAmount(HumanEntity player) { - int pageAmount = 0; - for (GuiElement element : elements.values()) { - int amount = calculateElementSize(player, element); - if (amount > 0 && (pageAmount - 1) * element.getSlots().length < amount && element.getSlots().length > 0) { - pageAmount = (int) Math.ceil((double) amount / element.getSlots().length); - } - } - setPageAmount(player, pageAmount); - if (getPageNumber(player) >= pageAmount) { - setPageNumberInternal(player, Math.min(0, pageAmount - 1)); - } - } - - private int calculateElementSize(HumanEntity player, GuiElement element) { - if (element instanceof GuiElementGroup) { - return ((GuiElementGroup) element).size(); - } else if (element instanceof GuiStorageElement) { - return ((GuiStorageElement) element).getStorage().getSize(); - } else if (element instanceof DynamicGuiElement) { - return calculateElementSize(player, ((DynamicGuiElement) element).getCachedElement(player)); - } - return 0; - } - - /** - * Show this GUI to a player - * @param player The Player to show the GUI to - */ - public void show(HumanEntity player) { - show(player, true); - } - - /** - * Show this GUI to a player - * @param player The Player to show the GUI to - * @param checkOpen Whether or not it should check if this gui is already open - */ - public void show(HumanEntity player, boolean checkOpen) { - // Draw the elements into an inventory, if the title was updated then also force-recreate the inventory if it exists - draw(player, true, titleUpdated); - if (titleUpdated || !checkOpen || !this.equals(getOpen(player))) { - InventoryType type = player.getOpenInventory().getType(); - if (type != InventoryType.CRAFTING && type != InventoryType.CREATIVE) { - // If the player already has a gui open then we assume that the call was from that gui. - // In order to not close it in a InventoryClickEvent listener (which will lead to errors) - // we delay the opening for one tick to run after it finished processing the event - runTask(player, () -> { - Inventory inventory = getInventory(player); - if (inventory != null) { - addHistory(player, this); - player.openInventory(inventory); - } - }); - } else { - Inventory inventory = getInventory(player); - if (inventory != null) { - clearHistory(player); - addHistory(player, this); - player.openInventory(inventory); - } - } - } - // Reset the field that indicates that the title changed - titleUpdated = false; - } - - /** - * Build the gui - */ - public void build() { - build(owner); - } - - /** - * Set the gui's owner and build it - * @param owner The {@link InventoryHolder} that owns the gui - */ - public void build(InventoryHolder owner) { - setOwner(owner); - listener.registerListeners(); - } - - /** - * Draw the elements in the inventory. This can be used to manually refresh the gui. Updates any dynamic elements. - */ - public void draw() { - for (UUID playerId : inventories.keySet()) { - Player player = plugin.getServer().getPlayer(playerId); - if (player != null) { - runTaskOrNow(player, () -> draw(player)); - } - } - } - - /** - * Draw the elements in the inventory. This can be used to manually refresh the gui. Updates any dynamic elements. - * @param who For who to draw the GUI - */ - public void draw(HumanEntity who) { - draw(who, true); - } - - /** - * Draw the elements in the inventory. This can be used to manually refresh the gui. - * @param who For who to draw the GUI - * @param updateDynamic Update dynamic elements - */ - public void draw(HumanEntity who, boolean updateDynamic) { - draw(who, updateDynamic, false); - } - - /** - * Draw the elements in the inventory. This can be used to manually refresh the gui. - * @param who For who to draw the GUI - * @param updateDynamic Update dynamic elements - * @param recreateInventory Recreate the inventory - */ - public void draw(HumanEntity who, boolean updateDynamic, boolean recreateInventory) { - if (updateDynamic) { - updateElements(who, elements.values()); - } - calculatePageAmount(who); - Inventory inventory = getInventory(who); - if (inventory == null || recreateInventory) { - build(); - if (slots.length != inventoryType.getDefaultSize()) { - inventory = getInventoryCreator().getSizeCreator().create(this, who, slots.length); - } else { - inventory = getInventoryCreator().getTypeCreator().create(this, who, inventoryType); - } - inventories.put(who != null ? who.getUniqueId() : null, inventory); - } else { - inventory.clear(); - } - for (int i = 0; i < inventory.getSize(); i++) { - GuiElement element = getElement(i); - if (element == null) { - element = getFiller(); - } - if (element != null) { - inventory.setItem(i, element.getItem(who, i)); - } - } - } - - /** - * Schedule a task on a {@link HumanEntity}/main thread to run on the next tick - * @param entity the human entity to schedule a task on - * @param task the task to be run - */ - protected void runTask(HumanEntity entity, Runnable task) { - if (FOLIA) { - entity.getScheduler().run(plugin, st -> task.run(), null); - } else { - plugin.getServer().getScheduler().runTask(plugin, task); - } - } - - /** - * Schedule a task on the global region/main thread to run on the next tick - * @param task the task to be run - */ - protected void runTask(Runnable task) { - if (FOLIA) { - plugin.getServer().getGlobalRegionScheduler().run(plugin, st -> task.run()); - } else { - plugin.getServer().getScheduler().runTask(plugin, task); - } - } - - /** - * Schedule a task on a {@link HumanEntity} to run on the next tick - * Alternatively if the current thread is already the right thread, execute immediately - * @param entity the human entity to schedule a task on - * @param task the task to be run - */ - protected void runTaskOrNow(HumanEntity entity, Runnable task) { - if (FOLIA) { - if (plugin.getServer().isOwnedByCurrentRegion(entity)) { - task.run(); - } else { - entity.getScheduler().run(plugin, st -> task.run(), null); - } - } else { - if (plugin.getServer().isPrimaryThread()) { - task.run(); - } else { - plugin.getServer().getScheduler().runTask(plugin, task); - } - } - } - - /** - * Update all dynamic elements in a collection of elements. - * @param who The player to update the elements for - * @param elements The elements to update - */ - public static void updateElements(HumanEntity who, Collection elements) { - for (GuiElement element : elements) { - if (element instanceof DynamicGuiElement) { - ((DynamicGuiElement) element).update(who); - } else if (element instanceof GuiElementGroup) { - updateElements(who, ((GuiElementGroup) element).getElements()); - } - } - } - - /** - * Closes the GUI for everyone viewing it - */ - public void close() { - close(true); - } - - /** - * Close the GUI for everyone viewing it - * @param clearHistory Whether to close the GUI completely (by clearing the history) - */ - public void close(boolean clearHistory) { - for (Inventory inventory : inventories.values()) { - for (HumanEntity viewer : new ArrayList<>(inventory.getViewers())) { - close(viewer, clearHistory); - } - } - } - - /** - * Closes the GUI for a specific viewer it - * @param viewer The player viewing it - */ - public void close(HumanEntity viewer) { - close(viewer, true); - } - - /** - * Closes the GUI for a specific viewer it - * @param viewer The player viewing it - * @param clearHistory Whether to close the GUI completely (by clearing the history) - */ - public void close(HumanEntity viewer, boolean clearHistory) { - if (clearHistory) { - clearHistory(viewer); - } - viewer.closeInventory(); - } - - /** - * Destroy this GUI. This unregisters all listeners and removes it from the GUI_MAP - */ - public void destroy() { - destroy(true); - } - - private void destroy(boolean closeInventories) { - if (closeInventories) { - close(); - } - for (Inventory inventory : inventories.values()) { - inventory.clear(); - } - inventories.clear(); - pageNumbers.clear(); - pageAmounts.clear(); - listener.unregisterListeners(); - removeFromMap(); - } - - /** - * Add a new history entry to the end of the history - * @param player The player to add the history entry for - * @param gui The GUI to add to the history - */ - public static void addHistory(HumanEntity player, InventoryGui gui) { - GUI_HISTORY.putIfAbsent(player.getUniqueId(), new ArrayDeque<>()); - Deque history = getHistory(player); - if (history.peekLast() != gui) { - history.add(gui); - } - } - - /** - * Get the history of a player - * @param player The player to get the history for - * @return The history as a deque of InventoryGuis; - * returns an empty one and not null! - */ - public static Deque getHistory(HumanEntity player) { - return GUI_HISTORY.getOrDefault(player.getUniqueId(), new ArrayDeque<>()); - } - - /** - * Go back one entry in the history - * @param player The player to show the previous gui to - * @return true if there was a gui to show; false if not - */ - public static boolean goBack(HumanEntity player) { - Deque history = getHistory(player); - history.pollLast(); - if (history.isEmpty()) { - return false; - } - InventoryGui previous = history.peekLast(); - if (previous != null) { - previous.show(player, false); - } - return true; - } - - /** - * Clear the history of a player - * @param player The player to clear the history for - * @return The history - */ - public static Deque clearHistory(HumanEntity player) { - Deque previous = GUI_HISTORY.remove(player.getUniqueId()); - return previous != null ? previous : new ArrayDeque<>(); - } - - /** - * Get the plugin which owns this GUI. Should be the one who created it. - * @return The plugin which owns this GUI - */ - public JavaPlugin getPlugin() { - return plugin; - } - - /** - * Get the helper class which will create the custom inventory for this gui. - * Simply uses {@link org.bukkit.Bukkit#createInventory(InventoryHolder, int, String)} by default. - * @return The used inventory creator instance - */ - public InventoryCreator getInventoryCreator() { - return creator; - } - - /** - * Set the helper class which will create the custom inventory for this gui. - * Can be used to create more special inventories. - * Simply uses {@link org.bukkit.Bukkit#createInventory(InventoryHolder, int, String)} by default. - * Should return a container inventory that can hold the size. Special inventories will break stuff. - * @param creator The new inventory creator instance - */ - public void setInventoryCreator(InventoryCreator creator) { - this.creator = creator; - } - - /** - * Get element in a certain slot - * @param slot The slot to get the element from - * @return The GuiElement or null if the slot is empty/there wasn't one - */ - public GuiElement getElement(int slot) { - return slot < 0 || slot >= elementSlots.length ? null : elementSlots[slot]; - } - - /** - * Get an element by its character - * @param c The character to get the element by - * @return The GuiElement or null if there is no element for that character - */ - public GuiElement getElement(char c) { - return elements.get(c); - } - - /** - * Get all elements of this gui. This collection is immutable, use the addElement and removeElement methods - * to modify the elements in this gui. - * @return An immutable collection of all elements in this group - */ - public Collection getElements() { - return Collections.unmodifiableCollection(elements.values()); - } - - /** - * Set the owner of this GUI. Will remove the previous assignment. - * @param owner The owner of the GUI - */ - public void setOwner(InventoryHolder owner) { - removeFromMap(); - this.owner = owner; - if (owner instanceof Entity) { - GUI_MAP.put(((Entity) owner).getUniqueId().toString(), this); - } else if (owner instanceof BlockState) { - GUI_MAP.put(((BlockState) owner).getLocation().toString(), this); - } - } - - /** - * Get the owner of this GUI. Will be null if th GUI doesn't have one - * @return The InventoryHolder of this GUI - */ - public InventoryHolder getOwner() { - return owner; - } - - /** - * Check whether or not the Owner of this GUI is real or fake - * @return true if the owner is a real world InventoryHolder; false if it is null - */ - public boolean hasRealOwner() { - return owner != null; - } - - /** - * Get the Action that is run when clicked outside of the inventory - * @return The Action for when the player clicks outside the inventory; can be null - */ - public GuiElement.Action getOutsideAction() { - return outsideAction; - } - - /** - * Set the Action that is run when clicked outside of the inventory - * @param outsideAction The Action for when the player clicks outside the inventory; can be null - */ - public void setOutsideAction(GuiElement.Action outsideAction) { - this.outsideAction = outsideAction; - } - - /** - * Get the action that is run when this GUI is closed - * @return The action for when the player closes this inventory; can be null - */ - public CloseAction getCloseAction() { - return closeAction; - } - - /** - * Set the action that is run when this GUI is closed; it should return true if the GUI should go back - * @param closeAction The action for when the player closes this inventory; can be null - */ - public void setCloseAction(CloseAction closeAction) { - this.closeAction = closeAction; - } - - /** - * Get the click sound to use for non-silent GUIs that don't have a specific one set - * @return The default click sound, if set null no sound will play - */ - public static String getDefaultClickSound() { - return DEFAULT_CLICK_SOUND; - } - - /** - * Set the click sound to use for non-silent GUIs that don't have a specific one set - * @param defaultClickSound The default click sound, if set to null no sound will play - */ - public static void setDefaultClickSound(String defaultClickSound) { - DEFAULT_CLICK_SOUND = defaultClickSound; - } - - /** - * Set the sound that plays when a button (that isn't preventing the item from being taken) is clicked in the GUI. - * Fillers will not play a click sound - * @return The key of the sound to play - */ - public String getClickSound() { - return clickSound; - } - - /** - * Set the sound that plays when a button (that isn't preventing the item from being taken) is clicked in the GUI. - * Fillers will not play a click sound - * @param soundKey The key of the sound to play, if null then no sound will play (same effect as {@link #setSilent(boolean)}) - */ - public void setClickSound(String soundKey) { - clickSound = soundKey; - } - - /** - * Get whether or not this GUI should make a sound when interacting with elements that make sound - * @return Whether or not to make a sound when interacted with - */ - public boolean isSilent() { - return silent; - } - - /** - * Set whether or not this GUI should make a sound when interacting with elements that make sound - * @param silent Whether or not to make a sound when interacted with - */ - public void setSilent(boolean silent) { - this.silent = silent; - } - - private void removeFromMap() { - if (owner instanceof Entity) { - GUI_MAP.remove(((Entity) owner).getUniqueId().toString(), this); - } else if (owner instanceof BlockState) { - GUI_MAP.remove(((BlockState) owner).getLocation().toString(), this); - } - } - - /** - * Get the GUI registered to an InventoryHolder - * @param holder The InventoryHolder to get the GUI for - * @return The InventoryGui registered to it or null if none was registered to it - */ - public static InventoryGui get(InventoryHolder holder) { - if (holder instanceof Entity) { - return GUI_MAP.get(((Entity) holder).getUniqueId().toString()); - } else if (holder instanceof BlockState) { - return GUI_MAP.get(((BlockState) holder).getLocation().toString()); - } - return null; - } - - /** - * Get the GUI that a player has currently open - * @param player The Player to get the GUI for - * @return The InventoryGui that the player has open - */ - public static InventoryGui getOpen(HumanEntity player) { - return getHistory(player).peekLast(); - } - - /** - * Get the title of the gui - * @return The title of the gui - */ - public String getTitle() { - return title; - } - - /** - * Set the title of the gui - * @param title The {@link String} that should be the title of the gui - */ - public void setTitle(String title) { - this.title = title; - this.titleUpdated = true; - } - - /** - * Play a click sound e.g. when an element acts as a button - */ - public void playClickSound() { - if (isSilent() || clickSound == null) return; - for (Inventory inventory : inventories.values()) { - for (HumanEntity humanEntity : inventory.getViewers()) { - if (humanEntity instanceof Player) { - ((Player) humanEntity).playSound(humanEntity.getEyeLocation(), getClickSound(), 1, 1); - } - } - } - } - - /** - * Get the inventory. Package scope as it should only be used by InventoryGui.Holder - * @return The GUI's generated inventory - */ - Inventory getInventory() { - return getInventory(null); - } - - /** - * Get the inventory of a certain player - * @param who The player, if null it will try to return the inventory created first or null if none was created - * @return The GUI's generated inventory, null if none was found - */ - private Inventory getInventory(HumanEntity who) { - return who != null ? inventories.get(who.getUniqueId()) : (inventories.isEmpty() ? null : inventories.values().iterator().next()); - } - - /** - * Get the width of the GUI in slots - * @return The width of the GUI - */ - int getWidth() { - return width; - } - - /** - * Handle interaction with a slot in this GUI - * @param event The event that triggered it - * @param clickType The type of click - * @param slot The slot - * @param cursor The item on the cursor - * @return The resulting click object - */ - private GuiElement.Click handleInteract(InventoryInteractEvent event, ClickType clickType, int slot, ItemStack cursor) { - GuiElement.Action action = null; - GuiElement element = null; - if (slot >= 0) { - element = getElement(slot); - if (element != null) { - action = element.getAction(event.getWhoClicked()); - } - } else if (slot == -999) { - action = outsideAction; - } else { - if (event instanceof InventoryClickEvent) { - // Click was neither for the top inventory nor outside - // E.g. click is in the bottom inventory - if (((InventoryClickEvent) event).getAction() == InventoryAction.COLLECT_TO_CURSOR) { - GuiElement.Click click = new GuiElement.Click(this, slot, clickType, cursor, null, event); - simulateCollectToCursor(click); - return click; - } else if (((InventoryClickEvent) event).getAction() == InventoryAction.MOVE_TO_OTHER_INVENTORY) { - // This was an action we can't handle, abort - event.setCancelled(true); - } - } - return null; - } - try { - GuiElement.Click click = new GuiElement.Click(this, slot, clickType, cursor, element, event); - if (action == null || action.onClick(click)) { - event.setCancelled(true); - if (event.getWhoClicked() instanceof Player) { - ((Player) event.getWhoClicked()).updateInventory(); - } - } - if (action != null) { - // Let's assume something changed and re-draw all currently shown inventories - for (UUID playerId : inventories.keySet()) { - if (!event.getWhoClicked().getUniqueId().equals(playerId)) { - Player player = plugin.getServer().getPlayer(playerId); - if (player != null) { - draw(player, false); - } - } - } - return click; - } - } catch (Throwable t) { - event.setCancelled(true); - if (event.getWhoClicked() instanceof Player) { - ((Player) event.getWhoClicked()).updateInventory(); - } - plugin.getLogger().log(Level.SEVERE, "Exception while trying to run action for click on " - + (element != null ? element.getClass().getSimpleName() : "empty element") - + " in slot " + slot + " of " + getTitle() + " GUI!"); - t.printStackTrace(); - } - return null; - } - - private abstract class UnregisterableListener implements Listener { - private final List listeners; - private boolean listenersRegistered = false; - - private UnregisterableListener() { - List listeners = new ArrayList<>(); - for (Class innerClass : getClass().getDeclaredClasses()) { - if (UnregisterableListener.class.isAssignableFrom(innerClass)) { - try { - UnregisterableListener listener = ((Class) innerClass).getDeclaredConstructor(getClass()).newInstance(this); - if (!(listener instanceof OptionalListener) || ((OptionalListener) listener).isCompatible()) { - listeners.add(listener); - } - } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { - e.printStackTrace(); - } - } - } - this.listeners = Collections.unmodifiableList(listeners); - } - - protected void registerListeners() { - if (listenersRegistered) { - return; - } - plugin.getServer().getPluginManager().registerEvents(this, plugin); - for (UnregisterableListener listener : listeners) { - listener.registerListeners(); - } - listenersRegistered = true; - } - - protected void unregisterListeners() { - HandlerList.unregisterAll(this); - for (UnregisterableListener listener : listeners) { - listener.unregisterListeners(); - } - listenersRegistered = false; - } - } - - private abstract class OptionalListener extends UnregisterableListener { - private boolean isCompatible() { - try { - getClass().getMethods(); - getClass().getDeclaredMethods(); - return true; - } catch (NoClassDefFoundError e) { - return false; - } - } - } - - /** - * All the listeners that InventoryGui needs to work - */ - private class GuiListener extends UnregisterableListener { - - @EventHandler(ignoreCancelled = true) - private void onInventoryClick(InventoryClickEvent event) { - if (event.getInventory().equals(getInventory(event.getWhoClicked()))) { - - int slot = -1; - if (event.getRawSlot() < event.getView().getTopInventory().getSize()) { - slot = event.getRawSlot(); - } else if (event.getAction() == InventoryAction.MOVE_TO_OTHER_INVENTORY) { - slot = event.getInventory().firstEmpty(); - } - - // Cache the original cursor - ItemStack originalCursor = event.getCursor() != null ? event.getCursor().clone() : null; - - // Forward the click - GuiElement.Click click = handleInteract(event, event.getClick(), slot, event.getCursor()); - - // Update the cursor if necessary - if (click != null && (originalCursor == null || !originalCursor.equals(click.getCursor()))) { - event.setCursor(click.getCursor()); - } - } else if (hasRealOwner() && owner.equals(event.getInventory().getHolder())) { - // Click into inventory by same owner but not the inventory of the GUI - // Assume that the underlying inventory changed and redraw the GUI - runTask(InventoryGui.this::draw); - } - } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) - public void onInventoryDrag(InventoryDragEvent event) { - Inventory inventory = getInventory(event.getWhoClicked()); - if (event.getInventory().equals(inventory)) { - // Check if we only drag over one slot if so then handle that as a click with the element - if (event.getRawSlots().size() == 1) { - int slot = event.getRawSlots().iterator().next(); - if (slot < event.getView().getTopInventory().getSize()) { - GuiElement.Click click = handleInteract( - event, - // Map drag type to the button that caused it - event.getType() == DragType.SINGLE ? ClickType.RIGHT : ClickType.LEFT, - slot, - event.getOldCursor() - ); - - // Update the cursor if necessary - if (click != null && !event.getOldCursor().equals(click.getCursor())) { - event.setCursor(click.getCursor()); - } - } - return; - } - - int rest = 0; - Map resetSlots = new HashMap<>(); - for (Map.Entry items : event.getNewItems().entrySet()) { - if (items.getKey() < inventory.getSize()) { - GuiElement element = getElement(items.getKey()); - if (!(element instanceof GuiStorageElement) - || !((GuiStorageElement) element).setStorageItem(event.getWhoClicked(), items.getKey(), items.getValue())) { - ItemStack slotItem = event.getInventory().getItem(items.getKey()); - if (!items.getValue().isSimilar(slotItem)) { - rest += items.getValue().getAmount(); - } else if (slotItem != null) { - rest += items.getValue().getAmount() - slotItem.getAmount(); - } - //items.getValue().setAmount(0); // can't change resulting items :/ - resetSlots.put(items.getKey(), event.getInventory().getItem(items.getKey())); // reset them manually - } - } - } - - runTask(event.getWhoClicked(), () -> { - for (Map.Entry items : resetSlots.entrySet()) { - event.getView().getTopInventory().setItem(items.getKey(), items.getValue()); - } - }); - - if (rest > 0) { - int cursorAmount = event.getCursor() != null ? event.getCursor().getAmount() : 0; - if (!event.getOldCursor().isSimilar(event.getCursor())) { - event.setCursor(event.getOldCursor()); - cursorAmount = 0; - } - int newCursorAmount = cursorAmount + rest; - if (newCursorAmount <= event.getCursor().getMaxStackSize()) { - event.getCursor().setAmount(newCursorAmount); - } else { - event.getCursor().setAmount(event.getCursor().getMaxStackSize()); - ItemStack add = event.getCursor().clone(); - int addAmount = newCursorAmount - event.getCursor().getMaxStackSize(); - if (addAmount > 0) { - add.setAmount(addAmount); - for (ItemStack drop : event.getWhoClicked().getInventory().addItem(add).values()) { - event.getWhoClicked().getLocation().getWorld().dropItem(event.getWhoClicked().getLocation(), drop); - } - } - } - } - } - } - - @EventHandler(priority = EventPriority.MONITOR) - public void onInventoryClose(InventoryCloseEvent event) { - Inventory inventory = getInventory(event.getPlayer()); - if (event.getInventory().equals(inventory)) { - // go back. that checks if the player is in gui and has history - if (InventoryGui.this.equals(getOpen(event.getPlayer()))) { - if (closeAction == null || closeAction.onClose(new Close(event.getPlayer(), InventoryGui.this, event))) { - goBack(event.getPlayer()); - } else { - clearHistory(event.getPlayer()); - } - } - if (inventories.size() <= 1) { - destroy(false); - } else { - inventory.clear(); - for (HumanEntity viewer : inventory.getViewers()) { - if (viewer != event.getPlayer()) { - viewer.closeInventory(); - } - } - inventories.remove(event.getPlayer().getUniqueId()); - pageAmounts.remove(event.getPlayer().getUniqueId()); - pageNumbers.remove(event.getPlayer().getUniqueId()); - for (GuiElement element : getElements()) { - if (element instanceof DynamicGuiElement) { - ((DynamicGuiElement) element).removeCachedElement(event.getPlayer()); - } - } - } - } - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onInventoryMoveItem(InventoryMoveItemEvent event) { - if (hasRealOwner() && (owner.equals(event.getDestination().getHolder()) || owner.equals(event.getSource().getHolder()))) { - runTask(InventoryGui.this::draw); - } - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onDispense(BlockDispenseEvent event) { - if (hasRealOwner() && owner.equals(event.getBlock().getState())) { - runTask(InventoryGui.this::draw); - } - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onBlockBreak(BlockBreakEvent event) { - if (hasRealOwner() && owner.equals(event.getBlock().getState())) { - destroy(); - } - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onEntityDeath(EntityDeathEvent event) { - if (hasRealOwner() && owner.equals(event.getEntity())) { - destroy(); - } - } - - @EventHandler(priority = EventPriority.MONITOR) - public void onPluginDisable(PluginDisableEvent event) { - if (event.getPlugin() == plugin) { - destroy(); - } - } - - /** - * Event isn't available on older version so just use a separate listener... - */ - protected class ItemSwapGuiListener extends OptionalListener { - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onInventoryMoveItem(PlayerSwapHandItemsEvent event) { - Inventory inventory = getInventory(event.getPlayer()); - if (event.getPlayer().getOpenInventory().getTopInventory().equals(inventory)) { - event.setCancelled(true); - } - } - } - } - - /** - * Fake InventoryHolder for the GUIs - */ - public static class Holder implements InventoryHolder { - private InventoryGui gui; - - public Holder(InventoryGui gui) { - this.gui = gui; - } - - @Override - public Inventory getInventory() { - return gui.getInventory(); - } - - public InventoryGui getGui() { - return gui; - } - } - - public static interface CloseAction { - - /** - * Executed when a player closes a GUI inventory - * @param close The close object holding information about this close - * @return Whether or not the close should go back or not - */ - boolean onClose(Close close); - - } - - public static class Close { - private final HumanEntity player; - private final InventoryGui gui; - private final InventoryCloseEvent event; - - public Close(HumanEntity player, InventoryGui gui, InventoryCloseEvent event) { - this.player = player; - this.gui = gui; - this.event = event; - } - - public HumanEntity getPlayer() { - return player; - } - - public InventoryGui getGui() { - return gui; - } - - public InventoryCloseEvent getEvent() { - return event; - } - } - - /** - * Set the text of an item using the display name and the lore. - * Also replaces any placeholders in the text and filters out empty lines. - * Use a single space to create an emtpy line. - * @param item The {@link ItemStack} to set the text for - * @param text The text lines to set - * @deprecated Use {@link #setItemText(HumanEntity, ItemStack, String...)} - */ - @Deprecated - public void setItemText(ItemStack item, String... text) { - setItemText(null, item, text); - } - - /** - * Set the text of an item using the display name and the lore. - * Also replaces any placeholders in the text and filters out empty lines. - * Use a single space to create an emtpy line. - * @param player The player viewing the GUI - * @param item The {@link ItemStack} to set the text for - * @param text The text lines to set - */ - public void setItemText(HumanEntity player, ItemStack item, String... text) { - if (item != null && text != null && text.length > 0) { - ItemMeta meta = item.getItemMeta(); - if (meta != null) { - String combined = replaceVars(player, Arrays.stream(text) - .map(s -> s == null ? " " : s) - .filter(s -> !s.isEmpty()) - .collect(Collectors.joining("\n"))); - String[] lines = combined.split("\n"); - if (text[0] != null) { - meta.setDisplayName(lines[0]); - } - if (lines.length > 1) { - meta.setLore(Arrays.asList(Arrays.copyOfRange(lines, 1, lines.length))); - } else { - meta.setLore(null); - } - item.setItemMeta(meta); - } - } - } - - /** - * Replace some placeholders in the with values regarding the gui's state. Replaced color codes.
- * The placeholders are:
- * %plugin% - The name of the plugin that this gui is from.
- * %owner% - The name of the owner of this gui. Will be an empty string when the owner is null.
- * %title% - The title of this GUI.
- * %page% - The current page that this gui is on.
- * %nextpage% - The next page. "none" if there is no next page.
- * %prevpage% - The previous page. "none" if there is no previous page.
- * %pages% - The amount of pages that this gui has. - * @param text The text to replace the placeholders in - * @param replacements Additional repplacements. i = placeholder, i+1 = replacements - * @return The text with all placeholders replaced - * @deprecated Use {@link #replaceVars(HumanEntity, String, String...)} - */ - @Deprecated - public String replaceVars(String text, String... replacements) { - return replaceVars(null, text, replacements); - } - - /** - * Replace some placeholders in the with values regarding the gui's state. Replaced color codes.
- * The placeholders are:
- * %plugin% - The name of the plugin that this gui is from.
- * %owner% - The name of the owner of this gui. Will be an empty string when the owner is null.
- * %title% - The title of this GUI.
- * %page% - The current page that this gui is on.
- * %nextpage% - The next page. "none" if there is no next page.
- * %prevpage% - The previous page. "none" if there is no previous page.
- * %pages% - The amount of pages that this gui has. - * @param player The player viewing the GUI - * @param text The text to replace the placeholders in - * @param replacements Additional repplacements. i = placeholder, i+1 = replacements - * @return The text with all placeholders replaced - */ - public String replaceVars(HumanEntity player, String text, String... replacements) { - Map map = new LinkedHashMap<>(); - for (int i = 0; i + 1 < replacements.length; i += 2) { - map.putIfAbsent(replacements[i], replacements[i + 1]); - } - - map.putIfAbsent("plugin", plugin.getName()); - try { - map.putIfAbsent("owner", owner instanceof Nameable ? ((Nameable) owner).getCustomName() : ""); - } catch (NoSuchMethodError | NoClassDefFoundError e) { - map.putIfAbsent("owner", owner instanceof Entity ? ((Entity) owner).getCustomName() : ""); - } - map.putIfAbsent("title", title); - map.putIfAbsent("page", String.valueOf(getPageNumber(player) + 1)); - map.putIfAbsent("nextpage", getPageNumber(player) + 1 < getPageAmount(player) ? String.valueOf(getPageNumber(player) + 2) : "none"); - map.putIfAbsent("prevpage", getPageNumber(player) > 0 ? String.valueOf(getPageNumber(player)) : "none"); - map.putIfAbsent("pages", String.valueOf(getPageAmount(player))); - - return ChatColor.translateAlternateColorCodes('&', replace(text, map)); - } - - /** - * Replace placeholders in a string - * @param string The string to replace in - * @param replacements What to replace the placeholders with. The n-th index is the placeholder, the n+1-th the value. - * @return The string with all placeholders replaced (using the configured placeholder prefix and suffix) - */ - private String replace(String string, Map replacements) { - for (Map.Entry entry : replacements.entrySet()) { - if (entry.getKey() == null) { - continue; - } - String placeholder = "%" + entry.getKey() + "%"; - Pattern pattern = PATTERN_CACHE.get(placeholder); - if (pattern == null) { - PATTERN_CACHE.put(placeholder, pattern = Pattern.compile(placeholder, Pattern.LITERAL)); - } - string = pattern.matcher(string).replaceAll(Matcher.quoteReplacement(entry.getValue() != null ? entry.getValue() : "null")); - } - return string; - } - - /** - * Simulate the collecting to the cursor while respecting elements that can't be modified - * @param click The click that startet it all - */ - void simulateCollectToCursor(GuiElement.Click click) { - if (!(click.getRawEvent() instanceof InventoryClickEvent)) { - // Only a click event can trigger the collection to the cursor - return; - } - InventoryClickEvent event = (InventoryClickEvent) click.getRawEvent(); - - ItemStack newCursor = click.getCursor().clone(); - - boolean itemInGui = false; - for (int i = 0; i < click.getRawEvent().getView().getTopInventory().getSize(); i++) { - if (i != event.getRawSlot()) { - ItemStack viewItem = click.getRawEvent().getView().getTopInventory().getItem(i); - if (newCursor.isSimilar(viewItem)) { - itemInGui = true; - } - GuiElement element = getElement(i); - if (element instanceof GuiStorageElement) { - GuiStorageElement storageElement = (GuiStorageElement) element; - ItemStack otherStorageItem = storageElement.getStorageItem(click.getWhoClicked(), i); - if (addToStack(newCursor, otherStorageItem)) { - if (otherStorageItem.getAmount() == 0) { - otherStorageItem = null; - } - storageElement.setStorageItem(i, otherStorageItem); - if (newCursor.getAmount() == newCursor.getMaxStackSize()) { - break; - } - } - } - } - } - - if (itemInGui) { - event.setCurrentItem(null); - click.getRawEvent().setCancelled(true); - if (click.getRawEvent().getWhoClicked() instanceof Player) { - ((Player) click.getRawEvent().getWhoClicked()).updateInventory(); - } - - if (click.getElement() instanceof GuiStorageElement) { - ((GuiStorageElement) click.getElement()).setStorageItem(click.getWhoClicked(), click.getSlot(), null); - } - - if (newCursor.getAmount() < newCursor.getMaxStackSize()) { - Inventory bottomInventory = click.getRawEvent().getView().getBottomInventory(); - for (ItemStack bottomIem : bottomInventory) { - if (addToStack(newCursor, bottomIem)) { - if (newCursor.getAmount() == newCursor.getMaxStackSize()) { - break; - } - } - } - } - event.setCursor(newCursor); - draw(); - } - } - - /** - * Add items to a stack up to the max stack size - * @param item The base item - * @param add The item stack to add - * @return true if the stack is finished; false if these stacks can't be merged - */ - private static boolean addToStack(ItemStack item, ItemStack add) { - if (item.isSimilar(add)) { - int newAmount = item.getAmount() + add.getAmount(); - if (newAmount >= item.getMaxStackSize()) { - item.setAmount(item.getMaxStackSize()); - add.setAmount(newAmount - item.getAmount()); - } else { - item.setAmount(newAmount); - add.setAmount(0); - } - return true; - } - return false; - } - - public static class InventoryCreator { - private final CreatorImplementation typeCreator; - private final CreatorImplementation sizeCreator; - - /** - * A new inventory creator which should be able to create an inventory based on the type and the size. - *

- * By default the creators are implemented as follows: - *
-         * typeCreator = (gui, who, type) -> plugin.getServer().createInventory(new Holder(gui), type, gui.replaceVars(who, title));
-         * sizeCreator = (gui, who, size) -> plugin.getServer().createInventory(new Holder(gui), size, gui.replaceVars(who, title));
-         * 
- * @param typeCreator The type creator. - * @param sizeCreator The size creator - */ - public InventoryCreator(CreatorImplementation typeCreator, CreatorImplementation sizeCreator) { - this.typeCreator = typeCreator; - this.sizeCreator = sizeCreator; - } - - public CreatorImplementation getTypeCreator() { - return typeCreator; - } - - public CreatorImplementation getSizeCreator() { - return sizeCreator; - } - - public interface CreatorImplementation { - /** - * Creates a new inventory - * @param gui The InventoryGui instance - * @param who The player to create the inventory for - * @param t The size or type of the inventory - * @return The created inventory - */ - Inventory create(InventoryGui gui, HumanEntity who, T t); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/libraries/inventorygui/StaticGuiElement.java b/plugin/src/main/java/net/momirealms/customfishing/libraries/inventorygui/StaticGuiElement.java deleted file mode 100644 index c6a23269..00000000 --- a/plugin/src/main/java/net/momirealms/customfishing/libraries/inventorygui/StaticGuiElement.java +++ /dev/null @@ -1,159 +0,0 @@ -package net.momirealms.customfishing.libraries.inventorygui; - -/* - * Copyright 2017 Max Lee (https://github.com/Phoenix616) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -import org.bukkit.entity.HumanEntity; -import org.bukkit.inventory.ItemStack; - -/** - * Represents a simple element in a gui to which an action can be assigned. - * If you want the item to change on click you have to do that yourself. - */ -public class StaticGuiElement extends GuiElement { - private ItemStack item; - private int number; - private String[] text; - - /** - * Represents an element in a gui - * @param slotChar The character to replace in the gui setup string - * @param item The item this element displays - * @param number The number, 1 will not display the number - * @param action The action to run when the player clicks on this element - * @param text The text to display on this element, placeholders are automatically - * replaced, see for a list of the - * placeholder variables. Empty text strings are also filter out, use - * a single space if you want to add an empty line!
- * If it's not set/empty the item's default name will be used - * @throws IllegalArgumentException If the number is below 1 or above the max stack count (currently 64) - */ - public StaticGuiElement(char slotChar, ItemStack item, int number, Action action, String... text) throws IllegalArgumentException { - super(slotChar, action); - this.item = item; - this.text = text; - setNumber(number); - } - - /** - * Represents an element in a gui - * @param slotChar The character to replace in the gui setup string - * @param item The item this element displays - * @param action The action to run when the player clicks on this element - * @param text The text to display on this element, placeholders are automatically - * replaced, see {@link InventoryGui#replaceVars} for a list of the - * placeholder variables. Empty text strings are also filter out, use - * a single space if you want to add an empty line!
- * If it's not set/empty the item's default name will be used - */ - public StaticGuiElement(char slotChar, ItemStack item, Action action, String... text) { - this(slotChar, item, item != null ? item.getAmount() : 1, action, text); - } - - /** - * Represents an element in a gui that doesn't have any action when clicked - * @param slotChar The character to replace in the gui setup string - * @param item The item this element displays - * @param text The text to display on this element, placeholders are automatically - * replaced, see {@link InventoryGui#replaceVars} for a list of the - * placeholder variables. Empty text strings are also filter out, use - * a single space if you want to add an empty line!
- * If it's not set/empty the item's default name will be used - */ - public StaticGuiElement(char slotChar, ItemStack item, String... text) { - this(slotChar, item, item != null ? item.getAmount() : 1, null, text); - } - - - /** - * Set the item that is displayed by this element - * @param item The item that should be displayed by this element - */ - public void setItem(ItemStack item) { - this.item = item; - } - - /** - * Get the raw item displayed by this element which was passed to the constructor or set with {@link #setItem(ItemStack)}. - * This item will not have the amount or text applied! Use {@link #getItem(HumanEntity, int)} for that! - * @return The raw item - */ - public ItemStack getRawItem() { - return item; - } - - @Override - public ItemStack getItem(HumanEntity who, int slot) { - if (item == null) { - return null; - } - ItemStack clone = item.clone(); - gui.setItemText(who, clone, getText()); - if (number > 0 && number <= 64) { - clone.setAmount(number); - } - return clone; - } - - /** - * Set this element's display text. If this is an empty array the item's name will be displayed - * @param text The text to display on this element, placeholders are automatically - * replaced, see {@link InventoryGui#replaceVars} for a list of the - * placeholder variables. Empty text strings are also filter out, use - * a single space if you want to add an empty line!
- * If it's not set/empty the item's default name will be used - */ - public void setText(String... text) { - this.text = text; - } - - /** - * Get the text that this element displays - * @return The text that is displayed on this element - */ - public String[] getText() { - return text; - } - - /** - * Set the number that this element should display (via the Item's amount) - * @param number The number, 1 will not display the number - * @return true if the number was set; false if it was below 1 or above 64 - */ - public boolean setNumber(int number) { - if (number < 1 || number > 64) { - this.number = 1; - return false; - } - this.number = number; - return true; - } - - /** - * Get the number that this element should display - * @return The number (item amount) that this element currently has - */ - public int getNumber() { - return number; - } - -} diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/action/ActionManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/action/ActionManagerImpl.java index 102c5f20..c05f4508 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/action/ActionManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/action/ActionManagerImpl.java @@ -20,6 +20,7 @@ import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; public class ActionManagerImpl implements ActionManager { @@ -43,6 +44,8 @@ public class ActionManagerImpl implements ActionManager { this.registerPluginExpAction(); this.registerTitleAction(); this.registerActionBarAction(); + this.registerCloseInvAction(); + this.registerDelayedAction(); } @Override @@ -153,6 +156,13 @@ public class ActionManagerImpl implements ActionManager { }); } + private void registerCloseInvAction() { + registerAction("close-inv", (args, chance) -> condition -> { + if (Math.random() > chance) return; + condition.getPlayer().closeInventory(); + }); + } + private void registerActionBarAction() { registerAction("actionbar", (args, chance) -> { String text = (String) args; @@ -217,6 +227,34 @@ public class ActionManagerImpl implements ActionManager { }); } + private void registerDelayedAction() { + registerAction("delay", (args, chance) -> { + List actions = new ArrayList<>(); + int delay; + if (args instanceof ConfigurationSection section) { + delay = section.getInt("delay", 1); + ConfigurationSection actionSection = section.getConfigurationSection("action"); + if (actionSection != null) { + for (Map.Entry entry : actionSection.getValues(false).entrySet()) { + if (entry.getValue() instanceof ConfigurationSection innerSection) { + actions.add(getAction(innerSection)); + } + } + } + } else { + delay = 1; + } + return condition -> { + if (Math.random() > chance) return; + plugin.getScheduler().runTaskSyncLater(() -> { + for (Action action : actions) { + action.trigger(condition); + } + }, condition.getLocation(), delay * 50L, TimeUnit.MILLISECONDS); + }; + }); + } + private void registerTitleAction() { registerAction("title", (args, chance) -> { if (args instanceof ConfigurationSection section) { diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/FishingManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/FishingManagerImpl.java index db2abfda..130957cb 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/FishingManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/fishing/FishingManagerImpl.java @@ -2,6 +2,7 @@ package net.momirealms.customfishing.mechanic.fishing; import com.destroystokyo.paper.event.player.PlayerJumpEvent; import de.tr7zw.changeme.nbtapi.NBTItem; +import io.papermc.paper.event.player.AsyncChatEvent; import net.momirealms.customfishing.CustomFishingPluginImpl; import net.momirealms.customfishing.api.common.Pair; import net.momirealms.customfishing.api.event.LavaFishingEvent; @@ -33,16 +34,14 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.inventory.InventoryPickupItemEvent; -import org.bukkit.event.player.PlayerAttemptPickupItemEvent; -import org.bukkit.event.player.PlayerFishEvent; -import org.bukkit.event.player.PlayerQuitEvent; -import org.bukkit.event.player.PlayerSwapHandItemsEvent; +import org.bukkit.event.player.*; import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.Nullable; import java.util.HashMap; import java.util.Objects; +import java.util.Optional; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -174,6 +173,16 @@ public class FishingManagerImpl implements Listener, FishingManager { } } + @EventHandler + public void onChat(AsyncPlayerChatEvent event) { + if (event.isCancelled()) return; + GamingPlayer gamingPlayer = gamingPlayerMap.get(event.getPlayer().getUniqueId()); + if (gamingPlayer != null) { + if (gamingPlayer.onChat(event.getMessage())) + event.setCancelled(true); + } + } + @Override public boolean removeHook(UUID uuid) { FishHook hook = hookCacheMap.remove(uuid); @@ -185,6 +194,11 @@ public class FishingManagerImpl implements Listener, FishingManager { } } + @Override + public Optional getHook(UUID uuid) { + return Optional.ofNullable(hookCacheMap.get(uuid)); + } + public void selectState(PlayerFishEvent event) { if (event.isCancelled()) return; switch (event.getState()) { diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/game/GameManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/game/GameManagerImpl.java index f8404a7e..967d39f3 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/game/GameManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/game/GameManagerImpl.java @@ -341,11 +341,9 @@ public class GameManagerImpl implements GameManager { + FontUtils.surroundWithFont(fishImage, font) + OffsetUtils.getOffsetChars((int) (barEffectiveWidth - fish_position - fishIconWidth + 1)) ; - AdventureManagerImpl.getInstance().sendTitle( player, - tip != null && !played ? tip : - title.replace("{progress}", progress[(int) ((hold_time / time_requirement) * progress.length)]) + tip != null && !played ? tip : title.replace("{progress}", progress[(int) ((hold_time / time_requirement) * progress.length)]) , bar, 0, diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketDynamicGUIElement.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketDynamicGUIElement.java new file mode 100644 index 00000000..3d5ca174 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketDynamicGUIElement.java @@ -0,0 +1,14 @@ +package net.momirealms.customfishing.mechanic.market; + +import org.bukkit.inventory.ItemStack; + +public class MarketDynamicGUIElement extends MarketGUIElement { + + public MarketDynamicGUIElement(char symbol, ItemStack itemStack) { + super(symbol, itemStack); + } + + public void setItemStack(ItemStack itemStack) { + super.itemStack = itemStack; + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketGUI.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketGUI.java new file mode 100644 index 00000000..93fb1eea --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketGUI.java @@ -0,0 +1,140 @@ +package net.momirealms.customfishing.mechanic.market; + +import net.momirealms.customfishing.adventure.AdventureManagerImpl; +import net.momirealms.customfishing.api.mechanic.market.MarketGUIHolder; +import net.momirealms.customfishing.api.util.InventoryUtils; +import net.momirealms.customfishing.api.util.LogUtils; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; + +public class MarketGUI { + + private final HashMap itemsCharMap; + private final HashMap itemsSlotMap; + private final Inventory inventory; + private final MarketManagerImpl manager; + private final Player owner; + + public MarketGUI(MarketManagerImpl manager, Player player) { + this.manager = manager; + this.owner = player; + this.itemsCharMap = new HashMap<>(); + this.itemsSlotMap = new HashMap<>(); + var holder = new MarketGUIHolder(); + this.inventory = InventoryUtils.createInventory( + holder, + manager.getLayout().length * 9, + AdventureManagerImpl.getInstance().getComponentFromMiniMessage(manager.getTitle()) + ); + holder.setInventory(this.inventory); + } + + private void init() { + int line = 0; + for (String content : manager.getLayout()) { + if (content.length() != 9) { + LogUtils.warn("Please make sure that GUI layout has 9 elements in each row"); + return; + } + for (int index = 0; index < 9; index++) { + char symbol = content.charAt(index); + MarketGUIElement element = itemsCharMap.get(symbol); + element.addSlot(index + line * 9); + itemsSlotMap.put(index + line * 9, element); + } + line++; + } + for (Map.Entry entry : itemsSlotMap.entrySet()) { + this.inventory.setItem(entry.getKey(), entry.getValue().getItemStack().clone()); + } + } + + public MarketGUI addElement(MarketGUIElement... elements) { + for (MarketGUIElement element : elements) { + itemsCharMap.put(element.getSymbol(), element); + } + return this; + } + + public MarketGUI build() { + init(); + return this; + } + + public void show(Player player) { + if (player != owner) return; + player.openInventory(inventory); + } + + @Nullable + public MarketGUIElement getElement(int slot) { + return itemsSlotMap.get(slot); + } + + @Nullable + public MarketGUIElement getElement(char slot) { + return itemsCharMap.get(slot); + } + + public void refresh() { + double totalWorth = getTotalWorth(); + if (totalWorth <= 0) { + addElement(new MarketDynamicGUIElement( + manager.getFunctionSlot(), + manager.getFunctionIconDenyBuilder().build(owner, + Map.of("{worth}", String.format("%.2f", totalWorth) + ,"{player}", owner.getName()) + ) + )); + } else { + addElement(new MarketDynamicGUIElement( + manager.getFunctionSlot(), + manager.getFunctionIconAllowBuilder().build(owner, + Map.of("{worth}", String.format("%.2f", totalWorth) + ,"{player}", owner.getName()) + ) + )); + } + for (Map.Entry entry : itemsSlotMap.entrySet()) { + if (entry.getValue() instanceof MarketDynamicGUIElement dynamicGUIElement) { + this.inventory.setItem(entry.getKey(), dynamicGUIElement.getItemStack().clone()); + } + } + } + + public double getTotalWorth() { + double money = 0d; + MarketGUIElement itemElement = getElement(manager.getItemSlot()); + if (itemElement == null) { + LogUtils.warn("No item slot available. Please check if GUI layout contains the item slot symbol."); + return money; + } + for (int slot : itemElement.getSlots()) { + money += manager.getItemPrice(this.inventory.getItem(slot)); + } + return money; + } + + public Inventory getInventory() { + return inventory; + } + + public void clearWorthyItems() { + MarketGUIElement itemElement = getElement(manager.getItemSlot()); + if (itemElement == null) { + return; + } + for (int slot : itemElement.getSlots()) { + double money = manager.getItemPrice(inventory.getItem(slot)); + if (money != 0) { + inventory.setItem(slot, new ItemStack(Material.AIR)); + } + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketGUIElement.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketGUIElement.java new file mode 100644 index 00000000..a8715b48 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketGUIElement.java @@ -0,0 +1,35 @@ +package net.momirealms.customfishing.mechanic.market; + +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.List; + +public class MarketGUIElement { + + private final char symbol; + private final List slots; + protected ItemStack itemStack; + + public MarketGUIElement(char symbol, ItemStack itemStack) { + this.symbol = symbol; + this.itemStack = itemStack; + this.slots = new ArrayList<>(); + } + + public void addSlot(int slot) { + slots.add(slot); + } + + public char getSymbol() { + return symbol; + } + + public ItemStack getItemStack() { + return itemStack; + } + + public List getSlots() { + return slots; + } +} diff --git a/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketManagerImpl.java b/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketManagerImpl.java index 36df2e26..a06265fe 100644 --- a/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketManagerImpl.java +++ b/plugin/src/main/java/net/momirealms/customfishing/mechanic/market/MarketManagerImpl.java @@ -1,29 +1,33 @@ package net.momirealms.customfishing.mechanic.market; import de.tr7zw.changeme.nbtapi.NBTItem; -import net.momirealms.customfishing.adventure.AdventureManagerImpl; import net.momirealms.customfishing.api.CustomFishingPlugin; +import net.momirealms.customfishing.api.data.user.OnlineUser; import net.momirealms.customfishing.api.manager.MarketManager; +import net.momirealms.customfishing.api.mechanic.item.BuildableItem; import net.momirealms.customfishing.api.mechanic.item.ItemBuilder; -import net.momirealms.customfishing.api.mechanic.market.MarketGUI; -import net.momirealms.customfishing.libraries.inventorygui.InventoryGui; -import net.momirealms.customfishing.libraries.inventorygui.StaticGuiElement; -import net.momirealms.customfishing.mechanic.item.ItemManagerImpl; +import net.momirealms.customfishing.api.mechanic.market.MarketGUIHolder; import net.momirealms.customfishing.util.ConfigUtils; import net.objecthunter.exp4j.Expression; import net.objecthunter.exp4j.ExpressionBuilder; +import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; -import java.util.Calendar; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; -public class MarketManagerImpl implements MarketManager { +public class MarketManagerImpl implements MarketManager, Listener { private final CustomFishingPlugin plugin; private final HashMap priceMap; @@ -32,14 +36,36 @@ public class MarketManagerImpl implements MarketManager { private String formula; private final HashMap decorativeIcons; private char itemSlot; + private char functionSlot; + private BuildableItem functionIconAllowBuilder; + private BuildableItem functionIconDenyBuilder; + private double earningLimit; + private ConcurrentHashMap marketGUIMap; public MarketManagerImpl(CustomFishingPlugin plugin) { this.plugin = plugin; this.priceMap = new HashMap<>(); this.decorativeIcons = new HashMap<>(); + this.marketGUIMap = new ConcurrentHashMap<>(); } public void load() { + this.loadConfig(); + Bukkit.getPluginManager().registerEvents(this, plugin); + } + + + public void unload() { + HandlerList.unregisterAll(this); + this.priceMap.clear(); + this.decorativeIcons.clear(); + } + + public void disable() { + unload(); + } + + private void loadConfig() { YamlConfiguration config = plugin.getConfig("market.yml"); this.layout = config.getStringList("layout").toArray(new String[0]); this.title = config.getString("title", "market.title"); @@ -62,38 +88,74 @@ public class MarketManagerImpl implements MarketManager { } } - public void unload() { - this.priceMap.clear(); - this.decorativeIcons.clear(); - } - - public void disable() { - unload(); - } - public void openMarketGUI(Player player) { - player.closeInventory(); + MarketGUI gui = new MarketGUI(this, player); - InventoryGui gui = new InventoryGui( - plugin, - new MarketGUI(), - AdventureManagerImpl.getInstance().getComponentFromMiniMessage(title), - layout - ); + } - gui.setCloseAction(close -> { - var elements = gui.getElement(itemSlot); + @EventHandler + public void onClickInv(InventoryClickEvent event) { + if (event.isCancelled()) + return; + Inventory clickedInv = event.getClickedInventory(); + if (clickedInv == null) + return; + HumanEntity human = event.getWhoClicked(); + if (!(clickedInv.getHolder() instanceof MarketGUIHolder holder)) + return; - - return false; - }); - - for (Map.Entry entry : decorativeIcons.entrySet()) { - gui.addElement(new StaticGuiElement( - entry.getKey(), - ((ItemManagerImpl.CFBuilder) entry.getValue()).build() - )); + MarketGUI gui = marketGUIMap.get(human.getUniqueId()); + if (gui == null) { + event.setCancelled(true); + human.closeInventory(); + return; } + + int slot = event.getSlot(); + MarketGUIElement element = gui.getElement(slot); + if (element == null) { + event.setCancelled(true); + return; + } + + if (element.getSymbol() == itemSlot) { + plugin.getScheduler().runTaskSyncLater(gui::refresh, human.getLocation(), 50, TimeUnit.MILLISECONDS); + return; + } + + if (element.getSymbol() == functionSlot) { + event.setCancelled(true); + double worth = gui.getTotalWorth(); + if (worth > 0) { + double remainingToEarn = getRemainingMoneyToEarn(human.getUniqueId()); + if (remainingToEarn < worth) { + + } else { + gui.clearWorthyItems(); + this.setRemainMoneyToEarn(human.getUniqueId(), remainingToEarn + worth); + } + } + plugin.getScheduler().runTaskSyncLater(gui::refresh, human.getLocation(), 50, TimeUnit.MILLISECONDS); + return; + } + + event.setCancelled(true); + } + + public double getRemainingMoneyToEarn(UUID uuid) { + OnlineUser user = plugin.getStorageManager().getOnlineUser(uuid); + if (user == null) { + return -1; + } + return earningLimit - user.getEarningData().earnings; + } + + public void setRemainMoneyToEarn(UUID uuid, double remaining) { + OnlineUser user = plugin.getStorageManager().getOnlineUser(uuid); + if (user == null) { + return; + } + user.getEarningData().earnings = remaining; } @Override @@ -133,4 +195,28 @@ public class MarketManagerImpl implements MarketManager { .setVariable("size", size); return expression.evaluate(); } + + public char getItemSlot() { + return itemSlot; + } + + public char getFunctionSlot() { + return functionSlot; + } + + public String[] getLayout() { + return layout; + } + + public String getTitle() { + return title; + } + + public BuildableItem getFunctionIconAllowBuilder() { + return functionIconAllowBuilder; + } + + public BuildableItem getFunctionIconDenyBuilder() { + return functionIconDenyBuilder; + } } diff --git a/plugin/src/main/resources/market.yml b/plugin/src/main/resources/market.yml index 091d1540..ac95ee05 100644 --- a/plugin/src/main/resources/market.yml +++ b/plugin/src/main/resources/market.yml @@ -9,10 +9,10 @@ layout: - 'AIIIIIIIA' - 'AAAABAAAA' -# Price formula (For customfishing loots) +# Price formula (For CustomFishing loots) price-formula: '{base} + {bonus} * {size}' -# Item price (For vanilla items & other plugin items) +# Item price (For vanilla items & other plugin items that have CustomModelData) item-price: # Vanilla Items COD: 10 @@ -22,30 +22,39 @@ item-price: # PAPER (CustomModelData: 999) PAPER:999: 5 -# Functional icons +# Slots to put items in +item-slot: + symbol: 'I' + +# Functional icon functional-icons: + symbol: 'B' sell-icon: - symbol: 'B' material: IRON_BLOCK display: - name: '<#00CED1>Sell the fish' + name: '<#00CED1>Ship the fish' lore: - - 'You will gain {money}$' + - 'You will get {money}$ by selling the fish' action: - message: + sound_action: + type: sound + message_action: type: message - text: 'You earned {money}$ from selling the fish! You can still gain {remains}$ from selling fish today' - chance: 1.0 + value: 'You earned {money}$ by selling the fish! You can still get {remains}$ from market today' + command_action: + type: command + value: 'money give {player} {money}' deny-icon: - symbol: 'B' material: REDSTONE_BLOCK display: - name: 'Denied' + name: 'Denied trade' lore: - 'Nothing to sell!' - item-slot: - symbol: 'I' + action: + sound_action: + type: sound +# Decorative icons decorative-icons: glass-pane: symbol: 'A'