From f91160727542df1752a8ae4562ddc655e2aca376 Mon Sep 17 00:00:00 2001 From: slohth Date: Mon, 9 Sep 2024 07:13:31 +0100 Subject: [PATCH] Created nice GUI builder :) --- README.md | 52 +++++++++++++ src/main/kotlin/gg/flyte/twilight/gui/GUI.kt | 79 ++++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 src/main/kotlin/gg/flyte/twilight/gui/GUI.kt diff --git a/README.md b/README.md index b6d067e..27f576a 100644 --- a/README.md +++ b/README.md @@ -266,6 +266,58 @@ repeat(5, 10, TimeUnit.SECONDS, true) { > Twilight `repeat` conflicting with Kotlin's `repeat`? As an alternative, you can use `repeatingTask`. +### GUI Builder +Creating GUI's can be an incredibly long and tedious process, however, in Twilight we offer a clean and efficient way to build GUIs. + +Here's an example of a simple, bog-standard GUI: +```kotlin +val basicGui = gui(Component.text("Click the apple!"), 9) { + set(4, ItemStack(Material.APPLE)) { + isCancelled = true + viewer.sendMessage(Component.text("This is an apple!")) + } +} +player.openInventory(basicGui) +``` +As you can see, setting the click event logic has never been easier. You can reference the player at any time using `viewer`. + +Here's an example of a gui implementing the pattern feature, making it much easier to visualise: +```kotlin +val complexGui = gui { + pattern( + "#########", + "# S #", + "#########" + ) + + set('S', ItemStack(Material.PLAYER_HEAD).apply { + val meta = itemMeta as SkullMeta + meta.displayName(Component.text("${viewer.name}'s Head!")) + meta.owningPlayer = viewer + itemMeta = meta + }) { + isCancelled = true + viewer.sendMessage(Component.text("Ouch!", NamedTextColor.RED)) + } + + set('#', ItemStack(Material.LIGHT_GRAY_STAINED_GLASS_PANE).apply { + val meta = itemMeta + meta.displayName(Component.empty()) + itemMeta = meta + }) { isCancelled = true } +} +player.openInventory(complexGui) +``` +You can, of course, also implement GUIs for other inventory types: +```kotlin +val dropperGui = gui(Component.text("Title"), 9, InventoryType.DROPPER) { + // You can also set multiple indexes at once + set(listOf(1, 3, 4, 5, 7), ItemStack(Material.GRAY_STAINED_GLASS_PANE)) +} +player.openInventory(dropperGui) +``` +To open the gui, simply do `Player#openInventory(GUI)` like shown in the examples. + ## Databases ### MongoDB Currently we have support for MongoDB. To configure it, you can take one of two routes: diff --git a/src/main/kotlin/gg/flyte/twilight/gui/GUI.kt b/src/main/kotlin/gg/flyte/twilight/gui/GUI.kt new file mode 100644 index 0000000..af3b215 --- /dev/null +++ b/src/main/kotlin/gg/flyte/twilight/gui/GUI.kt @@ -0,0 +1,79 @@ +package gg.flyte.twilight.gui + +import gg.flyte.twilight.event.event +import net.kyori.adventure.text.Component +import org.bukkit.Bukkit +import org.bukkit.entity.Player +import org.bukkit.event.inventory.InventoryClickEvent +import org.bukkit.event.inventory.InventoryType +import org.bukkit.inventory.ItemStack + +inline fun gui( + title: Component = Component.text("Custom GUI"), + size: Int = 27, + type: InventoryType = InventoryType.CHEST, + noinline context: GUI.() -> Unit +): GUI { + return GUI(title, size, type, context) +} + +class GUI(val title: Component, val size: Int, val type: InventoryType, val context: GUI.() -> Unit) { + + private val inventory = when (type) { + InventoryType.CHEST -> Bukkit.createInventory(null, size, title) + else -> Bukkit.createInventory(null, type, title) + } + + private val keySlot = mutableMapOf>() + private val slotAction = mutableMapOf Unit>() + + lateinit var viewer: Player + + private val clickEvent = event { + if (inventory != this@GUI.inventory) return@event + slotAction[slot]?.invoke(this) + } + + fun pattern(vararg pattern: String) { + for ((index, value) in pattern.joinToString("").withIndex()) { + keySlot.getOrPut(value) { mutableListOf() }.add(index) + } + } + + @JvmName("setSlot") + fun set(slot: Int, item: ItemStack, action: InventoryClickEvent.() -> Unit = {}) { + inventory.setItem(slot, item) + slotAction[slot] = action + } + + @JvmName("setSlots") + fun set(indexes: Collection, item: ItemStack, action: InventoryClickEvent.() -> Unit = {}) { + indexes.forEach { set(it, item, action) } + } + + @JvmName("setKey") + fun set(key: Char, item: ItemStack, action: InventoryClickEvent.() -> Unit = {}) { + keySlot[key]?.forEach { set(it, item, action) } + } + + @JvmName("setKeys") + fun set(keys: Collection, item: ItemStack, action: InventoryClickEvent.() -> Unit = {}) { + keys.forEach { set(it, item, action) } + } + + private fun remove() { + keySlot.clear() + slotAction.clear() + inventory.clear() + clickEvent.unregister() + } + + companion object { + fun Player.openInventory(gui: GUI) { + gui.viewer = this + gui.context.invoke(gui) + openInventory(gui.inventory) + } + } + +} \ No newline at end of file