diff --git a/src/main/kotlin/org/xodium/vanillaplus/VanillaPlus.kt b/src/main/kotlin/org/xodium/vanillaplus/VanillaPlus.kt index 6daf35ac..185d0adb 100644 --- a/src/main/kotlin/org/xodium/vanillaplus/VanillaPlus.kt +++ b/src/main/kotlin/org/xodium/vanillaplus/VanillaPlus.kt @@ -56,6 +56,7 @@ internal class VanillaPlus : JavaPlugin() { logger.info( listOf( + BookshelfModule, BooksModule, ChatModule, DimensionsModule, diff --git a/src/main/kotlin/org/xodium/vanillaplus/data/ConfigData.kt b/src/main/kotlin/org/xodium/vanillaplus/data/ConfigData.kt index fe4af80e..01392379 100644 --- a/src/main/kotlin/org/xodium/vanillaplus/data/ConfigData.kt +++ b/src/main/kotlin/org/xodium/vanillaplus/data/ConfigData.kt @@ -23,6 +23,7 @@ import kotlin.time.measureTime /** Configuration data for the plugin. */ @Serializable internal data class ConfigData( + var bookshelfModule: BookshelfModule.Config = BookshelfModule.Config(), var booksModule: BooksModule.Config = BooksModule.Config(), var chatModule: ChatModule.Config = ChatModule.Config(), var dimensionsModule: DimensionsModule.Config = DimensionsModule.Config(), diff --git a/src/main/kotlin/org/xodium/vanillaplus/modules/BookshelfModule.kt b/src/main/kotlin/org/xodium/vanillaplus/modules/BookshelfModule.kt new file mode 100644 index 00000000..b3254877 --- /dev/null +++ b/src/main/kotlin/org/xodium/vanillaplus/modules/BookshelfModule.kt @@ -0,0 +1,134 @@ +package org.xodium.vanillaplus.modules + +import kotlinx.serialization.Serializable +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder +import org.bukkit.block.ChiseledBookshelf +import org.bukkit.entity.Player +import org.bukkit.event.EventHandler +import org.bukkit.event.player.PlayerInteractEvent +import org.bukkit.inventory.meta.BookMeta +import org.bukkit.inventory.meta.EnchantmentStorageMeta +import org.xodium.vanillaplus.interfaces.ModuleInterface +import org.xodium.vanillaplus.utils.Utils.MM + +/** Represents a module handling bookshelf mechanics within the system. */ +internal object BookshelfModule : ModuleInterface { + @EventHandler + fun on(event: PlayerInteractEvent) { + val player = event.player + + if (!player.isSneaking) return + + val bookshelf = event.clickedBlock?.state as? ChiseledBookshelf ?: return + + event.isCancelled = true + + handleBookshelfInteraction(player, bookshelf) + } + + /** + * Handles the interaction logic for a chiseled bookshelf. + * @param player The player interacting with the bookshelf. + * @param bookshelf The clicked chiseled bookshelf. + */ + private fun handleBookshelfInteraction( + player: Player, + bookshelf: ChiseledBookshelf, + ) { + val inventory = bookshelf.inventory + + player.sendMessage(MM.deserialize(config.bookshelfModule.header)) + + val slots = 0 until inventory.size + + if (!slots.any { inventory.getItem(it) != null }) return + + for (i in slots) { + val item = inventory.getItem(i) ?: continue + val slotNumber = i + 1 + val slotPrefix = createSlotPrefix(slotNumber) + + when (val itemMeta = item.itemMeta) { + is BookMeta -> player.sendMessage(renderBook(slotPrefix, itemMeta)) + is EnchantmentStorageMeta -> player.sendMessage(renderEnchantedBook(slotPrefix, itemMeta)) + else -> player.sendMessage(renderGenericItem(slotPrefix, item.type.key.key)) + } + } + + player.sendMessage(MM.deserialize(config.bookshelfModule.footer)) + } + + /** + * Renders a written or writable book message. + * @param slotPrefix The formatted slot prefix component. + * @param meta The book meta. + * @return The rendered message component. + */ + private fun renderBook( + slotPrefix: Component, + meta: BookMeta, + ): Component { + val title = meta.title + val author = meta.author + + var message = + slotPrefix.append( + MM.deserialize( + " ", + ), + ) + + if (title != null) message = message.append(MM.deserialize(" $title")) + if (author != null) message = message.append(MM.deserialize(" by $author")) + + return message + } + + /** + * Renders an enchanted book message. + * @param slotPrefix The formatted slot prefix component. + * @param meta The enchantment storage meta. + * @return The rendered message component. + */ + private fun renderEnchantedBook( + slotPrefix: Component, + meta: EnchantmentStorageMeta, + ): Component { + val enchantmentList = meta.storedEnchants.entries.joinToString(", ") { "${it.key.key.key} ${it.value}" } + + return slotPrefix.append( + MM.deserialize( + " $enchantmentList", + ), + ) + } + + /** + * Renders a generic item message. + * @param slotPrefix The formatted slot prefix component. + * @param itemKey The namespaced item key. + * @return The rendered message component. + */ + private fun renderGenericItem( + slotPrefix: Component, + itemKey: String, + ): Component = slotPrefix.append(MM.deserialize(" ")) + + private fun createSlotPrefix(slotNumber: Int): Component = + MM.deserialize( + config.bookshelfModule.slotPrefix, + Placeholder.component("slot", MM.deserialize(slotNumber.toString())), + ) + + /** Represents the config of the module. */ + @Serializable + data class Config( + var enabled: Boolean = true, + var header: String = + "\n───────────────────────────────────", + var footer: String = + "───────────────────────────────────\n", + var slotPrefix: String = ".", + ) +}