1.0.0.3 - new modules
Added utility, menu, thirdparty and exposed modules A few little fixes
This commit is contained in:
1 parent
161f9aa268
commit
3c87e2d6a1
63 files changed
+5334
-12
No files matched your search
@@ -0,0 +1,42 @@
|
||||
.gradle
|
||||
build/
|
||||
!gradle/wrapper/gradle-wrapper.jar
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
out/
|
||||
!**/src/main/**/out/
|
||||
!**/src/test/**/out/
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
bin/
|
||||
!**/src/main/**/bin/
|
||||
!**/src/test/**/bin/
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
@@ -0,0 +1,117 @@
|
||||
plugins {
|
||||
id("java")
|
||||
kotlin("jvm")
|
||||
kotlin("plugin.serialization") version "1.8.21"
|
||||
id("com.github.johnrengelman.shadow") version "7.1.2"
|
||||
id("maven-publish")
|
||||
id("signing")
|
||||
}
|
||||
|
||||
group = "tech.carcadex"
|
||||
val globalArtifactId = "kotlinbukkitkit-menu"
|
||||
val globalName = "kotlinbukkitkit-menu"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
name = "spigotmc-repo"
|
||||
url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly("org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT")
|
||||
api(project(":architecture"))
|
||||
api(project(":extensions"))
|
||||
api(project(":utility"))
|
||||
compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(8)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
sourceSets.main {
|
||||
kotlin.srcDir("build/generated/ksp/main/kotlin")
|
||||
}
|
||||
sourceSets.test {
|
||||
kotlin.srcDir("build/generated/ksp/test/kotlin")
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
withJavadocJar()
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("maven") {
|
||||
artifactId = globalArtifactId
|
||||
groupId = "tech.carcadex"
|
||||
version = version
|
||||
from(components["java"])
|
||||
versionMapping {
|
||||
usage("java-api") {
|
||||
fromResolutionOf("runtimeClasspath")
|
||||
}
|
||||
usage("java-runtime") {
|
||||
fromResolutionResult()
|
||||
}
|
||||
}
|
||||
pom {
|
||||
artifactId = globalArtifactId
|
||||
groupId = "tech.carcadex"
|
||||
version = version
|
||||
name.set(globalArtifactId)
|
||||
description.set(globalArtifactId)
|
||||
url.set("https://github.com/CarcadeX/KotlinBukkitKit")
|
||||
|
||||
licenses {
|
||||
license {
|
||||
name.set("MIT")
|
||||
url.set("https://mit-license.org/")
|
||||
}
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id.set("itzRedTea")
|
||||
name.set("Red Tea")
|
||||
email.set("red__tea@outlook.com")
|
||||
}
|
||||
}
|
||||
scm {
|
||||
connection.set("scm:https://github.com/CarcadeX/$globalName.git")
|
||||
developerConnection.set("git@github.com:CarcadeX/$globalName.git")
|
||||
url.set("https://github.com/CarcadeX/KotlinBukkitKit")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
val releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
|
||||
val snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/"
|
||||
url = uri(if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl)
|
||||
credentials {
|
||||
username = "itzRedTea"
|
||||
password = project.findProperty("password").toString()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
signing {
|
||||
useGpgCmd()
|
||||
sign(publishing.publications.getByName("maven"))
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package tech.carcadex.kotlinbukkitkit.menu
|
||||
|
||||
import tech.carcadex.kotlinbukkitkit.architecture.extensions.WithPlugin
|
||||
import tech.carcadex.kotlinbukkitkit.menu.slot.Slot
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.inventory.Inventory
|
||||
import org.bukkit.inventory.InventoryHolder
|
||||
import org.bukkit.plugin.Plugin
|
||||
import java.util.*
|
||||
|
||||
public interface Menu<T : Slot> : WithPlugin<Plugin>, InventoryHolder {
|
||||
|
||||
public var title: String
|
||||
public var lines: Int
|
||||
public var cancelOnClick: Boolean
|
||||
|
||||
public val viewers: Map<Player, Inventory>
|
||||
public val slots: TreeMap<Int, T>
|
||||
|
||||
public val data: WeakHashMap<String, Any>
|
||||
public val playerData: WeakHashMap<Player, WeakHashMap<String, Any>>
|
||||
|
||||
public val eventHandler: MenuEventHandler
|
||||
|
||||
public var baseSlot: T
|
||||
public var updateDelay: Long
|
||||
|
||||
public fun setSlot(slot: Int, slotObj: T)
|
||||
|
||||
public fun update(players: Set<Player> = viewers.keys)
|
||||
public fun updateSlot(slot: Slot, players: Set<Player> = viewers.keys)
|
||||
|
||||
public fun update(vararg players: Player): Unit = update(players.toSet())
|
||||
public fun updateSlot(slot: Slot, vararg players: Player): Unit = updateSlot(slot, players.toSet())
|
||||
|
||||
public fun openToPlayer(vararg players: Player)
|
||||
|
||||
public fun clearData() {
|
||||
data.clear()
|
||||
for (slot in slotsWithBaseSlot())
|
||||
slot.clearSlotData()
|
||||
}
|
||||
|
||||
public fun clearPlayerData(player: Player) {
|
||||
playerData.remove(player)
|
||||
for (slot in slotsWithBaseSlot())
|
||||
slot.clearPlayerData(player)
|
||||
}
|
||||
|
||||
public fun close(player: Player, closeInventory: Boolean = true)
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package tech.carcadex.kotlinbukkitkit.menu
|
||||
|
||||
import tech.carcadex.kotlinbukkitkit.architecture.PluginDisableAware
|
||||
import tech.carcadex.kotlinbukkitkit.architecture.PluginDisableAwareController
|
||||
import tech.carcadex.kotlinbukkitkit.extensions.KListener
|
||||
import tech.carcadex.kotlinbukkitkit.extensions.onlinePlayers
|
||||
import tech.carcadex.kotlinbukkitkit.extensions.registerEvents
|
||||
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotInteract
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.event.EventHandler
|
||||
import org.bukkit.event.inventory.InventoryClickEvent
|
||||
import org.bukkit.event.inventory.InventoryCloseEvent
|
||||
import org.bukkit.event.inventory.InventoryDragEvent
|
||||
import org.bukkit.event.inventory.InventoryType
|
||||
import org.bukkit.event.player.PlayerPickupItemEvent
|
||||
import org.bukkit.plugin.Plugin
|
||||
|
||||
internal object MenuControllerPlugins : PluginDisableAwareController<MenuController>() {
|
||||
override val factory: (plugin: Plugin) -> MenuController = { MenuController(it) }
|
||||
}
|
||||
|
||||
internal class MenuController(
|
||||
override val plugin: Plugin,
|
||||
) : KListener<Plugin>, PluginDisableAware {
|
||||
|
||||
init {
|
||||
registerEvents(plugin)
|
||||
}
|
||||
|
||||
override fun onDisable() {
|
||||
onlinePlayers.forEach {
|
||||
val menu = getMenuFromPlayer(it)
|
||||
?.takeIf { it.plugin.name == plugin.name }
|
||||
menu?.close(it, true)
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
fun clickInventoryEvent(event: InventoryClickEvent) {
|
||||
if (event.view.type == InventoryType.CHEST) {
|
||||
val player = event.whoClicked as Player
|
||||
val inv = event.inventory
|
||||
|
||||
val menu = getMenuFromInventory(event.inventory)?.takeIfHasPlayer(player)
|
||||
|
||||
if (menu != null) {
|
||||
if (menu.plugin.name != plugin.name) return
|
||||
|
||||
if (event.rawSlot == event.slot) {
|
||||
val clickedSlot = event.slot + 1
|
||||
val slot = menu.slotOrBaseSlot(clickedSlot)
|
||||
|
||||
val interact = MenuPlayerSlotInteract(
|
||||
menu, clickedSlot, slot,
|
||||
player, inv, slot.cancel,
|
||||
event.click, event.action,
|
||||
event.currentItem, event.cursor,
|
||||
event.hotbarButton,
|
||||
)
|
||||
|
||||
slot.eventHandler.interact(interact)
|
||||
|
||||
if (interact.canceled) event.isCancelled = true
|
||||
} else {
|
||||
// TODO move item
|
||||
event.isCancelled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
fun clickInteractEvent(event: InventoryDragEvent) {
|
||||
if (event.view.type == InventoryType.CHEST) {
|
||||
val player = event.whoClicked as Player
|
||||
val menu = getMenuFromInventory(event.inventory)?.takeIfHasPlayer(player)
|
||||
if (menu != null) {
|
||||
if (menu.plugin.name != plugin.name) return
|
||||
val pass = event.inventorySlots.firstOrNull { it in event.rawSlots }
|
||||
if (pass != null) event.isCancelled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onClose(event: InventoryCloseEvent) {
|
||||
if (event.view.type == InventoryType.CHEST) {
|
||||
val player = event.player as Player
|
||||
val menu = getMenuFromPlayer(player)
|
||||
if (menu != null) {
|
||||
if (menu.plugin.name != plugin.name) return
|
||||
menu.close(player, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
fun onPickupItemEvent(event: PlayerPickupItemEvent) {
|
||||
val menu = getMenuFromPlayer(event.player)
|
||||
if (menu != null) {
|
||||
if (menu.plugin.name != plugin.name) return
|
||||
event.isCancelled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package tech.carcadex.kotlinbukkitkit.menu
|
||||
|
||||
public interface MenuEventHandler {
|
||||
|
||||
public fun update(update: MenuPlayerUpdate)
|
||||
|
||||
public fun close(close: MenuPlayerClose)
|
||||
|
||||
public fun moveToMenu(moveToMenu: MenuPlayerMoveTo)
|
||||
|
||||
public fun preOpen(preOpen: MenuPlayerPreOpen)
|
||||
|
||||
public fun open(open: MenuPlayerOpen)
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package tech.carcadex.kotlinbukkitkit.menu
|
||||
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.inventory.Inventory
|
||||
import org.bukkit.inventory.ItemStack
|
||||
import java.util.*
|
||||
|
||||
public interface MenuPlayer {
|
||||
public val menu: Menu<*>
|
||||
public val player: Player
|
||||
|
||||
public fun putPlayerData(key: String, value: Any) {
|
||||
val map = menu.playerData[player]
|
||||
if (map != null) {
|
||||
map[key] = value
|
||||
} else menu.playerData[player] = WeakHashMap<String, Any>().apply { put(key, value) }
|
||||
}
|
||||
|
||||
public fun getPlayerData(key: String): Any? = menu.playerData.get(player)?.get(key)
|
||||
}
|
||||
|
||||
public interface MenuPlayerInventory : MenuPlayer {
|
||||
public val inventory: Inventory
|
||||
|
||||
public fun close(): Unit = player.closeInventory()
|
||||
|
||||
public fun getItem(line: Int, slot: Int): ItemStack? {
|
||||
return getItem(calculateSlot(line, slot))
|
||||
}
|
||||
|
||||
public fun getItem(slot: Int): ItemStack? = inventory.getItem(slot - 1)
|
||||
|
||||
public fun setItem(line: Int, slot: Int, item: ItemStack?) {
|
||||
setItem(calculateSlot(line, slot), item)
|
||||
}
|
||||
|
||||
public fun setItem(slot: Int, item: ItemStack?) {
|
||||
inventory.setItem(rawSlot(slot), item)
|
||||
}
|
||||
|
||||
public fun getPlayerItem(line: Int, slot: Int): ItemStack? {
|
||||
return getPlayerItem(calculateSlot(line, slot))
|
||||
}
|
||||
|
||||
public fun getPlayerItem(slot: Int): ItemStack? = player.inventory.getItem(rawSlot(slot))
|
||||
|
||||
public fun setPlayerItem(line: Int, slot: Int, item: ItemStack?) {
|
||||
setPlayerItem(calculateSlot(line, slot), item)
|
||||
}
|
||||
|
||||
public fun setPlayerItem(slot: Int, item: ItemStack?) {
|
||||
player.inventory.setItem(rawSlot(slot), item)
|
||||
}
|
||||
|
||||
private fun getLineRange(line: Int) = calculateSlot(line, 1)..calculateEndLine(line)
|
||||
|
||||
public fun getLine(line: Int): List<ItemStack?> = getLineRange(line).map { getItem(it) }
|
||||
|
||||
public fun getPlayerLine(line: Int): List<ItemStack?> = getLineRange(line).map { getPlayerItem(it) }
|
||||
|
||||
public fun updateToPlayer() {
|
||||
menu.update(player)
|
||||
}
|
||||
}
|
||||
|
||||
public interface MenuPlayerCancellable : MenuPlayer {
|
||||
public var canceled: Boolean
|
||||
}
|
||||
|
||||
public open class MenuPlayerInteract(
|
||||
override val menu: Menu<*>,
|
||||
override val player: Player,
|
||||
override val inventory: Inventory,
|
||||
override var canceled: Boolean,
|
||||
) : MenuPlayerInventory, MenuPlayerCancellable
|
||||
|
||||
public class MenuPlayerPreOpen(
|
||||
override val menu: Menu<*>,
|
||||
override val player: Player,
|
||||
override var canceled: Boolean = false,
|
||||
) : MenuPlayerCancellable
|
||||
|
||||
public class MenuPlayerOpen(
|
||||
override val menu: Menu<*>,
|
||||
override val player: Player,
|
||||
override val inventory: Inventory,
|
||||
) : MenuPlayerInventory
|
||||
|
||||
public class MenuPlayerUpdate(
|
||||
override val menu: Menu<*>,
|
||||
override val player: Player,
|
||||
override val inventory: Inventory,
|
||||
public var title: String,
|
||||
) : MenuPlayerInventory
|
||||
|
||||
public class MenuPlayerClose(
|
||||
override val menu: Menu<*>,
|
||||
override val player: Player,
|
||||
) : MenuPlayer
|
||||
|
||||
public interface MenuPlayerMove : MenuPlayerInventory, MenuPlayerCancellable {
|
||||
public val toMoveSlot: Int
|
||||
public val toMoveItem: ItemStack?
|
||||
}
|
||||
|
||||
public class MenuPlayerMoveTo(
|
||||
menu: Menu<*>,
|
||||
player: Player,
|
||||
inventory: Inventory,
|
||||
canceled: Boolean,
|
||||
override val toMoveSlot: Int,
|
||||
override val toMoveItem: ItemStack?,
|
||||
public var targetSlot: Int,
|
||||
) : MenuPlayerInteract(menu, player, inventory, canceled), MenuPlayerMove {
|
||||
|
||||
public var targetCurrentItem: ItemStack?
|
||||
set(value) {
|
||||
setItem(targetSlot, value)
|
||||
}
|
||||
get() = getItem(targetSlot)
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package tech.carcadex.kotlinbukkitkit.menu
|
||||
|
||||
import tech.carcadex.kotlinbukkitkit.menu.slot.Slot
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.inventory.Inventory
|
||||
|
||||
public fun calculateSlot(line: Int, slot: Int): Int = calculateStartLine(line) + slot
|
||||
|
||||
public fun calculateStartLine(line: Int): Int = calculateEndLine(line) - 9
|
||||
public fun calculateEndLine(line: Int): Int = line * 9
|
||||
|
||||
public fun rawSlot(slot: Int): Int = slot - 1
|
||||
|
||||
public fun Menu<*>.slotOrBaseSlot(slot: Int): Slot = slots[slot] ?: baseSlot
|
||||
public fun Menu<*>.rangeOfSlots(): IntRange = 1..calculateEndLine(lines)
|
||||
public fun Menu<*>.viewersFromPlayers(players: Set<Player>): Map<Player, Inventory> = viewers.filterKeys { it in players }
|
||||
|
||||
public fun Menu<*>.slotsWithBaseSlot(): List<Slot> = slots.values + baseSlot
|
||||
|
||||
public fun Menu<*>.hasPlayer(player: Player): Boolean = viewers.containsKey(player)
|
||||
public fun Menu<*>.takeIfHasPlayer(player: Player): Menu<*>? = if (hasPlayer(player)) this else null
|
||||
|
||||
public fun inventoryIsMenu(inventory: Inventory): Boolean = inventory.holder is Menu<*>
|
||||
|
||||
public fun getMenuFromInventory(inventory: Inventory): Menu<*>? = inventory.holder as? Menu<*>
|
||||
|
||||
public fun getMenuFromPlayer(player: Player): Menu<*>? {
|
||||
return getMenuFromInventory(player.openInventory.topInventory)?.takeIfHasPlayer(player)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package tech.carcadex.kotlinbukkitkit.menu.dsl
|
||||
|
||||
import org.bukkit.entity.Player
|
||||
import java.util.*
|
||||
|
||||
public fun MenuDSL.putPlayerData(player: Player, key: String, value: Any): Any? = playerData.getOrPut(player, { WeakHashMap() }).put(key, value)
|
||||
|
||||
public fun MenuDSL.getPlayerData(player: Player, key: String): Any? = playerData.get(player)?.get(key)
|
||||
@@ -0,0 +1,46 @@
|
||||
package tech.carcadex.kotlinbukkitkit.menu.dsl
|
||||
|
||||
import tech.carcadex.kotlinbukkitkit.menu.Menu
|
||||
import tech.carcadex.kotlinbukkitkit.menu.calculateSlot
|
||||
import tech.carcadex.kotlinbukkitkit.menu.dsl.slot.SlotDSL
|
||||
import org.bukkit.inventory.ItemStack
|
||||
|
||||
public inline fun MenuDSL.slot(
|
||||
line: Int,
|
||||
slot: Int,
|
||||
item: ItemStack?,
|
||||
block: SlotDSL.() -> Unit = {},
|
||||
): SlotDSL = slot(calculateSlot(line, slot), item, block)
|
||||
|
||||
public inline fun MenuDSL.slot(
|
||||
slot: Int,
|
||||
item: ItemStack?,
|
||||
block: SlotDSL.() -> Unit = {},
|
||||
): SlotDSL = baseSlot.clone(item).apply(block).also {
|
||||
setSlot(slot, it)
|
||||
}
|
||||
|
||||
public interface MenuDSL : Menu<SlotDSL> {
|
||||
|
||||
override val eventHandler: MenuEventHandlerDSL
|
||||
|
||||
public fun onUpdate(update: MenuPlayerUpdateEvent) {
|
||||
eventHandler.updateCallbacks.add(update)
|
||||
}
|
||||
|
||||
public fun onClose(close: MenuPlayerCloseEvent) {
|
||||
eventHandler.closeCallbacks.add(close)
|
||||
}
|
||||
|
||||
public fun onMoveToMenu(moveToMenu: MenuPlayerMoveToEvent) {
|
||||
eventHandler.moveToMenuCallbacks.add(moveToMenu)
|
||||
}
|
||||
|
||||
public fun preOpen(preOpen: MenuPlayerPreOpenEvent) {
|
||||
eventHandler.preOpenCallbacks.add(preOpen)
|
||||
}
|
||||
|
||||
public fun onOpen(open: MenuPlayerOpenEvent) {
|
||||
eventHandler.openCallbacks.add(open)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
package tech.carcadex.kotlinbukkitkit.menu.dsl
|
||||
|
||||
import tech.carcadex.kotlinbukkitkit.architecture.extensions.WithPlugin
|
||||
import tech.carcadex.kotlinbukkitkit.extensions.task
|
||||
import tech.carcadex.kotlinbukkitkit.menu.MenuControllerPlugins
|
||||
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerClose
|
||||
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerOpen
|
||||
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerPreOpen
|
||||
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerUpdate
|
||||
import tech.carcadex.kotlinbukkitkit.menu.dsl.slot.SlotDSL
|
||||
import tech.carcadex.kotlinbukkitkit.menu.dsl.slot.SlotDSLImpl
|
||||
import tech.carcadex.kotlinbukkitkit.menu.dsl.slot.SlotEventHandlerDSL
|
||||
import tech.carcadex.kotlinbukkitkit.menu.rangeOfSlots
|
||||
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotRender
|
||||
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotUpdate
|
||||
import tech.carcadex.kotlinbukkitkit.menu.slot.Slot
|
||||
import tech.carcadex.kotlinbukkitkit.menu.slotOrBaseSlot
|
||||
import tech.carcadex.kotlinbukkitkit.menu.viewersFromPlayers
|
||||
import org.bukkit.Bukkit
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.inventory.Inventory
|
||||
import org.bukkit.plugin.Plugin
|
||||
import org.bukkit.scheduler.BukkitTask
|
||||
import java.util.*
|
||||
|
||||
public inline fun WithPlugin<*>.menu(
|
||||
displayName: String,
|
||||
lines: Int,
|
||||
cancelOnClick: Boolean = true,
|
||||
block: MenuDSL.() -> Unit,
|
||||
): MenuDSL = plugin.menu(displayName, lines, cancelOnClick, block)
|
||||
|
||||
public inline fun Plugin.menu(
|
||||
displayName: String,
|
||||
lines: Int,
|
||||
cancelOnClick: Boolean = true,
|
||||
block: MenuDSL.() -> Unit,
|
||||
): MenuDSL = menu(displayName, lines, this, cancelOnClick, block)
|
||||
|
||||
public inline fun menu(
|
||||
displayName: String,
|
||||
lines: Int,
|
||||
plugin: Plugin,
|
||||
cancelOnClick: Boolean = true,
|
||||
block: MenuDSL.() -> Unit,
|
||||
): MenuDSL = MenuDSLImpl(plugin, displayName, lines, cancelOnClick).apply(block)
|
||||
|
||||
public class MenuDSLImpl(
|
||||
public override val plugin: Plugin,
|
||||
override var title: String,
|
||||
override var lines: Int,
|
||||
override var cancelOnClick: Boolean,
|
||||
) : MenuDSL {
|
||||
|
||||
init {
|
||||
MenuControllerPlugins.ensureInitialized(plugin)
|
||||
}
|
||||
|
||||
private var task: BukkitTask? = null
|
||||
override var updateDelay: Long = 0
|
||||
set(value) {
|
||||
field = value
|
||||
removeTask()
|
||||
if (value > 0 && viewers.isNotEmpty()) {
|
||||
setNewTask()
|
||||
}
|
||||
}
|
||||
|
||||
override val viewers: MutableMap<Player, Inventory> = WeakHashMap()
|
||||
override val slots: TreeMap<Int, SlotDSL> = TreeMap()
|
||||
|
||||
override val data: WeakHashMap<String, Any> = WeakHashMap<String, Any>()
|
||||
override val playerData: WeakHashMap<Player, WeakHashMap<String, Any>> = WeakHashMap<Player, WeakHashMap<String, Any>>()
|
||||
|
||||
override val eventHandler: MenuEventHandlerDSL = MenuEventHandlerDSL(this)
|
||||
|
||||
override var baseSlot: SlotDSL = SlotDSLImpl(null, cancelOnClick, SlotEventHandlerDSL())
|
||||
|
||||
override fun setSlot(slot: Int, slotObj: SlotDSL) {
|
||||
slots.put(slot, slotObj)
|
||||
}
|
||||
|
||||
override fun update(players: Set<Player>) {
|
||||
val viewers = viewersFromPlayers(players)
|
||||
for ((player, inventory) in viewers) {
|
||||
val update = MenuPlayerUpdate(this, player, inventory, title)
|
||||
eventHandler.update(update)
|
||||
|
||||
// TODO title
|
||||
/*if(update.title != title)*/
|
||||
|
||||
for (i in rangeOfSlots()) {
|
||||
val slot = slotOrBaseSlot(i)
|
||||
updateSlotOnlyPos(i, slot, player, inventory)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateSlot(slot: Slot, players: Set<Player>) {
|
||||
val slots: Map<Int, SlotDSL> = if (slot === baseSlot) {
|
||||
rangeOfSlots().mapNotNull { if (slots[it] == null) it to slot else null }.toMap()
|
||||
} else {
|
||||
rangeOfSlots().mapNotNull { if (slot === slots[it]) it to slot else null }.toMap()
|
||||
}
|
||||
|
||||
for ((player, inventory) in viewersFromPlayers(players)) {
|
||||
for ((pos, s) in slots) {
|
||||
updateSlotOnlyPos(pos, s, player, inventory)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateSlotOnlyPos(pos: Int, slot: Slot, player: Player, inventory: Inventory) {
|
||||
val slotUpdate = MenuPlayerSlotUpdate(this, pos, slot, player, inventory)
|
||||
slot.eventHandler.update(slotUpdate)
|
||||
}
|
||||
|
||||
override fun openToPlayer(vararg players: Player) {
|
||||
for (player in players) {
|
||||
close(player, false)
|
||||
|
||||
try {
|
||||
val inv = inventory
|
||||
|
||||
viewers.put(player, inv)
|
||||
|
||||
val preOpen = MenuPlayerPreOpen(this, player)
|
||||
eventHandler.preOpen(preOpen)
|
||||
|
||||
if (preOpen.canceled) return
|
||||
|
||||
for (i in rangeOfSlots()) {
|
||||
val slot = slotOrBaseSlot(i)
|
||||
|
||||
val render = MenuPlayerSlotRender(this, i, slot, player, inv)
|
||||
|
||||
slot.eventHandler.render(render)
|
||||
}
|
||||
|
||||
player.openInventory(inv)
|
||||
|
||||
val open = MenuPlayerOpen(this, player, inventory)
|
||||
eventHandler.open(open)
|
||||
|
||||
if (task == null && updateDelay > 0 && viewers.isNotEmpty()) {
|
||||
setNewTask()
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
e.printStackTrace()
|
||||
removePlayer(player, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getInventory(): Inventory {
|
||||
val slots = rangeOfSlots()
|
||||
val inventory = Bukkit.createInventory(this, slots.endInclusive, title)
|
||||
|
||||
for (i in slots) {
|
||||
val slot = slotOrBaseSlot(i)
|
||||
|
||||
val item = slot.item?.clone()
|
||||
inventory.setItem(i - 1, item)
|
||||
}
|
||||
|
||||
return inventory
|
||||
}
|
||||
|
||||
private fun removePlayer(player: Player, closeInventory: Boolean): Boolean {
|
||||
if (closeInventory) player.closeInventory()
|
||||
|
||||
val viewing = viewers.remove(player) != null
|
||||
if (viewing) {
|
||||
clearPlayerData(player)
|
||||
}
|
||||
|
||||
return viewing
|
||||
}
|
||||
|
||||
override fun close(player: Player, closeInventory: Boolean) {
|
||||
if (removePlayer(player, closeInventory)) {
|
||||
val menuClose = MenuPlayerClose(this, player)
|
||||
eventHandler.close(menuClose)
|
||||
|
||||
if (task != null && updateDelay > 0 && viewers.isEmpty()) {
|
||||
removeTask()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setNewTask() {
|
||||
task = task(repeatDelay = updateDelay) { update() }
|
||||
}
|
||||
|
||||
private fun removeTask() {
|
||||
task?.cancel()
|
||||
task = null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package tech.carcadex.kotlinbukkitkit.menu.dsl
|
||||
|
||||
import tech.carcadex.kotlinbukkitkit.menu.MenuEventHandler
|
||||
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerClose
|
||||
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerMoveTo
|
||||
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerOpen
|
||||
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerPreOpen
|
||||
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerUpdate
|
||||
|
||||
public typealias MenuPlayerUpdateEvent = MenuPlayerUpdate.() -> Unit
|
||||
public typealias MenuPlayerCloseEvent = MenuPlayerClose.() -> Unit
|
||||
public typealias MenuPlayerMoveToEvent = MenuPlayerMoveTo.() -> Unit
|
||||
|
||||
public typealias MenuPlayerPreOpenEvent = MenuPlayerPreOpen.() -> Unit
|
||||
public typealias MenuPlayerOpenEvent = MenuPlayerOpen.() -> Unit
|
||||
|
||||
public class MenuEventHandlerDSL(public val menu: MenuDSL) : MenuEventHandler {
|
||||
|
||||
internal val updateCallbacks: MutableList<MenuPlayerUpdateEvent> = mutableListOf()
|
||||
internal val closeCallbacks: MutableList<MenuPlayerCloseEvent> = mutableListOf()
|
||||
internal val moveToMenuCallbacks: MutableList<MenuPlayerMoveToEvent> = mutableListOf()
|
||||
internal val preOpenCallbacks: MutableList<MenuPlayerPreOpenEvent> = mutableListOf()
|
||||
internal val openCallbacks: MutableList<MenuPlayerOpenEvent> = mutableListOf()
|
||||
|
||||
override fun update(update: MenuPlayerUpdate) {
|
||||
for (callback in updateCallbacks) {
|
||||
callback(update)
|
||||
}
|
||||
}
|
||||
|
||||
override fun close(close: MenuPlayerClose) {
|
||||
for (callback in closeCallbacks) {
|
||||
callback(close)
|
||||
}
|
||||
}
|
||||
|
||||
override fun moveToMenu(moveToMenu: MenuPlayerMoveTo) {
|
||||
for (callback in moveToMenuCallbacks) {
|
||||
callback(moveToMenu)
|
||||
}
|
||||
}
|
||||
|
||||
override fun preOpen(preOpen: MenuPlayerPreOpen) {
|
||||
for (callback in preOpenCallbacks) {
|
||||
callback(preOpen)
|
||||
}
|
||||
}
|
||||
|
||||
override fun open(open: MenuPlayerOpen) {
|
||||
for (callback in openCallbacks) {
|
||||
callback(open)
|
||||
}
|
||||
}
|
||||
}
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
package tech.carcadex.kotlinbukkitkit.menu.dsl.pagination
|
||||
|
||||
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayer
|
||||
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerInventory
|
||||
import tech.carcadex.kotlinbukkitkit.menu.dsl.MenuDSL
|
||||
import tech.carcadex.kotlinbukkitkit.menu.dsl.pagination.slot.PaginationSlotDSL
|
||||
import tech.carcadex.kotlinbukkitkit.menu.dsl.slot.SlotDSL
|
||||
import tech.carcadex.kotlinbukkitkit.utility.collections.ObservableCollection
|
||||
import org.bukkit.entity.Player
|
||||
import java.lang.IllegalArgumentException
|
||||
import java.util.TreeMap
|
||||
import java.util.WeakHashMap
|
||||
|
||||
public typealias ItemsProvider<T> = () -> ObservableCollection<T>
|
||||
public typealias ItemsAdapter<T> = MenuPlayer.(List<T>) -> List<T>
|
||||
|
||||
public enum class Orientation { HORIZONTAL, VERTICAL }
|
||||
|
||||
public inline fun <T> MenuPagination<T>.slot(
|
||||
builder: PaginationSlotDSL<T>.() -> Unit,
|
||||
) {
|
||||
for (paginationSlot in paginationSlots.values) {
|
||||
paginationSlot.builder()
|
||||
}
|
||||
}
|
||||
|
||||
public fun MenuDSL.setPlayerOpenPage(player: Player, page: Int) {
|
||||
playerData.put(
|
||||
player,
|
||||
WeakHashMap<String, Any>().apply { put(PAGINATION_OPEN_PAGE_KEY, page) },
|
||||
)
|
||||
}
|
||||
public inline fun <T> MenuDSL.pagination(
|
||||
itemsProvider: ObservableCollection<T>,
|
||||
nextPageSlot: SlotDSL,
|
||||
previousPageSlot: SlotDSL,
|
||||
autoUpdateSwitchPageSlot: Boolean = true,
|
||||
startLine: Int = 1,
|
||||
endLine: Int = lines - 1,
|
||||
startSlot: Int = 1,
|
||||
endSlot: Int = 9,
|
||||
orientation: Orientation = Orientation.HORIZONTAL,
|
||||
noinline itemsAdapterOnOpen: ItemsAdapter<T>? = null,
|
||||
noinline itemsAdapterOnUpdate: ItemsAdapter<T>? = null,
|
||||
builder: MenuPaginationImpl<T>.() -> Unit,
|
||||
): MenuPaginationImpl<T> {
|
||||
return pagination(
|
||||
{ itemsProvider },
|
||||
nextPageSlot,
|
||||
previousPageSlot,
|
||||
autoUpdateSwitchPageSlot,
|
||||
startLine,
|
||||
endLine,
|
||||
startSlot,
|
||||
endSlot,
|
||||
orientation,
|
||||
itemsAdapterOnOpen,
|
||||
itemsAdapterOnUpdate,
|
||||
builder,
|
||||
)
|
||||
}
|
||||
|
||||
public inline fun <T> MenuDSL.pagination(
|
||||
noinline itemsProvider: ItemsProvider<T>,
|
||||
nextPageSlot: SlotDSL,
|
||||
previousPageSlot: SlotDSL,
|
||||
autoUpdateSwitchPageSlot: Boolean = true,
|
||||
startLine: Int = 1,
|
||||
endLine: Int = lines - 1,
|
||||
startSlot: Int = 1,
|
||||
endSlot: Int = 9,
|
||||
orientation: Orientation = Orientation.HORIZONTAL,
|
||||
noinline itemsAdapterOnOpen: ItemsAdapter<T>? = null,
|
||||
noinline itemsAdapterOnUpdate: ItemsAdapter<T>? = null,
|
||||
builder: MenuPaginationImpl<T>.() -> Unit,
|
||||
): MenuPaginationImpl<T> {
|
||||
if (startSlot > endSlot) throw IllegalArgumentException()
|
||||
if (startLine > endLine) throw IllegalArgumentException()
|
||||
|
||||
return MenuPaginationImpl(
|
||||
this,
|
||||
itemsProvider,
|
||||
nextPageSlot,
|
||||
previousPageSlot,
|
||||
autoUpdateSwitchPageSlot,
|
||||
startLine,
|
||||
endLine,
|
||||
startSlot,
|
||||
endSlot,
|
||||
orientation,
|
||||
itemsAdapterOnOpen,
|
||||
itemsAdapterOnUpdate,
|
||||
).apply(builder)
|
||||
}
|
||||
|
||||
public interface MenuPagination<T> {
|
||||
public val menu: MenuDSL
|
||||
public val paginationSlots: TreeMap<Int, PaginationSlotDSL<T>>
|
||||
public val paginationEventHandler: PaginationEventHandler
|
||||
|
||||
public val itemsProvider: ItemsProvider<T>
|
||||
|
||||
public val nextPageSlot: SlotDSL
|
||||
public val previousPageSlot: SlotDSL
|
||||
|
||||
public val autoUpdateSwitchPageSlot: Boolean
|
||||
|
||||
public val startLine: Int
|
||||
public val endLine: Int
|
||||
|
||||
public val startSlot: Int
|
||||
public val endSlot: Int
|
||||
|
||||
public val orientation: Orientation
|
||||
|
||||
public val itemsAdapterOnOpen: ItemsAdapter<T>?
|
||||
public val itemsAdapterOnUpdate: ItemsAdapter<T>?
|
||||
|
||||
public fun onPageChange(pageChange: MenuPlayerPageChangeEvent) {
|
||||
paginationEventHandler.pageChangeCallbacks.add(pageChange)
|
||||
}
|
||||
|
||||
public fun onPageAvailable(pageAvailable: MenuPlayerPageAvailableEvent) {
|
||||
paginationEventHandler.pageAvailableCallbacks.add(pageAvailable)
|
||||
}
|
||||
|
||||
public fun hasPreviousPage(player: Player): Boolean
|
||||
|
||||
public fun hasNextPage(player: Player): Boolean
|
||||
|
||||
public fun getPlayerCurrentPage(player: Player): Int
|
||||
|
||||
public fun updateItemsToPlayer(menuPlayer: MenuPlayerInventory)
|
||||
}
|
||||
+265
@@ -0,0 +1,265 @@
|
||||
package tech.carcadex.kotlinbukkitkit.menu.dsl.pagination
|
||||
|
||||
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerInventory
|
||||
import tech.carcadex.kotlinbukkitkit.menu.calculateSlot
|
||||
import tech.carcadex.kotlinbukkitkit.menu.dsl.MenuDSL
|
||||
import tech.carcadex.kotlinbukkitkit.menu.dsl.pagination.slot.PaginationSlotDSL
|
||||
import tech.carcadex.kotlinbukkitkit.menu.dsl.pagination.slot.PaginationSlotDSLImpl
|
||||
import tech.carcadex.kotlinbukkitkit.menu.dsl.slot
|
||||
import tech.carcadex.kotlinbukkitkit.menu.dsl.slot.SlotDSL
|
||||
import org.bukkit.entity.Player
|
||||
import java.util.*
|
||||
|
||||
internal const val PAGINATION_OPEN_PAGE_KEY = "PAGINATION:open_page"
|
||||
|
||||
public class MenuPaginationImpl<T>(
|
||||
override val menu: MenuDSL,
|
||||
override val itemsProvider: ItemsProvider<T>,
|
||||
override val nextPageSlot: SlotDSL,
|
||||
override val previousPageSlot: SlotDSL,
|
||||
override val autoUpdateSwitchPageSlot: Boolean,
|
||||
override val startLine: Int = 1,
|
||||
override val endLine: Int = menu.lines - 1,
|
||||
override val startSlot: Int = 1,
|
||||
override val endSlot: Int = 9,
|
||||
override val orientation: Orientation = Orientation.HORIZONTAL,
|
||||
override val itemsAdapterOnOpen: ItemsAdapter<T>? = null,
|
||||
override val itemsAdapterOnUpdate: ItemsAdapter<T>? = null,
|
||||
) : MenuPagination<T> {
|
||||
override val paginationEventHandler: PaginationEventHandler = PaginationEventHandler()
|
||||
|
||||
override val paginationSlots: TreeMap<Int, PaginationSlotDSL<T>> = TreeMap()
|
||||
internal val currentPlayerPage = WeakHashMap<Player, Int>()
|
||||
|
||||
// should be null when is not mapped (for not allocate a new list everytime)
|
||||
internal val currentPlayerItems = WeakHashMap<Player, List<T>>()
|
||||
|
||||
internal val itemSlotData = WeakHashMap<T, WeakHashMap<String, Any>>()
|
||||
internal val itemPlayerSlotData = WeakHashMap<T, WeakHashMap<Player, WeakHashMap<String, Any>>>()
|
||||
|
||||
init {
|
||||
// setup next page button click
|
||||
nextPageSlot.onClick {
|
||||
if (hasNextPage(player)) {
|
||||
nextPage(this)
|
||||
}
|
||||
}
|
||||
|
||||
// setup previous page button click
|
||||
previousPageSlot.onClick {
|
||||
if (hasPreviousPage(player)) {
|
||||
previousPage(this)
|
||||
}
|
||||
}
|
||||
|
||||
menu.preOpen {
|
||||
// adapting items for player
|
||||
val items = itemsAdapterOnOpen?.let { it(getPlayerItems(player).toList()) }
|
||||
if (items != null) {
|
||||
currentPlayerItems[player] = items
|
||||
}
|
||||
|
||||
// set the menu page if player informed in [MenuDSL.setPlayerOpenPage]
|
||||
val openPage = menu.playerData.get(player)?.get(PAGINATION_OPEN_PAGE_KEY) as? Int?
|
||||
if (openPage != null) {
|
||||
// check if openPage is valid or cancel and trigger a event
|
||||
if (isPageAvailable(player, openPage)) {
|
||||
currentPlayerPage[player] = openPage
|
||||
} else {
|
||||
canceled = true
|
||||
paginationEventHandler.pageAvailable(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cleaning player data on menu close
|
||||
menu.onClose {
|
||||
currentPlayerPage.remove(player)
|
||||
currentPlayerItems.remove(player)
|
||||
|
||||
for (value in itemPlayerSlotData.values) {
|
||||
value.remove(player)
|
||||
}
|
||||
}
|
||||
|
||||
// if enabled, update next and previous slots when page change
|
||||
if (autoUpdateSwitchPageSlot) {
|
||||
onPageChange {
|
||||
menu.updateSlot(nextPageSlot, player)
|
||||
menu.updateSlot(previousPageSlot, player)
|
||||
}
|
||||
}
|
||||
|
||||
// setup pagination slots
|
||||
for (line in startLine..endLine) {
|
||||
for (slotPos in startSlot..endSlot) {
|
||||
val menuSlot = calculateSlot(line, slotPos)
|
||||
val slotRoot = menu.slot(menuSlot, null)
|
||||
|
||||
val paginationSlot = PaginationSlotDSLImpl(
|
||||
this,
|
||||
slotRoot,
|
||||
)
|
||||
|
||||
slotRoot.apply {
|
||||
fun getCurrentItemForPlayer(player: Player): T? {
|
||||
return getCurrentItemForPlayer(
|
||||
menuSlot,
|
||||
getPlayerCurrentPage(player),
|
||||
player,
|
||||
)
|
||||
}
|
||||
|
||||
onRender {
|
||||
paginationSlot.paginationEventHandler.handleRender(
|
||||
getCurrentItemForPlayer(player),
|
||||
this,
|
||||
)
|
||||
}
|
||||
onUpdate {
|
||||
paginationSlot.paginationEventHandler.handleUpdate(
|
||||
getCurrentItemForPlayer(player),
|
||||
this,
|
||||
)
|
||||
}
|
||||
onClick {
|
||||
paginationSlot.paginationEventHandler.handleInteract(
|
||||
getCurrentItemForPlayer(player),
|
||||
this,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
paginationSlots.put(menuSlot, paginationSlot)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun hasPreviousPage(player: Player): Boolean {
|
||||
return isPageAvailable(player, getPlayerCurrentPage(player) - 1)
|
||||
}
|
||||
override fun hasNextPage(player: Player): Boolean {
|
||||
return isPageAvailable(player, getPlayerCurrentPage(player) + 1)
|
||||
}
|
||||
|
||||
override fun getPlayerCurrentPage(player: Player): Int = currentPlayerPage.getOrDefault(player, 1)
|
||||
|
||||
/**
|
||||
* Update current item list calling itemsUpdateFilter
|
||||
*/
|
||||
override fun updateItemsToPlayer(menuPlayer: MenuPlayerInventory) {
|
||||
val player = menuPlayer.player
|
||||
|
||||
// checking if adapter is not null, other wise, ignore
|
||||
if (itemsAdapterOnUpdate != null) {
|
||||
val items = itemsAdapterOnUpdate.let { menuPlayer.it(itemsProvider().toList()) }
|
||||
|
||||
currentPlayerItems[player] = items
|
||||
|
||||
// setting player to the last page if he is not in a page with items after adapting
|
||||
if (!isPageAvailable(player, getPlayerCurrentPage(player))) {
|
||||
currentPlayerPage[player] = maxPages(player)
|
||||
}
|
||||
|
||||
// calling update slot to the PaginationSlotDSL
|
||||
forEachSlot { slotPos, pageSlot ->
|
||||
val currentPage = getPlayerCurrentPage(player)
|
||||
|
||||
val item: T? = getCurrentItemForPlayer(slotPos, currentPage, player)
|
||||
|
||||
pageSlot.updateSlot(item, item, slotPos, menuPlayer)
|
||||
}
|
||||
|
||||
// calling event because this remove items causing no more page needed
|
||||
paginationEventHandler.pageChange(menuPlayer)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun getCurrentItemForPlayer(slotPos: Int, page: Int, player: Player): T? {
|
||||
val items = getPlayerItems(player)
|
||||
|
||||
val startLineNotUsage = startLine - 1
|
||||
val lineEndSlotNotUsage = 9 - endSlot
|
||||
val startSlotNotUsage = startSlot - 1
|
||||
|
||||
val startLineSlotsUsage = startLineNotUsage * 9
|
||||
val currentLine = (slotPos / 9) + 1
|
||||
val currentLineUsage = currentLine - startLineNotUsage
|
||||
|
||||
val currentUsageStartSlots = startSlotNotUsage * currentLineUsage
|
||||
val currentEndSlotUsage = if (currentLineUsage > 1) lineEndSlotNotUsage * (currentLineUsage - 1) else 0
|
||||
|
||||
val slotItemIndex = pageStartIndex(page) + (slotPos - startLineSlotsUsage - currentUsageStartSlots - currentEndSlotUsage)
|
||||
|
||||
return items.asSequence().elementAtOrNull(slotItemIndex - 1)
|
||||
}
|
||||
|
||||
internal fun nextPage(menuPlayerInventory: MenuPlayerInventory) {
|
||||
val nextPage = getPlayerCurrentPage(menuPlayerInventory.player) + 1
|
||||
|
||||
changePage(menuPlayerInventory, nextPage)
|
||||
}
|
||||
|
||||
internal fun previousPage(menuPlayerInventory: MenuPlayerInventory) {
|
||||
val previousPage = getPlayerCurrentPage(menuPlayerInventory.player) - 1
|
||||
|
||||
changePage(menuPlayerInventory, previousPage)
|
||||
}
|
||||
|
||||
private fun changePage(menuPlayerInventory: MenuPlayerInventory, nextPage: Int) {
|
||||
val player = menuPlayerInventory.player
|
||||
|
||||
val currentPage = getPlayerCurrentPage(player)
|
||||
|
||||
currentPlayerPage[player] = nextPage
|
||||
|
||||
forEachSlot { slotPos, pageSlot ->
|
||||
val actualItem: T? = getCurrentItemForPlayer(slotPos, currentPage, player)
|
||||
|
||||
val nextItem: T? = getCurrentItemForPlayer(slotPos, nextPage, player)
|
||||
|
||||
pageSlot.updateSlot(actualItem, nextItem, slotPos, menuPlayerInventory, true)
|
||||
|
||||
paginationEventHandler.pageChange(menuPlayerInventory)
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun forEachSlot(
|
||||
block: (slotPos: Int, pageSlot: PaginationSlotDSLImpl<T>) -> Unit,
|
||||
) {
|
||||
for (line in startLine..endLine) {
|
||||
for (slot in startSlot..endSlot) {
|
||||
val slotPos = calculateSlot(line, slot)
|
||||
val pageSlot = paginationSlots[slotPos] as? PaginationSlotDSLImpl<T>
|
||||
?: continue
|
||||
|
||||
block(slotPos, pageSlot)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun pageStartIndex(page: Int) = ((page - 1) * maxSlotPerPage())
|
||||
|
||||
private fun getPlayerItems(player: Player) = currentPlayerItems[player] ?: itemsProvider()
|
||||
|
||||
private fun maxSlotPerPage() = (endLine - startLine + 1) * (endSlot - startSlot + 1)
|
||||
|
||||
private fun maxPages(player: Player): Int {
|
||||
val itemsCount = getPlayerItems(player).size
|
||||
|
||||
val maxPerPage = maxSlotPerPage()
|
||||
|
||||
var pages = (itemsCount / maxPerPage).toInt()
|
||||
val mod = itemsCount % maxPerPage
|
||||
|
||||
if (mod > 0) pages++
|
||||
|
||||
return pages
|
||||
}
|
||||
|
||||
private fun isPageAvailable(player: Player, page: Int): Boolean {
|
||||
val maxPages = maxPages(player)
|
||||
|
||||
return page in 1..maxPages
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package tech.carcadex.kotlinbukkitkit.menu.dsl.pagination
|
||||
|
||||
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayer
|
||||
|
||||
public typealias MenuPlayerPageChangeEvent = MenuPlayer.() -> Unit
|
||||
public typealias MenuPlayerPageAvailableEvent = MenuPlayer.() -> Unit
|
||||
|
||||
public class PaginationEventHandler {
|
||||
internal val pageChangeCallbacks: MutableList<MenuPlayerPageChangeEvent> = mutableListOf()
|
||||
internal val pageAvailableCallbacks: MutableList<MenuPlayerPageAvailableEvent> = mutableListOf()
|
||||
|
||||
public fun pageChange(pageChange: MenuPlayer) {
|
||||
for (callback in pageChangeCallbacks) {
|
||||
callback(pageChange)
|
||||
}
|
||||
}
|
||||
|
||||
public fun pageAvailable(pageAvailable: MenuPlayer) {
|
||||
for (callback in pageAvailableCallbacks) {
|
||||
callback(pageAvailable)
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package tech.carcadex.kotlinbukkitkit.menu.dsl.pagination.slot
|
||||
|
||||
import tech.carcadex.kotlinbukkitkit.menu.dsl.slot.SlotDSL
|
||||
import org.bukkit.entity.Player
|
||||
import java.util.*
|
||||
|
||||
public interface PaginationSlotDSL<T> {
|
||||
public val slotRoot: SlotDSL
|
||||
|
||||
public val paginationEventHandler: PaginationSlotEventHandler<T>
|
||||
|
||||
public val slotData: WeakHashMap<String, Any>
|
||||
public val playerSlotData: WeakHashMap<Player, WeakHashMap<String, Any>>
|
||||
|
||||
/**
|
||||
* Cancel the interaction with this slot
|
||||
*/
|
||||
public var cancel: Boolean
|
||||
|
||||
public fun onPageChange(pageChange: MenuPlayerSlotPageChangeEvent<T>) {
|
||||
paginationEventHandler.pageChangeCallbacks.add(pageChange)
|
||||
}
|
||||
|
||||
public fun onClick(click: MenuPlayerPageSlotInteractEvent<T>) {
|
||||
paginationEventHandler.interactCallbacks.add(click)
|
||||
}
|
||||
|
||||
public fun onRender(render: MenuPlayerPageSlotRenderEvent<T>) {
|
||||
paginationEventHandler.renderCallbacks.add(render)
|
||||
}
|
||||
|
||||
public fun onUpdate(update: MenuPlayerPageSlotUpdateEvent<T>) {
|
||||
paginationEventHandler.updateCallbacks.add(update)
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
package tech.carcadex.kotlinbukkitkit.menu.dsl.pagination.slot
|
||||
|
||||
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerInventory
|
||||
import tech.carcadex.kotlinbukkitkit.menu.dsl.pagination.MenuPaginationImpl
|
||||
import tech.carcadex.kotlinbukkitkit.menu.dsl.slot.SlotDSL
|
||||
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotRender
|
||||
import org.bukkit.entity.Player
|
||||
import java.util.*
|
||||
|
||||
public class PaginationSlotDSLImpl<T>(
|
||||
private val pagination: MenuPaginationImpl<T>,
|
||||
override val slotRoot: SlotDSL,
|
||||
) : PaginationSlotDSL<T> {
|
||||
override val paginationEventHandler: PaginationSlotEventHandler<T> = PaginationSlotEventHandler<T>()
|
||||
|
||||
override var cancel: Boolean
|
||||
get() = slotRoot.cancel
|
||||
set(value) { slotRoot.cancel = value }
|
||||
|
||||
override val slotData: WeakHashMap<String, Any>
|
||||
get() = slotRoot.slotData
|
||||
override val playerSlotData: WeakHashMap<Player, WeakHashMap<String, Any>>
|
||||
get() = slotRoot.playerSlotData
|
||||
|
||||
internal fun updateSlot(
|
||||
actualItem: T?,
|
||||
nextItem: T?,
|
||||
slotPos: Int,
|
||||
menuPlayerInventory: MenuPlayerInventory,
|
||||
isPageChange: Boolean = false,
|
||||
) {
|
||||
if (isPageChange) {
|
||||
relocateSlotData(actualItem, nextItem)
|
||||
|
||||
// triggering event
|
||||
paginationEventHandler.handlePageChange(
|
||||
actualItem,
|
||||
MenuPlayerSlotPageChange(
|
||||
pagination.menu,
|
||||
slotPos,
|
||||
slotRoot,
|
||||
menuPlayerInventory.player,
|
||||
menuPlayerInventory.inventory,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// cleaning item in the inventory slot
|
||||
menuPlayerInventory.setItem(slotPos, null)
|
||||
|
||||
paginationEventHandler.handleRender(
|
||||
nextItem,
|
||||
MenuPlayerSlotRender(
|
||||
pagination.menu,
|
||||
slotPos,
|
||||
slotRoot,
|
||||
menuPlayerInventory.player,
|
||||
menuPlayerInventory.inventory,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
internal fun relocateSlotData(actualItem: T?, nextItem: T?) {
|
||||
if (actualItem != null) {
|
||||
// caching the current Data from Slot
|
||||
val slotData = WeakHashMap(slotData)
|
||||
val playerSlotData = WeakHashMap(playerSlotData)
|
||||
|
||||
if (slotData.isNotEmpty()) {
|
||||
pagination.itemSlotData[actualItem] = slotData
|
||||
}
|
||||
|
||||
if (playerSlotData.isNotEmpty()) {
|
||||
pagination.itemPlayerSlotData[actualItem] = playerSlotData
|
||||
}
|
||||
}
|
||||
|
||||
// cleaning current data in the Slot
|
||||
slotData.clear()
|
||||
playerSlotData.clear()
|
||||
|
||||
if (nextItem != null) {
|
||||
val nextSlotData = pagination.itemSlotData[nextItem]
|
||||
val nextPlayerSlotData = pagination.itemPlayerSlotData[nextItem]
|
||||
|
||||
if (nextSlotData != null) {
|
||||
slotData.putAll(nextSlotData)
|
||||
}
|
||||
if (nextPlayerSlotData != null) {
|
||||
playerSlotData.putAll(nextPlayerSlotData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package tech.carcadex.kotlinbukkitkit.menu.dsl.pagination.slot
|
||||
|
||||
import tech.carcadex.kotlinbukkitkit.menu.Menu
|
||||
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerInventorySlot
|
||||
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotInteract
|
||||
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotRender
|
||||
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotUpdate
|
||||
import tech.carcadex.kotlinbukkitkit.menu.slot.Slot
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.inventory.Inventory
|
||||
|
||||
public typealias MenuPlayerSlotPageChangeEvent<T> = MenuPlayerSlotPageChange.(T?) -> Unit
|
||||
public typealias MenuPlayerPageSlotInteractEvent<T> = MenuPlayerSlotInteract.(T?) -> Unit
|
||||
public typealias MenuPlayerPageSlotRenderEvent<T> = MenuPlayerSlotRender.(T?) -> Unit
|
||||
public typealias MenuPlayerPageSlotUpdateEvent<T> = MenuPlayerSlotUpdate.(T?) -> Unit
|
||||
|
||||
public class PaginationSlotEventHandler<T> {
|
||||
internal val pageChangeCallbacks = mutableListOf<MenuPlayerSlotPageChangeEvent<T>>()
|
||||
internal val interactCallbacks = mutableListOf<MenuPlayerPageSlotInteractEvent<T>>()
|
||||
internal val renderCallbacks = mutableListOf<MenuPlayerPageSlotRenderEvent<T>>()
|
||||
internal val updateCallbacks = mutableListOf<MenuPlayerPageSlotUpdateEvent<T>>()
|
||||
|
||||
public fun handlePageChange(currentItem: T?, pageChange: MenuPlayerSlotPageChange) {
|
||||
for (callback in pageChangeCallbacks) {
|
||||
callback(pageChange, currentItem)
|
||||
}
|
||||
}
|
||||
|
||||
public fun handleRender(currentItem: T?, render: MenuPlayerSlotRender) {
|
||||
for (callback in renderCallbacks) {
|
||||
callback(render, currentItem)
|
||||
}
|
||||
}
|
||||
|
||||
public fun handleUpdate(currentItem: T?, update: MenuPlayerSlotUpdate) {
|
||||
for (callback in updateCallbacks) {
|
||||
callback(update, currentItem)
|
||||
}
|
||||
}
|
||||
|
||||
public fun handleInteract(currentItem: T?, interact: MenuPlayerSlotInteract) {
|
||||
for (callback in interactCallbacks) {
|
||||
callback(interact, currentItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class MenuPlayerSlotPageChange(
|
||||
override val menu: Menu<*>,
|
||||
override val slotPos: Int,
|
||||
override val slot: Slot,
|
||||
override val player: Player,
|
||||
override val inventory: Inventory,
|
||||
) : MenuPlayerInventorySlot
|
||||
@@ -0,0 +1,27 @@
|
||||
package tech.carcadex.kotlinbukkitkit.menu.dsl.slot
|
||||
|
||||
import tech.carcadex.kotlinbukkitkit.menu.slot.Slot
|
||||
import org.bukkit.inventory.ItemStack
|
||||
|
||||
public interface SlotDSL : Slot {
|
||||
|
||||
override val eventHandler: SlotEventHandlerDSL
|
||||
|
||||
public fun onClick(click: MenuPlayerSlotInteractEvent) {
|
||||
eventHandler.interactCallbacks.add(click)
|
||||
}
|
||||
|
||||
public fun onRender(render: MenuPlayerSlotRenderEvent) {
|
||||
eventHandler.renderCallbacks.add(render)
|
||||
}
|
||||
|
||||
public fun onUpdate(update: MenuPlayerSlotUpdateEvent) {
|
||||
eventHandler.updateCallbacks.add(update)
|
||||
}
|
||||
|
||||
public fun onMoveToSlot(moveToSlot: MenuPlayerSlotMoveToEvent) {
|
||||
eventHandler.moveToSlotCallbacks.add(moveToSlot)
|
||||
}
|
||||
|
||||
override fun clone(item: ItemStack?): SlotDSL
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package tech.carcadex.kotlinbukkitkit.menu.dsl.slot
|
||||
|
||||
import tech.carcadex.kotlinbukkitkit.menu.dsl.MenuDSL
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.inventory.ItemStack
|
||||
import java.util.*
|
||||
|
||||
public fun MenuDSL.newSlot(item: ItemStack?, builder: SlotDSL.() -> Unit): SlotDSL {
|
||||
return SlotDSLImpl(item, cancelOnClick).apply(builder)
|
||||
}
|
||||
|
||||
public class SlotDSLImpl(
|
||||
override val item: ItemStack?,
|
||||
override var cancel: Boolean,
|
||||
override val eventHandler: SlotEventHandlerDSL = SlotEventHandlerDSL(),
|
||||
) : SlotDSL {
|
||||
|
||||
override val slotData: WeakHashMap<String, Any> = WeakHashMap()
|
||||
override val playerSlotData: WeakHashMap<Player, WeakHashMap<String, Any>> = WeakHashMap()
|
||||
|
||||
override fun clone(item: ItemStack?): SlotDSL = SlotDSLImpl(item, cancel, eventHandler.clone())
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package tech.carcadex.kotlinbukkitkit.menu.dsl.slot
|
||||
|
||||
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotInteract
|
||||
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotMoveTo
|
||||
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotRender
|
||||
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotUpdate
|
||||
import tech.carcadex.kotlinbukkitkit.menu.slot.SlotEventHandler
|
||||
|
||||
public typealias MenuPlayerSlotInteractEvent = MenuPlayerSlotInteract.() -> Unit
|
||||
public typealias MenuPlayerSlotRenderEvent = MenuPlayerSlotRender.() -> Unit
|
||||
public typealias MenuPlayerSlotUpdateEvent = MenuPlayerSlotUpdate.() -> Unit
|
||||
public typealias MenuPlayerSlotMoveToEvent = MenuPlayerSlotMoveTo.() -> Unit
|
||||
|
||||
public class SlotEventHandlerDSL : SlotEventHandler {
|
||||
|
||||
internal val interactCallbacks = mutableListOf<MenuPlayerSlotInteractEvent>()
|
||||
internal val renderCallbacks = mutableListOf<MenuPlayerSlotRenderEvent>()
|
||||
internal val updateCallbacks = mutableListOf<MenuPlayerSlotUpdateEvent>()
|
||||
internal val moveToSlotCallbacks = mutableListOf<MenuPlayerSlotMoveToEvent>()
|
||||
|
||||
override fun interact(interact: MenuPlayerSlotInteract) {
|
||||
for (callback in interactCallbacks) {
|
||||
callback(interact)
|
||||
}
|
||||
}
|
||||
|
||||
override fun render(render: MenuPlayerSlotRender) {
|
||||
for (callback in renderCallbacks) {
|
||||
callback(render)
|
||||
}
|
||||
}
|
||||
|
||||
override fun update(update: MenuPlayerSlotUpdate) {
|
||||
for (callback in updateCallbacks) {
|
||||
callback(update)
|
||||
}
|
||||
}
|
||||
|
||||
override fun moveToSlot(moveToSlot: MenuPlayerSlotMoveTo) {
|
||||
for (callback in moveToSlotCallbacks) {
|
||||
callback(moveToSlot)
|
||||
}
|
||||
}
|
||||
|
||||
override fun clone(): SlotEventHandlerDSL {
|
||||
return SlotEventHandlerDSL().also {
|
||||
it.interactCallbacks.addAll(interactCallbacks)
|
||||
it.renderCallbacks.addAll(renderCallbacks)
|
||||
it.updateCallbacks.addAll(updateCallbacks)
|
||||
it.moveToSlotCallbacks.addAll(moveToSlotCallbacks)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package tech.carcadex.kotlinbukkitkit.menu.slot
|
||||
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.inventory.ItemStack
|
||||
import java.util.*
|
||||
|
||||
public interface Slot {
|
||||
|
||||
public val item: ItemStack?
|
||||
|
||||
public val eventHandler: SlotEventHandler
|
||||
|
||||
public val slotData: WeakHashMap<String, Any>
|
||||
public val playerSlotData: WeakHashMap<Player, WeakHashMap<String, Any>>
|
||||
|
||||
/**
|
||||
* Cancel the interaction with this slot
|
||||
*/
|
||||
public var cancel: Boolean
|
||||
|
||||
/**
|
||||
* a clone of Slot without slotData and playerSlotData
|
||||
*/
|
||||
public fun clone(item: ItemStack?): Slot
|
||||
|
||||
public fun clearSlotData() {
|
||||
slotData.clear()
|
||||
}
|
||||
|
||||
public fun clearPlayerData(player: Player) {
|
||||
playerSlotData.remove(player)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package tech.carcadex.kotlinbukkitkit.menu.slot
|
||||
|
||||
public interface SlotEventHandler {
|
||||
|
||||
public fun interact(interact: MenuPlayerSlotInteract)
|
||||
|
||||
public fun render(render: MenuPlayerSlotRender)
|
||||
|
||||
public fun update(update: MenuPlayerSlotUpdate)
|
||||
|
||||
public fun moveToSlot(moveToSlot: MenuPlayerSlotMoveTo)
|
||||
|
||||
public fun clone(): SlotEventHandler
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package tech.carcadex.kotlinbukkitkit.menu.slot
|
||||
|
||||
import tech.carcadex.kotlinbukkitkit.menu.Menu
|
||||
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayer
|
||||
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerInteract
|
||||
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerInventory
|
||||
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerMove
|
||||
import org.bukkit.Material
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.event.inventory.ClickType
|
||||
import org.bukkit.event.inventory.InventoryAction
|
||||
import org.bukkit.inventory.Inventory
|
||||
import org.bukkit.inventory.ItemStack
|
||||
import java.util.WeakHashMap
|
||||
|
||||
public val MenuPlayerSlot.rawSlotPos: Int get() = slotPos - 1
|
||||
|
||||
public interface MenuPlayerSlot : MenuPlayer {
|
||||
public val slotPos: Int
|
||||
public val slot: Slot
|
||||
|
||||
public fun putPlayerSlotData(key: String, value: Any) {
|
||||
slot.playerSlotData.getOrPut(player) {
|
||||
WeakHashMap()
|
||||
}[key] = value
|
||||
}
|
||||
|
||||
public fun getPlayerSlotData(key: String): Any? = slot.playerSlotData.get(player)?.get(key)
|
||||
}
|
||||
|
||||
public interface MenuPlayerInventorySlot : MenuPlayerSlot, MenuPlayerInventory {
|
||||
|
||||
public var showingItem: ItemStack?
|
||||
get() = getItem(slotPos)?.takeUnless { it.type == Material.AIR }
|
||||
set(value) = setItem(slotPos, value)
|
||||
|
||||
public fun updateSlotToPlayer() {
|
||||
menu.updateSlot(slot, player)
|
||||
}
|
||||
|
||||
public fun updateSlot() {
|
||||
menu.updateSlot(slot)
|
||||
}
|
||||
}
|
||||
|
||||
public open class MenuPlayerSlotInteract(
|
||||
menu: Menu<*>,
|
||||
override val slotPos: Int,
|
||||
override val slot: Slot,
|
||||
player: Player,
|
||||
inventory: Inventory,
|
||||
canceled: Boolean,
|
||||
public val click: ClickType,
|
||||
public val action: InventoryAction,
|
||||
public val clicked: ItemStack?,
|
||||
public val cursor: ItemStack?,
|
||||
public val hotbarKey: Int,
|
||||
) : MenuPlayerInteract(menu, player, inventory, canceled), MenuPlayerInventorySlot
|
||||
|
||||
public class MenuPlayerSlotMoveTo(
|
||||
override val menu: Menu<*>,
|
||||
override val slotPos: Int,
|
||||
override val slot: Slot,
|
||||
override val player: Player,
|
||||
override val inventory: Inventory,
|
||||
override var canceled: Boolean,
|
||||
override val toMoveSlot: Int,
|
||||
override var toMoveItem: ItemStack?,
|
||||
) : MenuPlayerInventorySlot, MenuPlayerMove
|
||||
|
||||
public class MenuPlayerSlotRender(
|
||||
override val menu: Menu<*>,
|
||||
override val slotPos: Int,
|
||||
override val slot: Slot,
|
||||
override val player: Player,
|
||||
override val inventory: Inventory,
|
||||
) : MenuPlayerInventorySlot
|
||||
|
||||
public class MenuPlayerSlotUpdate(
|
||||
override val menu: Menu<*>,
|
||||
override val slotPos: Int,
|
||||
override val slot: Slot,
|
||||
override val player: Player,
|
||||
override val inventory: Inventory,
|
||||
) : MenuPlayerInventorySlot
|
||||
Reference in new issue
Block a user