book edit kind of working

This commit is contained in:
alexcrea 2025-03-08 11:16:55 +01:00
parent 2a1cade9f1
commit 1b39707500
No known key found for this signature in database
GPG key ID: 43FD265DB0DBF91F
3 changed files with 180 additions and 82 deletions

View file

@ -15,18 +15,21 @@ import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.inventory.AnvilInventory import org.bukkit.inventory.AnvilInventory
import org.bukkit.inventory.InventoryView import org.bukkit.inventory.InventoryView
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.BookMeta
import xyz.alexcrea.cuanvil.dependency.DependencyManager 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_LEFT
import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_INPUT_RIGHT import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_INPUT_RIGHT
import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_OUTPUT_SLOT import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_OUTPUT_SLOT
import xyz.alexcrea.cuanvil.recipe.AnvilCustomRecipe import xyz.alexcrea.cuanvil.recipe.AnvilCustomRecipe
import xyz.alexcrea.cuanvil.util.AnvilLoreEditUtil
import xyz.alexcrea.cuanvil.util.AnvilUseType import xyz.alexcrea.cuanvil.util.AnvilUseType
import xyz.alexcrea.cuanvil.util.AnvilXpUtil import xyz.alexcrea.cuanvil.util.AnvilXpUtil
import xyz.alexcrea.cuanvil.util.CustomRecipeUtil import xyz.alexcrea.cuanvil.util.CustomRecipeUtil
import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair
import java.util.*
import kotlin.math.min import kotlin.math.min
class AnvilResultListener: Listener { class AnvilResultListener : Listener {
companion object { companion object {
// static slot container // static slot container
@ -48,57 +51,70 @@ class AnvilResultListener: Listener {
} }
// Test if the event should bypass custom anvil. // Test if the event should bypass custom anvil.
if(DependencyManager.tryClickAnvilResultBypass(event, inventory)) return if (DependencyManager.tryClickAnvilResultBypass(event, inventory)) return
val output = inventory.getItem(ANVIL_OUTPUT_SLOT) ?: return val output = inventory.getItem(ANVIL_OUTPUT_SLOT) ?: return
val leftItem = inventory.getItem(ANVIL_INPUT_LEFT) ?: return val leftItem = inventory.getItem(ANVIL_INPUT_LEFT) ?: return
val rightItem = inventory.getItem(ANVIL_INPUT_RIGHT) val rightItem = inventory.getItem(ANVIL_INPUT_RIGHT)
if(GameMode.CREATIVE != player.gameMode && inventory.repairCost >= inventory.maximumRepairCost) { if (GameMode.CREATIVE != player.gameMode && inventory.repairCost >= inventory.maximumRepairCost) {
event.result = Event.Result.DENY event.result = Event.Result.DENY
return return
} }
// Test custom recipe // Test custom recipe
val recipe = CustomRecipeUtil.getCustomRecipe(leftItem, rightItem) val recipe = CustomRecipeUtil.getCustomRecipe(leftItem, rightItem)
if(recipe != null){ if (recipe != null) {
event.result = Event.Result.ALLOW event.result = Event.Result.ALLOW
onCustomCraft( onCustomCraft(
event, recipe, player, event, recipe, player,
leftItem, rightItem, output, inventory) leftItem, rightItem, output, inventory
)
return return
} }
val canMerge = leftItem.canMergeWith(rightItem) // Do not continue if there was no change
val unitRepairResult = leftItem.getRepair(rightItem) if ((output == inventory.getItem(ANVIL_INPUT_LEFT))) {
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 event.result = Event.Result.DENY
return return
} }
// Rename
if (rightItem == null) { if (rightItem == null) {
event.result = Event.Result.ALLOW event.result = Event.Result.ALLOW
return return
} }
// Merge
val canMerge = leftItem.canMergeWith(rightItem)
if (canMerge) { if (canMerge) {
event.result = Event.Result.ALLOW event.result = Event.Result.ALLOW
} else if (unitRepairResult != null) { return
}
// Unit repair
val unitRepairResult = leftItem.getRepair(rightItem)
if (unitRepairResult != null) {
onUnitRepairExtract( onUnitRepairExtract(
leftItem, rightItem, output, leftItem, rightItem, output,
unitRepairResult, event, player, inventory unitRepairResult, event, player, inventory
) )
return return
} }
// For lore edit
if (handleBookLoreEdit(event, inventory, player, leftItem, rightItem, output)) {
return
} else if (Material.PAPER == rightItem.type) {
//TODO
} }
private fun onCustomCraft(event: InventoryClickEvent, // Else there was no working situation somehow so we deny
event.result = Event.Result.DENY
}
private fun onCustomCraft(
event: InventoryClickEvent,
recipe: AnvilCustomRecipe, recipe: AnvilCustomRecipe,
player: Player, player: Player,
leftItem: ItemStack, leftItem: ItemStack,
@ -108,7 +124,7 @@ class AnvilResultListener: Listener {
) { ) {
event.result = Event.Result.DENY event.result = Event.Result.DENY
if(recipe.leftItem == null) return // in case it changed if (recipe.leftItem == null) return // in case it changed
val amount = CustomRecipeUtil.getCustomRecipeAmount(recipe, leftItem, rightItem) val amount = CustomRecipeUtil.getCustomRecipeAmount(recipe, leftItem, rightItem)
val xpCost = amount * recipe.xpCostPerCraft val xpCost = amount * recipe.xpCostPerCraft
@ -123,7 +139,8 @@ class AnvilResultListener: Listener {
// Handle not creative middle click... // Handle not creative middle click...
if (event.click != ClickType.MIDDLE && if (event.click != ClickType.MIDDLE &&
!handleCustomCraftClick(event, recipe, inventory, player, leftItem, rightItem, amount, xpCost)) return !handleCustomCraftClick(event, recipe, inventory, player, leftItem, rightItem, amount, xpCost)
) return
// Finally, we add the item to the player // Finally, we add the item to the player
if (slotDestination.type == SlotType.CURSOR) { if (slotDestination.type == SlotType.CURSOR) {
@ -133,13 +150,15 @@ class AnvilResultListener: Listener {
} }
} }
private fun handleCustomCraftClick(event: InventoryClickEvent, recipe: AnvilCustomRecipe, private fun handleCustomCraftClick(
event: InventoryClickEvent, recipe: AnvilCustomRecipe,
inventory: AnvilInventory, player: Player, inventory: AnvilInventory, player: Player,
leftItem: ItemStack, rightItem: ItemStack?, leftItem: ItemStack, rightItem: ItemStack?,
amount: Int, xpCost: Int): Boolean { amount: Int, xpCost: Int
): Boolean {
// We remove what should be removed // We remove what should be removed
if(rightItem != null){ if (rightItem != null) {
if(recipe.rightItem == null) return false// in case it changed if (recipe.rightItem == null) return false// in case it changed
rightItem.amount -= amount * recipe.rightItem!!.amount rightItem.amount -= amount * recipe.rightItem!!.amount
inventory.setItem(ANVIL_INPUT_RIGHT, rightItem) inventory.setItem(ANVIL_INPUT_RIGHT, rightItem)
@ -148,7 +167,7 @@ class AnvilResultListener: Listener {
leftItem.amount -= amount * recipe.leftItem!!.amount leftItem.amount -= amount * recipe.leftItem!!.amount
inventory.setItem(ANVIL_INPUT_LEFT, leftItem) inventory.setItem(ANVIL_INPUT_LEFT, leftItem)
if(player.gameMode != GameMode.CREATIVE){ if (player.gameMode != GameMode.CREATIVE) {
player.level -= xpCost player.level -= xpCost
} }
@ -156,9 +175,9 @@ class AnvilResultListener: Listener {
val newAmount = CustomRecipeUtil.getCustomRecipeAmount(recipe, leftItem, rightItem) val newAmount = CustomRecipeUtil.getCustomRecipeAmount(recipe, leftItem, rightItem)
CustomAnvil.verboseLog("new amount is $newAmount") CustomAnvil.verboseLog("new amount is $newAmount")
if(newAmount <= 0 || recipe.exactCount){ if (newAmount <= 0 || recipe.exactCount) {
inventory.setItem(ANVIL_OUTPUT_SLOT, null) inventory.setItem(ANVIL_OUTPUT_SLOT, null)
}else{ } else {
val resultItem: ItemStack = recipe.resultItem!!.clone() val resultItem: ItemStack = recipe.resultItem!!.clone()
resultItem.amount *= newAmount resultItem.amount *= newAmount
@ -174,6 +193,48 @@ class AnvilResultListener: Listener {
return true return true
} }
private fun extractAnvilResult(
event: InventoryClickEvent,
player: Player,
inventory: AnvilInventory,
leftItem: ItemStack,
leftRemoveCount: Int,
rightItem: ItemStack,
rightRemoveCount: Int,
output: ItemStack,
repairCost: Int,
): Boolean {
// Assumed if player do not have enough xp then it returned MIN_VALUE
if (repairCost == Int.MIN_VALUE) return false
// Where should we get the item
val slotDestination = getActionSlot(event, player)
if (slotDestination.type == SlotType.NO_SLOT) return false
// If not creative middle click...
if (event.click != ClickType.MIDDLE) {
// We remove what should be removed
leftItem.amount -= leftRemoveCount
inventory.setItem(ANVIL_INPUT_LEFT, leftItem)
rightItem.amount -= rightRemoveCount
inventory.setItem(ANVIL_INPUT_RIGHT, rightItem)
inventory.setItem(ANVIL_OUTPUT_SLOT, null)
player.level -= repairCost
}
// Finally, we add the item to the player
if (SlotType.CURSOR == slotDestination.type) {
player.setItemOnCursor(output)
} else {// We assume SlotType == SlotType.INVENTORY
player.inventory.setItem(slotDestination.slot, output)
}
// TODO probably anvil damage & sound here ??
return true
}
private fun onUnitRepairExtract( private fun onUnitRepairExtract(
leftItem: ItemStack, leftItem: ItemStack,
rightItem: ItemStack, rightItem: ItemStack,
@ -191,36 +252,24 @@ class AnvilResultListener: Listener {
// To avoid vanilla, we cancel the event for unit repair // To avoid vanilla, we cancel the event for unit repair
event.result = Event.Result.DENY event.result = Event.Result.DENY
event.isCancelled = true 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 // Get repair cost
val repairCost = getUnitRepairCost(inventory, player, leftItem, output, resultCopy, resultAmount) val repairCost = getUnitRepairCost(inventory, player, leftItem, output, resultCopy, resultAmount)
if(repairCost == Int.MIN_VALUE) return
// If not creative middle click... // And then we give the item manually
if (event.click != ClickType.MIDDLE) { extractAnvilResult(
// We remove what should be removed event, player, inventory,
inventory.setItem(ANVIL_INPUT_LEFT, null) leftItem, 1,
rightItem.amount -= resultAmount rightItem, resultAmount,
inventory.setItem(ANVIL_INPUT_RIGHT, rightItem) output, repairCost
inventory.setItem(ANVIL_OUTPUT_SLOT, null) )
player.level -= repairCost
} }
// Finally, we add the item to the player private fun getUnitRepairCost(
if (slotDestination.type == SlotType.CURSOR) { inventory: AnvilInventory, player: Player,
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, leftItem: ItemStack, output: ItemStack,
resultCopy: ItemStack, resultAmount: Int): Int { resultCopy: ItemStack, resultAmount: Int
): Int {
if (player.gameMode == GameMode.CREATIVE) return 0 if (player.gameMode == GameMode.CREATIVE) return 0
var repairCost = 0 var repairCost = 0
@ -233,7 +282,7 @@ class AnvilResultListener: Listener {
repairCost += ConfigOptions.itemRenameCost repairCost += ConfigOptions.itemRenameCost
// Color cost // Color cost
if(it.displayName.contains('§')){ if (it.displayName.contains('§')) {
repairCost += ConfigOptions.useOfColorCost repairCost += ConfigOptions.useOfColorCost
} }
} }
@ -257,6 +306,51 @@ class AnvilResultListener: Listener {
return repairCost return repairCost
} }
private fun handleBookLoreEdit(
event: InventoryClickEvent,
inventory: AnvilInventory,
player: Player,
leftItem: ItemStack,
rightItem: ItemStack,
output: ItemStack,
): Boolean {
if (Material.WRITABLE_BOOK != rightItem.type) return false
val bookMeta = rightItem.itemMeta as BookMeta
val editType = AnvilLoreEditUtil.bookLoreEditTypeAppend(leftItem, rightItem) ?: return false
if (editType) {
if (output != AnvilLoreEditUtil.handleLoreAppendByBook(player, leftItem, bookMeta)) return false
// Remove pages to
val bookCopy = rightItem.clone()
bookMeta.pages = Collections.emptyList()
bookCopy.itemMeta = bookMeta
return extractAnvilResult(
event, player, inventory,
leftItem, 1,
bookCopy, 0,
output, 0
) //TODO DO REPAIR COST
} else {
if (output != AnvilLoreEditUtil.handleLoreRemoveByBook(player, leftItem, rightItem, bookMeta)) return false
// remove lore
val leftCopy = leftItem.clone()
val leftMeta = leftCopy.itemMeta
leftMeta!!.lore = null
leftCopy.itemMeta = leftMeta
return extractAnvilResult(
event, player, inventory,
leftCopy, 0,
rightItem, 1,
output, 0
) //TODO DO REPAIR COST
}
}
/** /**
* Get the destination slot or "NO_SLOT" slot container if there is no slot available * Get the destination slot or "NO_SLOT" slot container if there is no slot available
*/ */
@ -283,8 +377,7 @@ class AnvilResultListener: Listener {
return NO_SLOT return NO_SLOT
} }
return SlotContainer(SlotType.INVENTORY, slotIndex) return SlotContainer(SlotType.INVENTORY, slotIndex)
} } else if (player.itemOnCursor.type != Material.AIR) return NO_SLOT
else if (player.itemOnCursor.type != Material.AIR) return NO_SLOT
return CURSOR_SLOT return CURSOR_SLOT
} }

View file

@ -245,7 +245,7 @@ class PrepareAnvilListener : Listener {
event.result = result event.result = result
// TODO forgot about xp config & logic // TODO forgot about xp config & logic
// AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, anvilCost) AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, 1)
return true return true
} }

View file

@ -19,26 +19,27 @@ object AnvilLoreEditUtil {
return ConfigOptions.PaperLoreEditNeedPermission && player.hasPermission(LORE_BY_PAPER) return ConfigOptions.PaperLoreEditNeedPermission && player.hasPermission(LORE_BY_PAPER)
} }
private fun handleLoreAppendByBook(player: Permissible, first: ItemStack, book: BookMeta): ItemStack? { fun handleLoreAppendByBook(player: Permissible, first: ItemStack, book: BookMeta): ItemStack? {
if(!hasLoreEditByBookPermission(player)) return null if (!hasLoreEditByBookPermission(player)) return null
val result = first.clone() val result = first.clone()
val meta = result.itemMeta val meta = result.itemMeta
//TODO take into account previous lore
meta?.lore = book.pages[0].split("\n") //TODO check color if color is enabled meta?.lore = book.pages[0].split("\n") //TODO check color if color is enabled
result.itemMeta = meta result.itemMeta = meta
return result return result
} }
private fun handleLoreRemoveByBook(player: Permissible, first: ItemStack, second: ItemStack, book: BookMeta): ItemStack? { fun handleLoreRemoveByBook(player: Permissible, first: ItemStack, second: ItemStack, book: BookMeta): ItemStack? {
if(!hasLoreEditByBookPermission(player)) return null if (!hasLoreEditByBookPermission(player)) return null
val meta = first.itemMeta val meta = first.itemMeta
if(meta == null || !meta.hasLore()) return null if (meta == null || !meta.hasLore()) return null
val bookPage = StringBuilder() val bookPage = StringBuilder()
meta.lore!!.forEach { meta.lore!!.forEach {
if(bookPage.isNotEmpty()) bookPage.append('\n') if (bookPage.isNotEmpty()) bookPage.append('\n')
bookPage.append(it) bookPage.append(it)
} }
@ -52,15 +53,16 @@ object AnvilLoreEditUtil {
return result return result
} }
fun bookLoreEditType(second: ItemStack) : Boolean? { // Return true if append, false if remove, null if neither
fun bookLoreEditTypeAppend(first: ItemStack, second: ItemStack): Boolean? {
// Test if the book & quil contain content // Test if the book & quil contain content
val meta = second.itemMeta as BookMeta val meta = second.itemMeta as BookMeta
var hasContent = false var hasContent = false
if(meta.hasPages() && meta.pageCount >= 1){ if (meta.hasPages() && meta.pageCount >= 1) {
// Test if the pages is ok // Test if the pages is ok
for (page in meta.pages) { for (page in meta.pages) {
if(page.isNotEmpty()) { if (page.isNotEmpty()) {
hasContent = true hasContent = true
break break
} }
@ -68,22 +70,25 @@ object AnvilLoreEditUtil {
} }
// We don't want to "add" the first page is there is content and the first page is empty // We don't want to "add" the first page is there is content and the first page is empty
if(hasContent){ if (hasContent) {
if(meta.pages[0].isEmpty()) return null if (meta.pages[0].isEmpty()) return null
if(ConfigOptions.appendLoreBookAndQuil) if (ConfigOptions.appendLoreBookAndQuil)
return true return true
} } else if (ConfigOptions.removeLoreBookAndQuil) {
else if(ConfigOptions.removeLoreBookAndQuil) { if (!first.hasItemMeta()) return null
return false
val leftMeta = first.itemMeta!!
return if (leftMeta.hasLore()) false
else null
} }
return null return null
} }
fun tryLoreEditByBook(player: HumanEntity, first: ItemStack, second: ItemStack): ItemStack? { fun tryLoreEditByBook(player: HumanEntity, first: ItemStack, second: ItemStack): ItemStack? {
val bookType = bookLoreEditType(second) ?: return null val bookType = bookLoreEditTypeAppend(first, second) ?: return null
val meta = second.itemMeta as BookMeta val meta = second.itemMeta as BookMeta
return if(bookType) handleLoreAppendByBook(player, first, meta) return if (bookType) handleLoreAppendByBook(player, first, meta)
else handleLoreRemoveByBook(player, first, second, meta) else handleLoreRemoveByBook(player, first, second, meta)
} }