From 2c62add7cea75d2aafde2baa0a0d6feb02da4c1c Mon Sep 17 00:00:00 2001 From: Seggan Date: Fri, 20 Sep 2024 17:16:59 -0400 Subject: [PATCH] Slimefun structure blocks! --- README.md | 8 +- plugin/build.gradle.kts | 13 +- .../addoncommunity/galactifun/Galactifun2.kt | 11 - .../api/betteritem/BetterSlimefunItem.kt | 7 + .../galactifun/impl/GalactifunCategories.kt | 6 + .../galactifun/impl/GalactifunHeads.kt | 35 ++ .../galactifun/impl/GalactifunItems.kt | 14 +- .../galactifun/impl/Gf2Command.kt | 2 +- .../galactifun/impl/items/CommandComputer.kt | 4 +- .../impl/items/SlimefunStructureBlock.kt | 387 ++++++++++++++++++ .../addoncommunity/galactifun/util/SfUtils.kt | 4 + .../galactifun/util/SlimefunStructure.kt | 58 ++- .../galactifun/util/bukkit/McUtils.kt | 6 +- .../galactifun/util/bukkit/SusUtils.kt | 14 +- .../galactifun/util/bukkit/TextUtils.kt | 20 + .../galactifun/util/bukkit/UnitVector.kt | 9 + .../galactifun/util/items/ItemBuilder.kt | 4 + .../galactifun/util/menu/ItemContext.kt | 27 ++ .../galactifun/util/menu/MenuBuilder.kt | 59 ++- .../galactifun/util/menu/MenuItem.kt | 49 ++- .../util/worldgen/DoubleChunkGrid.kt | 31 +- 21 files changed, 676 insertions(+), 92 deletions(-) create mode 100644 plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/GalactifunHeads.kt create mode 100644 plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/items/SlimefunStructureBlock.kt create mode 100644 plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/bukkit/TextUtils.kt create mode 100644 plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/bukkit/UnitVector.kt create mode 100644 plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/menu/ItemContext.kt diff --git a/README.md b/README.md index 9e7c3c5..08725bd 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ # Galactifun2 -This is a rewrite of [Galactifun](https://github.com/Slimefun-Addon-Community/Galactifun) in Kotlin. Also planning to redo the rocket system. \ No newline at end of file +This is a rewrite of [Galactifun](https://github.com/Slimefun-Addon-Community/Galactifun) in Kotlin. Also planning to redo the rocket system. + +## Credits + +Big thanks to [minecraft-heads](https://minecraft-heads.com/) for providing the heads used in this plugin. + +[![](https://images.minecraft-heads.com/banners/minecraft-heads_fullbanner_468x60.png)](https://minecraft-heads.com/) \ No newline at end of file diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 8618f1e..026c99a 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -1,9 +1,8 @@ plugins { kotlin("jvm") kotlin("plugin.serialization") - id("com.google.devtools.ksp") version "2.0.0-1.0.21" - id("com.github.johnrengelman.shadow") version "8.1.1" + id("com.gradleup.shadow") version "8.3.2" id("net.minecrell.plugin-yml.bukkit") version "0.6.0" id("xyz.jpenilla.run-paper") version "2.3.0" @@ -44,12 +43,12 @@ dependencies { implementation("org.bstats:bstats-bukkit:3.0.2") implementation("co.aikar:acf-paper:0.5.1-SNAPSHOT") - implementation("io.github.seggan:sf4k:0.4.1") + implementation("io.github.seggan:sf4k:0.5.0") testImplementation(kotlin("test")) testImplementation("io.strikt:strikt-core:0.34.0") - testImplementation("com.github.seeseemelk:MockBukkit-v1.20:3.80.0") - testImplementation("com.github.shynixn.mccoroutine:mccoroutine-bukkit-test:2.14.0") + implementation("com.github.seeseemelk:MockBukkit-v1.20:3.93.2") + testImplementation("com.github.shynixn.mccoroutine:mccoroutine-bukkit-test:2.19.0") } group = "io.github.addoncommunity.galactifun" @@ -74,6 +73,10 @@ tasks.test { tasks.shadowJar { dependsOn(tasks.test) + manifest { + attributes["paperweight-mappings-namespace"] = "mojang" + } + fun doRelocate(from: String) { val last = from.split(".").last() relocate(from, "io.github.addoncommunity.galactifun.shadowlibs.$last") diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/Galactifun2.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/Galactifun2.kt index 7dcab93..44f0921 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/Galactifun2.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/Galactifun2.kt @@ -31,10 +31,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.withContext import kotlinx.datetime.Instant -import net.kyori.adventure.text.Component import net.kyori.adventure.text.format.NamedTextColor -import net.kyori.adventure.text.format.Style -import net.kyori.adventure.text.format.TextDecoration import org.bstats.bukkit.Metrics import org.bukkit.Bukkit import org.bukkit.Material @@ -168,14 +165,6 @@ open class Galactifun2 : AbstractAddon() { doTestingStuff() } - override suspend fun onDisableAsync() { - Bukkit.getConsoleSender().sendMessage( - Component.text() - .content("YOU MAY SAFELY IGNORE THE COROUTINE CANCELLATION EXCEPTION BELOW, I HAVE NO IDEA HOW TO FIX IT") - .style(Style.style(NamedTextColor.GREEN, TextDecoration.BOLD)) - ) - } - override fun getJavaPlugin(): JavaPlugin = this override fun getBugTrackerURL(): String = "https://github.com/Slimefun-Addon-Community/Galactifun2/issues" diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/api/betteritem/BetterSlimefunItem.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/api/betteritem/BetterSlimefunItem.kt index e2a63ad..f3e7469 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/api/betteritem/BetterSlimefunItem.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/api/betteritem/BetterSlimefunItem.kt @@ -1,6 +1,7 @@ package io.github.addoncommunity.galactifun.api.betteritem import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup +import io.github.thebusybiscuit.slimefun4.api.items.ItemSetting import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType @@ -120,6 +121,12 @@ open class BetterSlimefunItem : SlimefunItem { } } } + + protected fun itemSetting(key: String, default: T): ItemSetting { + val setting = ItemSetting(this, key, default) + addItemSetting(setting) + return setting + } } private fun Class<*>.getAllMethods(): List { diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/GalactifunCategories.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/GalactifunCategories.kt index db21d47..04cb7f3 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/GalactifunCategories.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/GalactifunCategories.kt @@ -24,4 +24,10 @@ object GalactifunCategories { MAIN, CustomItemStack(Material.BONE_MEAL, "&fGases") ) + + val HIDDEN = SubItemGroup( + "hidden".key(), + MAIN, + CustomItemStack(Material.BARRIER, "&fHidden") + ) } \ No newline at end of file diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/GalactifunHeads.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/GalactifunHeads.kt new file mode 100644 index 0000000..9d3c286 --- /dev/null +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/GalactifunHeads.kt @@ -0,0 +1,35 @@ +package io.github.addoncommunity.galactifun.impl + +import io.github.addoncommunity.galactifun.util.items.MaterialType +import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils + +enum class GalactifunHeads(private val texture: String) { + + // https://minecraft-heads.com/custom-heads/head/81364-floppy-disc-icon + FLOPPY_DISK("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTYyOWM4MTNlNTAxZjI0YmIyZjRjYTAyMWVlNTg2MmU0ZTIxNTgxODdmMzA3MjcyZWI2NWExZTY0ZDMwOTE0OCJ9fX0="), + + // https://minecraft-heads.com/custom-heads/head/57245-file-explorer + FILE_EXPLORER("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzczZThiZDNjNDNjNDUxNGM3NjQ4MWNhMWRhZjU1MTQ5ZGZjOTNiZDFiY2ZhOGFiOTQzN2I5ZjdlYjMzOTJkOSJ9fX0="), + + // https://minecraft-heads.com/custom-heads/head/94698-forest-green-l + LETTER_L("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYmZhMDI5NjgyMjhkOWM0MDExZjAxMWRiNzAxZjJjNzUwZTRlYTZhZTJkM2E2MDM1NWJkZTdlNjgyMGVjZThmNyJ9fX0="), + + // https://minecraft-heads.com/custom-heads/head/94709-forest-green-w + LETTER_W("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTZiZDJkZGMzZTFmYTdiY2I4YjZiYjViYTRmMGFlZWI4MjgzNDFlYWUwYTRkNzVmYzk4NWUzNzE5MWJkMGY1NCJ9fX0="), + + // https://minecraft-heads.com/custom-heads/head/94694-forest-green-h + LETTER_H("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOWRiMDJiMDQwYzM3MDE1ODkyYTNhNDNkM2IxYmZkYjJlMDFhMDJlZGNjMmY1YjgyMjUwZGNlYmYzZmY0ZjAxZSJ9fX0="), + + // https://minecraft-heads.com/custom-heads/head/94710-forest-green-x + LETTER_X("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDZhZTIxMWI0NDAzYjg5NjNjNjc0NWYzYzk4ZWJlNzNhMmI0ZTk3YzQwYTc4YjJmZDQwM2EwOWMwZmNhZDZkIn19fQ=="), + + // https://minecraft-heads.com/custom-heads/head/94711-forest-green-y + LETTER_Y("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOTE5N2MwNjFjM2E1M2RmYTQ1ODMzZDgwYzM3Y2U4ZDEyYTVjMzZhMTViYmRlMjQ4OWZjYjFjYjMzYTJhZGZmOCJ9fX0="), + + // https://minecraft-heads.com/custom-heads/head/94712-forest-green-z + LETTER_Z("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmEwOTg5NzVhNzI3ZTkyYmQ5YmM5OGIxY2JmMTQwZTdhNDFhOTkyZmU2NGNmYmI3MTk2ZTdkYmRhNDM0OTczIn19fQ=="), + ; + + val item = SlimefunUtils.getCustomHead(texture) + val materialType: MaterialType = MaterialType.Head(texture) +} \ No newline at end of file diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/GalactifunItems.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/GalactifunItems.kt index 2b2e875..95e500f 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/GalactifunItems.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/GalactifunItems.kt @@ -1,10 +1,7 @@ package io.github.addoncommunity.galactifun.impl import io.github.addoncommunity.galactifun.api.objects.properties.atmosphere.Gas -import io.github.addoncommunity.galactifun.impl.items.CaptainsChair -import io.github.addoncommunity.galactifun.impl.items.CommandComputer -import io.github.addoncommunity.galactifun.impl.items.FuelTank -import io.github.addoncommunity.galactifun.impl.items.RocketEngine +import io.github.addoncommunity.galactifun.impl.items.* import io.github.addoncommunity.galactifun.pluginInstance import io.github.addoncommunity.galactifun.units.Force.Companion.kilonewtons import io.github.addoncommunity.galactifun.units.Volume.Companion.liters @@ -17,6 +14,15 @@ import kotlin.time.Duration.Companion.seconds @Suppress("unused") object GalactifunItems { + val SLIMEFUN_STRUCTURE_BLOCK = buildSlimefunItem { + category = GalactifunCategories.HIDDEN + id = "SLIMEFUN_STRUCTURE_BLOCK" + name = "Slimefun Structure Block" + material = MaterialType.Material(Material.STRUCTURE_BLOCK) + recipeType = RecipeType.NULL + recipe = emptyArray() + } + val COMMAND_COMPUTER = buildSlimefunItem { category = GalactifunCategories.ROCKET_COMPONENTS id = "COMMAND_COMPUTER" diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/Gf2Command.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/Gf2Command.kt index ca36065..2769b10 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/Gf2Command.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/Gf2Command.kt @@ -9,7 +9,7 @@ import io.github.addoncommunity.galactifun.impl.managers.RocketManager import io.github.addoncommunity.galactifun.util.SerializedBlock import io.github.addoncommunity.galactifun.util.bukkit.galactifunTeleport import io.github.addoncommunity.galactifun.util.menu.PlanetMenu -import io.github.seggan.sf4k.location.plusAssign +import io.github.seggan.sf4k.extensions.plusAssign import kotlinx.datetime.Clock import org.bukkit.Location import org.bukkit.entity.BlockDisplay diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/items/CommandComputer.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/items/CommandComputer.kt index dae38a3..fd6db92 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/items/CommandComputer.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/items/CommandComputer.kt @@ -16,8 +16,8 @@ import io.github.addoncommunity.galactifun.units.times import io.github.addoncommunity.galactifun.units.unitSumOf import io.github.addoncommunity.galactifun.util.* import io.github.addoncommunity.galactifun.util.bukkit.* -import io.github.seggan.sf4k.location.plus -import io.github.seggan.sf4k.location.position +import io.github.seggan.sf4k.extensions.plus +import io.github.seggan.sf4k.extensions.position import io.github.seggan.sf4k.serial.blockstorage.getBlockStorage import io.github.seggan.sf4k.serial.blockstorage.setBlockStorage import io.github.seggan.sf4k.serial.pdc.get diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/items/SlimefunStructureBlock.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/items/SlimefunStructureBlock.kt new file mode 100644 index 0000000..e92bb78 --- /dev/null +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/items/SlimefunStructureBlock.kt @@ -0,0 +1,387 @@ +package io.github.addoncommunity.galactifun.impl.items + +import com.destroystokyo.paper.ParticleBuilder +import com.github.shynixn.mccoroutine.bukkit.launch +import io.github.addoncommunity.galactifun.api.betteritem.BetterSlimefunItem +import io.github.addoncommunity.galactifun.api.betteritem.ItemHandler +import io.github.addoncommunity.galactifun.api.betteritem.Ticker +import io.github.addoncommunity.galactifun.impl.GalactifunHeads +import io.github.addoncommunity.galactifun.pluginInstance +import io.github.addoncommunity.galactifun.util.SlimefunStructure +import io.github.addoncommunity.galactifun.util.bukkit.* +import io.github.addoncommunity.galactifun.util.getValue +import io.github.addoncommunity.galactifun.util.items.materialType +import io.github.addoncommunity.galactifun.util.menu.buildMenu +import io.github.seggan.sf4k.extensions.div +import io.github.seggan.sf4k.extensions.plus +import io.github.seggan.sf4k.extensions.plusAssign +import io.github.seggan.sf4k.extensions.times +import io.github.seggan.sf4k.serial.blockstorage.getBlockStorage +import io.github.seggan.sf4k.serial.blockstorage.setBlockStorage +import io.github.thebusybiscuit.slimefun4.api.events.PlayerRightClickEvent +import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack +import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType +import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler +import io.github.thebusybiscuit.slimefun4.core.handlers.BlockUseHandler +import io.github.thebusybiscuit.slimefun4.implementation.Slimefun +import io.github.thebusybiscuit.slimefun4.utils.HeadTexture +import me.mrCookieSlime.Slimefun.api.BlockStorage +import net.kyori.adventure.text.format.NamedTextColor +import org.bukkit.Color +import org.bukkit.Location +import org.bukkit.Particle +import org.bukkit.block.Block +import org.bukkit.block.Structure +import org.bukkit.block.structure.Mirror +import org.bukkit.block.structure.StructureRotation +import org.bukkit.block.structure.UsageMode +import org.bukkit.event.block.BlockPlaceEvent +import org.bukkit.inventory.ItemStack +import org.bukkit.util.BlockVector +import org.bukkit.util.Vector +import java.util.* + +class SlimefunStructureBlock( + itemGroup: ItemGroup, + item: SlimefunItemStack, + recipeType: RecipeType, + recipe: Array +) : BetterSlimefunItem(itemGroup, item, recipeType, recipe) { + + private val particlesPerBlock: Int by itemSetting("particles-per-block", 3) + + companion object { + + private const val KEY_NAME = "name" + private const val KEY_LENGTH = "length" + private const val KEY_WIDTH = "width" + private const val KEY_HEIGHT = "height" + private const val KEY_OFFSET_X = "offset_x" + private const val KEY_OFFSET_Y = "offset_y" + private const val KEY_OFFSET_Z = "offset_z" + + private val menu = buildMenu { + +"........." + +"..s.n.o.." + +"........." + +".lhw.xyz." + +"........." + + background('.') + + 's' means item { + name = "Save" + material = GalactifunHeads.FLOPPY_DISK.materialType + + +"" + +"Save the current structure" + +"with the given name" + + onClick { p, _ -> + p.closeInventory() + val structure = SlimefunStructure() + val size = getSize(block) + val offset = getOffset(block) + val offsetLocation = block.location + offset + structure.fill(offsetLocation, size, true) + + val keyName = block.getBlockStorage(KEY_NAME) + if (keyName.isNullOrBlank()) { + p.sendMessage(NamedTextColor.RED + "Please set a name first") + return@onClick + } + val key = keyName.key() + structure.save(key) + + p.sendMessage(NamedTextColor.GREEN + "Saved structure with key $key") + } + } + + 'o' means item { + name = "Load" + material = GalactifunHeads.FILE_EXPLORER.materialType + + +"" + +"Load a stored structure" + +"with the given name" + + onClick { p, _ -> + p.closeInventory() + val offset = getOffset(block) + val offsetLocation = block.location + offset + + val keyName = block.getBlockStorage(KEY_NAME) + if (keyName.isNullOrBlank()) { + p.sendMessage(NamedTextColor.RED + "Please set a name first") + return@onClick + } + val key = keyName.key() + val structure = SlimefunStructure.load(key) + if (structure == null) { + p.sendMessage(NamedTextColor.RED + "No structure found with key $key") + return@onClick + } + structure.place( + offsetLocation, + true, + StructureRotation.NONE, + Mirror.NONE, + 0, + 1f, + Random(block.world.seed) + ) + + p.sendMessage(NamedTextColor.GREEN + "Loaded structure with key $key") + } + } + + 'n' means item { + name = "Name: " + material = HeadTexture.SCRIPT_NEW.asItemStack.materialType + + +"" + +"Click to rename" + + onClick { p, _ -> + pluginInstance.launch { + p.closeInventory() + p.sendMessage("Type the new name in chat") + val name = p.awaitChatInput() + if (!name.all { it.isLetterOrDigit() || it == '_' }) { + p.sendMessage(NamedTextColor.RED + "Name can only contain letters, digits and underscores") + return@launch + } + block.setBlockStorage(KEY_NAME, name) + thisItem.editMeta { it.displayName("Name: $name".miniComponent()) } + p.sendMessage(NamedTextColor.GREEN + "Set name to $name") + } + } + + init { + val name = block.getBlockStorage(KEY_NAME) ?: "" + thisItem.editMeta { it.displayName("Name: $name".miniComponent()) } + } + } + + // + + 'l' means item { + name = "Length: 1" + material = GalactifunHeads.LETTER_L.materialType + + +"" + +"Left click to increase" + +"Right click to decrease" + + onClick { _, action -> + val addend = if (action.isRightClicked) -1 else 1 + val length = (block.getBlockStorage(KEY_LENGTH) ?: 1) + addend + if (length < 1) return@onClick + block.setBlockStorage(KEY_LENGTH, length) + thisItem.editMeta { it.displayName("Length: $length".miniComponent()) } + } + + init { + val length = block.getBlockStorage(KEY_LENGTH) ?: 1 + thisItem.editMeta { it.displayName("Length: $length".miniComponent()) } + } + } + + 'w' means item { + name = "Width: 1" + material = GalactifunHeads.LETTER_W.materialType + + +"" + +"Left click to increase" + +"Right click to decrease" + + onClick { _, action -> + val addend = if (action.isRightClicked) -1 else 1 + val width = (block.getBlockStorage(KEY_WIDTH) ?: 1) + addend + if (width < 1) return@onClick + block.setBlockStorage(KEY_WIDTH, width) + thisItem.editMeta { it.displayName("Width: $width".miniComponent()) } + } + + init { + val width = block.getBlockStorage(KEY_WIDTH) ?: 1 + thisItem.editMeta { it.displayName("Width: $width".miniComponent()) } + } + } + + 'h' means item { + name = "Height: 1" + material = GalactifunHeads.LETTER_H.materialType + + +"" + +"Left click to increase" + +"Right click to decrease" + + onClick { _, action -> + val addend = if (action.isRightClicked) -1 else 1 + val height = (block.getBlockStorage(KEY_HEIGHT) ?: 1) + addend + if (height < 1) return@onClick + block.setBlockStorage(KEY_HEIGHT, height) + thisItem.editMeta { it.displayName("Height: $height".miniComponent()) } + } + + init { + val height = block.getBlockStorage(KEY_HEIGHT) ?: 1 + thisItem.editMeta { it.displayName("Height: $height".miniComponent()) } + } + } + // + + // + + 'x' means item { + name = "Offset x: 0" + material = GalactifunHeads.LETTER_X.materialType + + +"" + +"Left click to increase" + +"Right click to decrease" + + onClick { _, action -> + val addend = if (action.isRightClicked) -1 else 1 + val x = (block.getBlockStorage(KEY_OFFSET_X) ?: 0) + addend + block.setBlockStorage(KEY_OFFSET_X, x) + thisItem.editMeta { it.displayName("Offset x: $x".miniComponent()) } + } + + init { + val x = block.getBlockStorage(KEY_OFFSET_X) ?: 0 + thisItem.editMeta { it.displayName("Offset x: $x".miniComponent()) } + } + } + + 'y' means item { + name = "Offset y: 1" + material = GalactifunHeads.LETTER_Y.materialType + + +"" + +"Left click to increase" + +"Right click to decrease" + + onClick { _, action -> + val addend = if (action.isRightClicked) -1 else 1 + val y = (block.getBlockStorage(KEY_OFFSET_Y) ?: 1) + addend + block.setBlockStorage(KEY_OFFSET_Y, y) + thisItem.editMeta { it.displayName("Offset y: $y".miniComponent()) } + } + + init { + val y = block.getBlockStorage(KEY_OFFSET_Y) ?: 1 + thisItem.editMeta { it.displayName("Offset y: $y".miniComponent()) } + } + } + + 'z' means item { + name = "Offset z: 0" + material = GalactifunHeads.LETTER_Z.materialType + + +"" + +"Left click to increase" + +"Right click to decrease" + + onClick { _, action -> + val addend = if (action.isRightClicked) -1 else 1 + val z = (block.getBlockStorage(KEY_OFFSET_Z) ?: 0) + addend + block.setBlockStorage(KEY_OFFSET_Z, z) + thisItem.editMeta { it.displayName("Offset z: $z".miniComponent()) } + } + + init { + val z = block.getBlockStorage(KEY_OFFSET_Z) ?: 0 + thisItem.editMeta { it.displayName("Offset z: $z".miniComponent()) } + } + } + // + } + + private fun getSize(block: Block): BlockVector { + val length = block.getBlockStorage(KEY_LENGTH) ?: 1 + val width = block.getBlockStorage(KEY_WIDTH) ?: 1 + val height = block.getBlockStorage(KEY_HEIGHT) ?: 1 + return BlockVector(length, height, width) + } + + private fun getOffset(block: Block): BlockVector { + val offsetX = block.getBlockStorage(KEY_OFFSET_X) ?: 0 + val offsetY = block.getBlockStorage(KEY_OFFSET_Y) ?: 1 + val offsetZ = block.getBlockStorage(KEY_OFFSET_Z) ?: 0 + return BlockVector(offsetX, offsetY, offsetZ) + } + } + + @ItemHandler(BlockPlaceHandler::class) + private fun onPlace(e: BlockPlaceEvent) { + val state = e.block.state as Structure + state.usageMode = UsageMode.SAVE + state.update() + } + + @ItemHandler(BlockUseHandler::class) + private fun onClick(e: PlayerRightClickEvent) { + e.cancel() + pluginInstance.launch { + delayTicks(1) + BlockStorage.getInventory(e.clickedBlock.orElseThrow()).open(e.player) + } + } + + @Ticker + private fun tick(b: Block) { + drawBoundingBox(b) + } + + private fun drawBoundingBox(block: Block) { + val size = getSize(block) + val offset = getOffset(block) + + val start = block.location + offset + val steps = size * particlesPerBlock + + for ((axis, opposite) in AXES) { + val stepVector = axis / particlesPerBlock + val (axis1, axis2) = opposite + val startPoints = listOf( + start, + start + (size * axis1), + start + (size * axis2), + start + size * (axis1 + axis2), + ) + for (startPoint in startPoints) { + generateParticles(startPoint, stepVector * axis, (steps * axis).length().toInt()) + } + } + } + + private fun generateParticles(start: Location, step: Vector, steps: Int) { + val locations = mutableListOf() + val current = start.clone() + repeat(steps) { + locations.add(current.clone()) + current += step + } + locations.consumeSpreadOut(Slimefun.getTickerTask().tickRate) { location -> + ParticleBuilder(Particle.DUST) + .count(1) + .location(location) + .color(Color.WHITE) + .spawn() + } + } + + override fun postRegister() { + menu.apply(this) + isHidden = true + } +} + +private val AXES = listOf( + UnitVector.X to (UnitVector.Y to UnitVector.Z), + UnitVector.Y to (UnitVector.X to UnitVector.Z), + UnitVector.Z to (UnitVector.X to UnitVector.Y), +) \ No newline at end of file diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/SfUtils.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/SfUtils.kt index a55c265..be89604 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/SfUtils.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/SfUtils.kt @@ -2,6 +2,7 @@ package io.github.addoncommunity.galactifun.util +import io.github.thebusybiscuit.slimefun4.api.items.ItemSetting import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem import io.github.thebusybiscuit.slimefun4.libraries.dough.blocks.BlockPosition import io.github.thebusybiscuit.slimefun4.libraries.dough.collections.RandomizedSet @@ -11,6 +12,7 @@ import me.mrCookieSlime.Slimefun.api.BlockStorage import org.bukkit.Location import org.bukkit.block.Block import org.bukkit.inventory.ItemStack +import kotlin.reflect.KProperty /** * Checks if the block at the given location is a Slimefun item of the given type. @@ -25,6 +27,8 @@ inline fun Location.checkBlock(): Block? { inline fun Block.checkBlock(): Block? = location.checkBlock() +operator fun ItemSetting.getValue(thisRef: SlimefunItem, property: KProperty<*>): T = value + inline fun buildRandomizedSet(builder: RandomizedSet.() -> Unit): RandomizedSet = RandomizedSet().apply(builder) diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/SlimefunStructure.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/SlimefunStructure.kt index c0b9dca..25598f4 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/SlimefunStructure.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/SlimefunStructure.kt @@ -1,13 +1,17 @@ package io.github.addoncommunity.galactifun.util +import io.github.addoncommunity.galactifun.serial.BlockVectorSerializer import io.github.addoncommunity.galactifun.util.bukkit.copy import io.github.addoncommunity.galactifun.util.bukkit.key +import io.github.seggan.sf4k.extensions.div +import io.github.seggan.sf4k.extensions.minus +import io.github.seggan.sf4k.extensions.plus import io.github.seggan.sf4k.serial.pdc.get import io.github.seggan.sf4k.serial.pdc.set +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.serializer import me.mrCookieSlime.Slimefun.api.BlockStorage -import org.bukkit.Location -import org.bukkit.Material -import org.bukkit.RegionAccessor +import org.bukkit.* import org.bukkit.block.structure.Mirror import org.bukkit.block.structure.StructureRotation import org.bukkit.structure.Structure @@ -19,12 +23,18 @@ import java.util.* import kotlin.collections.set @Suppress("UnstableApiUsage") -class SlimefunStructure(private val delegate: Structure) : Structure by delegate { +class SlimefunStructure( + private val delegate: Structure = Bukkit.getStructureManager().createStructure() +) : Structure by delegate { private var center: Vector = resetCenter() companion object { private val blockStorageKey = "block_storage".key() + + fun load(key: NamespacedKey): SlimefunStructure? { + return Bukkit.getStructureManager().loadStructure(key)?.let(::SlimefunStructure) + } } override fun place( @@ -110,15 +120,8 @@ class SlimefunStructure(private val delegate: Structure) : Structure by delegate blockTransformers: MutableCollection, entityTransformers: MutableCollection ) { - val data = persistentDataContainer.get>>(blockStorageKey) ?: emptyMap() - val rotated = data.mapKeys { (vector, _) -> - val newVec = structureRotation.rotateAroundCenter(vector) - when (mirror) { - Mirror.NONE -> newVec - Mirror.FRONT_BACK -> newVec.copy(x = -newVec.x) - Mirror.LEFT_RIGHT -> newVec.copy(z = -newVec.z) - } - } + val data = persistentDataContainer.get>>(blockStorageKey, serializersModule.serializer()) ?: emptyMap() + val rotated = data.mapKeys { (vector, _) -> vector.applyStructureTransforms(structureRotation, mirror) } blockTransformers.add(BlockTransformer { region, x, y, z, current, _ -> val vector = BlockVector(x, y, z).subtract(location) val (json, material) = rotated[vector] ?: return@BlockTransformer current @@ -147,7 +150,7 @@ class SlimefunStructure(private val delegate: Structure) : Structure by delegate for (y in 0 until size.blockY) { for (z in 0 until size.blockZ) { val vector = BlockVector(x, y, z) - val block = origin.clone().add(vector).block + val block = (origin + vector).block if (BlockStorage.hasBlockInfo(block)) { data[vector] = BlockStorage.getBlockInfoAsJson(block) to block.type } @@ -155,7 +158,8 @@ class SlimefunStructure(private val delegate: Structure) : Structure by delegate } } delegate.fill(origin, size, includeEntities) - persistentDataContainer.set(blockStorageKey, data) + persistentDataContainer.remove(blockStorageKey) + persistentDataContainer.set(blockStorageKey, data, serializersModule.serializer()) center = resetCenter() } @@ -163,18 +167,32 @@ class SlimefunStructure(private val delegate: Structure) : Structure by delegate fill(corner1, corner2.clone().subtract(corner1).toVector().toBlockVector(), includeEntities) } + fun save(key: NamespacedKey) { + Bukkit.getStructureManager().saveStructure(key, delegate) + } + private fun resetCenter(): Vector { - return size.clone().multiply(0.5).setY(0) + val center = size / 2 + return center.setY(0) } - private fun StructureRotation.rotateAroundCenter(location: Vector): BlockVector { - val centered = location.clone().subtract(center) - val rotated = when (this) { + private fun Vector.applyStructureTransforms(rotation: StructureRotation, mirror: Mirror): BlockVector { + val centered = this - center + val rotated = when (rotation) { StructureRotation.NONE -> centered StructureRotation.CLOCKWISE_90 -> centered.copy(x = -centered.z, z = centered.x) StructureRotation.CLOCKWISE_180 -> centered.copy(x = -centered.x, z = -centered.z) StructureRotation.COUNTERCLOCKWISE_90 -> centered.copy(x = centered.z, z = -centered.x) } - return rotated.add(center).toBlockVector() + val mirrored = when (mirror) { + Mirror.NONE -> rotated + Mirror.FRONT_BACK -> rotated.copy(x = -rotated.x) + Mirror.LEFT_RIGHT -> rotated.copy(z = -rotated.z) + } + return mirrored.add(center).toBlockVector() } +} + +private val serializersModule = SerializersModule { + contextual(BlockVector::class, BlockVectorSerializer) } \ No newline at end of file diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/bukkit/McUtils.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/bukkit/McUtils.kt index 99598ab..4502c9a 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/bukkit/McUtils.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/bukkit/McUtils.kt @@ -6,12 +6,11 @@ import net.kyori.adventure.text.Component import net.kyori.adventure.text.TextComponent import net.kyori.adventure.text.format.TextColor import net.kyori.adventure.text.format.TextDecoration -import net.kyori.adventure.text.minimessage.MiniMessage -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer import org.bukkit.* import org.bukkit.entity.Entity import org.bukkit.event.player.PlayerTeleportEvent import org.bukkit.inventory.ItemStack +import org.bukkit.persistence.PersistentDataContainer import org.bukkit.util.Vector import java.util.* import java.util.concurrent.CompletableFuture @@ -85,7 +84,6 @@ inline fun ItemStack.modifyLore(modifier: (MutableList) -> Unit) { operator fun Tag.contains(item: T): Boolean = isTagged(item) -fun String.miniMessageToLegacy(): String = LegacyComponentSerializer.legacyAmpersand() - .serialize(MiniMessage.miniMessage().deserialize(this)) +operator fun PersistentDataContainer.contains(key: NamespacedKey): Boolean = has(key) fun locationZero(world: World?): Location = Location(world, 0.0, 0.0, 0.0) \ No newline at end of file diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/bukkit/SusUtils.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/bukkit/SusUtils.kt index f646345..4eac29a 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/bukkit/SusUtils.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/bukkit/SusUtils.kt @@ -2,10 +2,8 @@ package io.github.addoncommunity.galactifun.util.bukkit import com.github.shynixn.mccoroutine.bukkit.launch import com.github.shynixn.mccoroutine.bukkit.ticks -import io.github.addoncommunity.galactifun.launchAsync import io.github.addoncommunity.galactifun.pluginInstance import io.github.thebusybiscuit.slimefun4.utils.ChatUtils -import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.delay import org.bukkit.entity.Player import kotlin.coroutines.resume @@ -24,19 +22,11 @@ suspend fun delayTicks(ticks: Int) { } inline fun Collection.consumeSpreadOut(ticks: Int, crossinline action: suspend (T) -> Unit) { - val channel = Channel() - pluginInstance.launchAsync { - for (item in this@consumeSpreadOut) { - channel.send(item) - } - channel.close() - } val itemsPerTick = size / ticks + 1 pluginInstance.launch { - var i = 0 - for (item in channel) { + for ((i, item) in this@consumeSpreadOut.withIndex()) { action(item) - if (++i % itemsPerTick == 0) { + if (i % itemsPerTick == 0) { delayTicks(1) } } diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/bukkit/TextUtils.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/bukkit/TextUtils.kt new file mode 100644 index 0000000..0fca1e9 --- /dev/null +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/bukkit/TextUtils.kt @@ -0,0 +1,20 @@ +package io.github.addoncommunity.galactifun.util.bukkit + +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.format.TextDecoration +import net.kyori.adventure.text.minimessage.MiniMessage +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer + +fun String.miniMessageToLegacy(): String = LegacyComponentSerializer.legacyAmpersand() + .serialize(MiniMessage.miniMessage().deserialize(this)) + +fun String.miniComponent(): Component = + Component.text() + .decoration(TextDecoration.ITALIC, false) + .append(MiniMessage.miniMessage().deserialize(this)) + .build() + +fun String.legacyDefaultColor(color: Char): String { + if (startsWith('&') || isBlank()) return this + return "&$color$this" +} \ No newline at end of file diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/bukkit/UnitVector.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/bukkit/UnitVector.kt new file mode 100644 index 0000000..82e9844 --- /dev/null +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/bukkit/UnitVector.kt @@ -0,0 +1,9 @@ +package io.github.addoncommunity.galactifun.util.bukkit + +import org.bukkit.util.BlockVector + +object UnitVector { + val X get() = BlockVector(1, 0, 0) + val Y get() = BlockVector(0, 1, 0) + val Z get() = BlockVector(0, 0, 1) +} \ No newline at end of file diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/items/ItemBuilder.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/items/ItemBuilder.kt index 988271b..89a47ee 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/items/ItemBuilder.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/items/ItemBuilder.kt @@ -8,6 +8,7 @@ import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils +import org.bukkit.Material import org.bukkit.inventory.ItemStack import kotlin.reflect.KClass import kotlin.reflect.full.primaryConstructor @@ -58,6 +59,9 @@ sealed interface MaterialType { } } +val Material.materialType get() = MaterialType.Material(this) +val ItemStack.materialType get() = MaterialType.ItemStack(this) + inline fun buildSlimefunItem( vararg otherArgs: Any?, builder: ItemBuilder.() -> Unit diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/menu/ItemContext.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/menu/ItemContext.kt new file mode 100644 index 0000000..b674fac --- /dev/null +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/menu/ItemContext.kt @@ -0,0 +1,27 @@ +package io.github.addoncommunity.galactifun.util.menu + +import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu +import org.bukkit.block.Block +import org.bukkit.inventory.ItemStack + +open class ItemContext( + val menu: BlockMenu, + val block: Block, + val thisItem: ItemStack, + private val slotMap: Map> +) { + fun getSlot(c: Char): Int = getSlots(c).single() + + fun getSlots(c: Char): List { + return slotMap[c] ?: emptyList() + } +} + +class ClickContext( + menu: BlockMenu, + block: Block, + thisItem: ItemStack, + slotMap: Map> +) : ItemContext(menu, block, thisItem, slotMap) { + var allowTaking = false +} \ No newline at end of file diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/menu/MenuBuilder.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/menu/MenuBuilder.kt index 25147ea..29fdf13 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/menu/MenuBuilder.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/menu/MenuBuilder.kt @@ -1,5 +1,6 @@ package io.github.addoncommunity.galactifun.util.menu +import io.github.addoncommunity.galactifun.util.set import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem import io.github.thebusybiscuit.slimefun4.implementation.Slimefun import io.github.thebusybiscuit.slimefun4.libraries.dough.protection.Interaction @@ -7,7 +8,7 @@ import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils import it.unimi.dsi.fastutil.chars.Char2ObjectOpenHashMap import it.unimi.dsi.fastutil.chars.CharOpenHashSet import it.unimi.dsi.fastutil.ints.IntOpenHashSet -import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ClickAction +import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu import me.mrCookieSlime.Slimefun.api.inventory.BlockMenuPreset import me.mrCookieSlime.Slimefun.api.item_transport.ItemTransportFlow import org.bukkit.block.Block @@ -29,17 +30,24 @@ class MenuBuilder { menu.add(this) } - infix fun Char.means(item: ItemStack): MenuItem { - return MenuItem(item).also { items[this] = it } + infix fun Char.means(item: MenuItem) { + items[this] = item + } + + infix fun Char.means(item: ItemStack) { + items[this] = MenuItem(item) } fun background(c: Char) { items[c] = MenuItem(ChestMenuUtils.getBackground()) } - infix fun MenuItem.onClick(block: (Player, ClickAction) -> Boolean): MenuItem { - onClick = block - return this + fun inputBorder(c: Char) { + items[c] = MenuItem(ChestMenuUtils.getInputSlotTexture()) + } + + fun outputBorder(c: Char) { + items[c] = MenuItem(ChestMenuUtils.getOutputSlotTexture()) } fun input(c: Char) { @@ -50,23 +58,32 @@ class MenuBuilder { outputs.add(c) } - fun apply(item: SlimefunItem) { - object : BlockMenuPreset(item.id, item.itemName) { + fun item(builder: MenuItem.Builder.() -> Unit): MenuItem { + return MenuItem.Builder().apply(builder).build() + } - private val inputSlots = IntOpenHashSet() - private val outputSlots = IntOpenHashSet() + fun apply(item: SlimefunItem) { + // They have to be up here because init() is called in the superclass constructor smh + val inputSlots = IntOpenHashSet() + val outputSlots = IntOpenHashSet() + val slotMap = mutableMapOf>() + val handlers = mutableListOf>() + val inits = mutableListOf Unit>>() + object : BlockMenuPreset(item.id, item.itemName) { override fun init() { var slot = 0 for (row in menu) { for (char in row) { + slotMap.getOrPut(char, ::mutableListOf).add(slot) val menuItem = items[char] if (menuItem != null) { addItem(slot, menuItem.item) if (menuItem.onClick != null) { - addMenuClickHandler(slot) { p, _, _, action -> - menuItem.onClick?.invoke(p, action) ?: false - } + handlers.add(slot to menuItem.onClick) + } + if (menuItem.init != null) { + inits.add(slot to menuItem.init) } } if (inputs.contains(char)) { @@ -81,9 +98,23 @@ class MenuBuilder { isEmptySlotsClickable = emptySlotsClickable } + override fun newInstance(menu: BlockMenu, b: Block) { + for ((slot, handler) in handlers) { + menu[slot] = { p, _, item, action -> + val context = ClickContext(menu, b, item, slotMap) + context.handler(p, action) + context.allowTaking + } + } + for ((slot, init) in inits) { + val context = ItemContext(menu, b, menu.getItemInSlot(slot), slotMap) + context.init() + } + } + override fun canOpen(b: Block, p: Player): Boolean { if (p.hasPermission("slimefun.inventory.bypass")) return true - return item.canUse(p, false) + return item.canUse(p, true) && Slimefun.getProtectionManager().hasPermission(p, b, Interaction.INTERACT_BLOCK) } diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/menu/MenuItem.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/menu/MenuItem.kt index 3309b34..d3ca4cb 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/menu/MenuItem.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/menu/MenuItem.kt @@ -1,9 +1,54 @@ package io.github.addoncommunity.galactifun.util.menu +import io.github.addoncommunity.galactifun.util.bukkit.legacyDefaultColor +import io.github.addoncommunity.galactifun.util.bukkit.miniMessageToLegacy +import io.github.addoncommunity.galactifun.util.general.RequiredProperty +import io.github.addoncommunity.galactifun.util.items.MaterialType +import io.github.thebusybiscuit.slimefun4.libraries.dough.items.CustomItemStack import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ClickAction import org.bukkit.entity.Player import org.bukkit.inventory.ItemStack -class MenuItem(val item: ItemStack) { - var onClick: ((Player, ClickAction) -> Boolean)? = { _, _ -> false } +typealias MenuItemClickHandler = ClickContext.(Player, ClickAction) -> Unit +typealias MenuItemInitHandler = ItemContext.() -> Unit + +class MenuItem( + val item: ItemStack, + val onClick: MenuItemClickHandler? = { _, _ -> }, + val init: MenuItemInitHandler? = null +) { + + init { + item.editMeta { it.setMaxStackSize(99) } + } + + class Builder { + var name: String by RequiredProperty() + var material: MaterialType by RequiredProperty() + val lore = mutableListOf() + + var onClick: MenuItemClickHandler? = { _, _ -> } + var init: MenuItemInitHandler? = null + + operator fun String.unaryPlus() { + lore += this.miniMessageToLegacy() + } + + fun onClick(handler: MenuItemClickHandler) { + onClick = handler + } + + fun init(builder: MenuItemInitHandler) { + init = builder + } + + fun build(): MenuItem { + val item = CustomItemStack( + material.convert(), + name.miniMessageToLegacy().legacyDefaultColor('f'), + *lore.map { it.legacyDefaultColor('7') }.toTypedArray() + ) + return MenuItem(item, onClick, init) + } + } } \ No newline at end of file diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/worldgen/DoubleChunkGrid.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/worldgen/DoubleChunkGrid.kt index 92e7844..9c9cc67 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/worldgen/DoubleChunkGrid.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/worldgen/DoubleChunkGrid.kt @@ -23,24 +23,23 @@ class DoubleChunkGrid { private val lock = ReentrantReadWriteLock() init { - if (pluginInstance.isEnabled) { - val ref = WeakReference(this) - pluginInstance.launchAsync(Dispatchers.IO) { - while (true) { - val time = System.currentTimeMillis() - val grid = ref.get() ?: break - grid.lock.writeLock().lock() - val iterator = grid.chunkCreations.long2LongEntrySet().fastIterator() - while (iterator.hasNext()) { - val entry = iterator.next() - if (time - entry.longValue > 3000) { - iterator.remove() - grid.chunks.remove(entry.longKey) - } + val ref = WeakReference(this) + pluginInstance.launchAsync(Dispatchers.IO) { + while (true) { + val time = System.currentTimeMillis() + val grid = ref.get() ?: break + grid.lock.writeLock().lock() + val iterator = grid.chunkCreations.long2LongEntrySet().fastIterator() + while (iterator.hasNext()) { + val entry = iterator.next() + if (time - entry.longValue > 3000) { + iterator.remove() + grid.chunks.remove(entry.longKey) } - grid.lock.writeLock().unlock() - delay(2999) } + grid.lock.writeLock().unlock() + if (!pluginInstance.isEnabled) return@launchAsync + delay(2999) } } }