From b18cf1fd59ec6593ec94e6ff83f3bbfbe71f8edf Mon Sep 17 00:00:00 2001 From: alexcrea Date: Sat, 23 May 2026 16:49:14 +0200 Subject: [PATCH 01/24] hook to vault economy api --- build.gradle.kts | 8 ++++- src/main/kotlin/io/delilaheve/CustomAnvil.kt | 5 ++- .../dependency/econmy/EconomyManager.kt | 31 +++++++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 src/main/kotlin/xyz/alexcrea/cuanvil/dependency/econmy/EconomyManager.kt diff --git a/build.gradle.kts b/build.gradle.kts index 49b00d9..4effecb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -40,11 +40,14 @@ repositories { // ItemsAdder maven(url = "https://maven.devs.beer/") - // for fast stats + // For fast stats maven { name = "thenextlvlReleases" url = uri("https://repo.thenextlvl.net/releases") } + + // For vault unlocked + maven { url = uri("https://jitpack.io") } } val reobfNMS = providers.gradleProperty("subprojects.reobfnms") @@ -103,6 +106,9 @@ dependencies { // ItemsAdder API compileOnly("dev.lone:api-itemsadder:4.0.10") + // Vault api + compileOnly("com.github.MilkBowl:VaultAPI:1.7") + // Include nms implementation(project(":nms:nms-common")) implementation(project(":nms:nms-paper")) diff --git a/src/main/kotlin/io/delilaheve/CustomAnvil.kt b/src/main/kotlin/io/delilaheve/CustomAnvil.kt index 2fa9e78..9548450 100644 --- a/src/main/kotlin/io/delilaheve/CustomAnvil.kt +++ b/src/main/kotlin/io/delilaheve/CustomAnvil.kt @@ -12,6 +12,7 @@ import xyz.alexcrea.cuanvil.command.ReloadExecutor import xyz.alexcrea.cuanvil.config.ConfigHolder import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.dependency.MinecraftVersionUtil +import xyz.alexcrea.cuanvil.dependency.econmy.EconomyManager import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil import xyz.alexcrea.cuanvil.enchant.CAEnchantmentRegistry import xyz.alexcrea.cuanvil.gui.config.MainConfigGui @@ -258,9 +259,11 @@ open class CustomAnvil : JavaPlugin() { MainConfigGui.getInstance().init(DependencyManager.packetManager) GuiSharedConstant.loadConstants() + // Prepare economy if possible + EconomyManager.setupEconomy(this) + // Finally, re add default we may be missing PluginSetDefault.reAddMissingDefault() - } fun reloadResource( diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/econmy/EconomyManager.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/econmy/EconomyManager.kt new file mode 100644 index 0000000..d3b8c21 --- /dev/null +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/econmy/EconomyManager.kt @@ -0,0 +1,31 @@ +package xyz.alexcrea.cuanvil.dependency.econmy + +import net.milkbowl.vault.economy.Economy +import org.bukkit.OfflinePlayer +import org.bukkit.plugin.Plugin +import org.bukkit.plugin.RegisteredServiceProvider + +object EconomyManager { + + private var economy: Economy? = null + + fun setupEconomy(plugin: Plugin) { + if (economy != null) return + + if (plugin.server.pluginManager.getPlugin("Vault") == null) + return + + val rsp: RegisteredServiceProvider? = + plugin.server.servicesManager.getRegistration(Economy::class.java) + if (rsp == null) return + + economy = rsp.getProvider() + } + + fun has(player: OfflinePlayer, amount: Double): Boolean { + return economy?.has(player, amount) == true + } + + + +} From d67380da1a68d86301fd7970afbc905473cc9f66 Mon Sep 17 00:00:00 2001 From: alexcrea Date: Sat, 23 May 2026 18:04:46 +0200 Subject: [PATCH 02/24] per type xp cost --- .../listener/CATreatAnvilResultEvent.java | 37 ++++++++++-- .../cuanvil/dependency/DependencyManager.kt | 5 +- .../plugins/DisenchantmentDependency.kt | 5 +- .../dependency/plugins/HavenBagsDependency.kt | 5 +- .../cuanvil/listener/AnvilResultListener.kt | 29 +++++----- .../cuanvil/listener/PrepareAnvilListener.kt | 56 ++++++++++--------- .../cuanvil/util/AnvilLoreEditUtil.kt | 50 ++++++++--------- .../xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt | 42 +++++++++++++- 8 files changed, 150 insertions(+), 79 deletions(-) diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResultEvent.java b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResultEvent.java index 1675d1a..4d68aca 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResultEvent.java +++ b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResultEvent.java @@ -7,6 +7,7 @@ import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import xyz.alexcrea.cuanvil.util.AnvilUseType; +import xyz.alexcrea.cuanvil.util.AnvilXpUtil.AnvilCost; /** * Called after custom anvil processed the click on the result on the anvil inventory. @@ -40,13 +41,13 @@ public class CATreatAnvilResultEvent extends Event { @Nullable private ItemStack result; - private int levelCost; + private final AnvilCost cost; - public CATreatAnvilResultEvent(@NotNull PrepareAnvilEvent event, AnvilUseType useType, @Nullable ItemStack result, int levelCost) { + public CATreatAnvilResultEvent(@NotNull PrepareAnvilEvent event, AnvilUseType useType, @Nullable ItemStack result, AnvilCost cost) { this.event = event; this.useType = useType; this.result = result; - this.levelCost = levelCost; + this.cost = cost; } /** @@ -103,10 +104,12 @@ public class CATreatAnvilResultEvent extends Event { *
  • Item rename
  • * * + * @deprecated use #{@link #getCost()} instead * @return The current cost. */ + @Deprecated(forRemoval = true, since = "1.17.0") public int getLevelCost() { - return levelCost; + return cost.sum(); } /** @@ -123,9 +126,33 @@ public class CATreatAnvilResultEvent extends Event { *
  • Item rename
  • * * + * @deprecated use #{@link #getCost()} and set value on this instead * @param levelCost The new cost. */ + @Deprecated(forRemoval = true, since = "1.17.0") public void setLevelCost(int levelCost) { - this.levelCost = levelCost; + cost.setGeneric(levelCost - cost.getGeneric() - cost.sum()); } + + /** + * Allow access to the current cost of the event + * Note that modifying this object will change the event resulting cost + * + *

    Important note:

    + * the final price are re calculated on click for the following use case: + *
      + *
    • Custom craft
    • + *
    • Unit repair
    • + *
    • Lore edit
    • + *
    + * This value will be used as final price for: + *
  • Item merge
  • + *
  • Item rename
  • + * + * @return the current anvil cost + */ + public AnvilCost getCost() { + return cost; + } + } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt index 0b5f396..4e59adc 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt @@ -30,6 +30,7 @@ import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil.componentLore import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_OUTPUT_SLOT import xyz.alexcrea.cuanvil.util.AnvilUseType +import xyz.alexcrea.cuanvil.util.AnvilXpUtil import xyz.alexcrea.cuanvil.util.MetricsUtil.trackError import java.util.logging.Level @@ -235,12 +236,12 @@ object DependencyManager { event: PrepareAnvilEvent, result: ItemStack, useType: AnvilUseType, - cost: Int + cost: AnvilXpUtil.AnvilCost ): CATreatAnvilResultEvent? { val treatEvent = CATreatAnvilResultEvent(event, useType, result, cost) try { unsafeTryTreatAnvilResult(treatEvent) - return treatEvent; + return treatEvent } catch (e: Exception) { logExceptionAndClear(event.view.player, event.inventory, e) return null diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/DisenchantmentDependency.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/DisenchantmentDependency.kt index 089a5fb..3dfba9c 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/DisenchantmentDependency.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/DisenchantmentDependency.kt @@ -16,6 +16,7 @@ import org.bukkit.inventory.AnvilInventory import org.bukkit.inventory.ItemStack import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener import xyz.alexcrea.cuanvil.util.AnvilXpUtil +import xyz.alexcrea.cuanvil.util.AnvilXpUtil.AnvilCost import xyz.alexcrea.cuanvil.util.MetricsUtil.trackError import java.util.logging.Level import kotlin.reflect.KClass @@ -58,14 +59,14 @@ class DisenchantmentDependency { DisenchantEvent.onEvent(event) if (event.result != null) { CustomAnvil.log("Detected pre anvil item extract bypass.") - AnvilXpUtil.setAnvilInvXp(event.inventory, event.view, player, event.inventory.repairCost) + AnvilXpUtil.setAnvilInvCost(event.inventory, event.view, player, AnvilCost(event.inventory.repairCost)) return true } ShatterEvent.onEvent(event) if (event.result != null) { CustomAnvil.log("Detected pre anvil split enchant bypass.") - AnvilXpUtil.setAnvilInvXp(event.inventory, event.view, player, event.inventory.repairCost) + AnvilXpUtil.setAnvilInvCost(event.inventory, event.view, player, AnvilCost(event.inventory.repairCost)) return true } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/HavenBagsDependency.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/HavenBagsDependency.kt index 6e7cf60..6b5f9c4 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/HavenBagsDependency.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/HavenBagsDependency.kt @@ -11,6 +11,7 @@ import valorless.havenbags.features.BagSkin import valorless.havenbags.features.BagUpgrade import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener import xyz.alexcrea.cuanvil.util.AnvilXpUtil +import xyz.alexcrea.cuanvil.util.AnvilXpUtil.AnvilCost class HavenBagsDependency { @@ -53,14 +54,14 @@ class HavenBagsDependency { bagSkin.onPrepareAnvil(event) if (event.result != null) { CustomAnvil.log("Detected pre anvil heaven bag anvil skin.") - AnvilXpUtil.setAnvilInvXp(event.inventory, event.view, player, event.inventory.repairCost) + AnvilXpUtil.setAnvilInvCost(event.inventory, event.view, player, AnvilCost(event.inventory.repairCost)) return true } bagUpgrade.onPrepareAnvil(event) if (event.result != null) { CustomAnvil.log("Detected pre anvil heaven bag anvil upgrade.") - AnvilXpUtil.setAnvilInvXp(event.inventory, event.view, player, event.inventory.repairCost) + AnvilXpUtil.setAnvilInvCost(event.inventory, event.view, player, AnvilCost(event.inventory.repairCost)) return true } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt index bc3afa4..1886589 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt @@ -25,6 +25,7 @@ import xyz.alexcrea.cuanvil.recipe.AnvilCustomRecipe import xyz.alexcrea.cuanvil.util.AnvilLoreEditUtil import xyz.alexcrea.cuanvil.util.AnvilUseType import xyz.alexcrea.cuanvil.util.AnvilXpUtil +import xyz.alexcrea.cuanvil.util.AnvilXpUtil.AnvilCost import xyz.alexcrea.cuanvil.util.CustomRecipeUtil import xyz.alexcrea.cuanvil.util.MiniMessageUtil import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair @@ -350,13 +351,13 @@ class AnvilResultListener : Listener { } private fun getFromLoreEditXpCost( - xpCost: AtomicInteger, + cost: AnvilCost, player: Player, inventory: AnvilInventory, ): Int { if (GameMode.CREATIVE == player.gameMode) return 0 - val repairCost = xpCost.get() + val repairCost = cost.sum() return if ((inventory.maximumRepairCost <= repairCost) || (player.level < repairCost) ) Int.MIN_VALUE @@ -376,9 +377,9 @@ class AnvilResultListener : Listener { val editType = AnvilLoreEditUtil.bookLoreEditIsAppend(leftItem, rightItem) ?: return false - val xpCost = AtomicInteger() + val cost = AnvilCost() if (editType) { - if (output != AnvilLoreEditUtil.handleLoreAppendByBook(player, leftItem, bookMeta, xpCost)) return false + if (output != AnvilLoreEditUtil.handleLoreAppendByBook(player, leftItem, bookMeta, cost)) return false // Remove pages to book val clearedBook: ItemStack? @@ -394,10 +395,10 @@ class AnvilResultListener : Listener { event, player, inventory, null, 0, clearedBook, 0, - output, getFromLoreEditXpCost(xpCost, player, inventory) + output, getFromLoreEditXpCost(cost, player, inventory) ) } else { - if (output != AnvilLoreEditUtil.handleLoreRemoveByBook(player, leftItem, xpCost)) return false + if (output != AnvilLoreEditUtil.handleLoreRemoveByBook(player, leftItem, cost)) return false // fill book meta val lore = DependencyManager.stripLore(leftItem) @@ -430,7 +431,7 @@ class AnvilResultListener : Listener { event, player, inventory, null, 0, rightCopy, 0, - output, getFromLoreEditXpCost(xpCost, player, inventory) + output, getFromLoreEditXpCost(cost, player, inventory) ) } } @@ -448,9 +449,9 @@ class AnvilResultListener : Listener { val editTypeIsAppend = AnvilLoreEditUtil.paperLoreEditIsAppend(leftItem, rightItem) ?: return false - val xpCost = AtomicInteger() + val cost = AnvilCost() if (editTypeIsAppend) { - if (output != AnvilLoreEditUtil.handleLoreAppendByPaper(player, leftItem, rightItem, xpCost)) return false + if (output != AnvilLoreEditUtil.handleLoreAppendByPaper(player, leftItem, rightItem, cost)) return false val paperCopy: ItemStack? if (LoreEditType.APPEND_PAPER.doConsume) { @@ -468,18 +469,18 @@ class AnvilResultListener : Listener { event, player, inventory, paperCopy, 0, rightItem, 1, - output, getFromLoreEditXpCost(xpCost, player, inventory) + output, getFromLoreEditXpCost(cost, player, inventory) ) } else { extractAnvilResult( event, player, inventory, null, 0, paperCopy, 0, - output, getFromLoreEditXpCost(xpCost, player, inventory) + output, getFromLoreEditXpCost(cost, player, inventory) ) } } else { - if (output != AnvilLoreEditUtil.handleLoreRemoveByPaper(player, leftItem, xpCost)) return false + if (output != AnvilLoreEditUtil.handleLoreRemoveByPaper(player, leftItem, cost)) return false val leftMeta = leftItem.itemMeta if (leftMeta == null || !leftMeta.hasLore()) return false @@ -512,14 +513,14 @@ class AnvilResultListener : Listener { event, player, inventory, rightClone, 0, rightItem, 1, - output, getFromLoreEditXpCost(xpCost, player, inventory) + output, getFromLoreEditXpCost(cost, player, inventory) ) } else { extractAnvilResult( event, player, inventory, null, 0, rightClone, 0, - output, getFromLoreEditXpCost(xpCost, player, inventory) + output, getFromLoreEditXpCost(cost, player, inventory) ) } } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt index 0525a85..bf8d3dd 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt @@ -26,10 +26,10 @@ import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.dialog.AnvilRenameDialog import xyz.alexcrea.cuanvil.enchant.CAEnchantment import xyz.alexcrea.cuanvil.util.* +import xyz.alexcrea.cuanvil.util.AnvilXpUtil.AnvilCost import xyz.alexcrea.cuanvil.util.MaterialUtil.isAir import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair import xyz.alexcrea.cuanvil.util.dialog.AnvilRenameDialogUtil -import java.util.concurrent.atomic.AtomicInteger /** * Listener for anvil events @@ -60,7 +60,7 @@ class PrepareAnvilListener : Listener { // Test if custom anvil is bypassed before immutability test if (DependencyManager.earlyTryEventPreAnvilBypass(event, player)) { // even if we got bypassed we still want to set price - AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, event.inventory.repairCost) + AnvilXpUtil.setAnvilInvCost(inventory, event.view, player, AnvilCost(event.inventory.repairCost)) return } @@ -90,7 +90,7 @@ class PrepareAnvilListener : Listener { // Test if the event should bypass custom anvil. if (DependencyManager.tryEventPreAnvilBypass(event, player)) { // even if we got bypassed we still want to set price - AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, event.inventory.repairCost) + AnvilXpUtil.setAnvilInvCost(inventory, event.view, player, AnvilCost(event.inventory.repairCost)) return } @@ -198,17 +198,17 @@ class PrepareAnvilListener : Listener { // Maybe add an option on custom craft to ignore/not ignore penalty ?? val xpCost = recipe.determineCost(amount, first, resultItem) - val levelCost = - if (recipe.removeExactLinearXp) AnvilXpUtil.calculateMinimumLevelForXp(xpCost) + val cost = AnvilCost() + cost.recipe = if (recipe.removeExactLinearXp) AnvilXpUtil.calculateMinimumLevelForXp(xpCost) else AnvilXpUtil.calculateLevelForXp(xpCost) - val finalResult = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.CUSTOM_CRAFT, levelCost) + val finalResult = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.CUSTOM_CRAFT, cost) if (finalResult == null) return false event.result = finalResult.result if (finalResult.result.isAir) return false - AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, finalResult.levelCost, true) + AnvilXpUtil.setAnvilInvCost(inventory, event.view, player, cost, true) return true } @@ -217,7 +217,8 @@ class PrepareAnvilListener : Listener { player: HumanEntity, first: ItemStack ) { val resultItem = DependencyManager.cloneItem(event, first) - var anvilCost = handleRename(resultItem, inventory, player) + val cost = AnvilCost() + cost.rename = handleRename(resultItem, inventory, player) // Test/stop if nothing changed. if (first == resultItem) { @@ -226,15 +227,15 @@ class PrepareAnvilListener : Listener { return } - anvilCost += AnvilXpUtil.calculatePenalty(first, null, resultItem, AnvilUseType.RENAME_ONLY) + cost.penalty = AnvilXpUtil.calculatePenalty(first, null, resultItem, AnvilUseType.RENAME_ONLY) - val finalResult = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.RENAME_ONLY, anvilCost) + val finalResult = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.RENAME_ONLY, cost) if (finalResult == null) return event.result = finalResult.result if (finalResult.result.isAir) return - AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, finalResult.levelCost) + AnvilXpUtil.setAnvilInvCost(inventory, event.view, player, cost) } private fun handleRename(resultItem: ItemStack, inventory: AnvilInventory, player: HumanEntity): Int { @@ -291,18 +292,18 @@ class PrepareAnvilListener : Listener { var hasChanged = !isIdentical(first.findEnchantments(), newEnchants) val resultItem = DependencyManager.cloneItem(event, first) - var anvilCost = 0 + val cost = AnvilCost() if(hasChanged){ resultItem.setEnchantmentsUnsafe(newEnchants) // Calculate enchantment cost - anvilCost+= AnvilXpUtil.getRightValues(second, resultItem) + cost.enchantment = AnvilXpUtil.getRightValues(second, resultItem) } // Calculate repair cost if (!first.isEnchantedBook() && !second.isEnchantedBook()) { // we only need to be concerned with repair when neither item is a book val repaired = resultItem.repairFrom(first, second) - anvilCost += if (repaired) ConfigOptions.itemRepairCost else 0 + cost.repair = if (repaired) ConfigOptions.itemRepairCost else 0 hasChanged = hasChanged || repaired } @@ -313,18 +314,18 @@ class PrepareAnvilListener : Listener { return } // As calculatePenalty edit result, we need to calculate penalty after checking equality - anvilCost += AnvilXpUtil.calculatePenalty(first, second, resultItem, AnvilUseType.MERGE) + cost.penalty = AnvilXpUtil.calculatePenalty(first, second, resultItem, AnvilUseType.MERGE) // Calculate rename cost - anvilCost += handleRename(resultItem, inventory, player) + cost.rename = handleRename(resultItem, inventory, player) // Finally, we set result - val finalResult = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.MERGE, anvilCost) + val finalResult = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.MERGE, cost) if (finalResult == null) return event.result = finalResult.result if (finalResult.result.isAir) return - AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, finalResult.levelCost) + AnvilXpUtil.setAnvilInvCost(inventory, event.view, player, cost) } private fun isIdentical( @@ -347,14 +348,15 @@ class PrepareAnvilListener : Listener { val unitRepairAmount = first.getRepair(second) ?: return false val resultItem = DependencyManager.cloneItem(event, first) - var anvilCost = handleRename(resultItem, inventory, player) + val cost = AnvilCost() + cost.rename = handleRename(resultItem, inventory, player) val repairAmount = resultItem.unitRepair(second.amount, unitRepairAmount) if (repairAmount > 0) { - anvilCost += repairAmount * ConfigOptions.unitRepairCost + cost.repair = repairAmount * ConfigOptions.unitRepairCost } // We do not care about right item penalty for unit repair - anvilCost += AnvilXpUtil.calculatePenalty(first, null, resultItem, AnvilUseType.UNIT_REPAIR) + cost.penalty = AnvilXpUtil.calculatePenalty(first, null, resultItem, AnvilUseType.UNIT_REPAIR) // Test/stop if nothing changed. if (first == resultItem) { @@ -363,13 +365,13 @@ class PrepareAnvilListener : Listener { return true } - val finalResult = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.UNIT_REPAIR, anvilCost) + val finalResult = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.UNIT_REPAIR, cost) if (finalResult == null) return false event.result = finalResult.result if (finalResult.result.isAir) return false - AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, finalResult.levelCost) + AnvilXpUtil.setAnvilInvCost(inventory, event.view, player, cost) return true } @@ -380,11 +382,11 @@ class PrepareAnvilListener : Listener { val type = second.type var result: ItemStack? = null - val xpCost = AtomicInteger() + val cost = AnvilCost() if (Material.WRITABLE_BOOK == type) { - result = AnvilLoreEditUtil.tryLoreEditByBook(player, first, second, xpCost) + result = AnvilLoreEditUtil.tryLoreEditByBook(player, first, second, cost) } else if (Material.PAPER == type) { - result = AnvilLoreEditUtil.tryLoreEditByPaper(player, first, second, xpCost) + result = AnvilLoreEditUtil.tryLoreEditByPaper(player, first, second, cost) } if (result.isAir || first == result) { @@ -394,7 +396,7 @@ class PrepareAnvilListener : Listener { } event.result = result - AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, xpCost.get()) + AnvilXpUtil.setAnvilInvCost(inventory, event.view, player, cost) return true } } \ 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 36f0efb..85a0ee8 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilLoreEditUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilLoreEditUtil.kt @@ -8,10 +8,10 @@ import org.bukkit.permissions.Permissible import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil.componentLore import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil.setComponentLore +import xyz.alexcrea.cuanvil.util.AnvilXpUtil.AnvilCost import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil import xyz.alexcrea.cuanvil.util.config.LoreEditType import java.util.* -import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicReference object AnvilLoreEditUtil { @@ -31,7 +31,7 @@ object AnvilLoreEditUtil { player: Permissible, first: ItemStack, book: BookMeta, - xpCost: AtomicInteger + cost: AnvilCost ): ItemStack? { if (!hasLoreEditByBookPermission(player)) return null @@ -53,14 +53,14 @@ object AnvilLoreEditUtil { if (result == first) return null // Handle xp - xpCost.addAndGet(colorCost) // Cost of using color - xpCost.addAndGet(outLines.size * LoreEditType.APPEND_BOOK.perLineCost) // per line cost - xpCost.addAndGet(baseEditLoreXpCost(first, result, LoreEditType.APPEND_BOOK)) // Fixed cost and work penalty + cost.lore = colorCost // Cost of using color + cost.lore += outLines.size * LoreEditType.APPEND_BOOK.perLineCost // per line cost + baseEditLoreXpCost(cost, first, result, LoreEditType.APPEND_BOOK) // Fixed cost and work penalty return result } - fun handleLoreRemoveByBook(player: Permissible, first: ItemStack, xpCost: AtomicInteger): ItemStack? { + fun handleLoreRemoveByBook(player: Permissible, first: ItemStack, cost: AnvilCost): ItemStack? { if (!hasLoreEditByBookPermission(player)) return null // remove lore @@ -78,9 +78,9 @@ object AnvilLoreEditUtil { 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)) + cost.lore = uncolorCost + cost.lore+= currentLore.size * LoreEditType.REMOVE_BOOK.perLineCost + baseEditLoreXpCost(cost, first, result, LoreEditType.REMOVE_BOOK) return result } @@ -116,12 +116,12 @@ object AnvilLoreEditUtil { return null } - fun tryLoreEditByBook(player: HumanEntity, first: ItemStack, second: ItemStack, xpCost: AtomicInteger): ItemStack? { + fun tryLoreEditByBook(player: HumanEntity, first: ItemStack, second: ItemStack, cost: AnvilCost): ItemStack? { val isAppend = bookLoreEditIsAppend(first, second) ?: return null val meta = second.itemMeta as BookMeta - return if (isAppend) handleLoreAppendByBook(player, first, meta, xpCost) - else handleLoreRemoveByBook(player, first, xpCost) + return if (isAppend) handleLoreAppendByBook(player, first, meta, cost) + else handleLoreRemoveByBook(player, first, cost) } // Return true if appended, false if removed, null if neither @@ -147,7 +147,7 @@ object AnvilLoreEditUtil { player: Permissible, first: ItemStack, second: ItemStack, - xpCost: AtomicInteger + cost: AnvilCost ): ItemStack? { if (!hasLoreEditByPaperPermission(player)) return null @@ -175,13 +175,13 @@ object AnvilLoreEditUtil { if (result == first) return null // Handle xp - xpCost.addAndGet(colorCost) - xpCost.addAndGet(baseEditLoreXpCost(first, result, LoreEditType.APPEND_PAPER)) + cost.lore = colorCost + baseEditLoreXpCost(cost, first, result, LoreEditType.APPEND_PAPER) return result } - fun handleLoreRemoveByPaper(player: Permissible, first: ItemStack, xpCost: AtomicInteger): ItemStack? { + fun handleLoreRemoveByPaper(player: Permissible, first: ItemStack, cost: AnvilCost): ItemStack? { if (!hasLoreEditByPaperPermission(player)) return null // remove lore line @@ -213,8 +213,8 @@ object AnvilLoreEditUtil { val uncolorCost = uncolorLine(player, line, LoreEditType.REMOVE_PAPER) // Handle other xp - xpCost.addAndGet(uncolorCost) - xpCost.addAndGet(baseEditLoreXpCost(first, result, LoreEditType.REMOVE_PAPER)) + cost.lore = uncolorCost + baseEditLoreXpCost(cost, first, result, LoreEditType.REMOVE_PAPER) return result } @@ -223,23 +223,23 @@ object AnvilLoreEditUtil { player: HumanEntity, first: ItemStack, second: ItemStack, - xpCost: AtomicInteger + cost: AnvilCost ): ItemStack? { val isAppend = paperLoreEditIsAppend(first, second) ?: return null - return if (isAppend) handleLoreAppendByPaper(player, first, second, xpCost) - else handleLoreRemoveByPaper(player, first, xpCost) + return if (isAppend) handleLoreAppendByPaper(player, first, second, cost) + else handleLoreRemoveByPaper(player, first, cost) } private fun baseEditLoreXpCost( + cost: AnvilCost, first: ItemStack, result: ItemStack, editType: LoreEditType - ): Int { - var xpCost = editType.fixedCost + ) { + cost.lore+= editType.fixedCost - xpCost += AnvilXpUtil.calculatePenalty(first, null, result, editType.useType) - return xpCost + cost.penalty = AnvilXpUtil.calculatePenalty(first, null, result, editType.useType) } fun colorPermission(player: Permissible, editType: LoreEditType): AnvilColorUtil.ColorPermissions { diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt index f60581f..5acc9b8 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt @@ -23,16 +23,55 @@ object AnvilXpUtil { const val EXCLUSIVE_PENALTY_PREFIX = "repair_cost" + class AnvilCost { + private val isAlone: Boolean + + var generic = 0 + var enchantment = 0 + var repair = 0 + var rename = 0 + var lore = 0 + var penalty = 0 + var recipe = 0 + + fun sum(): Int { + return generic + enchantment + repair + rename + lore + penalty + recipe + } + + constructor(generic: Int) { + this.generic = generic + isAlone = true + } + constructor() { + isAlone = false + } + } + + /** + * Display the required cost (either as xp or as ) + */ + fun setAnvilInvCost( + inventory: AnvilInventory, + view: InventoryView, + player: HumanEntity, + cost: AnvilCost, + ignoreRules: Boolean = false + ) { + // TODO check require money or xp cost & display appropriately + setAnvilInvXp(inventory, view, player, cost.sum(), ignoreRules) + } + /** * Display xp needed for the work on the anvil inventory */ - fun setAnvilInvXp( + private fun setAnvilInvXp( inventory: AnvilInventory, view: InventoryView, player: HumanEntity, anvilCost: Int, ignoreRules: Boolean = false ) { + // Test repair cost limit val finalAnvilCost = if ( !ignoreRules && @@ -78,7 +117,6 @@ object AnvilXpUtil { } player.updateInventory() - } } From 2c3e43cb846bd4fa60c2714c62352671f0deb24e Mon Sep 17 00:00:00 2001 From: alexcrea Date: Sun, 24 May 2026 11:55:37 +0200 Subject: [PATCH 03/24] moved to vault unlocked --- build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 4effecb..36cc2d6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -47,7 +47,7 @@ repositories { } // For vault unlocked - maven { url = uri("https://jitpack.io") } + maven { url = uri("https://repo.codemc.io/repository/creatorfromhell/") } } val reobfNMS = providers.gradleProperty("subprojects.reobfnms") @@ -107,7 +107,7 @@ dependencies { compileOnly("dev.lone:api-itemsadder:4.0.10") // Vault api - compileOnly("com.github.MilkBowl:VaultAPI:1.7") + compileOnly("net.milkbowl.vault:VaultUnlockedAPI:2.16") // Include nms implementation(project(":nms:nms-common")) From 856c1e08bd3199ba31a663871f0cee6314a8b82e Mon Sep 17 00:00:00 2001 From: alexcrea Date: Sun, 24 May 2026 20:47:26 +0200 Subject: [PATCH 04/24] add monetary config and generic progress --- defaultconfigs/1.18/config.yml | 19 ++++++++++ defaultconfigs/1.21.11/config.yml | 19 ++++++++++ defaultconfigs/1.21.9/config.yml | 19 ++++++++++ defaultconfigs/1.21/config.yml | 19 ++++++++++ src/main/kotlin/io/delilaheve/CustomAnvil.kt | 2 +- .../io/delilaheve/util/ConfigOptions.kt | 33 ++++++++++++++++ .../cuanvil/dependency/DependencyManager.kt | 4 +- .../dependency/econmy/EconomyManager.kt | 31 --------------- .../dependency/economy/EconomyManager.kt | 27 +++++++++++++ .../economy/UnlockedEconomyManager.kt | 35 +++++++++++++++++ .../dependency/economy/VaultEconomyManager.kt | 19 ++++++++++ .../cuanvil/listener/PrepareAnvilListener.kt | 38 +++++-------------- .../cuanvil/util/AnvilLoreEditUtil.kt | 2 +- .../xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt | 19 ++++------ src/main/resources/config.yml | 19 ++++++++++ 15 files changed, 230 insertions(+), 75 deletions(-) delete mode 100644 src/main/kotlin/xyz/alexcrea/cuanvil/dependency/econmy/EconomyManager.kt create mode 100644 src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/EconomyManager.kt create mode 100644 src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/UnlockedEconomyManager.kt create mode 100644 src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/VaultEconomyManager.kt diff --git a/defaultconfigs/1.18/config.yml b/defaultconfigs/1.18/config.yml index 12322e9..3edea26 100644 --- a/defaultconfigs/1.18/config.yml +++ b/defaultconfigs/1.18/config.yml @@ -430,6 +430,25 @@ lore_edit: allow_hexadecimal_color: false allow_minimessage: true +# Allow to replace the xp cost by a monetary cost +# If enabled it will not be bound to the experience level limits +monetary_cost: + enabled: false + # If using vault unlocked this allow to specify what currency should be used for anvil usage + # default being the default currency + currency: default + # multiply the anvil cost by a value to allow to have price a big bigger than like 40 + multipliers: + # global multipliers. all usage type will be multiplied by this value + global: 1.0 + # usage specific type. it will only apply for specific xp "reason" + enchantment: 1.0 # related to enchantments level + repair: 1.0 # for repairing via unit repair (per unit) + rename: 1.0 # for renaming the item + lore_edit: 1.0 # for changing the lore of the item (only if lore edit is enabled) + work_penalty: 1.0 # for work penalty (aka use penalty) + recipe: 1.0 # for custom anvil recipe cost + # Whether to show debug logging debug_log: false diff --git a/defaultconfigs/1.21.11/config.yml b/defaultconfigs/1.21.11/config.yml index 39c8504..53ffabd 100644 --- a/defaultconfigs/1.21.11/config.yml +++ b/defaultconfigs/1.21.11/config.yml @@ -450,6 +450,25 @@ lore_edit: allow_hexadecimal_color: false allow_minimessage: true +# Allow to replace the xp cost by a monetary cost +# If enabled it will not be bound to the experience level limits +monetary_cost: + enabled: false + # If using vault unlocked this allow to specify what currency should be used for anvil usage + # default being the default currency + currency: default + # multiply the anvil cost by a value to allow to have price a big bigger than like 40 + multipliers: + # global multipliers. all usage type will be multiplied by this value + global: 1.0 + # usage specific type. it will only apply for specific xp "reason" + enchantment: 1.0 # related to enchantments level + repair: 1.0 # for repairing via unit repair (per unit) + rename: 1.0 # for renaming the item + lore_edit: 1.0 # for changing the lore of the item (only if lore edit is enabled) + work_penalty: 1.0 # for work penalty (aka use penalty) + recipe: 1.0 # for custom anvil recipe cost + # Whether to show debug logging debug_log: false diff --git a/defaultconfigs/1.21.9/config.yml b/defaultconfigs/1.21.9/config.yml index 84eccba..40e558d 100644 --- a/defaultconfigs/1.21.9/config.yml +++ b/defaultconfigs/1.21.9/config.yml @@ -442,6 +442,25 @@ lore_edit: allow_hexadecimal_color: false allow_minimessage: true +# Allow to replace the xp cost by a monetary cost +# If enabled it will not be bound to the experience level limits +monetary_cost: + enabled: false + # If using vault unlocked this allow to specify what currency should be used for anvil usage + # default being the default currency + currency: default + # multiply the anvil cost by a value to allow to have price a big bigger than like 40 + multipliers: + # global multipliers. all usage type will be multiplied by this value + global: 1.0 + # usage specific type. it will only apply for specific xp "reason" + enchantment: 1.0 # related to enchantments level + repair: 1.0 # for repairing via unit repair (per unit) + rename: 1.0 # for renaming the item + lore_edit: 1.0 # for changing the lore of the item (only if lore edit is enabled) + work_penalty: 1.0 # for work penalty (aka use penalty) + recipe: 1.0 # for custom anvil recipe cost + # Whether to show debug logging debug_log: false diff --git a/defaultconfigs/1.21/config.yml b/defaultconfigs/1.21/config.yml index df62e6a..87372a5 100644 --- a/defaultconfigs/1.21/config.yml +++ b/defaultconfigs/1.21/config.yml @@ -430,6 +430,25 @@ lore_edit: allow_hexadecimal_color: false allow_minimessage: true +# Allow to replace the xp cost by a monetary cost +# If enabled it will not be bound to the experience level limits +monetary_cost: + enabled: false + # If using vault unlocked this allow to specify what currency should be used for anvil usage + # default being the default currency + currency: default + # multiply the anvil cost by a value to allow to have price a big bigger than like 40 + multipliers: + # global multipliers. all usage type will be multiplied by this value + global: 1.0 + # usage specific type. it will only apply for specific xp "reason" + enchantment: 1.0 # related to enchantments level + repair: 1.0 # for repairing via unit repair (per unit) + rename: 1.0 # for renaming the item + lore_edit: 1.0 # for changing the lore of the item (only if lore edit is enabled) + work_penalty: 1.0 # for work penalty (aka use penalty) + recipe: 1.0 # for custom anvil recipe cost + # Whether to show debug logging debug_log: false diff --git a/src/main/kotlin/io/delilaheve/CustomAnvil.kt b/src/main/kotlin/io/delilaheve/CustomAnvil.kt index 9548450..7f05fc6 100644 --- a/src/main/kotlin/io/delilaheve/CustomAnvil.kt +++ b/src/main/kotlin/io/delilaheve/CustomAnvil.kt @@ -12,7 +12,7 @@ import xyz.alexcrea.cuanvil.command.ReloadExecutor import xyz.alexcrea.cuanvil.config.ConfigHolder import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.dependency.MinecraftVersionUtil -import xyz.alexcrea.cuanvil.dependency.econmy.EconomyManager +import xyz.alexcrea.cuanvil.dependency.economy.EconomyManager import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil import xyz.alexcrea.cuanvil.enchant.CAEnchantmentRegistry import xyz.alexcrea.cuanvil.gui.config.MainConfigGui diff --git a/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt b/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt index 46f79bc..657fa38 100644 --- a/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt +++ b/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt @@ -72,6 +72,11 @@ object ConfigOptions { const val IMMUTABLE_ENCHANTMENT_LIST = "immutable_enchantments" + // Monetary configs + const val MONETARY_USAGE_ROOT = "monetary_cost" + const val SHOULD_USE_MONEY = "$MONETARY_USAGE_ROOT.enabled" + const val MONEY_CURRENCY = "$MONETARY_USAGE_ROOT.currency" + const val MONETARY_MULTIPLIER_ROOT = "$MONETARY_USAGE_ROOT.multipliers" // Keys for specific enchantment values private const val KEY_BOOK = "book" @@ -110,6 +115,11 @@ object ConfigOptions { const val DEFAULT_PER_COLOR_CODE_PERMISSION = false + // Monetary configs + const val DEFAULT_SHOULD_USE_MONEY = false + const val DEFAULT_MONEY_CURRENCY = "default" + const val DEFAULT_MONEY_MULTIPLIER = 1.0 + // Debug flag private const val DEFAULT_DEBUG_LOG = false private const val DEFAULT_VERBOSE_DEBUG_LOG = false @@ -625,4 +635,27 @@ object ConfigOptions { return false } + /* + * Monetary configs + */ + val shouldUseMoney: Boolean + get() { + return ConfigHolder.DEFAULT_CONFIG + .config + .getBoolean(SHOULD_USE_MONEY, DEFAULT_SHOULD_USE_MONEY) + } + + val usedCurrency: String + get() { + return ConfigHolder.DEFAULT_CONFIG + .config + .getString(MONEY_CURRENCY, DEFAULT_MONEY_CURRENCY)!! + } + + fun getMonetaryMultiplier(type: String): Double { + return ConfigHolder.DEFAULT_CONFIG + .config + .getDouble("$MONETARY_MULTIPLIER_ROOT.$type", DEFAULT_MONEY_MULTIPLIER) + } + } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt index 4e59adc..69b0237 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt @@ -237,11 +237,11 @@ object DependencyManager { result: ItemStack, useType: AnvilUseType, cost: AnvilXpUtil.AnvilCost - ): CATreatAnvilResultEvent? { + ): ItemStack? { val treatEvent = CATreatAnvilResultEvent(event, useType, result, cost) try { unsafeTryTreatAnvilResult(treatEvent) - return treatEvent + return treatEvent.result } catch (e: Exception) { logExceptionAndClear(event.view.player, event.inventory, e) return null diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/econmy/EconomyManager.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/econmy/EconomyManager.kt deleted file mode 100644 index d3b8c21..0000000 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/econmy/EconomyManager.kt +++ /dev/null @@ -1,31 +0,0 @@ -package xyz.alexcrea.cuanvil.dependency.econmy - -import net.milkbowl.vault.economy.Economy -import org.bukkit.OfflinePlayer -import org.bukkit.plugin.Plugin -import org.bukkit.plugin.RegisteredServiceProvider - -object EconomyManager { - - private var economy: Economy? = null - - fun setupEconomy(plugin: Plugin) { - if (economy != null) return - - if (plugin.server.pluginManager.getPlugin("Vault") == null) - return - - val rsp: RegisteredServiceProvider? = - plugin.server.servicesManager.getRegistration(Economy::class.java) - if (rsp == null) return - - economy = rsp.getProvider() - } - - fun has(player: OfflinePlayer, amount: Double): Boolean { - return economy?.has(player, amount) == true - } - - - -} diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/EconomyManager.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/EconomyManager.kt new file mode 100644 index 0000000..0777162 --- /dev/null +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/EconomyManager.kt @@ -0,0 +1,27 @@ +package xyz.alexcrea.cuanvil.dependency.economy + +import org.bukkit.plugin.Plugin + +interface EconomyManager { + + companion object { + var economy: EconomyManager? = null + + fun setupEconomy(plugin: Plugin) { + if (plugin.server.pluginManager.getPlugin("Vault") == null) + return + if(UnlockedEconomyManager.unlockedAvailable()) + economy = UnlockedEconomyManager(plugin) + + if(economy == null || !economy!!.initialized()) + economy = VaultEconomyManager(plugin) + } + + } + + fun initialized(): Boolean + + + + +} diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/UnlockedEconomyManager.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/UnlockedEconomyManager.kt new file mode 100644 index 0000000..303c559 --- /dev/null +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/UnlockedEconomyManager.kt @@ -0,0 +1,35 @@ +package xyz.alexcrea.cuanvil.dependency.economy + +import net.milkbowl.vault2.economy.Economy +import org.bukkit.plugin.Plugin + +class UnlockedEconomyManager: EconomyManager { + + val plugin: String + val economy: Economy? + + companion object { + fun unlockedAvailable(): Boolean { + try { + Class.forName("net.milkbowl.vault2.economy.Economy") + return true + } catch (_: ClassNotFoundException) { + return false + } + } + } + + constructor(plugin: Plugin) { + this.plugin = plugin.name + + val rsp = plugin.server.servicesManager.getRegistration(Economy::class.java) + economy = rsp?.getProvider() + } + + override fun initialized(): Boolean { + return economy != null + } + + + +} \ No newline at end of file diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/VaultEconomyManager.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/VaultEconomyManager.kt new file mode 100644 index 0000000..50837bf --- /dev/null +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/VaultEconomyManager.kt @@ -0,0 +1,19 @@ +package xyz.alexcrea.cuanvil.dependency.economy + +import net.milkbowl.vault.economy.Economy +import org.bukkit.plugin.Plugin +class VaultEconomyManager : EconomyManager { + + val economy: Economy? + + constructor(plugin: Plugin) { + val rsp = plugin.server.servicesManager.getRegistration(Economy::class.java) + economy = rsp?.getProvider() + } + + override fun initialized(): Boolean { + return economy != null + } + + +} \ No newline at end of file diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt index bf8d3dd..00623d5 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt @@ -202,12 +202,7 @@ class PrepareAnvilListener : Listener { cost.recipe = if (recipe.removeExactLinearXp) AnvilXpUtil.calculateMinimumLevelForXp(xpCost) else AnvilXpUtil.calculateLevelForXp(xpCost) - val finalResult = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.CUSTOM_CRAFT, cost) - if (finalResult == null) return false - - event.result = finalResult.result - if (finalResult.result.isAir) return false - + event.result = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.CUSTOM_CRAFT, cost) AnvilXpUtil.setAnvilInvCost(inventory, event.view, player, cost, true) return true } @@ -227,14 +222,9 @@ class PrepareAnvilListener : Listener { return } - cost.penalty = AnvilXpUtil.calculatePenalty(first, null, resultItem, AnvilUseType.RENAME_ONLY) - - val finalResult = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.RENAME_ONLY, cost) - if (finalResult == null) return - - event.result = finalResult.result - if (finalResult.result.isAir) return + cost.workPenalty = AnvilXpUtil.calculatePenalty(first, null, resultItem, AnvilUseType.RENAME_ONLY) + event.result = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.RENAME_ONLY, cost) AnvilXpUtil.setAnvilInvCost(inventory, event.view, player, cost) } @@ -296,7 +286,7 @@ class PrepareAnvilListener : Listener { if(hasChanged){ resultItem.setEnchantmentsUnsafe(newEnchants) // Calculate enchantment cost - cost.enchantment = AnvilXpUtil.getRightValues(second, resultItem) + AnvilXpUtil.getRightValues(second, resultItem, cost) } // Calculate repair cost @@ -309,22 +299,17 @@ class PrepareAnvilListener : Listener { // Test/stop if nothing changed. if (!hasChanged) { - CustomAnvil.log("Mergable with second, But input is same as output") + CustomAnvil.log("Mergeable with second, But input is same as output") event.result = null return } // As calculatePenalty edit result, we need to calculate penalty after checking equality - cost.penalty = AnvilXpUtil.calculatePenalty(first, second, resultItem, AnvilUseType.MERGE) + cost.workPenalty = AnvilXpUtil.calculatePenalty(first, second, resultItem, AnvilUseType.MERGE) // Calculate rename cost cost.rename = handleRename(resultItem, inventory, player) // Finally, we set result - val finalResult = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.MERGE, cost) - if (finalResult == null) return - - event.result = finalResult.result - if (finalResult.result.isAir) return - + event.result = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.MERGE, cost) AnvilXpUtil.setAnvilInvCost(inventory, event.view, player, cost) } @@ -356,7 +341,7 @@ class PrepareAnvilListener : Listener { cost.repair = repairAmount * ConfigOptions.unitRepairCost } // We do not care about right item penalty for unit repair - cost.penalty = AnvilXpUtil.calculatePenalty(first, null, resultItem, AnvilUseType.UNIT_REPAIR) + cost.workPenalty = AnvilXpUtil.calculatePenalty(first, null, resultItem, AnvilUseType.UNIT_REPAIR) // Test/stop if nothing changed. if (first == resultItem) { @@ -365,12 +350,7 @@ class PrepareAnvilListener : Listener { return true } - val finalResult = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.UNIT_REPAIR, cost) - if (finalResult == null) return false - - event.result = finalResult.result - if (finalResult.result.isAir) return false - + event.result = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.UNIT_REPAIR, cost) AnvilXpUtil.setAnvilInvCost(inventory, event.view, player, cost) return true } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilLoreEditUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilLoreEditUtil.kt index 85a0ee8..16eca40 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilLoreEditUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilLoreEditUtil.kt @@ -239,7 +239,7 @@ object AnvilLoreEditUtil { ) { cost.lore+= editType.fixedCost - cost.penalty = AnvilXpUtil.calculatePenalty(first, null, result, editType.useType) + cost.workPenalty = AnvilXpUtil.calculatePenalty(first, null, result, editType.useType) } fun colorPermission(player: Permissible, editType: LoreEditType): AnvilColorUtil.ColorPermissions { diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt index 5acc9b8..6565166 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt @@ -31,11 +31,12 @@ object AnvilXpUtil { var repair = 0 var rename = 0 var lore = 0 - var penalty = 0 + var illegalPenalty = 0 + var workPenalty = 0 var recipe = 0 fun sum(): Int { - return generic + enchantment + repair + rename + lore + penalty + recipe + return generic + enchantment + repair + rename + lore + illegalPenalty + workPenalty + recipe } constructor(generic: Int) { @@ -197,10 +198,8 @@ object AnvilXpUtil { * Function to calculate right enchantment values * it include enchantment placed on final item and conflicting enchantment */ - fun getRightValues(right: ItemStack, result: ItemStack): Int { + fun getRightValues(right: ItemStack, result: ItemStack, cost: AnvilCost) { // Calculate right value and illegal enchant penalty - var illegalPenalty = 0 - var rightValue = 0 val rightIsFormBook = right.isEnchantedBook() val resultEnchs = result.findEnchantments() @@ -218,7 +217,7 @@ object AnvilXpUtil { resultEnchsKeys.remove(enchantment.key) if (ConflictType.ENCHANTMENT_CONFLICT == conflictType) { - illegalPenalty += ConfigOptions.sacrificeIllegalCost + cost.illegalPenalty += ConfigOptions.sacrificeIllegalCost CustomAnvil.verboseLog("Big conflict. Adding illegal price penalty") } continue @@ -229,16 +228,14 @@ object AnvilXpUtil { val enchantmentMultiplier = ConfigOptions.enchantmentValue(enchantment.key, rightIsFormBook) val value = resultLevel * enchantmentMultiplier CustomAnvil.log("Value for ${enchantment.key.enchantmentName} level ${enchantment.value} is $value ($resultLevel * $enchantmentMultiplier)") - rightValue += value + cost.enchantment += value } CustomAnvil.log( "Calculated right values: " + - "rightValue: $rightValue, " + - "illegalPenalty: $illegalPenalty" + "rightValue: ${cost.enchantment}, " + + "illegalPenalty: ${cost.illegalPenalty}" ) - - return rightValue + illegalPenalty } /** diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 49b5e5a..f5e2c5a 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -432,6 +432,25 @@ lore_edit: allow_hexadecimal_color: false allow_minimessage: true +# Allow to replace the xp cost by a monetary cost +# If enabled it will not be bound to the experience level limits +monetary_cost: + enabled: false + # If using vault unlocked this allow to specify what currency should be used for anvil usage + # default being the default currency + currency: default + # multiply the anvil cost by a value to allow to have price a big bigger than like 40 + multipliers: + # global multipliers. all usage type will be multiplied by this value + global: 1.0 + # usage specific type. it will only apply for specific xp "reason" + enchantment: 1.0 # related to enchantments level + repair: 1.0 # for repairing via unit repair (per unit) + rename: 1.0 # for renaming the item + lore_edit: 1.0 # for changing the lore of the item (only if lore edit is enabled) + work_penalty: 1.0 # for work penalty (aka use penalty) + recipe: 1.0 # for custom anvil recipe cost + # Whether to show debug logging debug_log: false From 1660250ee1e40f3bdbc33470611ff15571f5ccb4 Mon Sep 17 00:00:00 2001 From: alexcrea Date: Sun, 24 May 2026 21:27:17 +0200 Subject: [PATCH 05/24] monetary dependency functions --- .../io/delilaheve/util/ConfigOptions.kt | 8 +++-- .../dependency/economy/EconomyManager.kt | 7 +++- .../economy/UnlockedEconomyManager.kt | 35 +++++++++++++++++++ .../dependency/economy/VaultEconomyManager.kt | 18 ++++++++++ 4 files changed, 64 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt b/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt index 657fa38..487bb87 100644 --- a/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt +++ b/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt @@ -7,6 +7,7 @@ import xyz.alexcrea.cuanvil.config.ConfigHolder import xyz.alexcrea.cuanvil.config.WorkPenaltyType import xyz.alexcrea.cuanvil.config.WorkPenaltyType.WorkPenaltyPart import xyz.alexcrea.cuanvil.dependency.DependencyManager +import xyz.alexcrea.cuanvil.dependency.economy.EconomyManager import xyz.alexcrea.cuanvil.enchant.CAEnchantment import xyz.alexcrea.cuanvil.util.AnvilUseType import java.util.* @@ -640,9 +641,10 @@ object ConfigOptions { */ val shouldUseMoney: Boolean get() { - return ConfigHolder.DEFAULT_CONFIG - .config - .getBoolean(SHOULD_USE_MONEY, DEFAULT_SHOULD_USE_MONEY) + return EconomyManager.economy?.initialized() == true && + ConfigHolder.DEFAULT_CONFIG + .config + .getBoolean(SHOULD_USE_MONEY, DEFAULT_SHOULD_USE_MONEY) } val usedCurrency: String diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/EconomyManager.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/EconomyManager.kt index 0777162..dc1038a 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/EconomyManager.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/EconomyManager.kt @@ -1,6 +1,8 @@ package xyz.alexcrea.cuanvil.dependency.economy +import org.bukkit.entity.Player import org.bukkit.plugin.Plugin +import java.math.BigDecimal interface EconomyManager { @@ -21,7 +23,10 @@ interface EconomyManager { fun initialized(): Boolean + // We assume "initialized" got checked before these function get called + fun has(player: Player, money: BigDecimal): Boolean + fun remove(player: Player, money: BigDecimal): Boolean - + fun format(money: BigDecimal): String; } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/UnlockedEconomyManager.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/UnlockedEconomyManager.kt index 303c559..9bea81c 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/UnlockedEconomyManager.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/UnlockedEconomyManager.kt @@ -1,7 +1,10 @@ package xyz.alexcrea.cuanvil.dependency.economy +import io.delilaheve.util.ConfigOptions import net.milkbowl.vault2.economy.Economy +import org.bukkit.entity.Player import org.bukkit.plugin.Plugin +import java.math.BigDecimal class UnlockedEconomyManager: EconomyManager { @@ -30,6 +33,38 @@ class UnlockedEconomyManager: EconomyManager { return economy != null } + private fun currency(): String { + val configured = ConfigOptions.usedCurrency + + return if ("default".equals(configured, true)) + economy!!.getDefaultCurrency(plugin) + else configured + } + + override fun has(player: Player, money: BigDecimal): Boolean { + if(money.signum() <= 0) return true + + return economy!!.has(plugin, + player.uniqueId, + player.world.name, + currency(), + money) + } + + override fun remove(player: Player, money: BigDecimal): Boolean { + if(money.signum() <= 0) return true + + return economy!!.withdraw(plugin, + player.uniqueId, + player.world.name, + currency(), + money) + .transactionSuccess() + } + + override fun format(money: BigDecimal): String { + return economy!!.format(plugin, money, currency()) + } } \ No newline at end of file diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/VaultEconomyManager.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/VaultEconomyManager.kt index 50837bf..79a8036 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/VaultEconomyManager.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/VaultEconomyManager.kt @@ -1,7 +1,10 @@ package xyz.alexcrea.cuanvil.dependency.economy import net.milkbowl.vault.economy.Economy +import org.bukkit.entity.Player import org.bukkit.plugin.Plugin +import java.math.BigDecimal + class VaultEconomyManager : EconomyManager { val economy: Economy? @@ -15,5 +18,20 @@ class VaultEconomyManager : EconomyManager { return economy != null } + override fun has(player: Player, money: BigDecimal): Boolean { + if(money.signum() <= 0) return true + + return economy!!.has(player, money.toDouble()) + } + + override fun remove(player: Player, money: BigDecimal): Boolean { + if(money.signum() <= 0) return true + + return economy!!.withdrawPlayer(player, money.toDouble()).transactionSuccess() + } + + override fun format(money: BigDecimal): String { + return economy!!.format(money.toDouble()) + } } \ No newline at end of file From ac9f4921258b8a952be913b7066b71bb79c550e1 Mon Sep 17 00:00:00 2001 From: alexcrea Date: Mon, 25 May 2026 17:50:02 +0200 Subject: [PATCH 06/24] monetary minimum version & rename impl --- defaultconfigs/1.18/config.yml | 2 ++ defaultconfigs/1.21.11/config.yml | 2 ++ defaultconfigs/1.21.9/config.yml | 2 ++ defaultconfigs/1.21/config.yml | 2 ++ .../xyz/alexcrea/cuanvil/util/RenameAnvilUtil.kt | 12 ++++++++++++ .../kotlin/io/delilaheve/util/ConfigOptions.kt | 15 +++++++++++---- src/main/resources/config.yml | 3 +++ 7 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 nms/v1_21R1/src/main/kotlin/xyz/alexcrea/cuanvil/util/RenameAnvilUtil.kt diff --git a/defaultconfigs/1.18/config.yml b/defaultconfigs/1.18/config.yml index 3edea26..1a1663a 100644 --- a/defaultconfigs/1.18/config.yml +++ b/defaultconfigs/1.18/config.yml @@ -432,6 +432,8 @@ lore_edit: # Allow to replace the xp cost by a monetary cost # If enabled it will not be bound to the experience level limits +# +# This feature can only be enabled starting with 1.21 monetary_cost: enabled: false # If using vault unlocked this allow to specify what currency should be used for anvil usage diff --git a/defaultconfigs/1.21.11/config.yml b/defaultconfigs/1.21.11/config.yml index 53ffabd..2daa373 100644 --- a/defaultconfigs/1.21.11/config.yml +++ b/defaultconfigs/1.21.11/config.yml @@ -452,6 +452,8 @@ lore_edit: # Allow to replace the xp cost by a monetary cost # If enabled it will not be bound to the experience level limits +# +# This feature can only be enabled starting with 1.21 monetary_cost: enabled: false # If using vault unlocked this allow to specify what currency should be used for anvil usage diff --git a/defaultconfigs/1.21.9/config.yml b/defaultconfigs/1.21.9/config.yml index 40e558d..8532881 100644 --- a/defaultconfigs/1.21.9/config.yml +++ b/defaultconfigs/1.21.9/config.yml @@ -444,6 +444,8 @@ lore_edit: # Allow to replace the xp cost by a monetary cost # If enabled it will not be bound to the experience level limits +# +# This feature can only be enabled starting with 1.21 monetary_cost: enabled: false # If using vault unlocked this allow to specify what currency should be used for anvil usage diff --git a/defaultconfigs/1.21/config.yml b/defaultconfigs/1.21/config.yml index 87372a5..25e47dc 100644 --- a/defaultconfigs/1.21/config.yml +++ b/defaultconfigs/1.21/config.yml @@ -432,6 +432,8 @@ lore_edit: # Allow to replace the xp cost by a monetary cost # If enabled it will not be bound to the experience level limits +# +# This feature can only be enabled starting with 1.21 monetary_cost: enabled: false # If using vault unlocked this allow to specify what currency should be used for anvil usage diff --git a/nms/v1_21R1/src/main/kotlin/xyz/alexcrea/cuanvil/util/RenameAnvilUtil.kt b/nms/v1_21R1/src/main/kotlin/xyz/alexcrea/cuanvil/util/RenameAnvilUtil.kt new file mode 100644 index 0000000..279016b --- /dev/null +++ b/nms/v1_21R1/src/main/kotlin/xyz/alexcrea/cuanvil/util/RenameAnvilUtil.kt @@ -0,0 +1,12 @@ +package xyz.alexcrea.cuanvil.util + +import org.bukkit.inventory.InventoryView + +// TODO yet another cleanup to do on legacy removal branch +object RenameAnvilUtil { + + fun rename(view: InventoryView, name: String) { + view.title = name + } + +} \ No newline at end of file diff --git a/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt b/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt index 487bb87..ddaa348 100644 --- a/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt +++ b/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt @@ -9,7 +9,10 @@ import xyz.alexcrea.cuanvil.config.WorkPenaltyType.WorkPenaltyPart import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.dependency.economy.EconomyManager import xyz.alexcrea.cuanvil.enchant.CAEnchantment +import xyz.alexcrea.cuanvil.update.UpdateUtils +import xyz.alexcrea.cuanvil.update.Version import xyz.alexcrea.cuanvil.util.AnvilUseType +import java.math.BigDecimal import java.util.* /** @@ -87,6 +90,9 @@ object ConfigOptions { const val DEBUG_LOGGING = "debug_log" const val VERBOSE_DEBUG_LOGGING = "debug_log_verbose" + // Minimum versions + val MINIMUM_MONETARY_COST_VER = Version(21, 0, 0) + // ---------------------- // Default config values // ---------------------- @@ -637,11 +643,12 @@ object ConfigOptions { } /* - * Monetary configs + * Monetary configs (only for 1.21+) */ val shouldUseMoney: Boolean get() { return EconomyManager.economy?.initialized() == true && + UpdateUtils.currentMinecraftVersion().greaterEqual(MINIMUM_MONETARY_COST_VER) && ConfigHolder.DEFAULT_CONFIG .config .getBoolean(SHOULD_USE_MONEY, DEFAULT_SHOULD_USE_MONEY) @@ -654,10 +661,10 @@ object ConfigOptions { .getString(MONEY_CURRENCY, DEFAULT_MONEY_CURRENCY)!! } - fun getMonetaryMultiplier(type: String): Double { - return ConfigHolder.DEFAULT_CONFIG + fun getMonetaryMultiplier(type: String): BigDecimal { + return BigDecimal(ConfigHolder.DEFAULT_CONFIG .config - .getDouble("$MONETARY_MULTIPLIER_ROOT.$type", DEFAULT_MONEY_MULTIPLIER) + .getDouble("$MONETARY_MULTIPLIER_ROOT.$type", DEFAULT_MONEY_MULTIPLIER)) } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index f5e2c5a..5ba61ec 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -434,6 +434,8 @@ lore_edit: # Allow to replace the xp cost by a monetary cost # If enabled it will not be bound to the experience level limits +# +# This feature can only be enabled starting with 1.21 monetary_cost: enabled: false # If using vault unlocked this allow to specify what currency should be used for anvil usage @@ -448,6 +450,7 @@ monetary_cost: repair: 1.0 # for repairing via unit repair (per unit) rename: 1.0 # for renaming the item lore_edit: 1.0 # for changing the lore of the item (only if lore edit is enabled) + illegal_penalty: 1.0 # for trying to combine illegal enchantment work_penalty: 1.0 # for work penalty (aka use penalty) recipe: 1.0 # for custom anvil recipe cost From 1b3447d0416fff9994fff3521da88ebbefc36087 Mon Sep 17 00:00:00 2001 From: alexcrea Date: Mon, 25 May 2026 18:05:29 +0200 Subject: [PATCH 07/24] monetary cost display --- .../{RenameAnvilUtil.kt => AnvilTitleUtil.kt} | 2 +- .../io/delilaheve/util/ConfigOptions.kt | 2 +- .../cuanvil/dependency/DependencyManager.kt | 5 +- .../dependency/economy/EconomyManager.kt | 4 +- .../economy/UnlockedEconomyManager.kt | 18 +++-- .../dependency/economy/VaultEconomyManager.kt | 4 +- .../plugins/DisenchantmentDependency.kt | 4 +- .../dependency/plugins/HavenBagsDependency.kt | 4 +- .../cuanvil/listener/PrepareAnvilListener.kt | 14 ++-- .../xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt | 69 +++++++++++++++++-- 10 files changed, 97 insertions(+), 29 deletions(-) rename nms/v1_21R1/src/main/kotlin/xyz/alexcrea/cuanvil/util/{RenameAnvilUtil.kt => AnvilTitleUtil.kt} (89%) diff --git a/nms/v1_21R1/src/main/kotlin/xyz/alexcrea/cuanvil/util/RenameAnvilUtil.kt b/nms/v1_21R1/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilTitleUtil.kt similarity index 89% rename from nms/v1_21R1/src/main/kotlin/xyz/alexcrea/cuanvil/util/RenameAnvilUtil.kt rename to nms/v1_21R1/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilTitleUtil.kt index 279016b..805b150 100644 --- a/nms/v1_21R1/src/main/kotlin/xyz/alexcrea/cuanvil/util/RenameAnvilUtil.kt +++ b/nms/v1_21R1/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilTitleUtil.kt @@ -3,7 +3,7 @@ package xyz.alexcrea.cuanvil.util import org.bukkit.inventory.InventoryView // TODO yet another cleanup to do on legacy removal branch -object RenameAnvilUtil { +object AnvilTitleUtil { fun rename(view: InventoryView, name: String) { view.title = name diff --git a/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt b/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt index ddaa348..9ee7317 100644 --- a/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt +++ b/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt @@ -91,7 +91,7 @@ object ConfigOptions { const val VERBOSE_DEBUG_LOGGING = "debug_log_verbose" // Minimum versions - val MINIMUM_MONETARY_COST_VER = Version(21, 0, 0) + val MINIMUM_MONETARY_COST_VER = Version(1, 21, 0) // ---------------------- // Default config values diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt index 69b0237..418dbf1 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt @@ -7,6 +7,7 @@ import org.bukkit.Bukkit import org.bukkit.ChatColor import org.bukkit.command.CommandSender import org.bukkit.entity.HumanEntity +import org.bukkit.entity.Player import org.bukkit.event.inventory.InventoryClickEvent import org.bukkit.event.inventory.PrepareAnvilEvent import org.bukkit.inventory.AnvilInventory @@ -199,7 +200,7 @@ object DependencyManager { } // Return true if should bypass (either by a dependency or error) - fun tryEventPreAnvilBypass(event: PrepareAnvilEvent, player: HumanEntity): Boolean { + fun tryEventPreAnvilBypass(event: PrepareAnvilEvent, player: Player): Boolean { try { return unsafeTryEventPreAnvilBypass(event, player) } catch (e: Exception) { @@ -208,7 +209,7 @@ object DependencyManager { } } - private fun unsafeTryEventPreAnvilBypass(event: PrepareAnvilEvent, player: HumanEntity): Boolean { + private fun unsafeTryEventPreAnvilBypass(event: PrepareAnvilEvent, player: Player): Boolean { // Run the event val bypassEvent = CAPreAnvilBypassEvent(event) Bukkit.getPluginManager().callEvent(bypassEvent) diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/EconomyManager.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/EconomyManager.kt index dc1038a..67a6c1e 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/EconomyManager.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/EconomyManager.kt @@ -12,10 +12,10 @@ interface EconomyManager { fun setupEconomy(plugin: Plugin) { if (plugin.server.pluginManager.getPlugin("Vault") == null) return - if(UnlockedEconomyManager.unlockedAvailable()) + if (UnlockedEconomyManager.unlockedAvailable()) economy = UnlockedEconomyManager(plugin) - if(economy == null || !economy!!.initialized()) + if (economy == null || !economy!!.initialized()) economy = VaultEconomyManager(plugin) } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/UnlockedEconomyManager.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/UnlockedEconomyManager.kt index 9bea81c..da66f30 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/UnlockedEconomyManager.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/UnlockedEconomyManager.kt @@ -6,7 +6,7 @@ import org.bukkit.entity.Player import org.bukkit.plugin.Plugin import java.math.BigDecimal -class UnlockedEconomyManager: EconomyManager { +class UnlockedEconomyManager : EconomyManager { val plugin: String val economy: Economy? @@ -42,23 +42,27 @@ class UnlockedEconomyManager: EconomyManager { } override fun has(player: Player, money: BigDecimal): Boolean { - if(money.signum() <= 0) return true + if (money.signum() <= 0) return true - return economy!!.has(plugin, + return economy!!.has( + plugin, player.uniqueId, player.world.name, currency(), - money) + money + ) } override fun remove(player: Player, money: BigDecimal): Boolean { - if(money.signum() <= 0) return true + if (money.signum() <= 0) return true - return economy!!.withdraw(plugin, + return economy!!.withdraw( + plugin, player.uniqueId, player.world.name, currency(), - money) + money + ) .transactionSuccess() } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/VaultEconomyManager.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/VaultEconomyManager.kt index 79a8036..058485e 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/VaultEconomyManager.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/economy/VaultEconomyManager.kt @@ -19,13 +19,13 @@ class VaultEconomyManager : EconomyManager { } override fun has(player: Player, money: BigDecimal): Boolean { - if(money.signum() <= 0) return true + if (money.signum() <= 0) return true return economy!!.has(player, money.toDouble()) } override fun remove(player: Player, money: BigDecimal): Boolean { - if(money.signum() <= 0) return true + if (money.signum() <= 0) return true return economy!!.withdrawPlayer(player, money.toDouble()).transactionSuccess() } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/DisenchantmentDependency.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/DisenchantmentDependency.kt index 3dfba9c..7a7a5eb 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/DisenchantmentDependency.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/DisenchantmentDependency.kt @@ -8,7 +8,7 @@ import com.jankominek.disenchantment.events.ShatterEvent import com.jankominek.disenchantment.listeners.DisenchantClickListener import com.jankominek.disenchantment.listeners.ShatterClickListener import io.delilaheve.CustomAnvil -import org.bukkit.entity.HumanEntity +import org.bukkit.entity.Player import org.bukkit.event.Listener import org.bukkit.event.inventory.InventoryClickEvent import org.bukkit.event.inventory.PrepareAnvilEvent @@ -51,7 +51,7 @@ class DisenchantmentDependency { InventoryClickEvent.getHandlerList().unregister(listener) } - fun testPrepareAnvil(event: PrepareAnvilEvent, player: HumanEntity): Boolean { + fun testPrepareAnvil(event: PrepareAnvilEvent, player: Player): Boolean { val previousResult = event.result event.result = null diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/HavenBagsDependency.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/HavenBagsDependency.kt index 6b5f9c4..b2e7ef4 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/HavenBagsDependency.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/HavenBagsDependency.kt @@ -1,7 +1,7 @@ package xyz.alexcrea.cuanvil.dependency.plugins import io.delilaheve.CustomAnvil -import org.bukkit.entity.HumanEntity +import org.bukkit.entity.Player import org.bukkit.event.inventory.InventoryClickEvent import org.bukkit.event.inventory.PrepareAnvilEvent import org.bukkit.inventory.AnvilInventory @@ -46,7 +46,7 @@ class HavenBagsDependency { } - fun testPrepareAnvil(event: PrepareAnvilEvent, player: HumanEntity): Boolean { + fun testPrepareAnvil(event: PrepareAnvilEvent, player: Player): Boolean { val previousResult = event.result event.result = null diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt index 00623d5..c71e82e 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt @@ -13,6 +13,7 @@ import io.delilaheve.util.ItemUtil.unitRepair import org.bukkit.ChatColor import org.bukkit.Material import org.bukkit.entity.HumanEntity +import org.bukkit.entity.Player import org.bukkit.event.EventHandler import org.bukkit.event.EventPriority import org.bukkit.event.Listener @@ -54,7 +55,8 @@ class PrepareAnvilListener : Listener { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) fun anvilCombineCheck(event: PrepareAnvilEvent) { // Should find player - val player: HumanEntity = InventoryViewUtil.getInstance().getPlayer(event.view) + val player = InventoryViewUtil.getInstance().getPlayer(event.view) + if(player !is Player) return val inventory = event.inventory // Test if custom anvil is bypassed before immutability test @@ -183,7 +185,7 @@ class PrepareAnvilListener : Listener { // return true if a custom recipe exist with these ingredients private fun testCustomRecipe( event: PrepareAnvilEvent, inventory: AnvilInventory, - player: HumanEntity, + player: Player, first: ItemStack, second: ItemStack? ): Boolean { val recipe = CustomRecipeUtil.getCustomRecipe(first, second) @@ -209,7 +211,7 @@ class PrepareAnvilListener : Listener { private fun doRenaming( event: PrepareAnvilEvent, inventory: AnvilInventory, - player: HumanEntity, first: ItemStack + player: Player, first: ItemStack ) { val resultItem = DependencyManager.cloneItem(event, first) val cost = AnvilCost() @@ -274,7 +276,7 @@ class PrepareAnvilListener : Listener { private fun doMerge( event: PrepareAnvilEvent, inventory: AnvilInventory, - player: HumanEntity, + player: Player, first: ItemStack, second: ItemStack ) { val newEnchants = first.findEnchantments() @@ -327,7 +329,7 @@ class PrepareAnvilListener : Listener { // return true if there is a valid unit repair with these ingredients private fun testUnitRepair( - event: PrepareAnvilEvent, inventory: AnvilInventory, player: HumanEntity, + event: PrepareAnvilEvent, inventory: AnvilInventory, player: Player, first: ItemStack, second: ItemStack ): Boolean { val unitRepairAmount = first.getRepair(second) ?: return false @@ -356,7 +358,7 @@ class PrepareAnvilListener : Listener { } private fun testLoreEdit( - event: PrepareAnvilEvent, inventory: AnvilInventory, player: HumanEntity, + event: PrepareAnvilEvent, inventory: AnvilInventory, player: Player, first: ItemStack, second: ItemStack ): Boolean { val type = second.type diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt index 6565166..07f3d23 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt @@ -2,9 +2,12 @@ package xyz.alexcrea.cuanvil.util import io.delilaheve.CustomAnvil import io.delilaheve.util.ConfigOptions +import io.delilaheve.util.ConfigOptions.getMonetaryMultiplier as moneyMultiplier import io.delilaheve.util.EnchantmentUtil.enchantmentName import io.delilaheve.util.ItemUtil.findEnchantments import io.delilaheve.util.ItemUtil.isEnchantedBook +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.format.TextColor import org.bukkit.GameMode import org.bukkit.NamespacedKey import org.bukkit.entity.HumanEntity @@ -16,7 +19,9 @@ import org.bukkit.inventory.meta.Repairable import org.bukkit.persistence.PersistentDataType import xyz.alexcrea.cuanvil.config.ConfigHolder import xyz.alexcrea.cuanvil.dependency.DependencyManager +import xyz.alexcrea.cuanvil.dependency.economy.EconomyManager import xyz.alexcrea.cuanvil.group.ConflictType +import java.math.BigDecimal import kotlin.math.min object AnvilXpUtil { @@ -43,6 +48,7 @@ object AnvilXpUtil { this.generic = generic isAlone = true } + constructor() { isAlone = false } @@ -54,12 +60,14 @@ object AnvilXpUtil { fun setAnvilInvCost( inventory: AnvilInventory, view: InventoryView, - player: HumanEntity, + player: Player, cost: AnvilCost, ignoreRules: Boolean = false ) { - // TODO check require money or xp cost & display appropriately - setAnvilInvXp(inventory, view, player, cost.sum(), ignoreRules) + if (ConfigOptions.shouldUseMoney) + setAnvilPrice(inventory, view, player, cost) + else + setAnvilInvXp(inventory, view, player, cost.sum(), ignoreRules) } /** @@ -72,7 +80,7 @@ object AnvilXpUtil { anvilCost: Int, ignoreRules: Boolean = false ) { - + // Test repair cost limit val finalAnvilCost = if ( !ignoreRules && @@ -121,6 +129,59 @@ object AnvilXpUtil { } } + fun asMonetaryCost(cost: AnvilCost): BigDecimal { + // multiply by per use type multipliers + return BigDecimal(cost.generic) + .add(BigDecimal(cost.enchantment).multiply(moneyMultiplier("enchantment"))) + .add(BigDecimal(cost.repair).multiply(moneyMultiplier("repair"))) + .add(BigDecimal(cost.rename).multiply(moneyMultiplier("rename"))) + .add(BigDecimal(cost.lore).multiply(moneyMultiplier("lore_edit"))) + .add(BigDecimal(cost.enchantment).multiply(moneyMultiplier("enchantment"))) + .add(BigDecimal(cost.illegalPenalty).multiply(moneyMultiplier("work_penalty"))) + .add(BigDecimal(cost.workPenalty).multiply(moneyMultiplier("work_penalty"))) + .add(BigDecimal(cost.recipe).multiply(moneyMultiplier("recipe"))) + .multiply(moneyMultiplier("global")) + } + + /** + * Display monetary cost needed for the work on the anvil inventory + */ + private fun setAnvilPrice( + inventory: AnvilInventory, + view: InventoryView, + player: Player, + cost: AnvilCost, + ) { + val finalCost = asMonetaryCost(cost) + + val has = EconomyManager.economy!!.has(player, finalCost) + + val text = "Cost: " + (if(has) "§2" else "§4") + + EconomyManager.economy!!.format(finalCost) + AnvilTitleUtil.rename(view, text) + + clearAnvilXpCost(inventory, view, player) + } + + private fun clearAnvilXpCost( + inventory: AnvilInventory, + view: InventoryView, + player: HumanEntity, + ) { + // TODO for 2.x.x use anvil view & set directly there + inventory.repairCost = 0 + + // retry after a tick + DependencyManager.scheduler.scheduleOnEntity( + CustomAnvil.instance, player + ) { + inventory.repairCost = 0 + + if (player !is Player) return@scheduleOnEntity + player.updateInventory() + } + } + /** * Function to calculate work penalty of anvil work * Also change result work penalty if right item is not null From fb27ad2e552890095aa3ad734f9d59f6d600dd21 Mon Sep 17 00:00:00 2001 From: alexcrea Date: Thu, 28 May 2026 20:11:02 +0200 Subject: [PATCH 08/24] avoid looping on same name --- .../xyz/alexcrea/cuanvil/dialog/AnvilRenameDialogImpl.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/nms/nms-paper/src/main/kotlin/xyz/alexcrea/cuanvil/dialog/AnvilRenameDialogImpl.kt b/nms/nms-paper/src/main/kotlin/xyz/alexcrea/cuanvil/dialog/AnvilRenameDialogImpl.kt index 603a215..fb5fa0c 100644 --- a/nms/nms-paper/src/main/kotlin/xyz/alexcrea/cuanvil/dialog/AnvilRenameDialogImpl.kt +++ b/nms/nms-paper/src/main/kotlin/xyz/alexcrea/cuanvil/dialog/AnvilRenameDialogImpl.kt @@ -109,13 +109,16 @@ class AnvilRenameDialogImpl( private fun setName(player: HumanEntity, view: CraftAnvilView, name: String?) { val menu = (containerField.get(view) as AnvilMenu) + val isSameName = menu.itemName == name menu.itemName = name if(name == null) lastNames.remove(player.uniqueId) else lastNames[player.uniqueId] = name - CraftEventFactory.callPrepareResultEvent(menu, 2); + + if(!isSameName) + CraftEventFactory.callPrepareResultEvent(menu, 2); } private fun nameFromItem(player: HumanEntity, item: ItemStack?): String? { From 171a8cad6dc6296902dea40767aaa391ab83a445 Mon Sep 17 00:00:00 2001 From: alexcrea Date: Thu, 28 May 2026 20:21:40 +0200 Subject: [PATCH 09/24] monetary cost require dialog rename --- defaultconfigs/1.18/config.yml | 5 +++- defaultconfigs/1.21.11/config.yml | 5 +++- defaultconfigs/1.21.9/config.yml | 5 +++- defaultconfigs/1.21/config.yml | 5 +++- .../alexcrea/cuanvil/util/AnvilTitleUtil.kt | 2 ++ .../io/delilaheve/util/ConfigOptions.kt | 28 +++++++++++++------ .../cuanvil/listener/PrepareAnvilListener.kt | 21 ++++++-------- .../xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt | 10 +++++-- src/main/resources/config.yml | 5 +++- 9 files changed, 57 insertions(+), 29 deletions(-) rename nms/{v1_21R1 => nms-paper}/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilTitleUtil.kt (86%) diff --git a/defaultconfigs/1.18/config.yml b/defaultconfigs/1.18/config.yml index 1a1663a..fb70505 100644 --- a/defaultconfigs/1.18/config.yml +++ b/defaultconfigs/1.18/config.yml @@ -433,7 +433,10 @@ lore_edit: # Allow to replace the xp cost by a monetary cost # If enabled it will not be bound to the experience level limits # -# This feature can only be enabled starting with 1.21 +# It also requires to enable dialog rename (set "enable_dialog_rename: false" a bit higher) +# If dialog rename permission is enabled and player do not have the permission merge will fall back to vanilla xp cost +# +# As this feature require dialog rename, it can only be enabled starting with paper 1.21.6 monetary_cost: enabled: false # If using vault unlocked this allow to specify what currency should be used for anvil usage diff --git a/defaultconfigs/1.21.11/config.yml b/defaultconfigs/1.21.11/config.yml index 2daa373..b70798c 100644 --- a/defaultconfigs/1.21.11/config.yml +++ b/defaultconfigs/1.21.11/config.yml @@ -453,7 +453,10 @@ lore_edit: # Allow to replace the xp cost by a monetary cost # If enabled it will not be bound to the experience level limits # -# This feature can only be enabled starting with 1.21 +# It also requires to enable dialog rename (set "enable_dialog_rename: false" a bit higher) +# If dialog rename permission is enabled and player do not have the permission merge will fall back to vanilla xp cost +# +# As this feature require dialog rename, it can only be enabled starting with paper 1.21.6 monetary_cost: enabled: false # If using vault unlocked this allow to specify what currency should be used for anvil usage diff --git a/defaultconfigs/1.21.9/config.yml b/defaultconfigs/1.21.9/config.yml index 8532881..31bdb1f 100644 --- a/defaultconfigs/1.21.9/config.yml +++ b/defaultconfigs/1.21.9/config.yml @@ -445,7 +445,10 @@ lore_edit: # Allow to replace the xp cost by a monetary cost # If enabled it will not be bound to the experience level limits # -# This feature can only be enabled starting with 1.21 +# It also requires to enable dialog rename (set "enable_dialog_rename: false" a bit higher) +# If dialog rename permission is enabled and player do not have the permission merge will fall back to vanilla xp cost +# +# As this feature require dialog rename, it can only be enabled starting with paper 1.21.6 monetary_cost: enabled: false # If using vault unlocked this allow to specify what currency should be used for anvil usage diff --git a/defaultconfigs/1.21/config.yml b/defaultconfigs/1.21/config.yml index 25e47dc..1dafa54 100644 --- a/defaultconfigs/1.21/config.yml +++ b/defaultconfigs/1.21/config.yml @@ -433,7 +433,10 @@ lore_edit: # Allow to replace the xp cost by a monetary cost # If enabled it will not be bound to the experience level limits # -# This feature can only be enabled starting with 1.21 +# It also requires to enable dialog rename (set "enable_dialog_rename: false" a bit higher) +# If dialog rename permission is enabled and player do not have the permission merge will fall back to vanilla xp cost +# +# As this feature require dialog rename, it can only be enabled starting with paper 1.21.6 monetary_cost: enabled: false # If using vault unlocked this allow to specify what currency should be used for anvil usage diff --git a/nms/v1_21R1/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilTitleUtil.kt b/nms/nms-paper/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilTitleUtil.kt similarity index 86% rename from nms/v1_21R1/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilTitleUtil.kt rename to nms/nms-paper/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilTitleUtil.kt index 805b150..cc92b78 100644 --- a/nms/v1_21R1/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilTitleUtil.kt +++ b/nms/nms-paper/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilTitleUtil.kt @@ -6,6 +6,8 @@ import org.bukkit.inventory.InventoryView object AnvilTitleUtil { fun rename(view: InventoryView, name: String) { + if(view.title == name) return + view.title = name } diff --git a/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt b/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt index 9ee7317..b163de8 100644 --- a/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt +++ b/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt @@ -3,15 +3,16 @@ package io.delilaheve.util import io.delilaheve.CustomAnvil import io.delilaheve.util.EnchantmentUtil.enchantmentName import org.bukkit.NamespacedKey +import org.bukkit.entity.HumanEntity import xyz.alexcrea.cuanvil.config.ConfigHolder import xyz.alexcrea.cuanvil.config.WorkPenaltyType import xyz.alexcrea.cuanvil.config.WorkPenaltyType.WorkPenaltyPart import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.dependency.economy.EconomyManager import xyz.alexcrea.cuanvil.enchant.CAEnchantment -import xyz.alexcrea.cuanvil.update.UpdateUtils import xyz.alexcrea.cuanvil.update.Version import xyz.alexcrea.cuanvil.util.AnvilUseType +import xyz.alexcrea.cuanvil.util.dialog.AnvilRenameDialogUtil import java.math.BigDecimal import java.util.* @@ -90,9 +91,6 @@ object ConfigOptions { const val DEBUG_LOGGING = "debug_log" const val VERBOSE_DEBUG_LOGGING = "debug_log_verbose" - // Minimum versions - val MINIMUM_MONETARY_COST_VER = Version(1, 21, 0) - // ---------------------- // Default config values // ---------------------- @@ -181,6 +179,11 @@ object ConfigOptions { // Default max before merge disabled (negative mean enabled) const val DEFAULT_MAX_BEFORE_MERGE_DISABLED = -1 + // ----------- + // Permissions + // ----------- + private const val RENAME_DIALOG_PERMISSION = "ca.rename.dialog" + // ------------- // Get methods // ------------- @@ -469,6 +472,13 @@ object ConfigOptions { .getBoolean(DIALOG_RENAME_USE_PERMISSION, DEFAULT_DIALOG_RENAME_USE_PERMISSION) } + fun canUseDialogRename(player: HumanEntity): Boolean { + if(!doRenameDialog || !AnvilRenameDialogUtil.anvilRenameDialog.canSendDialog()) return false + if(doRenameDialogUsePermission && !player.hasPermission(RENAME_DIALOG_PERMISSION)) return false + + return true + } + /** * Do the dialog menu require permission */ @@ -643,16 +653,16 @@ object ConfigOptions { } /* - * Monetary configs (only for 1.21+) + * Monetary configs (only for 1.21.6+) + * Also require dialog rename */ - val shouldUseMoney: Boolean - get() { + fun shouldUseMoney(player: HumanEntity): Boolean { return EconomyManager.economy?.initialized() == true && - UpdateUtils.currentMinecraftVersion().greaterEqual(MINIMUM_MONETARY_COST_VER) && + canUseDialogRename(player) && ConfigHolder.DEFAULT_CONFIG .config .getBoolean(SHOULD_USE_MONEY, DEFAULT_SHOULD_USE_MONEY) - } + } val usedCurrency: String get() { diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt index c71e82e..fc04dcf 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt @@ -1,6 +1,7 @@ package xyz.alexcrea.cuanvil.listener import com.github.stefvanschie.inventoryframework.util.InventoryViewUtil +import com.jankominek.disenchantment.utils.AnvilCostUtils import io.delilaheve.CustomAnvil import io.delilaheve.util.ConfigOptions import io.delilaheve.util.EnchantmentUtil.combineWith @@ -19,6 +20,7 @@ import org.bukkit.event.EventPriority import org.bukkit.event.Listener import org.bukkit.event.inventory.PrepareAnvilEvent import org.bukkit.inventory.AnvilInventory +import org.bukkit.inventory.InventoryView import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.EnchantmentStorageMeta import org.bukkit.inventory.meta.ItemMeta @@ -45,8 +47,6 @@ class PrepareAnvilListener : Listener { const val ANVIL_OUTPUT_SLOT = 2 var IS_EMPTY_TEST = false - - private const val RENAME_DIALOG_PERMISSION = "ca.rename.dialog" } /** @@ -121,29 +121,26 @@ class PrepareAnvilListener : Listener { // Test for lore edit if (testLoreEdit(event, inventory, player, first, second)) return - CustomAnvil.log("no anvil fuse type found") event.result = null } + private fun setNoResult(event: PrepareAnvilEvent, view: InventoryView) { + event.result = null + AnvilXpUtil.onNoResult(view) + } + private fun tryRenameDialog( player: HumanEntity, event: PrepareAnvilEvent ) { - if(!canUseRenameDialog(player)) return + if(!ConfigOptions.canUseDialogRename(player)) return AnvilRenameDialogUtil.anvilRenameDialog.tryShowDialog(player, event) } - private fun canUseRenameDialog(player: HumanEntity): Boolean { - if(!ConfigOptions.doRenameDialog || !AnvilRenameDialogUtil.anvilRenameDialog.canSendDialog()) return false - if(ConfigOptions.doRenameDialogUsePermission && !player.hasPermission(RENAME_DIALOG_PERMISSION)) return false - - return true - } - private fun processDialogPCD(it: ItemMeta, player: HumanEntity) { - val keepDialog = canUseRenameDialog(player) && ConfigOptions.shouldKeepRenameText + val keepDialog = ConfigOptions.canUseDialogRename(player) && ConfigOptions.shouldKeepRenameText val pdc = it.persistentDataContainer if(!keepDialog) diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt index 07f3d23..c8829f3 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt @@ -6,8 +6,6 @@ import io.delilaheve.util.ConfigOptions.getMonetaryMultiplier as moneyMultiplier import io.delilaheve.util.EnchantmentUtil.enchantmentName import io.delilaheve.util.ItemUtil.findEnchantments import io.delilaheve.util.ItemUtil.isEnchantedBook -import net.kyori.adventure.text.Component -import net.kyori.adventure.text.format.TextColor import org.bukkit.GameMode import org.bukkit.NamespacedKey import org.bukkit.entity.HumanEntity @@ -21,6 +19,7 @@ import xyz.alexcrea.cuanvil.config.ConfigHolder import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.dependency.economy.EconomyManager import xyz.alexcrea.cuanvil.group.ConflictType +import xyz.alexcrea.cuanvil.util.dialog.AnvilRenameDialogUtil import java.math.BigDecimal import kotlin.math.min @@ -64,7 +63,7 @@ object AnvilXpUtil { cost: AnvilCost, ignoreRules: Boolean = false ) { - if (ConfigOptions.shouldUseMoney) + if (ConfigOptions.shouldUseMoney(player)) setAnvilPrice(inventory, view, player, cost) else setAnvilInvXp(inventory, view, player, cost.sum(), ignoreRules) @@ -228,6 +227,11 @@ object AnvilXpUtil { return resultSum } + fun onNoResult(player: HumanEntity, view: InventoryView) { + if (ConfigOptions.shouldUseMoney(player)) + AnvilTitleUtil.rename(view, "") + } + private fun exclusivePenaltyKey(useType: AnvilUseType): NamespacedKey { return NamespacedKey(CustomAnvil.instance, "${EXCLUSIVE_PENALTY_PREFIX}_${useType.typeName}") } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 5ba61ec..27c0248 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -435,7 +435,10 @@ lore_edit: # Allow to replace the xp cost by a monetary cost # If enabled it will not be bound to the experience level limits # -# This feature can only be enabled starting with 1.21 +# It also requires to enable dialog rename (set "enable_dialog_rename: false" a bit higher) +# If dialog rename permission is enabled and player do not have the permission merge will fall back to vanilla xp cost +# +# As this feature require dialog rename, it can only be enabled starting with paper 1.21.6 monetary_cost: enabled: false # If using vault unlocked this allow to specify what currency should be used for anvil usage From 7aac325c709b3cd73d46cac94f5873cbacdd86e6 Mon Sep 17 00:00:00 2001 From: alexcrea Date: Fri, 29 May 2026 00:39:12 +0200 Subject: [PATCH 10/24] hell --- .../cuanvil/dialog/AnvilRenameDialog.kt | 2 + .../cuanvil/dialog/AnvilRenameDialogImpl.kt | 127 +++++++++++------- .../alexcrea/cuanvil/util/AnvilTitleUtil.kt | 37 ++++- .../cuanvil/listener/PrepareAnvilListener.kt | 6 +- .../xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt | 10 +- .../util/dialog/AnvilRenameDialogUtil.kt | 4 + 6 files changed, 130 insertions(+), 56 deletions(-) diff --git a/nms/nms-common/src/main/kotlin/xyz/alexcrea/cuanvil/dialog/AnvilRenameDialog.kt b/nms/nms-common/src/main/kotlin/xyz/alexcrea/cuanvil/dialog/AnvilRenameDialog.kt index 62e1cf0..df6e883 100644 --- a/nms/nms-common/src/main/kotlin/xyz/alexcrea/cuanvil/dialog/AnvilRenameDialog.kt +++ b/nms/nms-common/src/main/kotlin/xyz/alexcrea/cuanvil/dialog/AnvilRenameDialog.kt @@ -18,4 +18,6 @@ interface AnvilRenameDialog { fun currentText(player: HumanEntity): String? + fun isOpenFor(player: HumanEntity): Boolean + } \ No newline at end of file diff --git a/nms/nms-paper/src/main/kotlin/xyz/alexcrea/cuanvil/dialog/AnvilRenameDialogImpl.kt b/nms/nms-paper/src/main/kotlin/xyz/alexcrea/cuanvil/dialog/AnvilRenameDialogImpl.kt index fb5fa0c..c6973f1 100644 --- a/nms/nms-paper/src/main/kotlin/xyz/alexcrea/cuanvil/dialog/AnvilRenameDialogImpl.kt +++ b/nms/nms-paper/src/main/kotlin/xyz/alexcrea/cuanvil/dialog/AnvilRenameDialogImpl.kt @@ -32,7 +32,7 @@ class AnvilRenameDialogImpl( val keepUserPreviousDialog: Supplier, val maxLength: Supplier, val plugin: Plugin, -): AnvilRenameDialog { +) : AnvilRenameDialog { companion object { private const val RENAME_TEXT_KEY = "rename" @@ -43,16 +43,24 @@ class AnvilRenameDialogImpl( // Need to be able to translate it later ! private val USER_FACING_RENAME_TITLE = Component.text("Rename Your Item") - private val USER_FACING_WARNING = Component.text("Note that the repair text will appear blank after Confirm\n" + - "But the name will be correctly applied") + private val USER_FACING_WARNING = Component.text( + "Note that the repair text will appear blank after Confirm\n" + + "But the name will be correctly applied" + ) private val USER_FACING_CONFIRM = Component.text("Confirm").color(TextColor.fromHexString("#40FF40")) private val USER_FACING_CANCEL = Component.text("Cancel").color(TextColor.fromHexString("#FF4040")) } private val lastNames = HashMap() + private val lastRenames = HashMap() + + private val lastLeftItem = HashMap() private val runTaskMap = HashMap() + // For monetary cost + val hasUiOpen = HashSet() + private val containerField = CraftInventoryView::class.java.getDeclaredField("container") init { @@ -63,75 +71,90 @@ class AnvilRenameDialogImpl( return true } - fun makeDialog(initial: String?, callback: Consumer): Dialog { + fun makeDialog(playerID: UUID, initial: String?, callback: Consumer): Dialog { val maxLength = this.maxLength.get() val initialFinal = initial?.take(maxLength) val baseBuilder = DialogBase.builder(USER_FACING_RENAME_TITLE) - .canCloseWithEscape(true) + .canCloseWithEscape(false) .afterAction(DialogBase.DialogAfterAction.CLOSE) - .inputs(listOf( - DialogInput.text(RENAME_TEXT_KEY, Component.text("Rename text")) - .maxLength(maxLength) - .initial(initialFinal ?: "") - .labelVisible(false) - .width(MAX_WIDTH) - .build(), + .inputs( + listOf( + DialogInput.text(RENAME_TEXT_KEY, Component.text("Rename text")) + .maxLength(maxLength) + .initial(initialFinal ?: "") + .labelVisible(false) + .width(MAX_WIDTH) + .build(), ), ) - baseBuilder.body(listOf( + baseBuilder.body( + listOf( DialogBody.plainMessage(USER_FACING_WARNING, MAX_WIDTH) - )) + ) + ) - return Dialog.create { builder -> builder.empty() - .base(baseBuilder.build()) - .type(DialogType.confirmation( - ActionButton.builder(USER_FACING_CONFIRM) - .action(DialogAction.customClick({ response, _ -> - val text = response.getText(RENAME_TEXT_KEY)!! - callback.accept(text) - }, ClickCallback.Options.builder().build())) - .build(), - ActionButton.builder(USER_FACING_CANCEL) - .build(), - )) + return Dialog.create { builder -> + builder.empty() + .base(baseBuilder.build()) + .type( + DialogType.confirmation( + ActionButton.builder(USER_FACING_CONFIRM) + .action(DialogAction.customClick({ response, _ -> + hasUiOpen.remove(playerID) + val text = response.getText(RENAME_TEXT_KEY)!! + callback.accept(text) + }, ClickCallback.Options.builder().build())) + .build(), + ActionButton.builder(USER_FACING_CANCEL) + .action(DialogAction.customClick({ response, _ -> + hasUiOpen.remove(playerID) + }, ClickCallback.Options.builder().build())) + .build(), + ) + ) } } private fun setResult(player: HumanEntity, view: CraftAnvilView, result: String?) { val defaultName = PLAIN_TEXT_SERIALIZER.serializeOrNull(view.getItem(0)?.effectiveName()) - if(defaultName == result) { - setName(player, view, "") - if(defaultName != null) lastNames[player.uniqueId] = defaultName - } - else setName(player, view, result) + if (defaultName == result) { + setName(player, view, "", null) + if (defaultName != null) lastNames[player.uniqueId] = defaultName + } else setName(player, view, lastNames[player.uniqueId], result) } - private fun setName(player: HumanEntity, view: CraftAnvilView, name: String?) { + private fun setName(player: HumanEntity, view: CraftAnvilView, name: String?, rename: String?) { val menu = (containerField.get(view) as AnvilMenu) val isSameName = menu.itemName == name menu.itemName = name - if(name == null) + if (name == null) lastNames.remove(player.uniqueId) else lastNames[player.uniqueId] = name - if(!isSameName) + if (rename == null) + lastRenames.remove(player.uniqueId) + else + lastRenames[player.uniqueId] = rename + + if (!isSameName) CraftEventFactory.callPrepareResultEvent(menu, 2); } private fun nameFromItem(player: HumanEntity, item: ItemStack?): String? { // Already has text - if(item?.hasItemMeta() != true || !item.itemMeta.hasCustomName()) + if (item?.hasItemMeta() != true || !item.itemMeta.hasCustomName()) return PLAIN_TEXT_SERIALIZER.serializeOrNull(item?.effectiveName()) - if(keepUserPreviousDialog.get() && item.hasItemMeta()) { + if (keepUserPreviousDialog.get() && item.hasItemMeta()) { val lastName = item.itemMeta.persistentDataContainer.get( AnvilRenameDialog.PCD_KEEP_RENAME_TEXT_KEY, - PersistentDataType.STRING) + PersistentDataType.STRING + ) - if(lastName != null) return lastName + if (lastName != null) return lastName } return fromFormated.apply(player, item.effectiveName()) @@ -139,33 +162,37 @@ class AnvilRenameDialogImpl( private fun tryShowDialogScheduled(player: HumanEntity, event: PrepareAnvilEvent) { val view = event.view - if(view !is CraftAnvilView) return + if (view !is CraftAnvilView) return val renameText = view.renameText val leftItem = view.getItem(0) val leftItemStr = leftItem?.toString() - val lastName = lastNames.getOrDefault(player.uniqueId, null) - if(lastLeftItem.getOrDefault(player.uniqueId, null) != leftItemStr) { - if(leftItemStr == null) + val lastName = lastNames.getOrDefault(player.uniqueId, null) + val lastRename = lastRenames.getOrDefault(player.uniqueId, null) + + if (lastLeftItem.getOrDefault(player.uniqueId, null) != leftItemStr) { + if (leftItemStr == null) lastLeftItem.remove(player.uniqueId) else lastLeftItem[player.uniqueId] = leftItemStr - setName(player, view, nameFromItem(player, leftItem)) + setName(player, view, renameText, nameFromItem(player, leftItem)) return } - if(lastName == renameText) + if (lastName == renameText) return - if(renameText?.isBlank() == true) { - setName(player, view, lastNames[player.uniqueId]) + if (renameText?.isBlank() == true) { + setName(player, view, lastName, lastRename) return } - val dialog = makeDialog(lastName) + val dialog = makeDialog(player.uniqueId, lastRename) { result -> setResult(player, view, result) } player.showDialog(dialog) + + hasUiOpen.add(player.uniqueId) } // We need to wait for a short time as changing item will change the name BEFORE the item change @@ -181,7 +208,7 @@ class AnvilRenameDialogImpl( {}, 2 ) - if(task == null) return + if (task == null) return runTaskMap[player.uniqueId] = task } @@ -196,4 +223,8 @@ class AnvilRenameDialogImpl( return lastNames[player.uniqueId] } + override fun isOpenFor(player: HumanEntity): Boolean { + return hasUiOpen.contains(player.uniqueId) + } + } \ No newline at end of file diff --git a/nms/nms-paper/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilTitleUtil.kt b/nms/nms-paper/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilTitleUtil.kt index cc92b78..e40e7ed 100644 --- a/nms/nms-paper/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilTitleUtil.kt +++ b/nms/nms-paper/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilTitleUtil.kt @@ -1,14 +1,45 @@ package xyz.alexcrea.cuanvil.util +import io.papermc.paper.threadedregions.scheduler.ScheduledTask +import org.bukkit.entity.HumanEntity import org.bukkit.inventory.InventoryView +import org.bukkit.plugin.Plugin +import xyz.alexcrea.cuanvil.dialog.AnvilRenameDialog +import java.util.HashMap +import java.util.UUID -// TODO yet another cleanup to do on legacy removal branch object AnvilTitleUtil { - fun rename(view: InventoryView, name: String) { - if(view.title == name) return + private val runTaskMap = HashMap() + + private fun actualRename(view: InventoryView, name: String, player: HumanEntity, anvilDialog: AnvilRenameDialog) { + runTaskMap.remove(player.uniqueId) + if (view.title == name) return + + // We assume rename impl is used + if (anvilDialog.isOpenFor(player)) return view.title = name } + // We don't want to rename instantly it is causing issue with rename text + // especially as it can "override" current ui when it is rename ui time but rename ui also need some delay + fun rename(view: InventoryView, name: String, player: HumanEntity, anvilDialog: AnvilRenameDialog, plugin: Plugin) { + runTaskMap.remove(player.uniqueId)?.cancel() + + val task = player.scheduler.runDelayed( + plugin, + { _ -> + run { actualRename(view, name, player, anvilDialog) } + }, + { + runTaskMap.remove(player.uniqueId) + }, + 2 + ) + + if (task == null) return + runTaskMap[player.uniqueId] = task + } + } \ No newline at end of file diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt index fc04dcf..5ef3e27 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt @@ -59,6 +59,8 @@ class PrepareAnvilListener : Listener { if(player !is Player) return val inventory = event.inventory + tryRenameDialog(player, event) + // Test if custom anvil is bypassed before immutability test if (DependencyManager.earlyTryEventPreAnvilBypass(event, player)) { // even if we got bypassed we still want to set price @@ -87,8 +89,6 @@ class PrepareAnvilListener : Listener { return } - tryRenameDialog(player, event) - // Test if the event should bypass custom anvil. if (DependencyManager.tryEventPreAnvilBypass(event, player)) { // even if we got bypassed we still want to set price @@ -127,7 +127,7 @@ class PrepareAnvilListener : Listener { private fun setNoResult(event: PrepareAnvilEvent, view: InventoryView) { event.result = null - AnvilXpUtil.onNoResult(view) + // TODO AnvilXpUtil.onNoResult(view) } private fun tryRenameDialog( diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt index c8829f3..2d7bf0c 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt @@ -157,7 +157,10 @@ object AnvilXpUtil { val text = "Cost: " + (if(has) "§2" else "§4") + EconomyManager.economy!!.format(finalCost) - AnvilTitleUtil.rename(view, text) + AnvilTitleUtil.rename(view, text, + player, + AnvilRenameDialogUtil.anvilRenameDialog, + CustomAnvil.instance) clearAnvilXpCost(inventory, view, player) } @@ -229,7 +232,10 @@ object AnvilXpUtil { fun onNoResult(player: HumanEntity, view: InventoryView) { if (ConfigOptions.shouldUseMoney(player)) - AnvilTitleUtil.rename(view, "") + AnvilTitleUtil.rename(view, "", + player, + AnvilRenameDialogUtil.anvilRenameDialog, + CustomAnvil.instance) } private fun exclusivePenaltyKey(useType: AnvilUseType): NamespacedKey { diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/dialog/AnvilRenameDialogUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/dialog/AnvilRenameDialogUtil.kt index dbff77b..a83d8b0 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/dialog/AnvilRenameDialogUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/dialog/AnvilRenameDialogUtil.kt @@ -47,5 +47,9 @@ object AnvilRenameDialogUtil { return null } + override fun isOpenFor(player: HumanEntity): Boolean { + return false + } + } } \ No newline at end of file From 3992ce1662012a0c4ce4180dd6f51e22458b9f0b Mon Sep 17 00:00:00 2001 From: alexcrea Date: Fri, 29 May 2026 02:48:16 +0200 Subject: [PATCH 11/24] no price on no result --- .../cuanvil/listener/PrepareAnvilListener.kt | 53 +++++++++++-------- .../xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt | 2 +- .../cuanvil/util/JustForEasierHotswapUtil.kt | 17 ++++++ 3 files changed, 48 insertions(+), 24 deletions(-) create mode 100644 src/main/kotlin/xyz/alexcrea/cuanvil/util/JustForEasierHotswapUtil.kt diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt index 5ef3e27..ec5cac2 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt @@ -54,25 +54,26 @@ class PrepareAnvilListener : Listener { */ @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) fun anvilCombineCheck(event: PrepareAnvilEvent) { - // Should find player - val player = InventoryViewUtil.getInstance().getPlayer(event.view) - if(player !is Player) return + val view = event.view val inventory = event.inventory + val player = JustForEasierHotswapUtil.getPlayerFromView(view) + if(player !is Player) return + tryRenameDialog(player, event) // Test if custom anvil is bypassed before immutability test if (DependencyManager.earlyTryEventPreAnvilBypass(event, player)) { // even if we got bypassed we still want to set price - AnvilXpUtil.setAnvilInvCost(inventory, event.view, player, AnvilCost(event.inventory.repairCost)) + AnvilXpUtil.setAnvilInvCost(inventory, view, player, AnvilCost(event.inventory.repairCost)) return } - val first = inventory.getItem(ANVIL_INPUT_LEFT) ?: return + val first = inventory.getItem(ANVIL_INPUT_LEFT) val second = inventory.getItem(ANVIL_INPUT_RIGHT) if(IS_EMPTY_TEST) { - event.result = null + setNoResult(event, player, view) IS_EMPTY_TEST = false return } @@ -85,33 +86,40 @@ class PrepareAnvilListener : Listener { if (isImmutable(first) || isImmutable(second)) { CustomAnvil.verboseLog("Skipping anvil process as one of the two item is immutable") - event.result = null + setNoResult(event, player, view) return } // Test if the event should bypass custom anvil. if (DependencyManager.tryEventPreAnvilBypass(event, player)) { // even if we got bypassed we still want to set price - AnvilXpUtil.setAnvilInvCost(inventory, event.view, player, AnvilCost(event.inventory.repairCost)) + AnvilXpUtil.setAnvilInvCost(inventory, view, player, AnvilCost(event.inventory.repairCost)) return } if (!player.hasPermission(CustomAnvil.affectedByPluginPermission)) return + if(first == null) { + setNoResult(event, player, view) + return + } + // Test custom recipe if (testCustomRecipe(event, inventory, player, first, second)) return // Test rename lonely item - val isAir = second.isAir - CustomAnvil.verboseLog("checking air in main logic: $isAir") - if (isAir) { - doRenaming(event, inventory, player, first) + val shouldTryRename = second.isAir + CustomAnvil.verboseLog("checking air in main logic: $shouldTryRename") + if (shouldTryRename) { + if(!doRenaming(event, inventory, player, first)) + setNoResult(event, player, view) return } // Test for merge if (first.canMergeWith(second!!)) { - doMerge(event, inventory, player, first, second) + if(!doMerge(event, inventory, player, first, second)) + setNoResult(event, player, view) return } @@ -121,13 +129,12 @@ class PrepareAnvilListener : Listener { // Test for lore edit if (testLoreEdit(event, inventory, player, first, second)) return - event.result = null - + setNoResult(event, player, view) } - private fun setNoResult(event: PrepareAnvilEvent, view: InventoryView) { + private fun setNoResult(event: PrepareAnvilEvent, player: Player, view: InventoryView) { event.result = null - // TODO AnvilXpUtil.onNoResult(view) + AnvilXpUtil.onNoResult(player, view) } private fun tryRenameDialog( @@ -209,7 +216,7 @@ class PrepareAnvilListener : Listener { private fun doRenaming( event: PrepareAnvilEvent, inventory: AnvilInventory, player: Player, first: ItemStack - ) { + ): Boolean { val resultItem = DependencyManager.cloneItem(event, first) val cost = AnvilCost() cost.rename = handleRename(resultItem, inventory, player) @@ -217,14 +224,14 @@ class PrepareAnvilListener : Listener { // Test/stop if nothing changed. if (first == resultItem) { CustomAnvil.log("no right item, But input is same as output") - event.result = null - return + return false } cost.workPenalty = AnvilXpUtil.calculatePenalty(first, null, resultItem, AnvilUseType.RENAME_ONLY) event.result = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.RENAME_ONLY, cost) AnvilXpUtil.setAnvilInvCost(inventory, event.view, player, cost) + return true } private fun handleRename(resultItem: ItemStack, inventory: AnvilInventory, player: HumanEntity): Int { @@ -275,7 +282,7 @@ class PrepareAnvilListener : Listener { event: PrepareAnvilEvent, inventory: AnvilInventory, player: Player, first: ItemStack, second: ItemStack - ) { + ): Boolean { val newEnchants = first.findEnchantments() .combineWith(second.findEnchantments(), first, player) var hasChanged = !isIdentical(first.findEnchantments(), newEnchants) @@ -299,8 +306,7 @@ class PrepareAnvilListener : Listener { // Test/stop if nothing changed. if (!hasChanged) { CustomAnvil.log("Mergeable with second, But input is same as output") - event.result = null - return + return false } // As calculatePenalty edit result, we need to calculate penalty after checking equality cost.workPenalty = AnvilXpUtil.calculatePenalty(first, second, resultItem, AnvilUseType.MERGE) @@ -310,6 +316,7 @@ class PrepareAnvilListener : Listener { // Finally, we set result event.result = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.MERGE, cost) AnvilXpUtil.setAnvilInvCost(inventory, event.view, player, cost) + return true } private fun isIdentical( diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt index 2d7bf0c..a581ed3 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt @@ -232,7 +232,7 @@ object AnvilXpUtil { fun onNoResult(player: HumanEntity, view: InventoryView) { if (ConfigOptions.shouldUseMoney(player)) - AnvilTitleUtil.rename(view, "", + AnvilTitleUtil.rename(view, "Repair & Name", player, AnvilRenameDialogUtil.anvilRenameDialog, CustomAnvil.instance) diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/JustForEasierHotswapUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/JustForEasierHotswapUtil.kt new file mode 100644 index 0000000..ea0f7ec --- /dev/null +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/JustForEasierHotswapUtil.kt @@ -0,0 +1,17 @@ +package xyz.alexcrea.cuanvil.util + +import com.github.stefvanschie.inventoryframework.util.InventoryViewUtil +import org.bukkit.entity.HumanEntity +import org.bukkit.inventory.InventoryView + +// Hotswap to not relocate +// So I just put small thing calling relocating method here to enable to hotswap more class +// Especially for PrepareAnvilListener +// Will be able to replace that on legacy removal so really temporary +object JustForEasierHotswapUtil { + + fun getPlayerFromView(view: InventoryView): HumanEntity { + return InventoryViewUtil.getInstance().getPlayer(view) + } + +} \ No newline at end of file From 2d31a7f5a8dac3a618659fec3896bc1ed07813de Mon Sep 17 00:00:00 2001 From: alexcrea Date: Fri, 29 May 2026 03:18:59 +0200 Subject: [PATCH 12/24] seems to work better --- .../cuanvil/dialog/AnvilRenameDialogImpl.kt | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/nms/nms-paper/src/main/kotlin/xyz/alexcrea/cuanvil/dialog/AnvilRenameDialogImpl.kt b/nms/nms-paper/src/main/kotlin/xyz/alexcrea/cuanvil/dialog/AnvilRenameDialogImpl.kt index c6973f1..e210daa 100644 --- a/nms/nms-paper/src/main/kotlin/xyz/alexcrea/cuanvil/dialog/AnvilRenameDialogImpl.kt +++ b/nms/nms-paper/src/main/kotlin/xyz/alexcrea/cuanvil/dialog/AnvilRenameDialogImpl.kt @@ -18,6 +18,7 @@ import org.bukkit.craftbukkit.inventory.CraftInventoryView import org.bukkit.craftbukkit.inventory.view.CraftAnvilView import org.bukkit.entity.HumanEntity import org.bukkit.event.inventory.PrepareAnvilEvent +import org.bukkit.inventory.InventoryView import org.bukkit.inventory.ItemStack import org.bukkit.persistence.PersistentDataType import org.bukkit.plugin.Plugin @@ -49,6 +50,10 @@ class AnvilRenameDialogImpl( ) private val USER_FACING_CONFIRM = Component.text("Confirm").color(TextColor.fromHexString("#40FF40")) private val USER_FACING_CANCEL = Component.text("Cancel").color(TextColor.fromHexString("#FF4040")) + + fun itemDefaultName(item: ItemStack?): String? { + return PLAIN_TEXT_SERIALIZER.serializeOrNull(item?.effectiveName()) + } } private val lastNames = HashMap() @@ -76,7 +81,7 @@ class AnvilRenameDialogImpl( val initialFinal = initial?.take(maxLength) val baseBuilder = DialogBase.builder(USER_FACING_RENAME_TITLE) - .canCloseWithEscape(false) + .canCloseWithEscape(true) .afterAction(DialogBase.DialogAfterAction.CLOSE) .inputs( listOf( @@ -116,18 +121,18 @@ class AnvilRenameDialogImpl( } } - private fun setResult(player: HumanEntity, view: CraftAnvilView, result: String?) { - val defaultName = PLAIN_TEXT_SERIALIZER.serializeOrNull(view.getItem(0)?.effectiveName()) + private fun setResult(player: HumanEntity, view: InventoryView, result: String?) { + val defaultName = itemDefaultName(view.getItem(0)) if (defaultName == result) { setName(player, view, "", null) if (defaultName != null) lastNames[player.uniqueId] = defaultName - } else setName(player, view, lastNames[player.uniqueId], result) + } else setName(player, view, result, result) } - private fun setName(player: HumanEntity, view: CraftAnvilView, name: String?, rename: String?) { + private fun setName(player: HumanEntity, view: InventoryView, name: String?, rename: String?) { val menu = (containerField.get(view) as AnvilMenu) val isSameName = menu.itemName == name - menu.itemName = name + menu.itemName = rename if (name == null) lastNames.remove(player.uniqueId) @@ -180,10 +185,10 @@ class AnvilRenameDialogImpl( return } - if (lastName == renameText) + if (lastName == renameText || lastRename == renameText) return - if (renameText?.isBlank() == true) { + if (renameText?.isBlank() == true || renameText == itemDefaultName(leftItem)) { setName(player, view, lastName, lastRename) return } @@ -215,6 +220,7 @@ class AnvilRenameDialogImpl( override fun closeInventory(player: HumanEntity) { lastNames.remove(player.uniqueId) + lastRenames.remove(player.uniqueId) lastLeftItem.remove(player.uniqueId) runTaskMap.remove(player.uniqueId)?.cancel() } From bf8144ad06146f0983f58771ce165ce3f1ab55a4 Mon Sep 17 00:00:00 2001 From: alexcrea Date: Sat, 30 May 2026 03:53:31 +0200 Subject: [PATCH 13/24] result work for unity repair and custom craft --- .../listener/CATreatAnvilResultEvent.java | 4 +- .../cuanvil/listener/AnvilResultListener.kt | 77 +++++++++++++------ .../xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt | 64 ++++++++------- 3 files changed, 90 insertions(+), 55 deletions(-) diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResultEvent.java b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResultEvent.java index 4d68aca..7bc8fa3 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResultEvent.java +++ b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResultEvent.java @@ -109,7 +109,7 @@ public class CATreatAnvilResultEvent extends Event { */ @Deprecated(forRemoval = true, since = "1.17.0") public int getLevelCost() { - return cost.sum(); + return cost.asXpCost(); } /** @@ -131,7 +131,7 @@ public class CATreatAnvilResultEvent extends Event { */ @Deprecated(forRemoval = true, since = "1.17.0") public void setLevelCost(int levelCost) { - cost.setGeneric(levelCost - cost.getGeneric() - cost.sum()); + cost.setGeneric(levelCost - cost.getGeneric() - cost.asXpCost()); } /** diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt index 1886589..77bca4a 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt @@ -17,6 +17,7 @@ import org.bukkit.inventory.InventoryView import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.BookMeta import xyz.alexcrea.cuanvil.dependency.DependencyManager +import xyz.alexcrea.cuanvil.dependency.economy.EconomyManager import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil.setComponentDisplayName import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_INPUT_LEFT import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_INPUT_RIGHT @@ -32,7 +33,6 @@ import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil import xyz.alexcrea.cuanvil.util.config.LoreEditType import java.util.* -import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicReference import kotlin.math.min @@ -89,6 +89,7 @@ class AnvilResultListener : Listener { // Rename if (rightItem == null) { + // BRUH event.result = Event.Result.ALLOW return } @@ -246,14 +247,13 @@ class AnvilResultListener : Listener { rightItem: ItemStack?, rightRemoveCount: Int, output: ItemStack, - repairCost: Int, + cost: AnvilCost, ): Boolean { // To avoid vanilla, we cancel the event event.result = Event.Result.DENY event.isCancelled = true - // Assumed if player do not have enough xp then it returned MIN_VALUE - if (repairCost == Int.MIN_VALUE) return false + if (!cost.valid) return false // Where should we get the item val slotDestination = getActionSlot(event, player) @@ -261,6 +261,13 @@ class AnvilResultListener : Listener { // If not creative middle click... if (event.click != ClickType.MIDDLE) { + if(cost.isMonetary) { + val result = EconomyManager.economy!!.remove(player, cost.asMonetaryCost()) + if(!result) return false + } else { + player.level -= cost.asXpCost() + } + // We remove what should be removed if (leftItem != null) leftItem.amount -= leftRemoveCount inventory.setItem(ANVIL_INPUT_LEFT, leftItem) @@ -269,7 +276,7 @@ class AnvilResultListener : Listener { inventory.setItem(ANVIL_INPUT_RIGHT, rightItem) inventory.setItem(ANVIL_OUTPUT_SLOT, null) - player.level -= repairCost + } // Finally, we add the item to the player @@ -313,55 +320,75 @@ class AnvilResultListener : Listener { inventory: AnvilInventory, player: Player, leftItem: ItemStack, output: ItemStack, resultCopy: ItemStack, resultAmount: Int - ): Int { - if (player.gameMode == GameMode.CREATIVE) return 0 + ): AnvilCost { + if (player.gameMode == GameMode.CREATIVE) return AnvilCost(0) - var repairCost = 0 + val cost = AnvilCost() // Get repairCost leftItem.itemMeta?.let { leftMeta -> val leftName = leftMeta.displayName output.itemMeta?.let { // Rename cost if (!leftName.contentEquals(it.displayName)) { - repairCost += ConfigOptions.itemRenameCost + cost.rename += ConfigOptions.itemRenameCost // Color cost if (it.displayName.contains('§')) { - repairCost += ConfigOptions.useOfColorCost + cost.rename += ConfigOptions.useOfColorCost } } } } - repairCost += AnvilXpUtil.calculatePenalty(leftItem, null, resultCopy, AnvilUseType.UNIT_REPAIR) - repairCost += resultAmount * ConfigOptions.unitRepairCost + cost.workPenalty = AnvilXpUtil.calculatePenalty(leftItem, null, resultCopy, AnvilUseType.UNIT_REPAIR) + cost.repair = resultAmount * ConfigOptions.unitRepairCost + + var sum = cost.repair if ( !ConfigOptions.doRemoveCostLimit && ConfigOptions.doCapCost ) { - repairCost = min(repairCost, ConfigOptions.maxAnvilCost) + val final = min(sum, ConfigOptions.maxAnvilCost) + cost.generic += (final - sum) + + sum = final } - if ((inventory.maximumRepairCost <= repairCost) - || (player.level < repairCost) - ) return Int.MIN_VALUE + if (ConfigOptions.shouldUseMoney(player)) { + cost.isMonetary = true + if (!EconomyManager.economy!!.has(player, cost.asMonetaryCost())) + cost.valid = false + } else { + if ((inventory.maximumRepairCost <= sum) + || (player.level < sum) + ) cost.valid = false + } - return repairCost + return cost } private fun getFromLoreEditXpCost( cost: AnvilCost, player: Player, inventory: AnvilInventory, - ): Int { - if (GameMode.CREATIVE == player.gameMode) return 0 + ): AnvilCost { + if (GameMode.CREATIVE == player.gameMode) return AnvilCost(0) - val repairCost = cost.sum() - return if ((inventory.maximumRepairCost <= repairCost) - || (player.level < repairCost) - ) Int.MIN_VALUE - else repairCost + if (ConfigOptions.shouldUseMoney(player)) { + cost.isMonetary = true + if (!EconomyManager.economy!!.has(player, cost.asMonetaryCost())) + cost.valid = false + } else { + val repairCost = cost.asXpCost() + + if ((inventory.maximumRepairCost <= repairCost) + || (player.level < repairCost) + ) + cost.valid = false + } + + return cost } private fun handleBookLoreEdit( @@ -414,7 +441,7 @@ class AnvilResultListener : Listener { val bookPage = StringBuilder() lore.forEach { if (bookPage.isNotEmpty()) bookPage.append('\n') - if(it == null) return@forEach + if (it == null) return@forEach bookPage.append(MiniMessageUtil.plain_text_mm.serialize(it)) } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt index a581ed3..cedc07c 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt @@ -29,6 +29,8 @@ object AnvilXpUtil { class AnvilCost { private val isAlone: Boolean + var valid = true // Get set as invalid if cost can be satisfied + var isMonetary = false var generic = 0 var enchantment = 0 @@ -39,10 +41,6 @@ object AnvilXpUtil { var workPenalty = 0 var recipe = 0 - fun sum(): Int { - return generic + enchantment + repair + rename + lore + illegalPenalty + workPenalty + recipe - } - constructor(generic: Int) { this.generic = generic isAlone = true @@ -51,6 +49,24 @@ object AnvilXpUtil { constructor() { isAlone = false } + + fun asXpCost(): Int { + return generic + enchantment + repair + rename + lore + illegalPenalty + workPenalty + recipe + } + + fun asMonetaryCost(): BigDecimal { + // multiply by per use type multipliers + return BigDecimal(generic) + .add(BigDecimal(enchantment).multiply(moneyMultiplier("enchantment"))) + .add(BigDecimal(repair).multiply(moneyMultiplier("repair"))) + .add(BigDecimal(rename).multiply(moneyMultiplier("rename"))) + .add(BigDecimal(lore).multiply(moneyMultiplier("lore_edit"))) + .add(BigDecimal(enchantment).multiply(moneyMultiplier("enchantment"))) + .add(BigDecimal(illegalPenalty).multiply(moneyMultiplier("work_penalty"))) + .add(BigDecimal(workPenalty).multiply(moneyMultiplier("work_penalty"))) + .add(BigDecimal(recipe).multiply(moneyMultiplier("recipe"))) + .multiply(moneyMultiplier("global")) + } } /** @@ -63,10 +79,11 @@ object AnvilXpUtil { cost: AnvilCost, ignoreRules: Boolean = false ) { - if (ConfigOptions.shouldUseMoney(player)) + if (ConfigOptions.shouldUseMoney(player)) { + cost.isMonetary = true setAnvilPrice(inventory, view, player, cost) - else - setAnvilInvXp(inventory, view, player, cost.sum(), ignoreRules) + } else + setAnvilInvXp(inventory, view, player, cost.asXpCost(), ignoreRules) } /** @@ -128,20 +145,6 @@ object AnvilXpUtil { } } - fun asMonetaryCost(cost: AnvilCost): BigDecimal { - // multiply by per use type multipliers - return BigDecimal(cost.generic) - .add(BigDecimal(cost.enchantment).multiply(moneyMultiplier("enchantment"))) - .add(BigDecimal(cost.repair).multiply(moneyMultiplier("repair"))) - .add(BigDecimal(cost.rename).multiply(moneyMultiplier("rename"))) - .add(BigDecimal(cost.lore).multiply(moneyMultiplier("lore_edit"))) - .add(BigDecimal(cost.enchantment).multiply(moneyMultiplier("enchantment"))) - .add(BigDecimal(cost.illegalPenalty).multiply(moneyMultiplier("work_penalty"))) - .add(BigDecimal(cost.workPenalty).multiply(moneyMultiplier("work_penalty"))) - .add(BigDecimal(cost.recipe).multiply(moneyMultiplier("recipe"))) - .multiply(moneyMultiplier("global")) - } - /** * Display monetary cost needed for the work on the anvil inventory */ @@ -151,16 +154,19 @@ object AnvilXpUtil { player: Player, cost: AnvilCost, ) { - val finalCost = asMonetaryCost(cost) + val finalCost = cost.asMonetaryCost() - val has = EconomyManager.economy!!.has(player, finalCost) + val has = player.gameMode == GameMode.CREATIVE || + EconomyManager.economy!!.has(player, finalCost) - val text = "Cost: " + (if(has) "§2" else "§4") + + val text = "Cost: " + (if (has) "§2" else "§4") + EconomyManager.economy!!.format(finalCost) - AnvilTitleUtil.rename(view, text, + AnvilTitleUtil.rename( + view, text, player, AnvilRenameDialogUtil.anvilRenameDialog, - CustomAnvil.instance) + CustomAnvil.instance + ) clearAnvilXpCost(inventory, view, player) } @@ -232,10 +238,12 @@ object AnvilXpUtil { fun onNoResult(player: HumanEntity, view: InventoryView) { if (ConfigOptions.shouldUseMoney(player)) - AnvilTitleUtil.rename(view, "Repair & Name", + AnvilTitleUtil.rename( + view, "Repair & Name", player, AnvilRenameDialogUtil.anvilRenameDialog, - CustomAnvil.instance) + CustomAnvil.instance + ) } private fun exclusivePenaltyKey(useType: AnvilUseType): NamespacedKey { From 7a705f3bfcef7bc648bd56a53aa40060110c3799 Mon Sep 17 00:00:00 2001 From: alexcrea Date: Tue, 2 Jun 2026 13:29:26 +0200 Subject: [PATCH 14/24] move a lot of function to AnvilMergeLogic.kt --- .../listener/CATreatAnvilResultEvent.java | 8 +- .../cuanvil/config/WorkPenaltyType.java | 2 +- .../settings/WorkPenaltyTypeSettingGui.java | 2 +- .../cuanvil/update/plugin/PUpdate_1_8_0.java | 2 +- .../io/delilaheve/util/ConfigOptions.kt | 3 +- .../alexcrea/cuanvil/anvil/AnvilMergeLogic.kt | 271 ++++++++++++++++ .../cuanvil/{util => anvil}/AnvilUseType.kt | 26 +- .../cuanvil/dependency/DependencyManager.kt | 15 +- .../plugins/DisenchantmentDependency.kt | 4 +- .../dependency/plugins/HavenBagsDependency.kt | 4 +- .../cuanvil/listener/AnvilResultListener.kt | 8 +- .../cuanvil/listener/PrepareAnvilListener.kt | 295 +++--------------- .../cuanvil/recipe/AnvilCustomRecipe.kt | 4 +- .../util/{ => anvil}/AnvilColorUtil.kt | 4 +- .../util/{ => anvil}/AnvilLoreEditUtil.kt | 18 +- .../util/{ => anvil}/AnvilUseTypeUtil.kt | 2 +- .../cuanvil/util/{ => anvil}/AnvilXpUtil.kt | 6 +- .../cuanvil/util/config/LoreEditType.kt | 2 +- .../util/dialog/AnvilRenameDialogUtil.kt | 2 +- 19 files changed, 373 insertions(+), 305 deletions(-) create mode 100644 src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt rename src/main/kotlin/xyz/alexcrea/cuanvil/{util => anvil}/AnvilUseType.kt (66%) rename src/main/kotlin/xyz/alexcrea/cuanvil/util/{ => anvil}/AnvilColorUtil.kt (99%) rename src/main/kotlin/xyz/alexcrea/cuanvil/util/{ => anvil}/AnvilLoreEditUtil.kt (96%) rename src/main/kotlin/xyz/alexcrea/cuanvil/util/{ => anvil}/AnvilUseTypeUtil.kt (96%) rename src/main/kotlin/xyz/alexcrea/cuanvil/util/{ => anvil}/AnvilXpUtil.kt (98%) diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResultEvent.java b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResultEvent.java index 7bc8fa3..ddbaf23 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResultEvent.java +++ b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResultEvent.java @@ -6,8 +6,8 @@ import org.bukkit.event.inventory.PrepareAnvilEvent; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import xyz.alexcrea.cuanvil.util.AnvilUseType; -import xyz.alexcrea.cuanvil.util.AnvilXpUtil.AnvilCost; +import xyz.alexcrea.cuanvil.anvil.AnvilUseType; +import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil.AnvilCost; /** * Called after custom anvil processed the click on the result on the anvil inventory. @@ -18,8 +18,12 @@ import xyz.alexcrea.cuanvil.util.AnvilXpUtil.AnvilCost; * and {@link CAEarlyPreAnvilBypassEvent} for your use case *

    * A null result will cancel this pre anvil event + * + * @deprecated Prepare anvil Event should not be provided as it can be called on result and therefor not have prepare anvil event + * TODO a replacement is necessary but not yet made */ @SuppressWarnings("unused") +@Deprecated(forRemoval = true, since = "1.17.0") public class CATreatAnvilResultEvent extends Event { private static final HandlerList HANDLERS = new HandlerList(); diff --git a/src/main/java/xyz/alexcrea/cuanvil/config/WorkPenaltyType.java b/src/main/java/xyz/alexcrea/cuanvil/config/WorkPenaltyType.java index 75b0861..d374999 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/config/WorkPenaltyType.java +++ b/src/main/java/xyz/alexcrea/cuanvil/config/WorkPenaltyType.java @@ -2,7 +2,7 @@ package xyz.alexcrea.cuanvil.config; import com.google.common.collect.ImmutableMap; import org.jetbrains.annotations.Nullable; -import xyz.alexcrea.cuanvil.util.AnvilUseType; +import xyz.alexcrea.cuanvil.anvil.AnvilUseType; import java.util.EnumMap; diff --git a/src/main/java/xyz/alexcrea/cuanvil/gui/config/settings/WorkPenaltyTypeSettingGui.java b/src/main/java/xyz/alexcrea/cuanvil/gui/config/settings/WorkPenaltyTypeSettingGui.java index 888aa25..4345aa1 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/gui/config/settings/WorkPenaltyTypeSettingGui.java +++ b/src/main/java/xyz/alexcrea/cuanvil/gui/config/settings/WorkPenaltyTypeSettingGui.java @@ -11,11 +11,11 @@ import org.bukkit.entity.HumanEntity; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.jetbrains.annotations.NotNull; +import xyz.alexcrea.cuanvil.anvil.AnvilUseType; import xyz.alexcrea.cuanvil.config.ConfigHolder; import xyz.alexcrea.cuanvil.config.WorkPenaltyType; import xyz.alexcrea.cuanvil.gui.config.global.BasicConfigGui; import xyz.alexcrea.cuanvil.gui.util.GuiGlobalActions; -import xyz.alexcrea.cuanvil.util.AnvilUseType; import java.util.ArrayList; import java.util.EnumMap; diff --git a/src/main/java/xyz/alexcrea/cuanvil/update/plugin/PUpdate_1_8_0.java b/src/main/java/xyz/alexcrea/cuanvil/update/plugin/PUpdate_1_8_0.java index 7685f92..81ce1aa 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/update/plugin/PUpdate_1_8_0.java +++ b/src/main/java/xyz/alexcrea/cuanvil/update/plugin/PUpdate_1_8_0.java @@ -2,10 +2,10 @@ package xyz.alexcrea.cuanvil.update.plugin; import io.delilaheve.util.ConfigOptions; import org.bukkit.configuration.file.FileConfiguration; +import xyz.alexcrea.cuanvil.anvil.AnvilUseType; import xyz.alexcrea.cuanvil.config.ConfigHolder; import xyz.alexcrea.cuanvil.config.WorkPenaltyType; import xyz.alexcrea.cuanvil.gui.config.settings.WorkPenaltyTypeSettingGui; -import xyz.alexcrea.cuanvil.util.AnvilUseType; import javax.annotation.Nonnull; import java.util.EnumMap; diff --git a/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt b/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt index b163de8..9dc85f9 100644 --- a/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt +++ b/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt @@ -4,14 +4,13 @@ import io.delilaheve.CustomAnvil import io.delilaheve.util.EnchantmentUtil.enchantmentName import org.bukkit.NamespacedKey import org.bukkit.entity.HumanEntity +import xyz.alexcrea.cuanvil.anvil.AnvilUseType import xyz.alexcrea.cuanvil.config.ConfigHolder import xyz.alexcrea.cuanvil.config.WorkPenaltyType import xyz.alexcrea.cuanvil.config.WorkPenaltyType.WorkPenaltyPart import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.dependency.economy.EconomyManager import xyz.alexcrea.cuanvil.enchant.CAEnchantment -import xyz.alexcrea.cuanvil.update.Version -import xyz.alexcrea.cuanvil.util.AnvilUseType import xyz.alexcrea.cuanvil.util.dialog.AnvilRenameDialogUtil import java.math.BigDecimal import java.util.* diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt new file mode 100644 index 0000000..9840cb8 --- /dev/null +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt @@ -0,0 +1,271 @@ +package xyz.alexcrea.cuanvil.anvil + +import io.delilaheve.CustomAnvil +import io.delilaheve.util.ConfigOptions +import io.delilaheve.util.EnchantmentUtil.combineWith +import io.delilaheve.util.ItemUtil.findEnchantments +import io.delilaheve.util.ItemUtil.isEnchantedBook +import io.delilaheve.util.ItemUtil.repairFrom +import io.delilaheve.util.ItemUtil.setEnchantmentsUnsafe +import io.delilaheve.util.ItemUtil.unitRepair +import org.bukkit.ChatColor +import org.bukkit.Material +import org.bukkit.entity.HumanEntity +import org.bukkit.entity.Player +import org.bukkit.inventory.AnvilInventory +import org.bukkit.inventory.ItemStack +import org.bukkit.inventory.meta.ItemMeta +import org.bukkit.persistence.PersistentDataType +import xyz.alexcrea.cuanvil.dependency.DependencyManager +import xyz.alexcrea.cuanvil.dialog.AnvilRenameDialog +import xyz.alexcrea.cuanvil.enchant.CAEnchantment +import xyz.alexcrea.cuanvil.util.CasedStringUtil +import xyz.alexcrea.cuanvil.util.CustomRecipeUtil +import xyz.alexcrea.cuanvil.util.MaterialUtil.isAir +import xyz.alexcrea.cuanvil.util.MiniMessageUtil +import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair +import xyz.alexcrea.cuanvil.util.anvil.AnvilColorUtil +import xyz.alexcrea.cuanvil.util.anvil.AnvilLoreEditUtil +import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil +import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil.AnvilCost +import xyz.alexcrea.cuanvil.util.dialog.AnvilRenameDialogUtil + +object AnvilMergeUtil { + + open class AnvilResult { + companion object { + val EMPTY = AnvilResult(null, AnvilCost()) + } + + val item: ItemStack? + val cost: AnvilCost + val ignoreXpRules: Boolean + + constructor(item: ItemStack?, cost: AnvilCost, ignoreXpRules: Boolean = false) { + this.item = item + this.cost = cost + this.ignoreXpRules = ignoreXpRules + } + + fun isEmpty(): Boolean { + return item == null + } + } + + class UnitRepairResult: AnvilResult { + companion object { + val EMPTY = UnitRepairResult(null, AnvilCost(), 0) + } + + val repairAmount: Int + + constructor(item: ItemStack?, cost: AnvilCost, repairAmount: Int) : super(item, cost) { + this.repairAmount = repairAmount + } + } + + fun doRenaming(inventory: AnvilInventory, + player: Player, first: ItemStack + ): AnvilResult { + val resultItem = DependencyManager.cloneItem(player, first) + val cost = AnvilCost() + cost.rename = handleRename(resultItem, inventory, player) + + // Test/stop if nothing changed. + if (first == resultItem) { + CustomAnvil.log("no right item, But input is same as output") + return AnvilResult.EMPTY + } + + cost.workPenalty = AnvilXpUtil.calculatePenalty(first, null, resultItem, AnvilUseType.RENAME_ONLY) + val result = DependencyManager.tryTreatAnvilResult(resultItem, AnvilUseType.RENAME_ONLY, cost) + + return AnvilResult(result, cost) + } + + private fun processDialogPCD(it: ItemMeta, player: HumanEntity) { + val keepDialog = ConfigOptions.canUseDialogRename(player) && ConfigOptions.shouldKeepRenameText + + val pdc = it.persistentDataContainer + if(!keepDialog) + pdc.remove(AnvilRenameDialog.PCD_KEEP_RENAME_TEXT_KEY) + else { + val text = AnvilRenameDialogUtil.anvilRenameDialog.currentText(player) + if(text == null || text.isBlank()) + pdc.remove(AnvilRenameDialog.PCD_KEEP_RENAME_TEXT_KEY) + else pdc.set(AnvilRenameDialog.PCD_KEEP_RENAME_TEXT_KEY, PersistentDataType.STRING, text) + } + } + + private fun handleRename(resultItem: ItemStack, inventory: AnvilInventory, player: HumanEntity): Int { + // Can be null + var renameText = ChatColor.stripColor(inventory.renameText) + + var sumCost = 0 + var useColor = false + if (ConfigOptions.renameColorPossible && renameText != null) { + val component = AnvilColorUtil.handleColor( + renameText, + AnvilColorUtil.renamePermission(player)) + + if (component != null) { + renameText = MiniMessageUtil.legacy_mm.serialize(component) + + sumCost += ConfigOptions.useOfColorCost + useColor = true + } + } + + // Rename item and add renaming cost + resultItem.itemMeta?.let { + val hasDisplayName = it.hasDisplayName() + val displayName = if (!hasDisplayName) null + else if (useColor) it.displayName + else ChatColor.stripColor(it.displayName) + + + if (!displayName.contentEquals(renameText) && !(displayName == null && + renameText == "" || + //TODO on recent paper check effective name instead + renameText == CasedStringUtil.snakeToUpperSpacedCase(resultItem.type.name.lowercase()) + )) { + it.setDisplayName(renameText) + processDialogPCD(it, player) + resultItem.itemMeta = it + + sumCost += ConfigOptions.itemRenameCost + } + + return sumCost + } + return 0 + } + + fun doMerge( + inventory: AnvilInventory, + player: Player, + first: ItemStack, second: ItemStack + ): AnvilResult { + val newEnchants = first.findEnchantments() + .combineWith(second.findEnchantments(), first, player) + var hasChanged = !isIdentical(first.findEnchantments(), newEnchants) + + val resultItem = DependencyManager.cloneItem(player, first) + val cost = AnvilCost() + if(hasChanged){ + resultItem.setEnchantmentsUnsafe(newEnchants) + // Calculate enchantment cost + AnvilXpUtil.getRightValues(second, resultItem, cost) + } + + // Calculate repair cost + if (!first.isEnchantedBook() && !second.isEnchantedBook()) { + // we only need to be concerned with repair when neither item is a book + val repaired = resultItem.repairFrom(first, second) + cost.repair = if (repaired) ConfigOptions.itemRepairCost else 0 + hasChanged = hasChanged || repaired + } + + // Test/stop if nothing changed. + if (!hasChanged) { + CustomAnvil.log("Mergeable with second, But input is same as output") + return AnvilResult.EMPTY + } + // As calculatePenalty edit result, we need to calculate penalty after checking equality + cost.workPenalty = AnvilXpUtil.calculatePenalty(first, second, resultItem, AnvilUseType.MERGE) + // Calculate rename cost + cost.rename = handleRename(resultItem, inventory, player) + + val result = DependencyManager.tryTreatAnvilResult(resultItem, AnvilUseType.MERGE, cost) + + return AnvilResult(result, cost) + } + + private fun isIdentical( + firstEnchants: MutableMap, + resultEnchants: MutableMap + ): Boolean { + if(firstEnchants.size != resultEnchants.size) return false + for (entry in resultEnchants) { + if(firstEnchants.getOrDefault(entry.key, entry.value-1) != entry.value) return false + } + + return true + } + + // return true if a custom recipe exist with these ingredients + fun testCustomRecipe( + player: Player, + first: ItemStack, second: ItemStack? + ): AnvilResult { + val recipe = CustomRecipeUtil.getCustomRecipe(first, second) + CustomAnvil.verboseLog("custom recipe not null? ${recipe != null}") + if (recipe == null) return AnvilResult.EMPTY + + val amount = CustomRecipeUtil.getCustomRecipeAmount(recipe, first, second) + + val resultItem: ItemStack = DependencyManager.cloneItem(player, recipe.resultItem!!) + resultItem.amount *= amount + + // Maybe add an option on custom craft to ignore/not ignore penalty ?? + val xpCost = recipe.determineCost(amount, first, resultItem) + + val cost = AnvilCost() + cost.recipe = if (recipe.removeExactLinearXp) AnvilXpUtil.calculateMinimumLevelForXp(xpCost) + else AnvilXpUtil.calculateLevelForXp(xpCost) + + val result = DependencyManager.tryTreatAnvilResult(resultItem, AnvilUseType.CUSTOM_CRAFT, cost) + return AnvilResult(result, cost, true) + } + + fun testUnitRepair( + inventory: AnvilInventory, + player: Player, + first: ItemStack, second: ItemStack + ): UnitRepairResult { + val unitRepairAmount = first.getRepair(second) ?: return UnitRepairResult.EMPTY + + val resultItem = DependencyManager.cloneItem(player, first) + val cost = AnvilCost() + cost.rename = handleRename(resultItem, inventory, player) + + val repairAmount = resultItem.unitRepair(second.amount, unitRepairAmount) + if (repairAmount > 0) + cost.repair = repairAmount * ConfigOptions.unitRepairCost + + // We do not care about right item penalty for unit repair + cost.workPenalty = AnvilXpUtil.calculatePenalty(first, null, resultItem, AnvilUseType.UNIT_REPAIR) + + // Test/stop if nothing changed. + if (first == resultItem) { + CustomAnvil.log("unit repair, But input is same as output") + return UnitRepairResult.EMPTY + } + + val result = DependencyManager.tryTreatAnvilResult(resultItem, AnvilUseType.UNIT_REPAIR, cost) + return UnitRepairResult(result, cost, repairAmount) + } + + fun testLoreEdit( + player: Player, + first: ItemStack, second: ItemStack + ): AnvilResult { + val type = second.type + var resultItem: ItemStack? = null + + val cost = AnvilCost() + if (Material.WRITABLE_BOOK == type) { + resultItem = AnvilLoreEditUtil.tryLoreEditByBook(player, first, second, cost) + } else if (Material.PAPER == type) { + resultItem = AnvilLoreEditUtil.tryLoreEditByPaper(player, first, second, cost) + } + + if (resultItem.isAir || first == resultItem) { + CustomAnvil.log("lore edit, But input is same as output") + return AnvilResult.EMPTY + } + + return AnvilResult(resultItem, cost) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilUseType.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilUseType.kt similarity index 66% rename from src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilUseType.kt rename to src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilUseType.kt index d17e908..67782f3 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilUseType.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilUseType.kt @@ -1,60 +1,60 @@ -package xyz.alexcrea.cuanvil.util +package xyz.alexcrea.cuanvil.anvil import org.bukkit.Material -import xyz.alexcrea.cuanvil.config.WorkPenaltyType.WorkPenaltyPart -import xyz.alexcrea.cuanvil.util.config.LoreEditType +import xyz.alexcrea.cuanvil.config.WorkPenaltyType +import xyz.alexcrea.cuanvil.util.anvil.AnvilUseTypeUtil enum class AnvilUseType( val typeName: String, val path: String, - val defaultPenalty: WorkPenaltyPart, + val defaultPenalty: WorkPenaltyType.WorkPenaltyPart, val displayName: String, val displayMat: Material ) { RENAME_ONLY( "rename_only", - WorkPenaltyPart(false, true), + WorkPenaltyType.WorkPenaltyPart(false, true), "Rename Only", Material.NAME_TAG ), MERGE( "merge", - WorkPenaltyPart(true, true), + WorkPenaltyType.WorkPenaltyPart(true, true), "Merge", Material.ANVIL ), UNIT_REPAIR( "unit_repair", - WorkPenaltyPart(true, true), + WorkPenaltyType.WorkPenaltyPart(true, true), "Unit Repair", Material.DIAMOND ), CUSTOM_CRAFT( "custom_craft", - WorkPenaltyPart(false, false), + WorkPenaltyType.WorkPenaltyPart(false, false), "Custom Craft", Material.CRAFTING_TABLE ), LORE_EDIT_BOOK_APPEND( "lore_edit_book_append", "lore_edit.book_and_quil.append", - WorkPenaltyPart(false, false), + WorkPenaltyType.WorkPenaltyPart(false, false), "Book Add", Material.WRITABLE_BOOK ), LORE_EDIT_BOOK_REMOVE( "lore_edit_book_remove", "lore_edit.book_and_quil.remove", - WorkPenaltyPart(false, false), + WorkPenaltyType.WorkPenaltyPart(false, false), "Book Remove", Material.WRITABLE_BOOK ), LORE_EDIT_PAPER_APPEND( "lore_edit_paper_append", "lore_edit.paper.append_line", - WorkPenaltyPart(false, false), + WorkPenaltyType.WorkPenaltyPart(false, false), "Paper Add", Material.WRITABLE_BOOK ), LORE_EDIT_PAPER_REMOVE( "lore_edit_paper_remove", "lore_edit.paper.remove_line", - WorkPenaltyPart(false, false), + WorkPenaltyType.WorkPenaltyPart(false, false), "Paper Remove", Material.WRITABLE_BOOK ), ; constructor( typeName: String, - defaultPenalty: WorkPenaltyPart, + defaultPenalty: WorkPenaltyType.WorkPenaltyPart, displayName: String, displayMat: Material ) : this( diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt index 418dbf1..4ad6c9b 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt @@ -13,6 +13,7 @@ import org.bukkit.event.inventory.PrepareAnvilEvent import org.bukkit.inventory.AnvilInventory import org.bukkit.inventory.Inventory import org.bukkit.inventory.ItemStack +import xyz.alexcrea.cuanvil.anvil.AnvilUseType import xyz.alexcrea.cuanvil.api.event.listener.CAClickResultBypassEvent import xyz.alexcrea.cuanvil.api.event.listener.CAEarlyPreAnvilBypassEvent import xyz.alexcrea.cuanvil.api.event.listener.CAPreAnvilBypassEvent @@ -30,9 +31,8 @@ import xyz.alexcrea.cuanvil.dependency.scheduler.TaskScheduler import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil.componentLore import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_OUTPUT_SLOT -import xyz.alexcrea.cuanvil.util.AnvilUseType -import xyz.alexcrea.cuanvil.util.AnvilXpUtil import xyz.alexcrea.cuanvil.util.MetricsUtil.trackError +import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil import java.util.logging.Level object DependencyManager { @@ -234,19 +234,20 @@ object DependencyManager { // Return null if there was an issue fun tryTreatAnvilResult( - event: PrepareAnvilEvent, result: ItemStack, useType: AnvilUseType, cost: AnvilXpUtil.AnvilCost ): ItemStack? { - val treatEvent = CATreatAnvilResultEvent(event, useType, result, cost) + //TODO + /*val treatEvent = CATreatAnvilResultEvent(event, useType, result, cost) try { unsafeTryTreatAnvilResult(treatEvent) return treatEvent.result } catch (e: Exception) { logExceptionAndClear(event.view.player, event.inventory, e) return null - } + }*/ + return result } private fun unsafeTryTreatAnvilResult(event: CATreatAnvilResultEvent) { @@ -295,11 +296,11 @@ object DependencyManager { } // Clone item and use plugin specific clone if needed - fun cloneItem(event: PrepareAnvilEvent, item: ItemStack): ItemStack { + fun cloneItem(player: HumanEntity, item: ItemStack): ItemStack { try { return unsafeCloneItem(item) } catch (e: Exception) { - logException(event.view.player, e) + logException(player, e) return item.clone() } } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/DisenchantmentDependency.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/DisenchantmentDependency.kt index 7a7a5eb..32ca99d 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/DisenchantmentDependency.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/DisenchantmentDependency.kt @@ -15,9 +15,9 @@ import org.bukkit.event.inventory.PrepareAnvilEvent import org.bukkit.inventory.AnvilInventory import org.bukkit.inventory.ItemStack import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener -import xyz.alexcrea.cuanvil.util.AnvilXpUtil -import xyz.alexcrea.cuanvil.util.AnvilXpUtil.AnvilCost import xyz.alexcrea.cuanvil.util.MetricsUtil.trackError +import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil +import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil.AnvilCost import java.util.logging.Level import kotlin.reflect.KClass diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/HavenBagsDependency.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/HavenBagsDependency.kt index b2e7ef4..62d9e4e 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/HavenBagsDependency.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/HavenBagsDependency.kt @@ -10,8 +10,8 @@ import valorless.havenbags.HavenBags import valorless.havenbags.features.BagSkin import valorless.havenbags.features.BagUpgrade import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener -import xyz.alexcrea.cuanvil.util.AnvilXpUtil -import xyz.alexcrea.cuanvil.util.AnvilXpUtil.AnvilCost +import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil +import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil.AnvilCost class HavenBagsDependency { diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt index 77bca4a..7497e7f 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt @@ -16,6 +16,7 @@ import org.bukkit.inventory.AnvilInventory import org.bukkit.inventory.InventoryView import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.BookMeta +import xyz.alexcrea.cuanvil.anvil.AnvilUseType import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.dependency.economy.EconomyManager import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil.setComponentDisplayName @@ -23,13 +24,12 @@ import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_INPUT_ import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_INPUT_RIGHT import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_OUTPUT_SLOT import xyz.alexcrea.cuanvil.recipe.AnvilCustomRecipe -import xyz.alexcrea.cuanvil.util.AnvilLoreEditUtil -import xyz.alexcrea.cuanvil.util.AnvilUseType -import xyz.alexcrea.cuanvil.util.AnvilXpUtil -import xyz.alexcrea.cuanvil.util.AnvilXpUtil.AnvilCost import xyz.alexcrea.cuanvil.util.CustomRecipeUtil import xyz.alexcrea.cuanvil.util.MiniMessageUtil import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair +import xyz.alexcrea.cuanvil.util.anvil.AnvilLoreEditUtil +import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil +import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil.AnvilCost import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil import xyz.alexcrea.cuanvil.util.config.LoreEditType import java.util.* diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt index ec5cac2..312baba 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt @@ -1,18 +1,8 @@ package xyz.alexcrea.cuanvil.listener -import com.github.stefvanschie.inventoryframework.util.InventoryViewUtil -import com.jankominek.disenchantment.utils.AnvilCostUtils import io.delilaheve.CustomAnvil import io.delilaheve.util.ConfigOptions -import io.delilaheve.util.EnchantmentUtil.combineWith import io.delilaheve.util.ItemUtil.canMergeWith -import io.delilaheve.util.ItemUtil.findEnchantments -import io.delilaheve.util.ItemUtil.isEnchantedBook -import io.delilaheve.util.ItemUtil.repairFrom -import io.delilaheve.util.ItemUtil.setEnchantmentsUnsafe -import io.delilaheve.util.ItemUtil.unitRepair -import org.bukkit.ChatColor -import org.bukkit.Material import org.bukkit.entity.HumanEntity import org.bukkit.entity.Player import org.bukkit.event.EventHandler @@ -20,18 +10,20 @@ import org.bukkit.event.EventPriority import org.bukkit.event.Listener import org.bukkit.event.inventory.PrepareAnvilEvent import org.bukkit.inventory.AnvilInventory -import org.bukkit.inventory.InventoryView import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.EnchantmentStorageMeta import org.bukkit.inventory.meta.ItemMeta -import org.bukkit.persistence.PersistentDataType +import xyz.alexcrea.cuanvil.anvil.AnvilMergeUtil.AnvilResult +import xyz.alexcrea.cuanvil.anvil.AnvilMergeUtil.doMerge +import xyz.alexcrea.cuanvil.anvil.AnvilMergeUtil.doRenaming +import xyz.alexcrea.cuanvil.anvil.AnvilMergeUtil.testCustomRecipe +import xyz.alexcrea.cuanvil.anvil.AnvilMergeUtil.testLoreEdit +import xyz.alexcrea.cuanvil.anvil.AnvilMergeUtil.testUnitRepair import xyz.alexcrea.cuanvil.dependency.DependencyManager -import xyz.alexcrea.cuanvil.dialog.AnvilRenameDialog -import xyz.alexcrea.cuanvil.enchant.CAEnchantment -import xyz.alexcrea.cuanvil.util.* -import xyz.alexcrea.cuanvil.util.AnvilXpUtil.AnvilCost +import xyz.alexcrea.cuanvil.util.JustForEasierHotswapUtil import xyz.alexcrea.cuanvil.util.MaterialUtil.isAir -import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair +import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil +import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil.AnvilCost import xyz.alexcrea.cuanvil.util.dialog.AnvilRenameDialogUtil /** @@ -73,8 +65,8 @@ class PrepareAnvilListener : Listener { val second = inventory.getItem(ANVIL_INPUT_RIGHT) if(IS_EMPTY_TEST) { - setNoResult(event, player, view) IS_EMPTY_TEST = false + applyResult(event, player, AnvilResult.EMPTY) return } @@ -86,7 +78,7 @@ class PrepareAnvilListener : Listener { if (isImmutable(first) || isImmutable(second)) { CustomAnvil.verboseLog("Skipping anvil process as one of the two item is immutable") - setNoResult(event, player, view) + applyResult(event, player, AnvilResult.EMPTY) return } @@ -99,42 +91,44 @@ class PrepareAnvilListener : Listener { if (!player.hasPermission(CustomAnvil.affectedByPluginPermission)) return - if(first == null) { - setNoResult(event, player, view) - return - } + val result = getResult(inventory, player, first, second) + applyResult(event, player, result) + } + + fun getResult( + inventory: AnvilInventory, + player: Player, + first: ItemStack?, second: ItemStack?) : AnvilResult + { + if(first == null) + return AnvilResult.EMPTY // Test custom recipe - if (testCustomRecipe(event, inventory, player, first, second)) return + var result = testCustomRecipe(player, first, second) + if (!result.isEmpty()) + return result // Test rename lonely item val shouldTryRename = second.isAir CustomAnvil.verboseLog("checking air in main logic: $shouldTryRename") - if (shouldTryRename) { - if(!doRenaming(event, inventory, player, first)) - setNoResult(event, player, view) - return - } + if (shouldTryRename) + return doRenaming(inventory, player, first) // Test for merge - if (first.canMergeWith(second!!)) { - if(!doMerge(event, inventory, player, first, second)) - setNoResult(event, player, view) - return - } + if (first.canMergeWith(second!!)) + return doMerge(inventory, player, first, second) // Test for unit repair - if (testUnitRepair(event, inventory, player, first, second)) return + result = testUnitRepair(inventory, player, first, second) + if (!result.isEmpty()) + return result // Test for lore edit - if (testLoreEdit(event, inventory, player, first, second)) return + result = testLoreEdit(player, first, second) + if (!result.isEmpty()) + return result - setNoResult(event, player, view) - } - - private fun setNoResult(event: PrepareAnvilEvent, player: Player, view: InventoryView) { - event.result = null - AnvilXpUtil.onNoResult(player, view) + return AnvilResult.EMPTY } private fun tryRenameDialog( @@ -146,20 +140,6 @@ class PrepareAnvilListener : Listener { AnvilRenameDialogUtil.anvilRenameDialog.tryShowDialog(player, event) } - private fun processDialogPCD(it: ItemMeta, player: HumanEntity) { - val keepDialog = ConfigOptions.canUseDialogRename(player) && ConfigOptions.shouldKeepRenameText - - val pdc = it.persistentDataContainer - if(!keepDialog) - pdc.remove(AnvilRenameDialog.PCD_KEEP_RENAME_TEXT_KEY) - else { - val text = AnvilRenameDialogUtil.anvilRenameDialog.currentText(player) - if(text == null || text.isBlank()) - pdc.remove(AnvilRenameDialog.PCD_KEEP_RENAME_TEXT_KEY) - else pdc.set(AnvilRenameDialog.PCD_KEEP_RENAME_TEXT_KEY, PersistentDataType.STRING, text) - } - } - private fun isImmutable(item: ItemStack?): Boolean { if (item.isAir) return false @@ -186,203 +166,14 @@ class PrepareAnvilListener : Listener { return false } - // return true if a custom recipe exist with these ingredients - private fun testCustomRecipe( - event: PrepareAnvilEvent, inventory: AnvilInventory, - player: Player, - first: ItemStack, second: ItemStack? - ): Boolean { - val recipe = CustomRecipeUtil.getCustomRecipe(first, second) - CustomAnvil.verboseLog("custom recipe not null? ${recipe != null}") - if (recipe == null) return false + private fun applyResult(event: PrepareAnvilEvent, player: Player, result: AnvilResult) { + event.result = result.item - val amount = CustomRecipeUtil.getCustomRecipeAmount(recipe, first, second) - - val resultItem: ItemStack = DependencyManager.cloneItem(event, recipe.resultItem!!) - resultItem.amount *= amount - - // Maybe add an option on custom craft to ignore/not ignore penalty ?? - val xpCost = recipe.determineCost(amount, first, resultItem) - - val cost = AnvilCost() - cost.recipe = if (recipe.removeExactLinearXp) AnvilXpUtil.calculateMinimumLevelForXp(xpCost) - else AnvilXpUtil.calculateLevelForXp(xpCost) - - event.result = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.CUSTOM_CRAFT, cost) - AnvilXpUtil.setAnvilInvCost(inventory, event.view, player, cost, true) - return true + if(result.item == null) { + AnvilXpUtil.onNoResult(player, event.view) + return + } + AnvilXpUtil.setAnvilInvCost(event.inventory, event.view, player, result.cost, result.ignoreXpRules) } - private fun doRenaming( - event: PrepareAnvilEvent, inventory: AnvilInventory, - player: Player, first: ItemStack - ): Boolean { - val resultItem = DependencyManager.cloneItem(event, first) - val cost = AnvilCost() - cost.rename = handleRename(resultItem, inventory, player) - - // Test/stop if nothing changed. - if (first == resultItem) { - CustomAnvil.log("no right item, But input is same as output") - return false - } - - cost.workPenalty = AnvilXpUtil.calculatePenalty(first, null, resultItem, AnvilUseType.RENAME_ONLY) - - event.result = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.RENAME_ONLY, cost) - AnvilXpUtil.setAnvilInvCost(inventory, event.view, player, cost) - return true - } - - private fun handleRename(resultItem: ItemStack, inventory: AnvilInventory, player: HumanEntity): Int { - // Can be null - var renameText = ChatColor.stripColor(inventory.renameText) - - var sumCost = 0 - var useColor = false - if (ConfigOptions.renameColorPossible && renameText != null) { - val component = AnvilColorUtil.handleColor( - renameText, - AnvilColorUtil.renamePermission(player)) - - if (component != null) { - renameText = MiniMessageUtil.legacy_mm.serialize(component) - - sumCost += ConfigOptions.useOfColorCost - useColor = true - } - } - - // Rename item and add renaming cost - resultItem.itemMeta?.let { - val hasDisplayName = it.hasDisplayName() - val displayName = if (!hasDisplayName) null - else if (useColor) it.displayName - else ChatColor.stripColor(it.displayName) - - - if (!displayName.contentEquals(renameText) && !(displayName == null && - renameText == "" || - //TODO on recent paper check effective name instead - renameText == CasedStringUtil.snakeToUpperSpacedCase(resultItem.type.name.lowercase()) - )) { - it.setDisplayName(renameText) - processDialogPCD(it, player) - resultItem.itemMeta = it - - sumCost += ConfigOptions.itemRenameCost - } - - return sumCost - } - return 0 - } - - private fun doMerge( - event: PrepareAnvilEvent, inventory: AnvilInventory, - player: Player, - first: ItemStack, second: ItemStack - ): Boolean { - val newEnchants = first.findEnchantments() - .combineWith(second.findEnchantments(), first, player) - var hasChanged = !isIdentical(first.findEnchantments(), newEnchants) - - val resultItem = DependencyManager.cloneItem(event, first) - val cost = AnvilCost() - if(hasChanged){ - resultItem.setEnchantmentsUnsafe(newEnchants) - // Calculate enchantment cost - AnvilXpUtil.getRightValues(second, resultItem, cost) - } - - // Calculate repair cost - if (!first.isEnchantedBook() && !second.isEnchantedBook()) { - // we only need to be concerned with repair when neither item is a book - val repaired = resultItem.repairFrom(first, second) - cost.repair = if (repaired) ConfigOptions.itemRepairCost else 0 - hasChanged = hasChanged || repaired - } - - // Test/stop if nothing changed. - if (!hasChanged) { - CustomAnvil.log("Mergeable with second, But input is same as output") - return false - } - // As calculatePenalty edit result, we need to calculate penalty after checking equality - cost.workPenalty = AnvilXpUtil.calculatePenalty(first, second, resultItem, AnvilUseType.MERGE) - // Calculate rename cost - cost.rename = handleRename(resultItem, inventory, player) - - // Finally, we set result - event.result = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.MERGE, cost) - AnvilXpUtil.setAnvilInvCost(inventory, event.view, player, cost) - return true - } - - private fun isIdentical( - firstEnchants: MutableMap, - resultEnchants: MutableMap - ): Boolean { - if(firstEnchants.size != resultEnchants.size) return false - for (entry in resultEnchants) { - if(firstEnchants.getOrDefault(entry.key, entry.value-1) != entry.value) return false - } - - return true - } - - // return true if there is a valid unit repair with these ingredients - private fun testUnitRepair( - event: PrepareAnvilEvent, inventory: AnvilInventory, player: Player, - first: ItemStack, second: ItemStack - ): Boolean { - val unitRepairAmount = first.getRepair(second) ?: return false - - val resultItem = DependencyManager.cloneItem(event, first) - val cost = AnvilCost() - cost.rename = handleRename(resultItem, inventory, player) - - val repairAmount = resultItem.unitRepair(second.amount, unitRepairAmount) - if (repairAmount > 0) { - cost.repair = repairAmount * ConfigOptions.unitRepairCost - } - // We do not care about right item penalty for unit repair - cost.workPenalty = AnvilXpUtil.calculatePenalty(first, null, resultItem, AnvilUseType.UNIT_REPAIR) - - // Test/stop if nothing changed. - if (first == resultItem) { - CustomAnvil.log("unit repair, But input is same as output") - event.result = null - return true - } - - event.result = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.UNIT_REPAIR, cost) - AnvilXpUtil.setAnvilInvCost(inventory, event.view, player, cost) - return true - } - - private fun testLoreEdit( - event: PrepareAnvilEvent, inventory: AnvilInventory, player: Player, - first: ItemStack, second: ItemStack - ): Boolean { - val type = second.type - var result: ItemStack? = null - - val cost = AnvilCost() - if (Material.WRITABLE_BOOK == type) { - result = AnvilLoreEditUtil.tryLoreEditByBook(player, first, second, cost) - } else if (Material.PAPER == type) { - result = AnvilLoreEditUtil.tryLoreEditByPaper(player, first, second, cost) - } - - if (result.isAir || first == result) { - CustomAnvil.log("lore edit, But input is same as output") - event.result = null - return false - } - - event.result = result - AnvilXpUtil.setAnvilInvCost(inventory, event.view, player, cost) - return true - } } \ No newline at end of file diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/recipe/AnvilCustomRecipe.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/recipe/AnvilCustomRecipe.kt index 57eb124..fd079c3 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/recipe/AnvilCustomRecipe.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/recipe/AnvilCustomRecipe.kt @@ -3,11 +3,11 @@ package xyz.alexcrea.cuanvil.recipe import io.delilaheve.CustomAnvil import org.bukkit.configuration.ConfigurationSection import org.bukkit.inventory.ItemStack +import xyz.alexcrea.cuanvil.anvil.AnvilUseType import xyz.alexcrea.cuanvil.config.ConfigHolder import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant -import xyz.alexcrea.cuanvil.util.AnvilUseType -import xyz.alexcrea.cuanvil.util.AnvilXpUtil import xyz.alexcrea.cuanvil.util.MaterialUtil.isAir +import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil class AnvilCustomRecipe( val name: String, diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilColorUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilColorUtil.kt similarity index 99% rename from src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilColorUtil.kt rename to src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilColorUtil.kt index 0492d32..9564b2e 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilColorUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilColorUtil.kt @@ -1,11 +1,11 @@ -package xyz.alexcrea.cuanvil.util +package xyz.alexcrea.cuanvil.util.anvil import io.delilaheve.util.ConfigOptions import net.kyori.adventure.text.Component import org.bukkit.permissions.Permissible +import xyz.alexcrea.cuanvil.util.MiniMessageUtil import java.util.regex.Matcher import java.util.regex.Pattern -import kotlin.text.indexOf object AnvilColorUtil { private val HEX_PATTERN: Pattern = Pattern.compile("#[A-Fa-f0-9]{6}") // pattern to find hexadecimal string diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilLoreEditUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilLoreEditUtil.kt similarity index 96% rename from src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilLoreEditUtil.kt rename to src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilLoreEditUtil.kt index 16eca40..edb4a77 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilLoreEditUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilLoreEditUtil.kt @@ -1,4 +1,4 @@ -package xyz.alexcrea.cuanvil.util +package xyz.alexcrea.cuanvil.util.anvil import net.kyori.adventure.text.Component import org.bukkit.entity.HumanEntity @@ -8,7 +8,7 @@ import org.bukkit.permissions.Permissible import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil.componentLore import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil.setComponentLore -import xyz.alexcrea.cuanvil.util.AnvilXpUtil.AnvilCost +import xyz.alexcrea.cuanvil.util.MiniMessageUtil import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil import xyz.alexcrea.cuanvil.util.config.LoreEditType import java.util.* @@ -31,7 +31,7 @@ object AnvilLoreEditUtil { player: Permissible, first: ItemStack, book: BookMeta, - cost: AnvilCost + cost: AnvilXpUtil.AnvilCost ): ItemStack? { if (!hasLoreEditByBookPermission(player)) return null @@ -60,7 +60,7 @@ object AnvilLoreEditUtil { return result } - fun handleLoreRemoveByBook(player: Permissible, first: ItemStack, cost: AnvilCost): ItemStack? { + fun handleLoreRemoveByBook(player: Permissible, first: ItemStack, cost: AnvilXpUtil.AnvilCost): ItemStack? { if (!hasLoreEditByBookPermission(player)) return null // remove lore @@ -116,7 +116,7 @@ object AnvilLoreEditUtil { return null } - fun tryLoreEditByBook(player: HumanEntity, first: ItemStack, second: ItemStack, cost: AnvilCost): ItemStack? { + fun tryLoreEditByBook(player: HumanEntity, first: ItemStack, second: ItemStack, cost: AnvilXpUtil.AnvilCost): ItemStack? { val isAppend = bookLoreEditIsAppend(first, second) ?: return null val meta = second.itemMeta as BookMeta @@ -147,7 +147,7 @@ object AnvilLoreEditUtil { player: Permissible, first: ItemStack, second: ItemStack, - cost: AnvilCost + cost: AnvilXpUtil.AnvilCost ): ItemStack? { if (!hasLoreEditByPaperPermission(player)) return null @@ -181,7 +181,7 @@ object AnvilLoreEditUtil { return result } - fun handleLoreRemoveByPaper(player: Permissible, first: ItemStack, cost: AnvilCost): ItemStack? { + fun handleLoreRemoveByPaper(player: Permissible, first: ItemStack, cost: AnvilXpUtil.AnvilCost): ItemStack? { if (!hasLoreEditByPaperPermission(player)) return null // remove lore line @@ -223,7 +223,7 @@ object AnvilLoreEditUtil { player: HumanEntity, first: ItemStack, second: ItemStack, - cost: AnvilCost + cost: AnvilXpUtil.AnvilCost ): ItemStack? { val isAppend = paperLoreEditIsAppend(first, second) ?: return null @@ -232,7 +232,7 @@ object AnvilLoreEditUtil { } private fun baseEditLoreXpCost( - cost: AnvilCost, + cost: AnvilXpUtil.AnvilCost, first: ItemStack, result: ItemStack, editType: LoreEditType diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilUseTypeUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilUseTypeUtil.kt similarity index 96% rename from src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilUseTypeUtil.kt rename to src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilUseTypeUtil.kt index c72a35a..a9c7f39 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilUseTypeUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilUseTypeUtil.kt @@ -1,4 +1,4 @@ -package xyz.alexcrea.cuanvil.util +package xyz.alexcrea.cuanvil.util.anvil import io.delilaheve.util.ConfigOptions diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilXpUtil.kt similarity index 98% rename from src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt rename to src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilXpUtil.kt index cedc07c..0f94661 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilXpUtil.kt @@ -1,8 +1,7 @@ -package xyz.alexcrea.cuanvil.util +package xyz.alexcrea.cuanvil.util.anvil import io.delilaheve.CustomAnvil import io.delilaheve.util.ConfigOptions -import io.delilaheve.util.ConfigOptions.getMonetaryMultiplier as moneyMultiplier import io.delilaheve.util.EnchantmentUtil.enchantmentName import io.delilaheve.util.ItemUtil.findEnchantments import io.delilaheve.util.ItemUtil.isEnchantedBook @@ -15,13 +14,16 @@ import org.bukkit.inventory.InventoryView import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.Repairable import org.bukkit.persistence.PersistentDataType +import xyz.alexcrea.cuanvil.anvil.AnvilUseType import xyz.alexcrea.cuanvil.config.ConfigHolder import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.dependency.economy.EconomyManager import xyz.alexcrea.cuanvil.group.ConflictType +import xyz.alexcrea.cuanvil.util.AnvilTitleUtil import xyz.alexcrea.cuanvil.util.dialog.AnvilRenameDialogUtil import java.math.BigDecimal import kotlin.math.min +import io.delilaheve.util.ConfigOptions.getMonetaryMultiplier as moneyMultiplier object AnvilXpUtil { diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/config/LoreEditType.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/config/LoreEditType.kt index 8bd926a..8dab543 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/config/LoreEditType.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/config/LoreEditType.kt @@ -1,6 +1,6 @@ package xyz.alexcrea.cuanvil.util.config -import xyz.alexcrea.cuanvil.util.AnvilUseType +import xyz.alexcrea.cuanvil.anvil.AnvilUseType import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil.ALLOW_COLOR_CODE import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil.ALLOW_HEX_COLOR import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil.ALLOW_MINIMESSAGE diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/dialog/AnvilRenameDialogUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/dialog/AnvilRenameDialogUtil.kt index a83d8b0..07d4e08 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/dialog/AnvilRenameDialogUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/dialog/AnvilRenameDialogUtil.kt @@ -8,7 +8,7 @@ import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil import xyz.alexcrea.cuanvil.dialog.AnvilRenameDialog import xyz.alexcrea.cuanvil.dialog.AnvilRenameDialogImpl import xyz.alexcrea.cuanvil.update.UpdateUtils -import xyz.alexcrea.cuanvil.util.AnvilColorUtil +import xyz.alexcrea.cuanvil.util.anvil.AnvilColorUtil object AnvilRenameDialogUtil { From e6293be1c68dc93a86f6afa689669a9b638245ff Mon Sep 17 00:00:00 2001 From: alexcrea Date: Tue, 2 Jun 2026 14:01:49 +0200 Subject: [PATCH 15/24] deduplicate unit repair logic --- .../alexcrea/cuanvil/anvil/AnvilMergeLogic.kt | 9 ++ .../cuanvil/listener/AnvilResultListener.kt | 108 ++++++++---------- 2 files changed, 57 insertions(+), 60 deletions(-) diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt index 9840cb8..ce0fe0d 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt @@ -225,6 +225,15 @@ object AnvilMergeUtil { ): UnitRepairResult { val unitRepairAmount = first.getRepair(second) ?: return UnitRepairResult.EMPTY + return testUnitRepair(inventory, player, first, second, unitRepairAmount) + } + + fun testUnitRepair( + inventory: AnvilInventory, + player: Player, + first: ItemStack, second: ItemStack, + unitRepairAmount: Double + ): UnitRepairResult { val resultItem = DependencyManager.cloneItem(player, first) val cost = AnvilCost() cost.rename = handleRename(resultItem, inventory, player) diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt index 7497e7f..2235183 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt @@ -3,7 +3,6 @@ package xyz.alexcrea.cuanvil.listener import io.delilaheve.CustomAnvil import io.delilaheve.util.ConfigOptions import io.delilaheve.util.ItemUtil.canMergeWith -import io.delilaheve.util.ItemUtil.unitRepair import org.bukkit.GameMode import org.bukkit.Material import org.bukkit.entity.Player @@ -16,7 +15,8 @@ import org.bukkit.inventory.AnvilInventory import org.bukkit.inventory.InventoryView import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.BookMeta -import xyz.alexcrea.cuanvil.anvil.AnvilUseType +import xyz.alexcrea.cuanvil.anvil.AnvilMergeUtil +import xyz.alexcrea.cuanvil.anvil.AnvilMergeUtil.AnvilResult import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.dependency.economy.EconomyManager import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil.setComponentDisplayName @@ -65,8 +65,9 @@ class AnvilResultListener : Listener { val leftItem = inventory.getItem(ANVIL_INPUT_LEFT) ?: return val rightItem = inventory.getItem(ANVIL_INPUT_RIGHT) + // Deny by default. allow if working + event.result = Event.Result.DENY if (GameMode.CREATIVE != player.gameMode && inventory.repairCost >= inventory.maximumRepairCost) { - event.result = Event.Result.DENY return } @@ -83,7 +84,6 @@ class AnvilResultListener : Listener { // Do not continue if there was no change if ((output == inventory.getItem(ANVIL_INPUT_LEFT))) { - event.result = Event.Result.DENY return } @@ -102,10 +102,10 @@ class AnvilResultListener : Listener { } // Unit repair - val unitRepairResult = leftItem.getRepair(rightItem) + val unitRepairResult = leftItem.getRepair(rightItem) // Maybe this should be handlded "above" and like prepare result if (unitRepairResult != null) { onUnitRepairExtract( - leftItem, rightItem, output, + leftItem, rightItem, unitRepairResult, event, player, inventory ) return @@ -238,6 +238,25 @@ class AnvilResultListener : Listener { return true } + // I don't know about side effect of "handling cost" at this level and not before so let be safe + private fun extractAnvilResult( + event: InventoryClickEvent, + player: Player, + inventory: AnvilInventory, + leftItem: ItemStack?, + leftRemoveCount: Int, + rightItem: ItemStack?, + rightRemoveCount: Int, + result: AnvilResult + ): Boolean { + if(result.isEmpty()) return false + + processCost(inventory, player, result.cost) + + return extractAnvilResult(event, player, inventory, leftItem, leftRemoveCount, rightItem, + rightRemoveCount, result.item!!, result.cost) + } + private fun extractAnvilResult( event: InventoryClickEvent, player: Player, @@ -290,59 +309,7 @@ class AnvilResultListener : Listener { return true } - private fun onUnitRepairExtract( - leftItem: ItemStack, - rightItem: ItemStack, - output: ItemStack, - unitRepairResult: Double, - event: InventoryClickEvent, - player: Player, - inventory: AnvilInventory - ) { - val resultCopy = leftItem.clone() - val resultAmount = resultCopy.unitRepair( - rightItem.amount, unitRepairResult - ) - - // Get repair cost - val repairCost = getUnitRepairCost(inventory, player, leftItem, output, resultCopy, resultAmount) - - // And then we give the item manually - extractAnvilResult( - event, player, inventory, - null, 0, - rightItem, resultAmount, - resultCopy, repairCost - ) - } - - private fun getUnitRepairCost( - inventory: AnvilInventory, player: Player, - leftItem: ItemStack, output: ItemStack, - resultCopy: ItemStack, resultAmount: Int - ): AnvilCost { - if (player.gameMode == GameMode.CREATIVE) return AnvilCost(0) - - val cost = AnvilCost() - // Get repairCost - leftItem.itemMeta?.let { leftMeta -> - val leftName = leftMeta.displayName - output.itemMeta?.let { - // Rename cost - if (!leftName.contentEquals(it.displayName)) { - cost.rename += ConfigOptions.itemRenameCost - - // Color cost - if (it.displayName.contains('§')) { - cost.rename += ConfigOptions.useOfColorCost - } - } - } - } - - cost.workPenalty = AnvilXpUtil.calculatePenalty(leftItem, null, resultCopy, AnvilUseType.UNIT_REPAIR) - cost.repair = resultAmount * ConfigOptions.unitRepairCost - + private fun processCost(inventory: AnvilInventory, player: Player, cost: AnvilCost) { var sum = cost.repair if ( @@ -364,8 +331,29 @@ class AnvilResultListener : Listener { || (player.level < sum) ) cost.valid = false } + } - return cost + private fun onUnitRepairExtract( + leftItem: ItemStack, + rightItem: ItemStack, + unitRepairResult: Double, + event: InventoryClickEvent, + player: Player, + inventory: AnvilInventory + ) { + val result = AnvilMergeUtil.testUnitRepair(inventory, player, + leftItem.clone(), rightItem, + unitRepairResult) + + if(result.isEmpty()) return + + // And then we give the item manually + extractAnvilResult( + event, player, inventory, + null, 0, + rightItem, result.repairAmount, + result + ) } private fun getFromLoreEditXpCost( From 106bc724a13f350f87aeccbbba9478bd039b84d9 Mon Sep 17 00:00:00 2001 From: alexcrea Date: Tue, 2 Jun 2026 14:36:09 +0200 Subject: [PATCH 16/24] deduplicate lore edit logic --- .../alexcrea/cuanvil/anvil/AnvilMergeLogic.kt | 38 +- .../cuanvil/listener/AnvilResultListener.kt | 328 +++++++++--------- .../cuanvil/listener/PrepareAnvilListener.kt | 12 +- .../cuanvil/util/anvil/AnvilLoreEditUtil.kt | 37 +- .../cuanvil/util/config/LoreEditType.kt | 13 +- 5 files changed, 230 insertions(+), 198 deletions(-) diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt index ce0fe0d..b7d8bdb 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt @@ -28,9 +28,10 @@ import xyz.alexcrea.cuanvil.util.anvil.AnvilColorUtil import xyz.alexcrea.cuanvil.util.anvil.AnvilLoreEditUtil import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil.AnvilCost +import xyz.alexcrea.cuanvil.util.config.LoreEditType import xyz.alexcrea.cuanvil.util.dialog.AnvilRenameDialogUtil -object AnvilMergeUtil { +object AnvilMergeLogic { open class AnvilResult { companion object { @@ -64,6 +65,19 @@ object AnvilMergeUtil { } } + + class LoreEditResult: AnvilResult { + companion object { + val EMPTY = LoreEditResult(null, AnvilCost(), LoreEditType.APPEND_PAPER) + } + + val type: LoreEditType + + constructor(item: ItemStack?, cost: AnvilCost, type: LoreEditType) : super(item, cost) { + this.type = type + } + } + fun doRenaming(inventory: AnvilInventory, player: Player, first: ItemStack ): AnvilResult { @@ -258,23 +272,23 @@ object AnvilMergeUtil { fun testLoreEdit( player: Player, first: ItemStack, second: ItemStack - ): AnvilResult { + ): LoreEditResult { val type = second.type - var resultItem: ItemStack? = null - val cost = AnvilCost() - if (Material.WRITABLE_BOOK == type) { - resultItem = AnvilLoreEditUtil.tryLoreEditByBook(player, first, second, cost) - } else if (Material.PAPER == type) { - resultItem = AnvilLoreEditUtil.tryLoreEditByPaper(player, first, second, cost) - } + val result = if (Material.WRITABLE_BOOK == type) + AnvilLoreEditUtil.tryLoreEditByBook(player, first, second) + else if (Material.PAPER == type) + AnvilLoreEditUtil.tryLoreEditByPaper(player, first, second) + else LoreEditResult.EMPTY - if (resultItem.isAir || first == resultItem) { + if(result.isEmpty()) return result + + if (result.item!!.isAir || first == result.item) { CustomAnvil.log("lore edit, But input is same as output") - return AnvilResult.EMPTY + return LoreEditResult.EMPTY } - return AnvilResult(resultItem, cost) + return result } } \ No newline at end of file diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt index 2235183..3e0371a 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt @@ -15,8 +15,9 @@ import org.bukkit.inventory.AnvilInventory import org.bukkit.inventory.InventoryView import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.BookMeta -import xyz.alexcrea.cuanvil.anvil.AnvilMergeUtil -import xyz.alexcrea.cuanvil.anvil.AnvilMergeUtil.AnvilResult +import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic +import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.AnvilResult +import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.LoreEditResult import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.dependency.economy.EconomyManager import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil.setComponentDisplayName @@ -112,14 +113,14 @@ class AnvilResultListener : Listener { } // For lore edit - if (handleBookLoreEdit(event, inventory, player, leftItem, rightItem, output)) { - return - } else if (handlePaperLoreEdit(event, inventory, player, leftItem, rightItem, output)) { + val loreResult = AnvilMergeLogic.testLoreEdit(player, leftItem, rightItem) + if(!loreResult.isEmpty()) { + if(loreResult.type.isBook) + handleBookLoreEdit(event, inventory, player, leftItem, rightItem, loreResult) + else + handlePaperLoreEdit(event, inventory, player, leftItem, rightItem, loreResult) return } - - // Else there was no working situation somehow so we deny - event.result = Event.Result.DENY } private fun onCustomCraft( @@ -341,7 +342,7 @@ class AnvilResultListener : Listener { player: Player, inventory: AnvilInventory ) { - val result = AnvilMergeUtil.testUnitRepair(inventory, player, + val result = AnvilMergeLogic.testUnitRepair(inventory, player, leftItem.clone(), rightItem, unitRepairResult) @@ -356,99 +357,90 @@ class AnvilResultListener : Listener { ) } - private fun getFromLoreEditXpCost( - cost: AnvilCost, - player: Player, - inventory: AnvilInventory, - ): AnvilCost { - if (GameMode.CREATIVE == player.gameMode) return AnvilCost(0) - - if (ConfigOptions.shouldUseMoney(player)) { - cost.isMonetary = true - if (!EconomyManager.economy!!.has(player, cost.asMonetaryCost())) - cost.valid = false - } else { - val repairCost = cost.asXpCost() - - if ((inventory.maximumRepairCost <= repairCost) - || (player.level < repairCost) - ) - cost.valid = false - } - - return cost - } - private fun handleBookLoreEdit( event: InventoryClickEvent, inventory: AnvilInventory, player: Player, leftItem: ItemStack, rightItem: ItemStack, - output: ItemStack, - ): Boolean { - if (Material.WRITABLE_BOOK != rightItem.type) return false - val bookMeta = rightItem.itemMeta as BookMeta? ?: return false + result: LoreEditResult + ) { + if (result.type.isAppend) + handleBookLoreAppend(event, inventory, player, rightItem, result) + else + handleBookLoreRemove(event, inventory, player, leftItem, rightItem, result) + } - val editType = AnvilLoreEditUtil.bookLoreEditIsAppend(leftItem, rightItem) ?: return false + private fun handleBookLoreAppend( + event: InventoryClickEvent, + inventory: AnvilInventory, + player: Player, + rightItem: ItemStack, + result: LoreEditResult + ) { + val bookMeta = rightItem.itemMeta as BookMeta? ?: return - val cost = AnvilCost() - if (editType) { - if (output != AnvilLoreEditUtil.handleLoreAppendByBook(player, leftItem, bookMeta, cost)) return false - - // Remove pages to book - val clearedBook: ItemStack? - if (LoreEditType.APPEND_BOOK.doConsume) { - clearedBook = null - } else { - clearedBook = rightItem.clone() - bookMeta.pages = Collections.emptyList() - clearedBook.itemMeta = bookMeta - } - - return extractAnvilResult( - event, player, inventory, - null, 0, - clearedBook, 0, - output, getFromLoreEditXpCost(cost, player, inventory) - ) + // Remove pages to book + val clearedBook: ItemStack? + if (LoreEditType.APPEND_BOOK.doConsume) { + clearedBook = null } else { - if (output != AnvilLoreEditUtil.handleLoreRemoveByBook(player, leftItem, cost)) return false + clearedBook = rightItem.clone() + bookMeta.pages = Collections.emptyList() + clearedBook.itemMeta = bookMeta + } - // fill book meta - val lore = DependencyManager.stripLore(leftItem) - if (lore.isEmpty()) return false + extractAnvilResult( + event, player, inventory, + null, 0, + clearedBook, 0, + result + ) + } - val rightCopy: ItemStack? - if (LoreEditType.REMOVE_BOOK.doConsume) { - rightCopy = null - } else { - // Uncolor the page - AnvilLoreEditUtil.uncolorLines(player, lore, LoreEditType.REMOVE_BOOK) + private fun handleBookLoreRemove( + event: InventoryClickEvent, + inventory: AnvilInventory, + player: Player, + leftItem: ItemStack, + rightItem: ItemStack, + result: LoreEditResult + ){ + val bookMeta = rightItem.itemMeta as BookMeta? ?: return - val bookPage = StringBuilder() - lore.forEach { - if (bookPage.isNotEmpty()) bookPage.append('\n') - if (it == null) return@forEach + // fill book meta + val lore = DependencyManager.stripLore(leftItem) + if (lore.isEmpty()) return - bookPage.append(MiniMessageUtil.plain_text_mm.serialize(it)) - } + val rightCopy: ItemStack? + if (LoreEditType.REMOVE_BOOK.doConsume) { + rightCopy = null + } else { + // Uncolor the page + AnvilLoreEditUtil.uncolorLines(player, lore, LoreEditType.REMOVE_BOOK) - val resultPage = bookPage.toString() - //TODO maybe check page size ? bc it may be too big ??? + val bookPage = StringBuilder() + lore.forEach { + if (bookPage.isNotEmpty()) bookPage.append('\n') + if (it == null) return@forEach - rightCopy = rightItem.clone() - bookMeta.setPages(resultPage) - rightCopy.itemMeta = bookMeta + bookPage.append(MiniMessageUtil.plain_text_mm.serialize(it)) } - return extractAnvilResult( - event, player, inventory, - null, 0, - rightCopy, 0, - output, getFromLoreEditXpCost(cost, player, inventory) - ) + val resultPage = bookPage.toString() + //TODO maybe check page size ? bc it may be too big ??? + + rightCopy = rightItem.clone() + bookMeta.setPages(resultPage) + rightCopy.itemMeta = bookMeta } + + extractAnvilResult( + event, player, inventory, + null, 0, + rightCopy, 0, + result + ) } private fun handlePaperLoreEdit( @@ -457,89 +449,101 @@ class AnvilResultListener : Listener { player: Player, leftItem: ItemStack, rightItem: ItemStack, - output: ItemStack, - ): Boolean { - if (Material.PAPER != rightItem.type) return false - val paperMeta = rightItem.itemMeta ?: return false + result: LoreEditResult + ) { + if (result.type.isAppend) + handlePaperLoreAppend(event, inventory, player, rightItem, result) + else + handlePaperLoreRemove(event, inventory, player, leftItem, rightItem, result) + } - val editTypeIsAppend = AnvilLoreEditUtil.paperLoreEditIsAppend(leftItem, rightItem) ?: return false + private fun handlePaperLoreAppend( + event: InventoryClickEvent, + inventory: AnvilInventory, + player: Player, + rightItem: ItemStack, + result: LoreEditResult + ) { + val paperMeta = rightItem.itemMeta ?: return - val cost = AnvilCost() - if (editTypeIsAppend) { - if (output != AnvilLoreEditUtil.handleLoreAppendByPaper(player, leftItem, rightItem, cost)) return false - - val paperCopy: ItemStack? - if (LoreEditType.APPEND_PAPER.doConsume) { - paperCopy = null - } else { - // Remove custom name to paper - paperCopy = rightItem.clone() - paperCopy.amount = 1 - paperMeta.setComponentDisplayName(null) - paperCopy.itemMeta = paperMeta - } - - return if (rightItem.amount > 1) { - extractAnvilResult( - event, player, inventory, - paperCopy, 0, - rightItem, 1, - output, getFromLoreEditXpCost(cost, player, inventory) - ) - } else { - extractAnvilResult( - event, player, inventory, - null, 0, - paperCopy, 0, - output, getFromLoreEditXpCost(cost, player, inventory) - ) - } + val paperCopy: ItemStack? + if (LoreEditType.APPEND_PAPER.doConsume) { + paperCopy = null } else { - if (output != AnvilLoreEditUtil.handleLoreRemoveByPaper(player, leftItem, cost)) return false - - val leftMeta = leftItem.itemMeta - if (leftMeta == null || !leftMeta.hasLore()) return false - val lore = DependencyManager.stripLore(leftItem) - if (lore.isEmpty()) return false - - // Create result item - val rightClone: ItemStack? - if (LoreEditType.REMOVE_PAPER.doConsume) { - rightClone = null - } else { - val removeEnd = LoreEditConfigUtil.paperLoreOrderIsEnd - val line = if (removeEnd) lore[lore.size - 1] - else lore[0] - - // uncolor the line - val ref = AtomicReference(line) - AnvilLoreEditUtil.uncolorLine(player, ref, LoreEditType.REMOVE_PAPER) - - rightClone = rightItem.clone() - rightClone.amount = 1 - - val resultMeta = rightClone.itemMeta ?: return false - resultMeta.setComponentDisplayName(ref.get()) - rightClone.itemMeta = resultMeta - } - - return if (rightItem.amount > 1) { - extractAnvilResult( - event, player, inventory, - rightClone, 0, - rightItem, 1, - output, getFromLoreEditXpCost(cost, player, inventory) - ) - } else { - extractAnvilResult( - event, player, inventory, - null, 0, - rightClone, 0, - output, getFromLoreEditXpCost(cost, player, inventory) - ) - } + // Remove custom name to paper + paperCopy = rightItem.clone() + paperCopy.amount = 1 + paperMeta.setComponentDisplayName(null) + paperCopy.itemMeta = paperMeta } + if (rightItem.amount > 1) { + extractAnvilResult( + event, player, inventory, + paperCopy, 0, + rightItem, 1, + result + ) + } else { + extractAnvilResult( + event, player, inventory, + null, 0, + paperCopy, 0, + result + ) + } + } + + private fun handlePaperLoreRemove( + event: InventoryClickEvent, + inventory: AnvilInventory, + player: Player, + leftItem: ItemStack, + rightItem: ItemStack, + result: LoreEditResult + ) { + val leftMeta = leftItem.itemMeta + if (leftMeta == null || !leftMeta.hasLore()) return + + val lore = DependencyManager.stripLore(leftItem) + if (lore.isEmpty()) return + + // Create result item + val rightClone: ItemStack? + if (LoreEditType.REMOVE_PAPER.doConsume) { + rightClone = null + } else { + val removeEnd = LoreEditConfigUtil.paperLoreOrderIsEnd + val line = if (removeEnd) lore[lore.size - 1] + else lore[0] + + // uncolor the line + val ref = AtomicReference(line) + AnvilLoreEditUtil.uncolorLine(player, ref, LoreEditType.REMOVE_PAPER) + + rightClone = rightItem.clone() + rightClone.amount = 1 + + val resultMeta = rightClone.itemMeta ?: return + resultMeta.setComponentDisplayName(ref.get()) + rightClone.itemMeta = resultMeta + } + + if (rightItem.amount > 1) { + extractAnvilResult( + event, player, inventory, + rightClone, 0, + rightItem, 1, + result + ) + } else { + extractAnvilResult( + event, player, inventory, + null, 0, + rightClone, 0, + result + ) + } } /** diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt index 312baba..0c7121f 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt @@ -13,12 +13,12 @@ import org.bukkit.inventory.AnvilInventory import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.EnchantmentStorageMeta import org.bukkit.inventory.meta.ItemMeta -import xyz.alexcrea.cuanvil.anvil.AnvilMergeUtil.AnvilResult -import xyz.alexcrea.cuanvil.anvil.AnvilMergeUtil.doMerge -import xyz.alexcrea.cuanvil.anvil.AnvilMergeUtil.doRenaming -import xyz.alexcrea.cuanvil.anvil.AnvilMergeUtil.testCustomRecipe -import xyz.alexcrea.cuanvil.anvil.AnvilMergeUtil.testLoreEdit -import xyz.alexcrea.cuanvil.anvil.AnvilMergeUtil.testUnitRepair +import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.AnvilResult +import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.doMerge +import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.doRenaming +import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.testCustomRecipe +import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.testLoreEdit +import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.testUnitRepair import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.util.JustForEasierHotswapUtil import xyz.alexcrea.cuanvil.util.MaterialUtil.isAir diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilLoreEditUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilLoreEditUtil.kt index edb4a77..e9bb633 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilLoreEditUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilLoreEditUtil.kt @@ -5,10 +5,12 @@ import org.bukkit.entity.HumanEntity import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.BookMeta import org.bukkit.permissions.Permissible +import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.LoreEditResult import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil.componentLore import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil.setComponentLore import xyz.alexcrea.cuanvil.util.MiniMessageUtil +import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil.AnvilCost import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil import xyz.alexcrea.cuanvil.util.config.LoreEditType import java.util.* @@ -31,7 +33,7 @@ object AnvilLoreEditUtil { player: Permissible, first: ItemStack, book: BookMeta, - cost: AnvilXpUtil.AnvilCost + cost: AnvilCost ): ItemStack? { if (!hasLoreEditByBookPermission(player)) return null @@ -60,7 +62,7 @@ object AnvilLoreEditUtil { return result } - fun handleLoreRemoveByBook(player: Permissible, first: ItemStack, cost: AnvilXpUtil.AnvilCost): ItemStack? { + fun handleLoreRemoveByBook(player: Permissible, first: ItemStack, cost: AnvilCost): ItemStack? { if (!hasLoreEditByBookPermission(player)) return null // remove lore @@ -116,12 +118,17 @@ object AnvilLoreEditUtil { return null } - fun tryLoreEditByBook(player: HumanEntity, first: ItemStack, second: ItemStack, cost: AnvilXpUtil.AnvilCost): ItemStack? { - val isAppend = bookLoreEditIsAppend(first, second) ?: return null + fun tryLoreEditByBook(player: HumanEntity, first: ItemStack, second: ItemStack): LoreEditResult { + val isAppend = bookLoreEditIsAppend(first, second) ?: return LoreEditResult.EMPTY + val type = if(isAppend) LoreEditType.APPEND_BOOK else LoreEditType.REMOVE_BOOK val meta = second.itemMeta as BookMeta - return if (isAppend) handleLoreAppendByBook(player, first, meta, cost) + val cost = AnvilCost() + val item = if (isAppend) + handleLoreAppendByBook(player, first, meta, cost) else handleLoreRemoveByBook(player, first, cost) + + return LoreEditResult(item, cost, type) } // Return true if appended, false if removed, null if neither @@ -147,7 +154,7 @@ object AnvilLoreEditUtil { player: Permissible, first: ItemStack, second: ItemStack, - cost: AnvilXpUtil.AnvilCost + cost: AnvilCost ): ItemStack? { if (!hasLoreEditByPaperPermission(player)) return null @@ -181,7 +188,7 @@ object AnvilLoreEditUtil { return result } - fun handleLoreRemoveByPaper(player: Permissible, first: ItemStack, cost: AnvilXpUtil.AnvilCost): ItemStack? { + fun handleLoreRemoveByPaper(player: Permissible, first: ItemStack, cost: AnvilCost): ItemStack? { if (!hasLoreEditByPaperPermission(player)) return null // remove lore line @@ -222,17 +229,21 @@ object AnvilLoreEditUtil { fun tryLoreEditByPaper( player: HumanEntity, first: ItemStack, - second: ItemStack, - cost: AnvilXpUtil.AnvilCost - ): ItemStack? { - val isAppend = paperLoreEditIsAppend(first, second) ?: return null + second: ItemStack + ): LoreEditResult { + val isAppend = paperLoreEditIsAppend(first, second) ?: return LoreEditResult.EMPTY + val type = if(isAppend) LoreEditType.APPEND_BOOK else LoreEditType.REMOVE_BOOK - return if (isAppend) handleLoreAppendByPaper(player, first, second, cost) + val cost = AnvilCost() + val item = if (isAppend) + handleLoreAppendByPaper(player, first, second, cost) else handleLoreRemoveByPaper(player, first, cost) + + return LoreEditResult(item, cost, type) } private fun baseEditLoreXpCost( - cost: AnvilXpUtil.AnvilCost, + cost: AnvilCost, first: ItemStack, result: ItemStack, editType: LoreEditType diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/config/LoreEditType.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/config/LoreEditType.kt index 8dab543..c094c71 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/config/LoreEditType.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/config/LoreEditType.kt @@ -18,20 +18,23 @@ import xyz.alexcrea.cuanvil.config.ConfigHolder.DEFAULT_CONFIG as CONFIG enum class LoreEditType( val rootPath: String, val useType: AnvilUseType, + val isBook: Boolean, val isAppend: Boolean, val isMultiLine: Boolean, ) { - APPEND_BOOK(AnvilUseType.LORE_EDIT_BOOK_APPEND, true, true), - REMOVE_BOOK(AnvilUseType.LORE_EDIT_BOOK_REMOVE, false, true), - APPEND_PAPER(AnvilUseType.LORE_EDIT_PAPER_APPEND, true, false), - REMOVE_PAPER(AnvilUseType.LORE_EDIT_PAPER_REMOVE, false, false), + APPEND_BOOK(AnvilUseType.LORE_EDIT_BOOK_APPEND, true, true, true), + REMOVE_BOOK(AnvilUseType.LORE_EDIT_BOOK_REMOVE, true, false, true), + APPEND_PAPER(AnvilUseType.LORE_EDIT_PAPER_APPEND, false, true, false), + REMOVE_PAPER(AnvilUseType.LORE_EDIT_PAPER_REMOVE, false, false, false), ; constructor( useType: AnvilUseType, + isPaper: Boolean, isAppend: Boolean, isMultiLine: Boolean, - ) : this(useType.path, useType, isAppend, isMultiLine) + ) : this(useType.path, useType, + isPaper, isAppend, isMultiLine) /** * If this edit type is enabled From edceba879fc34f733cd4c61eeb5b0f0f4bedf618 Mon Sep 17 00:00:00 2001 From: alexcrea Date: Tue, 2 Jun 2026 16:18:16 +0200 Subject: [PATCH 17/24] custom craft logic deduplication --- .../alexcrea/cuanvil/anvil/AnvilMergeLogic.kt | 27 ++++++++-- .../cuanvil/listener/AnvilResultListener.kt | 54 ++++++++----------- .../cuanvil/listener/PrepareAnvilListener.kt | 2 +- .../cuanvil/util/anvil/AnvilXpUtil.kt | 4 +- .../cuanvil/util/AnvilFuseTestUtil.java | 4 +- 5 files changed, 51 insertions(+), 40 deletions(-) diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt index b7d8bdb..8699285 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt @@ -19,6 +19,7 @@ import org.bukkit.persistence.PersistentDataType import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.dialog.AnvilRenameDialog import xyz.alexcrea.cuanvil.enchant.CAEnchantment +import xyz.alexcrea.cuanvil.recipe.AnvilCustomRecipe import xyz.alexcrea.cuanvil.util.CasedStringUtil import xyz.alexcrea.cuanvil.util.CustomRecipeUtil import xyz.alexcrea.cuanvil.util.MaterialUtil.isAir @@ -28,6 +29,7 @@ import xyz.alexcrea.cuanvil.util.anvil.AnvilColorUtil import xyz.alexcrea.cuanvil.util.anvil.AnvilLoreEditUtil import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil.AnvilCost +import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil.CustomCraftCost import xyz.alexcrea.cuanvil.util.config.LoreEditType import xyz.alexcrea.cuanvil.util.dialog.AnvilRenameDialogUtil @@ -65,6 +67,22 @@ object AnvilMergeLogic { } } + class CustomCraftResult: AnvilResult { + companion object { + val EMPTY = CustomCraftResult(null, CustomCraftCost(0), 0, null) + } + + val customCraftCost: CustomCraftCost + val amount: Int + val recipe: AnvilCustomRecipe? + + constructor(item: ItemStack?, cost: CustomCraftCost, + amount: Int, recipe: AnvilCustomRecipe?) : super(item, cost, true) { + this.customCraftCost = cost + this.amount = amount + this.recipe = recipe + } + } class LoreEditResult: AnvilResult { companion object { @@ -211,10 +229,10 @@ object AnvilMergeLogic { fun testCustomRecipe( player: Player, first: ItemStack, second: ItemStack? - ): AnvilResult { + ): CustomCraftResult { val recipe = CustomRecipeUtil.getCustomRecipe(first, second) CustomAnvil.verboseLog("custom recipe not null? ${recipe != null}") - if (recipe == null) return AnvilResult.EMPTY + if (recipe == null) return CustomCraftResult.EMPTY val amount = CustomRecipeUtil.getCustomRecipeAmount(recipe, first, second) @@ -224,12 +242,13 @@ object AnvilMergeLogic { // Maybe add an option on custom craft to ignore/not ignore penalty ?? val xpCost = recipe.determineCost(amount, first, resultItem) - val cost = AnvilCost() + val cost = CustomCraftCost(xpCost) + // This is for displayed cost cost.recipe = if (recipe.removeExactLinearXp) AnvilXpUtil.calculateMinimumLevelForXp(xpCost) else AnvilXpUtil.calculateLevelForXp(xpCost) val result = DependencyManager.tryTreatAnvilResult(resultItem, AnvilUseType.CUSTOM_CRAFT, cost) - return AnvilResult(result, cost, true) + return CustomCraftResult(result, cost, amount, recipe) } fun testUnitRepair( diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt index 3e0371a..d63e2a6 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt @@ -17,7 +17,9 @@ import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.BookMeta import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.AnvilResult +import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.CustomCraftResult import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.LoreEditResult +import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.UnitRepairResult import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.dependency.economy.EconomyManager import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil.setComponentDisplayName @@ -27,7 +29,6 @@ import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_OUTPUT import xyz.alexcrea.cuanvil.recipe.AnvilCustomRecipe import xyz.alexcrea.cuanvil.util.CustomRecipeUtil import xyz.alexcrea.cuanvil.util.MiniMessageUtil -import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair import xyz.alexcrea.cuanvil.util.anvil.AnvilLoreEditUtil import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil.AnvilCost @@ -73,12 +74,11 @@ class AnvilResultListener : Listener { } // Test custom recipe - val recipe = CustomRecipeUtil.getCustomRecipe(leftItem, rightItem) - if (recipe != null) { - event.result = Event.Result.ALLOW + val customRecipeResult = AnvilMergeLogic.testCustomRecipe(player, leftItem, rightItem) + if (!customRecipeResult.isEmpty()) { onCustomCraft( - event, recipe, player, - leftItem, rightItem, output, inventory + event, player, inventory, + leftItem, rightItem, customRecipeResult ) return } @@ -103,11 +103,13 @@ class AnvilResultListener : Listener { } // Unit repair - val unitRepairResult = leftItem.getRepair(rightItem) // Maybe this should be handlded "above" and like prepare result - if (unitRepairResult != null) { + val unitRepairResult = AnvilMergeLogic.testUnitRepair( + inventory, player, + leftItem, rightItem) + if (unitRepairResult.isEmpty()) { onUnitRepairExtract( - leftItem, rightItem, - unitRepairResult, event, player, inventory + rightItem, event, player, inventory, + unitRepairResult ) return } @@ -125,19 +127,14 @@ class AnvilResultListener : Listener { private fun onCustomCraft( event: InventoryClickEvent, - recipe: AnvilCustomRecipe, player: Player, + inventory: AnvilInventory, leftItem: ItemStack, rightItem: ItemStack?, - output: ItemStack, - inventory: AnvilInventory + result: CustomCraftResult, ) { - event.result = Event.Result.DENY - - if (recipe.leftItem == null) return // in case it changed - - val amount = CustomRecipeUtil.getCustomRecipeAmount(recipe, leftItem, rightItem) - val xpCost = recipe.determineCost(amount, leftItem, output) + val recipe = result.recipe!! + val xpCost = result.customCraftCost.rawCost val finalCost = if (recipe.removeExactLinearXp) xpCost else AnvilXpUtil.calculateLevelForXp(xpCost) @@ -166,7 +163,7 @@ class AnvilResultListener : Listener { player, leftItem, rightItem, - amount, + result.amount, finalCost, recipe.removeExactLinearXp ) @@ -174,9 +171,9 @@ class AnvilResultListener : Listener { // Finally, we add the item to the player if (slotDestination.type == SlotType.CURSOR) { - player.setItemOnCursor(output) + player.setItemOnCursor(result.item) } else {// We assume SlotType == SlotType.INVENTORY - player.inventory.setItem(slotDestination.slot, output) + player.inventory.setItem(slotDestination.slot, result.item) } } @@ -335,20 +332,13 @@ class AnvilResultListener : Listener { } private fun onUnitRepairExtract( - leftItem: ItemStack, rightItem: ItemStack, - unitRepairResult: Double, event: InventoryClickEvent, player: Player, - inventory: AnvilInventory + inventory: AnvilInventory, + result: UnitRepairResult, ) { - val result = AnvilMergeLogic.testUnitRepair(inventory, player, - leftItem.clone(), rightItem, - unitRepairResult) - - if(result.isEmpty()) return - - // And then we give the item manually + // We give the item manually extractAnvilResult( event, player, inventory, null, 0, diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt index 0c7121f..00a2053 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt @@ -104,7 +104,7 @@ class PrepareAnvilListener : Listener { return AnvilResult.EMPTY // Test custom recipe - var result = testCustomRecipe(player, first, second) + var result: AnvilResult = testCustomRecipe(player, first, second) if (!result.isEmpty()) return result diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilXpUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilXpUtil.kt index 0f94661..997b260 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilXpUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilXpUtil.kt @@ -29,7 +29,7 @@ object AnvilXpUtil { const val EXCLUSIVE_PENALTY_PREFIX = "repair_cost" - class AnvilCost { + open class AnvilCost { private val isAlone: Boolean var valid = true // Get set as invalid if cost can be satisfied var isMonetary = false @@ -71,6 +71,8 @@ object AnvilXpUtil { } } + class CustomCraftCost(val rawCost: Int): AnvilCost() + /** * Display the required cost (either as xp or as ) */ diff --git a/src/test/java/xyz/alexcrea/cuanvil/util/AnvilFuseTestUtil.java b/src/test/java/xyz/alexcrea/cuanvil/util/AnvilFuseTestUtil.java index 1d3c5f4..6f5c7bb 100644 --- a/src/test/java/xyz/alexcrea/cuanvil/util/AnvilFuseTestUtil.java +++ b/src/test/java/xyz/alexcrea/cuanvil/util/AnvilFuseTestUtil.java @@ -195,7 +195,7 @@ public class AnvilFuseTestUtil { simulateClick(anvil, player, data.expectedResult()); - // Should have similated the click + // Should have simulated the click assertEqual(data.leftItem(), anvil.getFirstItem()); assertEqual(data.rightItem(), anvil.getSecondItem()); assertEqual(data.resultSlotItem(), anvil.getResult()); @@ -260,7 +260,7 @@ public class AnvilFuseTestUtil { } public static boolean isAir(@Nullable ItemStack item) { - return item == null || item.isEmpty(); + return item == null || item.isEmpty() || item.getAmount() == 0; } public static void assertPriceEqual(Integer expectedPrice, int price) { From d0078e528d91bf841f350500a235e64d1391ae4d Mon Sep 17 00:00:00 2001 From: alexcrea Date: Tue, 2 Jun 2026 16:50:14 +0200 Subject: [PATCH 18/24] fix creative price and other small fixes --- .../cuanvil/listener/AnvilResultListener.kt | 66 ++++++++++--------- .../cuanvil/listener/PrepareAnvilListener.kt | 4 +- .../cuanvil/util/JustForEasierHotswapUtil.kt | 17 ----- 3 files changed, 38 insertions(+), 49 deletions(-) delete mode 100644 src/main/kotlin/xyz/alexcrea/cuanvil/util/JustForEasierHotswapUtil.kt diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt index d63e2a6..552e777 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt @@ -90,15 +90,29 @@ class AnvilResultListener : Listener { // Rename if (rightItem == null) { - // BRUH - event.result = Event.Result.ALLOW + val result = AnvilMergeLogic.doRenaming(inventory, player, leftItem) + if(result.isEmpty()) return + + extractAnvilResult( + event, player, inventory, + null, 0, + null, 0, + result + ) return } // Merge val canMerge = leftItem.canMergeWith(rightItem) if (canMerge) { - event.result = Event.Result.ALLOW + val result = AnvilMergeLogic.doMerge(inventory, player, leftItem, rightItem) + + extractAnvilResult( + event, player, inventory, + null, 0, + null, 0, + result + ) return } @@ -195,6 +209,7 @@ class AnvilResultListener : Listener { inventory.setItem(ANVIL_INPUT_LEFT, leftItem) if (player.gameMode != GameMode.CREATIVE) { + //TODO monetary cost ? somehow if (linearCost) { val levelXp = AnvilXpUtil.calculateXpForLevel(player.level) val delta = AnvilXpUtil.calculateXpForLevel(player.level + 1) - levelXp @@ -236,7 +251,18 @@ class AnvilResultListener : Listener { return true } - // I don't know about side effect of "handling cost" at this level and not before so let be safe + private fun tryRemoveCost(player: Player, cost: AnvilCost): Boolean { + if(player.gameMode == GameMode.CREATIVE) return true + if(cost.isMonetary) { + val result = EconomyManager.economy!!.remove(player, cost.asMonetaryCost()) + if(!result) return false + } else { + player.level -= cost.asXpCost() + } + + return true + } + private fun extractAnvilResult( event: InventoryClickEvent, player: Player, @@ -249,28 +275,13 @@ class AnvilResultListener : Listener { ): Boolean { if(result.isEmpty()) return false - processCost(inventory, player, result.cost) - - return extractAnvilResult(event, player, inventory, leftItem, leftRemoveCount, rightItem, - rightRemoveCount, result.item!!, result.cost) - } - - private fun extractAnvilResult( - event: InventoryClickEvent, - player: Player, - inventory: AnvilInventory, - leftItem: ItemStack?, - leftRemoveCount: Int, - rightItem: ItemStack?, - rightRemoveCount: Int, - output: ItemStack, - cost: AnvilCost, - ): Boolean { // To avoid vanilla, we cancel the event event.result = Event.Result.DENY event.isCancelled = true + val cost = result.cost - if (!cost.valid) return false + processCost(inventory, player, cost) + if (!cost.valid && player.gameMode != GameMode.CREATIVE) return false // Where should we get the item val slotDestination = getActionSlot(event, player) @@ -278,12 +289,7 @@ class AnvilResultListener : Listener { // If not creative middle click... if (event.click != ClickType.MIDDLE) { - if(cost.isMonetary) { - val result = EconomyManager.economy!!.remove(player, cost.asMonetaryCost()) - if(!result) return false - } else { - player.level -= cost.asXpCost() - } + if(!tryRemoveCost(player, cost)) return false // We remove what should be removed if (leftItem != null) leftItem.amount -= leftRemoveCount @@ -298,9 +304,9 @@ class AnvilResultListener : Listener { // Finally, we add the item to the player if (SlotType.CURSOR == slotDestination.type) { - player.setItemOnCursor(output) + player.setItemOnCursor(result.item) } else {// We assume SlotType == SlotType.INVENTORY - player.inventory.setItem(slotDestination.slot, output) + player.inventory.setItem(slotDestination.slot, result.item) } // TODO probably anvil damage & sound here ?? diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt index 00a2053..21438ae 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt @@ -1,5 +1,6 @@ package xyz.alexcrea.cuanvil.listener +import com.github.stefvanschie.inventoryframework.util.InventoryViewUtil import io.delilaheve.CustomAnvil import io.delilaheve.util.ConfigOptions import io.delilaheve.util.ItemUtil.canMergeWith @@ -20,7 +21,6 @@ import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.testCustomRecipe import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.testLoreEdit import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.testUnitRepair import xyz.alexcrea.cuanvil.dependency.DependencyManager -import xyz.alexcrea.cuanvil.util.JustForEasierHotswapUtil import xyz.alexcrea.cuanvil.util.MaterialUtil.isAir import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil.AnvilCost @@ -49,7 +49,7 @@ class PrepareAnvilListener : Listener { val view = event.view val inventory = event.inventory - val player = JustForEasierHotswapUtil.getPlayerFromView(view) + val player = InventoryViewUtil.getInstance().getPlayer(view) if(player !is Player) return tryRenameDialog(player, event) diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/JustForEasierHotswapUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/JustForEasierHotswapUtil.kt deleted file mode 100644 index ea0f7ec..0000000 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/JustForEasierHotswapUtil.kt +++ /dev/null @@ -1,17 +0,0 @@ -package xyz.alexcrea.cuanvil.util - -import com.github.stefvanschie.inventoryframework.util.InventoryViewUtil -import org.bukkit.entity.HumanEntity -import org.bukkit.inventory.InventoryView - -// Hotswap to not relocate -// So I just put small thing calling relocating method here to enable to hotswap more class -// Especially for PrepareAnvilListener -// Will be able to replace that on legacy removal so really temporary -object JustForEasierHotswapUtil { - - fun getPlayerFromView(view: InventoryView): HumanEntity { - return InventoryViewUtil.getInstance().getPlayer(view) - } - -} \ No newline at end of file From 2768c0a0dc77972e449e834e0a3fb101d589ae17 Mon Sep 17 00:00:00 2001 From: alexcrea Date: Tue, 2 Jun 2026 23:53:18 +0200 Subject: [PATCH 19/24] custom craft monetary cost fixed --- .../cuanvil/listener/AnvilResultListener.kt | 80 +++++++++++-------- .../cuanvil/util/anvil/AnvilXpUtil.kt | 10 ++- 2 files changed, 56 insertions(+), 34 deletions(-) diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt index 552e777..549fd9a 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt @@ -26,7 +26,6 @@ import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil.setComponentDisplayName import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_INPUT_LEFT import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_INPUT_RIGHT import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_OUTPUT_SLOT -import xyz.alexcrea.cuanvil.recipe.AnvilCustomRecipe import xyz.alexcrea.cuanvil.util.CustomRecipeUtil import xyz.alexcrea.cuanvil.util.MiniMessageUtil import xyz.alexcrea.cuanvil.util.anvil.AnvilLoreEditUtil @@ -34,6 +33,7 @@ import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil.AnvilCost import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil import xyz.alexcrea.cuanvil.util.config.LoreEditType +import java.math.BigDecimal import java.util.* import java.util.concurrent.atomic.AtomicReference import kotlin.math.min @@ -148,14 +148,20 @@ class AnvilResultListener : Listener { result: CustomCraftResult, ) { val recipe = result.recipe!! - val xpCost = result.customCraftCost.rawCost + val rawCost = result.customCraftCost.rawCost val finalCost = - if (recipe.removeExactLinearXp) xpCost - else AnvilXpUtil.calculateLevelForXp(xpCost) + if (recipe.removeExactLinearXp) rawCost + else AnvilXpUtil.calculateLevelForXp(rawCost) + + CustomAnvil.log("gamemode: ${player.gameMode != GameMode.CREATIVE}, " + + "cost: $finalCost, level: ${player.level}, " + + "result: ${player.totalExperience < finalCost} ${player.level < finalCost}") - CustomAnvil.log("gamemode: ${player.gameMode != GameMode.CREATIVE}, cost: $finalCost, level: ${player.level}, result: ${player.totalExperience < finalCost} ${player.level < finalCost}") if (player.gameMode != GameMode.CREATIVE) { - if (recipe.removeExactLinearXp) { + if(ConfigOptions.shouldUseMoney(player)) { + result.cost.isMonetary = true + if(!EconomyManager.economy!!.has(player, BigDecimal(rawCost))) return + } else if (recipe.removeExactLinearXp) { val levelXp = AnvilXpUtil.calculateXpForLevel(player.level) val delta = AnvilXpUtil.calculateXpForLevel(player.level + 1) - levelXp val totalXp = levelXp + player.exp * delta @@ -172,14 +178,11 @@ class AnvilResultListener : Listener { if (event.click != ClickType.MIDDLE && !handleCustomCraftClick( event, - recipe, inventory, player, leftItem, rightItem, - result.amount, - finalCost, - recipe.removeExactLinearXp + result ) ) return @@ -192,11 +195,14 @@ class AnvilResultListener : Listener { } private fun handleCustomCraftClick( - event: InventoryClickEvent, recipe: AnvilCustomRecipe, + event: InventoryClickEvent, inventory: AnvilInventory, player: Player, leftItem: ItemStack, rightItem: ItemStack?, - amount: Int, xpCost: Int, linearCost: Boolean = false + result: CustomCraftResult ): Boolean { + val amount = result.amount + val recipe = result.recipe!! + // We remove what should be removed if (rightItem != null) { if (recipe.rightItem == null) return false// in case it changed @@ -208,26 +214,7 @@ class AnvilResultListener : Listener { leftItem.amount -= amount * recipe.leftItem!!.amount inventory.setItem(ANVIL_INPUT_LEFT, leftItem) - if (player.gameMode != GameMode.CREATIVE) { - //TODO monetary cost ? somehow - if (linearCost) { - val levelXp = AnvilXpUtil.calculateXpForLevel(player.level) - val delta = AnvilXpUtil.calculateXpForLevel(player.level + 1) - levelXp - var totalXp = levelXp + player.exp * delta - totalXp -= xpCost - - val newLevel = AnvilXpUtil.calculateLevelForXp(totalXp.toInt()) - - val newLevelXp = AnvilXpUtil.calculateXpForLevel(newLevel) - val newDelta = AnvilXpUtil.calculateXpForLevel(newLevel + 1) - newLevelXp - val xp = (totalXp - newLevelXp) / newDelta - - player.level = newLevel - player.exp = xp / newDelta - } else { - player.level -= xpCost - } - } + removeCustomCraftCost(player, result) // Then we try to find the new values for the anvil val newAmount = CustomRecipeUtil.getCustomRecipeAmount(recipe, leftItem, rightItem) @@ -251,6 +238,35 @@ class AnvilResultListener : Listener { return true } + private fun removeCustomCraftCost(player: Player, result: CustomCraftResult) { + if (player.gameMode == GameMode.CREATIVE) return + + val rawCost = result.customCraftCost.rawCost + if(result.cost.isMonetary) { + EconomyManager.economy!!.remove(player, BigDecimal(rawCost)) + return + } + + if (result.recipe!!.removeExactLinearXp) { + val levelXp = AnvilXpUtil.calculateXpForLevel(player.level) + val delta = AnvilXpUtil.calculateXpForLevel(player.level + 1) - levelXp + var totalXp = levelXp + player.exp * delta + totalXp -= rawCost + + val newLevel = AnvilXpUtil.calculateLevelForXp(totalXp.toInt()) + + val newLevelXp = AnvilXpUtil.calculateXpForLevel(newLevel) + val newDelta = AnvilXpUtil.calculateXpForLevel(newLevel + 1) - newLevelXp + val xp = (totalXp - newLevelXp) / newDelta + + player.level = newLevel + player.exp = xp / newDelta + } else { + player.level -= rawCost + } + + } + private fun tryRemoveCost(player: Player, cost: AnvilCost): Boolean { if(player.gameMode == GameMode.CREATIVE) return true if(cost.isMonetary) { diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilXpUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilXpUtil.kt index 997b260..b57a696 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilXpUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilXpUtil.kt @@ -56,7 +56,7 @@ object AnvilXpUtil { return generic + enchantment + repair + rename + lore + illegalPenalty + workPenalty + recipe } - fun asMonetaryCost(): BigDecimal { + open fun asMonetaryCost(): BigDecimal { // multiply by per use type multipliers return BigDecimal(generic) .add(BigDecimal(enchantment).multiply(moneyMultiplier("enchantment"))) @@ -71,7 +71,13 @@ object AnvilXpUtil { } } - class CustomCraftCost(val rawCost: Int): AnvilCost() + class CustomCraftCost(val rawCost: Int): AnvilCost() { + + override fun asMonetaryCost(): BigDecimal { + return BigDecimal(rawCost) + } + + } /** * Display the required cost (either as xp or as ) From bf4395ba3f2d21ecc515e4a319a6c721ba3b38f4 Mon Sep 17 00:00:00 2001 From: alexcrea Date: Wed, 3 Jun 2026 02:27:20 +0200 Subject: [PATCH 20/24] fix multiples issues --- defaultconfigs/1.18/config.yml | 5 ++++- defaultconfigs/1.21.11/config.yml | 5 ++++- defaultconfigs/1.21.9/config.yml | 5 ++++- defaultconfigs/1.21/config.yml | 5 ++++- .../xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt | 6 +++--- .../kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilXpUtil.kt | 1 + src/main/resources/config.yml | 5 ++++- 7 files changed, 24 insertions(+), 8 deletions(-) diff --git a/defaultconfigs/1.18/config.yml b/defaultconfigs/1.18/config.yml index fb70505..b5ad3b0 100644 --- a/defaultconfigs/1.18/config.yml +++ b/defaultconfigs/1.18/config.yml @@ -436,7 +436,10 @@ lore_edit: # It also requires to enable dialog rename (set "enable_dialog_rename: false" a bit higher) # If dialog rename permission is enabled and player do not have the permission merge will fall back to vanilla xp cost # -# As this feature require dialog rename, it can only be enabled starting with paper 1.21.6 +# If you are using custom craft I recommend using Linear Xp Cost with Exact Linear Xp as normal Xp Cost will act "weird" +# But Linear Xp will act as 1$ time global multiplier. In other word: like you expect +# +# As this feature require dialog rename, it can only be enabled starting with paper 1.21.6 and later monetary_cost: enabled: false # If using vault unlocked this allow to specify what currency should be used for anvil usage diff --git a/defaultconfigs/1.21.11/config.yml b/defaultconfigs/1.21.11/config.yml index b70798c..44885b7 100644 --- a/defaultconfigs/1.21.11/config.yml +++ b/defaultconfigs/1.21.11/config.yml @@ -456,7 +456,10 @@ lore_edit: # It also requires to enable dialog rename (set "enable_dialog_rename: false" a bit higher) # If dialog rename permission is enabled and player do not have the permission merge will fall back to vanilla xp cost # -# As this feature require dialog rename, it can only be enabled starting with paper 1.21.6 +# If you are using custom craft I recommend using Linear Xp Cost with Exact Linear Xp as normal Xp Cost will act "weird" +# But Linear Xp will act as 1$ time global multiplier. In other word: like you expect +# +# As this feature require dialog rename, it can only be enabled starting with paper 1.21.6 and later monetary_cost: enabled: false # If using vault unlocked this allow to specify what currency should be used for anvil usage diff --git a/defaultconfigs/1.21.9/config.yml b/defaultconfigs/1.21.9/config.yml index 31bdb1f..4df5876 100644 --- a/defaultconfigs/1.21.9/config.yml +++ b/defaultconfigs/1.21.9/config.yml @@ -448,7 +448,10 @@ lore_edit: # It also requires to enable dialog rename (set "enable_dialog_rename: false" a bit higher) # If dialog rename permission is enabled and player do not have the permission merge will fall back to vanilla xp cost # -# As this feature require dialog rename, it can only be enabled starting with paper 1.21.6 +# If you are using custom craft I recommend using Linear Xp Cost with Exact Linear Xp as normal Xp Cost will act "weird" +# But Linear Xp will act as 1$ time global multiplier. In other word: like you expect +# +# As this feature require dialog rename, it can only be enabled starting with paper 1.21.6 and later monetary_cost: enabled: false # If using vault unlocked this allow to specify what currency should be used for anvil usage diff --git a/defaultconfigs/1.21/config.yml b/defaultconfigs/1.21/config.yml index 1dafa54..5d59e5a 100644 --- a/defaultconfigs/1.21/config.yml +++ b/defaultconfigs/1.21/config.yml @@ -436,7 +436,10 @@ lore_edit: # It also requires to enable dialog rename (set "enable_dialog_rename: false" a bit higher) # If dialog rename permission is enabled and player do not have the permission merge will fall back to vanilla xp cost # -# As this feature require dialog rename, it can only be enabled starting with paper 1.21.6 +# If you are using custom craft I recommend using Linear Xp Cost with Exact Linear Xp as normal Xp Cost will act "weird" +# But Linear Xp will act as 1$ time global multiplier. In other word: like you expect +# +# As this feature require dialog rename, it can only be enabled starting with paper 1.21.6 and later monetary_cost: enabled: false # If using vault unlocked this allow to specify what currency should be used for anvil usage diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt index 549fd9a..6b5f510 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt @@ -160,7 +160,7 @@ class AnvilResultListener : Listener { if (player.gameMode != GameMode.CREATIVE) { if(ConfigOptions.shouldUseMoney(player)) { result.cost.isMonetary = true - if(!EconomyManager.economy!!.has(player, BigDecimal(rawCost))) return + if(!EconomyManager.economy!!.has(player, result.cost.asMonetaryCost())) return } else if (recipe.removeExactLinearXp) { val levelXp = AnvilXpUtil.calculateXpForLevel(player.level) val delta = AnvilXpUtil.calculateXpForLevel(player.level + 1) - levelXp @@ -243,7 +243,7 @@ class AnvilResultListener : Listener { val rawCost = result.customCraftCost.rawCost if(result.cost.isMonetary) { - EconomyManager.economy!!.remove(player, BigDecimal(rawCost)) + EconomyManager.economy!!.remove(player, result.cost.asMonetaryCost()) return } @@ -262,7 +262,7 @@ class AnvilResultListener : Listener { player.level = newLevel player.exp = xp / newDelta } else { - player.level -= rawCost + player.level -= AnvilXpUtil.calculateLevelForXp(rawCost) } } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilXpUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilXpUtil.kt index b57a696..8c39a0a 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilXpUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilXpUtil.kt @@ -75,6 +75,7 @@ object AnvilXpUtil { override fun asMonetaryCost(): BigDecimal { return BigDecimal(rawCost) + .multiply(moneyMultiplier("global")) } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 27c0248..7d6e396 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -438,7 +438,10 @@ lore_edit: # It also requires to enable dialog rename (set "enable_dialog_rename: false" a bit higher) # If dialog rename permission is enabled and player do not have the permission merge will fall back to vanilla xp cost # -# As this feature require dialog rename, it can only be enabled starting with paper 1.21.6 +# If you are using custom craft I recommend using Linear Xp Cost with Exact Linear Xp as normal Xp Cost will act "weird" +# But Linear Xp will act as 1$ time global multiplier. In other word: like you expect +# +# As this feature require dialog rename, it can only be enabled starting with paper 1.21.6 and later monetary_cost: enabled: false # If using vault unlocked this allow to specify what currency should be used for anvil usage From d679cd73f98243735700aafe0f7556e433423aa1 Mon Sep 17 00:00:00 2001 From: alexcrea Date: Tue, 9 Jun 2026 14:08:17 +0200 Subject: [PATCH 21/24] remove rename pdc on paper lore append --- .../alexcrea/cuanvil/anvil/AnvilMergeLogic.kt | 55 +++++++++++-------- .../cuanvil/listener/AnvilResultListener.kt | 39 +++++++------ 2 files changed, 55 insertions(+), 39 deletions(-) diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt index 8699285..e018eff 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt @@ -55,7 +55,7 @@ object AnvilMergeLogic { } } - class UnitRepairResult: AnvilResult { + class UnitRepairResult : AnvilResult { companion object { val EMPTY = UnitRepairResult(null, AnvilCost(), 0) } @@ -67,7 +67,7 @@ object AnvilMergeLogic { } } - class CustomCraftResult: AnvilResult { + class CustomCraftResult : AnvilResult { companion object { val EMPTY = CustomCraftResult(null, CustomCraftCost(0), 0, null) } @@ -76,15 +76,17 @@ object AnvilMergeLogic { val amount: Int val recipe: AnvilCustomRecipe? - constructor(item: ItemStack?, cost: CustomCraftCost, - amount: Int, recipe: AnvilCustomRecipe?) : super(item, cost, true) { + constructor( + item: ItemStack?, cost: CustomCraftCost, + amount: Int, recipe: AnvilCustomRecipe? + ) : super(item, cost, true) { this.customCraftCost = cost this.amount = amount this.recipe = recipe } } - class LoreEditResult: AnvilResult { + class LoreEditResult : AnvilResult { companion object { val EMPTY = LoreEditResult(null, AnvilCost(), LoreEditType.APPEND_PAPER) } @@ -96,8 +98,9 @@ object AnvilMergeLogic { } } - fun doRenaming(inventory: AnvilInventory, - player: Player, first: ItemStack + fun doRenaming( + inventory: AnvilInventory, + player: Player, first: ItemStack ): AnvilResult { val resultItem = DependencyManager.cloneItem(player, first) val cost = AnvilCost() @@ -115,15 +118,19 @@ object AnvilMergeLogic { return AnvilResult(result, cost) } - private fun processDialogPCD(it: ItemMeta, player: HumanEntity) { + private fun processDialogPCD(meta: ItemMeta, player: HumanEntity) { + val text = AnvilRenameDialogUtil.anvilRenameDialog.currentText(player) + return processPCD(meta, player, text) + } + + fun processPCD(meta: ItemMeta, player: HumanEntity, text: String?) { val keepDialog = ConfigOptions.canUseDialogRename(player) && ConfigOptions.shouldKeepRenameText - val pdc = it.persistentDataContainer - if(!keepDialog) + val pdc = meta.persistentDataContainer + if (!keepDialog) pdc.remove(AnvilRenameDialog.PCD_KEEP_RENAME_TEXT_KEY) else { - val text = AnvilRenameDialogUtil.anvilRenameDialog.currentText(player) - if(text == null || text.isBlank()) + if (text == null || text.isBlank()) pdc.remove(AnvilRenameDialog.PCD_KEEP_RENAME_TEXT_KEY) else pdc.set(AnvilRenameDialog.PCD_KEEP_RENAME_TEXT_KEY, PersistentDataType.STRING, text) } @@ -138,7 +145,8 @@ object AnvilMergeLogic { if (ConfigOptions.renameColorPossible && renameText != null) { val component = AnvilColorUtil.handleColor( renameText, - AnvilColorUtil.renamePermission(player)) + AnvilColorUtil.renamePermission(player) + ) if (component != null) { renameText = MiniMessageUtil.legacy_mm.serialize(component) @@ -160,7 +168,8 @@ object AnvilMergeLogic { renameText == "" || //TODO on recent paper check effective name instead renameText == CasedStringUtil.snakeToUpperSpacedCase(resultItem.type.name.lowercase()) - )) { + ) + ) { it.setDisplayName(renameText) processDialogPCD(it, player) resultItem.itemMeta = it @@ -184,7 +193,7 @@ object AnvilMergeLogic { val resultItem = DependencyManager.cloneItem(player, first) val cost = AnvilCost() - if(hasChanged){ + if (hasChanged) { resultItem.setEnchantmentsUnsafe(newEnchants) // Calculate enchantment cost AnvilXpUtil.getRightValues(second, resultItem, cost) @@ -217,9 +226,9 @@ object AnvilMergeLogic { firstEnchants: MutableMap, resultEnchants: MutableMap ): Boolean { - if(firstEnchants.size != resultEnchants.size) return false + if (firstEnchants.size != resultEnchants.size) return false for (entry in resultEnchants) { - if(firstEnchants.getOrDefault(entry.key, entry.value-1) != entry.value) return false + if (firstEnchants.getOrDefault(entry.key, entry.value - 1) != entry.value) return false } return true @@ -262,11 +271,11 @@ object AnvilMergeLogic { } fun testUnitRepair( - inventory: AnvilInventory, - player: Player, - first: ItemStack, second: ItemStack, - unitRepairAmount: Double - ): UnitRepairResult { + inventory: AnvilInventory, + player: Player, + first: ItemStack, second: ItemStack, + unitRepairAmount: Double + ): UnitRepairResult { val resultItem = DependencyManager.cloneItem(player, first) val cost = AnvilCost() cost.rename = handleRename(resultItem, inventory, player) @@ -300,7 +309,7 @@ object AnvilMergeLogic { AnvilLoreEditUtil.tryLoreEditByPaper(player, first, second) else LoreEditResult.EMPTY - if(result.isEmpty()) return result + if (result.isEmpty()) return result if (result.item!!.isAir || first == result.item) { CustomAnvil.log("lore edit, But input is same as output") diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt index 6b5f510..a232046 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt @@ -91,7 +91,7 @@ class AnvilResultListener : Listener { // Rename if (rightItem == null) { val result = AnvilMergeLogic.doRenaming(inventory, player, leftItem) - if(result.isEmpty()) return + if (result.isEmpty()) return extractAnvilResult( event, player, inventory, @@ -119,7 +119,8 @@ class AnvilResultListener : Listener { // Unit repair val unitRepairResult = AnvilMergeLogic.testUnitRepair( inventory, player, - leftItem, rightItem) + leftItem, rightItem + ) if (unitRepairResult.isEmpty()) { onUnitRepairExtract( rightItem, event, player, inventory, @@ -130,8 +131,8 @@ class AnvilResultListener : Listener { // For lore edit val loreResult = AnvilMergeLogic.testLoreEdit(player, leftItem, rightItem) - if(!loreResult.isEmpty()) { - if(loreResult.type.isBook) + if (!loreResult.isEmpty()) { + if (loreResult.type.isBook) handleBookLoreEdit(event, inventory, player, leftItem, rightItem, loreResult) else handlePaperLoreEdit(event, inventory, player, leftItem, rightItem, loreResult) @@ -153,14 +154,16 @@ class AnvilResultListener : Listener { if (recipe.removeExactLinearXp) rawCost else AnvilXpUtil.calculateLevelForXp(rawCost) - CustomAnvil.log("gamemode: ${player.gameMode != GameMode.CREATIVE}, " + - "cost: $finalCost, level: ${player.level}, " + - "result: ${player.totalExperience < finalCost} ${player.level < finalCost}") + CustomAnvil.log( + "gamemode: ${player.gameMode != GameMode.CREATIVE}, " + + "cost: $finalCost, level: ${player.level}, " + + "result: ${player.totalExperience < finalCost} ${player.level < finalCost}" + ) if (player.gameMode != GameMode.CREATIVE) { - if(ConfigOptions.shouldUseMoney(player)) { + if (ConfigOptions.shouldUseMoney(player)) { result.cost.isMonetary = true - if(!EconomyManager.economy!!.has(player, result.cost.asMonetaryCost())) return + if (!EconomyManager.economy!!.has(player, result.cost.asMonetaryCost())) return } else if (recipe.removeExactLinearXp) { val levelXp = AnvilXpUtil.calculateXpForLevel(player.level) val delta = AnvilXpUtil.calculateXpForLevel(player.level + 1) - levelXp @@ -242,7 +245,7 @@ class AnvilResultListener : Listener { if (player.gameMode == GameMode.CREATIVE) return val rawCost = result.customCraftCost.rawCost - if(result.cost.isMonetary) { + if (result.cost.isMonetary) { EconomyManager.economy!!.remove(player, result.cost.asMonetaryCost()) return } @@ -268,10 +271,10 @@ class AnvilResultListener : Listener { } private fun tryRemoveCost(player: Player, cost: AnvilCost): Boolean { - if(player.gameMode == GameMode.CREATIVE) return true - if(cost.isMonetary) { + if (player.gameMode == GameMode.CREATIVE) return true + if (cost.isMonetary) { val result = EconomyManager.economy!!.remove(player, cost.asMonetaryCost()) - if(!result) return false + if (!result) return false } else { player.level -= cost.asXpCost() } @@ -289,7 +292,7 @@ class AnvilResultListener : Listener { rightRemoveCount: Int, result: AnvilResult ): Boolean { - if(result.isEmpty()) return false + if (result.isEmpty()) return false // To avoid vanilla, we cancel the event event.result = Event.Result.DENY @@ -305,7 +308,7 @@ class AnvilResultListener : Listener { // If not creative middle click... if (event.click != ClickType.MIDDLE) { - if(!tryRemoveCost(player, cost)) return false + if (!tryRemoveCost(player, cost)) return false // We remove what should be removed if (leftItem != null) leftItem.amount -= leftRemoveCount @@ -417,7 +420,7 @@ class AnvilResultListener : Listener { leftItem: ItemStack, rightItem: ItemStack, result: LoreEditResult - ){ + ) { val bookMeta = rightItem.itemMeta as BookMeta? ?: return // fill book meta @@ -486,6 +489,10 @@ class AnvilResultListener : Listener { paperCopy = rightItem.clone() paperCopy.amount = 1 paperMeta.setComponentDisplayName(null) + + // Remove pcd name + AnvilMergeLogic.processPCD(paperMeta, player, null) + paperCopy.itemMeta = paperMeta } From 2efb6e55e285e23c83bcec8e66f6a286e7c1cadf Mon Sep 17 00:00:00 2001 From: alexcrea Date: Wed, 10 Jun 2026 14:54:13 +0200 Subject: [PATCH 22/24] new treat anvil event --- .../listener/CAClickResultBypassEvent.java | 2 +- .../listener/CAEarlyPreAnvilBypassEvent.java | 2 +- .../event/listener/CAPreAnvilBypassEvent.java | 2 +- .../listener/CATreatAnvilResult2Event.java | 196 ++++++++++++++++++ .../listener/CATreatAnvilResultEvent.java | 4 +- .../alexcrea/cuanvil/anvil/AnvilMergeLogic.kt | 17 +- .../cuanvil/dependency/DependencyManager.kt | 16 +- .../plugins/ExcellentEnchantsDependency.kt | 13 +- .../cuanvil/listener/AnvilResultListener.kt | 11 +- .../cuanvil/listener/PrepareAnvilListener.kt | 12 +- 10 files changed, 244 insertions(+), 31 deletions(-) create mode 100644 src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResult2Event.java diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CAClickResultBypassEvent.java b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CAClickResultBypassEvent.java index a27c65e..fe5e199 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CAClickResultBypassEvent.java +++ b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CAClickResultBypassEvent.java @@ -17,7 +17,7 @@ import org.jetbrains.annotations.NotNull; * Most of the time you would likely need {@link CAPreAnvilBypassEvent} or {@link CAEarlyPreAnvilBypassEvent} * for this event to be useful. *

    - * There is also {@link CATreatAnvilResultEvent} that may be better for some use case. + * There is also {@link CATreatAnvilResult2Event} that may be better for some use case. */ public class CAClickResultBypassEvent extends Event implements Cancellable { diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CAEarlyPreAnvilBypassEvent.java b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CAEarlyPreAnvilBypassEvent.java index 2fbd275..e92b4cd 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CAEarlyPreAnvilBypassEvent.java +++ b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CAEarlyPreAnvilBypassEvent.java @@ -15,7 +15,7 @@ import org.jetbrains.annotations.NotNull; *

    * You should also use {@link CAClickResultBypassEvent} if you want to use this event for something useful. *

    - * It is also recommended that you read about {@link CAPreAnvilBypassEvent} and {@link CATreatAnvilResultEvent} + * It is also recommended that you read about {@link CAPreAnvilBypassEvent} and {@link CATreatAnvilResult2Event} * as your use case may be more prone to use theses. */ public class CAEarlyPreAnvilBypassEvent extends Event implements Cancellable { diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CAPreAnvilBypassEvent.java b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CAPreAnvilBypassEvent.java index 18334e3..9103a4b 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CAPreAnvilBypassEvent.java +++ b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CAPreAnvilBypassEvent.java @@ -18,7 +18,7 @@ import org.jetbrains.annotations.NotNull; *

    * You should also use {@link CAClickResultBypassEvent} if you want to use this event for something useful. *

    - * It is also recommended that you read about {@link CAEarlyPreAnvilBypassEvent} and {@link CATreatAnvilResultEvent} + * It is also recommended that you read about {@link CAEarlyPreAnvilBypassEvent} and {@link CATreatAnvilResult2Event} * as your use case may be more prone to use theses. */ public class CAPreAnvilBypassEvent extends Event implements Cancellable { diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResult2Event.java b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResult2Event.java new file mode 100644 index 0000000..aa8f8e1 --- /dev/null +++ b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResult2Event.java @@ -0,0 +1,196 @@ +package xyz.alexcrea.cuanvil.api.event.listener; + +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import xyz.alexcrea.cuanvil.anvil.AnvilUseType; +import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil; + +/** + * Called after custom anvil processed the click on the result on the anvil inventory. + * This event should be used to modify the result of an anvil use. + *

    + * You may also want to check {@link CAClickResultBypassEvent}, + * {@link CAPreAnvilBypassEvent} + * and {@link CAEarlyPreAnvilBypassEvent} for your use case + *

    + * A null result will cancel this event + */ +@SuppressWarnings("unused") +public class CATreatAnvilResult2Event extends Event { + + private static final HandlerList HANDLERS = new HandlerList(); + + public static HandlerList getHandlerList() { + return HANDLERS; + } + + @Override + public @NotNull HandlerList getHandlers() { + return HANDLERS; + } + + @NotNull + private final InventoryView view; + + private final AnvilUseType useType; + + @Nullable + private final ItemStack left; + @Nullable + private final ItemStack right; + + @Nullable + private ItemStack result; + + private final AnvilXpUtil.AnvilCost cost; + + @ApiStatus.Internal + public CATreatAnvilResult2Event( + @NotNull InventoryView view, + Inventory inv, + AnvilUseType useType, + @Nullable ItemStack result, + AnvilXpUtil.AnvilCost cost) { + this.view = view; + this.useType = useType; + + this.left = inv.getItem(0); // TODO use view here + this.right = inv.getItem(1); + this.result = result; + this.cost = cost; + } + + /** + * Get the bukkit inventory view. + *

    + * Temporarily marked as internal as it will get changed to anvil view on legacy removal + * so signature will change + * + * @return The inventory view of this event. + */ + @ApiStatus.Internal + public @NotNull InventoryView getView() { + return view; + } + + + /** + * Get the type of use source of the result. + * + * @return The craft use type. + */ + public AnvilUseType getUseType() { + return useType; + } + + /** + * Get the left item of the anvil use + * + * @return the left item + */ + public @Nullable ItemStack getLeftItem() { + return left; + } + + /** + * Get the right item of the anvil use + * + * @return the right item + */ + public @Nullable ItemStack getRightItem() { + return right; + } + + /** + * Get the current result + *

    + * note that it will not be null unless another listener previously set it to null. + * + * @return The current result. + */ + public @Nullable ItemStack getResult() { + return result; + } + + /** + * Set the current result + *

    + * note that a null result will cancel this anvil use. + * + * @param result The new result + */ + public void setResult(@Nullable ItemStack result) { + this.result = result; + } + + /** + * Get the level cost displayed on the anvil. + *

    Important note:

    + * the final price are re calculated on click for the following use case: + *
      + *
    • Custom craft
    • + *
    • Unit repair
    • + *
    • Lore edit
    • + *
    + * This value will be used as final price for: + *
  • Item merge
  • + *
  • Item rename
  • + * + * + * @return The current cost. + * @deprecated use #{@link #getCost()} instead + */ + @Deprecated(forRemoval = true, since = "1.17.0") + public int getLevelCost() { + return cost.asXpCost(); + } + + /** + * Set the level cost displayed on the anvil. + *

    Important note:

    + * the final price are re calculated on click for the following use case: + *
      + *
    • Custom craft
    • + *
    • Unit repair
    • + *
    • Lore edit
    • + *
    + * This value will be used as final price for: + *
  • Item merge
  • + *
  • Item rename
  • + * + * + * @param levelCost The new cost. + * @deprecated use #{@link #getCost()} and set value on this instead + */ + @Deprecated(forRemoval = true, since = "1.17.0") + public void setLevelCost(int levelCost) { + cost.setGeneric(levelCost - cost.getGeneric() - cost.asXpCost()); + } + + /** + * Allow access to the current cost of the event + * Note that modifying this object will change the event resulting cost + * + *

    Important note:

    + * the final price are re calculated on click for the following use case: + *
      + *
    • Custom craft
    • + *
    • Unit repair
    • + *
    • Lore edit
    • + *
    + * This value will be used as final price for: + *
  • Item merge
  • + *
  • Item rename
  • + * + * @return the current anvil cost + */ + public AnvilXpUtil.AnvilCost getCost() { + return cost; + } +} diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResultEvent.java b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResultEvent.java index ddbaf23..6e6358d 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResultEvent.java +++ b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResultEvent.java @@ -19,8 +19,8 @@ import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil.AnvilCost; *

    * A null result will cancel this pre anvil event * - * @deprecated Prepare anvil Event should not be provided as it can be called on result and therefor not have prepare anvil event - * TODO a replacement is necessary but not yet made + * @deprecated Prepare anvil Event cannot be provided as it can be called on result and therefore not have prepared anvil event + * use {@link CATreatAnvilResult2Event} instead */ @SuppressWarnings("unused") @Deprecated(forRemoval = true, since = "1.17.0") diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt index e018eff..67b9b7d 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt @@ -13,6 +13,7 @@ import org.bukkit.Material import org.bukkit.entity.HumanEntity import org.bukkit.entity.Player import org.bukkit.inventory.AnvilInventory +import org.bukkit.inventory.InventoryView import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.ItemMeta import org.bukkit.persistence.PersistentDataType @@ -99,6 +100,7 @@ object AnvilMergeLogic { } fun doRenaming( + view: InventoryView, //TODO use anvil view inventory: AnvilInventory, player: Player, first: ItemStack ): AnvilResult { @@ -113,7 +115,7 @@ object AnvilMergeLogic { } cost.workPenalty = AnvilXpUtil.calculatePenalty(first, null, resultItem, AnvilUseType.RENAME_ONLY) - val result = DependencyManager.tryTreatAnvilResult(resultItem, AnvilUseType.RENAME_ONLY, cost) + val result = DependencyManager.tryTreatAnvilResult(view, inventory, player, resultItem, AnvilUseType.RENAME_ONLY, cost) return AnvilResult(result, cost) } @@ -183,6 +185,7 @@ object AnvilMergeLogic { } fun doMerge( + view: InventoryView, //TODO use anvil view instead inventory: AnvilInventory, player: Player, first: ItemStack, second: ItemStack @@ -217,7 +220,7 @@ object AnvilMergeLogic { // Calculate rename cost cost.rename = handleRename(resultItem, inventory, player) - val result = DependencyManager.tryTreatAnvilResult(resultItem, AnvilUseType.MERGE, cost) + val result = DependencyManager.tryTreatAnvilResult(view, inventory, player, resultItem, AnvilUseType.MERGE, cost) return AnvilResult(result, cost) } @@ -236,6 +239,8 @@ object AnvilMergeLogic { // return true if a custom recipe exist with these ingredients fun testCustomRecipe( + view: InventoryView, //TODO use anvil view instead + inventory: AnvilInventory, player: Player, first: ItemStack, second: ItemStack? ): CustomCraftResult { @@ -256,21 +261,23 @@ object AnvilMergeLogic { cost.recipe = if (recipe.removeExactLinearXp) AnvilXpUtil.calculateMinimumLevelForXp(xpCost) else AnvilXpUtil.calculateLevelForXp(xpCost) - val result = DependencyManager.tryTreatAnvilResult(resultItem, AnvilUseType.CUSTOM_CRAFT, cost) + val result = DependencyManager.tryTreatAnvilResult(view, inventory, player, resultItem, AnvilUseType.CUSTOM_CRAFT, cost) return CustomCraftResult(result, cost, amount, recipe) } fun testUnitRepair( + view: InventoryView, //TODO use anvil view inventory: AnvilInventory, player: Player, first: ItemStack, second: ItemStack ): UnitRepairResult { val unitRepairAmount = first.getRepair(second) ?: return UnitRepairResult.EMPTY - return testUnitRepair(inventory, player, first, second, unitRepairAmount) + return testUnitRepair(view, inventory, player, first, second, unitRepairAmount) } fun testUnitRepair( + view: InventoryView, //TODO use anvil view instead inventory: AnvilInventory, player: Player, first: ItemStack, second: ItemStack, @@ -293,7 +300,7 @@ object AnvilMergeLogic { return UnitRepairResult.EMPTY } - val result = DependencyManager.tryTreatAnvilResult(resultItem, AnvilUseType.UNIT_REPAIR, cost) + val result = DependencyManager.tryTreatAnvilResult(view, inventory, player, resultItem, AnvilUseType.UNIT_REPAIR, cost) return UnitRepairResult(result, cost, repairAmount) } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt index 4ad6c9b..0da6b30 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt @@ -12,12 +12,13 @@ import org.bukkit.event.inventory.InventoryClickEvent import org.bukkit.event.inventory.PrepareAnvilEvent import org.bukkit.inventory.AnvilInventory import org.bukkit.inventory.Inventory +import org.bukkit.inventory.InventoryView import org.bukkit.inventory.ItemStack import xyz.alexcrea.cuanvil.anvil.AnvilUseType import xyz.alexcrea.cuanvil.api.event.listener.CAClickResultBypassEvent import xyz.alexcrea.cuanvil.api.event.listener.CAEarlyPreAnvilBypassEvent import xyz.alexcrea.cuanvil.api.event.listener.CAPreAnvilBypassEvent -import xyz.alexcrea.cuanvil.api.event.listener.CATreatAnvilResultEvent +import xyz.alexcrea.cuanvil.api.event.listener.CATreatAnvilResult2Event import xyz.alexcrea.cuanvil.config.ConfigHolder import xyz.alexcrea.cuanvil.dependency.datapack.DataPackDependency import xyz.alexcrea.cuanvil.dependency.gui.GenericExternGuiTester @@ -234,23 +235,24 @@ object DependencyManager { // Return null if there was an issue fun tryTreatAnvilResult( + view: InventoryView, + inventory: Inventory, // TODO REMOVE, use view instead on legacy removal + player: HumanEntity, result: ItemStack, useType: AnvilUseType, cost: AnvilXpUtil.AnvilCost ): ItemStack? { - //TODO - /*val treatEvent = CATreatAnvilResultEvent(event, useType, result, cost) + val treatEvent = CATreatAnvilResult2Event(view, inventory, useType, result, cost) try { unsafeTryTreatAnvilResult(treatEvent) return treatEvent.result } catch (e: Exception) { - logExceptionAndClear(event.view.player, event.inventory, e) + logExceptionAndClear(player, inventory, e) return null - }*/ - return result + } } - private fun unsafeTryTreatAnvilResult(event: CATreatAnvilResultEvent) { + private fun unsafeTryTreatAnvilResult(event: CATreatAnvilResult2Event) { Bukkit.getPluginManager().callEvent(event) excellentEnchantsCompatibility?.treatAnvilResult(event) diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/ExcellentEnchantsDependency.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/ExcellentEnchantsDependency.kt index 54ea2f4..39bfad7 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/ExcellentEnchantsDependency.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/ExcellentEnchantsDependency.kt @@ -8,7 +8,7 @@ import org.bukkit.event.inventory.PrepareAnvilEvent import org.bukkit.inventory.ItemStack import org.bukkit.plugin.RegisteredListener import xyz.alexcrea.cuanvil.api.EnchantmentApi -import xyz.alexcrea.cuanvil.api.event.listener.CATreatAnvilResultEvent +import xyz.alexcrea.cuanvil.api.event.listener.CATreatAnvilResult2Event import xyz.alexcrea.cuanvil.enchant.wrapped.CAEEPreV5Enchantment import xyz.alexcrea.cuanvil.enchant.wrapped.CAEEV5Enchantment import xyz.alexcrea.cuanvil.enchant.wrapped.CAEEV5_4Enchantment @@ -218,14 +218,17 @@ class ExcellentEnchantsDependency { return handleRechargeMethod.invoke(this.usedAnvilListener, event, first, second) as Boolean } - fun treatAnvilResult(event: CATreatAnvilResultEvent) { + fun treatAnvilResult(event: CATreatAnvilResult2Event) { val result = event.result if (result == null) return - val first: ItemStack = treatInput(event.event.inventory.getItem(0)) - val second: ItemStack = treatInput(event.event.inventory.getItem(1)) + val first: ItemStack = treatInput(event.leftItem) + val second: ItemStack = treatInput(event.rightItem) - handleCombineMethod.invoke(this.usedAnvilListener, event.event, first, second, result) + val fakeEvent = PrepareAnvilEvent(event.view, result) + handleCombineMethod.invoke(this.usedAnvilListener, fakeEvent, first, second, result) + + event.result = fakeEvent.result } fun testAnvilResult(event: InventoryClickEvent): Any { diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt index a232046..775a685 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt @@ -53,6 +53,7 @@ class AnvilResultListener : Listener { fun anvilExtractionCheck(event: InventoryClickEvent) { val player = event.whoClicked as? Player ?: return val inventory = event.inventory as? AnvilInventory ?: return + val view = event.view if (event.rawSlot != ANVIL_OUTPUT_SLOT) { return @@ -74,7 +75,7 @@ class AnvilResultListener : Listener { } // Test custom recipe - val customRecipeResult = AnvilMergeLogic.testCustomRecipe(player, leftItem, rightItem) + val customRecipeResult = AnvilMergeLogic.testCustomRecipe(view, inventory, player, leftItem, rightItem) if (!customRecipeResult.isEmpty()) { onCustomCraft( event, player, inventory, @@ -90,7 +91,7 @@ class AnvilResultListener : Listener { // Rename if (rightItem == null) { - val result = AnvilMergeLogic.doRenaming(inventory, player, leftItem) + val result = AnvilMergeLogic.doRenaming(view, inventory, player, leftItem) if (result.isEmpty()) return extractAnvilResult( @@ -105,7 +106,7 @@ class AnvilResultListener : Listener { // Merge val canMerge = leftItem.canMergeWith(rightItem) if (canMerge) { - val result = AnvilMergeLogic.doMerge(inventory, player, leftItem, rightItem) + val result = AnvilMergeLogic.doMerge(view, inventory, player, leftItem, rightItem) extractAnvilResult( event, player, inventory, @@ -118,7 +119,7 @@ class AnvilResultListener : Listener { // Unit repair val unitRepairResult = AnvilMergeLogic.testUnitRepair( - inventory, player, + view, inventory, player, leftItem, rightItem ) if (unitRepairResult.isEmpty()) { @@ -481,6 +482,8 @@ class AnvilResultListener : Listener { ) { val paperMeta = rightItem.itemMeta ?: return + + val paperCopy: ItemStack? if (LoreEditType.APPEND_PAPER.doConsume) { paperCopy = null diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt index 21438ae..b2f6785 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt @@ -11,6 +11,7 @@ import org.bukkit.event.EventPriority import org.bukkit.event.Listener import org.bukkit.event.inventory.PrepareAnvilEvent import org.bukkit.inventory.AnvilInventory +import org.bukkit.inventory.InventoryView import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.EnchantmentStorageMeta import org.bukkit.inventory.meta.ItemMeta @@ -91,11 +92,12 @@ class PrepareAnvilListener : Listener { if (!player.hasPermission(CustomAnvil.affectedByPluginPermission)) return - val result = getResult(inventory, player, first, second) + val result = getResult(view, inventory, player, first, second) applyResult(event, player, result) } fun getResult( + view: InventoryView, //TODO use anvil view inventory: AnvilInventory, player: Player, first: ItemStack?, second: ItemStack?) : AnvilResult @@ -104,7 +106,7 @@ class PrepareAnvilListener : Listener { return AnvilResult.EMPTY // Test custom recipe - var result: AnvilResult = testCustomRecipe(player, first, second) + var result: AnvilResult = testCustomRecipe(view, inventory, player, first, second) if (!result.isEmpty()) return result @@ -112,14 +114,14 @@ class PrepareAnvilListener : Listener { val shouldTryRename = second.isAir CustomAnvil.verboseLog("checking air in main logic: $shouldTryRename") if (shouldTryRename) - return doRenaming(inventory, player, first) + return doRenaming(view, inventory, player, first) // Test for merge if (first.canMergeWith(second!!)) - return doMerge(inventory, player, first, second) + return doMerge(view, inventory, player, first, second) // Test for unit repair - result = testUnitRepair(inventory, player, first, second) + result = testUnitRepair(view, inventory, player, first, second) if (!result.isEmpty()) return result From 49b0054ecaec04170230f4d8bbae589171c79ec2 Mon Sep 17 00:00:00 2001 From: alexcrea Date: Wed, 10 Jun 2026 14:59:28 +0200 Subject: [PATCH 23/24] move anvil cost to its own class --- .../listener/CATreatAnvilResult2Event.java | 8 +-- .../listener/CATreatAnvilResultEvent.java | 6 +- .../xyz/alexcrea/cuanvil/anvil/AnvilCost.kt | 55 +++++++++++++++++++ .../alexcrea/cuanvil/anvil/AnvilMergeLogic.kt | 14 +++-- .../cuanvil/dependency/DependencyManager.kt | 4 +- .../plugins/DisenchantmentDependency.kt | 2 +- .../dependency/plugins/HavenBagsDependency.kt | 2 +- .../cuanvil/listener/AnvilResultListener.kt | 4 +- .../cuanvil/listener/PrepareAnvilListener.kt | 2 +- .../cuanvil/util/anvil/AnvilLoreEditUtil.kt | 40 ++++++++------ .../cuanvil/util/anvil/AnvilXpUtil.kt | 54 +----------------- 11 files changed, 101 insertions(+), 90 deletions(-) create mode 100644 src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilCost.kt diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResult2Event.java b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResult2Event.java index aa8f8e1..30c5380 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResult2Event.java +++ b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResult2Event.java @@ -8,8 +8,8 @@ import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import xyz.alexcrea.cuanvil.anvil.AnvilCost; import xyz.alexcrea.cuanvil.anvil.AnvilUseType; -import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil; /** * Called after custom anvil processed the click on the result on the anvil inventory. @@ -48,7 +48,7 @@ public class CATreatAnvilResult2Event extends Event { @Nullable private ItemStack result; - private final AnvilXpUtil.AnvilCost cost; + private final AnvilCost cost; @ApiStatus.Internal public CATreatAnvilResult2Event( @@ -56,7 +56,7 @@ public class CATreatAnvilResult2Event extends Event { Inventory inv, AnvilUseType useType, @Nullable ItemStack result, - AnvilXpUtil.AnvilCost cost) { + AnvilCost cost) { this.view = view; this.useType = useType; @@ -190,7 +190,7 @@ public class CATreatAnvilResult2Event extends Event { * * @return the current anvil cost */ - public AnvilXpUtil.AnvilCost getCost() { + public AnvilCost getCost() { return cost; } } diff --git a/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResultEvent.java b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResultEvent.java index 6e6358d..80965b5 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResultEvent.java +++ b/src/main/java/xyz/alexcrea/cuanvil/api/event/listener/CATreatAnvilResultEvent.java @@ -6,8 +6,8 @@ import org.bukkit.event.inventory.PrepareAnvilEvent; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import xyz.alexcrea.cuanvil.anvil.AnvilCost; import xyz.alexcrea.cuanvil.anvil.AnvilUseType; -import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil.AnvilCost; /** * Called after custom anvil processed the click on the result on the anvil inventory. @@ -108,8 +108,8 @@ public class CATreatAnvilResultEvent extends Event { *

  • Item rename
  • * * - * @deprecated use #{@link #getCost()} instead * @return The current cost. + * @deprecated use #{@link #getCost()} instead */ @Deprecated(forRemoval = true, since = "1.17.0") public int getLevelCost() { @@ -130,8 +130,8 @@ public class CATreatAnvilResultEvent extends Event { *
  • Item rename
  • * * - * @deprecated use #{@link #getCost()} and set value on this instead * @param levelCost The new cost. + * @deprecated use #{@link #getCost()} and set value on this instead */ @Deprecated(forRemoval = true, since = "1.17.0") public void setLevelCost(int levelCost) { diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilCost.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilCost.kt new file mode 100644 index 0000000..f8ff89c --- /dev/null +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilCost.kt @@ -0,0 +1,55 @@ +package xyz.alexcrea.cuanvil.anvil + +import java.math.BigDecimal +import io.delilaheve.util.ConfigOptions.getMonetaryMultiplier as moneyMultiplier + +open class AnvilCost { + private val isAlone: Boolean + var valid = true // Get set as invalid if cost can be satisfied + var isMonetary = false + + var generic = 0 + var enchantment = 0 + var repair = 0 + var rename = 0 + var lore = 0 + var illegalPenalty = 0 + var workPenalty = 0 + var recipe = 0 + + constructor(generic: Int) { + this.generic = generic + isAlone = true + } + + constructor() { + isAlone = false + } + + fun asXpCost(): Int { + return generic + enchantment + repair + rename + lore + illegalPenalty + workPenalty + recipe + } + + open fun asMonetaryCost(): BigDecimal { + // multiply by per use type multipliers + return BigDecimal(generic) + .add(BigDecimal(enchantment).multiply(moneyMultiplier("enchantment"))) + .add(BigDecimal(repair).multiply(moneyMultiplier("repair"))) + .add(BigDecimal(rename).multiply(moneyMultiplier("rename"))) + .add(BigDecimal(lore).multiply(moneyMultiplier("lore_edit"))) + .add(BigDecimal(enchantment).multiply(moneyMultiplier("enchantment"))) + .add(BigDecimal(illegalPenalty).multiply(moneyMultiplier("work_penalty"))) + .add(BigDecimal(workPenalty).multiply(moneyMultiplier("work_penalty"))) + .add(BigDecimal(recipe).multiply(moneyMultiplier("recipe"))) + .multiply(moneyMultiplier("global")) + } +} + +class CustomCraftCost(val rawCost: Int): AnvilCost() { + + override fun asMonetaryCost(): BigDecimal { + return BigDecimal(rawCost) + .multiply(moneyMultiplier("global")) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt index 67b9b7d..6b106fc 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt @@ -29,8 +29,6 @@ import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair import xyz.alexcrea.cuanvil.util.anvil.AnvilColorUtil import xyz.alexcrea.cuanvil.util.anvil.AnvilLoreEditUtil import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil -import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil.AnvilCost -import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil.CustomCraftCost import xyz.alexcrea.cuanvil.util.config.LoreEditType import xyz.alexcrea.cuanvil.util.dialog.AnvilRenameDialogUtil @@ -115,7 +113,8 @@ object AnvilMergeLogic { } cost.workPenalty = AnvilXpUtil.calculatePenalty(first, null, resultItem, AnvilUseType.RENAME_ONLY) - val result = DependencyManager.tryTreatAnvilResult(view, inventory, player, resultItem, AnvilUseType.RENAME_ONLY, cost) + val result = + DependencyManager.tryTreatAnvilResult(view, inventory, player, resultItem, AnvilUseType.RENAME_ONLY, cost) return AnvilResult(result, cost) } @@ -220,7 +219,8 @@ object AnvilMergeLogic { // Calculate rename cost cost.rename = handleRename(resultItem, inventory, player) - val result = DependencyManager.tryTreatAnvilResult(view, inventory, player, resultItem, AnvilUseType.MERGE, cost) + val result = + DependencyManager.tryTreatAnvilResult(view, inventory, player, resultItem, AnvilUseType.MERGE, cost) return AnvilResult(result, cost) } @@ -261,7 +261,8 @@ object AnvilMergeLogic { cost.recipe = if (recipe.removeExactLinearXp) AnvilXpUtil.calculateMinimumLevelForXp(xpCost) else AnvilXpUtil.calculateLevelForXp(xpCost) - val result = DependencyManager.tryTreatAnvilResult(view, inventory, player, resultItem, AnvilUseType.CUSTOM_CRAFT, cost) + val result = + DependencyManager.tryTreatAnvilResult(view, inventory, player, resultItem, AnvilUseType.CUSTOM_CRAFT, cost) return CustomCraftResult(result, cost, amount, recipe) } @@ -300,7 +301,8 @@ object AnvilMergeLogic { return UnitRepairResult.EMPTY } - val result = DependencyManager.tryTreatAnvilResult(view, inventory, player, resultItem, AnvilUseType.UNIT_REPAIR, cost) + val result = + DependencyManager.tryTreatAnvilResult(view, inventory, player, resultItem, AnvilUseType.UNIT_REPAIR, cost) return UnitRepairResult(result, cost, repairAmount) } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt index 0da6b30..cb24eaa 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt @@ -14,6 +14,7 @@ import org.bukkit.inventory.AnvilInventory import org.bukkit.inventory.Inventory import org.bukkit.inventory.InventoryView import org.bukkit.inventory.ItemStack +import xyz.alexcrea.cuanvil.anvil.AnvilCost import xyz.alexcrea.cuanvil.anvil.AnvilUseType import xyz.alexcrea.cuanvil.api.event.listener.CAClickResultBypassEvent import xyz.alexcrea.cuanvil.api.event.listener.CAEarlyPreAnvilBypassEvent @@ -33,7 +34,6 @@ import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil.componentLore import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_OUTPUT_SLOT import xyz.alexcrea.cuanvil.util.MetricsUtil.trackError -import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil import java.util.logging.Level object DependencyManager { @@ -240,7 +240,7 @@ object DependencyManager { player: HumanEntity, result: ItemStack, useType: AnvilUseType, - cost: AnvilXpUtil.AnvilCost + cost: AnvilCost ): ItemStack? { val treatEvent = CATreatAnvilResult2Event(view, inventory, useType, result, cost) try { diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/DisenchantmentDependency.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/DisenchantmentDependency.kt index 32ca99d..690b384 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/DisenchantmentDependency.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/DisenchantmentDependency.kt @@ -14,10 +14,10 @@ import org.bukkit.event.inventory.InventoryClickEvent import org.bukkit.event.inventory.PrepareAnvilEvent import org.bukkit.inventory.AnvilInventory import org.bukkit.inventory.ItemStack +import xyz.alexcrea.cuanvil.anvil.AnvilCost import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener import xyz.alexcrea.cuanvil.util.MetricsUtil.trackError import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil -import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil.AnvilCost import java.util.logging.Level import kotlin.reflect.KClass diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/HavenBagsDependency.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/HavenBagsDependency.kt index 62d9e4e..6f30497 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/HavenBagsDependency.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/HavenBagsDependency.kt @@ -9,9 +9,9 @@ import org.bukkit.plugin.RegisteredListener import valorless.havenbags.HavenBags import valorless.havenbags.features.BagSkin import valorless.havenbags.features.BagUpgrade +import xyz.alexcrea.cuanvil.anvil.AnvilCost import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil -import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil.AnvilCost class HavenBagsDependency { diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt index 775a685..feee833 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt @@ -15,6 +15,7 @@ import org.bukkit.inventory.AnvilInventory import org.bukkit.inventory.InventoryView import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.BookMeta +import xyz.alexcrea.cuanvil.anvil.AnvilCost import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.AnvilResult import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.CustomCraftResult @@ -30,10 +31,8 @@ import xyz.alexcrea.cuanvil.util.CustomRecipeUtil import xyz.alexcrea.cuanvil.util.MiniMessageUtil import xyz.alexcrea.cuanvil.util.anvil.AnvilLoreEditUtil import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil -import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil.AnvilCost import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil import xyz.alexcrea.cuanvil.util.config.LoreEditType -import java.math.BigDecimal import java.util.* import java.util.concurrent.atomic.AtomicReference import kotlin.math.min @@ -483,7 +482,6 @@ class AnvilResultListener : Listener { val paperMeta = rightItem.itemMeta ?: return - val paperCopy: ItemStack? if (LoreEditType.APPEND_PAPER.doConsume) { paperCopy = null diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt index b2f6785..0217983 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt @@ -15,6 +15,7 @@ import org.bukkit.inventory.InventoryView import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.EnchantmentStorageMeta import org.bukkit.inventory.meta.ItemMeta +import xyz.alexcrea.cuanvil.anvil.AnvilCost import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.AnvilResult import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.doMerge import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.doRenaming @@ -24,7 +25,6 @@ import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.testUnitRepair import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.util.MaterialUtil.isAir import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil -import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil.AnvilCost import xyz.alexcrea.cuanvil.util.dialog.AnvilRenameDialogUtil /** diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilLoreEditUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilLoreEditUtil.kt index e9bb633..a021b46 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilLoreEditUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilLoreEditUtil.kt @@ -5,12 +5,12 @@ import org.bukkit.entity.HumanEntity import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.BookMeta import org.bukkit.permissions.Permissible +import xyz.alexcrea.cuanvil.anvil.AnvilCost import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.LoreEditResult import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil.componentLore import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil.setComponentLore import xyz.alexcrea.cuanvil.util.MiniMessageUtil -import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil.AnvilCost import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil import xyz.alexcrea.cuanvil.util.config.LoreEditType import java.util.* @@ -44,8 +44,10 @@ object AnvilLoreEditUtil { val page = book.pages[0] val lines = ArrayList(page.split("\n")) val outLines = ArrayList(lines.size) - val colorCost = colorLines(player, LoreEditType.APPEND_BOOK, - lines, outLines) + val colorCost = colorLines( + player, LoreEditType.APPEND_BOOK, + lines, outLines + ) lore.addAll(outLines) @@ -81,7 +83,7 @@ object AnvilLoreEditUtil { // Handle xp cost.lore = uncolorCost - cost.lore+= currentLore.size * LoreEditType.REMOVE_BOOK.perLineCost + cost.lore += currentLore.size * LoreEditType.REMOVE_BOOK.perLineCost baseEditLoreXpCost(cost, first, result, LoreEditType.REMOVE_BOOK) return result @@ -120,7 +122,7 @@ object AnvilLoreEditUtil { fun tryLoreEditByBook(player: HumanEntity, first: ItemStack, second: ItemStack): LoreEditResult { val isAppend = bookLoreEditIsAppend(first, second) ?: return LoreEditResult.EMPTY - val type = if(isAppend) LoreEditType.APPEND_BOOK else LoreEditType.REMOVE_BOOK + val type = if (isAppend) LoreEditType.APPEND_BOOK else LoreEditType.REMOVE_BOOK val meta = second.itemMeta as BookMeta val cost = AnvilCost() @@ -166,9 +168,11 @@ object AnvilLoreEditUtil { // A bit overdone to color 1 line but hey val outList = ArrayList(1) - val colorCost = colorLines(player, LoreEditType.APPEND_PAPER, + val colorCost = colorLines( + player, LoreEditType.APPEND_PAPER, Collections.singletonList(second.itemMeta!!.displayName), - outList) + outList + ) val line = outList[0] if (appendEnd) @@ -232,7 +236,7 @@ object AnvilLoreEditUtil { second: ItemStack ): LoreEditResult { val isAppend = paperLoreEditIsAppend(first, second) ?: return LoreEditResult.EMPTY - val type = if(isAppend) LoreEditType.APPEND_BOOK else LoreEditType.REMOVE_BOOK + val type = if (isAppend) LoreEditType.APPEND_BOOK else LoreEditType.REMOVE_BOOK val cost = AnvilCost() val item = if (isAppend) @@ -248,18 +252,20 @@ object AnvilLoreEditUtil { result: ItemStack, editType: LoreEditType ) { - cost.lore+= editType.fixedCost + cost.lore += editType.fixedCost cost.workPenalty = AnvilXpUtil.calculatePenalty(first, null, result, editType.useType) } fun colorPermission(player: Permissible, editType: LoreEditType): AnvilColorUtil.ColorPermissions { - return AnvilColorUtil.calculatePermissions(player, + return AnvilColorUtil.calculatePermissions( + player, false, editType.allowColorCode, editType.allowHexColor, editType.allowMinimessage, - AnvilColorUtil.ColorUseType.LORE_EDIT) + AnvilColorUtil.ColorUseType.LORE_EDIT + ) } private fun colorLine(line: String, permission: AnvilColorUtil.ColorPermissions): Component? { @@ -269,8 +275,10 @@ object AnvilLoreEditUtil { ) } - private fun colorLines(player: Permissible, editType: LoreEditType, - lines: List, outLines: MutableList): Int { + private fun colorLines( + player: Permissible, editType: LoreEditType, + lines: List, outLines: MutableList + ): Int { val permission = colorPermission(player, editType) val colorCost = editType.useColorCost @@ -297,7 +305,7 @@ object AnvilLoreEditUtil { // Now handle color of each lines var hasUndidColor = false for ((index, line) in lines.withIndex()) { - if(line == null){ + if (line == null) { lines[index] = null continue } @@ -312,7 +320,7 @@ object AnvilLoreEditUtil { hasUndidColor = true result = clearedLine } else { - result = MiniMessageUtil.plain_text_mm.serialize(line) + result = MiniMessageUtil.plain_text_mm.serialize(line) } lines[index] = MiniMessageUtil.plain_text_mm.deserialize(result) @@ -341,7 +349,7 @@ object AnvilLoreEditUtil { var hasUndidColor = false val result: String - if(clearedLine != null){ + if (clearedLine != null) { hasUndidColor = true result = clearedLine } else { diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilXpUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilXpUtil.kt index 8c39a0a..f6ee12f 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilXpUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/anvil/AnvilXpUtil.kt @@ -14,6 +14,7 @@ import org.bukkit.inventory.InventoryView import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.Repairable import org.bukkit.persistence.PersistentDataType +import xyz.alexcrea.cuanvil.anvil.AnvilCost import xyz.alexcrea.cuanvil.anvil.AnvilUseType import xyz.alexcrea.cuanvil.config.ConfigHolder import xyz.alexcrea.cuanvil.dependency.DependencyManager @@ -21,65 +22,12 @@ import xyz.alexcrea.cuanvil.dependency.economy.EconomyManager import xyz.alexcrea.cuanvil.group.ConflictType import xyz.alexcrea.cuanvil.util.AnvilTitleUtil import xyz.alexcrea.cuanvil.util.dialog.AnvilRenameDialogUtil -import java.math.BigDecimal import kotlin.math.min -import io.delilaheve.util.ConfigOptions.getMonetaryMultiplier as moneyMultiplier object AnvilXpUtil { const val EXCLUSIVE_PENALTY_PREFIX = "repair_cost" - open class AnvilCost { - private val isAlone: Boolean - var valid = true // Get set as invalid if cost can be satisfied - var isMonetary = false - - var generic = 0 - var enchantment = 0 - var repair = 0 - var rename = 0 - var lore = 0 - var illegalPenalty = 0 - var workPenalty = 0 - var recipe = 0 - - constructor(generic: Int) { - this.generic = generic - isAlone = true - } - - constructor() { - isAlone = false - } - - fun asXpCost(): Int { - return generic + enchantment + repair + rename + lore + illegalPenalty + workPenalty + recipe - } - - open fun asMonetaryCost(): BigDecimal { - // multiply by per use type multipliers - return BigDecimal(generic) - .add(BigDecimal(enchantment).multiply(moneyMultiplier("enchantment"))) - .add(BigDecimal(repair).multiply(moneyMultiplier("repair"))) - .add(BigDecimal(rename).multiply(moneyMultiplier("rename"))) - .add(BigDecimal(lore).multiply(moneyMultiplier("lore_edit"))) - .add(BigDecimal(enchantment).multiply(moneyMultiplier("enchantment"))) - .add(BigDecimal(illegalPenalty).multiply(moneyMultiplier("work_penalty"))) - .add(BigDecimal(workPenalty).multiply(moneyMultiplier("work_penalty"))) - .add(BigDecimal(recipe).multiply(moneyMultiplier("recipe"))) - .multiply(moneyMultiplier("global")) - } - } - - class CustomCraftCost(val rawCost: Int): AnvilCost() { - - override fun asMonetaryCost(): BigDecimal { - return BigDecimal(rawCost) - .multiply(moneyMultiplier("global")) - } - - } - /** * Display the required cost (either as xp or as ) */ From 2f9d25bfe9950526fcade3883392192cdbf928a4 Mon Sep 17 00:00:00 2001 From: alexcrea Date: Wed, 10 Jun 2026 15:18:31 +0200 Subject: [PATCH 24/24] fix fake prepare anvil on modern versions --- .../cuanvil/util/ModernPrepareAnvilCreator.kt | 12 ++++++++++++ .../plugins/ExcellentEnchantsDependency.kt | 12 ++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 nms/v1_21R1/src/main/kotlin/xyz/alexcrea/cuanvil/util/ModernPrepareAnvilCreator.kt diff --git a/nms/v1_21R1/src/main/kotlin/xyz/alexcrea/cuanvil/util/ModernPrepareAnvilCreator.kt b/nms/v1_21R1/src/main/kotlin/xyz/alexcrea/cuanvil/util/ModernPrepareAnvilCreator.kt new file mode 100644 index 0000000..0c51e0a --- /dev/null +++ b/nms/v1_21R1/src/main/kotlin/xyz/alexcrea/cuanvil/util/ModernPrepareAnvilCreator.kt @@ -0,0 +1,12 @@ +package xyz.alexcrea.cuanvil.util + +import org.bukkit.event.inventory.PrepareAnvilEvent +import org.bukkit.inventory.InventoryView +import org.bukkit.inventory.ItemStack +import org.bukkit.inventory.view.AnvilView + +object ModernPrepareAnvilCreator { + fun createPrepareAnvil(view: InventoryView, item: ItemStack?): PrepareAnvilEvent { + return PrepareAnvilEvent(view as AnvilView, item) + } +} diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/ExcellentEnchantsDependency.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/ExcellentEnchantsDependency.kt index 39bfad7..c2d3019 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/ExcellentEnchantsDependency.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/plugins/ExcellentEnchantsDependency.kt @@ -13,6 +13,7 @@ import xyz.alexcrea.cuanvil.enchant.wrapped.CAEEPreV5Enchantment import xyz.alexcrea.cuanvil.enchant.wrapped.CAEEV5Enchantment import xyz.alexcrea.cuanvil.enchant.wrapped.CAEEV5_4Enchantment import xyz.alexcrea.cuanvil.enchant.wrapped.CALegacyEEEnchantment +import xyz.alexcrea.cuanvil.util.ModernPrepareAnvilCreator import java.lang.reflect.Method import su.nightexpress.excellentenchants.api.EnchantRegistry as V5EnchantRegistry import su.nightexpress.excellentenchants.enchantment.impl.universal.CurseOfFragilityEnchant as LegacyCurseOfFragilityEnchant @@ -219,13 +220,16 @@ class ExcellentEnchantsDependency { } fun treatAnvilResult(event: CATreatAnvilResult2Event) { - val result = event.result - if (result == null) return + val result = event.result ?: return val first: ItemStack = treatInput(event.leftItem) val second: ItemStack = treatInput(event.rightItem) - - val fakeEvent = PrepareAnvilEvent(event.view, result) + val fakeEvent: PrepareAnvilEvent = try { + //TODO remove this on legacy removal + PrepareAnvilEvent(event.view, result) + } catch (_: NoSuchMethodError) { + ModernPrepareAnvilCreator.createPrepareAnvil(event.view, result) + } handleCombineMethod.invoke(this.usedAnvilListener, fakeEvent, first, second, result) event.result = fakeEvent.result