diff --git a/Dockerfile b/Dockerfile index 752823e1..bf0606c5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM gradle:8.3.0 as build +FROM gradle:8.7.0 as build WORKDIR /app @@ -6,7 +6,7 @@ COPY . . RUN --mount=type=cache,target=/root/.gradle gradle build --parallel --no-daemon -FROM openjdk:20-slim as serverBuilder +FROM openjdk:21-slim as serverBuilder WORKDIR /app diff --git a/gradle.properties b/gradle.properties index e69de29b..df832267 100644 --- a/gradle.properties +++ b/gradle.properties @@ -0,0 +1,2 @@ +osshrToken=3gSBY7kZ +osshrPassword=LPx4MlixFR1eJ21Oe3g1CAtmVl6vFfoCQW0Fw+VNzYdc \ No newline at end of file diff --git a/src/main/kotlin/de/staticred/kia/KIA.kt b/src/main/kotlin/de/staticred/kia/KIA.kt index 78dfad98..95ba2669 100644 --- a/src/main/kotlin/de/staticred/kia/KIA.kt +++ b/src/main/kotlin/de/staticred/kia/KIA.kt @@ -3,6 +3,7 @@ package de.staticred.kia import de.staticred.kia.events.InventoryClickListener import de.staticred.kia.events.InventoryDragItemListener import de.staticred.kia.events.InventoryOpenCloseListener +import de.staticred.kia.events.PlayerInteractListener import de.staticred.kia.example.InventoryExample import org.bukkit.Bukkit import org.bukkit.plugin.java.JavaPlugin @@ -11,11 +12,16 @@ import org.bukkit.plugin.java.JavaPlugin * Plugin class of KIA */ object KIA { + /** + * The paper plugin kia is attached to + */ lateinit var plugin: JavaPlugin private set /** * Create new loaded instance of KIA + * @param javaPlugin plugin instance KIA will use to attach its eventlisteners + * @param exampleCommand by default false. If true, will register the /kia example command */ fun create( javaPlugin: JavaPlugin, @@ -37,5 +43,6 @@ object KIA { plugin.server.pluginManager.registerEvents(InventoryClickListener(), plugin) plugin.server.pluginManager.registerEvents(InventoryOpenCloseListener(), plugin) plugin.server.pluginManager.registerEvents(InventoryDragItemListener(), plugin) + plugin.server.pluginManager.registerEvents(PlayerInteractListener(), plugin) } } diff --git a/src/main/kotlin/de/staticred/kia/events/PlayerInteractListener.kt b/src/main/kotlin/de/staticred/kia/events/PlayerInteractListener.kt new file mode 100644 index 00000000..590d5f30 --- /dev/null +++ b/src/main/kotlin/de/staticred/kia/events/PlayerInteractListener.kt @@ -0,0 +1,25 @@ +package de.staticred.kia.events + +import de.staticred.kia.inventory.item.RegisteredKItem +import org.bukkit.event.EventHandler +import org.bukkit.event.Listener +import org.bukkit.event.block.Action +import org.bukkit.event.player.PlayerInteractEvent + +class PlayerInteractListener : Listener { + @EventHandler + fun onPlayerInteract(event: PlayerInteractEvent) { + val item = event.item ?: return + val player = event.player + + if (item !is RegisteredKItem) return + + if (event.action == Action.LEFT_CLICK_AIR || event.action == Action.LEFT_CLICK_BLOCK) { + item.leftClicked(player, event) + } + + if (event.action == Action.RIGHT_CLICK_AIR || event.action == Action.RIGHT_CLICK_BLOCK) { + item.rightClicked(player, event) + } + } +} diff --git a/src/main/kotlin/de/staticred/kia/example/InventoryExample.kt b/src/main/kotlin/de/staticred/kia/example/InventoryExample.kt index 2969fcbd..10fe01e5 100644 --- a/src/main/kotlin/de/staticred/kia/example/InventoryExample.kt +++ b/src/main/kotlin/de/staticred/kia/example/InventoryExample.kt @@ -4,7 +4,9 @@ import de.staticred.kia.inventory.builder.animation import de.staticred.kia.inventory.builder.kInventory import de.staticred.kia.inventory.builder.kItem import de.staticred.kia.inventory.extensions.openInventory +import de.staticred.kia.inventory.extensions.setHotbarItem import de.staticred.kia.util.rows +import net.kyori.adventure.text.Component import net.kyori.adventure.text.event.ClickEvent import net.kyori.adventure.text.minimessage.MiniMessage import org.bukkit.Material @@ -14,42 +16,73 @@ import org.bukkit.entity.Player import org.bukkit.event.inventory.InventoryType import java.util.concurrent.TimeUnit -class InventoryExample: Command("kia") { - - - override fun execute(sender: CommandSender, command: String, args: Array?): Boolean { +class InventoryExample : Command("kia") { + override fun execute( + sender: CommandSender, + command: String, + args: Array?, + ): Boolean { if (sender !is Player) return false val miniMessage = MiniMessage.miniMessage() - val text = miniMessage.deserialize("--------------------------------- ") - .append { miniMessage.deserialize("\nKIA - Kotlin Inventory API") } - .append { miniMessage.deserialize("\nGitHub: https://github.com/StaticFX/KIA ") } - .append { miniMessage.deserialize("\nBy: StaticFX / Devin ") } - .append { miniMessage.deserialize("\nClick to open example inventory") }.clickEvent( - ClickEvent.clickEvent(ClickEvent.Action.RUN_COMMAND, "/kia inv")) - .append { miniMessage.deserialize("\n---------------------------------") } - - val inventory = kInventory(sender, 3.rows, InventoryType.CHEST) { - title = miniMessage.deserialize("KIA - Kotlin Inventory API") - - openingAnimation = animation(27, 50, TimeUnit.MILLISECONDS) { - onAnimationFrame { frame -> - setItem(frame, kItem(Material.GRAY_STAINED_GLASS_PANE) { - setDisplayName(miniMessage.deserialize("|")) - } ) - } - - onEnd { - setItem(1, 4, kItem(Material.PAPER) { - setDisplayName(miniMessage.deserialize("Thanks for using KIA!")) - }) - } + val text = + miniMessage + .deserialize("--------------------------------- ") + .append { miniMessage.deserialize("\nKIA - Kotlin Inventory API") } + .append { miniMessage.deserialize("\nGitHub: https://github.com/StaticFX/KIA ") } + .append { miniMessage.deserialize("\nBy: StaticFX / Devin ") } + .append { + miniMessage.deserialize( + "\nClick to open example inventory", + ) + }.clickEvent( + ClickEvent.clickEvent(ClickEvent.Action.RUN_COMMAND, "/kia inv"), + ).append { miniMessage.deserialize("\n---------------------------------") } + + val inventory = + kInventory(sender, 3.rows, InventoryType.CHEST) { + title = miniMessage.deserialize("KIA - Kotlin Inventory API") + + openingAnimation = + animation(27, 50, TimeUnit.MILLISECONDS) { + onAnimationFrame { frame -> + setItem( + frame, + kItem(Material.GRAY_STAINED_GLASS_PANE) { + setDisplayName(miniMessage.deserialize("|")) + }, + ) + } + + onEnd { + setItem( + 1, + 4, + kItem(Material.PAPER) { + setDisplayName(miniMessage.deserialize("Thanks for using KIA!")) + }, + ) + } + } } - } if (args != null && args.isNotEmpty()) { if (args[0] == "inv") { sender.openInventory(inventory) + } else if (args[0] == "items") { + val item = + kItem(Material.PAPER) { + setDisplayName(Component.text("Left or right click me")) + onLeftClick { clicker, _ -> + clicker.sendMessage("You left clicked") + } + + onRightClick { clicker, _ -> + clicker.sendMessage("You right clicked") + } + } + + sender.setHotbarItem(0, item) } } else { sender.sendMessage(text) @@ -57,4 +90,4 @@ class InventoryExample: Command("kia") { return true } -} \ No newline at end of file +} diff --git a/src/main/kotlin/de/staticred/kia/inventory/extensions/PlayerExtensions.kt b/src/main/kotlin/de/staticred/kia/inventory/extensions/PlayerExtensions.kt index f9a8721a..5a26523b 100644 --- a/src/main/kotlin/de/staticred/kia/inventory/extensions/PlayerExtensions.kt +++ b/src/main/kotlin/de/staticred/kia/inventory/extensions/PlayerExtensions.kt @@ -1,6 +1,7 @@ package de.staticred.kia.inventory.extensions import de.staticred.kia.inventory.KInventory +import de.staticred.kia.inventory.item.KItem import org.bukkit.entity.Player /** @@ -15,4 +16,21 @@ fun Player.openInventory(inventory: KInventory) { } openInventory(inventory.toBukkitInventory()) -} \ No newline at end of file +} + +/** + * Sets the given item at the given slot in the players hotbar + * @param slot number between 0-8 referring to the players hotbar + * @param item the item to set + * @throws IllegalArgumentException when slot is out of bounds + */ +fun Player.setHotbarItem( + slot: Int, + item: KItem, +) { + if (slot !in 0..8) { + throw IllegalArgumentException("Slot must be between 0 and 8") + } + + inventory.setItem(slot, item.toItemStack()) +} diff --git a/src/main/kotlin/de/staticred/kia/inventory/item/RegisteredKItem.kt b/src/main/kotlin/de/staticred/kia/inventory/item/RegisteredKItem.kt index 31931c2e..486fd425 100644 --- a/src/main/kotlin/de/staticred/kia/inventory/item/RegisteredKItem.kt +++ b/src/main/kotlin/de/staticred/kia/inventory/item/RegisteredKItem.kt @@ -3,18 +3,24 @@ package de.staticred.kia.inventory.item import de.staticred.kia.inventory.KInventory import de.staticred.kia.util.Identifiable import org.bukkit.entity.Player +import org.bukkit.event.player.PlayerInteractEvent import java.util.* /** * Models a registered KItems * - * Registered KItems will, when initialized, add a UUID to their NBT tags, so they can be identified later again. + * Registered KItems will, when initialized + * add a UUID to their NBT tags, so they can be identified later again. + * + * This allows the item to be tracked inside bukkit events, which allows + * for event listeners on this item * * @See ItemManager * @since 1.0.2 */ -interface RegisteredKItem: KItem, Identifiable { - +interface RegisteredKItem : + KItem, + Identifiable { /** * Whether the item can be clicked while inside an animation or not */ @@ -35,5 +41,54 @@ interface RegisteredKItem: KItem, Identifiable { * @param player who clicked the item * @param kInventory the inventory the item is inside */ - fun clicked(player: Player, kInventory: KInventory) -} \ No newline at end of file + fun clicked( + player: Player, + kInventory: KInventory, + ) + + /** + * Executed when the player left-clicked with this item + * + * This can occur outside an inventory, therefore the parent might be null + * @see parent + * + * The code block will receive a [PlayerInteractEvent] which is validated + * to have a valid item and a valid click. + * + * @param action code block which is executed + */ + fun onLeftClick(action: RegisteredKItem.(Player, PlayerInteractEvent) -> Unit) + + /** + * Executed when the player right-clicked with this item + * + * This can occur outside an inventory, therefore the parent might be null + * @see parent + * + * The code block will receive a [PlayerInteractEvent] which is validated + * to have a valid item and a valid click. + * + * @param action code block which is executed + */ + fun onRightClick(action: RegisteredKItem.(Player, PlayerInteractEvent) -> Unit) + + /** + * When the item has been left-clicked by a player + * @param player who clicked + * @param event the bukkit event + */ + fun leftClicked( + player: Player, + event: PlayerInteractEvent, + ) + + /** + * When the item has been right-clicked by a player + * @param player who clicked + * @param event the bukkit event + */ + fun rightClicked( + player: Player, + event: PlayerInteractEvent, + ) +} diff --git a/src/main/kotlin/de/staticred/kia/inventory/item/RegisteredKItemImpl.kt b/src/main/kotlin/de/staticred/kia/inventory/item/RegisteredKItemImpl.kt index feb169e4..bf95386b 100644 --- a/src/main/kotlin/de/staticred/kia/inventory/item/RegisteredKItemImpl.kt +++ b/src/main/kotlin/de/staticred/kia/inventory/item/RegisteredKItemImpl.kt @@ -6,6 +6,7 @@ import de.staticred.kia.inventory.KInventory import org.bukkit.Material import org.bukkit.NamespacedKey import org.bukkit.entity.Player +import org.bukkit.event.player.PlayerInteractEvent import org.bukkit.inventory.ItemStack import org.bukkit.persistence.PersistentDataType import java.util.* @@ -25,6 +26,8 @@ class RegisteredKItemImpl( override var clickableInAnimation: Boolean = true private val clickListeners = mutableListOf Unit>() + private val leftClickListeners = mutableListOf Unit>() + private val rightClickListeners = mutableListOf Unit>() companion object { /** @@ -70,5 +73,27 @@ class RegisteredKItemImpl( parent?.let { clickListeners.forEach { listener -> listener(kInventory, this, player) } } } + override fun onLeftClick(action: RegisteredKItem.(Player, PlayerInteractEvent) -> Unit) { + leftClickListeners += action + } + + override fun onRightClick(action: RegisteredKItem.(Player, PlayerInteractEvent) -> Unit) { + rightClickListeners += action + } + + override fun leftClicked( + player: Player, + event: PlayerInteractEvent, + ) { + leftClickListeners.forEach { it(this, player, event) } + } + + override fun rightClicked( + player: Player, + event: PlayerInteractEvent, + ) { + rightClickListeners.forEach { it(this, player, event) } + } + override fun hasID(): Boolean = true }