Merge pull request #9 from bluefireoly/guispace
Added GUI Space functionality
This commit is contained in:
@@ -58,7 +58,7 @@ dependencies {
|
|||||||
testCompileOnly("org.spigotmc", "spigot", "1.16.3-R0.1-SNAPSHOT")
|
testCompileOnly("org.spigotmc", "spigot", "1.16.3-R0.1-SNAPSHOT")
|
||||||
|
|
||||||
// KHTTP
|
// KHTTP
|
||||||
implementation("khttp", "khttp", "1.0.0")
|
api("khttp", "khttp", "1.0.0")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
package net.axay.kspigot.config
|
package net.axay.kspigot.config
|
||||||
|
|
||||||
import net.axay.kspigot.languageextensions.createIfNotExists
|
import net.axay.kspigot.languageextensions.kotlinextensions.createIfNotExists
|
||||||
import net.axay.kspigot.main.ValueHolder.getGson
|
import net.axay.kspigot.main.ValueHolder.getGson
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileReader
|
import java.io.FileReader
|
||||||
|
@@ -2,76 +2,9 @@
|
|||||||
|
|
||||||
package net.axay.kspigot.inventory
|
package net.axay.kspigot.inventory
|
||||||
|
|
||||||
import net.axay.kspigot.event.listen
|
|
||||||
import org.bukkit.entity.HumanEntity
|
|
||||||
import org.bukkit.event.inventory.InventoryClickEvent
|
|
||||||
import org.bukkit.inventory.Inventory
|
import org.bukkit.inventory.Inventory
|
||||||
import org.bukkit.inventory.InventoryView
|
|
||||||
import org.bukkit.inventory.ItemStack
|
import org.bukkit.inventory.ItemStack
|
||||||
|
|
||||||
// EXTENSIONS
|
|
||||||
|
|
||||||
fun HumanEntity.openGUI(gui: InventoryGUI<*>, page: Int? = null): InventoryView? {
|
|
||||||
|
|
||||||
closeInventory()
|
|
||||||
|
|
||||||
if (page != null)
|
|
||||||
gui.loadPageUnsafe(page)
|
|
||||||
|
|
||||||
return openInventory(gui.bukkitInventory)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// GUI HOLDER
|
|
||||||
|
|
||||||
object InventoryGUIHolder : AutoCloseable {
|
|
||||||
|
|
||||||
private val registered = HashSet<InventoryGUI<ForInventory>>()
|
|
||||||
|
|
||||||
fun register(inventoryGUI: InventoryGUI<ForInventory>) {
|
|
||||||
registered.add(inventoryGUI)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun unregister(inventoryGUI: InventoryGUI<ForInventory>) {
|
|
||||||
registered.remove(inventoryGUI)
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
|
|
||||||
listen<InventoryClickEvent> {
|
|
||||||
|
|
||||||
val clickedInv = it.clickedInventory ?: return@listen
|
|
||||||
|
|
||||||
val inv = registered.find { search -> search.isThisInv(clickedInv) } ?: return@listen
|
|
||||||
val invPage = inv.currentPageInt
|
|
||||||
|
|
||||||
val slot = inv.data.pages[invPage]?.slots?.get(it.slot)
|
|
||||||
if (slot != null)
|
|
||||||
slot.onClick(InventoryGUIClickEvent(it, inv))
|
|
||||||
else
|
|
||||||
it.isCancelled = true
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
registered.forEach { inv -> inv.bukkitInventory.viewers.forEach { it.closeInventory() } }
|
|
||||||
registered.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// EVENT
|
|
||||||
|
|
||||||
class InventoryGUIClickEvent<T : ForInventory>(
|
|
||||||
val bukkitEvent: InventoryClickEvent,
|
|
||||||
val gui: InventoryGUI<T>
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* INVENTORY GUI
|
|
||||||
*/
|
|
||||||
|
|
||||||
private const val DEFAULT_PAGE = 1
|
private const val DEFAULT_PAGE = 1
|
||||||
|
|
||||||
class InventoryGUIData<T : ForInventory>(
|
class InventoryGUIData<T : ForInventory>(
|
||||||
@@ -94,13 +27,25 @@ abstract class InventoryGUI<T : ForInventory>(
|
|||||||
|
|
||||||
internal abstract val bukkitInventory: Inventory
|
internal abstract val bukkitInventory: Inventory
|
||||||
|
|
||||||
|
internal var isInMove: Boolean = false
|
||||||
|
|
||||||
|
internal abstract fun loadPageUnsafe(
|
||||||
|
page: Int,
|
||||||
|
offsetHorizontally: Int = 0,
|
||||||
|
offsetVertically: Int = 0
|
||||||
|
)
|
||||||
|
|
||||||
internal abstract fun loadPageUnsafe(
|
internal abstract fun loadPageUnsafe(
|
||||||
page: InventoryGUIPage<*>,
|
page: InventoryGUIPage<*>,
|
||||||
offsetHorizontally: Int = 0,
|
offsetHorizontally: Int = 0,
|
||||||
offsetVertically: Int = 0
|
offsetVertically: Int = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
internal abstract fun loadPageUnsafe(page: Int, offsetHorizontally: Int = 0, offsetVertically: Int = 0)
|
internal abstract fun loadContent(
|
||||||
|
content: Map<Int, InventoryGUISlot<*>>,
|
||||||
|
offsetHorizontally: Int = 0,
|
||||||
|
offsetVertically: Int = 0
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return True, if the [inventory] belongs to this GUI.
|
* @return True, if the [inventory] belongs to this GUI.
|
||||||
@@ -136,6 +81,14 @@ abstract class InventoryGUI<T : ForInventory>(
|
|||||||
*/
|
*/
|
||||||
fun getPage(page: Int?) = data.pages[page]
|
fun getPage(page: Int?) = data.pages[page]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reloads the current page.
|
||||||
|
*/
|
||||||
|
fun reloadCurrentPage() {
|
||||||
|
if (!isInMove)
|
||||||
|
loadPage(currentPage)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inventory GUI implementations
|
// Inventory GUI implementations
|
||||||
@@ -152,29 +105,47 @@ class InventoryGUIShared<T : ForInventory>(
|
|||||||
|
|
||||||
override fun isThisInv(inventory: Inventory) = inventory == bukkitInventory
|
override fun isThisInv(inventory: Inventory) = inventory == bukkitInventory
|
||||||
|
|
||||||
|
override fun loadPageUnsafe(page: Int, offsetHorizontally: Int, offsetVertically: Int) {
|
||||||
|
data.pages[page]?.let { loadPageUnsafe(it, offsetHorizontally, offsetVertically) }
|
||||||
|
}
|
||||||
|
|
||||||
override fun loadPageUnsafe(page: InventoryGUIPage<*>, offsetHorizontally: Int, offsetVertically: Int) {
|
override fun loadPageUnsafe(page: InventoryGUIPage<*>, offsetHorizontally: Int, offsetVertically: Int) {
|
||||||
|
|
||||||
val ifOffset = offsetHorizontally != 0 || offsetVertically != 0
|
val ifOffset = offsetHorizontally != 0 || offsetVertically != 0
|
||||||
|
|
||||||
if (!ifOffset)
|
if (!ifOffset) {
|
||||||
|
|
||||||
|
// unregister this inv from all elements on the previous page
|
||||||
|
HashSet(currentPage.slots.values).forEach { if (it is InventoryGUIElement) it.stopUsing(this) }
|
||||||
|
|
||||||
currentPageInt = page.number
|
currentPageInt = page.number
|
||||||
|
|
||||||
page.slots.let { slots ->
|
}
|
||||||
|
|
||||||
|
loadContent(page.slots, offsetHorizontally, offsetVertically)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun loadContent(
|
||||||
|
content: Map<Int, InventoryGUISlot<*>>,
|
||||||
|
offsetHorizontally: Int,
|
||||||
|
offsetVertically: Int
|
||||||
|
) {
|
||||||
|
|
||||||
|
val ifOffset = offsetHorizontally != 0 || offsetVertically != 0
|
||||||
|
|
||||||
val dimensions = data.inventoryType.dimensions
|
val dimensions = data.inventoryType.dimensions
|
||||||
|
|
||||||
|
// clear the space which will be redefined
|
||||||
if (ifOffset) {
|
if (ifOffset) {
|
||||||
dimensions.invSlots.forEach {
|
dimensions.invSlots.forEach {
|
||||||
dimensions.invSlotsWithRealSlots[it.add(offsetHorizontally, offsetVertically)]?.let { slotToClear ->
|
val slotToClear = dimensions.invSlotsWithRealSlots[it.add(offsetHorizontally, offsetVertically)]
|
||||||
if (dimensions.realSlots.contains(slotToClear))
|
if (slotToClear != null) bukkitInventory.clear(slotToClear)
|
||||||
bukkitInventory.clear(slotToClear)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
bukkitInventory.clear()
|
|
||||||
}
|
}
|
||||||
|
} else bukkitInventory.clear()
|
||||||
|
|
||||||
|
content.forEach {
|
||||||
|
|
||||||
slots.forEach {
|
|
||||||
val slot = it.value
|
val slot = it.value
|
||||||
if (slot is InventoryGUIElement) {
|
if (slot is InventoryGUIElement) {
|
||||||
|
|
||||||
@@ -182,11 +153,11 @@ class InventoryGUIShared<T : ForInventory>(
|
|||||||
val invSlot = InventorySlot.fromRealSlot(it.key, dimensions)
|
val invSlot = InventorySlot.fromRealSlot(it.key, dimensions)
|
||||||
if (invSlot != null) {
|
if (invSlot != null) {
|
||||||
val offsetSlot = invSlot.add(offsetHorizontally, offsetVertically).realSlotIn(dimensions)
|
val offsetSlot = invSlot.add(offsetHorizontally, offsetVertically).realSlotIn(dimensions)
|
||||||
if (offsetSlot != null)
|
if (offsetSlot != null) bukkitInventory.setItem(offsetSlot, slot.getItemStack(offsetSlot))
|
||||||
bukkitInventory.setItem(offsetSlot, slot.inventoryGUIElementData.itemStack)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bukkitInventory.setItem(it.key, slot.inventoryGUIElementData.itemStack)
|
bukkitInventory.setItem(it.key, slot.getItemStack(it.key))
|
||||||
|
slot.startUsing(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -195,12 +166,6 @@ class InventoryGUIShared<T : ForInventory>(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun loadPageUnsafe(page: Int, offsetHorizontally: Int, offsetVertically: Int) {
|
|
||||||
data.pages[page]?.let { loadPageUnsafe(it, offsetHorizontally, offsetVertically) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override operator fun set(slot: InventorySlotCompound<T>, value: ItemStack) {
|
override operator fun set(slot: InventorySlotCompound<T>, value: ItemStack) {
|
||||||
slot.realSlotsWithInvType(data.inventoryType).forEach {
|
slot.realSlotsWithInvType(data.inventoryType).forEach {
|
||||||
bukkitInventory.setItem(it, value)
|
bukkitInventory.setItem(it, value)
|
||||||
@@ -208,10 +173,3 @@ class InventoryGUIShared<T : ForInventory>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class InventoryGUIPage<T : ForInventory>(
|
|
||||||
val number: Int,
|
|
||||||
internal val slots: Map<Int, InventoryGUISlot<T>>,
|
|
||||||
val transitionTo: PageChangeEffect?,
|
|
||||||
val transitionFrom: PageChangeEffect?
|
|
||||||
)
|
|
@@ -5,13 +5,15 @@ package net.axay.kspigot.inventory
|
|||||||
import net.axay.kspigot.inventory.elements.*
|
import net.axay.kspigot.inventory.elements.*
|
||||||
import org.bukkit.inventory.ItemStack
|
import org.bukkit.inventory.ItemStack
|
||||||
|
|
||||||
fun <T : ForInventory> inventoryGUI(
|
fun <T : ForInventory> kSpigotGUI(
|
||||||
type: InventoryType<T>,
|
type: InventoryType<T>,
|
||||||
|
shared: Boolean = true,
|
||||||
builder: InventoryGUIBuilder<T>.() -> Unit,
|
builder: InventoryGUIBuilder<T>.() -> Unit,
|
||||||
) = InventoryGUIBuilder(type).apply(builder).build()
|
) = InventoryGUIBuilder(type, shared).apply(builder).build()
|
||||||
|
|
||||||
class InventoryGUIBuilder<T : ForInventory>(
|
class InventoryGUIBuilder<T : ForInventory>(
|
||||||
val type: InventoryType<T>
|
val type: InventoryType<T>,
|
||||||
|
val shared: Boolean
|
||||||
) {
|
) {
|
||||||
|
|
||||||
var title: String = ""
|
var title: String = ""
|
||||||
@@ -36,14 +38,17 @@ class InventoryGUIBuilder<T : ForInventory>(
|
|||||||
onClickElement = onClick
|
onClickElement = onClick
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun build() = InventoryGUIShared(
|
internal fun build(): InventoryGUI<T> {
|
||||||
InventoryGUIData(type, title, guiSlots, transitionTo, transitionFrom, onClickElement)
|
val guiData = InventoryGUIData(type, title, guiSlots, transitionTo, transitionFrom, onClickElement)
|
||||||
).apply { register() }
|
val gui =
|
||||||
|
if (shared) InventoryGUIShared(guiData) else TODO("Currently, there is no non-shared GUI implementation available.")
|
||||||
|
return gui.apply { register() }
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class InventoryGUIPageBuilder<T : ForInventory>(
|
class InventoryGUIPageBuilder<T : ForInventory>(
|
||||||
val type: InventoryType<T>,
|
private val type: InventoryType<T>,
|
||||||
val page: Int
|
val page: Int
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@@ -60,14 +65,14 @@ class InventoryGUIPageBuilder<T : ForInventory>(
|
|||||||
* function is invoked.
|
* function is invoked.
|
||||||
*/
|
*/
|
||||||
fun button(slots: InventorySlotCompound<T>, itemStack: ItemStack, onClick: (InventoryGUIClickEvent<T>) -> Unit) =
|
fun button(slots: InventorySlotCompound<T>, itemStack: ItemStack, onClick: (InventoryGUIClickEvent<T>) -> Unit) =
|
||||||
defineSlots(slots, InventoryGUIButton(InventoryGUIElementData(itemStack), onClick))
|
defineSlots(slots, InventoryGUIButton(itemStack, onClick))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An item protected from any player actions.
|
* An item protected from any player actions.
|
||||||
* This is not a button.
|
* This is not a button.
|
||||||
*/
|
*/
|
||||||
fun placeholder(slots: InventorySlotCompound<T>, itemStack: ItemStack) =
|
fun placeholder(slots: InventorySlotCompound<T>, itemStack: ItemStack) =
|
||||||
defineSlots(slots, InventoryGUIPlaceholder(InventoryGUIElementData(itemStack)))
|
defineSlots(slots, InventoryGUIPlaceholder(itemStack))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A free slot does not block any player actions.
|
* A free slot does not block any player actions.
|
||||||
@@ -87,7 +92,7 @@ class InventoryGUIPageBuilder<T : ForInventory>(
|
|||||||
onChange: ((InventoryGUIClickEvent<T>) -> Unit)? = null
|
onChange: ((InventoryGUIClickEvent<T>) -> Unit)? = null
|
||||||
) = defineSlots(
|
) = defineSlots(
|
||||||
slots, InventoryGUIButtonPageChange(
|
slots, InventoryGUIButtonPageChange(
|
||||||
InventoryGUIElementData(itemStack),
|
itemStack,
|
||||||
InventoryGUIPageChangeCalculator.InventoryGUIConsistentPageCalculator(toPage),
|
InventoryGUIPageChangeCalculator.InventoryGUIConsistentPageCalculator(toPage),
|
||||||
onChange
|
onChange
|
||||||
)
|
)
|
||||||
@@ -104,7 +109,7 @@ class InventoryGUIPageBuilder<T : ForInventory>(
|
|||||||
onChange: ((InventoryGUIClickEvent<T>) -> Unit)? = null
|
onChange: ((InventoryGUIClickEvent<T>) -> Unit)? = null
|
||||||
) = defineSlots(
|
) = defineSlots(
|
||||||
slots, InventoryGUIButtonPageChange(
|
slots, InventoryGUIButtonPageChange(
|
||||||
InventoryGUIElementData(itemStack),
|
itemStack,
|
||||||
InventoryGUIPageChangeCalculator.InventoryGUIPreviousPageCalculator,
|
InventoryGUIPageChangeCalculator.InventoryGUIPreviousPageCalculator,
|
||||||
onChange
|
onChange
|
||||||
)
|
)
|
||||||
@@ -121,7 +126,7 @@ class InventoryGUIPageBuilder<T : ForInventory>(
|
|||||||
onChange: ((InventoryGUIClickEvent<T>) -> Unit)? = null
|
onChange: ((InventoryGUIClickEvent<T>) -> Unit)? = null
|
||||||
) = defineSlots(
|
) = defineSlots(
|
||||||
slots, InventoryGUIButtonPageChange(
|
slots, InventoryGUIButtonPageChange(
|
||||||
InventoryGUIElementData(itemStack),
|
itemStack,
|
||||||
InventoryGUIPageChangeCalculator.InventoryGUINextPageCalculator,
|
InventoryGUIPageChangeCalculator.InventoryGUINextPageCalculator,
|
||||||
onChange
|
onChange
|
||||||
)
|
)
|
||||||
@@ -139,13 +144,37 @@ class InventoryGUIPageBuilder<T : ForInventory>(
|
|||||||
onChange: ((InventoryGUIClickEvent<T>) -> Unit)? = null
|
onChange: ((InventoryGUIClickEvent<T>) -> Unit)? = null
|
||||||
) = defineSlots(
|
) = defineSlots(
|
||||||
slots, InventoryGUIButtonInventoryChange(
|
slots, InventoryGUIButtonInventoryChange(
|
||||||
InventoryGUIElementData(itemStack),
|
itemStack,
|
||||||
newGUI,
|
newGUI,
|
||||||
newPage,
|
newPage,
|
||||||
onChange
|
onChange
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines an area where the content of the given compound
|
||||||
|
* is displayed.
|
||||||
|
*/
|
||||||
|
fun <E> compoundSpace(
|
||||||
|
slots: InventorySlotCompound<T>,
|
||||||
|
compound: InventoryGUISpaceCompound<T, E>
|
||||||
|
) {
|
||||||
|
compound.addSlots(slots)
|
||||||
|
defineSlots(
|
||||||
|
slots,
|
||||||
|
InventoryGUISpaceCompoundElement(compound)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new compound, holding data which can be displayed
|
||||||
|
* in any compound space.
|
||||||
|
*/
|
||||||
|
fun <E> createCompound(
|
||||||
|
iconGenerator: (E) -> ItemStack,
|
||||||
|
onClick: (clickEvent: InventoryGUIClickEvent<T>, element: E) -> Unit
|
||||||
|
) = InventoryGUISpaceCompound(type, iconGenerator, onClick)
|
||||||
|
|
||||||
private fun defineSlots(slots: InventorySlotCompound<T>, element: InventoryGUISlot<T>) =
|
private fun defineSlots(slots: InventorySlotCompound<T>, element: InventoryGUISlot<T>) =
|
||||||
slots.withInvType(type).forEach { curSlot ->
|
slots.withInvType(type).forEach { curSlot ->
|
||||||
curSlot.realSlotIn(type.dimensions)?.let { guiSlots[it] = element }
|
curSlot.realSlotIn(type.dimensions)?.let { guiSlots[it] = element }
|
||||||
|
@@ -0,0 +1,8 @@
|
|||||||
|
package net.axay.kspigot.inventory
|
||||||
|
|
||||||
|
import org.bukkit.event.inventory.InventoryClickEvent
|
||||||
|
|
||||||
|
class InventoryGUIClickEvent<T : ForInventory>(
|
||||||
|
val bukkitEvent: InventoryClickEvent,
|
||||||
|
val gui: InventoryGUI<T>
|
||||||
|
)
|
@@ -8,13 +8,9 @@ abstract class InventoryGUISlot<T : ForInventory> {
|
|||||||
|
|
||||||
// ELEMENT
|
// ELEMENT
|
||||||
|
|
||||||
class InventoryGUIElementData(
|
abstract class InventoryGUIElement<T : ForInventory> : InventoryGUISlot<T>() {
|
||||||
val itemStack: ItemStack
|
|
||||||
)
|
|
||||||
|
|
||||||
abstract class InventoryGUIElement<T : ForInventory>(
|
abstract fun getItemStack(slot: Int): ItemStack
|
||||||
val inventoryGUIElementData: InventoryGUIElementData
|
|
||||||
) : InventoryGUISlot<T>() {
|
|
||||||
|
|
||||||
final override fun onClick(clickEvent: InventoryGUIClickEvent<T>) {
|
final override fun onClick(clickEvent: InventoryGUIClickEvent<T>) {
|
||||||
clickEvent.gui.data.generalOnClick?.invoke(clickEvent)
|
clickEvent.gui.data.generalOnClick?.invoke(clickEvent)
|
||||||
@@ -23,4 +19,7 @@ abstract class InventoryGUIElement<T : ForInventory>(
|
|||||||
|
|
||||||
protected abstract fun onClickElement(clickEvent: InventoryGUIClickEvent<T>)
|
protected abstract fun onClickElement(clickEvent: InventoryGUIClickEvent<T>)
|
||||||
|
|
||||||
|
internal open fun startUsing(gui: InventoryGUI<*>) { }
|
||||||
|
internal open fun stopUsing(gui: InventoryGUI<*>) { }
|
||||||
|
|
||||||
}
|
}
|
@@ -0,0 +1,15 @@
|
|||||||
|
package net.axay.kspigot.inventory
|
||||||
|
|
||||||
|
import org.bukkit.entity.HumanEntity
|
||||||
|
import org.bukkit.inventory.InventoryView
|
||||||
|
|
||||||
|
fun HumanEntity.openGUI(gui: InventoryGUI<*>, page: Int? = null): InventoryView? {
|
||||||
|
|
||||||
|
closeInventory()
|
||||||
|
|
||||||
|
if (page != null)
|
||||||
|
gui.loadPageUnsafe(page)
|
||||||
|
|
||||||
|
return openInventory(gui.bukkitInventory)
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,47 @@
|
|||||||
|
package net.axay.kspigot.inventory
|
||||||
|
|
||||||
|
import net.axay.kspigot.event.listen
|
||||||
|
import org.bukkit.event.inventory.InventoryClickEvent
|
||||||
|
|
||||||
|
object InventoryGUIHolder : AutoCloseable {
|
||||||
|
|
||||||
|
private val registered = HashSet<InventoryGUI<ForInventory>>()
|
||||||
|
|
||||||
|
fun register(inventoryGUI: InventoryGUI<ForInventory>) {
|
||||||
|
registered.add(inventoryGUI)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun unregister(inventoryGUI: InventoryGUI<ForInventory>) {
|
||||||
|
registered.remove(inventoryGUI)
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
|
||||||
|
listen<InventoryClickEvent> {
|
||||||
|
|
||||||
|
val clickedInv = it.clickedInventory ?: return@listen
|
||||||
|
|
||||||
|
val inv = registered.find { search -> search.isThisInv(clickedInv) } ?: return@listen
|
||||||
|
|
||||||
|
if (inv.isInMove) {
|
||||||
|
it.isCancelled = true
|
||||||
|
return@listen
|
||||||
|
}
|
||||||
|
|
||||||
|
val invPage = inv.currentPageInt
|
||||||
|
|
||||||
|
val slot = inv.data.pages[invPage]?.slots?.get(it.slot)
|
||||||
|
if (slot != null)
|
||||||
|
slot.onClick(InventoryGUIClickEvent(it, inv))
|
||||||
|
else
|
||||||
|
it.isCancelled = true
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
registered.forEach { inv -> inv.bukkitInventory.viewers.forEach { it.closeInventory() } }
|
||||||
|
registered.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
package net.axay.kspigot.inventory
|
||||||
|
|
||||||
|
class InventoryGUIPage<T : ForInventory>(
|
||||||
|
val number: Int,
|
||||||
|
internal val slots: Map<Int, InventoryGUISlot<T>>,
|
||||||
|
val transitionTo: PageChangeEffect?,
|
||||||
|
val transitionFrom: PageChangeEffect?
|
||||||
|
)
|
@@ -67,7 +67,7 @@ internal fun InventoryGUI<*>.changePage(
|
|||||||
|
|
||||||
PageChangeEffect.SLIDE_VERTICALLY -> {
|
PageChangeEffect.SLIDE_VERTICALLY -> {
|
||||||
|
|
||||||
val height = data.inventoryType.dimensions.heigth
|
val height = data.inventoryType.dimensions.height
|
||||||
|
|
||||||
changePageEffect(fromPageInt, toPageInt, height) { currentOffset, ifInverted ->
|
changePageEffect(fromPageInt, toPageInt, height) { currentOffset, ifInverted ->
|
||||||
if (ifInverted) {
|
if (ifInverted) {
|
||||||
@@ -97,7 +97,7 @@ internal fun InventoryGUI<*>.changePage(
|
|||||||
|
|
||||||
PageChangeEffect.SWIPE_VERTICALLY -> {
|
PageChangeEffect.SWIPE_VERTICALLY -> {
|
||||||
|
|
||||||
val height = data.inventoryType.dimensions.heigth
|
val height = data.inventoryType.dimensions.height
|
||||||
|
|
||||||
changePageEffect(fromPageInt, toPageInt, height) { currentOffset, ifInverted ->
|
changePageEffect(fromPageInt, toPageInt, height) { currentOffset, ifInverted ->
|
||||||
if (ifInverted) {
|
if (ifInverted) {
|
||||||
|
@@ -2,15 +2,17 @@
|
|||||||
|
|
||||||
package net.axay.kspigot.inventory
|
package net.axay.kspigot.inventory
|
||||||
|
|
||||||
import net.axay.kspigot.languageextensions.MinMaxPair
|
import net.axay.kspigot.languageextensions.kotlinextensions.MinMaxPair
|
||||||
|
|
||||||
// INVENTORY
|
// INVENTORY
|
||||||
|
|
||||||
data class InventoryDimensions(val width: Int, val heigth: Int) {
|
data class InventoryDimensions(val width: Int, val height: Int) {
|
||||||
|
|
||||||
|
val slotAmount = width * height
|
||||||
|
|
||||||
val invSlots by lazy {
|
val invSlots by lazy {
|
||||||
ArrayList<InventorySlot>().apply {
|
ArrayList<InventorySlot>().apply {
|
||||||
(1..heigth).forEach { row ->
|
(1..height).forEach { row ->
|
||||||
(1..width).forEach { slotInRow ->
|
(1..width).forEach { slotInRow ->
|
||||||
this += InventorySlot(row, slotInRow)
|
this += InventorySlot(row, slotInRow)
|
||||||
}
|
}
|
||||||
@@ -18,14 +20,6 @@ data class InventoryDimensions(val width: Int, val heigth: Int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val realSlots by lazy {
|
|
||||||
ArrayList<Int>().apply {
|
|
||||||
invSlots.forEach { curSlot ->
|
|
||||||
curSlot.realSlotIn(this@InventoryDimensions)?.let { this += it }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val invSlotsWithRealSlots by lazy {
|
val invSlotsWithRealSlots by lazy {
|
||||||
HashMap<InventorySlot, Int>().apply {
|
HashMap<InventorySlot, Int>().apply {
|
||||||
invSlots.forEach { curSlot ->
|
invSlots.forEach { curSlot ->
|
||||||
@@ -34,6 +28,8 @@ data class InventoryDimensions(val width: Int, val heigth: Int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val realSlots by lazy { invSlotsWithRealSlots.values }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SLOTS
|
// SLOTS
|
||||||
@@ -57,13 +53,13 @@ data class InventorySlot(val row: Int, val slotInRow: Int) : Comparable<Inventor
|
|||||||
|
|
||||||
fun realSlotIn(inventoryDimensions: InventoryDimensions): Int? {
|
fun realSlotIn(inventoryDimensions: InventoryDimensions): Int? {
|
||||||
if (!isInDimension(inventoryDimensions)) return null
|
if (!isInDimension(inventoryDimensions)) return null
|
||||||
val realRow = inventoryDimensions.heigth - (row - 1)
|
val realRow = inventoryDimensions.height - (row - 1)
|
||||||
val rowsUnder = if (realRow - 1 >= 0) realRow - 1 else 0
|
val rowsUnder = if (realRow - 1 >= 0) realRow - 1 else 0
|
||||||
return ((rowsUnder * inventoryDimensions.width) + slotInRow) - 1
|
return ((rowsUnder * inventoryDimensions.width) + slotInRow) - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isInDimension(inventoryDimensions: InventoryDimensions) =
|
fun isInDimension(inventoryDimensions: InventoryDimensions) =
|
||||||
(1..inventoryDimensions.width).contains(slotInRow) && (1..inventoryDimensions.heigth).contains(row)
|
(1..inventoryDimensions.width).contains(slotInRow) && (1..inventoryDimensions.height).contains(row)
|
||||||
|
|
||||||
fun add(offsetHorizontally: Int, offsetVertically: Int) = InventorySlot(
|
fun add(offsetHorizontally: Int, offsetVertically: Int) = InventorySlot(
|
||||||
row + offsetVertically,
|
row + offsetVertically,
|
||||||
@@ -182,7 +178,7 @@ class InventoryColumnSlots<T : ForInventory> internal constructor(
|
|||||||
) : InventorySlotCompound<T> {
|
) : InventorySlotCompound<T> {
|
||||||
|
|
||||||
override fun withInvType(invType: InventoryType<T>) = HashSet<InventorySlot>().apply {
|
override fun withInvType(invType: InventoryType<T>) = HashSet<InventorySlot>().apply {
|
||||||
for (row in 1..invType.dimensions.heigth)
|
for (row in 1..invType.dimensions.height)
|
||||||
this += InventorySlot(row, column)
|
this += InventorySlot(row, column)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,9 +195,9 @@ class InventoryBorderSlots<T : ForInventory> internal constructor(
|
|||||||
for (currentPadding in 0 until padding) {
|
for (currentPadding in 0 until padding) {
|
||||||
for (slotInRow in 1 + currentPadding..dimensions.width - currentPadding) {
|
for (slotInRow in 1 + currentPadding..dimensions.width - currentPadding) {
|
||||||
this += InventorySlot(1, slotInRow)
|
this += InventorySlot(1, slotInRow)
|
||||||
this += InventorySlot(dimensions.heigth, slotInRow)
|
this += InventorySlot(dimensions.height, slotInRow)
|
||||||
}
|
}
|
||||||
for (row in 2 + currentPadding until dimensions.heigth - currentPadding) {
|
for (row in 2 + currentPadding until dimensions.height - currentPadding) {
|
||||||
this += InventorySlot(row, 1)
|
this += InventorySlot(row, 1)
|
||||||
this += InventorySlot(row, dimensions.width)
|
this += InventorySlot(row, dimensions.width)
|
||||||
}
|
}
|
||||||
@@ -224,8 +220,8 @@ class InventoryCornerSlots<T : ForInventory> internal constructor(
|
|||||||
|
|
||||||
if (ifBottomLeft) this += InventorySlot(1, 1)
|
if (ifBottomLeft) this += InventorySlot(1, 1)
|
||||||
if (ifBottomRight) this += InventorySlot(1, dimensions.width)
|
if (ifBottomRight) this += InventorySlot(1, dimensions.width)
|
||||||
if (ifTopLeft) this += InventorySlot(dimensions.heigth, 1)
|
if (ifTopLeft) this += InventorySlot(dimensions.height, 1)
|
||||||
if (ifTopRight) this += InventorySlot(dimensions.heigth, dimensions.width)
|
if (ifTopRight) this += InventorySlot(dimensions.height, dimensions.width)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -12,8 +12,6 @@ class InventoryType<in T : ForInventory>(
|
|||||||
val bukkitType: InventoryType? = null
|
val bukkitType: InventoryType? = null
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val size = dimensions.width * dimensions.heigth
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
val ONE_BY_NINE = InventoryType<ForInventoryOneByNine>(InventoryDimensions(9, 1))
|
val ONE_BY_NINE = InventoryType<ForInventoryOneByNine>(InventoryDimensions(9, 1))
|
||||||
@@ -33,7 +31,7 @@ class InventoryType<in T : ForInventory>(
|
|||||||
val realTitle = title ?: ""
|
val realTitle = title ?: ""
|
||||||
return when {
|
return when {
|
||||||
bukkitType != null -> Bukkit.createInventory(holder, bukkitType, realTitle)
|
bukkitType != null -> Bukkit.createInventory(holder, bukkitType, realTitle)
|
||||||
else -> Bukkit.createInventory(holder, size, realTitle)
|
else -> Bukkit.createInventory(holder, dimensions.slotAmount, realTitle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,14 +1,14 @@
|
|||||||
package net.axay.kspigot.inventory.elements
|
package net.axay.kspigot.inventory.elements
|
||||||
|
|
||||||
import net.axay.kspigot.inventory.ForInventory
|
import net.axay.kspigot.inventory.*
|
||||||
import net.axay.kspigot.inventory.InventoryGUIClickEvent
|
import org.bukkit.inventory.ItemStack
|
||||||
import net.axay.kspigot.inventory.InventoryGUIElement
|
|
||||||
import net.axay.kspigot.inventory.InventoryGUIElementData
|
|
||||||
|
|
||||||
open class InventoryGUIButton<T : ForInventory>(
|
open class InventoryGUIButton<T : ForInventory>(
|
||||||
inventoryGUIElementData: InventoryGUIElementData,
|
private val icon: ItemStack,
|
||||||
val action: (InventoryGUIClickEvent<T>) -> Unit,
|
val action: (InventoryGUIClickEvent<T>) -> Unit,
|
||||||
) : InventoryGUIElement<T>(inventoryGUIElementData) {
|
) : InventoryGUIElement<T>() {
|
||||||
|
|
||||||
|
override fun getItemStack(slot: Int) = icon
|
||||||
|
|
||||||
override fun onClickElement(clickEvent: InventoryGUIClickEvent<T>) {
|
override fun onClickElement(clickEvent: InventoryGUIClickEvent<T>) {
|
||||||
clickEvent.bukkitEvent.isCancelled = true
|
clickEvent.bukkitEvent.isCancelled = true
|
||||||
|
@@ -1,13 +1,14 @@
|
|||||||
package net.axay.kspigot.inventory.elements
|
package net.axay.kspigot.inventory.elements
|
||||||
|
|
||||||
import net.axay.kspigot.inventory.*
|
import net.axay.kspigot.inventory.*
|
||||||
|
import org.bukkit.inventory.ItemStack
|
||||||
|
|
||||||
class InventoryGUIButtonInventoryChange<T : ForInventory>(
|
class InventoryGUIButtonInventoryChange<T : ForInventory>(
|
||||||
inventoryGUIElementData: InventoryGUIElementData,
|
icon: ItemStack,
|
||||||
changeToGUICallback: () -> InventoryGUI<*>,
|
changeToGUICallback: () -> InventoryGUI<*>,
|
||||||
changeToPageInt: Int?,
|
changeToPageInt: Int?,
|
||||||
onChange: ((InventoryGUIClickEvent<T>) -> Unit)?
|
onChange: ((InventoryGUIClickEvent<T>) -> Unit)?
|
||||||
) : InventoryGUIButton<T>(inventoryGUIElementData, {
|
) : InventoryGUIButton<T>(icon, {
|
||||||
|
|
||||||
val changeToGUI = changeToGUICallback.invoke()
|
val changeToGUI = changeToGUICallback.invoke()
|
||||||
|
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
package net.axay.kspigot.inventory.elements
|
package net.axay.kspigot.inventory.elements
|
||||||
|
|
||||||
import net.axay.kspigot.inventory.*
|
import net.axay.kspigot.inventory.*
|
||||||
|
import org.bukkit.inventory.ItemStack
|
||||||
|
|
||||||
class InventoryGUIButtonPageChange<T : ForInventory>(
|
class InventoryGUIButtonPageChange<T : ForInventory>(
|
||||||
inventoryGUIElementData: InventoryGUIElementData,
|
icon: ItemStack,
|
||||||
calculator: InventoryGUIPageChangeCalculator,
|
calculator: InventoryGUIPageChangeCalculator,
|
||||||
onChange: ((InventoryGUIClickEvent<T>) -> Unit)?
|
onChange: ((InventoryGUIClickEvent<T>) -> Unit)?
|
||||||
) : InventoryGUIButton<T>(inventoryGUIElementData, {
|
) : InventoryGUIButton<T>(icon, {
|
||||||
|
|
||||||
val currentPage = it.gui.currentPage
|
val currentPage = it.gui.currentPage
|
||||||
val newPage = it.gui.getPage(calculator.calculateNewPage(it.gui.currentPageInt, it.gui.data.pages.keys))
|
val newPage = it.gui.getPage(calculator.calculateNewPage(it.gui.currentPageInt, it.gui.data.pages.keys))
|
||||||
|
@@ -1,13 +1,13 @@
|
|||||||
package net.axay.kspigot.inventory.elements
|
package net.axay.kspigot.inventory.elements
|
||||||
|
|
||||||
import net.axay.kspigot.inventory.ForInventory
|
import net.axay.kspigot.inventory.*
|
||||||
import net.axay.kspigot.inventory.InventoryGUIClickEvent
|
import org.bukkit.inventory.ItemStack
|
||||||
import net.axay.kspigot.inventory.InventoryGUIElement
|
|
||||||
import net.axay.kspigot.inventory.InventoryGUIElementData
|
|
||||||
|
|
||||||
class InventoryGUIPlaceholder<T : ForInventory>(
|
class InventoryGUIPlaceholder<T : ForInventory>(
|
||||||
inventoryGUIElementData: InventoryGUIElementData
|
private val icon: ItemStack
|
||||||
) : InventoryGUIElement<T>(inventoryGUIElementData) {
|
) : InventoryGUIElement<T>() {
|
||||||
|
|
||||||
|
override fun getItemStack(slot: Int) = icon
|
||||||
|
|
||||||
override fun onClickElement(clickEvent: InventoryGUIClickEvent<T>) {
|
override fun onClickElement(clickEvent: InventoryGUIClickEvent<T>) {
|
||||||
clickEvent.bukkitEvent.isCancelled = true
|
clickEvent.bukkitEvent.isCancelled = true
|
||||||
|
@@ -0,0 +1,114 @@
|
|||||||
|
@file:Suppress("MemberVisibilityCanBePrivate", "unused")
|
||||||
|
|
||||||
|
package net.axay.kspigot.inventory.elements
|
||||||
|
|
||||||
|
import net.axay.kspigot.inventory.*
|
||||||
|
import org.bukkit.Material
|
||||||
|
import org.bukkit.inventory.ItemStack
|
||||||
|
|
||||||
|
class InventoryGUISpaceCompoundElement<T : ForInventory, E>(
|
||||||
|
private val compound: InventoryGUISpaceCompound<T, E>
|
||||||
|
) : InventoryGUIElement<T>() {
|
||||||
|
|
||||||
|
override fun getItemStack(slot: Int) = compound.getItemStack(slot)
|
||||||
|
|
||||||
|
override fun onClickElement(clickEvent: InventoryGUIClickEvent<T>) {
|
||||||
|
compound.onClickElement(clickEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun startUsing(gui: InventoryGUI<*>) = compound.registerGUI(gui)
|
||||||
|
|
||||||
|
override fun stopUsing(gui: InventoryGUI<*>) = compound.unregisterGUI(gui)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class InventoryGUISpaceCompound<T : ForInventory, E>(
|
||||||
|
private val invType: InventoryType<T>,
|
||||||
|
private val iconGenerator: (E) -> ItemStack,
|
||||||
|
private val onClick: (InventoryGUIClickEvent<T>, E) -> Unit
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val content = ArrayList<E>()
|
||||||
|
|
||||||
|
private val realInternalSlots = ArrayList<Int>()
|
||||||
|
|
||||||
|
private val currentInternalSlots: List<Int> get() {
|
||||||
|
|
||||||
|
val result = ArrayList(realInternalSlots)
|
||||||
|
|
||||||
|
var more = 1
|
||||||
|
while (content.size > result.size) {
|
||||||
|
result += realInternalSlots.mapTo(ArrayList()) { it + (more * invType.dimensions.slotAmount) }
|
||||||
|
more++
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private var scrolledLines: Int = 0
|
||||||
|
|
||||||
|
private var contentSort: () -> Unit = { }
|
||||||
|
|
||||||
|
private val registeredGUIs = HashSet<InventoryGUI<*>>()
|
||||||
|
|
||||||
|
private fun translateSlot(slot: Int) = (scrolledLines * invType.dimensions.width) + slot
|
||||||
|
|
||||||
|
private fun contentAtSlot(slot: Int) = content.getOrNull(
|
||||||
|
realInternalSlots.indexOf(translateSlot(slot))
|
||||||
|
)
|
||||||
|
|
||||||
|
internal fun getItemStack(slot: Int): ItemStack {
|
||||||
|
return contentAtSlot(slot)?.let { return@let iconGenerator.invoke(it) }
|
||||||
|
?: ItemStack(Material.AIR)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun onClickElement(clickEvent: InventoryGUIClickEvent<T>) {
|
||||||
|
val element = contentAtSlot(clickEvent.bukkitEvent.slot) ?: return
|
||||||
|
onClick.invoke(clickEvent, element)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun addSlots(slots: InventorySlotCompound<T>) {
|
||||||
|
slots.realSlotsWithInvType(invType).forEach {
|
||||||
|
if (!realInternalSlots.contains(it))
|
||||||
|
realInternalSlots.add(it)
|
||||||
|
}
|
||||||
|
realInternalSlots.sort()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun registerGUI(gui: InventoryGUI<*>) {
|
||||||
|
registeredGUIs += gui
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun unregisterGUI(gui: InventoryGUI<*>) {
|
||||||
|
registeredGUIs -= gui
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the sort behaviour which gets applied to the content
|
||||||
|
* automatically.
|
||||||
|
*/
|
||||||
|
fun <R : Comparable<R>> sortContentBy(reverse: Boolean = false, selector: (E) -> R?) {
|
||||||
|
contentSort = {
|
||||||
|
if (!reverse) content.sortBy(selector) else content.sortByDescending(selector)
|
||||||
|
}
|
||||||
|
contentSort.invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new element to the compound.
|
||||||
|
*/
|
||||||
|
fun addContent(element: E) {
|
||||||
|
addContent(listOf(element))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds new elements to the compound.
|
||||||
|
*/
|
||||||
|
fun addContent(elements: Collection<E>) {
|
||||||
|
content += elements
|
||||||
|
contentSort.invoke()
|
||||||
|
registeredGUIs.forEach { it.reloadCurrentPage() }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -1,7 +1,7 @@
|
|||||||
package net.axay.kspigot.main
|
package net.axay.kspigot.main
|
||||||
|
|
||||||
import net.axay.kspigot.inventory.InventoryGUIHolder
|
import net.axay.kspigot.inventory.InventoryGUIHolder
|
||||||
import net.axay.kspigot.languageextensions.closeIfInitialized
|
import net.axay.kspigot.languageextensions.kotlinextensions.closeIfInitialized
|
||||||
import net.axay.kspigot.runnables.KRunnableHolder
|
import net.axay.kspigot.runnables.KRunnableHolder
|
||||||
import org.bukkit.plugin.java.JavaPlugin
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
package net.axay.kspigot.particles
|
package net.axay.kspigot.particles
|
||||||
|
|
||||||
import net.axay.kspigot.extensions.bukkit.worldOrException
|
import net.axay.kspigot.extensions.bukkit.worldOrException
|
||||||
import net.axay.kspigot.languageextensions.applyIfNotNull
|
import net.axay.kspigot.languageextensions.kotlinextensions.applyIfNotNull
|
||||||
import org.bukkit.Location
|
import org.bukkit.Location
|
||||||
import org.bukkit.Particle
|
import org.bukkit.Particle
|
||||||
import org.bukkit.entity.Player
|
import org.bukkit.entity.Player
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
package net.axay.kspigot.sound
|
package net.axay.kspigot.sound
|
||||||
|
|
||||||
import net.axay.kspigot.extensions.bukkit.worldOrException
|
import net.axay.kspigot.extensions.bukkit.worldOrException
|
||||||
import net.axay.kspigot.languageextensions.applyIfNotNull
|
import net.axay.kspigot.languageextensions.kotlinextensions.applyIfNotNull
|
||||||
import org.bukkit.Location
|
import org.bukkit.Location
|
||||||
import org.bukkit.Sound
|
import org.bukkit.Sound
|
||||||
import org.bukkit.SoundCategory
|
import org.bukkit.SoundCategory
|
||||||
|
Reference in New Issue
Block a user