diff --git a/build.gradle.kts b/build.gradle.kts index 42277a1..c2bb1a2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,7 @@ plugins { } group = "io.delilaheve" -version = "1.0.1" +version = "1.0.2" repositories { mavenCentral() diff --git a/src/main/kotlin/io/delilaheve/AnvilEventListener.kt b/src/main/kotlin/io/delilaheve/AnvilEventListener.kt index aedd6d6..d8e79b5 100644 --- a/src/main/kotlin/io/delilaheve/AnvilEventListener.kt +++ b/src/main/kotlin/io/delilaheve/AnvilEventListener.kt @@ -1,22 +1,23 @@ package io.delilaheve import io.delilaheve.util.ConfigOptions -import io.delilaheve.util.EnchantmentUtil.calculateValue import io.delilaheve.util.EnchantmentUtil.combineWith import io.delilaheve.util.EnchantmentUtil.hasConflicts import io.delilaheve.util.ItemUtil.canMergeWith import io.delilaheve.util.ItemUtil.findEnchantments import io.delilaheve.util.ItemUtil.isBook +import io.delilaheve.util.ItemUtil.repairCost +import io.delilaheve.util.ItemUtil.repairFrom import io.delilaheve.util.ItemUtil.setEnchantmentsUnsafe -import org.bukkit.Material import org.bukkit.entity.Player import org.bukkit.event.Event import org.bukkit.event.EventHandler +import org.bukkit.event.EventPriority.HIGHEST import org.bukkit.event.Listener import org.bukkit.event.inventory.InventoryClickEvent import org.bukkit.event.inventory.PrepareAnvilEvent import org.bukkit.inventory.AnvilInventory -import org.bukkit.inventory.InventoryView +import org.bukkit.inventory.InventoryView.Property.REPAIR_COST import org.bukkit.permissions.Permission import kotlin.math.min @@ -26,8 +27,6 @@ import kotlin.math.min class AnvilEventListener : Listener { companion object { - // Vanilla repair cost limit - private const val VANILLA_REPAIR_LIMIT = 40 // Anvil's output slot private const val ANVIL_OUTPUT_SLOT = 2 } @@ -39,34 +38,42 @@ class AnvilEventListener : Listener { /** * Event handler logic for when an anvil contains items to be combined */ - @EventHandler + @EventHandler(priority = HIGHEST) fun anvilCombineCheck(event: PrepareAnvilEvent) { val inventory = event.inventory val first = inventory.getItem(0) ?: return val second = inventory.getItem(1) ?: return if (first.canMergeWith(second)) { - val firstEnchants = first.findEnchantments().toMutableMap() - val secondEnchants = second.findEnchantments().toMutableMap() - if (ConfigOptions.removeRepairLimit) { - inventory.maximumRepairCost = Int.MAX_VALUE - } - val newEnchants = firstEnchants.combineWith(secondEnchants) - val enchantsString = newEnchants.map { "${it.key.key} ${it.value}" }.joinToString(", ") - UnsafeEnchants.log("New enchants for this item: $enchantsString") + val newEnchants = first.findEnchantments() + .combineWith(second.findEnchantments()) val resultItem = first.clone() + resultItem.itemMeta?.let { + it.setDisplayName(inventory.renameText) + resultItem.itemMeta = it + } resultItem.setEnchantmentsUnsafe(newEnchants) - val firstValue = firstEnchants.calculateValue(first.isBook()) - val secondValue = secondEnchants.calculateValue(second.isBook()) - var repairCost = firstValue + secondValue - if (first.isBook() && second.isBook()) { - repairCost = firstEnchants.values.sum() + secondEnchants.values.sum() + if (!first.isBook() && !second.isBook()) { + // we only need to be concerned with repair when neither item is a book + resultItem.repairFrom(first, second) } + var repairCost = first.repairCost + second.repairCost if (ConfigOptions.limitRepairCost) { - repairCost = min(repairCost, VANILLA_REPAIR_LIMIT) + repairCost = min(repairCost, ConfigOptions.limitRepairValue) } - inventory.repairCost = repairCost - event.view.setProperty(InventoryView.Property.REPAIR_COST, repairCost) event.result = resultItem + /* Because Minecraft likes to have the final say in the repair cost displayed + * we need to wait for the event to end before overriding it, this ensures that + * we have the final say in the process. */ + UnsafeEnchants.instance + .server + .scheduler + .runTask(UnsafeEnchants.instance, Runnable { + if (ConfigOptions.removeRepairLimit) { + inventory.maximumRepairCost = Int.MAX_VALUE + } + inventory.repairCost = repairCost + event.view.setProperty(REPAIR_COST, repairCost) + }) } } @@ -80,8 +87,6 @@ class AnvilEventListener : Listener { val output = inventory.getItem(ANVIL_OUTPUT_SLOT) ?: return if (output.findEnchantments().hasConflicts() && !player.hasPermission(requirePermission)) { return } if (event.rawSlot != ANVIL_OUTPUT_SLOT) { return } - if (output.type == Material.AIR) { return } - if (player.level < inventory.repairCost) { return } event.result = Event.Result.ALLOW } diff --git a/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt b/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt index 5f5bf12..1d402e5 100644 --- a/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt +++ b/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt @@ -15,6 +15,8 @@ object ConfigOptions { private const val ALLOW_UNSAFE_PATH = "allow_unsafe" // Path for limiting repair cost private const val LIMIT_REPAIR_COST = "limit_repair_cost" + // Path for repair value limit + private const val LIMIT_REPAIR_VALUE = "limit_repair_value" // Path for removing repair cost limits private const val REMOVE_REPAIR_LIMIT = "remove_repair_limit" // Root path for enchantment limits @@ -33,6 +35,10 @@ object ConfigOptions { private const val DEFAULT_ALLOW_UNSAFE = true // Default value for limiting repair cost private const val DEFAULT_LIMIT_REPAIR = true + // Default value for repair cost limit + private const val DEFAULT_LIMIT_REPAIR_VALUE = 39 + // Valid range for repair cost limit + private val REPAIR_LIMIT_RANGE = 1..39 // Default for removing repair cost limits private const val DEFAULT_REMOVE_LIMIT = false // Valid range for an enchantment limit @@ -72,6 +78,18 @@ object ConfigOptions { .getBoolean(LIMIT_REPAIR_COST, DEFAULT_LIMIT_REPAIR) } + /** + * Value to limit repair costs to + */ + val limitRepairValue: Int + get() { + return UnsafeEnchants.instance + .config + .getInt(LIMIT_REPAIR_VALUE, DEFAULT_LIMIT_REPAIR_VALUE) + .takeIf { it in REPAIR_LIMIT_RANGE } + ?: DEFAULT_LIMIT_REPAIR_VALUE + } + /** * Whether to remove repair cost limit */ diff --git a/src/main/kotlin/io/delilaheve/util/EnchantmentUtil.kt b/src/main/kotlin/io/delilaheve/util/EnchantmentUtil.kt index 2065a7a..093e543 100644 --- a/src/main/kotlin/io/delilaheve/util/EnchantmentUtil.kt +++ b/src/main/kotlin/io/delilaheve/util/EnchantmentUtil.kt @@ -19,8 +19,8 @@ object EnchantmentUtil { /** * Combine 2 sets of enchantments according to our configuration */ - fun MutableMap.combineWith( - other: MutableMap + fun Map.combineWith( + other: Map ) = mutableMapOf().apply { putAll(this@combineWith) other.forEach { (enchantment, level) -> diff --git a/src/main/kotlin/io/delilaheve/util/ItemUtil.kt b/src/main/kotlin/io/delilaheve/util/ItemUtil.kt index 72cecbb..d54b28c 100644 --- a/src/main/kotlin/io/delilaheve/util/ItemUtil.kt +++ b/src/main/kotlin/io/delilaheve/util/ItemUtil.kt @@ -1,17 +1,36 @@ package io.delilaheve.util import io.delilaheve.UnsafeEnchants +import io.delilaheve.util.EnchantmentUtil.calculateValue +import io.delilaheve.util.ItemUtil.isBook import org.bukkit.Material.BOOK import org.bukkit.Material.ENCHANTED_BOOK import org.bukkit.enchantments.Enchantment import org.bukkit.inventory.ItemStack +import org.bukkit.inventory.meta.Damageable import org.bukkit.inventory.meta.EnchantmentStorageMeta +import org.bukkit.inventory.meta.ItemMeta +import org.bukkit.inventory.meta.Repairable +import kotlin.math.min /** * Item manipulation utilities */ object ItemUtil { + /** + * Determine the value of an item for repair + * + * ToDo - use native repair cost + */ + val ItemStack.repairCost: Int + get() { + val nativeCost = (itemMeta as? Repairable)?.repairCost + val pluginCost = findEnchantments().calculateValue(isBook()) + UnsafeEnchants.log("Native Cost: $nativeCost; Plugin Cost: $pluginCost") + return nativeCost.takeIf { it != 0 } ?: pluginCost + } + /** * Check if this [ItemStack] is a [BOOK] or [ENCHANTED_BOOK] */ @@ -72,6 +91,25 @@ object ItemUtil { } } + /** + * Set this [ItemStack]s durability from a combination of the + * [first] and [second] item's durability values + */ + fun ItemStack.repairFrom( + first: ItemStack, + second: ItemStack + ) { + (itemMeta as? Damageable)?.let { + val maxDurability = type.maxDurability.toInt() + val firstDurability = (first.itemMeta as? Damageable)?.damage ?: 0 + val secondDurability = (second.itemMeta as? Damageable)?.damage ?: 0 + var newDurability = firstDurability + secondDurability + newDurability = min(maxDurability, newDurability) + it.damage = newDurability + itemMeta = it as ItemMeta + } + } + /** * Check that this [ItemStack] can merge with the [other] * diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 00b208c..5a38ff4 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -11,9 +11,16 @@ default_limit: 10 # i.e. Protection and Blast Protection can be on the same piece of armour allow_unsafe: true -# Whether all anvil actions should be capped to the vanilla repair limit (40 levels) +# Whether all anvil actions should be capped +# +# If true, all anvil repairs will max out at the value of limit_repair_value limit_repair_cost: true +# Value to limit repair costs to when limit_repair_cost is true +# +# Valid range of 1 - 39 (vanilla will consider 40+ as "too expensive") +limit_repair_value: 39 + # Whether the anvil's repair limit should be removed entirely # # The anvil will still visually display "too expensive" however the action will be completable diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index cfc4adb..b5c9f0b 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,6 +1,6 @@ main: io.delilaheve.UnsafeEnchants name: UnsafeEnchants -version: 1.0.1 +version: 1.0.2 description: Allow all enchants to be combined api-version: 1.18 load: POSTWORLD