Fix most issues with xp cost

This commit is contained in:
alexcrea 2024-02-03 22:24:33 +01:00
parent 0bb496938c
commit 87b8738c6e
6 changed files with 94 additions and 79 deletions

View file

@ -2,15 +2,12 @@ package io.delilaheve
import io.delilaheve.util.ConfigOptions import io.delilaheve.util.ConfigOptions
import io.delilaheve.util.EnchantmentUtil.combineWith import io.delilaheve.util.EnchantmentUtil.combineWith
import io.delilaheve.util.EnchantmentUtil.hasConflicts import io.delilaheve.util.EnchantmentUtil.enchantmentName
import io.delilaheve.util.ItemUtil.canMergeWith import io.delilaheve.util.ItemUtil.canMergeWith
import io.delilaheve.util.ItemUtil.findEnchantments import io.delilaheve.util.ItemUtil.findEnchantments
import io.delilaheve.util.ItemUtil.isBook import io.delilaheve.util.ItemUtil.isBook
import io.delilaheve.util.ItemUtil.repairCost
import io.delilaheve.util.ItemUtil.repairFrom import io.delilaheve.util.ItemUtil.repairFrom
import io.delilaheve.util.ItemUtil.setEnchantmentsUnsafe import io.delilaheve.util.ItemUtil.setEnchantmentsUnsafe
import org.bukkit.entity.HumanEntity
import org.bukkit.entity.Player
import org.bukkit.event.Event import org.bukkit.event.Event
import org.bukkit.event.EventHandler import org.bukkit.event.EventHandler
import org.bukkit.event.EventPriority.HIGHEST import org.bukkit.event.EventPriority.HIGHEST
@ -20,6 +17,7 @@ import org.bukkit.event.inventory.PrepareAnvilEvent
import org.bukkit.inventory.AnvilInventory import org.bukkit.inventory.AnvilInventory
import org.bukkit.inventory.InventoryView.Property.REPAIR_COST import org.bukkit.inventory.InventoryView.Property.REPAIR_COST
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.Repairable
import kotlin.math.min import kotlin.math.min
/** /**
@ -43,20 +41,19 @@ class AnvilEventListener : Listener {
val first = inventory.getItem(ANVIL_INPUT_LEFT) ?: return val first = inventory.getItem(ANVIL_INPUT_LEFT) ?: return
val second = inventory.getItem(ANVIL_INPUT_RIGHT) ?: return val second = inventory.getItem(ANVIL_INPUT_RIGHT) ?: return
if (first.canMergeWith(second)) { if (first.canMergeWith(second)) {
// Try to find player // Should find player
val player = event.view.player val player = event.view.player
val newEnchants = first.findEnchantments() val newEnchants = first.findEnchantments()
.combineWith(second.findEnchantments(), first.type, player) .combineWith(second.findEnchantments(), first.type, player)
val resultItem = first.clone() val resultItem = first.clone()
resultItem.setEnchantmentsUnsafe(newEnchants) resultItem.setEnchantmentsUnsafe(newEnchants)
var repairCost: Int
var anvilCost = calculateCost(first, second, resultItem, player.hasPermission(UnsafeEnchants.bypassFusePermission))
if (!first.isBook() && !second.isBook()) { if (!first.isBook() && !second.isBook()) {
repairCost = first.repairCost + second.repairCost
// we only need to be concerned with repair when neither item is a book // we only need to be concerned with repair when neither item is a book
resultItem.repairFrom(first, second) val repaired = resultItem.repairFrom(first, second)
}else{ anvilCost += if(repaired) 2 else 0
repairCost = resultItem.repairCost
} }
// Test if nothing change and stop. // Test if nothing change and stop.
@ -69,13 +66,13 @@ class AnvilEventListener : Listener {
resultItem.itemMeta?.let { resultItem.itemMeta?.let {
if(!it.displayName.contentEquals(inventory.renameText)){ if(!it.displayName.contentEquals(inventory.renameText)){
it.setDisplayName(inventory.renameText) it.setDisplayName(inventory.renameText)
resultItem.itemMeta = it anvilCost += 1
repairCost += 1
} }
resultItem.itemMeta = it
} }
if (ConfigOptions.limitRepairCost) { if (ConfigOptions.limitRepairCost) {
repairCost = min(repairCost, ConfigOptions.limitRepairValue) anvilCost = min(anvilCost, ConfigOptions.limitRepairValue)
} }
event.result = resultItem event.result = resultItem
@ -90,8 +87,8 @@ class AnvilEventListener : Listener {
if (ConfigOptions.removeRepairLimit) { if (ConfigOptions.removeRepairLimit) {
inventory.maximumRepairCost = Int.MAX_VALUE inventory.maximumRepairCost = Int.MAX_VALUE
} }
inventory.repairCost = repairCost inventory.repairCost = anvilCost
event.view.setProperty(REPAIR_COST, repairCost) event.view.setProperty(REPAIR_COST, anvilCost)
}) })
} }
} }
@ -113,4 +110,58 @@ class AnvilEventListener : Listener {
event.result = Event.Result.ALLOW event.result = Event.Result.ALLOW
} }
/**
* Function to calculate most of the xp requirement for the anvil fuse
* Change result work penalty for future use
*/
private fun calculateCost(left: ItemStack, right: ItemStack, result: ItemStack, bypassFuse: Boolean): Int{
// Extracted From https://minecraft.fandom.com/wiki/Anvil_mechanics#Enchantment_equation
// Calculate work penality
val leftPenality = (left.itemMeta as? Repairable)?.repairCost ?: 0
val rightPenality = (right.itemMeta as? Repairable)?.repairCost ?: 0
// Calculate right value and illegal enchant penalty
var rightValue = 0
var illegalPenalty = 0
val rightIsFormBook = right.isBook()
val resultEnchs = result.findEnchantments().keys
for (enchantment in right.findEnchantments()) {
// count enchant as illegal enchant if it conflicts with another enchant or not in result
if(!bypassFuse && (
(enchantment.key !in resultEnchs) ||
UnsafeEnchants.conflictManager.isConflicting(resultEnchs,result.type,enchantment.key)
)){
// There may an issue when illegal enchant are trying to combine
// at least that what I think, but can't find why
illegalPenalty++
UnsafeEnchants.log("Conflict for ${enchantment.key.enchantmentName}, add 1 of value")
continue
}
val enchantmentMultiplier = ConfigOptions.enchantmentValue(enchantment.key, rightIsFormBook)
val value = enchantment.value * enchantmentMultiplier
UnsafeEnchants.log("Value for ${enchantment.key.enchantmentName} level ${enchantment.value} is $value")
rightValue+=value
}
// Try to set work penality for the result item
result.itemMeta?.let {
(it as? Repairable)?.repairCost = leftPenality*2+1
result.itemMeta = it
}
UnsafeEnchants.log("Calculated cost: " +
"leftPenality: $leftPenality, " +
"rightPenality: $rightPenality, " +
"rightValue: $rightValue, " +
"illegalPenalty: $illegalPenalty," +
"result penality: ${(result.itemMeta as? Repairable)?.repairCost ?: "none"}")
// We are missing [Renaming Cost] + [Refilling Durability] but it will be handled later
return rightValue + leftPenality + rightPenality + illegalPenalty
}
} }

