From ea8751f65e4eae235d1c3e0815f93104c08aee4e Mon Sep 17 00:00:00 2001 From: Pepich Date: Sun, 17 Sep 2017 15:42:00 +0200 Subject: Initial commit --- src/de/pepich/chestapi/CallbackHandler.java | 9 + src/de/pepich/chestapi/ChestAPI.java | 21 ++ src/de/pepich/chestapi/ClickableInventory.java | 306 +++++++++++++++++++++++++ src/de/pepich/chestapi/DefaultSize.java | 101 ++++++++ 4 files changed, 437 insertions(+) create mode 100644 src/de/pepich/chestapi/CallbackHandler.java create mode 100644 src/de/pepich/chestapi/ChestAPI.java create mode 100644 src/de/pepich/chestapi/ClickableInventory.java create mode 100644 src/de/pepich/chestapi/DefaultSize.java diff --git a/src/de/pepich/chestapi/CallbackHandler.java b/src/de/pepich/chestapi/CallbackHandler.java new file mode 100644 index 0000000..66a4e4a --- /dev/null +++ b/src/de/pepich/chestapi/CallbackHandler.java @@ -0,0 +1,9 @@ +package de.pepich.chestapi; + +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; + +public interface CallbackHandler +{ + public void run(Player player, ClickType type); +} diff --git a/src/de/pepich/chestapi/ChestAPI.java b/src/de/pepich/chestapi/ChestAPI.java new file mode 100644 index 0000000..19ea34f --- /dev/null +++ b/src/de/pepich/chestapi/ChestAPI.java @@ -0,0 +1,21 @@ +package de.pepich.chestapi; + +import org.bukkit.Bukkit; +import org.bukkit.plugin.java.JavaPlugin; + +public class ChestAPI +{ + private static JavaPlugin plugin = null; + + public static void init(JavaPlugin plugin) + { + ChestAPI.plugin = plugin; + } + + public static JavaPlugin getPlugin() + { + if (plugin == null) + plugin = (JavaPlugin) Bukkit.getPluginManager().getPlugins()[0]; + return plugin; + } +} diff --git a/src/de/pepich/chestapi/ClickableInventory.java b/src/de/pepich/chestapi/ClickableInventory.java new file mode 100644 index 0000000..2cc969e --- /dev/null +++ b/src/de/pepich/chestapi/ClickableInventory.java @@ -0,0 +1,306 @@ +package de.pepich.chestapi; + +import java.util.HashMap; + +import javax.annotation.Nullable; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; + +public class ClickableInventory implements Listener +{ + private int open = 0; + private final String title; + private DefaultSize size; + private HashMap handlers = new HashMap(); + private CallbackHandler background; + private CallbackHandler fallback; + private CallbackHandler close; + + private Inventory inventory; + private final InventoryHolder holder; + + /** Creates a new ClickableInventory with the given name.
+ * The ClickableInventory will be created with a DefaultSize as specified by {@link DefaultSize#DYNAMIC_AUTO(int, int) DYNAMIC_AUTO(9, 54)}. + * + * @param name The name to be displayed. */ + public ClickableInventory(final String title) + { + this(title, DefaultSize.DYNAMIC_AUTO(9, 54)); + } + + /** Creates a new ClickableInventory with the given name and the specified DefaultSize. + * + * @param name The name to be displayed. + * @param size The DefaultSize for the inventory to be used. */ + public ClickableInventory(final String title, final DefaultSize size) + { + this.size = size; + this.title = title; + holder = new CustomHolder(this); + if (size.allowSquareShape() && size.getPreferredSize() == 9) + inventory = Bukkit.createInventory(holder, InventoryType.DROPPER, title); + else + inventory = Bukkit.createInventory(holder, size.getPreferredSize(), title); + } + + /** Displays the inventory. + * + * @param player The player that is going to see the inventory. */ + public void show(Player player) + { + player.openInventory(inventory); + if (open == 0) + Bukkit.getPluginManager().registerEvents(this, ChestAPI.getPlugin()); + open++; + } + + // ------------------------------------------------------------- // + // -------------------- GETTERS AND SETTERS -------------------- // + // ------------------------------------------------------------- // + // If you want docs on these, go fork off. Getters/Setters. Duh. // + + public String getName() + { + return title; + } + + public int getFirstFree() + { + if (handlers.size() == inventory.getSize()) + return -1; + for (int i = 1; i <= inventory.getSize(); i++) + if (!handlers.containsKey(i)) + return i; + return -1; + } + + public int getLastFree() + { + if (handlers.size() == inventory.getSize()) + return -1; + for (int i = inventory.getSize(); i > 0; i--) + if (!handlers.containsKey(i)) + return i; + return -1; + } + + public int getFirstUsed() + { + if (handlers.size() == inventory.getSize()) + return -1; + for (int i = 1; i <= inventory.getSize(); i++) + if (handlers.containsKey(i)) + return i; + return -1; + } + + public int getLastUsed() + { + if (handlers.size() == inventory.getSize()) + return -1; + for (int i = inventory.getSize(); i > 0; i--) + if (handlers.containsKey(i)) + return i; + return -1; + } + + protected Inventory getInventory() + { + return inventory; + } + + // ------------------------------------------------------------- // + // ----------------------- SETUP METHODS ----------------------- // + // ------------------------------------------------------------- // + + /** Assigns an item and a handler to the given item slot.
+ * Setting a handler to null will NOT remove it, but it will flag the slot as "used", meaning that it will be skipped when searching for the first/last free spot. + * + * @param slot An integer value representing the slot for the ItemStack. Must be any of -998, [-3:max_length].
+ * The value -998 represents background clicks.
+ * The value -3 represents the "close handler", which will be fired when the player closes the inventory.
+ * The value -2 represents the "fallback handler", which will be used if no appropriate handler could be found.
+ * The value -1 represents the last free slot, 0 points to the first free slot. If an actual number is given, the specified slot will be overwritten.
+ * The first slot is numbered with 1, as opposed to starting at 0, due to the additional meaning that 0 has. + * @param item An ItemStack that will be displayed. Set to null if you want to assign functionality to an empty slot. + * @param handler A CallbackHandler which will be notified on click. When the handler is null then the handler will be removed, but the item will still be assigned. + * @throws IllegalArgumentException when the location does not exist or when one of -1 or 0 are specified and the inventory is already full. */ + public void set(int slot, @Nullable final ItemStack item, @Nullable final CallbackHandler handler) + { + if (slot > inventory.getSize()) + resize(slot); + if (slot < -1 || slot > size.getMaxSize()) + throw new IllegalArgumentException( + "The given slot (" + slot + ") is out of bounds (-1, 0, [1:" + size.getMaxSize() + "])"); + else + { + if (slot == -998) + background = handler; + else if (slot == -2) + fallback = handler; + else if (slot == -3) + close = handler; + else + { + if (slot == 0) + slot = getFirstFree(); + else if (slot == -1) + slot = getLastFree(); + if (slot == -1) + throw new IllegalArgumentException("No free spot could be found!"); + + if (item == null) + inventory.remove(inventory.getItem(slot - 1)); + else + inventory.setItem(slot - 1, item); + + handlers.put(slot, handler); + } + } + if (size.doAutoResize()) + resize(); + } + + /** Removes an assigned action from the inventory. Most be any of -1, 0, [1:max_length].
+ * Calling this method will free up a slot again so that it can be filled up by using the "first/last free" selector. + * + * @param slot An integer value representing the slot for the ItemStack. Must be any of -998, [-3:max_length].
+ * The value -998 represents background clicks.
+ * The value -3 represents the "close handler", which will be fired when the player closes the inventory.
+ * The value -2 represents the "fallback handler", which will be used if no appropriate handler could be found.
+ * The value -1 represents the last used slot, 0 points to the first used slot. If an actual number is given, the specified slot will be overwritten.
+ * The first slot is numbered with 1, as opposed to starting at 0, due to the additional meaning that 0 has. + * @throws IllegalArgumentException when the location does not exist. */ + public void remove(int slot) + { + if (slot == -998) + background = null; + else if (slot == -2) + fallback = null; + else if (slot == -3) + close = null; + else + { + if (slot < -1 || slot > size.getMaxSize()) + throw new IllegalArgumentException( + "The given slot (" + slot + ") is out of bounds (-1, 0, [1:" + size.getMaxSize() + "])"); + if (slot == 0) + slot = getFirstUsed(); + else if (slot == -1) + slot = getLastUsed(); + if (slot == -1) + throw new IllegalArgumentException("No free spot could be found!"); + handlers.remove(slot); + inventory.setItem(slot - 1, null); + if (size.doAutoResize()) + resize(); + } + } + + /** This method removes all TRAILING empty lines.
+ * This method will utilize hopper and dropper inventories if possible. Dropper inventories can be disabled by selection a RECTANGLE size constraint.
+ * Can not be used on final sized inventories. + * + * @return the new size. + * @throws IllegalAccessError if the Inventories size is final. */ + public int resize() + { + if (size.isFinalSize()) + throw new IllegalAccessError("Can not resize an inventory with a FINAL size constraint."); + final int target_size = getLastUsed(); + Inventory new_inventory; + if (target_size <= 5) + new_inventory = Bukkit.createInventory(holder, InventoryType.HOPPER, title); + else if (target_size <= 9 && size.allowSquareShape()) + new_inventory = Bukkit.createInventory(holder, InventoryType.DROPPER, title); + else + new_inventory = Bukkit.createInventory(holder, target_size + ((9 - (target_size % 9)) % 9), title); + for (int i = 0; i < new_inventory.getSize() && i < inventory.getSize(); i++) + new_inventory.setItem(i, inventory.getItem(i)); + inventory = new_inventory; + return inventory.getSize(); + } + + /** This method removes all TRAILING empty lines.
+ * This method will utilize hopper and dropper inventories if possible. Dropper inventories can be disabled by selection a RECTANGLE size constraint.
+ * Can not be used on final sized inventories. + * + * @return the new size. + * @throws IllegalAccessError if the Inventories size is final. */ + private void resize(int target_size) + { + if (size.isFinalSize()) + throw new IllegalAccessError("Can not resize an inventory with a FINAL size constraint."); + target_size = Math.max(target_size, getLastUsed()); + Inventory new_inventory; + if (target_size <= 5) + new_inventory = Bukkit.createInventory(holder, InventoryType.HOPPER, title); + else if (target_size <= 9 && size.allowSquareShape()) + new_inventory = Bukkit.createInventory(holder, InventoryType.DROPPER, title); + else + new_inventory = Bukkit.createInventory(holder, target_size + ((9 - (target_size % 9)) % 9), title); + for (int i = 0; i < new_inventory.getSize() && i < inventory.getSize(); i++) + new_inventory.setItem(i, inventory.getItem(i)); + inventory = new_inventory; + } + + // ------------------------------------------------------------- // + // ------------------------- LISTENERS ------------------------- // + // ------------------------------------------------------------- // + + @EventHandler + public void onInventoryClose(InventoryCloseEvent event) + { + if (this.inventory.equals(event.getInventory())) + { + open--; + if (open == 0) + event.getHandlers().unregister(this); + if (close != null) + close.run((Player) event.getPlayer(), ClickType.UNKNOWN); + } + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent event) + { + if (this.inventory.equals(event.getInventory())) + { + event.setCancelled(true); + final int slot = event.getSlot() + 1; + CallbackHandler handler; + if (slot == -998) + handler = background; + else + handler = handlers.get(slot); + if ((handler = (handler == null ? fallback : handler)) != null) + handler.run((Player) event.getWhoClicked(), event.getClick()); + } + } +} + +class CustomHolder implements InventoryHolder +{ + ClickableInventory inv; + + protected CustomHolder(ClickableInventory inv) + { + this.inv = inv; + } + + @Override + public Inventory getInventory() + { + return inv.getInventory(); + } +} diff --git a/src/de/pepich/chestapi/DefaultSize.java b/src/de/pepich/chestapi/DefaultSize.java new file mode 100644 index 0000000..13ae5da --- /dev/null +++ b/src/de/pepich/chestapi/DefaultSize.java @@ -0,0 +1,101 @@ +package de.pepich.chestapi; + +public final class DefaultSize +{ + private int size; + private int max_size = 54; + private boolean allow_square; + private final boolean dynamic; + private final boolean automatic; + + // -------------------------------------------------------------- // + // ------------------------- Variations ------------------------- // + // -------------------------------------------------------------- // + + public static final DefaultSize DYNAMIC_AUTO(final int preferred_size, final int max_size) + { + return new DefaultSize(preferred_size, max_size, true, true, true); + } + + public static final DefaultSize DYNAMIC_AUTO_RECTANGLE(final int preferred_size, final int max_size) + { + return new DefaultSize(preferred_size, max_size, true, true, false); + } + + public static final DefaultSize DYNAMIC_FIXED(final int preferred_size, final int max_size) + { + return new DefaultSize(preferred_size, max_size, true, false, true); + } + + public static final DefaultSize DYNAMIC_FIXED_RECTANGLE(final int preferred_size, final int max_size) + { + return new DefaultSize(preferred_size, max_size, true, false, false); + } + + public static final DefaultSize FINAL_FIXED(final int size) + { + return new DefaultSize(size, size, false, false, true); + } + + public static final DefaultSize FINAL_FIXED_RECTANGLE(final int size) + { + return new DefaultSize(size, size, false, false, false); + } + + // ------------------------------------------------------------- // + // ------------------------ Constructor ------------------------ // + // ------------------------------------------------------------- // + + private DefaultSize(final int size, final int max_size, final boolean dynamic, final boolean automatic, + final boolean allow_square) + { + this.size = size; + this.dynamic = dynamic; + this.max_size = max_size; + this.automatic = automatic; + this.allow_square = allow_square; + } + + // ------------------------------------------------------------- // + // -------------------- GETTERS AND SETTERS -------------------- // + // ------------------------------------------------------------- // + + public int getPreferredSize() + { + return size; + } + + public int getMaxSize() + { + return max_size; + } + + public boolean allowSquareShape() + { + return allow_square; + } + + public boolean doAutoResize() + { + return automatic; + } + + public boolean isFinalSize() + { + return !dynamic; + } + + public void setPreferredSize(final int size) throws IllegalAccessException + { + if (!dynamic) + throw new IllegalAccessException("Instances of FINAL_SIZE can not be modified"); + this.size = size; + } + + public void setMaxSize(final int size) throws IllegalAccessException + { + if (!dynamic) + throw new IllegalAccessException("Instances of FINAL_SIZE can not be modified"); + this.max_size = size; + } +} -- cgit v1.2.3