Skip to content

Commit 36e3d05

Browse files
committed
feat(item): 添加物品附魔功能支持
- 新增 DisplayEnchant 数据类处理附魔配置 - 实现附魔解析逻辑支持多种配置格式 - 添加动态附魔功能支持占位符变量 - 在 Item 类中集成附魔应用逻辑 - 更新 Meta 类添加附魔处理方法 - 修改 DialogNMSImpl 移除描述文本相关代码 - 更新版本号到 3.12.0
1 parent 6cfe46c commit 36e3d05

7 files changed

Lines changed: 219 additions & 23 deletions

File tree

api/receptacle/src/main/kotlin/trplugins/menu/api/receptacle/dialog/DialogNMSImpl.kt

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -213,27 +213,10 @@ class DialogNMSImpl : DialogNMS() {
213213

214214
private fun buildItemBody(element: DialogElementPayload): Any {
215215
val item = toNmsItem(element.item ?: ItemStack(org.bukkit.Material.AIR))
216-
val descriptionText = buildString {
217-
element.item?.itemMeta?.displayName?.takeIf { it.isNotBlank() }?.also { append(it) }
218-
val lore = element.item?.itemMeta?.lore.orEmpty()
219-
if (lore.isNotEmpty()) {
220-
if (isNotBlank()) append('\n')
221-
append(lore.joinToString("\n"))
222-
}
223-
}.takeIf { it.isNotBlank() }
224-
val description = descriptionText?.let {
225-
Optional.of(
226-
newInstance(
227-
PLAIN_MESSAGE_CLASS_NAME,
228-
component(it),
229-
element.width ?: DEFAULT_TEXT_WIDTH
230-
)
231-
)
232-
} ?: Optional.empty<Any>()
233216
return newInstance(
234217
ITEM_BODY_CLASS_NAME,
235218
item,
236-
description,
219+
Optional.empty<Any>(),
237220
true,
238221
true,
239222
element.width ?: DEFAULT_ITEM_SIZE,

common/src/main/kotlin/trplugins/menu/util/conf/Property.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,11 @@ enum class Property(val default: String, val regex: Regex) {
358358
*/
359359
ICON_DISPLAY_AMOUNT("amount", "(amt|amount)s?"),
360360

361+
/**
362+
* 菜单图标显示 - 附魔
363+
*/
364+
ICON_DISPLAY_ENCHANT("enchant", "enchant(ment)?s?"),
365+
361366
/**
362367
* 菜单图标显示 - 发光
363368
*/

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
group=me.arasple.mc.trmenu
2-
version=3.11.1
2+
version=3.12.0

plugin/src/main/kotlin/trplugins/menu/module/conf/MenuSerializer.kt

Lines changed: 120 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,15 @@ import trplugins.menu.module.display.dialog.model.DialogWidgetSpec
5252
import trplugins.menu.module.display.icon.Icon
5353
import trplugins.menu.module.display.icon.IconProperty
5454
import trplugins.menu.module.display.icon.Position
55+
import trplugins.menu.module.display.item.DisplayEnchant
5556
import trplugins.menu.module.display.item.Item
5657
import trplugins.menu.module.display.item.Lore
5758
import trplugins.menu.module.display.item.Meta
5859
import trplugins.menu.module.display.layout.Layout
5960
import trplugins.menu.module.display.layout.MenuLayout
6061
import trplugins.menu.module.display.texture.Texture
6162
import trplugins.menu.module.internal.script.js.ScriptFunction
63+
import trplugins.menu.util.Regexs
6264
import trplugins.menu.util.bukkit.ItemMatcher
6365
import trplugins.menu.util.collections.CycleList
6466
import trplugins.menu.util.collections.IndivList
@@ -395,7 +397,12 @@ object MenuSerializer : ISerializer {
395397

396398
// Meta
397399
val amount = if (inherit.contains(Property.ICON_DISPLAY_AMOUNT)) def!!.display.meta.amount else Property.ICON_DISPLAY_AMOUNT.ofString(display, "1")
398-
val shiny = if (inherit.contains(Property.ICON_DISPLAY_SHINY)) def!!.display.meta.shiny else Property.ICON_DISPLAY_SHINY.ofString(display, "false")
400+
val shiny = if (inherit.contains(Property.ICON_DISPLAY_SHINY)) def!!.display.meta.shiny else parseDisplayShiny(display)
401+
val enchants = if (inherit.contains(Property.ICON_DISPLAY_ENCHANT)) {
402+
def!!.display.meta.enchants
403+
} else {
404+
parseDisplayEnchants(display)
405+
}
399406
val flags = if (inherit.contains(Property.ICON_DISPLAY_FLAGS)) {
400407
def!!.display.meta.flags
401408
} else Property.ICON_DISPLAY_FLAGS.ofStringList(display).mapNotNull { flag ->
@@ -458,7 +465,7 @@ object MenuSerializer : ISerializer {
458465
if (def != null && inherit.contains(Property.ICON_DISPLAY_LORE) && lore.isEmpty()) def.display.lore
459466
else CycleList(lore.map { Lore(line(it)) }),
460467
// 图标附加属性
461-
Meta(amount, shiny, flags, nbt, tooltipStyle, itemModel, hideTooltip, unbreakable, data)
468+
Meta(amount, shiny, flags, enchants, nbt, tooltipStyle, itemModel, hideTooltip, unbreakable, data)
462469
)
463470

464471
// i18n
@@ -686,6 +693,117 @@ object MenuSerializer : ISerializer {
686693
return major * 10000 + minor * 100 + patch
687694
}
688695

696+
private fun parseDisplayShiny(display: Configuration?): String {
697+
if (display == null) {
698+
return "false"
699+
}
700+
display.getKeys(false).firstOrNull { it.equals("shiny", true) || it.equals("glow", true) }?.let { key ->
701+
return display[key].toString()
702+
}
703+
display.getKeys(false).firstOrNull { it.matches(Property.ICON_DISPLAY_ENCHANT.regex) }?.let { key ->
704+
val value = display[key]
705+
if (value is Boolean) {
706+
return value.toString()
707+
}
708+
val content = value?.toString()?.trim().orEmpty()
709+
if (content.matches(Regexs.BOOLEAN)) {
710+
return content
711+
}
712+
}
713+
return "false"
714+
}
715+
716+
private fun parseDisplayEnchants(display: Configuration?): List<DisplayEnchant> {
717+
if (display == null) {
718+
return emptyList()
719+
}
720+
val key = display.getKeys(false).firstOrNull { it.matches(Property.ICON_DISPLAY_ENCHANT.regex) } ?: return emptyList()
721+
return parseDisplayEnchantValue(display[key])
722+
}
723+
724+
private fun parseDisplayEnchantValue(raw: Any?): List<DisplayEnchant> {
725+
return when (raw) {
726+
null -> emptyList()
727+
is List<*> -> raw.flatMap { parseDisplayEnchantElement(it) }
728+
is ConfigurationSection -> parseDisplayEnchantMap(raw.getValues(false))
729+
is Map<*, *> -> parseDisplayEnchantMap(raw)
730+
else -> listOfNotNull(parseDisplayEnchantString(raw.toString()))
731+
}
732+
}
733+
734+
private fun parseDisplayEnchantElement(raw: Any?): List<DisplayEnchant> {
735+
return when (raw) {
736+
null -> emptyList()
737+
is List<*> -> raw.flatMap { parseDisplayEnchantElement(it) }
738+
is ConfigurationSection -> parseDisplayEnchantMap(raw.getValues(false))
739+
is Map<*, *> -> parseDisplayEnchantMap(raw)
740+
else -> listOfNotNull(parseDisplayEnchantString(raw.toString()))
741+
}
742+
}
743+
744+
private fun parseDisplayEnchantMap(raw: Map<*, *>): List<DisplayEnchant> {
745+
if (raw.isEmpty()) {
746+
return emptyList()
747+
}
748+
if (isDisplayEnchantDescriptor(raw)) {
749+
return listOfNotNull(parseDisplayEnchantDescriptor(raw))
750+
}
751+
return raw.entries.mapNotNull { (key, value) ->
752+
val enchantKey = key?.toString()?.trim().orEmpty()
753+
val enchantLevel = value?.toString()?.trim().orEmpty()
754+
if (enchantKey.isEmpty() || enchantLevel.isEmpty()) {
755+
null
756+
} else {
757+
DisplayEnchant(enchantKey, enchantLevel)
758+
}
759+
}
760+
}
761+
762+
private fun isDisplayEnchantDescriptor(raw: Map<*, *>): Boolean {
763+
return raw.keys.any { key ->
764+
val content = key?.toString().orEmpty()
765+
content.equals("id", true) ||
766+
content.equals("key", true) ||
767+
content.equals("type", true) ||
768+
content.equals("enchant", true) ||
769+
content.equals("enchantment", true)
770+
}
771+
}
772+
773+
private fun parseDisplayEnchantDescriptor(raw: Map<*, *>): DisplayEnchant? {
774+
val enchantKey = readDisplayEnchantField(raw, "id", "key", "type", "enchant", "enchantment") ?: return null
775+
val enchantLevel = readDisplayEnchantField(raw, "level", "lvl", "value", "amount") ?: "1"
776+
return DisplayEnchant(enchantKey, enchantLevel)
777+
}
778+
779+
private fun readDisplayEnchantField(raw: Map<*, *>, vararg names: String): String? {
780+
return raw.entries.firstOrNull { entry -> names.any { entry.key?.toString().equals(it, true) } }
781+
?.value
782+
?.toString()
783+
?.trim()
784+
?.takeIf { it.isNotEmpty() }
785+
}
786+
787+
private fun parseDisplayEnchantString(raw: String): DisplayEnchant? {
788+
val content = raw.trim()
789+
if (content.isEmpty()) {
790+
return null
791+
}
792+
val split = content.split(Regex("[,\\s]+"), limit = 2).map { it.trim() }.filter { it.isNotEmpty() }
793+
if (split.size == 2) {
794+
return DisplayEnchant(split[0], split[1])
795+
}
796+
val lastColon = content.lastIndexOf(':')
797+
if (lastColon in 1 until content.lastIndex) {
798+
val enchantKey = content.substring(0, lastColon).trim()
799+
val enchantLevel = content.substring(lastColon + 1).trim()
800+
if (enchantKey.isNotEmpty() && enchantLevel.toIntOrNull() != null) {
801+
return DisplayEnchant(enchantKey, enchantLevel)
802+
}
803+
}
804+
return DisplayEnchant(content, "1")
805+
}
806+
689807
val line: (List<String>) -> List<String> =
690808
{ origin -> mutableListOf<String>().also { list -> origin.forEach { list.addAll(it.split("\n")) } } }
691809

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* *
3+
* * @author Arasple
4+
* * @date 2021/2/1 17:39
5+
*
6+
*/
7+
8+
package trplugins.menu.module.display.item
9+
10+
import trplugins.menu.module.display.MenuSession
11+
import trplugins.menu.module.internal.script.evalScript
12+
import trplugins.menu.util.Regexs
13+
14+
data class DisplayEnchant(
15+
val key: String,
16+
val level: String = "1"
17+
) {
18+
19+
private val isKeyDynamic = Regexs.containsPlaceholder(key)
20+
private val isLevelDynamic = Regexs.containsPlaceholder(level) || level.toIntOrNull() == null
21+
val isDynamic = isKeyDynamic || isLevelDynamic
22+
23+
fun enchantKey(session: MenuSession): String {
24+
return if (isKeyDynamic) session.parse(key) else key
25+
}
26+
27+
fun enchantLevel(session: MenuSession): Int {
28+
val parsed = if (isLevelDynamic) session.parse(level) else level
29+
return parsed.toIntOrNull() ?: session.placeholderPlayer.evalScript(level).asInt(1)
30+
}
31+
}

plugin/src/main/kotlin/trplugins/menu/module/display/item/Item.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ open class Item(
9999
meta.nbt(session, itemStack)?.run {
100100
itemStack.itemMeta = this
101101
}
102+
meta.enchants(session, itemStack)
102103

103104
return itemStack
104105
}

plugin/src/main/kotlin/trplugins/menu/module/display/item/Meta.kt

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,33 @@
11
package trplugins.menu.module.display.item
22

3+
import org.bukkit.Material
34
import org.bukkit.NamespacedKey
5+
import org.bukkit.enchantments.Enchantment
46
import org.bukkit.inventory.ItemFlag
57
import org.bukkit.inventory.ItemStack
8+
import org.bukkit.inventory.meta.EnchantmentStorageMeta
69
import org.bukkit.inventory.meta.ItemMeta
10+
import taboolib.library.xseries.XEnchantment
711
import taboolib.module.nms.ItemTag
812
import taboolib.module.nms.getItemTag
913
import taboolib.platform.util.ItemBuilder
1014
import trplugins.menu.module.display.MenuSession
1115
import trplugins.menu.module.internal.script.evalScript
1216
import trplugins.menu.util.Regexs
17+
import kotlin.jvm.optionals.getOrNull
1318

1419
/**
1520
* @author Arasple
1621
* @date 2021/1/24 18:50
1722
* 显示物品的非动画, 支持动态的属性
1823
*/
24+
25+
1926
class Meta(
2027
val amount: String,
2128
val shiny: String,
2229
val flags: Array<ItemFlag>,
30+
val enchants: List<DisplayEnchant>,
2331
val nbt: ItemTag?,
2432
val tooltip: String?,
2533
val itemModel: String?,
@@ -30,11 +38,12 @@ class Meta(
3038

3139
private val isAmountDynamic = amount.toIntOrNull() == null
3240
private val isShinyDynamic = !shiny.matches(Regexs.BOOLEAN)
41+
private val isEnchantDynamic = enchants.any { it.isDynamic }
3342
private val isHideTooltipDynamic = !hideTooltip.matches(Regexs.BOOLEAN)
3443
private val isNBTDynamic = nbt != null && Regexs.containsPlaceholder(nbt.toJsonSimplified())
3544
private val isUnbreakableDynamic = !unbreakable.matches(Regexs.BOOLEAN)
3645
private val isDataDynamic = data.toIntOrNull() == null
37-
val isDynamic = isAmountDynamic || isNBTDynamic || isShinyDynamic || isHideTooltipDynamic
46+
val isDynamic = isAmountDynamic || isNBTDynamic || isShinyDynamic || isEnchantDynamic || isHideTooltipDynamic
3847

3948
fun amount(session: MenuSession): Int {
4049
return (if (isAmountDynamic) session.parse(amount) else amount).toDoubleOrNull()?.toInt() ?: 1
@@ -53,6 +62,20 @@ class Meta(
5362
}
5463
}
5564

65+
fun enchants(session: MenuSession, itemStack: ItemStack) {
66+
if (enchants.isEmpty()) {
67+
return
68+
}
69+
enchants.forEach { displayEnchant ->
70+
val enchant = resolveEnchantment(displayEnchant.enchantKey(session)) ?: return@forEach
71+
val level = displayEnchant.enchantLevel(session)
72+
if (level <= 0) {
73+
return@forEach
74+
}
75+
applyEnchant(itemStack, enchant, level)
76+
}
77+
}
78+
5679
fun nbt(session: MenuSession, itemStack: ItemStack): ItemMeta? {
5780
if (!nbt.isNullOrEmpty()) {
5881
val nbt = if (isNBTDynamic) ItemTag.fromJson(session.parse(nbt.toJson())) else nbt
@@ -105,4 +128,39 @@ class Meta(
105128
builder.damage = evalData
106129
}
107130

108-
}
131+
private fun applyEnchant(item: ItemStack, enchant: Enchantment, level: Int) {
132+
if (item.type == Material.BOOK) {
133+
item.type = Material.ENCHANTED_BOOK
134+
}
135+
if (item.hasItemMeta()) {
136+
val meta = item.itemMeta ?: return
137+
if (meta is EnchantmentStorageMeta) {
138+
meta.addStoredEnchant(enchant, level, true)
139+
} else {
140+
meta.addEnchant(enchant, level, true)
141+
}
142+
item.itemMeta = meta
143+
} else {
144+
item.addUnsafeEnchantment(enchant, level)
145+
}
146+
}
147+
148+
@Suppress("DEPRECATION")
149+
private fun resolveEnchantment(source: String): Enchantment? {
150+
val text = source.trim()
151+
if (text.isEmpty()) {
152+
return null
153+
}
154+
XEnchantment.of(text).getOrNull()?.get()?.let { return it }
155+
val namespacedKey = NamespacedKey.fromString(text.lowercase()) ?: if (':' !in text) NamespacedKey.minecraft(text.lowercase()) else null
156+
if (namespacedKey != null) {
157+
Enchantment.getByKey(namespacedKey)?.let { return it }
158+
}
159+
return Enchantment.values().firstOrNull {
160+
it.name.equals(text, true) ||
161+
it.key.key.equals(text, true) ||
162+
it.key.toString().equals(text, true)
163+
}
164+
}
165+
166+
}

0 commit comments

Comments
 (0)