mirror of
https://github.com/alexcrea/CustomAnvil.git
synced 2026-08-28 08:25:56 +02:00
330 lines
No EOL
12 KiB
Kotlin
330 lines
No EOL
12 KiB
Kotlin
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.isEnchantedBook
|
|
import io.delilaheve.util.ItemUtil.repairFrom
|
|
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.ItemStack
|
|
import org.bukkit.inventory.meta.ItemMeta
|
|
import org.bukkit.inventory.view.AnvilView
|
|
import org.bukkit.persistence.PersistentDataType
|
|
import xyz.alexcrea.cuanvil.api.EnchantmentApi
|
|
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
|
|
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.config.LoreEditType
|
|
import xyz.alexcrea.cuanvil.util.dialog.AnvilRenameDialogUtil
|
|
|
|
@Suppress("UnstableApiUsage")
|
|
object AnvilMergeLogic {
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
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 {
|
|
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(
|
|
view: AnvilView,
|
|
player: Player, first: ItemStack
|
|
): AnvilResult {
|
|
val resultItem = DependencyManager.cloneItem(player, first)
|
|
val cost = AnvilCost()
|
|
cost.rename = handleRename(resultItem, view, 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(view, player, resultItem, AnvilUseType.RENAME_ONLY, cost)
|
|
|
|
return AnvilResult(result, cost)
|
|
}
|
|
|
|
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 = meta.persistentDataContainer
|
|
if (!keepDialog)
|
|
pdc.remove(AnvilRenameDialog.PCD_KEEP_RENAME_TEXT_KEY)
|
|
else {
|
|
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, view: AnvilView, player: HumanEntity): Int {
|
|
// Can be null
|
|
var renameText = ChatColor.stripColor(view.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(
|
|
view: AnvilView,
|
|
player: Player,
|
|
first: ItemStack, second: ItemStack
|
|
): AnvilResult {
|
|
val firstEnchants = EnchantmentApi.getEnchantments(first)
|
|
val secondEnchants = EnchantmentApi.getEnchantments(second)
|
|
|
|
// newEnchants will be mutated by combineWith
|
|
val newEnchants = HashMap(firstEnchants)
|
|
newEnchants.combineWith(secondEnchants, first, player)
|
|
|
|
var hasChanged = !isIdentical(firstEnchants, newEnchants)
|
|
|
|
val resultItem = DependencyManager.cloneItem(player, first)
|
|
val cost = AnvilCost()
|
|
if (hasChanged) {
|
|
EnchantmentApi.setEnchantments(resultItem, newEnchants)
|
|
// Calculate enchantment cost
|
|
AnvilXpUtil.getRightValues(first, 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, view, player)
|
|
|
|
val result =
|
|
DependencyManager.tryTreatAnvilResult(view, player, 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(
|
|
view: AnvilView,
|
|
player: Player,
|
|
first: ItemStack, second: ItemStack?
|
|
): CustomCraftResult {
|
|
val recipe = CustomRecipeUtil.getCustomRecipe(first, second)
|
|
CustomAnvil.verboseLog("custom recipe not null? ${recipe != null}")
|
|
if (recipe == null) return CustomCraftResult.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 = CustomCraftCost(xpCost)
|
|
// This is for displayed cost
|
|
cost.recipe = if (recipe.removeExactLinearXp) AnvilXpUtil.calculateMinimumLevelForXp(xpCost)
|
|
else AnvilXpUtil.calculateLevelForXp(xpCost)
|
|
|
|
val result =
|
|
DependencyManager.tryTreatAnvilResult(view, player, resultItem, AnvilUseType.CUSTOM_CRAFT, cost)
|
|
return CustomCraftResult(result, cost, amount, recipe)
|
|
}
|
|
|
|
fun testUnitRepair(
|
|
view: AnvilView,
|
|
player: Player,
|
|
first: ItemStack, second: ItemStack
|
|
): UnitRepairResult {
|
|
val unitRepairAmount = first.getRepair(second) ?: return UnitRepairResult.EMPTY
|
|
|
|
return testUnitRepair(view, player, first, second, unitRepairAmount)
|
|
}
|
|
|
|
fun testUnitRepair(
|
|
view: AnvilView,
|
|
player: Player,
|
|
first: ItemStack, second: ItemStack,
|
|
unitRepairAmount: Double
|
|
): UnitRepairResult {
|
|
val resultItem = DependencyManager.cloneItem(player, first)
|
|
val cost = AnvilCost()
|
|
cost.rename = handleRename(resultItem, view, 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(view, player, resultItem, AnvilUseType.UNIT_REPAIR, cost)
|
|
return UnitRepairResult(result, cost, repairAmount)
|
|
}
|
|
|
|
fun testLoreEdit(
|
|
player: Player,
|
|
first: ItemStack, second: ItemStack
|
|
): LoreEditResult {
|
|
val type = second.type
|
|
|
|
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 (result.isEmpty()) return result
|
|
|
|
if (result.item!!.isAir || first == result.item) {
|
|
CustomAnvil.log("lore edit, But input is same as output")
|
|
return LoreEditResult.EMPTY
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
} |