mirror of
https://github.com/alexcrea/CustomAnvil.git
synced 2026-06-24 00:26:16 +02:00
Splitted main logic to be more readable
This commit is contained in:
parent
7029254526
commit
7c283dc7f8
8 changed files with 672 additions and 584 deletions
|
|
@ -1,580 +0,0 @@
|
|||
package io.delilaheve
|
||||
|
||||
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.GameMode
|
||||
import org.bukkit.Material
|
||||
import org.bukkit.entity.HumanEntity
|
||||
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.ClickType
|
||||
import org.bukkit.event.inventory.InventoryClickEvent
|
||||
import org.bukkit.event.inventory.InventoryCloseEvent
|
||||
import org.bukkit.event.inventory.PrepareAnvilEvent
|
||||
import org.bukkit.inventory.AnvilInventory
|
||||
import org.bukkit.inventory.InventoryView.Property.REPAIR_COST
|
||||
import org.bukkit.inventory.ItemStack
|
||||
import xyz.alexcrea.cuanvil.config.ConfigHolder
|
||||
import xyz.alexcrea.cuanvil.dependency.DependencyManager
|
||||
import xyz.alexcrea.cuanvil.dependency.packet.PacketManager
|
||||
import xyz.alexcrea.cuanvil.recipe.AnvilCustomRecipe
|
||||
import xyz.alexcrea.cuanvil.util.AnvilXpUtil.calculatePenalty
|
||||
import xyz.alexcrea.cuanvil.util.AnvilXpUtil.getRightValues
|
||||
import xyz.alexcrea.cuanvil.util.AnvilXpUtil.setAnvilInvXp
|
||||
import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair
|
||||
import java.util.regex.Matcher
|
||||
import java.util.regex.Pattern
|
||||
import kotlin.math.min
|
||||
|
||||
|
||||
/**
|
||||
* Listener for anvil events
|
||||
*/
|
||||
class AnvilEventListener(private val packetManager: PacketManager) : Listener {
|
||||
|
||||
companion object {
|
||||
// Anvil's output slot
|
||||
const val ANVIL_INPUT_LEFT = 0
|
||||
const val ANVIL_INPUT_RIGHT = 1
|
||||
const val ANVIL_OUTPUT_SLOT = 2
|
||||
|
||||
// static slot container
|
||||
private val NO_SLOT = SlotContainer(SlotType.NO_SLOT, 0)
|
||||
private val CURSOR_SLOT = SlotContainer(SlotType.CURSOR, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handler logic for when an anvil contains items to be combined
|
||||
*/
|
||||
@EventHandler(priority = HIGHEST, ignoreCancelled = true)
|
||||
fun anvilCombineCheck(event: PrepareAnvilEvent) {
|
||||
// Test if the event should bypass custom anvil.
|
||||
if(DependencyManager.tryEventPreAnvilBypass(event)) return
|
||||
|
||||
val inventory = event.inventory
|
||||
val first = inventory.getItem(ANVIL_INPUT_LEFT) ?: return
|
||||
val second = inventory.getItem(ANVIL_INPUT_RIGHT)
|
||||
|
||||
// Should find player
|
||||
val player = event.view.player
|
||||
if (!player.hasPermission(CustomAnvil.affectedByPluginPermission)) return
|
||||
|
||||
// Test custom recipe
|
||||
val recipe = getCustomRecipe(first, second)
|
||||
CustomAnvil.verboseLog("custom recipe not null? ${recipe != null}")
|
||||
if(recipe != null){
|
||||
val amount = getCustomRecipeAmount(recipe, first, second)
|
||||
|
||||
val resultItem: ItemStack = recipe.resultItem!!.clone()
|
||||
resultItem.amount *= amount
|
||||
|
||||
event.result = resultItem
|
||||
setAnvilInvXp(inventory, event.view, recipe.xpCostPerCraft * amount, true)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Test rename lonely item
|
||||
if (second == null) {
|
||||
val resultItem = first.clone()
|
||||
var anvilCost = handleRename(resultItem, inventory, player)
|
||||
|
||||
// Test/stop if nothing changed.
|
||||
if (first == resultItem) {
|
||||
CustomAnvil.log("no right item, But input is same as output")
|
||||
event.result = null
|
||||
return
|
||||
}
|
||||
|
||||
event.result = resultItem
|
||||
|
||||
anvilCost += calculatePenalty(first, null, resultItem)
|
||||
|
||||
setAnvilInvXp(inventory, event.view, anvilCost)
|
||||
return
|
||||
}
|
||||
|
||||
// Test for merge
|
||||
if (first.canMergeWith(second)) {
|
||||
val newEnchants = first.findEnchantments()
|
||||
.combineWith(second.findEnchantments(), first, player)
|
||||
val resultItem = first.clone()
|
||||
resultItem.setEnchantmentsUnsafe(newEnchants)
|
||||
|
||||
// Calculate enchantment cost
|
||||
var anvilCost = getRightValues(second, resultItem)
|
||||
// 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)
|
||||
anvilCost += if (repaired) ConfigOptions.itemRepairCost else 0
|
||||
}
|
||||
|
||||
// Test/stop if nothing changed.
|
||||
if (first == resultItem) {
|
||||
CustomAnvil.log("Mergable with second, But input is same as output")
|
||||
event.result = null
|
||||
return
|
||||
}
|
||||
// As calculatePenalty edit result, we need to calculate penalty after checking equality
|
||||
anvilCost += calculatePenalty(first, second, resultItem)
|
||||
// Calculate rename cost
|
||||
anvilCost += handleRename(resultItem, inventory, player)
|
||||
|
||||
// Finally, we set result
|
||||
event.result = resultItem
|
||||
|
||||
setAnvilInvXp(inventory, event.view, anvilCost)
|
||||
return
|
||||
}
|
||||
|
||||
// Test for unit repair
|
||||
val unitRepairAmount = first.getRepair(second)
|
||||
if (unitRepairAmount != null) {
|
||||
val resultItem = first.clone()
|
||||
var anvilCost = handleRename(resultItem, inventory, player)
|
||||
|
||||
val repairAmount = resultItem.unitRepair(second.amount, unitRepairAmount)
|
||||
if (repairAmount > 0) {
|
||||
anvilCost += repairAmount * ConfigOptions.unitRepairCost
|
||||
}
|
||||
// We do not care about right item penalty for unit repair
|
||||
anvilCost += calculatePenalty(first, null, resultItem, true)
|
||||
|
||||
// Test/stop if nothing changed.
|
||||
if (first == resultItem) {
|
||||
CustomAnvil.log("unit repair, But input is same as output")
|
||||
event.result = null
|
||||
return
|
||||
}
|
||||
event.result = resultItem
|
||||
|
||||
setAnvilInvXp(inventory, event.view, anvilCost)
|
||||
} else {
|
||||
CustomAnvil.log("no anvil fuse type found")
|
||||
event.result = null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun handleRename(resultItem: ItemStack, inventory: AnvilInventory, player: HumanEntity): Int {
|
||||
// Rename item and add renaming cost
|
||||
resultItem.itemMeta?.let {
|
||||
val displayName = ChatColor.stripColor(it.displayName)
|
||||
var inventoryName = ChatColor.stripColor(inventory.renameText)
|
||||
|
||||
var sumCost = 0
|
||||
|
||||
var useColor = false
|
||||
if(ConfigOptions.renameColorPossible){
|
||||
val resultString = StringBuilder(inventoryName)
|
||||
|
||||
useColor = handleRenamingColor(resultString, player)
|
||||
|
||||
if(useColor) {
|
||||
inventoryName = resultString.toString()
|
||||
|
||||
sumCost+= ConfigOptions.useOfColorCost
|
||||
}
|
||||
}
|
||||
|
||||
if ((!useColor && (!displayName.contentEquals(inventoryName))) || (useColor && !(it.displayName).contentEquals(inventoryName))) {
|
||||
it.setDisplayName(inventoryName)
|
||||
resultItem.itemMeta = it
|
||||
|
||||
sumCost+= ConfigOptions.itemRenameCost
|
||||
}
|
||||
|
||||
return sumCost
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
private fun handleRenamingColor(textToColor: StringBuilder, player: HumanEntity): Boolean {
|
||||
val usePermission = ConfigOptions.permissionNeededForColor
|
||||
val canUseColorCode = ConfigOptions.allowColorCode && (!usePermission || player.hasPermission("ca.color.code"))
|
||||
val canUseHexColor = ConfigOptions.allowHexadecimalColor && (!usePermission || player.hasPermission("ca.color.hex"))
|
||||
|
||||
if((!canUseColorCode) && (!canUseHexColor)) return false
|
||||
|
||||
var useColor = false
|
||||
// Handle color code
|
||||
if(canUseColorCode){
|
||||
var nbReplacement = replaceAll(textToColor, "&", "§", 2)
|
||||
nbReplacement -= 2 * replaceAll(textToColor, "§§", "&", 2)
|
||||
|
||||
if(nbReplacement > 0) useColor = true
|
||||
}
|
||||
|
||||
if(canUseHexColor){
|
||||
val nbReplacement = replaceHexToColor(textToColor, 7)
|
||||
|
||||
if(nbReplacement > 0) useColor = true
|
||||
}
|
||||
|
||||
return useColor
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace every instance of "from" to "to".
|
||||
* @param builder The builder to replace the string from.
|
||||
* @param from The source that should be replaced.
|
||||
* @param to The string that should replace.
|
||||
* @param endOffset Amount of character that should be ignored at the end.
|
||||
* @return The number of replacement was that was done.
|
||||
*/
|
||||
private fun replaceAll(builder: java.lang.StringBuilder, from: String, to: String, endOffset: Int): Int {
|
||||
var index = builder.indexOf(from)
|
||||
var numberOfChanges = 0
|
||||
|
||||
while (index != -1 && index < builder.length - endOffset) {
|
||||
builder.replace(index, index + from.length, to)
|
||||
index += to.length
|
||||
index = builder.indexOf(from, index)
|
||||
|
||||
numberOfChanges+=1
|
||||
}
|
||||
|
||||
return numberOfChanges
|
||||
}
|
||||
|
||||
val HEX_PATTERN: Pattern = Pattern.compile("#[A-Fa-f0-9]{6}") // pattern to find hexadecimal string
|
||||
/**
|
||||
* Replace every hex color formatted like #000000 to the minecraft format
|
||||
* @param builder The builder to replace the hex color from.
|
||||
* @param endOffset Amount of character that should be ignored at the end.
|
||||
* @return The number of replacement was that was done.
|
||||
*/
|
||||
private fun replaceHexToColor(builder: StringBuilder, endOffset: Int): Int {
|
||||
val matcher: Matcher = HEX_PATTERN.matcher(builder)
|
||||
|
||||
var numberOfChanges = 0
|
||||
var startIndex = 0
|
||||
|
||||
while(matcher.find(startIndex)){
|
||||
startIndex = matcher.start()
|
||||
if(startIndex >= builder.length - endOffset) break
|
||||
|
||||
builder.replace(startIndex, startIndex + 1, "§x")
|
||||
startIndex+=2
|
||||
for (i in 0..5) {
|
||||
builder.insert(startIndex, '§')
|
||||
startIndex+=2
|
||||
}
|
||||
|
||||
numberOfChanges+=1
|
||||
}
|
||||
|
||||
return numberOfChanges
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handler logic for when a player is trying to pull an item out of the anvil
|
||||
*/
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
fun anvilExtractionCheck(event: InventoryClickEvent) {
|
||||
val player = event.whoClicked as? Player ?: return
|
||||
if (!player.hasPermission(CustomAnvil.affectedByPluginPermission)) return
|
||||
val inventory = event.inventory as? AnvilInventory ?: return
|
||||
|
||||
if (event.rawSlot != ANVIL_OUTPUT_SLOT) {
|
||||
return
|
||||
}
|
||||
// Test if the event should bypass custom anvil.
|
||||
if(DependencyManager.tryClickAnvilResultBypass(event, inventory)) return
|
||||
|
||||
val output = inventory.getItem(ANVIL_OUTPUT_SLOT) ?: return
|
||||
val leftItem = inventory.getItem(ANVIL_INPUT_LEFT) ?: return
|
||||
val rightItem = inventory.getItem(ANVIL_INPUT_RIGHT)
|
||||
|
||||
// Test custom recipe
|
||||
val recipe = getCustomRecipe(leftItem, rightItem)
|
||||
if(recipe != null){
|
||||
event.result = Event.Result.ALLOW
|
||||
onCustomCraft(
|
||||
event, recipe, player,
|
||||
leftItem, rightItem, output, inventory)
|
||||
return
|
||||
}
|
||||
|
||||
val canMerge = leftItem.canMergeWith(rightItem)
|
||||
val unitRepairResult = leftItem.getRepair(rightItem)
|
||||
val allowed = (rightItem == null)
|
||||
|| (canMerge)
|
||||
|| (unitRepairResult != null)
|
||||
|
||||
// True if there was no change or not allowed
|
||||
if ((output == inventory.getItem(ANVIL_INPUT_LEFT))
|
||||
|| !allowed
|
||||
) {
|
||||
event.result = Event.Result.DENY
|
||||
return
|
||||
}
|
||||
if (rightItem == null) {
|
||||
event.result = Event.Result.ALLOW
|
||||
return
|
||||
}
|
||||
if (canMerge) {
|
||||
event.result = Event.Result.ALLOW
|
||||
} else if (unitRepairResult != null) {
|
||||
onUnitRepairExtract(
|
||||
leftItem, rightItem, output,
|
||||
unitRepairResult, event, player, inventory
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
private fun onCustomCraft(event: InventoryClickEvent,
|
||||
recipe: AnvilCustomRecipe,
|
||||
player: Player,
|
||||
leftItem: ItemStack,
|
||||
rightItem: ItemStack?,
|
||||
output: ItemStack,
|
||||
inventory: AnvilInventory) {
|
||||
event.result = Event.Result.DENY
|
||||
|
||||
if(recipe.leftItem == null) return // in case it changed
|
||||
|
||||
val amount = getCustomRecipeAmount(recipe, leftItem, rightItem)
|
||||
val xpCost = amount * recipe.xpCostPerCraft
|
||||
|
||||
if ((player.gameMode != GameMode.CREATIVE) && (player.level < xpCost)) return
|
||||
|
||||
// We give the item manually
|
||||
// But first we check if we should give the item
|
||||
val slotDestination = getActionSlot(event, player)
|
||||
if (slotDestination.type == SlotType.NO_SLOT) return
|
||||
|
||||
// If not creative middle click...
|
||||
if (event.click != ClickType.MIDDLE) {
|
||||
// We remove what should be removed
|
||||
leftItem.amount -= amount * recipe.leftItem!!.amount
|
||||
inventory.setItem(ANVIL_INPUT_LEFT, leftItem)
|
||||
|
||||
if(rightItem != null){
|
||||
if(recipe.rightItem == null) return // in case it changed
|
||||
|
||||
rightItem.amount -= amount * recipe.rightItem!!.amount
|
||||
inventory.setItem(ANVIL_INPUT_RIGHT, rightItem)
|
||||
}
|
||||
|
||||
if(player.gameMode != GameMode.CREATIVE){
|
||||
player.level -= xpCost
|
||||
}
|
||||
|
||||
// Then we try to find the new values for the anvil
|
||||
val newAmount = getCustomRecipeAmount(recipe, leftItem, rightItem)
|
||||
|
||||
CustomAnvil.verboseLog("new amount is $newAmount")
|
||||
if(newAmount <= 0 || recipe.exactCount){
|
||||
inventory.setItem(ANVIL_OUTPUT_SLOT, null)
|
||||
}else{
|
||||
val resultItem: ItemStack = recipe.resultItem!!.clone()
|
||||
resultItem.amount *= newAmount
|
||||
|
||||
val newXp = newAmount * newAmount
|
||||
|
||||
inventory.repairCost = newXp
|
||||
event.view.setProperty(REPAIR_COST, newXp)
|
||||
|
||||
inventory.setItem(ANVIL_OUTPUT_SLOT, resultItem)
|
||||
|
||||
player.updateInventory()
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, we add the item to the player
|
||||
if (slotDestination.type == SlotType.CURSOR) {
|
||||
player.setItemOnCursor(output)
|
||||
} else {// We assume SlotType == SlotType.INVENTORY
|
||||
player.inventory.setItem(slotDestination.slot, output)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private fun onUnitRepairExtract(
|
||||
leftItem: ItemStack,
|
||||
rightItem: ItemStack,
|
||||
output: ItemStack,
|
||||
unitRepairResult: Double,
|
||||
event: InventoryClickEvent,
|
||||
player: Player,
|
||||
inventory: AnvilInventory
|
||||
) {
|
||||
val resultCopy = leftItem.clone()
|
||||
val resultAmount = resultCopy.unitRepair(
|
||||
rightItem.amount, unitRepairResult
|
||||
)
|
||||
|
||||
// To avoid vanilla, we cancel the event for unit repair
|
||||
event.result = Event.Result.DENY
|
||||
event.isCancelled = true
|
||||
// And we give the item manually
|
||||
// But first we check if we should give the item
|
||||
val slotDestination = getActionSlot(event, player)
|
||||
if (slotDestination.type == SlotType.NO_SLOT) return
|
||||
|
||||
// Test repair cost
|
||||
var repairCost = 0
|
||||
if (player.gameMode != GameMode.CREATIVE) {
|
||||
// Get repairCost
|
||||
leftItem.itemMeta?.let { leftMeta ->
|
||||
val leftName = leftMeta.displayName
|
||||
output.itemMeta?.let {
|
||||
// Rename cost
|
||||
if (!leftName.contentEquals(it.displayName)) {
|
||||
repairCost += ConfigOptions.itemRenameCost
|
||||
|
||||
// Color cost
|
||||
if(it.displayName.contains('§')){
|
||||
repairCost += ConfigOptions.useOfColorCost
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repairCost += calculatePenalty(leftItem, null, resultCopy)
|
||||
repairCost += resultAmount * ConfigOptions.unitRepairCost
|
||||
|
||||
if (
|
||||
!ConfigOptions.doRemoveCostLimit &&
|
||||
ConfigOptions.doCapCost) {
|
||||
|
||||
repairCost = min(repairCost, ConfigOptions.maxAnvilCost)
|
||||
}
|
||||
|
||||
if ((inventory.maximumRepairCost <= repairCost)
|
||||
|| (player.level < repairCost)
|
||||
) return
|
||||
}
|
||||
// If not creative middle click...
|
||||
if (event.click != ClickType.MIDDLE) {
|
||||
// We remove what should be removed
|
||||
inventory.setItem(ANVIL_INPUT_LEFT, null)
|
||||
rightItem.amount -= resultAmount
|
||||
inventory.setItem(ANVIL_INPUT_RIGHT, rightItem)
|
||||
inventory.setItem(ANVIL_OUTPUT_SLOT, null)
|
||||
player.level -= repairCost
|
||||
}
|
||||
|
||||
// Finally, we add the item to the player
|
||||
if (slotDestination.type == SlotType.CURSOR) {
|
||||
player.setItemOnCursor(output)
|
||||
} else {// We assume SlotType == SlotType.INVENTORY
|
||||
player.inventory.setItem(slotDestination.slot, output)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the destination slot or "NO_SLOT" slot container if there is no slot available
|
||||
*/
|
||||
private fun getActionSlot(event: InventoryClickEvent, player: Player): SlotContainer {
|
||||
if (event.isShiftClick) {
|
||||
val inventory = player.inventory
|
||||
val firstEmpty = inventory.firstEmpty()
|
||||
if (firstEmpty == -1) {
|
||||
return NO_SLOT
|
||||
}
|
||||
//check hotbare full
|
||||
var slotIndex = 8
|
||||
while (slotIndex >= 0 && ((inventory.getItem(slotIndex)?.type ?: Material.AIR) != Material.AIR)) {
|
||||
slotIndex--
|
||||
}
|
||||
if (slotIndex >= 0) {
|
||||
return SlotContainer(SlotType.INVENTORY, slotIndex)
|
||||
}
|
||||
slotIndex = 35 //4*9 - 1 (max of player inventory)
|
||||
while (slotIndex >= 9 && ((inventory.getItem(slotIndex)?.type ?: Material.AIR) != Material.AIR)) {
|
||||
slotIndex--
|
||||
}
|
||||
if (slotIndex < 9) {
|
||||
return NO_SLOT
|
||||
}
|
||||
return SlotContainer(SlotType.INVENTORY, slotIndex)
|
||||
} else {
|
||||
if (player.itemOnCursor.type != Material.AIR) {
|
||||
return NO_SLOT
|
||||
}
|
||||
return CURSOR_SLOT
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCustomRecipe (
|
||||
leftItem: ItemStack,
|
||||
rightItem: ItemStack?) : AnvilCustomRecipe? {
|
||||
|
||||
val recipeList = ConfigHolder.CUSTOM_RECIPE_HOLDER.recipeManager.recipeByMat[leftItem.type] ?: return null
|
||||
|
||||
CustomAnvil.verboseLog("Testing " + recipeList.size+" recipe...")
|
||||
for (recipe in recipeList) {
|
||||
if(recipe.testItem(leftItem, rightItem)){
|
||||
return recipe
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun getCustomRecipeAmount(
|
||||
recipe: AnvilCustomRecipe,
|
||||
leftItem: ItemStack,
|
||||
rightItem: ItemStack?
|
||||
): Int{
|
||||
return if(recipe.exactCount) {
|
||||
if(leftItem.amount != recipe.leftItem!!.amount){
|
||||
0
|
||||
}else if(rightItem != null && rightItem.amount != recipe.rightItem!!.amount){
|
||||
0
|
||||
}else{
|
||||
1
|
||||
}
|
||||
}
|
||||
else {
|
||||
// test amount
|
||||
val resultItem = recipe.resultItem!! // we know exist as the recipe was returned to us
|
||||
val maxResultAmount = resultItem.type.maxStackSize/resultItem.amount
|
||||
val maxLeftAmount = leftItem.amount/recipe.leftItem!!.amount
|
||||
val maxRightAmount = if(rightItem == null){ maxLeftAmount } else{ rightItem.amount/recipe.rightItem!!.amount }
|
||||
|
||||
CustomAnvil.verboseLog("resultItem: $resultItem, maxResultAmount: $maxResultAmount, maxLeftAmount: $maxLeftAmount, maxRightAmount: $maxRightAmount")
|
||||
|
||||
min(min(maxResultAmount, maxLeftAmount), maxRightAmount)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@EventHandler
|
||||
fun onAnvilClose(event: InventoryCloseEvent){
|
||||
val player = event.player
|
||||
if(event.inventory !is AnvilInventory) return
|
||||
if(player is Player && GameMode.CREATIVE != player.gameMode){
|
||||
packetManager.setInstantBuild(player, false)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private class SlotContainer(val type: SlotType, val slot: Int)
|
||||
private enum class SlotType {
|
||||
CURSOR,
|
||||
INVENTORY,
|
||||
NO_SLOT
|
||||
|
||||
}
|
||||
|
|
@ -13,7 +13,10 @@ import xyz.alexcrea.cuanvil.dependency.DependencyManager
|
|||
import xyz.alexcrea.cuanvil.enchant.CAEnchantmentRegistry
|
||||
import xyz.alexcrea.cuanvil.gui.config.MainConfigGui
|
||||
import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant
|
||||
import xyz.alexcrea.cuanvil.listener.AnvilCloseListener
|
||||
import xyz.alexcrea.cuanvil.listener.AnvilResultListener
|
||||
import xyz.alexcrea.cuanvil.listener.ChatEventListener
|
||||
import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener
|
||||
import xyz.alexcrea.cuanvil.update.PluginSetDefault
|
||||
import xyz.alexcrea.cuanvil.update.Update_1_21
|
||||
import xyz.alexcrea.cuanvil.update.plugin.PluginUpdates
|
||||
|
|
@ -110,7 +113,9 @@ class CustomAnvil : JavaPlugin() {
|
|||
DependencyManager.loadDependency()
|
||||
|
||||
// Register anvil events
|
||||
server.pluginManager.registerEvents(AnvilEventListener(DependencyManager.packetManager), this)
|
||||
server.pluginManager.registerEvents(PrepareAnvilListener(), this)
|
||||
server.pluginManager.registerEvents(AnvilResultListener(), this)
|
||||
server.pluginManager.registerEvents(AnvilCloseListener(DependencyManager.packetManager), this)
|
||||
|
||||
// Load metrics
|
||||
Metrics(this, bstatsPluginId)
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ import cz.kominekjan.disenchantment.events.DisenchantClickEvent
|
|||
import cz.kominekjan.disenchantment.events.DisenchantEvent
|
||||
import cz.kominekjan.disenchantment.events.ShatterClickEvent
|
||||
import cz.kominekjan.disenchantment.events.ShatterEvent
|
||||
import io.delilaheve.AnvilEventListener
|
||||
import io.delilaheve.CustomAnvil
|
||||
import org.bukkit.event.inventory.InventoryClickEvent
|
||||
import org.bukkit.event.inventory.PrepareAnvilEvent
|
||||
import org.bukkit.inventory.AnvilInventory
|
||||
import org.bukkit.inventory.ItemStack
|
||||
import org.bukkit.plugin.RegisteredListener
|
||||
import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener
|
||||
import xyz.alexcrea.cuanvil.util.AnvilXpUtil
|
||||
|
||||
class DisenchantmentDependency {
|
||||
|
|
@ -96,7 +96,7 @@ class DisenchantmentDependency {
|
|||
}
|
||||
|
||||
fun testAnvilResult(event: InventoryClickEvent, inventory: AnvilInventory): Boolean {
|
||||
val previousResultSlot = inventory.getItem(AnvilEventListener.ANVIL_OUTPUT_SLOT)?.clone()
|
||||
val previousResultSlot = inventory.getItem(PrepareAnvilListener.ANVIL_OUTPUT_SLOT)?.clone()
|
||||
|
||||
// Test event if change the result
|
||||
itemClickEvent.onDisenchantmentClickEvent(event)
|
||||
|
|
@ -115,7 +115,7 @@ class DisenchantmentDependency {
|
|||
}
|
||||
|
||||
private fun testAnvilInventoryChange(inventory: AnvilInventory, previous: ItemStack?): Boolean {
|
||||
val currentResult = inventory.getItem(AnvilEventListener.ANVIL_OUTPUT_SLOT)
|
||||
val currentResult = inventory.getItem(PrepareAnvilListener.ANVIL_OUTPUT_SLOT)
|
||||
|
||||
return currentResult == previous
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
package xyz.alexcrea.cuanvil.listener
|
||||
|
||||
import org.bukkit.GameMode
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.event.EventHandler
|
||||
import org.bukkit.event.Listener
|
||||
import org.bukkit.event.inventory.InventoryCloseEvent
|
||||
import org.bukkit.inventory.AnvilInventory
|
||||
import xyz.alexcrea.cuanvil.dependency.packet.PacketManager
|
||||
|
||||
class AnvilCloseListener(private val packetManager: PacketManager) : Listener {
|
||||
|
||||
@EventHandler
|
||||
fun onAnvilClose(event: InventoryCloseEvent){
|
||||
val player = event.player
|
||||
if(event.inventory !is AnvilInventory) return
|
||||
if(player is Player && GameMode.CREATIVE != player.gameMode){
|
||||
packetManager.setInstantBuild(player, false)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,292 @@
|
|||
package xyz.alexcrea.cuanvil.listener
|
||||
|
||||
import io.delilaheve.CustomAnvil
|
||||
import io.delilaheve.util.ConfigOptions
|
||||
import io.delilaheve.util.ItemUtil.canMergeWith
|
||||
import io.delilaheve.util.ItemUtil.unitRepair
|
||||
import org.bukkit.GameMode
|
||||
import org.bukkit.Material
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.event.Event
|
||||
import org.bukkit.event.EventHandler
|
||||
import org.bukkit.event.Listener
|
||||
import org.bukkit.event.inventory.ClickType
|
||||
import org.bukkit.event.inventory.InventoryClickEvent
|
||||
import org.bukkit.inventory.AnvilInventory
|
||||
import org.bukkit.inventory.InventoryView
|
||||
import org.bukkit.inventory.ItemStack
|
||||
import xyz.alexcrea.cuanvil.dependency.DependencyManager
|
||||
import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_INPUT_LEFT
|
||||
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.AnvilXpUtil
|
||||
import xyz.alexcrea.cuanvil.util.CustomRecipeUtil
|
||||
import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair
|
||||
import kotlin.math.min
|
||||
|
||||
class AnvilResultListener: Listener {
|
||||
|
||||
companion object {
|
||||
// static slot container
|
||||
private val NO_SLOT = SlotContainer(SlotType.NO_SLOT, 0)
|
||||
private val CURSOR_SLOT = SlotContainer(SlotType.CURSOR, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handler logic for when a player is trying to pull an item out of the anvil
|
||||
*/
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
fun anvilExtractionCheck(event: InventoryClickEvent) {
|
||||
val player = event.whoClicked as? Player ?: return
|
||||
if (!player.hasPermission(CustomAnvil.affectedByPluginPermission)) return
|
||||
val inventory = event.inventory as? AnvilInventory ?: return
|
||||
|
||||
if (event.rawSlot != ANVIL_OUTPUT_SLOT) {
|
||||
return
|
||||
}
|
||||
// Test if the event should bypass custom anvil.
|
||||
if(DependencyManager.tryClickAnvilResultBypass(event, inventory)) return
|
||||
|
||||
val output = inventory.getItem(ANVIL_OUTPUT_SLOT) ?: return
|
||||
val leftItem = inventory.getItem(ANVIL_INPUT_LEFT) ?: return
|
||||
val rightItem = inventory.getItem(ANVIL_INPUT_RIGHT)
|
||||
|
||||
// Test custom recipe
|
||||
val recipe = CustomRecipeUtil.getCustomRecipe(leftItem, rightItem)
|
||||
if(recipe != null){
|
||||
event.result = Event.Result.ALLOW
|
||||
onCustomCraft(
|
||||
event, recipe, player,
|
||||
leftItem, rightItem, output, inventory)
|
||||
return
|
||||
}
|
||||
|
||||
val canMerge = leftItem.canMergeWith(rightItem)
|
||||
val unitRepairResult = leftItem.getRepair(rightItem)
|
||||
val allowed = (rightItem == null)
|
||||
|| (canMerge)
|
||||
|| (unitRepairResult != null)
|
||||
|
||||
// True if there was no change or not allowed
|
||||
if ((output == inventory.getItem(ANVIL_INPUT_LEFT))
|
||||
|| !allowed
|
||||
) {
|
||||
event.result = Event.Result.DENY
|
||||
return
|
||||
}
|
||||
if (rightItem == null) {
|
||||
event.result = Event.Result.ALLOW
|
||||
return
|
||||
}
|
||||
if (canMerge) {
|
||||
event.result = Event.Result.ALLOW
|
||||
} else if (unitRepairResult != null) {
|
||||
onUnitRepairExtract(
|
||||
leftItem, rightItem, output,
|
||||
unitRepairResult, event, player, inventory
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun onCustomCraft(event: InventoryClickEvent,
|
||||
recipe: AnvilCustomRecipe,
|
||||
player: Player,
|
||||
leftItem: ItemStack,
|
||||
rightItem: ItemStack?,
|
||||
output: ItemStack,
|
||||
inventory: AnvilInventory
|
||||
) {
|
||||
event.result = Event.Result.DENY
|
||||
|
||||
if(recipe.leftItem == null) return // in case it changed
|
||||
|
||||
val amount = CustomRecipeUtil.getCustomRecipeAmount(recipe, leftItem, rightItem)
|
||||
val xpCost = amount * recipe.xpCostPerCraft
|
||||
|
||||
if ((player.gameMode != GameMode.CREATIVE) && (player.level < xpCost)) return
|
||||
|
||||
// We give the item manually
|
||||
// But first we check if we should give the item
|
||||
val slotDestination = getActionSlot(event, player)
|
||||
if (slotDestination.type == SlotType.NO_SLOT) return
|
||||
|
||||
// Handle not creative middle click...
|
||||
if (event.click != ClickType.MIDDLE &&
|
||||
!handleCustomCraftClick(event, recipe, inventory, player, leftItem, rightItem, amount, xpCost)) return;
|
||||
|
||||
// Finally, we add the item to the player
|
||||
if (slotDestination.type == SlotType.CURSOR) {
|
||||
player.setItemOnCursor(output)
|
||||
} else {// We assume SlotType == SlotType.INVENTORY
|
||||
player.inventory.setItem(slotDestination.slot, output)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCustomCraftClick(event: InventoryClickEvent, recipe: AnvilCustomRecipe,
|
||||
inventory: AnvilInventory, player: Player,
|
||||
leftItem: ItemStack, rightItem: ItemStack?,
|
||||
amount: Int, xpCost: Int): Boolean {
|
||||
// We remove what should be removed
|
||||
if(rightItem != null){
|
||||
if(recipe.rightItem == null) return false// in case it changed
|
||||
|
||||
rightItem.amount -= amount * recipe.rightItem!!.amount
|
||||
inventory.setItem(ANVIL_INPUT_RIGHT, rightItem)
|
||||
}
|
||||
|
||||
leftItem.amount -= amount * recipe.leftItem!!.amount
|
||||
inventory.setItem(ANVIL_INPUT_LEFT, leftItem)
|
||||
|
||||
if(player.gameMode != GameMode.CREATIVE){
|
||||
player.level -= xpCost
|
||||
}
|
||||
|
||||
// Then we try to find the new values for the anvil
|
||||
val newAmount = CustomRecipeUtil.getCustomRecipeAmount(recipe, leftItem, rightItem)
|
||||
|
||||
CustomAnvil.verboseLog("new amount is $newAmount")
|
||||
if(newAmount <= 0 || recipe.exactCount){
|
||||
inventory.setItem(ANVIL_OUTPUT_SLOT, null)
|
||||
}else{
|
||||
val resultItem: ItemStack = recipe.resultItem!!.clone()
|
||||
resultItem.amount *= newAmount
|
||||
|
||||
val newXp = newAmount * newAmount
|
||||
|
||||
inventory.repairCost = newXp
|
||||
event.view.setProperty(InventoryView.Property.REPAIR_COST, newXp)
|
||||
|
||||
inventory.setItem(ANVIL_OUTPUT_SLOT, resultItem)
|
||||
|
||||
player.updateInventory()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun onUnitRepairExtract(
|
||||
leftItem: ItemStack,
|
||||
rightItem: ItemStack,
|
||||
output: ItemStack,
|
||||
unitRepairResult: Double,
|
||||
event: InventoryClickEvent,
|
||||
player: Player,
|
||||
inventory: AnvilInventory
|
||||
) {
|
||||
val resultCopy = leftItem.clone()
|
||||
val resultAmount = resultCopy.unitRepair(
|
||||
rightItem.amount, unitRepairResult
|
||||
)
|
||||
|
||||
// To avoid vanilla, we cancel the event for unit repair
|
||||
event.result = Event.Result.DENY
|
||||
event.isCancelled = true
|
||||
// And we give the item manually
|
||||
// But first we check if we should give the item
|
||||
val slotDestination = getActionSlot(event, player)
|
||||
if (slotDestination.type == SlotType.NO_SLOT) return
|
||||
|
||||
// Test repair cost
|
||||
val repairCost = getUnitRepairCost(inventory, player, leftItem, output, resultCopy, resultAmount)
|
||||
if(repairCost == Int.MIN_VALUE) return
|
||||
|
||||
// If not creative middle click...
|
||||
if (event.click != ClickType.MIDDLE) {
|
||||
// We remove what should be removed
|
||||
inventory.setItem(ANVIL_INPUT_LEFT, null)
|
||||
rightItem.amount -= resultAmount
|
||||
inventory.setItem(ANVIL_INPUT_RIGHT, rightItem)
|
||||
inventory.setItem(ANVIL_OUTPUT_SLOT, null)
|
||||
player.level -= repairCost
|
||||
}
|
||||
|
||||
// Finally, we add the item to the player
|
||||
if (slotDestination.type == SlotType.CURSOR) {
|
||||
player.setItemOnCursor(output)
|
||||
} else {// We assume SlotType == SlotType.INVENTORY
|
||||
player.inventory.setItem(slotDestination.slot, output)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getUnitRepairCost(inventory: AnvilInventory, player: Player,
|
||||
leftItem: ItemStack, output: ItemStack,
|
||||
resultCopy: ItemStack, resultAmount: Int): Int {
|
||||
if (player.gameMode == GameMode.CREATIVE) return 0
|
||||
|
||||
var repairCost = 0;
|
||||
// Get repairCost
|
||||
leftItem.itemMeta?.let { leftMeta ->
|
||||
val leftName = leftMeta.displayName
|
||||
output.itemMeta?.let {
|
||||
// Rename cost
|
||||
if (!leftName.contentEquals(it.displayName)) {
|
||||
repairCost += ConfigOptions.itemRenameCost
|
||||
|
||||
// Color cost
|
||||
if(it.displayName.contains('§')){
|
||||
repairCost += ConfigOptions.useOfColorCost
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repairCost += AnvilXpUtil.calculatePenalty(leftItem, null, resultCopy)
|
||||
repairCost += resultAmount * ConfigOptions.unitRepairCost
|
||||
|
||||
if (
|
||||
!ConfigOptions.doRemoveCostLimit &&
|
||||
ConfigOptions.doCapCost
|
||||
) {
|
||||
repairCost = min(repairCost, ConfigOptions.maxAnvilCost)
|
||||
}
|
||||
|
||||
if ((inventory.maximumRepairCost <= repairCost)
|
||||
|| (player.level < repairCost)
|
||||
) return Int.MIN_VALUE
|
||||
|
||||
return repairCost
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the destination slot or "NO_SLOT" slot container if there is no slot available
|
||||
*/
|
||||
private fun getActionSlot(event: InventoryClickEvent, player: Player): SlotContainer {
|
||||
if (event.isShiftClick) {
|
||||
val inventory = player.inventory
|
||||
val firstEmpty = inventory.firstEmpty()
|
||||
if (firstEmpty == -1) {
|
||||
return NO_SLOT
|
||||
}
|
||||
//check hotbare full
|
||||
var slotIndex = 8
|
||||
while (slotIndex >= 0 && ((inventory.getItem(slotIndex)?.type ?: Material.AIR) != Material.AIR)) {
|
||||
slotIndex--
|
||||
}
|
||||
if (slotIndex >= 0) {
|
||||
return SlotContainer(SlotType.INVENTORY, slotIndex)
|
||||
}
|
||||
slotIndex = 35 //4*9 - 1 (max of player inventory)
|
||||
while (slotIndex >= 9 && ((inventory.getItem(slotIndex)?.type ?: Material.AIR) != Material.AIR)) {
|
||||
slotIndex--
|
||||
}
|
||||
if (slotIndex < 9) {
|
||||
return NO_SLOT
|
||||
}
|
||||
return SlotContainer(SlotType.INVENTORY, slotIndex)
|
||||
}
|
||||
else if (player.itemOnCursor.type != Material.AIR) return NO_SLOT
|
||||
return CURSOR_SLOT
|
||||
}
|
||||
|
||||
private class SlotContainer(val type: SlotType, val slot: Int)
|
||||
private enum class SlotType {
|
||||
CURSOR,
|
||||
INVENTORY,
|
||||
NO_SLOT
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
package xyz.alexcrea.cuanvil.listener
|
||||
|
||||
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.entity.HumanEntity
|
||||
import org.bukkit.event.EventHandler
|
||||
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.ItemStack
|
||||
import xyz.alexcrea.cuanvil.dependency.DependencyManager
|
||||
import xyz.alexcrea.cuanvil.util.AnvilColorUtil
|
||||
import xyz.alexcrea.cuanvil.util.AnvilXpUtil
|
||||
import xyz.alexcrea.cuanvil.util.CustomRecipeUtil
|
||||
import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair
|
||||
|
||||
/**
|
||||
* Listener for anvil events
|
||||
*/
|
||||
class PrepareAnvilListener : Listener {
|
||||
|
||||
companion object {
|
||||
|
||||
// Anvil's output slot
|
||||
const val ANVIL_INPUT_LEFT = 0
|
||||
const val ANVIL_INPUT_RIGHT = 1
|
||||
const val ANVIL_OUTPUT_SLOT = 2
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handler logic for when an anvil contains items to be combined
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
fun anvilCombineCheck(event: PrepareAnvilEvent) {
|
||||
// Test if the event should bypass custom anvil.
|
||||
if(DependencyManager.tryEventPreAnvilBypass(event)) return
|
||||
|
||||
val inventory = event.inventory
|
||||
val first = inventory.getItem(ANVIL_INPUT_LEFT) ?: return
|
||||
val second = inventory.getItem(ANVIL_INPUT_RIGHT)
|
||||
|
||||
// Should find player
|
||||
val player = event.view.player
|
||||
if (!player.hasPermission(CustomAnvil.affectedByPluginPermission)) return
|
||||
|
||||
// Test custom recipe
|
||||
if(testCustomRecipe(event, inventory, first, second)) return
|
||||
|
||||
// Test rename lonely item
|
||||
if(second == null) {
|
||||
doRenaming(event, inventory, player, first)
|
||||
return
|
||||
}
|
||||
|
||||
// Test for merge
|
||||
if (first.canMergeWith(second)) {
|
||||
doMerge(event, inventory, player, first, second)
|
||||
return
|
||||
}
|
||||
|
||||
// Test for unit repair
|
||||
if(testUnitRepair(event, inventory, player, first, second)) return
|
||||
|
||||
CustomAnvil.log("no anvil fuse type found")
|
||||
event.result = null
|
||||
|
||||
}
|
||||
|
||||
private fun testCustomRecipe(event: PrepareAnvilEvent, inventory: AnvilInventory, first: ItemStack, second: ItemStack?): Boolean {
|
||||
val recipe = CustomRecipeUtil.getCustomRecipe(first, second)
|
||||
CustomAnvil.verboseLog("custom recipe not null? ${recipe != null}")
|
||||
if(recipe == null) return false
|
||||
|
||||
val amount = CustomRecipeUtil.getCustomRecipeAmount(recipe, first, second)
|
||||
|
||||
val resultItem: ItemStack = recipe.resultItem!!.clone()
|
||||
resultItem.amount *= amount
|
||||
|
||||
event.result = resultItem
|
||||
AnvilXpUtil.setAnvilInvXp(inventory, event.view, recipe.xpCostPerCraft * amount, true)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun doRenaming(event: PrepareAnvilEvent, inventory: AnvilInventory,
|
||||
player: HumanEntity, first: ItemStack) {
|
||||
val resultItem = first.clone()
|
||||
var anvilCost = handleRename(resultItem, inventory, player)
|
||||
|
||||
// Test/stop if nothing changed.
|
||||
if (first == resultItem) {
|
||||
CustomAnvil.log("no right item, But input is same as output")
|
||||
event.result = null
|
||||
return
|
||||
}
|
||||
|
||||
event.result = resultItem
|
||||
|
||||
anvilCost += AnvilXpUtil.calculatePenalty(first, null, resultItem)
|
||||
|
||||
AnvilXpUtil.setAnvilInvXp(inventory, event.view, anvilCost)
|
||||
}
|
||||
|
||||
private fun handleRename(resultItem: ItemStack, inventory: AnvilInventory, player: HumanEntity): Int {
|
||||
// Rename item and add renaming cost
|
||||
resultItem.itemMeta?.let {
|
||||
val displayName = ChatColor.stripColor(it.displayName)
|
||||
var inventoryName = ChatColor.stripColor(inventory.renameText)
|
||||
|
||||
var sumCost = 0
|
||||
|
||||
var useColor = false
|
||||
if(ConfigOptions.renameColorPossible){
|
||||
val resultString = StringBuilder(inventoryName)
|
||||
|
||||
useColor = AnvilColorUtil.handleRenamingColor(resultString, player)
|
||||
|
||||
if(useColor) {
|
||||
inventoryName = resultString.toString()
|
||||
|
||||
sumCost+= ConfigOptions.useOfColorCost
|
||||
}
|
||||
}
|
||||
|
||||
if ((!useColor && (!displayName.contentEquals(inventoryName))) || (useColor && !(it.displayName).contentEquals(inventoryName))) {
|
||||
it.setDisplayName(inventoryName)
|
||||
resultItem.itemMeta = it
|
||||
|
||||
sumCost+= ConfigOptions.itemRenameCost
|
||||
}
|
||||
|
||||
return sumCost
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
private fun doMerge(event: PrepareAnvilEvent, inventory: AnvilInventory,
|
||||
player: HumanEntity,
|
||||
first: ItemStack, second: ItemStack) {
|
||||
val newEnchants = first.findEnchantments()
|
||||
.combineWith(second.findEnchantments(), first, player)
|
||||
val resultItem = first.clone()
|
||||
resultItem.setEnchantmentsUnsafe(newEnchants)
|
||||
|
||||
// Calculate enchantment cost
|
||||
var anvilCost = AnvilXpUtil.getRightValues(second, resultItem)
|
||||
// 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)
|
||||
anvilCost += if (repaired) ConfigOptions.itemRepairCost else 0
|
||||
}
|
||||
|
||||
// Test/stop if nothing changed.
|
||||
if (first == resultItem) {
|
||||
CustomAnvil.log("Mergable with second, But input is same as output")
|
||||
event.result = null
|
||||
return
|
||||
}
|
||||
// As calculatePenalty edit result, we need to calculate penalty after checking equality
|
||||
anvilCost += AnvilXpUtil.calculatePenalty(first, second, resultItem)
|
||||
// Calculate rename cost
|
||||
anvilCost += handleRename(resultItem, inventory, player)
|
||||
|
||||
// Finally, we set result
|
||||
event.result = resultItem
|
||||
|
||||
AnvilXpUtil.setAnvilInvXp(inventory, event.view, anvilCost)
|
||||
}
|
||||
|
||||
private fun testUnitRepair(event: PrepareAnvilEvent, inventory: AnvilInventory, player: HumanEntity,
|
||||
first: ItemStack, second: ItemStack): Boolean {
|
||||
val unitRepairAmount = first.getRepair(second) ?: return false
|
||||
|
||||
val resultItem = first.clone()
|
||||
var anvilCost = handleRename(resultItem, inventory, player)
|
||||
|
||||
val repairAmount = resultItem.unitRepair(second.amount, unitRepairAmount)
|
||||
if (repairAmount > 0) {
|
||||
anvilCost += repairAmount * ConfigOptions.unitRepairCost
|
||||
}
|
||||
// We do not care about right item penalty for unit repair
|
||||
anvilCost += AnvilXpUtil.calculatePenalty(first, null, resultItem, true)
|
||||
|
||||
// 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 = resultItem
|
||||
|
||||
AnvilXpUtil.setAnvilInvXp(inventory, event.view, anvilCost)
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
88
src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilColorUtil.kt
Normal file
88
src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilColorUtil.kt
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
package xyz.alexcrea.cuanvil.util
|
||||
|
||||
import io.delilaheve.util.ConfigOptions
|
||||
import org.bukkit.entity.HumanEntity
|
||||
import java.util.regex.Matcher
|
||||
import java.util.regex.Pattern
|
||||
|
||||
object AnvilColorUtil {
|
||||
private val HEX_PATTERN: Pattern = Pattern.compile("#[A-Fa-f0-9]{6}") // pattern to find hexadecimal string
|
||||
|
||||
fun handleRenamingColor(textToColor: StringBuilder, player: HumanEntity): Boolean {
|
||||
val usePermission = ConfigOptions.permissionNeededForColor
|
||||
val canUseColorCode = ConfigOptions.allowColorCode && (!usePermission || player.hasPermission("ca.color.code"))
|
||||
val canUseHexColor = ConfigOptions.allowHexadecimalColor && (!usePermission || player.hasPermission("ca.color.hex"))
|
||||
|
||||
if((!canUseColorCode) && (!canUseHexColor)) return false
|
||||
|
||||
var useColor = false
|
||||
// Handle color code
|
||||
if(canUseColorCode){
|
||||
var nbReplacement = replaceAll(textToColor, "&", "§", 2)
|
||||
nbReplacement -= 2 * replaceAll(textToColor, "§§", "&", 2)
|
||||
|
||||
if(nbReplacement > 0) useColor = true
|
||||
}
|
||||
|
||||
if(canUseHexColor){
|
||||
val nbReplacement = replaceHexToColor(textToColor, 7)
|
||||
|
||||
if(nbReplacement > 0) useColor = true
|
||||
}
|
||||
|
||||
return useColor
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace every instance of "from" to "to".
|
||||
* @param builder The builder to replace the string from.
|
||||
* @param from The source that should be replaced.
|
||||
* @param to The string that should replace.
|
||||
* @param endOffset Amount of character that should be ignored at the end.
|
||||
* @return The number of replacement was that was done.
|
||||
*/
|
||||
private fun replaceAll(builder: java.lang.StringBuilder, from: String, to: String, endOffset: Int): Int {
|
||||
var index = builder.indexOf(from)
|
||||
var numberOfChanges = 0
|
||||
|
||||
while (index != -1 && index < builder.length - endOffset) {
|
||||
builder.replace(index, index + from.length, to)
|
||||
index += to.length
|
||||
index = builder.indexOf(from, index)
|
||||
|
||||
numberOfChanges+=1
|
||||
}
|
||||
|
||||
return numberOfChanges
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace every hex color formatted like #000000 to the minecraft format
|
||||
* @param builder The builder to replace the hex color from.
|
||||
* @param endOffset Amount of character that should be ignored at the end.
|
||||
* @return The number of replacement was that was done.
|
||||
*/
|
||||
private fun replaceHexToColor(builder: StringBuilder, endOffset: Int): Int {
|
||||
val matcher: Matcher = HEX_PATTERN.matcher(builder)
|
||||
|
||||
var numberOfChanges = 0
|
||||
var startIndex = 0
|
||||
|
||||
while(matcher.find(startIndex)){
|
||||
startIndex = matcher.start()
|
||||
if(startIndex >= builder.length - endOffset) break
|
||||
|
||||
builder.replace(startIndex, startIndex + 1, "§x")
|
||||
startIndex+=2
|
||||
for (i in 0..5) {
|
||||
builder.insert(startIndex, '§')
|
||||
startIndex+=2
|
||||
}
|
||||
|
||||
numberOfChanges+=1
|
||||
}
|
||||
|
||||
return numberOfChanges
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package xyz.alexcrea.cuanvil.util
|
||||
|
||||
import io.delilaheve.CustomAnvil
|
||||
import org.bukkit.inventory.ItemStack
|
||||
import xyz.alexcrea.cuanvil.config.ConfigHolder
|
||||
import xyz.alexcrea.cuanvil.recipe.AnvilCustomRecipe
|
||||
import kotlin.math.min
|
||||
|
||||
object CustomRecipeUtil {
|
||||
|
||||
fun getCustomRecipe (
|
||||
leftItem: ItemStack,
|
||||
rightItem: ItemStack?) : AnvilCustomRecipe? {
|
||||
|
||||
val recipeList = ConfigHolder.CUSTOM_RECIPE_HOLDER.recipeManager.recipeByMat[leftItem.type] ?: return null
|
||||
|
||||
CustomAnvil.verboseLog("Testing " + recipeList.size + " recipe...")
|
||||
for (recipe in recipeList) {
|
||||
if(recipe.testItem(leftItem, rightItem)){
|
||||
return recipe
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun getCustomRecipeAmount(
|
||||
recipe: AnvilCustomRecipe,
|
||||
leftItem: ItemStack,
|
||||
rightItem: ItemStack?
|
||||
): Int{
|
||||
return if(recipe.exactCount) {
|
||||
if(leftItem.amount != recipe.leftItem!!.amount){
|
||||
0
|
||||
}else if(rightItem != null && rightItem.amount != recipe.rightItem!!.amount){
|
||||
0
|
||||
}else{
|
||||
1
|
||||
}
|
||||
}
|
||||
else {
|
||||
// test amount
|
||||
val resultItem = recipe.resultItem!! // we know exist as the recipe was returned to us
|
||||
val maxResultAmount = resultItem.type.maxStackSize/resultItem.amount
|
||||
val maxLeftAmount = leftItem.amount/recipe.leftItem!!.amount
|
||||
val maxRightAmount = if(rightItem == null){ maxLeftAmount } else{ rightItem.amount/recipe.rightItem!!.amount }
|
||||
|
||||
CustomAnvil.verboseLog("resultItem: $resultItem, maxResultAmount: $maxResultAmount, maxLeftAmount: $maxLeftAmount, maxRightAmount: $maxRightAmount")
|
||||
|
||||
min(min(maxResultAmount, maxLeftAmount), maxRightAmount)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue