Skip to content

Commit 1499918

Browse files
committed
feat: anvil gui
1 parent 5a06fd0 commit 1499918

File tree

3 files changed

+135
-0
lines changed

3 files changed

+135
-0
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package cc.modlabs.kpaper.inventory
2+
3+
import cc.modlabs.kpaper.inventory._internal.AnvilListener
4+
import dev.fruxz.stacked.text
5+
import net.kyori.adventure.text.Component
6+
import org.bukkit.Bukkit
7+
import org.bukkit.entity.Player
8+
import org.bukkit.event.inventory.InventoryClickEvent
9+
import org.bukkit.event.inventory.InventoryType
10+
import org.bukkit.inventory.Inventory
11+
import org.bukkit.inventory.ItemStack
12+
13+
/**
14+
* Enum class representing the slots in an anvil inventory.
15+
* @param index The index of the slot in the inventory.
16+
*/
17+
enum class AnvilSlot(val index: Int) {
18+
INPUT_LEFT(0),
19+
INPUT_RIGHT(1),
20+
OUTPUT(2)
21+
}
22+
23+
/**
24+
* Configuration for an anvil slot.
25+
* @param item The item to display in the slot.
26+
* @param onClick The action to perform when the slot is clicked.
27+
* @param consumeItem Whether to consume the item when the slot is clicked.
28+
*/
29+
data class SlotConfig(
30+
var item: ItemStack? = null,
31+
var onClick: ((player: Player, event: InventoryClickEvent) -> Unit)? = null,
32+
var consumeItem: Boolean = true
33+
)
34+
35+
class AnvilGUI {
36+
var title: String = "Anvil"
37+
val slotConfigs: MutableMap<AnvilSlot, SlotConfig> = mutableMapOf()
38+
var onComplete: ((player: Player, input: Component?) -> Unit)? = null
39+
var onClose: ((player: Player) -> Unit)? = null
40+
41+
fun slot(slot: AnvilSlot, builder: SlotBuilder.() -> Unit) {
42+
val slotBuilder = SlotBuilder().apply(builder)
43+
slotConfigs[slot] = SlotConfig(
44+
item = slotBuilder.item,
45+
onClick = slotBuilder.onClick,
46+
consumeItem = slotBuilder.consumeItem
47+
)
48+
}
49+
}
50+
51+
class SlotBuilder {
52+
var item: ItemStack? = null
53+
var onClick: ((player: Player, event: InventoryClickEvent) -> Unit)? = null
54+
var consumeItem: Boolean = true
55+
}
56+
57+
fun Player.openAnvilGUI(builder: AnvilGUI.() -> Unit) {
58+
val gui = AnvilGUI().apply(builder)
59+
val inv: Inventory = Bukkit.createInventory(null, InventoryType.ANVIL, text(gui.title))
60+
gui.slotConfigs.forEach { (slot, config) ->
61+
config.item?.let { inv.setItem(slot.index, it) }
62+
}
63+
AnvilListener.registerGUI(this, inv, gui)
64+
this.openInventory(inv)
65+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package cc.modlabs.kpaper.inventory._internal
2+
3+
import cc.modlabs.kpaper.inventory.AnvilGUI
4+
import cc.modlabs.kpaper.inventory.AnvilSlot
5+
import org.bukkit.entity.Player
6+
import org.bukkit.event.EventHandler
7+
import org.bukkit.event.Listener
8+
import org.bukkit.event.inventory.InventoryClickEvent
9+
import org.bukkit.event.inventory.InventoryCloseEvent
10+
import org.bukkit.inventory.Inventory
11+
12+
object AnvilListener : Listener {
13+
private val activeGUIs = mutableMapOf<Inventory, Pair<Player, AnvilGUI>>()
14+
15+
fun registerGUI(player: Player, inventory: Inventory, gui: AnvilGUI) {
16+
activeGUIs[inventory] = player to gui
17+
}
18+
19+
fun unregisterGUI(inventory: Inventory) {
20+
activeGUIs.remove(inventory)
21+
}
22+
23+
// Listen for clicks in the anvil GUI
24+
@EventHandler
25+
fun onInventoryClick(event: InventoryClickEvent) {
26+
val pair = activeGUIs[event.inventory] ?: return
27+
val (player, gui) = pair
28+
29+
// Ensure the click comes from the intended player
30+
if (event.whoClicked != player) return
31+
32+
// Check if the clicked slot is one of our defined slots
33+
val clickedSlot = AnvilSlot.entries.find { it.index == event.slot }
34+
if (clickedSlot != null) {
35+
val slotConfig = gui.slotConfigs[clickedSlot]
36+
// Call the custom onClick handler if one is defined
37+
slotConfig?.onClick?.invoke(player, event)
38+
// For the output slot, if no custom onClick is provided, use the default onComplete callback
39+
if (clickedSlot == AnvilSlot.OUTPUT && slotConfig?.onClick == null) {
40+
val clickedItem = event.currentItem
41+
// Typically the "renamed" text is taken from the item's display name
42+
val inputText = clickedItem?.itemMeta?.displayName()
43+
gui.onComplete?.invoke(player, inputText)
44+
player.closeInventory()
45+
unregisterGUI(event.inventory)
46+
return
47+
}
48+
// Cancel the event if consumeItem is true (default) to prevent default item transfer
49+
if (slotConfig?.consumeItem != false) {
50+
event.isCancelled = true
51+
}
52+
} else {
53+
// Cancel clicks outside our defined slots
54+
event.isCancelled = true
55+
}
56+
}
57+
58+
// Clean up when the anvil inventory is closed
59+
@EventHandler
60+
fun onInventoryClose(event: InventoryCloseEvent) {
61+
if (activeGUIs.containsKey(event.inventory)) {
62+
val (player, gui) = activeGUIs[event.inventory]!!
63+
gui.onClose?.invoke(player)
64+
unregisterGUI(event.inventory)
65+
}
66+
}
67+
}

src/main/kotlin/cc/modlabs/kpaper/main/KPlugin.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package cc.modlabs.kpaper.main
22

33
import cc.modlabs.kpaper.event.CustomEventListener
4+
import cc.modlabs.kpaper.inventory._internal.AnvilListener
45
import cc.modlabs.kpaper.inventory._internal.ItemClickListener
6+
import org.bukkit.Bukkit
57
import org.bukkit.plugin.java.JavaPlugin
68

79
/**
@@ -46,6 +48,7 @@ abstract class KPlugin : JavaPlugin() {
4648

4749
final override fun onEnable() {
4850
if (isFeatureEnabled(Feature.ITEM_CLICK)) {
51+
Bukkit.getPluginManager().registerEvents(AnvilListener, this)
4952
ItemClickListener.load()
5053
}
5154
if (isFeatureEnabled(Feature.CUSTOM_EVENTS)) {

0 commit comments

Comments
 (0)