View file

@ -89,16 +89,4 @@ object EnchantmentUtil {
return false return false
} }
/**
* Calculate the value of a set of enchantments
*/
fun Map<Enchantment, Int>.calculateValue(
fromBook: Boolean
) = entries.sumOf { (enchantment, level) ->
val enchantmentMultiplier = ConfigOptions.enchantmentValue(enchantment, fromBook)
val value = level * enchantmentMultiplier
UnsafeEnchants.log("Value for ${enchantment.enchantmentName} is $value")
value
}
} }

View file

@ -1,7 +1,6 @@
package io.delilaheve.util package io.delilaheve.util
import io.delilaheve.UnsafeEnchants import io.delilaheve.UnsafeEnchants
import io.delilaheve.util.EnchantmentUtil.calculateValue
import org.bukkit.Material.BOOK import org.bukkit.Material.BOOK
import org.bukkit.Material.ENCHANTED_BOOK import org.bukkit.Material.ENCHANTED_BOOK
import org.bukkit.enchantments.Enchantment import org.bukkit.enchantments.Enchantment
@ -9,7 +8,6 @@ import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.Damageable import org.bukkit.inventory.meta.Damageable
import org.bukkit.inventory.meta.EnchantmentStorageMeta import org.bukkit.inventory.meta.EnchantmentStorageMeta
import org.bukkit.inventory.meta.ItemMeta import org.bukkit.inventory.meta.ItemMeta
import org.bukkit.inventory.meta.Repairable
import kotlin.math.min import kotlin.math.min
/** /**
@ -17,19 +15,6 @@ import kotlin.math.min
*/ */
object ItemUtil { 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] * Check if this [ItemStack] is a [BOOK] or [ENCHANTED_BOOK]
*/ */
@ -93,14 +78,17 @@ object ItemUtil {
/** /**
* Set this [ItemStack]s durability from a combination of the * Set this [ItemStack]s durability from a combination of the
* [first] and [second] item's durability values * [first] and [second] item's durability values
* @return if the item was repaired
*/ */
fun ItemStack.repairFrom( fun ItemStack.repairFrom(
first: ItemStack, first: ItemStack,
second: ItemStack second: ItemStack
) { ): Boolean {
(itemMeta as? Damageable)?.let { (itemMeta as? Damageable)?.let {
val durability = type.maxDurability.toInt() val durability = type.maxDurability.toInt()
val firstDamage = (first.itemMeta as? Damageable)?.damage ?: 0 val firstDamage = (first.itemMeta as? Damageable)?.damage ?: 0
if( firstDamage == 0) return false
val firstDurability = durability - firstDamage val firstDurability = durability - firstDamage
val secondDamage = (second.itemMeta as? Damageable)?.damage ?: 0 val secondDamage = (second.itemMeta as? Damageable)?.damage ?: 0
val secondDurability = durability - secondDamage val secondDurability = durability - secondDamage
@ -108,7 +96,9 @@ object ItemUtil {
val newDurability = min(combinedDurability, durability) val newDurability = min(combinedDurability, durability)
it.damage = durability - newDurability it.damage = durability - newDurability
itemMeta = it as ItemMeta itemMeta = it as ItemMeta
return true
} }
return false
} }
/** /**

View file

@ -6,29 +6,43 @@ import java.util.EnumSet
abstract class AbstractMaterialGroup(private val name: String) { abstract class AbstractMaterialGroup(private val name: String) {
protected val includedMaterial by lazy {createDefaultSet()} protected val includedMaterial by lazy {createDefaultSet()}
// Get the group as a set /**
* Get the group default set
*/
protected abstract fun createDefaultSet(): EnumSet<Material> protected abstract fun createDefaultSet(): EnumSet<Material>
// Get if a material is allowed following the group policy /**
* Get if a material is allowed following the group policy
*/
fun contain(mat : Material): Boolean { fun contain(mat : Material): Boolean {
return mat in includedMaterial return mat in includedMaterial
} }
// Get if a group is referenced by this /**
* Get if a group is referenced by this:
*/
abstract fun isReferencing(other : AbstractMaterialGroup): Boolean abstract fun isReferencing(other : AbstractMaterialGroup): Boolean
// Push a material to this group to follow this group policy /**
* Push a material to this group to follow this group policy
*/
abstract fun addToPolicy(mat : Material) abstract fun addToPolicy(mat : Material)
// Push a group to this group to follow this group policy /**
* Push a group to this group to follow this group policy
*/
abstract fun addToPolicy(other : AbstractMaterialGroup) abstract fun addToPolicy(other : AbstractMaterialGroup)
// Get the group name in case something is wrong /**
* Get the group name in case something is wrong
*/
fun getName(): String { fun getName(): String {
return name return name
} }
// Get the group as a set /**
* Get the group as a set
*/
fun getSet(): Set<Material> { fun getSet(): Set<Material> {
return includedMaterial return includedMaterial
} }

View file

@ -1,9 +1,7 @@
package xyz.alexcrea.group package xyz.alexcrea.group
import io.delilaheve.util.ItemUtil.findEnchantments
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.enchantments.Enchantment import org.bukkit.enchantments.Enchantment
import org.bukkit.inventory.ItemStack
class EnchantConflictGroup(private val cantConflict: AbstractMaterialGroup, private val minBeforeBlock: Int){ class EnchantConflictGroup(private val cantConflict: AbstractMaterialGroup, private val minBeforeBlock: Int){
@ -12,19 +10,11 @@ class EnchantConflictGroup(private val cantConflict: AbstractMaterialGroup, priv
fun addEnchantment(ench: Enchantment){ fun addEnchantment(ench: Enchantment){
enchantments.add(ench) enchantments.add(ench)
} }
fun allowed(enchants: Set<Enchantment>, mat: Material) : Boolean{
fun allowed(item: ItemStack) : Boolean{
if(enchantments.size < minBeforeBlock){ if(enchantments.size < minBeforeBlock){
return true return true
} }
if(cantConflict.contain(item.type)){
return true
}
return allowed(item.enchantments.keys, item.type)
}
fun allowed(enchants: Set<Enchantment>, mat: Material) : Boolean{
if(cantConflict.contain(mat)){ if(cantConflict.contain(mat)){
return true return true
} }

View file

@ -6,7 +6,6 @@ import org.bukkit.NamespacedKey
import org.bukkit.configuration.ConfigurationSection import org.bukkit.configuration.ConfigurationSection
import org.bukkit.configuration.file.YamlConfiguration import org.bukkit.configuration.file.YamlConfiguration
import org.bukkit.enchantments.Enchantment import org.bukkit.enchantments.Enchantment
import org.bukkit.inventory.ItemStack
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
class EnchantConflictManager { class EnchantConflictManager {
@ -123,23 +122,6 @@ class EnchantConflictManager {
return group return group
} }
fun isConflicting(item: ItemStack): Boolean{
val toTest = HashSet<EnchantConflictGroup>()
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<Enchantment>,mat: Material, newEnchant: Enchantment): Boolean{ fun isConflicting(base: Set<Enchantment>,mat: Material, newEnchant: Enchantment): Boolean{
val conflictList = conflictMap[newEnchant] ?: return false val conflictList = conflictMap[newEnchant] ?: return false