move a lot of function to AnvilMergeLogic.kt

This commit is contained in:
alexcrea 2026-06-02 13:29:26 +02:00
parent bf8144ad06
commit 7a705f3bfc
Signed by: alexcrea
GPG key ID: E346CD16413450E3
19 changed files with 373 additions and 305 deletions

View file

@ -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
* <p>
* 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();

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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.*

View file

@ -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<CAEnchantment, Int>,
resultEnchants: MutableMap<CAEnchantment, Int>
): 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)
}
}

View file

@ -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(

View file

@ -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()
}
}

View file

@ -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

View file

@ -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 {

View file

@ -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.*

View file

@ -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<CAEnchantment, Int>,
resultEnchants: MutableMap<CAEnchantment, Int>
): 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
}
}

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -1,4 +1,4 @@
package xyz.alexcrea.cuanvil.util
package xyz.alexcrea.cuanvil.util.anvil
import io.delilaheve.util.ConfigOptions

View file

@ -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 {

View file

@ -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

View file

@ -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 {