Overhauled GUI component-based backend to support reactive and static slots
This commit is contained in:
@@ -1,6 +1,12 @@
|
||||
package com.willfp.eco.core.gui.component;
|
||||
|
||||
import com.willfp.eco.core.gui.menu.Menu;
|
||||
import com.willfp.eco.core.gui.slot.FillerSlot;
|
||||
import com.willfp.eco.core.gui.slot.Slot;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
@@ -30,6 +36,28 @@ public interface GUIComponent {
|
||||
* @return The slot, or null if no slot at the location.
|
||||
*/
|
||||
@Nullable
|
||||
Slot getSlotAt(final int row,
|
||||
final int column);
|
||||
default Slot getSlotAt(int row,
|
||||
int column) {
|
||||
return new FillerSlot(new ItemStack(Material.AIR));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the slot at a certain position in the component.
|
||||
* <p>
|
||||
* If your component doesn't use context data (player, menu),
|
||||
* then it will default to the raw slot.
|
||||
*
|
||||
* @param row The row (1-indexed).
|
||||
* @param column The column (1-indexed).
|
||||
* @param player The player.
|
||||
* @param menu The menu.
|
||||
* @return The slot, or null if no slot at the location.
|
||||
*/
|
||||
@Nullable
|
||||
default Slot getSlotAt(int row,
|
||||
int column,
|
||||
@NotNull Player player,
|
||||
@NotNull Menu menu) {
|
||||
return getSlotAt(row, column);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,10 @@ public interface Menu {
|
||||
int getRows();
|
||||
|
||||
/**
|
||||
* Get slot at given row and column.
|
||||
* Get a static slot at a given row and column.
|
||||
* <p>
|
||||
* If the slot at the location is reactive, this will return
|
||||
* an empty slot.
|
||||
*
|
||||
* @param row The row.
|
||||
* @param column The column.
|
||||
@@ -37,6 +40,24 @@ public interface Menu {
|
||||
Slot getSlot(int row,
|
||||
int column);
|
||||
|
||||
/**
|
||||
* Get a slot at a given row and column.
|
||||
* <p>
|
||||
* Defaults to static slot if no reactive slot exists.
|
||||
*
|
||||
* @param row The row.
|
||||
* @param column The column.
|
||||
* @param player The player
|
||||
* @param menu The menu.
|
||||
* @return The slot.
|
||||
*/
|
||||
default Slot getSlot(int row,
|
||||
int column,
|
||||
@NotNull Player player,
|
||||
@NotNull Menu menu) {
|
||||
return this.getSlot(row, column);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the menu title.
|
||||
*
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
package com.willfp.eco.internal.gui.menu
|
||||
|
||||
data class Anchor(
|
||||
val row: Int,
|
||||
val column: Int
|
||||
)
|
||||
@@ -1,12 +1,14 @@
|
||||
package com.willfp.eco.internal.gui.menu
|
||||
|
||||
import com.willfp.eco.core.gui.component.GUIComponent
|
||||
import com.willfp.eco.core.gui.menu.CloseHandler
|
||||
import com.willfp.eco.core.gui.menu.Menu
|
||||
import com.willfp.eco.core.gui.menu.OpenHandler
|
||||
import com.willfp.eco.core.gui.slot.Slot
|
||||
import com.willfp.eco.internal.gui.slot.EmptyFillerSlot
|
||||
import com.willfp.eco.internal.gui.slot.EcoFillerSlot
|
||||
import com.willfp.eco.util.NamespacedKeyUtils
|
||||
import org.bukkit.Bukkit
|
||||
import org.bukkit.Material
|
||||
import org.bukkit.NamespacedKey
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.event.inventory.InventoryCloseEvent
|
||||
@@ -17,18 +19,38 @@ import org.bukkit.persistence.PersistentDataType
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
class EcoMenu(
|
||||
private val rows: Int,
|
||||
private val slots: Map<Anchor, Slot>,
|
||||
private val componentsAtPoints: Map<Anchor, OffsetComponent>,
|
||||
private val title: String,
|
||||
private val onClose: CloseHandler,
|
||||
private val onRender: (Player, Menu) -> Unit,
|
||||
private val onOpen: OpenHandler
|
||||
) : Menu {
|
||||
override fun getSlot(row: Int, column: Int): Slot {
|
||||
private fun getComponent(row: Int, column: Int): OffsetComponent? {
|
||||
if (row < 1 || row > this.rows || column < 1 || column > 9) {
|
||||
return EmptyFillerSlot
|
||||
return emptyOffsetComponent
|
||||
}
|
||||
|
||||
return slots[Anchor(row, column)] ?: EmptyFillerSlot
|
||||
return componentsAtPoints[Anchor(row, column)]
|
||||
}
|
||||
|
||||
override fun getSlot(row: Int, column: Int): Slot {
|
||||
val found = getComponent(row, column) ?: return emptyFillerSlot
|
||||
|
||||
return found.component.getSlotAt(
|
||||
found.rowOffset,
|
||||
found.columnOffset
|
||||
) ?: emptyFillerSlot
|
||||
}
|
||||
|
||||
override fun getSlot(row: Int, column: Int, player: Player, menu: Menu): Slot {
|
||||
val found = getComponent(row, column) ?: return emptyFillerSlot
|
||||
|
||||
return found.component.getSlotAt(
|
||||
found.rowOffset,
|
||||
found.columnOffset,
|
||||
player,
|
||||
menu
|
||||
) ?: emptyFillerSlot
|
||||
}
|
||||
|
||||
override fun open(player: Player): Inventory {
|
||||
@@ -63,6 +85,7 @@ class EcoMenu(
|
||||
return inventory.captiveItems
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java", ReplaceWith("addState(player, key.toString(), value)"))
|
||||
override fun <T : Any, Z : Any> writeData(
|
||||
player: Player,
|
||||
key: NamespacedKey,
|
||||
@@ -70,9 +93,11 @@ class EcoMenu(
|
||||
value: Z
|
||||
) = addState(player, key.toString(), value)
|
||||
|
||||
@Deprecated("Deprecated in Java", ReplaceWith("getState(player, key.toString())"))
|
||||
override fun <T : Any, Z : Any> readData(player: Player, key: NamespacedKey, type: PersistentDataType<T, Z>): T? =
|
||||
getState(player, key.toString())
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun getKeys(player: Player): Set<NamespacedKey> {
|
||||
val inventory = player.openInventory.topInventory.asRenderedInventory() ?: return emptySet()
|
||||
return inventory.state.keys.mapNotNull { NamespacedKeyUtils.fromStringOrNull(it) }.toSet()
|
||||
@@ -109,3 +134,17 @@ class EcoMenu(
|
||||
|
||||
fun runOnRender(player: Player) = onRender(player, this)
|
||||
}
|
||||
|
||||
data class OffsetComponent(
|
||||
val component: GUIComponent,
|
||||
val rowOffset: Int,
|
||||
val columnOffset: Int
|
||||
)
|
||||
|
||||
data class Anchor(
|
||||
val row: Int,
|
||||
val column: Int
|
||||
)
|
||||
|
||||
val emptyFillerSlot = EcoFillerSlot(ItemStack(Material.AIR))
|
||||
val emptyOffsetComponent = OffsetComponent(emptyFillerSlot, 0, 0)
|
||||
|
||||
@@ -64,7 +64,7 @@ class EcoMenuBuilder(private val rows: Int) : MenuBuilder {
|
||||
}
|
||||
|
||||
override fun build(): Menu {
|
||||
val slots = mutableMapOf<Anchor, Slot>()
|
||||
val componentsAtPoints = mutableMapOf<Anchor, OffsetComponent>()
|
||||
|
||||
for (row in (1..rows)) {
|
||||
for (column in (1..9)) {
|
||||
@@ -84,16 +84,20 @@ class EcoMenuBuilder(private val rows: Int) : MenuBuilder {
|
||||
|
||||
val slot = component.getSlotAt(rowOffset, columnOffset)
|
||||
if (slot != null) {
|
||||
slots[Anchor(row, column)] = slot
|
||||
componentsAtPoints[Anchor(row, column)] = OffsetComponent(
|
||||
component,
|
||||
rowOffset,
|
||||
columnOffset
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return EcoMenu(rows, slots, title, onClose, onRender, onOpen)
|
||||
return EcoMenu(rows, componentsAtPoints, title, onClose, onRender, onOpen)
|
||||
}
|
||||
|
||||
init {
|
||||
maskSlots = ListUtils.create2DList(rows, 9)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ class MenuRenderedInventory(
|
||||
for (row in (1..menu.rows)) {
|
||||
for (column in (1..9)) {
|
||||
val bukkit = MenuUtils.rowColumnToSlot(row, column)
|
||||
val item = menu.getSlot(row, column).getItemStack(player)
|
||||
val item = menu.getSlot(row, column, player, menu).getItemStack(player)
|
||||
|
||||
inventory.setItem(bukkit, item)
|
||||
}
|
||||
@@ -36,7 +36,9 @@ class MenuRenderedInventory(
|
||||
captiveItems.clear()
|
||||
for (i in 0 until inventory.size) {
|
||||
val (row, column) = MenuUtils.convertSlotToRowColumn(i)
|
||||
val slot = menu.getSlot(row, column)
|
||||
|
||||
val slot = menu.getSlot(row, column, player, menu)
|
||||
|
||||
if (slot.isCaptive) {
|
||||
val renderedItem = slot.getItemStack(player)
|
||||
val itemStack = inventory.getItem(i) ?: continue
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
package com.willfp.eco.internal.gui.slot
|
||||
|
||||
import org.bukkit.Material
|
||||
import com.willfp.eco.core.gui.slot.functional.SlotHandler
|
||||
import org.bukkit.inventory.ItemStack
|
||||
|
||||
open class EcoFillerSlot(itemStack: ItemStack) : EcoSlot(
|
||||
private val noop = SlotHandler { _, _, _ -> }
|
||||
|
||||
class EcoFillerSlot(itemStack: ItemStack) : EcoSlot(
|
||||
{ _, _ -> itemStack },
|
||||
{ _, _, _ -> },
|
||||
{ _, _, _ -> },
|
||||
{ _, _, _ -> },
|
||||
{ _, _, _ -> },
|
||||
{ _, _, _ -> },
|
||||
noop,
|
||||
noop,
|
||||
noop,
|
||||
noop,
|
||||
noop,
|
||||
{ _, _, prev -> prev }
|
||||
)
|
||||
|
||||
object EmptyFillerSlot : EcoFillerSlot(ItemStack(Material.AIR))
|
||||
|
||||
@@ -29,11 +29,13 @@ class GUIListener(private val plugin: EcoPlugin) : Listener {
|
||||
fun handleSlotClick(event: InventoryClickEvent) {
|
||||
val rendered = event.clickedInventory?.asRenderedInventory() ?: return
|
||||
|
||||
val player = event.whoClicked as? Player ?: return
|
||||
|
||||
val menu = rendered.menu
|
||||
|
||||
val (row, column) = MenuUtils.convertSlotToRowColumn(event.slot)
|
||||
|
||||
menu.getSlot(row, column).handle(event, menu)
|
||||
menu.getSlot(row, column, player, menu).handle(event, menu)
|
||||
|
||||
plugin.scheduler.run { rendered.render() }
|
||||
}
|
||||
@@ -56,7 +58,7 @@ class GUIListener(private val plugin: EcoPlugin) : Listener {
|
||||
|
||||
val (row, column) = MenuUtils.convertSlotToRowColumn(inv.firstEmpty())
|
||||
|
||||
val slot = menu.getSlot(row, column)
|
||||
val slot = menu.getSlot(row, column, player, menu)
|
||||
|
||||
if (!slot.isCaptive) {
|
||||
event.isCancelled = true
|
||||
|
||||
Reference in New Issue
Block a user