From bd28c5b71c151fe1dd5b34186473163462c4fe43 Mon Sep 17 00:00:00 2001 From: alexcrea Date: Sat, 3 Feb 2024 13:25:18 +0100 Subject: [PATCH] add xp rename cost and fix non vanilla anvil beavior. but keep some protection to avoid useless fuse. --- .../io/delilaheve/AnvilEventListener.kt | 54 +++++++-------- .../io/delilaheve/util/EnchantmentUtil.kt | 68 ++++++++++++------- .../alexcrea/group/EnchantConflictGroup.kt | 20 ++++-- .../alexcrea/group/EnchantConflictManager.kt | 41 +++++++++-- 4 files changed, 115 insertions(+), 68 deletions(-) diff --git a/src/main/kotlin/io/delilaheve/AnvilEventListener.kt b/src/main/kotlin/io/delilaheve/AnvilEventListener.kt index 83ff030..12a9da5 100644 --- a/src/main/kotlin/io/delilaheve/AnvilEventListener.kt +++ b/src/main/kotlin/io/delilaheve/AnvilEventListener.kt @@ -29,6 +29,8 @@ class AnvilEventListener : Listener { companion object { // Anvil's output slot + private const val ANVIL_INPUT_LEFT = 0 + private const val ANVIL_INPUT_RIGHT = 1 private const val ANVIL_OUTPUT_SLOT = 2 } @@ -38,19 +40,15 @@ class AnvilEventListener : Listener { @EventHandler(priority = HIGHEST) fun anvilCombineCheck(event: PrepareAnvilEvent) { val inventory = event.inventory - val first = inventory.getItem(0) ?: return - val second = inventory.getItem(1) ?: return + val first = inventory.getItem(ANVIL_INPUT_LEFT) ?: return + val second = inventory.getItem(ANVIL_INPUT_RIGHT) ?: return if (first.canMergeWith(second)) { // Try to find player val player = event.view.player val newEnchants = first.findEnchantments() - .combineWith(second.findEnchantments(),player) + .combineWith(second.findEnchantments(), first.type, player) val resultItem = first.clone() - resultItem.itemMeta?.let { - it.setDisplayName(inventory.renameText) - resultItem.itemMeta = it - } resultItem.setEnchantmentsUnsafe(newEnchants) var repairCost: Int if (!first.isBook() && !second.isBook()) { @@ -61,17 +59,26 @@ class AnvilEventListener : Listener { repairCost = resultItem.repairCost } + // Test if nothing change and stop. + if(first == resultItem){ + event.result = null + return + } + + // Rename item and add renaming cost + resultItem.itemMeta?.let { + if(!it.displayName.contentEquals(inventory.renameText)){ + it.setDisplayName(inventory.renameText) + resultItem.itemMeta = it + repairCost += 1 + } + } + if (ConfigOptions.limitRepairCost) { repairCost = min(repairCost, ConfigOptions.limitRepairValue) } - // Set object only if allowed - if(itemAllowed(resultItem,player)){ - event.result = resultItem - } else{ - event.result = null - return - } + 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 @@ -94,29 +101,16 @@ class AnvilEventListener : Listener { */ @EventHandler(ignoreCancelled = true) fun anvilExtractionCheck(event: InventoryClickEvent) { - val player = event.whoClicked as? Player ?: return + //val player = event.whoClicked as? Player ?: return val inventory = event.inventory as? AnvilInventory ?: return if (event.rawSlot != ANVIL_OUTPUT_SLOT) { return } val output = inventory.getItem(ANVIL_OUTPUT_SLOT) ?: return - // Should be true most of the time - // But if permissions change in the anvil it can be false - if(!itemAllowed(output,player)){ + // Is true if there was no change. probably when there are conflict + if(output == inventory.getItem(ANVIL_INPUT_LEFT)){ event.result = Event.Result.DENY return } event.result = Event.Result.ALLOW } - - private fun itemAllowed(item: ItemStack, player: HumanEntity): Boolean{ - if(player.hasPermission(UnsafeEnchants.bypassFusePermission)) return true - - if(player.hasPermission(UnsafeEnchants.unsafePermission)){ - if(UnsafeEnchants.conflictManager.isConflicting(item)) - return false - }else if (item.findEnchantments().hasConflicts()){ - return false - } - return true - } } diff --git a/src/main/kotlin/io/delilaheve/util/EnchantmentUtil.kt b/src/main/kotlin/io/delilaheve/util/EnchantmentUtil.kt index fc627bb..ebe962f 100644 --- a/src/main/kotlin/io/delilaheve/util/EnchantmentUtil.kt +++ b/src/main/kotlin/io/delilaheve/util/EnchantmentUtil.kt @@ -1,6 +1,7 @@ package io.delilaheve.util import io.delilaheve.UnsafeEnchants +import org.bukkit.Material import org.bukkit.enchantments.Enchantment import org.bukkit.entity.HumanEntity import kotlin.math.max @@ -21,43 +22,58 @@ object EnchantmentUtil { * Combine 2 sets of enchantments according to our configuration */ fun Map.combineWith( - other: Map, player: HumanEntity + other: Map, + mat: Material, + player: HumanEntity ) = mutableMapOf().apply { putAll(this@combineWith) other.forEach { (enchantment, level) -> - when { - // Enchantment not yet in result list - !containsKey(enchantment) -> { - // Add the enchantment if it doesn't have conflicts, or, if we're allowing unsafe enchantments - if (!keys.any { enchantment.conflictsWith(it) } || ConfigOptions.allowUnsafe) { - this[enchantment] = level + // Enchantment not yet in result list + if (!containsKey(enchantment)) { + if(player.hasPermission(UnsafeEnchants.unsafePermission)){ + // Add the enchantment if it doesn't have conflicts, or, if player is allowed to bypass enchantment restrictions + this[enchantment] = level + if(!player.hasPermission(UnsafeEnchants.bypassFusePermission) && + UnsafeEnchants.conflictManager.isConflicting(this.keys,mat,enchantment)){ + this.remove(enchantment) } + }else if(!keys.any { enchantment.conflictsWith(it) }){ + + this[enchantment] = level } - // Enchantment already in result list... - else -> when { - // ... and they're not the same level - this[enchantment] != other[enchantment] -> { - val newLevel = max(this[enchantment] ?: 0, other[enchantment] ?: 0) - // apply the greater of the two if non-zero - if (newLevel > 0) { this[enchantment] = newLevel } - } - // ... and they're the same level - else -> { - // try to increase the enchantment level by 1 - var newLevel = this[enchantment]?.plus(1) ?: 0 - val maxLevel = if(player.hasPermission(UnsafeEnchants.bypassLevelPermission)){ - 255 - }else{ - ConfigOptions.enchantLimit(enchantment) - } - newLevel = min(newLevel, maxLevel) - if (newLevel > 0) { this[enchantment] = newLevel } + } + // Enchantment already in result list + else{ + // ... and they are conflicting + if(UnsafeEnchants.conflictManager.isConflicting(this.keys,mat,enchantment) + && !player.hasPermission(UnsafeEnchants.bypassFusePermission)){ + return@forEach + } + + // ... and they're not the same level + if(this[enchantment] != other[enchantment]){ + val newLevel = max(this[enchantment] ?: 0, other[enchantment] ?: 0) + // apply the greater of the two if non-zero + if (newLevel > 0) { this[enchantment] = newLevel } + } + // ... and they're the same level + else { + // try to increase the enchantment level by 1 + var newLevel = this[enchantment]!! +1 + // Get max level or 255 if player can bypass + val maxLevel = if(player.hasPermission(UnsafeEnchants.bypassLevelPermission)){ + 255 + }else{ + ConfigOptions.enchantLimit(enchantment) } + newLevel = min(newLevel, maxLevel) + if (newLevel > 0) { this[enchantment] = newLevel } } } } } + /** * Check if a set of enchantments has any conflicts */ diff --git a/src/main/kotlin/xyz/alexcrea/group/EnchantConflictGroup.kt b/src/main/kotlin/xyz/alexcrea/group/EnchantConflictGroup.kt index d0f425f..c4cded9 100644 --- a/src/main/kotlin/xyz/alexcrea/group/EnchantConflictGroup.kt +++ b/src/main/kotlin/xyz/alexcrea/group/EnchantConflictGroup.kt @@ -1,10 +1,11 @@ package xyz.alexcrea.group import io.delilaheve.util.ItemUtil.findEnchantments +import org.bukkit.Material import org.bukkit.enchantments.Enchantment import org.bukkit.inventory.ItemStack -class EnchantConflictGroup(val cantConflict: AbstractMaterialGroup, val minBeforeBlock: Int){ +class EnchantConflictGroup(private val cantConflict: AbstractMaterialGroup, private val minBeforeBlock: Int){ private val enchantments = HashSet() @@ -12,7 +13,7 @@ class EnchantConflictGroup(val cantConflict: AbstractMaterialGroup, val minBefor enchantments.add(ench) } - fun allow(item: ItemStack) : Boolean{ + fun allowed(item: ItemStack) : Boolean{ if(enchantments.size < minBeforeBlock){ return true } @@ -21,22 +22,27 @@ class EnchantConflictGroup(val cantConflict: AbstractMaterialGroup, val minBefor return true } + return allowed(item.enchantments.keys, item.type) + } + fun allowed(enchants: Set, mat: Material) : Boolean{ + if(cantConflict.contain(mat)){ + return true + } + // Count the amount of enchantment that are in the list var enchantAmount = 0 - for (enchantment in item.findEnchantments().keys) { + for (enchantment in enchants) { if(enchantment !in enchantments) continue if(++enchantAmount > minBeforeBlock){ return false } } - return true } - fun isEnchantEmpty(): Boolean { - return enchantments.size == 0 + fun getEnchants(): HashSet { + return enchantments } - } \ No newline at end of file diff --git a/src/main/kotlin/xyz/alexcrea/group/EnchantConflictManager.kt b/src/main/kotlin/xyz/alexcrea/group/EnchantConflictManager.kt index 42afb83..1e90544 100644 --- a/src/main/kotlin/xyz/alexcrea/group/EnchantConflictManager.kt +++ b/src/main/kotlin/xyz/alexcrea/group/EnchantConflictManager.kt @@ -1,6 +1,7 @@ package xyz.alexcrea.group import io.delilaheve.UnsafeEnchants +import org.bukkit.Material import org.bukkit.NamespacedKey import org.bukkit.configuration.ConfigurationSection import org.bukkit.configuration.file.YamlConfiguration @@ -26,23 +27,34 @@ class EnchantConflictManager { private const val DEFAULT_GROUP_NAME = "joinedGroup" } - private lateinit var conflictList: ArrayList + private lateinit var conflictMap: HashMap> // Read and prepare all conflict fun prepareConflicts(config: YamlConfiguration, itemManager: ItemGroupManager){ - conflictList = ArrayList() + conflictMap = HashMap() val keys = config.getKeys(false) for (key in keys) { val section = config.getConfigurationSection(key)!! val conflict = createConflict(section,itemManager,key) if(conflict != null){ - conflictList.add(conflict) + addToMap(conflict) } + } } + // Add the conflict to the map + private fun addToMap(conflict: EnchantConflictGroup){ + conflict.getEnchants().forEach{ enchant -> + if(!conflictMap.containsKey(enchant)){ + conflictMap[enchant] = ArrayList() + } + conflictMap[enchant]!!.add(conflict) + } + } + // create and read a conflict from a yaml section private fun createConflict(section: ConfigurationSection, itemManager: ItemGroupManager, @@ -64,7 +76,7 @@ class EnchantConflictManager { } conflict.addEnchantment(enchant) } - if(conflict.isEnchantEmpty()){ + if(conflict.getEnchants().size == 0){ if(!futureUse){ UnsafeEnchants.instance.logger.warning("Conflict $conflictName do not have valid enchantment, it will not work") } @@ -112,8 +124,27 @@ class EnchantConflictManager { } fun isConflicting(item: ItemStack): Boolean{ + val toTest = HashSet() + item.enchantments.forEach{enchant -> + val conflictList = conflictMap[enchant.key] + if(conflictList != null){ + toTest.addAll(conflictList) + } + } + + for (conflict in toTest) { + if(!conflict.allowed(item)) { + return true + } + } + return false + } + + fun isConflicting(base: Set,mat: Material, newEnchant: Enchantment): Boolean{ + val conflictList = conflictMap[newEnchant] ?: return false + for (conflict in conflictList) { - if(!conflict.allow(item)) { + if(!conflict.allowed(base,mat)) { return true } }