Reworked GUI backend to be component-based

This commit is contained in:
Auxilor
2022-09-29 14:10:41 +01:00
parent e667537404
commit ab8c946914
6 changed files with 67 additions and 86 deletions

View File

@@ -3,7 +3,6 @@ package com.willfp.eco.core.gui.menu;
import com.willfp.eco.core.gui.component.GUIComponent;
import com.willfp.eco.core.gui.slot.FillerMask;
import com.willfp.eco.core.gui.slot.Slot;
import org.apache.commons.lang3.Validate;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.jetbrains.annotations.NotNull;
@@ -38,9 +37,12 @@ public interface MenuBuilder {
* @param slot The slot.
* @return The builder.
*/
MenuBuilder setSlot(int row,
int column,
@NotNull Slot slot);
default MenuBuilder setSlot(final int row,
final int column,
@NotNull final Slot slot) {
return this.addComponent(row, column, slot);
}
/**
* Add a component.
@@ -50,23 +52,9 @@ public interface MenuBuilder {
* @param component The component.
* @return The builder.
*/
default MenuBuilder addComponent(final int row,
final int column,
@NotNull GUIComponent component) {
Validate.isTrue(column + component.getColumns() - 1 <= 9, "Component is too large to be placed here!");
Validate.isTrue(row + component.getRows() - 1 <= this.getRows(), "Component is too large to be placed here!");
for (int currentRow = row; currentRow < row + component.getRows(); currentRow++) {
for (int currentCol = column; currentCol < column + component.getColumns(); currentCol++) {
Slot slot = component.getSlotAt(currentRow, currentCol);
if (slot != null) {
setSlot(currentRow, currentCol, slot);
}
}
}
return this;
}
MenuBuilder addComponent(int row,
int column,
@NotNull GUIComponent component);
/**
* Run function to modify the builder.

View File

@@ -0,0 +1,6 @@
package com.willfp.eco.internal.gui.menu
data class Anchor(
val row: Int,
val column: Int
)

View File

@@ -4,7 +4,7 @@ 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.EcoSlot
import com.willfp.eco.internal.gui.slot.EmptyFillerSlot
import com.willfp.eco.util.NamespacedKeyUtils
import org.bukkit.Bukkit
import org.bukkit.NamespacedKey
@@ -17,41 +17,29 @@ import org.bukkit.persistence.PersistentDataType
@Suppress("UNCHECKED_CAST")
class EcoMenu(
private val rows: Int,
val slots: List<MutableList<EcoSlot>>,
private val slots: Map<Anchor, Slot>,
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 {
if (row < 1 || row > this.rows) {
return slots[0][0]
if (row < 1 || row > this.rows || column < 1 || column > 9) {
return EmptyFillerSlot
}
if (column < 1 || column > 9) {
return slots[0][0]
}
return slots[row - 1][column - 1]
return slots[Anchor(row, column)] ?: EmptyFillerSlot
}
override fun open(player: Player): Inventory {
val inventory = Bukkit.createInventory(null, rows * 9, title)
var i = 0
for (row in slots) {
for (slot in row) {
if (i == rows * 9) {
break
}
inventory.setItem(i, slot.getItemStack(player, this))
i++
}
}
inventory.asRenderedInventory()?.render(noSideEffects = true)
player.openInventory(inventory)
MenuHandler.registerInventory(inventory, this, player)
onOpen.handle(player, this)
inventory.asRenderedInventory()?.generateCaptive()
return inventory
}

View File

@@ -1,26 +1,22 @@
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.MenuBuilder
import com.willfp.eco.core.gui.menu.OpenHandler
import com.willfp.eco.core.gui.slot.FillerMask
import com.willfp.eco.core.gui.slot.FillerSlot
import com.willfp.eco.core.gui.slot.Slot
import com.willfp.eco.internal.gui.slot.EcoFillerSlot
import com.willfp.eco.internal.gui.slot.EcoSlot
import com.willfp.eco.util.ListUtils
import com.willfp.eco.util.StringUtils
import org.bukkit.Material
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import java.util.function.BiConsumer
import java.util.function.Consumer
class EcoMenuBuilder(private val rows: Int) : MenuBuilder {
private var title = "Menu"
private var maskSlots: List<MutableList<Slot?>>
private val slots: List<MutableList<Slot?>> = ListUtils.create2DList(rows, 9)
private val components = mutableMapOf<Anchor, GUIComponent>()
private var onClose = CloseHandler { _, _ -> }
private var onOpen = OpenHandler { _, _ -> }
private var onRender: (Player, Menu) -> Unit = { _, _ -> }
@@ -32,14 +28,13 @@ class EcoMenuBuilder(private val rows: Int) : MenuBuilder {
return this
}
override fun setSlot(
row: Int,
column: Int,
slot: Slot
): MenuBuilder {
override fun addComponent(row: Int, column: Int, component: GUIComponent): MenuBuilder {
require(!(row < 1 || row > rows)) { "Invalid row number!" }
require(!(column < 1 || column > 9)) { "Invalid column number!" }
slots[row - 1][column - 1] = slot
require(column + component.columns - 1 <= 9) { "Component is too large to be placed here!" }
require(row + component.rows - 1 <= getRows()) { "Component is too large to be placed here!" }
components[Anchor(row, column)] = component
return this
}
@@ -69,30 +64,33 @@ class EcoMenuBuilder(private val rows: Int) : MenuBuilder {
}
override fun build(): Menu {
val tempSlots = mutableListOf<MutableList<Slot?>>()
val slots = mutableMapOf<Anchor, Slot>()
for (i in slots.indices) {
for (j in slots[i].indices) {
val slot = slots[i][j] ?: continue
tempSlots[i][j] = slot
}
}
for (row in (1..rows)) {
for (column in (1..9)) {
for ((anchor, component) in components) {
// Too far to the top / left to be in bounds
if (anchor.row > row || anchor.column > column) {
continue
}
val finalSlots = mutableListOf<MutableList<EcoSlot>>()
// Too far to the bottom / left to be in bounds
if (row > anchor.row + component.rows - 1 || column > anchor.column + component.columns - 1) {
continue
}
for (row in tempSlots) {
val tempRow = mutableListOf<EcoSlot>()
for (slot in row) {
var tempSlot = slot
if (tempSlot is FillerSlot) {
tempSlot = EcoFillerSlot(tempSlot.itemStack)
val rowOffset = anchor.row - row
val columnOffset = anchor.column - column
val slot = component.getSlotAt(rowOffset, columnOffset)
if (slot != null) {
slots[Anchor(row, column)] = slot
}
}
tempRow.add((tempSlot ?: EcoFillerSlot(ItemStack(Material.AIR))) as EcoSlot)
}
finalSlots.add(tempRow)
}
return EcoMenu(rows, finalSlots, title, onClose, onRender, onOpen)
return EcoMenu(rows, slots, title, onClose, onRender, onOpen)
}
init {

View File

@@ -13,25 +13,23 @@ class MenuRenderedInventory(
val captiveItems = mutableListOf<ItemStack>()
val state = mutableMapOf<String, Any?>()
fun render() {
generateCaptive()
fun render(noSideEffects: Boolean = false) {
if (!noSideEffects) {
generateCaptive()
}
var i = 0
for (row in menu.slots) {
for (slot in row) {
if (i == menu.rows * 9) {
break
}
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)
if (!slot.isCaptive) {
inventory.setItem(i, slot.getItemStack(player, menu))
}
i++
inventory.setItem(bukkit, item)
}
}
menu.runOnRender(player)
if (!noSideEffects) {
menu.runOnRender(player)
}
}
fun generateCaptive() {

View File

@@ -1,8 +1,9 @@
package com.willfp.eco.internal.gui.slot
import org.bukkit.Material
import org.bukkit.inventory.ItemStack
class EcoFillerSlot(itemStack: ItemStack) : EcoSlot(
open class EcoFillerSlot(itemStack: ItemStack) : EcoSlot(
{ _, _ -> itemStack },
{ _, _, _ -> },
{ _, _, _ -> },
@@ -10,4 +11,6 @@ class EcoFillerSlot(itemStack: ItemStack) : EcoSlot(
{ _, _, _ -> },
{ _, _, _ -> },
{ _, _, prev -> prev }
)
)
object EmptyFillerSlot : EcoFillerSlot(ItemStack(Material.AIR))