From 762e7d4e0b4dd482d06bdc98707f43281e736931 Mon Sep 17 00:00:00 2001 From: alexcrea <42614139+alexcrea@users.noreply.github.com> Date: Mon, 17 Mar 2025 14:07:44 +0100 Subject: [PATCH] lore edit color should work --- .../cuanvil/listener/AnvilResultListener.kt | 16 +- .../cuanvil/listener/PrepareAnvilListener.kt | 7 +- .../alexcrea/cuanvil/util/AnvilColorUtil.kt | 132 +++++++++++++--- .../cuanvil/util/AnvilLoreEditUtil.kt | 143 +++++++++++++++--- .../cuanvil/util/config/LoreEditConfigUtil.kt | 6 +- 5 files changed, 259 insertions(+), 45 deletions(-) diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt index 2c19183..ce0f3af 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt @@ -30,6 +30,7 @@ import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil import xyz.alexcrea.cuanvil.util.config.LoreEditType import java.util.* import java.util.concurrent.atomic.AtomicInteger +import kotlin.collections.ArrayList import kotlin.math.min class AnvilResultListener : Listener { @@ -348,13 +349,15 @@ class AnvilResultListener : Listener { // fill book meta val meta = leftItem.itemMeta if (meta == null || !meta.hasLore()) return false - val lore = meta.lore!! + val lore = ArrayList(meta.lore!!) if (lore.isEmpty()) return false + // Uncolor the page + AnvilLoreEditUtil.uncolorLines(player, lore, LoreEditType.REMOVE_BOOK) + val bookPage = StringBuilder() lore.forEach { if (bookPage.isNotEmpty()) bookPage.append('\n') - //TODO check & do color bookPage.append(it) } @@ -426,10 +429,15 @@ class AnvilResultListener : Listener { if (lore.isEmpty()) return false val removeEnd = LoreEditConfigUtil.paperLoreOrderIsEnd - //TODO check & do color - val line = if (removeEnd) lore[lore.size - 1] + var line = if (removeEnd) lore[lore.size - 1] else lore[0] + // Overkill but uncolor the line + val tempList = ArrayList(1) + tempList.add(line) + AnvilLoreEditUtil.uncolorLines(player, tempList, LoreEditType.REMOVE_PAPER) + line = tempList[0] + // Create result item val rightClone = rightItem.clone() rightClone.amount = 1 diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt index d2ad027..5708126 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt @@ -80,7 +80,7 @@ class PrepareAnvilListener : Listener { } - // return true if a custom recipe exist with these ingredient + // return true if a custom recipe exist with these ingredients private fun testCustomRecipe(event: PrepareAnvilEvent, inventory: AnvilInventory, player: HumanEntity, first: ItemStack, second: ItemStack?): Boolean { @@ -134,7 +134,10 @@ class PrepareAnvilListener : Listener { if(ConfigOptions.renameColorPossible && inventoryName != null){ val resultString = StringBuilder(inventoryName) - useColor = AnvilColorUtil.handleRenamingColor(resultString, player) + useColor = AnvilColorUtil.handleColor(resultString, player, + ConfigOptions.permissionNeededForColor, + ConfigOptions.allowColorCode, ConfigOptions.allowHexadecimalColor, + AnvilColorUtil.ColorUseType.RENAME) if(useColor) { inventoryName = resultString.toString() diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilColorUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilColorUtil.kt index 0e216e8..0bb1fe9 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilColorUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilColorUtil.kt @@ -1,38 +1,99 @@ package xyz.alexcrea.cuanvil.util -import io.delilaheve.util.ConfigOptions -import org.bukkit.entity.HumanEntity +import org.bukkit.permissions.Permissible import java.util.regex.Matcher import java.util.regex.Pattern object AnvilColorUtil { private val HEX_PATTERN: Pattern = Pattern.compile("#[A-Fa-f0-9]{6}") // pattern to find hexadecimal string + private val TRANSFORMED_HEX_PATTERN = Pattern.compile("§x(§[0-9a-fA-F]){6}") // pattern to find minecraft hex string - fun handleRenamingColor(textToColor: StringBuilder, player: HumanEntity): Boolean { - val usePermission = ConfigOptions.permissionNeededForColor - val canUseColorCode = ConfigOptions.allowColorCode && (!usePermission || player.hasPermission("ca.color.code")) - val canUseHexColor = ConfigOptions.allowHexadecimalColor && (!usePermission || player.hasPermission("ca.color.hex")) + /** + * Color a stringbuilder object depending on allowed color type and player permissions on color use type + * @return if the stringbuilder was changed and color applied + */ + fun handleColor( + textToColor: StringBuilder, + player: Permissible, + usePermission: Boolean, + allowColorCode: Boolean, + allowHexadecimalColor: Boolean, + useType: ColorUseType + ): Boolean { + if (!allowColorCode && !allowHexadecimalColor) return false - if((!canUseColorCode) && (!canUseHexColor)) return false + val canUseColorCode = + allowColorCode && (!usePermission || useType.colorCodePerm == null || player.hasPermission( + useType.colorCodePerm + )) + val canUseHexColor = + allowHexadecimalColor && (!usePermission || useType.hexColorPerm == null || player.hasPermission( + useType.hexColorPerm + )) + + if ((!canUseColorCode) && (!canUseHexColor)) return false var useColor = false // Handle color code - if(canUseColorCode){ + if (canUseColorCode) { var nbReplacement = replaceAll(textToColor, "&", "§", 2) nbReplacement -= 2 * replaceAll(textToColor, "§§", "&", 2) - if(nbReplacement > 0) useColor = true + if (nbReplacement > 0) useColor = true } - if(canUseHexColor){ + if (canUseHexColor) { val nbReplacement = replaceHexToColor(textToColor, 7) - if(nbReplacement > 0) useColor = true + if (nbReplacement > 0) useColor = true } return useColor } + /** + * Revert a stringbuilder to a state where applying handleColor with the same options would give the same result + * @return if the stringbuilder was changed and color unapplied + */ + fun revertColor( + colorToText: StringBuilder, + player: Permissible, + usePermission: Boolean, + allowColorCode: Boolean, + allowHexadecimalColor: Boolean, + useType: ColorUseType + ): Boolean { + if (!allowColorCode && !allowHexadecimalColor) return false + + val canUseColorCode = + allowColorCode && (!usePermission || useType.colorCodePerm == null || player.hasPermission( + useType.colorCodePerm + )) + val canUseHexColor = + allowHexadecimalColor && (!usePermission || useType.hexColorPerm == null || player.hasPermission( + useType.hexColorPerm + )) + + if ((!canUseColorCode) && (!canUseHexColor)) return false + var hasReversed = false + + // Reverse hex pattern + if (canUseHexColor) { + val nbReplacement = replaceHexToColor(colorToText, 14) + + if (nbReplacement > 0) hasReversed = true + } + + if (canUseColorCode) { + replaceAll(colorToText, "&", "&&", 1) + val nbReplacement = replaceAll(colorToText, "§", "&", 2) + + if (nbReplacement > 0) hasReversed = true + } + + return hasReversed + } + /** * Replace every instance of "from" to "to". * @param builder The builder to replace the string from. @@ -50,7 +111,7 @@ object AnvilColorUtil { index += to.length index = builder.indexOf(from, index) - numberOfChanges+=1 + numberOfChanges += 1 } return numberOfChanges @@ -68,21 +129,58 @@ object AnvilColorUtil { var numberOfChanges = 0 var startIndex = 0 - while(matcher.find(startIndex)){ + while (matcher.find(startIndex)) { startIndex = matcher.start() - if(startIndex >= builder.length - endOffset) break + if (startIndex >= builder.length - endOffset) break //HOW AND WHERE WOULD THIS HAPPEN ????? builder.replace(startIndex, startIndex + 1, "§x") - startIndex+=2 + startIndex += 2 for (i in 0..5) { builder.insert(startIndex, '§') - startIndex+=2 + startIndex += 2 } - numberOfChanges+=1 + numberOfChanges += 1 } return numberOfChanges } + /** + * Replace every hex color from the minecraft format to a format like #000000 + * @param builder The builder to replace the minecraft hex color from. + * @param endOffset Amount of character that should be ignored at the end. + * @return The number of replacement was that was done. + */ + private fun replaceColorToHex(builder: StringBuilder, endOffset: Int): Int { + val matcher: Matcher = TRANSFORMED_HEX_PATTERN.matcher(builder) + + var numberOfChanges = 0 + var startIndex = 0 + + while (matcher.find(startIndex)) { + startIndex = matcher.start() + if (startIndex >= builder.length - endOffset) break //HOW AND WHERE WOULD THIS HAPPEN ????? + + builder.replace(startIndex, startIndex + 2, "#") + startIndex += 1 + for (i in 0..5) { + builder.deleteCharAt(startIndex) + startIndex += 1 + } + + numberOfChanges += 1 + } + + return numberOfChanges + } + + enum class ColorUseType( + val colorCodePerm: String?, + val hexColorPerm: String? + ) { + RENAME("ca.color.code", "ca.color.hex"), + LORE_EDIT(null, null) + } + } \ No newline at end of file diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilLoreEditUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilLoreEditUtil.kt index cbe0594..05f5831 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilLoreEditUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilLoreEditUtil.kt @@ -30,21 +30,26 @@ object AnvilLoreEditUtil { if (!hasLoreEditByBookPermission(player)) return null val result = first.clone() - val meta = result.itemMeta?: return null + val meta = result.itemMeta ?: return null val lore = if (meta.hasLore()) { ArrayList(meta.lore!!) } else ArrayList() - //TODO check color if color if enabled - val lines = book.pages[0].split("\n") + val page = book.pages[0] + val lines = ArrayList(page.split("\n")) + val colorCost = colorLines(player, lines, LoreEditType.APPEND_BOOK) + lore.addAll(lines) meta.lore = lore result.itemMeta = meta - // Handle other xp - xpCost.addAndGet(lines.size * LoreEditType.APPEND_BOOK.perLineCost) - xpCost.addAndGet(baseEditLoreXpCost(first, result, LoreEditType.APPEND_BOOK)) + if (result == first) return null + + // Handle xp + xpCost.addAndGet(colorCost) // Cost of using color + xpCost.addAndGet(lines.size * LoreEditType.APPEND_BOOK.perLineCost) // per line cost + xpCost.addAndGet(baseEditLoreXpCost(first, result, LoreEditType.APPEND_BOOK)) // Fixed cost and work penalty return result } @@ -54,20 +59,25 @@ object AnvilLoreEditUtil { // remove lore val result = first.clone() - val leftMeta = result.itemMeta?: return null - val currentLore = leftMeta.lore?: return null + val leftMeta = result.itemMeta ?: return null + val currentLore = ArrayList(leftMeta.lore ?: return null) + + val uncolorCost = uncolorLines(player, currentLore, LoreEditType.REMOVE_BOOK) leftMeta.lore = null result.itemMeta = leftMeta - // Handle other xp + if (result == first) return null + + // Handle xp + xpCost.addAndGet(uncolorCost) xpCost.addAndGet(currentLore.size * LoreEditType.REMOVE_BOOK.perLineCost) xpCost.addAndGet(baseEditLoreXpCost(first, result, LoreEditType.REMOVE_BOOK)) return result } - // Return true if append, false if remove, null if neither + // Return true if appended, false if removed, null if neither fun bookLoreEditIsAppend(first: ItemStack, second: ItemStack): Boolean? { // Test if the book & quil contain content val meta = second.itemMeta as BookMeta? ?: return false @@ -106,7 +116,7 @@ object AnvilLoreEditUtil { else handleLoreRemoveByBook(player, first, xpCost) } - // Return true if append, false if remove, null if neither + // Return true if appended, false if removed, null if neither fun paperLoreEditIsAppend(first: ItemStack, second: ItemStack): Boolean? { // Test if the paper contain a display name val meta = second.itemMeta ?: return false @@ -134,24 +144,31 @@ object AnvilLoreEditUtil { if (!hasLoreEditByPaperPermission(player)) return null val result = first.clone() - val meta = result.itemMeta - val lore = if (meta?.hasLore() == true) { + val meta = result.itemMeta?: return null + val lore = if (meta.hasLore()) { ArrayList(meta.lore!!) } else ArrayList() val appendEnd = LoreEditConfigUtil.paperLoreOrderIsEnd - //TODO check color if color if enabled - val line = second.itemMeta!!.displayName + // A bit overdone to color 1 line but hey + val tempList = ArrayList(1) + tempList.add(second.itemMeta!!.displayName) + val colorCost = colorLines(player, tempList, LoreEditType.APPEND_PAPER) + + val line = tempList[0] if (appendEnd) lore.add(line) else lore.add(0, line) - meta?.lore = lore + meta.lore = lore result.itemMeta = meta - // Handle other xp + if (result == first) return null + + // Handle xp + xpCost.addAndGet(colorCost) xpCost.addAndGet(baseEditLoreXpCost(first, result, LoreEditType.APPEND_PAPER)) return result @@ -167,13 +184,21 @@ object AnvilLoreEditUtil { val removeEnd = LoreEditConfigUtil.paperLoreOrderIsEnd val lore: ArrayList = ArrayList(meta.lore!!) - if (removeEnd) lore.removeAt(lore.size - 1) + val line = if (removeEnd) lore.removeAt(lore.size - 1) else lore.removeAt(0) meta.lore = if (lore.isEmpty()) null else lore result.itemMeta = meta + // Get color cost to uncolor this line + val tempList = ArrayList(1) + tempList.add(line) + val uncolorCost = uncolorLines(player, tempList, LoreEditType.REMOVE_PAPER) + + if (result == first) return null + // Handle other xp + xpCost.addAndGet(uncolorCost) xpCost.addAndGet(baseEditLoreXpCost(first, result, LoreEditType.REMOVE_PAPER)) return result @@ -202,4 +227,86 @@ object AnvilLoreEditUtil { return xpCost } + private fun colorLines(player: Permissible, lines: ArrayList, useType: LoreEditType): Int { + val canUseHex: Boolean + val canUseColorCode: Boolean + val colorCost: Int + // If is book + if (useType == LoreEditType.REMOVE_BOOK || useType == LoreEditType.APPEND_BOOK) { + canUseHex = LoreEditConfigUtil.bookAllowHexColor + canUseColorCode = LoreEditConfigUtil.bookAllowColorCode + colorCost = LoreEditConfigUtil.bookUseColorCost + } // Else if is paper + else { + canUseHex = LoreEditConfigUtil.paperAllowHexColor + canUseColorCode = LoreEditConfigUtil.paperAllowColorCode + colorCost = LoreEditConfigUtil.paperUseColorCost + } + + // Now handle color of each lines + var hasUsedColor = false + for ((index, line) in lines.withIndex()) { + val coloredLine = StringBuilder(line) + + val lineUsedColor = AnvilColorUtil.handleColor( + coloredLine, + player, + false, canUseColorCode, canUseHex, + AnvilColorUtil.ColorUseType.LORE_EDIT + ) + + if (lineUsedColor) { + hasUsedColor = true + lines[index] = coloredLine.toString() + } + } + + return if (hasUsedColor) { + colorCost + } else { + 0 + } + } + + fun uncolorLines(player: Permissible, lines: ArrayList, useType: LoreEditType): Int { + val canUseHex: Boolean + val canUseColorCode: Boolean + val colorCost: Int + // If is book + if (useType == LoreEditType.REMOVE_BOOK || useType == LoreEditType.APPEND_BOOK) { + canUseHex = LoreEditConfigUtil.bookAllowHexColor + canUseColorCode = LoreEditConfigUtil.bookAllowColorCode + colorCost = LoreEditConfigUtil.bookUseColorCost + } // Else if is paper + else { + canUseHex = LoreEditConfigUtil.paperAllowHexColor + canUseColorCode = LoreEditConfigUtil.paperAllowColorCode + colorCost = LoreEditConfigUtil.paperUseColorCost + } + + // Now handle color of each lines + var hasUndidColor = false + for ((index, line) in lines.withIndex()) { + val uncoloredLine = StringBuilder(line) + + val lineUndidColor = AnvilColorUtil.revertColor( + uncoloredLine, + player, + false, canUseColorCode, canUseHex, + AnvilColorUtil.ColorUseType.LORE_EDIT + ) + + if (lineUndidColor) { + hasUndidColor = true + lines[index] = uncoloredLine.toString() + } + } + + return if (hasUndidColor) { + colorCost + } else { + 0 + } + } + } \ No newline at end of file diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/config/LoreEditConfigUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/config/LoreEditConfigUtil.kt index 680bb57..e393279 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/config/LoreEditConfigUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/config/LoreEditConfigUtil.kt @@ -8,8 +8,6 @@ object LoreEditConfigUtil { const val IS_ENABLED = "enabled" const val FIXED_COST = "fixed_cost" const val PER_LINE_COST = "per_line_cost" - const val USE_COST_PENALTY = "use_cost_penalty" - const val INCREASE_COST_PENALTY = "increase_cost_penalty" const val DO_CONSUME = "do_consume" // Permission configs path @@ -61,8 +59,8 @@ object LoreEditConfigUtil { val FIXED_COST_RANGE = 0..1000 val PER_LINE_COST_RANGE = 0..1000 - val COLOR_BOOK_COST_RANGE = 0..1000 - val COLOR_PAPER_COST_RANGE = 0..1000 + private val COLOR_BOOK_COST_RANGE = 0..1000 + private val COLOR_PAPER_COST_RANGE = 0..1000 // ------------------- // Generic Get methods