Изменения:
- ТЕПЕРЬ ЗАЛАМИНИРОВАННЫЕ КАРТЫ НЕЛЬЗЯ ДЮПАТЬ В КРАФТЕРЕ (ОТКЛЮЧАЕМ ВАНИЛЬНЫЕ МЕХАНИКИ, ОУУ Е)
- Теперь можно подписывать предметы при помощи бирки в наковальне и окрашивать это название путём совмещения предмета с биркой и красителем в наковальне.
This commit is contained in:
lonewo 2025-09-02 08:58:49 +05:00
parent 1789f5907e
commit e0789c4a9d
3 changed files with 690 additions and 1 deletions

View file

@ -1,6 +1,7 @@
package dev.marrow.zsign
import com.github.shynixn.mccoroutine.bukkit.SuspendingJavaPlugin
import dev.marrow.zsign.listeners.AnvilUseListener
import dev.marrow.zsign.listeners.MapLaminationListener
import dev.marrow.zsign.utils.CommonUtils.gson
import dev.marrow.zsign.utils.ComponentExtension.deserializeMiniMessage
@ -24,6 +25,7 @@ class Core : SuspendingJavaPlugin() {
// Только наш cartography-слушатель
server.pluginManager.registerEvents(MapLaminationListener(this), this)
server.pluginManager.registerEvents(AnvilUseListener(this), this)
this.lifecycleManager.registerEventHandler(LifecycleEvents.COMMANDS) {
}

View file

@ -0,0 +1,202 @@
package dev.marrow.zsign.listeners
import dev.marrow.zsign.Core
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor
import net.kyori.adventure.text.format.TextColor
import net.kyori.adventure.text.format.TextDecoration
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer
import org.bukkit.Material
import org.bukkit.entity.Player
import org.bukkit.event.EventHandler
import org.bukkit.event.EventPriority
import org.bukkit.event.Listener
import org.bukkit.event.inventory.ClickType
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.InventoryType
import org.bukkit.event.inventory.PrepareAnvilEvent
import org.bukkit.inventory.Inventory
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.Repairable
class AnvilUseListener(private val plugin: Core) : Listener {
private val config get() = plugin.config
private val banSymbols get() = config.getStringList("ban-symbols")
private val streamerWords get() = config.getStringList("streamer-words")
companion object {
private const val SECOND_SLOT = 1
private val colorMap = mapOf(
Material.RED_DYE to TextColor.color(0xE6060C),
Material.BROWN_DYE to TextColor.color(0xAA5500),
Material.ORANGE_DYE to TextColor.color(0xFFAA00),
Material.YELLOW_DYE to TextColor.color(0xFFEE4C),
Material.LIME_DYE to TextColor.color(0x55FF55),
Material.GREEN_DYE to TextColor.color(0x00AA00),
Material.LIGHT_BLUE_DYE to TextColor.color(0x55FFFF),
Material.CYAN_DYE to TextColor.color(0x00AAAA),
Material.BLUE_DYE to TextColor.color(0x4842EA),
Material.PURPLE_DYE to TextColor.color(0xAA00AA),
Material.MAGENTA_DYE to TextColor.color(0xDC89BF),
Material.PINK_DYE to TextColor.color(0xFFB6C1),
Material.WHITE_DYE to TextColor.color(0xFFFFFF),
Material.LIGHT_GRAY_DYE to TextColor.color(0xAAAAAA),
Material.GRAY_DYE to TextColor.color(0x555555),
Material.BLACK_DYE to TextColor.color(0x000000)
)
private val plain = PlainTextComponentSerializer.plainText()
private fun ItemStack?.isAir(): Boolean = this == null || type == Material.AIR
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
fun onPrepareAnvil(e: PrepareAnvilEvent) {
val inv = e.inventory
val firstItem = inv.firstItem ?: return
val secondItem = inv.secondItem
val renameText = inv.renameText?.takeIf { it.isNotBlank() }
if (secondItem != null && (secondItem.type == Material.NAME_TAG || secondItem.type in colorMap.keys) && secondItem.amount > 1) {
e.result = null
return
}
var newName: String? = null
if (renameText != null) {
var censored = renameText
streamerWords.forEach { word ->
censored = censored?.replace(word, "*".repeat(word.length), ignoreCase = true)
}
banSymbols.forEach { symbol ->
if (censored?.contains(symbol) ?: false) {
e.result = null
return
}
}
newName = censored
}
val isCustom = secondItem != null && (secondItem.type == Material.NAME_TAG || secondItem.type in colorMap.keys)
var result: ItemStack? = e.result
if (result == null && !isCustom && newName == null) return
if (result == null) result = firstItem.clone()
val meta = result.itemMeta ?: run {
e.result = null
return
}
if (newName != null) {
meta.displayName(Component.text(newName))
}
if (secondItem != null) {
val lore = meta.lore()?.toMutableList() ?: mutableListOf()
val hashtagIndex = lore.indexOfFirst { plain.serialize(it).startsWith("#") }
val hasHashtag = hashtagIndex != -1
if (secondItem.type == Material.NAME_TAG) {
if (hasHashtag) {
e.result = null
return
}
val playerName = e.viewers.firstOrNull()?.name ?: "player"
lore.add(Component.empty())
lore.add(Component.text("#$playerName", NamedTextColor.GRAY).decoration(TextDecoration.ITALIC, false))
meta.lore(lore)
(meta as? Repairable)?.repairCost = 1
inv.repairCost = 1
} else if (secondItem.type in colorMap.keys) {
if (!hasHashtag) {
e.result = null
return
}
val currentHashtagText = plain.serialize(lore[hashtagIndex])
val playerName = currentHashtagText.removePrefix("#")
val color = colorMap.getValue(secondItem.type)
lore[hashtagIndex] = Component.text("#$playerName", color).decoration(TextDecoration.ITALIC, false)
meta.lore(lore)
(meta as? Repairable)?.repairCost = 1
inv.repairCost = 1
}
}
result.itemMeta = meta
if (result.isSimilar(firstItem)) {
e.result = null
return
}
e.result = result
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
fun onAnvilInsert(e: InventoryClickEvent) {
val inv = e.inventory
if (inv.type != InventoryType.ANVIL) return
val p = e.whoClicked as? Player ?: return
val firstItem = inv.first()
if (firstItem.isAir()) return
val cursor = p.itemOnCursor
if (e.clickedInventory === inv && e.rawSlot == SECOND_SLOT && (cursor.type == Material.NAME_TAG || cursor.type in colorMap.keys)) {
e.isCancelled = true
val placeAmount = if (e.click == ClickType.RIGHT) 1 else cursor.amount
val toPlace = placeAmount.coerceAtMost(1)
if (toPlace > 0) {
val current = inv.getItem(SECOND_SLOT)
val newItem = cursor.clone()
newItem.amount = 1
inv.setItem(SECOND_SLOT, newItem)
cursor.amount -= 1
p.setItemOnCursor(if (cursor.amount > 0) cursor else ItemStack(Material.AIR))
if (current != null && !current.isAir()) {
p.inventory.addItem(current.clone().apply { amount = 1 })
}
p.scheduler.runDelayed(plugin, { _ -> p.updateInventory() }, null, 1L)
}
return
}
if (e.clickedInventory === inv && e.rawSlot == SECOND_SLOT && e.click == ClickType.NUMBER_KEY) {
val idx = e.hotbarButton
if (idx in 0..8) {
val hot = p.inventory.getItem(idx)
if ((hot?.type == Material.NAME_TAG || hot?.type in colorMap.keys) && (hot?.amount ?: 0) > 0) {
e.isCancelled = true
val current = inv.getItem(SECOND_SLOT)
val newItem = hot!!.clone()
newItem.amount = 1
inv.setItem(SECOND_SLOT, newItem)
hot.amount -= 1
p.inventory.setItem(idx, if (hot.amount > 0) hot else ItemStack(Material.AIR))
if (current != null && !current.isAir()) {
p.inventory.addItem(current.clone().apply { amount = 1 })
}
p.scheduler.runDelayed(plugin, { _ -> p.updateInventory() }, null, 1L)
}
}
return
}
if (e.clickedInventory !== inv && e.isShiftClick) {
val clicked = e.currentItem ?: return
if (clicked.type == Material.NAME_TAG || clicked.type in colorMap.keys) {
e.isCancelled = true
val current = inv.getItem(SECOND_SLOT)
val newItem = clicked.clone()
newItem.amount = 1
inv.setItem(SECOND_SLOT, newItem)
clicked.amount -= 1
e.clickedInventory?.setItem(e.slot, if (clicked.amount > 0) clicked else ItemStack(Material.AIR))
if (current != null && !current.isAir()) {
p.inventory.addItem(current.clone().apply { amount = 1 })
}
p.scheduler.runDelayed(plugin, { _ -> p.updateInventory() }, null, 1L)
}
}
}
}