diff --git a/README.md b/README.md index 8f5dd96..a53ff85 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ repositories { } dependencies { - implementation("com.github.DebitCardz:mc-chestui-plus:1.3.1") + implementation("com.github.DebitCardz:mc-chestui-plus:1.3.2") } ``` ### Maven @@ -34,7 +34,7 @@ dependencies { com.github.DebitCardz mc-chestui-plus - 1.3.1 + 1.3.2 ``` diff --git a/build.gradle.kts b/build.gradle.kts index 4744fe9..305771c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ val githubActor = project.findProperty("gpr.user") as String? ?: System.getenv(" val githubToken = project.findProperty("gpr.key") as String? ?: System.getenv("GITHUB_TOKEN") group = "me.tech" -version = "1.3.1" +version = "1.3.2" repositories { mavenCentral() diff --git a/src/main/kotlin/me/tech/mcchestui/GUI.kt b/src/main/kotlin/me/tech/mcchestui/GUI.kt index 151ca91..cd7fbda 100644 --- a/src/main/kotlin/me/tech/mcchestui/GUI.kt +++ b/src/main/kotlin/me/tech/mcchestui/GUI.kt @@ -8,7 +8,9 @@ package me.tech.mcchestui import me.tech.mcchestui.item.GUIItem -import me.tech.mcchestui.listeners.GUIListener +import me.tech.mcchestui.listeners.* +import me.tech.mcchestui.listeners.item.* +import me.tech.mcchestui.listeners.hotbar.* import me.tech.mcchestui.utils.GUICloseEvent import me.tech.mcchestui.utils.GUIDragItemEvent import me.tech.mcchestui.utils.GUIItemPickupEvent @@ -138,7 +140,17 @@ class GUI( */ internal var templateSlots = mutableMapOf() - private val guiListener = GUIListener(this) + private val eventListeners = listOf( + GUISlotClickListener(this), + + GUIItemPickupListener(this), + GUIItemPlaceListener(this), + GUIItemDragListener(this), + + GUIHotbarListener(this), + + GUICloseListener(this) + ) /** * Define weather the [GUI] has been unregistered. @@ -147,8 +159,10 @@ class GUI( internal var unregistered = false init { - plugin.server.pluginManager - .registerEvents(guiListener, plugin) + eventListeners.forEach { + plugin.server.pluginManager + .registerEvents(it, plugin) + } } /** @@ -308,7 +322,7 @@ class GUI( * Mark a [GUI] as unregistered and remove its [Listener]. */ fun unregister() { - HandlerList.unregisterAll(guiListener) + eventListeners.forEach(HandlerList::unregisterAll) unregistered = true } } \ No newline at end of file diff --git a/src/main/kotlin/me/tech/mcchestui/listeners/GUICloseListener.kt b/src/main/kotlin/me/tech/mcchestui/listeners/GUICloseListener.kt new file mode 100644 index 0000000..deae2a3 --- /dev/null +++ b/src/main/kotlin/me/tech/mcchestui/listeners/GUICloseListener.kt @@ -0,0 +1,28 @@ +package me.tech.mcchestui.listeners + +import me.tech.mcchestui.GUI +import org.bukkit.event.EventHandler +import org.bukkit.event.inventory.InventoryCloseEvent + +internal class GUICloseListener(gui: GUI) : GUIEventListener(gui) { + @EventHandler + internal fun InventoryCloseEvent.onClose() { + if(!gui.isBukkitInventory(inventory)) { + return + } + + gui.onCloseInventory?.let { uiEvent -> + uiEvent(this, player) + } + + if(gui.singleInstance) { + return + } + + if(inventory.viewers.size > 1) { + return + } + + gui.unregister() + } +} \ No newline at end of file diff --git a/src/main/kotlin/me/tech/mcchestui/listeners/GUIEventListener.kt b/src/main/kotlin/me/tech/mcchestui/listeners/GUIEventListener.kt new file mode 100644 index 0000000..f1f5ea4 --- /dev/null +++ b/src/main/kotlin/me/tech/mcchestui/listeners/GUIEventListener.kt @@ -0,0 +1,45 @@ +package me.tech.mcchestui.listeners + +import me.tech.mcchestui.GUI +import org.bukkit.event.Listener +import org.bukkit.event.inventory.InventoryAction + +internal abstract class GUIEventListener( + protected val gui: GUI +) : Listener { + companion object { + /** + * All actions related to placing an item. + */ + @JvmStatic + protected val PLACE_ACTIONS = listOf( + InventoryAction.PLACE_ONE, + InventoryAction.PLACE_SOME, + InventoryAction.PLACE_ALL, + InventoryAction.SWAP_WITH_CURSOR + ) + + /** + * All actions related to using hotkeys to place/pickup + * an item. + */ + @JvmStatic + protected val HOTBAR_ACTIONS = listOf( + InventoryAction.HOTBAR_SWAP, + InventoryAction.HOTBAR_MOVE_AND_READD + ) + + /** + * All actions related to picking up an item. + */ + @JvmStatic + protected val PICKUP_ACTIONS = listOf( + InventoryAction.PICKUP_ONE, + InventoryAction.PICKUP_SOME, + InventoryAction.PICKUP_HALF, + InventoryAction.PICKUP_ALL, + InventoryAction.SWAP_WITH_CURSOR, + InventoryAction.COLLECT_TO_CURSOR + ) + } +} \ No newline at end of file diff --git a/src/main/kotlin/me/tech/mcchestui/listeners/GUIListener.kt b/src/main/kotlin/me/tech/mcchestui/listeners/GUIListener.kt deleted file mode 100644 index a19840e..0000000 --- a/src/main/kotlin/me/tech/mcchestui/listeners/GUIListener.kt +++ /dev/null @@ -1,337 +0,0 @@ -package me.tech.mcchestui.listeners - -import me.tech.mcchestui.GUI -import org.bukkit.Material -import org.bukkit.entity.Player -import org.bukkit.event.Cancellable -import org.bukkit.event.EventHandler -import org.bukkit.event.Listener -import org.bukkit.event.inventory.* -import org.bukkit.inventory.Inventory - -internal class GUIListener(private val gui: GUI): Listener { - /** - * When a [GUI.Slot] is clicked in the [Inventory]. - * - * This listener should not be used to modify the [Cancellable] of - * the bukkit event, handle that in the onPickup event. - */ - @EventHandler - internal fun InventoryClickEvent.onSlotClick() { - // ensure clicked inventory is ui inventory. - if(!gui.isBukkitInventory(clickedInventory)) { - return - } - - val guiSlot = gui.slots.getOrNull(slot) - ?: return // handle cancellation of task in onPlace. - - guiSlot.onClick?.let { uiEvent -> - uiEvent(this, whoClicked as Player) - } - } - - @EventHandler - internal fun InventoryClickEvent.onItemPlace() { - // ensure top inventory is ui inventory. - if(!gui.isBukkitInventory(inventory)) { - return - } - - if( - action == InventoryAction.MOVE_TO_OTHER_INVENTORY - && isShiftClick - && !gui.isBukkitInventory(clickedInventory) // make sure its incoming. - ) { - if(!gui.allowItemPlacement) { - isCancelled = true - return - } - } else if( - action in PLACE_ACTIONS - && gui.isBukkitInventory(clickedInventory) - ) { - if(!gui.allowItemPlacement) { - isCancelled = true - return - } - } else { - return - } - - val guiSlot = gui.slots.getOrNull(slot) - if(guiSlot != null) { - if(!guiSlot.allowPickup) { - isCancelled = true - return - } - } - - val itemStack = cursor - ?: return - if(itemStack.type == Material.AIR) { - return - } - - gui.onPlaceItem?.let { uiEvent -> - uiEvent(this, whoClicked as Player, itemStack, slot).let { outcome -> - isCancelled = outcome - } - } - } - - @EventHandler - internal fun InventoryDragEvent.onItemDrag() { - if(!gui.isBukkitInventory(inventory)) { - return - } - - if(!gui.allowItemPlacement) { - isCancelled = true - return - } - - // single item drag handler, move to onPlaceItem event. - if(rawSlots.size == 1 && newItems.size == 1) { - val slotIndex = rawSlots.first() - - val guiSlot = gui.slots.getOrNull(slotIndex) - if(guiSlot != null) { - if(!guiSlot.allowPickup) { - isCancelled = true - return - } - } - - val itemStack = newItems.values.firstOrNull() - ?: return - if(itemStack.type == Material.AIR) { - return - } - - gui.onPlaceItem?.let { uiEvent -> - // TODO: Look for better method of handling this. - val fakeEvent = InventoryClickEvent( - view, - InventoryType.SlotType.CONTAINER, - slotIndex, - if(type == DragType.SINGLE) ClickType.RIGHT else ClickType.LEFT, - InventoryAction.PLACE_ALL - ) - - uiEvent(fakeEvent, whoClicked as Player, itemStack, slotIndex).let { outcome -> - isCancelled = outcome - } - } - return - } - - if(!gui.allowItemDrag) { - isCancelled = true - return - } - - // ensure our drag doesn't override a slot. - if(newItems.any { (index, _) -> - val guiSlot = gui.slots.getOrNull(index) - ?: return@any false - - !guiSlot.allowPickup - }) { - isCancelled = true - return - } - - gui.onDragItem?.let { uiEvent -> - uiEvent(this, whoClicked as Player, newItems).let { outcome -> - isCancelled = outcome - } - } - } - - @EventHandler(ignoreCancelled = true) - internal fun InventoryClickEvent.onHotBarSwitchToUIInventory() { - if(!gui.isBukkitInventory(inventory)) { - return - } - - // player inv -> gui inv - if( - action !in HOTBAR_ACTIONS - || click != ClickType.NUMBER_KEY - ) { - return - } - - if(!gui.allowHotBarSwap) { - if(gui.isBukkitInventory(clickedInventory)) { - isCancelled = true - } - - return - } - - if(clickedInventory == whoClicked.inventory) { - return - } - - val itemStack = if(click == ClickType.NUMBER_KEY) { - whoClicked.inventory.getItem(hotbarButton) - } else { - currentItem - } ?: return - - if(itemStack.type == Material.AIR) { - return - } - - gui.onPlaceItem?.let { uiEvent -> - uiEvent(this, whoClicked as Player, itemStack, slot).let { outcome -> - isCancelled = outcome - } - } - } - - @EventHandler(ignoreCancelled = true) - internal fun InventoryClickEvent.onHotBarSwitchToPlayerInventory() { - if(!gui.isBukkitInventory(inventory)) { - return - } - - // gui inv -> player inv - if( - action !in HOTBAR_ACTIONS - || click != ClickType.NUMBER_KEY - ) { - return - } - - if(!gui.allowHotBarSwap) { - if(gui.isBukkitInventory(clickedInventory)) { - isCancelled = true - } - - return - } - - if(clickedInventory == whoClicked.inventory) { - return - } - - val itemStack = currentItem - ?: return - if(itemStack.type == Material.AIR) { - return - } - - gui.onPickupItem?.let { uiEvent -> - uiEvent(this, whoClicked as Player, itemStack, slot).let { outcome -> - isCancelled = outcome - } - } - } - - @EventHandler - internal fun InventoryClickEvent.onItemPickup() { - // ensure top inventory is ui inventory. - if(!gui.isBukkitInventory(inventory)) { - return - } - - // handle shift click - if( - action == InventoryAction.MOVE_TO_OTHER_INVENTORY - && isShiftClick - && gui.isBukkitInventory(clickedInventory) // make sure its outgoing. - ) { - if(!gui.allowItemPickup) { - isCancelled = true - return - } - } else if( - action in PICKUP_ACTIONS - && gui.isBukkitInventory(clickedInventory) - ) { - if(!gui.allowItemPickup) { - isCancelled = true - return - } - } else { - return - } - - val guiSlot = gui.slots.getOrNull(slot) - if(guiSlot != null) { - if(!guiSlot.allowPickup) { - isCancelled = true - return - } - } - - val itemStack = currentItem - ?: return - if(itemStack.type == Material.AIR) { - return - } - - gui.onPickupItem?.let { uiEvent -> - uiEvent(this, whoClicked as Player, itemStack, slot).let { outcome -> - isCancelled = outcome - } - } - } - - @EventHandler - internal fun InventoryCloseEvent.onClose() { - if(!gui.isBukkitInventory(inventory)) { - return - } - - gui.onCloseInventory?.let { uiEvent -> - uiEvent(this, player) - } - - if(gui.singleInstance) { - return - } - - if(inventory.viewers.size > 1) { - return - } - - gui.unregister() - } - - companion object { - /** - * All actions related to placing an item. - */ - private val PLACE_ACTIONS = listOf( - InventoryAction.PLACE_ONE, - InventoryAction.PLACE_SOME, - InventoryAction.PLACE_ALL, - InventoryAction.SWAP_WITH_CURSOR - ) - - /** - * All actions related to using hotkeys to place/pickup - * an item. - */ - private val HOTBAR_ACTIONS = listOf( - InventoryAction.HOTBAR_SWAP, - InventoryAction.HOTBAR_MOVE_AND_READD - ) - - /** - * All actions related to picking up an item. - */ - private val PICKUP_ACTIONS = listOf( - InventoryAction.PICKUP_ONE, - InventoryAction.PICKUP_SOME, - InventoryAction.PICKUP_HALF, - InventoryAction.PICKUP_ALL, - InventoryAction.SWAP_WITH_CURSOR, - InventoryAction.COLLECT_TO_CURSOR - ) - } -} \ No newline at end of file diff --git a/src/main/kotlin/me/tech/mcchestui/listeners/GUISlotClickListener.kt b/src/main/kotlin/me/tech/mcchestui/listeners/GUISlotClickListener.kt new file mode 100644 index 0000000..2b65971 --- /dev/null +++ b/src/main/kotlin/me/tech/mcchestui/listeners/GUISlotClickListener.kt @@ -0,0 +1,23 @@ +package me.tech.mcchestui.listeners + +import me.tech.mcchestui.GUI +import org.bukkit.entity.Player +import org.bukkit.event.EventHandler +import org.bukkit.event.inventory.InventoryClickEvent + +internal class GUISlotClickListener(gui: GUI) : GUIEventListener(gui) { + @EventHandler + internal fun InventoryClickEvent.slotClick() { + // ensure clicked inventory is ui inventory. + if(!gui.isBukkitInventory(clickedInventory)) { + return + } + + val guiSlot = gui.slots.getOrNull(slot) + ?: return // handle cancellation of task in onPlace. + + guiSlot.onClick?.let { uiEvent -> + uiEvent(this, whoClicked as Player) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/me/tech/mcchestui/listeners/hotbar/GUIHotbarListener.kt b/src/main/kotlin/me/tech/mcchestui/listeners/hotbar/GUIHotbarListener.kt new file mode 100644 index 0000000..98e1dfb --- /dev/null +++ b/src/main/kotlin/me/tech/mcchestui/listeners/hotbar/GUIHotbarListener.kt @@ -0,0 +1,93 @@ +package me.tech.mcchestui.listeners.hotbar + +import me.tech.mcchestui.GUI +import me.tech.mcchestui.listeners.GUIEventListener +import org.bukkit.Material +import org.bukkit.entity.Player +import org.bukkit.event.EventHandler +import org.bukkit.event.inventory.ClickType +import org.bukkit.event.inventory.InventoryClickEvent + +internal class GUIHotbarListener(gui: GUI) : GUIEventListener(gui) { + @EventHandler(ignoreCancelled = true) + internal fun InventoryClickEvent.hotBarSwitchToUIInventory() { + if(!gui.isBukkitInventory(inventory)) { + return + } + + // player inv -> gui inv + if( + action !in HOTBAR_ACTIONS + || click != ClickType.NUMBER_KEY + ) { + return + } + + if(!gui.allowHotBarSwap) { + if(gui.isBukkitInventory(clickedInventory)) { + isCancelled = true + } + + return + } + + if(clickedInventory == whoClicked.inventory) { + return + } + + val itemStack = if(click == ClickType.NUMBER_KEY) { + whoClicked.inventory.getItem(hotbarButton) + } else { + currentItem + } ?: return + + if(itemStack.type == Material.AIR) { + return + } + + gui.onPlaceItem?.let { uiEvent -> + uiEvent(this, whoClicked as Player, itemStack, slot).let { outcome -> + isCancelled = outcome + } + } + } + + @EventHandler(ignoreCancelled = true) + internal fun InventoryClickEvent.hotBarSwitchToPlayerInventory() { + if(!gui.isBukkitInventory(inventory)) { + return + } + + // gui inv -> player inv + if( + action !in HOTBAR_ACTIONS + || click != ClickType.NUMBER_KEY + ) { + return + } + + if(!gui.allowHotBarSwap) { + if(gui.isBukkitInventory(clickedInventory)) { + isCancelled = true + } + + return + } + + if(clickedInventory == whoClicked.inventory) { + return + } + + val itemStack = currentItem + ?: return + if(itemStack.type == Material.AIR) { + return + } + + gui.onPickupItem?.let { uiEvent -> + uiEvent(this, whoClicked as Player, itemStack, slot).let { outcome -> + isCancelled = outcome + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/me/tech/mcchestui/listeners/item/GUIItemDragListener.kt b/src/main/kotlin/me/tech/mcchestui/listeners/item/GUIItemDragListener.kt new file mode 100644 index 0000000..4afbab1 --- /dev/null +++ b/src/main/kotlin/me/tech/mcchestui/listeners/item/GUIItemDragListener.kt @@ -0,0 +1,79 @@ +package me.tech.mcchestui.listeners.item + +import me.tech.mcchestui.GUI +import me.tech.mcchestui.listeners.GUIEventListener +import org.bukkit.Material +import org.bukkit.entity.Player +import org.bukkit.event.EventHandler +import org.bukkit.event.inventory.* + +internal class GUIItemDragListener(gui: GUI) : GUIEventListener(gui) { + @EventHandler + internal fun InventoryDragEvent.itemDrag() { + if(!gui.isBukkitInventory(inventory)) { + return + } + + if(!gui.allowItemPlacement) { + isCancelled = true + return + } + + // single item drag handler, move to onPlaceItem event. + if(rawSlots.size == 1 && newItems.size == 1) { + val slotIndex = rawSlots.first() + + val guiSlot = gui.slots.getOrNull(slotIndex) + if(guiSlot != null) { + if(!guiSlot.allowPickup) { + isCancelled = true + return + } + } + + val itemStack = newItems.values.firstOrNull() + ?: return + if(itemStack.type == Material.AIR) { + return + } + + gui.onPlaceItem?.let { uiEvent -> + // TODO: Look for better method of handling this. + val fakeEvent = InventoryClickEvent( + view, + InventoryType.SlotType.CONTAINER, + slotIndex, + if(type == DragType.SINGLE) ClickType.RIGHT else ClickType.LEFT, + InventoryAction.PLACE_ALL + ) + + uiEvent(fakeEvent, whoClicked as Player, itemStack, slotIndex).let { outcome -> + isCancelled = outcome + } + } + return + } + + if(!gui.allowItemDrag) { + isCancelled = true + return + } + + // ensure our drag doesn't override a slot. + if(newItems.any { (index, _) -> + val guiSlot = gui.slots.getOrNull(index) + ?: return@any false + + !guiSlot.allowPickup + }) { + isCancelled = true + return + } + + gui.onDragItem?.let { uiEvent -> + uiEvent(this, whoClicked as Player, newItems).let { outcome -> + isCancelled = outcome + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/me/tech/mcchestui/listeners/item/GUIItemPickupListener.kt b/src/main/kotlin/me/tech/mcchestui/listeners/item/GUIItemPickupListener.kt new file mode 100644 index 0000000..5b0c3a7 --- /dev/null +++ b/src/main/kotlin/me/tech/mcchestui/listeners/item/GUIItemPickupListener.kt @@ -0,0 +1,61 @@ +package me.tech.mcchestui.listeners.item + +import me.tech.mcchestui.GUI +import me.tech.mcchestui.listeners.GUIEventListener +import org.bukkit.Material +import org.bukkit.entity.Player +import org.bukkit.event.EventHandler +import org.bukkit.event.inventory.InventoryAction +import org.bukkit.event.inventory.InventoryClickEvent + +internal class GUIItemPickupListener(gui: GUI): GUIEventListener(gui) { + @EventHandler + internal fun InventoryClickEvent.onItemPickup() { + // ensure top inventory is ui inventory. + if(!gui.isBukkitInventory(inventory)) { + return + } + + // handle shift click + if( + action == InventoryAction.MOVE_TO_OTHER_INVENTORY + && isShiftClick + && gui.isBukkitInventory(clickedInventory) // make sure its outgoing. + ) { + if(!gui.allowItemPickup) { + isCancelled = true + return + } + } else if( + action in PICKUP_ACTIONS + && gui.isBukkitInventory(clickedInventory) + ) { + if(!gui.allowItemPickup) { + isCancelled = true + return + } + } else { + return + } + + val guiSlot = gui.slots.getOrNull(slot) + if(guiSlot != null) { + if(!guiSlot.allowPickup) { + isCancelled = true + return + } + } + + val itemStack = currentItem + ?: return + if(itemStack.type == Material.AIR) { + return + } + + gui.onPickupItem?.let { uiEvent -> + uiEvent(this, whoClicked as Player, itemStack, slot).let { outcome -> + isCancelled = outcome + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/me/tech/mcchestui/listeners/item/GUIItemPlaceListener.kt b/src/main/kotlin/me/tech/mcchestui/listeners/item/GUIItemPlaceListener.kt new file mode 100644 index 0000000..915d98b --- /dev/null +++ b/src/main/kotlin/me/tech/mcchestui/listeners/item/GUIItemPlaceListener.kt @@ -0,0 +1,60 @@ +package me.tech.mcchestui.listeners.item + +import me.tech.mcchestui.GUI +import me.tech.mcchestui.listeners.GUIEventListener +import org.bukkit.Material +import org.bukkit.entity.Player +import org.bukkit.event.EventHandler +import org.bukkit.event.inventory.InventoryAction +import org.bukkit.event.inventory.InventoryClickEvent + +internal class GUIItemPlaceListener(gui: GUI) : GUIEventListener(gui) { + @EventHandler + internal fun InventoryClickEvent.itemPlace() { + // ensure top inventory is ui inventory. + if(!gui.isBukkitInventory(inventory)) { + return + } + + if( + action == InventoryAction.MOVE_TO_OTHER_INVENTORY + && isShiftClick + && !gui.isBukkitInventory(clickedInventory) // make sure its incoming. + ) { + if(!gui.allowItemPlacement) { + isCancelled = true + return + } + } else if( + action in PLACE_ACTIONS + && gui.isBukkitInventory(clickedInventory) + ) { + if(!gui.allowItemPlacement) { + isCancelled = true + return + } + } else { + return + } + + val guiSlot = gui.slots.getOrNull(slot) + if(guiSlot != null) { + if(!guiSlot.allowPickup) { + isCancelled = true + return + } + } + + val itemStack = cursor + ?: return + if(itemStack.type == Material.AIR) { + return + } + + gui.onPlaceItem?.let { uiEvent -> + uiEvent(this, whoClicked as Player, itemStack, slot).let { outcome -> + isCancelled = outcome + } + } + } +} \ No newline at end of file