mirror of
https://github.com/alexcrea/CustomAnvil.git
synced 2026-06-23 08:14:00 +02:00
Lot of internal change and monetary cost (#116)
Internal changes this big was not intentional but had to do it for monetary cost excluding that: - add monetary cost, with dependency on rename dialogue - also change some a bit rename dialog
This commit is contained in:
commit
380b0de92f
40 changed files with 1702 additions and 717 deletions
|
|
@ -40,11 +40,14 @@ repositories {
|
|||
// ItemsAdder
|
||||
maven(url = "https://maven.devs.beer/")
|
||||
|
||||
// for fast stats
|
||||
// For fast stats
|
||||
maven {
|
||||
name = "thenextlvlReleases"
|
||||
url = uri("https://repo.thenextlvl.net/releases")
|
||||
}
|
||||
|
||||
// For vault unlocked
|
||||
maven { url = uri("https://repo.codemc.io/repository/creatorfromhell/") }
|
||||
}
|
||||
|
||||
val reobfNMS = providers.gradleProperty("subprojects.reobfnms")
|
||||
|
|
@ -103,6 +106,9 @@ dependencies {
|
|||
// ItemsAdder API
|
||||
compileOnly("dev.lone:api-itemsadder:4.0.10")
|
||||
|
||||
// Vault api
|
||||
compileOnly("net.milkbowl.vault:VaultUnlockedAPI:2.16")
|
||||
|
||||
// Include nms
|
||||
implementation(project(":nms:nms-common"))
|
||||
implementation(project(":nms:nms-paper"))
|
||||
|
|
|
|||
|
|
@ -430,6 +430,33 @@ lore_edit:
|
|||
allow_hexadecimal_color: false
|
||||
allow_minimessage: true
|
||||
|
||||
# Allow to replace the xp cost by a monetary cost
|
||||
# If enabled it will not be bound to the experience level limits
|
||||
#
|
||||
# It also requires to enable dialog rename (set "enable_dialog_rename: false" a bit higher)
|
||||
# If dialog rename permission is enabled and player do not have the permission merge will fall back to vanilla xp cost
|
||||
#
|
||||
# If you are using custom craft I recommend using Linear Xp Cost with Exact Linear Xp as normal Xp Cost will act "weird"
|
||||
# But Linear Xp will act as 1$ time global multiplier. In other word: like you expect
|
||||
#
|
||||
# As this feature require dialog rename, it can only be enabled starting with paper 1.21.6 and later
|
||||
monetary_cost:
|
||||
enabled: false
|
||||
# If using vault unlocked this allow to specify what currency should be used for anvil usage
|
||||
# default being the default currency
|
||||
currency: default
|
||||
# multiply the anvil cost by a value to allow to have price a big bigger than like 40
|
||||
multipliers:
|
||||
# global multipliers. all usage type will be multiplied by this value
|
||||
global: 1.0
|
||||
# usage specific type. it will only apply for specific xp "reason"
|
||||
enchantment: 1.0 # related to enchantments level
|
||||
repair: 1.0 # for repairing via unit repair (per unit)
|
||||
rename: 1.0 # for renaming the item
|
||||
lore_edit: 1.0 # for changing the lore of the item (only if lore edit is enabled)
|
||||
work_penalty: 1.0 # for work penalty (aka use penalty)
|
||||
recipe: 1.0 # for custom anvil recipe cost
|
||||
|
||||
# Whether to show debug logging
|
||||
debug_log: false
|
||||
|
||||
|
|
|
|||
|
|
@ -450,6 +450,33 @@ lore_edit:
|
|||
allow_hexadecimal_color: false
|
||||
allow_minimessage: true
|
||||
|
||||
# Allow to replace the xp cost by a monetary cost
|
||||
# If enabled it will not be bound to the experience level limits
|
||||
#
|
||||
# It also requires to enable dialog rename (set "enable_dialog_rename: false" a bit higher)
|
||||
# If dialog rename permission is enabled and player do not have the permission merge will fall back to vanilla xp cost
|
||||
#
|
||||
# If you are using custom craft I recommend using Linear Xp Cost with Exact Linear Xp as normal Xp Cost will act "weird"
|
||||
# But Linear Xp will act as 1$ time global multiplier. In other word: like you expect
|
||||
#
|
||||
# As this feature require dialog rename, it can only be enabled starting with paper 1.21.6 and later
|
||||
monetary_cost:
|
||||
enabled: false
|
||||
# If using vault unlocked this allow to specify what currency should be used for anvil usage
|
||||
# default being the default currency
|
||||
currency: default
|
||||
# multiply the anvil cost by a value to allow to have price a big bigger than like 40
|
||||
multipliers:
|
||||
# global multipliers. all usage type will be multiplied by this value
|
||||
global: 1.0
|
||||
# usage specific type. it will only apply for specific xp "reason"
|
||||
enchantment: 1.0 # related to enchantments level
|
||||
repair: 1.0 # for repairing via unit repair (per unit)
|
||||
rename: 1.0 # for renaming the item
|
||||
lore_edit: 1.0 # for changing the lore of the item (only if lore edit is enabled)
|
||||
work_penalty: 1.0 # for work penalty (aka use penalty)
|
||||
recipe: 1.0 # for custom anvil recipe cost
|
||||
|
||||
# Whether to show debug logging
|
||||
debug_log: false
|
||||
|
||||
|
|
|
|||
|
|
@ -442,6 +442,33 @@ lore_edit:
|
|||
allow_hexadecimal_color: false
|
||||
allow_minimessage: true
|
||||
|
||||
# Allow to replace the xp cost by a monetary cost
|
||||
# If enabled it will not be bound to the experience level limits
|
||||
#
|
||||
# It also requires to enable dialog rename (set "enable_dialog_rename: false" a bit higher)
|
||||
# If dialog rename permission is enabled and player do not have the permission merge will fall back to vanilla xp cost
|
||||
#
|
||||
# If you are using custom craft I recommend using Linear Xp Cost with Exact Linear Xp as normal Xp Cost will act "weird"
|
||||
# But Linear Xp will act as 1$ time global multiplier. In other word: like you expect
|
||||
#
|
||||
# As this feature require dialog rename, it can only be enabled starting with paper 1.21.6 and later
|
||||
monetary_cost:
|
||||
enabled: false
|
||||
# If using vault unlocked this allow to specify what currency should be used for anvil usage
|
||||
# default being the default currency
|
||||
currency: default
|
||||
# multiply the anvil cost by a value to allow to have price a big bigger than like 40
|
||||
multipliers:
|
||||
# global multipliers. all usage type will be multiplied by this value
|
||||
global: 1.0
|
||||
# usage specific type. it will only apply for specific xp "reason"
|
||||
enchantment: 1.0 # related to enchantments level
|
||||
repair: 1.0 # for repairing via unit repair (per unit)
|
||||
rename: 1.0 # for renaming the item
|
||||
lore_edit: 1.0 # for changing the lore of the item (only if lore edit is enabled)
|
||||
work_penalty: 1.0 # for work penalty (aka use penalty)
|
||||
recipe: 1.0 # for custom anvil recipe cost
|
||||
|
||||
# Whether to show debug logging
|
||||
debug_log: false
|
||||
|
||||
|
|
|
|||
|
|
@ -430,6 +430,33 @@ lore_edit:
|
|||
allow_hexadecimal_color: false
|
||||
allow_minimessage: true
|
||||
|
||||
# Allow to replace the xp cost by a monetary cost
|
||||
# If enabled it will not be bound to the experience level limits
|
||||
#
|
||||
# It also requires to enable dialog rename (set "enable_dialog_rename: false" a bit higher)
|
||||
# If dialog rename permission is enabled and player do not have the permission merge will fall back to vanilla xp cost
|
||||
#
|
||||
# If you are using custom craft I recommend using Linear Xp Cost with Exact Linear Xp as normal Xp Cost will act "weird"
|
||||
# But Linear Xp will act as 1$ time global multiplier. In other word: like you expect
|
||||
#
|
||||
# As this feature require dialog rename, it can only be enabled starting with paper 1.21.6 and later
|
||||
monetary_cost:
|
||||
enabled: false
|
||||
# If using vault unlocked this allow to specify what currency should be used for anvil usage
|
||||
# default being the default currency
|
||||
currency: default
|
||||
# multiply the anvil cost by a value to allow to have price a big bigger than like 40
|
||||
multipliers:
|
||||
# global multipliers. all usage type will be multiplied by this value
|
||||
global: 1.0
|
||||
# usage specific type. it will only apply for specific xp "reason"
|
||||
enchantment: 1.0 # related to enchantments level
|
||||
repair: 1.0 # for repairing via unit repair (per unit)
|
||||
rename: 1.0 # for renaming the item
|
||||
lore_edit: 1.0 # for changing the lore of the item (only if lore edit is enabled)
|
||||
work_penalty: 1.0 # for work penalty (aka use penalty)
|
||||
recipe: 1.0 # for custom anvil recipe cost
|
||||
|
||||
# Whether to show debug logging
|
||||
debug_log: false
|
||||
|
||||
|
|
|
|||
|
|
@ -18,4 +18,6 @@ interface AnvilRenameDialog {
|
|||
|
||||
fun currentText(player: HumanEntity): String?
|
||||
|
||||
fun isOpenFor(player: HumanEntity): Boolean
|
||||
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ import org.bukkit.craftbukkit.inventory.CraftInventoryView
|
|||
import org.bukkit.craftbukkit.inventory.view.CraftAnvilView
|
||||
import org.bukkit.entity.HumanEntity
|
||||
import org.bukkit.event.inventory.PrepareAnvilEvent
|
||||
import org.bukkit.inventory.InventoryView
|
||||
import org.bukkit.inventory.ItemStack
|
||||
import org.bukkit.persistence.PersistentDataType
|
||||
import org.bukkit.plugin.Plugin
|
||||
|
|
@ -32,7 +33,7 @@ class AnvilRenameDialogImpl(
|
|||
val keepUserPreviousDialog: Supplier<Boolean>,
|
||||
val maxLength: Supplier<Int>,
|
||||
val plugin: Plugin,
|
||||
): AnvilRenameDialog {
|
||||
) : AnvilRenameDialog {
|
||||
|
||||
companion object {
|
||||
private const val RENAME_TEXT_KEY = "rename"
|
||||
|
|
@ -43,16 +44,28 @@ class AnvilRenameDialogImpl(
|
|||
|
||||
// Need to be able to translate it later !
|
||||
private val USER_FACING_RENAME_TITLE = Component.text("Rename Your Item")
|
||||
private val USER_FACING_WARNING = Component.text("Note that the repair text will appear blank after Confirm\n" +
|
||||
"But the name will be correctly applied")
|
||||
private val USER_FACING_WARNING = Component.text(
|
||||
"Note that the repair text will appear blank after Confirm\n" +
|
||||
"But the name will be correctly applied"
|
||||
)
|
||||
private val USER_FACING_CONFIRM = Component.text("Confirm").color(TextColor.fromHexString("#40FF40"))
|
||||
private val USER_FACING_CANCEL = Component.text("Cancel").color(TextColor.fromHexString("#FF4040"))
|
||||
|
||||
fun itemDefaultName(item: ItemStack?): String? {
|
||||
return PLAIN_TEXT_SERIALIZER.serializeOrNull(item?.effectiveName())
|
||||
}
|
||||
}
|
||||
|
||||
private val lastNames = HashMap<UUID, String>()
|
||||
private val lastRenames = HashMap<UUID, String>()
|
||||
|
||||
|
||||
private val lastLeftItem = HashMap<UUID, String>()
|
||||
private val runTaskMap = HashMap<UUID, ScheduledTask>()
|
||||
|
||||
// For monetary cost
|
||||
val hasUiOpen = HashSet<UUID>()
|
||||
|
||||
private val containerField = CraftInventoryView::class.java.getDeclaredField("container")
|
||||
|
||||
init {
|
||||
|
|
@ -63,72 +76,90 @@ class AnvilRenameDialogImpl(
|
|||
return true
|
||||
}
|
||||
|
||||
fun makeDialog(initial: String?, callback: Consumer<String?>): Dialog {
|
||||
fun makeDialog(playerID: UUID, initial: String?, callback: Consumer<String?>): Dialog {
|
||||
val maxLength = this.maxLength.get()
|
||||
val initialFinal = initial?.take(maxLength)
|
||||
|
||||
val baseBuilder = DialogBase.builder(USER_FACING_RENAME_TITLE)
|
||||
.canCloseWithEscape(true)
|
||||
.afterAction(DialogBase.DialogAfterAction.CLOSE)
|
||||
.inputs(listOf(
|
||||
DialogInput.text(RENAME_TEXT_KEY, Component.text("Rename text"))
|
||||
.maxLength(maxLength)
|
||||
.initial(initialFinal ?: "")
|
||||
.labelVisible(false)
|
||||
.width(MAX_WIDTH)
|
||||
.build(),
|
||||
.inputs(
|
||||
listOf(
|
||||
DialogInput.text(RENAME_TEXT_KEY, Component.text("Rename text"))
|
||||
.maxLength(maxLength)
|
||||
.initial(initialFinal ?: "")
|
||||
.labelVisible(false)
|
||||
.width(MAX_WIDTH)
|
||||
.build(),
|
||||
),
|
||||
)
|
||||
baseBuilder.body(listOf(
|
||||
baseBuilder.body(
|
||||
listOf(
|
||||
DialogBody.plainMessage(USER_FACING_WARNING, MAX_WIDTH)
|
||||
))
|
||||
)
|
||||
)
|
||||
|
||||
return Dialog.create { builder -> builder.empty()
|
||||
.base(baseBuilder.build())
|
||||
.type(DialogType.confirmation(
|
||||
ActionButton.builder(USER_FACING_CONFIRM)
|
||||
.action(DialogAction.customClick({ response, _ ->
|
||||
val text = response.getText(RENAME_TEXT_KEY)!!
|
||||
callback.accept(text)
|
||||
}, ClickCallback.Options.builder().build()))
|
||||
.build(),
|
||||
ActionButton.builder(USER_FACING_CANCEL)
|
||||
.build(),
|
||||
))
|
||||
return Dialog.create { builder ->
|
||||
builder.empty()
|
||||
.base(baseBuilder.build())
|
||||
.type(
|
||||
DialogType.confirmation(
|
||||
ActionButton.builder(USER_FACING_CONFIRM)
|
||||
.action(DialogAction.customClick({ response, _ ->
|
||||
hasUiOpen.remove(playerID)
|
||||
val text = response.getText(RENAME_TEXT_KEY)!!
|
||||
callback.accept(text)
|
||||
}, ClickCallback.Options.builder().build()))
|
||||
.build(),
|
||||
ActionButton.builder(USER_FACING_CANCEL)
|
||||
.action(DialogAction.customClick({ response, _ ->
|
||||
hasUiOpen.remove(playerID)
|
||||
}, ClickCallback.Options.builder().build()))
|
||||
.build(),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setResult(player: HumanEntity, view: CraftAnvilView, result: String?) {
|
||||
val defaultName = PLAIN_TEXT_SERIALIZER.serializeOrNull(view.getItem(0)?.effectiveName())
|
||||
if(defaultName == result) {
|
||||
setName(player, view, "")
|
||||
if(defaultName != null) lastNames[player.uniqueId] = defaultName
|
||||
}
|
||||
else setName(player, view, result)
|
||||
private fun setResult(player: HumanEntity, view: InventoryView, result: String?) {
|
||||
val defaultName = itemDefaultName(view.getItem(0))
|
||||
if (defaultName == result) {
|
||||
setName(player, view, "", null)
|
||||
if (defaultName != null) lastNames[player.uniqueId] = defaultName
|
||||
} else setName(player, view, result, result)
|
||||
}
|
||||
|
||||
private fun setName(player: HumanEntity, view: CraftAnvilView, name: String?) {
|
||||
private fun setName(player: HumanEntity, view: InventoryView, name: String?, rename: String?) {
|
||||
val menu = (containerField.get(view) as AnvilMenu)
|
||||
menu.itemName = name
|
||||
val isSameName = menu.itemName == name
|
||||
menu.itemName = rename
|
||||
|
||||
if(name == null)
|
||||
if (name == null)
|
||||
lastNames.remove(player.uniqueId)
|
||||
else
|
||||
lastNames[player.uniqueId] = name
|
||||
CraftEventFactory.callPrepareResultEvent(menu, 2);
|
||||
|
||||
if (rename == null)
|
||||
lastRenames.remove(player.uniqueId)
|
||||
else
|
||||
lastRenames[player.uniqueId] = rename
|
||||
|
||||
if (!isSameName)
|
||||
CraftEventFactory.callPrepareResultEvent(menu, 2);
|
||||
}
|
||||
|
||||
private fun nameFromItem(player: HumanEntity, item: ItemStack?): String? {
|
||||
// Already has text
|
||||
if(item?.hasItemMeta() != true || !item.itemMeta.hasCustomName())
|
||||
if (item?.hasItemMeta() != true || !item.itemMeta.hasCustomName())
|
||||
return PLAIN_TEXT_SERIALIZER.serializeOrNull(item?.effectiveName())
|
||||
|
||||
if(keepUserPreviousDialog.get() && item.hasItemMeta()) {
|
||||
if (keepUserPreviousDialog.get() && item.hasItemMeta()) {
|
||||
val lastName = item.itemMeta.persistentDataContainer.get(
|
||||
AnvilRenameDialog.PCD_KEEP_RENAME_TEXT_KEY,
|
||||
PersistentDataType.STRING)
|
||||
PersistentDataType.STRING
|
||||
)
|
||||
|
||||
if(lastName != null) return lastName
|
||||
if (lastName != null) return lastName
|
||||
}
|
||||
|
||||
return fromFormated.apply(player, item.effectiveName())
|
||||
|
|
@ -136,33 +167,37 @@ class AnvilRenameDialogImpl(
|
|||
|
||||
private fun tryShowDialogScheduled(player: HumanEntity, event: PrepareAnvilEvent) {
|
||||
val view = event.view
|
||||
if(view !is CraftAnvilView) return
|
||||
if (view !is CraftAnvilView) return
|
||||
|
||||
val renameText = view.renameText
|
||||
val leftItem = view.getItem(0)
|
||||
val leftItemStr = leftItem?.toString()
|
||||
val lastName = lastNames.getOrDefault(player.uniqueId, null)
|
||||
|
||||
if(lastLeftItem.getOrDefault(player.uniqueId, null) != leftItemStr) {
|
||||
if(leftItemStr == null)
|
||||
val lastName = lastNames.getOrDefault(player.uniqueId, null)
|
||||
val lastRename = lastRenames.getOrDefault(player.uniqueId, null)
|
||||
|
||||
if (lastLeftItem.getOrDefault(player.uniqueId, null) != leftItemStr) {
|
||||
if (leftItemStr == null)
|
||||
lastLeftItem.remove(player.uniqueId)
|
||||
else lastLeftItem[player.uniqueId] = leftItemStr
|
||||
|
||||
setName(player, view, nameFromItem(player, leftItem))
|
||||
setName(player, view, renameText, nameFromItem(player, leftItem))
|
||||
return
|
||||
}
|
||||
|
||||
if(lastName == renameText)
|
||||
if (lastName == renameText || lastRename == renameText)
|
||||
return
|
||||
|
||||
if(renameText?.isBlank() == true) {
|
||||
setName(player, view, lastNames[player.uniqueId])
|
||||
if (renameText?.isBlank() == true || renameText == itemDefaultName(leftItem)) {
|
||||
setName(player, view, lastName, lastRename)
|
||||
return
|
||||
}
|
||||
|
||||
val dialog = makeDialog(lastName)
|
||||
val dialog = makeDialog(player.uniqueId, lastRename)
|
||||
{ result -> setResult(player, view, result) }
|
||||
player.showDialog(dialog)
|
||||
|
||||
hasUiOpen.add(player.uniqueId)
|
||||
}
|
||||
|
||||
// We need to wait for a short time as changing item will change the name BEFORE the item change
|
||||
|
|
@ -178,13 +213,14 @@ class AnvilRenameDialogImpl(
|
|||
{},
|
||||
2
|
||||
)
|
||||
if(task == null) return
|
||||
if (task == null) return
|
||||
|
||||
runTaskMap[player.uniqueId] = task
|
||||
}
|
||||
|
||||
override fun closeInventory(player: HumanEntity) {
|
||||
lastNames.remove(player.uniqueId)
|
||||
lastRenames.remove(player.uniqueId)
|
||||
lastLeftItem.remove(player.uniqueId)
|
||||
runTaskMap.remove(player.uniqueId)?.cancel()
|
||||
}
|
||||
|
|
@ -193,4 +229,8 @@ class AnvilRenameDialogImpl(
|
|||
return lastNames[player.uniqueId]
|
||||
}
|
||||
|
||||
override fun isOpenFor(player: HumanEntity): Boolean {
|
||||
return hasUiOpen.contains(player.uniqueId)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package xyz.alexcrea.cuanvil.util
|
||||
|
||||
import io.papermc.paper.threadedregions.scheduler.ScheduledTask
|
||||
import org.bukkit.entity.HumanEntity
|
||||
import org.bukkit.inventory.InventoryView
|
||||
import org.bukkit.plugin.Plugin
|
||||
import xyz.alexcrea.cuanvil.dialog.AnvilRenameDialog
|
||||
import java.util.HashMap
|
||||
import java.util.UUID
|
||||
|
||||
object AnvilTitleUtil {
|
||||
|
||||
private val runTaskMap = HashMap<UUID, ScheduledTask>()
|
||||
|
||||
private fun actualRename(view: InventoryView, name: String, player: HumanEntity, anvilDialog: AnvilRenameDialog) {
|
||||
runTaskMap.remove(player.uniqueId)
|
||||
if (view.title == name) return
|
||||
|
||||
// We assume rename impl is used
|
||||
if (anvilDialog.isOpenFor(player)) return
|
||||
|
||||
view.title = name
|
||||
}
|
||||
|
||||
// We don't want to rename instantly it is causing issue with rename text
|
||||
// especially as it can "override" current ui when it is rename ui time but rename ui also need some delay
|
||||
fun rename(view: InventoryView, name: String, player: HumanEntity, anvilDialog: AnvilRenameDialog, plugin: Plugin) {
|
||||
runTaskMap.remove(player.uniqueId)?.cancel()
|
||||
|
||||
val task = player.scheduler.runDelayed(
|
||||
plugin,
|
||||
{ _ ->
|
||||
run { actualRename(view, name, player, anvilDialog) }
|
||||
},
|
||||
{
|
||||
runTaskMap.remove(player.uniqueId)
|
||||
},
|
||||
2
|
||||
)
|
||||
|
||||
if (task == null) return
|
||||
runTaskMap[player.uniqueId] = task
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package xyz.alexcrea.cuanvil.util
|
||||
|
||||
import org.bukkit.event.inventory.PrepareAnvilEvent
|
||||
import org.bukkit.inventory.InventoryView
|
||||
import org.bukkit.inventory.ItemStack
|
||||
import org.bukkit.inventory.view.AnvilView
|
||||
|
||||
object ModernPrepareAnvilCreator {
|
||||
fun createPrepareAnvil(view: InventoryView, item: ItemStack?): PrepareAnvilEvent {
|
||||
return PrepareAnvilEvent(view as AnvilView, item)
|
||||
}
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@ import org.jetbrains.annotations.NotNull;
|
|||
* Most of the time you would likely need {@link CAPreAnvilBypassEvent} or {@link CAEarlyPreAnvilBypassEvent}
|
||||
* for this event to be useful.
|
||||
* <p>
|
||||
* There is also {@link CATreatAnvilResultEvent} that may be better for some use case.
|
||||
* There is also {@link CATreatAnvilResult2Event} that may be better for some use case.
|
||||
*/
|
||||
public class CAClickResultBypassEvent extends Event implements Cancellable {
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import org.jetbrains.annotations.NotNull;
|
|||
* <p>
|
||||
* You should also use {@link CAClickResultBypassEvent} if you want to use this event for something useful.
|
||||
* <p>
|
||||
* It is also recommended that you read about {@link CAPreAnvilBypassEvent} and {@link CATreatAnvilResultEvent}
|
||||
* It is also recommended that you read about {@link CAPreAnvilBypassEvent} and {@link CATreatAnvilResult2Event}
|
||||
* as your use case may be more prone to use theses.
|
||||
*/
|
||||
public class CAEarlyPreAnvilBypassEvent extends Event implements Cancellable {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import org.jetbrains.annotations.NotNull;
|
|||
* <p>
|
||||
* You should also use {@link CAClickResultBypassEvent} if you want to use this event for something useful.
|
||||
* <p>
|
||||
* It is also recommended that you read about {@link CAEarlyPreAnvilBypassEvent} and {@link CATreatAnvilResultEvent}
|
||||
* It is also recommended that you read about {@link CAEarlyPreAnvilBypassEvent} and {@link CATreatAnvilResult2Event}
|
||||
* as your use case may be more prone to use theses.
|
||||
*/
|
||||
public class CAPreAnvilBypassEvent extends Event implements Cancellable {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,196 @@
|
|||
package xyz.alexcrea.cuanvil.api.event.listener;
|
||||
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import xyz.alexcrea.cuanvil.anvil.AnvilCost;
|
||||
import xyz.alexcrea.cuanvil.anvil.AnvilUseType;
|
||||
|
||||
/**
|
||||
* Called after custom anvil processed the click on the result on the anvil inventory.
|
||||
* This event should be used to modify the result of an anvil use.
|
||||
* <p>
|
||||
* You may also want to check {@link CAClickResultBypassEvent},
|
||||
* {@link CAPreAnvilBypassEvent}
|
||||
* and {@link CAEarlyPreAnvilBypassEvent} for your use case
|
||||
* <p>
|
||||
* A null result will cancel this event
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class CATreatAnvilResult2Event extends Event {
|
||||
|
||||
private static final HandlerList HANDLERS = new HandlerList();
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return HANDLERS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull HandlerList getHandlers() {
|
||||
return HANDLERS;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private final InventoryView view;
|
||||
|
||||
private final AnvilUseType useType;
|
||||
|
||||
@Nullable
|
||||
private final ItemStack left;
|
||||
@Nullable
|
||||
private final ItemStack right;
|
||||
|
||||
@Nullable
|
||||
private ItemStack result;
|
||||
|
||||
private final AnvilCost cost;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public CATreatAnvilResult2Event(
|
||||
@NotNull InventoryView view,
|
||||
Inventory inv,
|
||||
AnvilUseType useType,
|
||||
@Nullable ItemStack result,
|
||||
AnvilCost cost) {
|
||||
this.view = view;
|
||||
this.useType = useType;
|
||||
|
||||
this.left = inv.getItem(0); // TODO use view here
|
||||
this.right = inv.getItem(1);
|
||||
this.result = result;
|
||||
this.cost = cost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bukkit inventory view.
|
||||
* <p>
|
||||
* Temporarily marked as internal as it will get changed to anvil view on legacy removal
|
||||
* so signature will change
|
||||
*
|
||||
* @return The inventory view of this event.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public @NotNull InventoryView getView() {
|
||||
return view;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the type of use source of the result.
|
||||
*
|
||||
* @return The craft use type.
|
||||
*/
|
||||
public AnvilUseType getUseType() {
|
||||
return useType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the left item of the anvil use
|
||||
*
|
||||
* @return the left item
|
||||
*/
|
||||
public @Nullable ItemStack getLeftItem() {
|
||||
return left;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the right item of the anvil use
|
||||
*
|
||||
* @return the right item
|
||||
*/
|
||||
public @Nullable ItemStack getRightItem() {
|
||||
return right;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current result
|
||||
* <p>
|
||||
* note that it will not be null unless another listener previously set it to null.
|
||||
*
|
||||
* @return The current result.
|
||||
*/
|
||||
public @Nullable ItemStack getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current result
|
||||
* <p>
|
||||
* note that a null result will cancel this anvil use.
|
||||
*
|
||||
* @param result The new result
|
||||
*/
|
||||
public void setResult(@Nullable ItemStack result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the level cost displayed on the anvil.
|
||||
* <h3>Important note:</h3>
|
||||
* the final price are re calculated on click for the following use case:
|
||||
* <ul>
|
||||
* <li>Custom craft</li>
|
||||
* <li>Unit repair</li>
|
||||
* <li>Lore edit</li>
|
||||
* </ul>
|
||||
* This value will be used as final price for:
|
||||
* <li>Item merge</li>
|
||||
* <li>Item rename</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return The current cost.
|
||||
* @deprecated use #{@link #getCost()} instead
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.17.0")
|
||||
public int getLevelCost() {
|
||||
return cost.asXpCost();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the level cost displayed on the anvil.
|
||||
* <h3>Important note:</h3>
|
||||
* the final price are re calculated on click for the following use case:
|
||||
* <ul>
|
||||
* <li>Custom craft</li>
|
||||
* <li>Unit repair</li>
|
||||
* <li>Lore edit</li>
|
||||
* </ul>
|
||||
* This value will be used as final price for:
|
||||
* <li>Item merge</li>
|
||||
* <li>Item rename</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param levelCost The new cost.
|
||||
* @deprecated use #{@link #getCost()} and set value on this instead
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.17.0")
|
||||
public void setLevelCost(int levelCost) {
|
||||
cost.setGeneric(levelCost - cost.getGeneric() - cost.asXpCost());
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow access to the current cost of the event
|
||||
* Note that modifying this object will change the event resulting cost
|
||||
*
|
||||
* <h3>Important note:</h3>
|
||||
* the final price are re calculated on click for the following use case:
|
||||
* <ul>
|
||||
* <li>Custom craft</li>
|
||||
* <li>Unit repair</li>
|
||||
* <li>Lore edit</li>
|
||||
* </ul>
|
||||
* This value will be used as final price for:
|
||||
* <li>Item merge</li>
|
||||
* <li>Item rename</li>
|
||||
*
|
||||
* @return the current anvil cost
|
||||
*/
|
||||
public AnvilCost getCost() {
|
||||
return cost;
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +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.anvil.AnvilCost;
|
||||
import xyz.alexcrea.cuanvil.anvil.AnvilUseType;
|
||||
|
||||
/**
|
||||
* Called after custom anvil processed the click on the result on the anvil inventory.
|
||||
|
|
@ -17,8 +18,12 @@ import xyz.alexcrea.cuanvil.util.AnvilUseType;
|
|||
* and {@link CAEarlyPreAnvilBypassEvent} for your use case
|
||||
* <p>
|
||||
* A null result will cancel this pre anvil event
|
||||
*
|
||||
* @deprecated Prepare anvil Event cannot be provided as it can be called on result and therefore not have prepared anvil event
|
||||
* use {@link CATreatAnvilResult2Event} instead
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@Deprecated(forRemoval = true, since = "1.17.0")
|
||||
public class CATreatAnvilResultEvent extends Event {
|
||||
|
||||
private static final HandlerList HANDLERS = new HandlerList();
|
||||
|
|
@ -40,13 +45,13 @@ public class CATreatAnvilResultEvent extends Event {
|
|||
@Nullable
|
||||
private ItemStack result;
|
||||
|
||||
private int levelCost;
|
||||
private final AnvilCost cost;
|
||||
|
||||
public CATreatAnvilResultEvent(@NotNull PrepareAnvilEvent event, AnvilUseType useType, @Nullable ItemStack result, int levelCost) {
|
||||
public CATreatAnvilResultEvent(@NotNull PrepareAnvilEvent event, AnvilUseType useType, @Nullable ItemStack result, AnvilCost cost) {
|
||||
this.event = event;
|
||||
this.useType = useType;
|
||||
this.result = result;
|
||||
this.levelCost = levelCost;
|
||||
this.cost = cost;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -104,9 +109,11 @@ public class CATreatAnvilResultEvent extends Event {
|
|||
* </ul>
|
||||
*
|
||||
* @return The current cost.
|
||||
* @deprecated use #{@link #getCost()} instead
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.17.0")
|
||||
public int getLevelCost() {
|
||||
return levelCost;
|
||||
return cost.asXpCost();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -124,8 +131,32 @@ public class CATreatAnvilResultEvent extends Event {
|
|||
* </ul>
|
||||
*
|
||||
* @param levelCost The new cost.
|
||||
* @deprecated use #{@link #getCost()} and set value on this instead
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.17.0")
|
||||
public void setLevelCost(int levelCost) {
|
||||
this.levelCost = levelCost;
|
||||
cost.setGeneric(levelCost - cost.getGeneric() - cost.asXpCost());
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow access to the current cost of the event
|
||||
* Note that modifying this object will change the event resulting cost
|
||||
*
|
||||
* <h3>Important note:</h3>
|
||||
* the final price are re calculated on click for the following use case:
|
||||
* <ul>
|
||||
* <li>Custom craft</li>
|
||||
* <li>Unit repair</li>
|
||||
* <li>Lore edit</li>
|
||||
* </ul>
|
||||
* This value will be used as final price for:
|
||||
* <li>Item merge</li>
|
||||
* <li>Item rename</li>
|
||||
*
|
||||
* @return the current anvil cost
|
||||
*/
|
||||
public AnvilCost getCost() {
|
||||
return cost;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import xyz.alexcrea.cuanvil.command.ReloadExecutor
|
|||
import xyz.alexcrea.cuanvil.config.ConfigHolder
|
||||
import xyz.alexcrea.cuanvil.dependency.DependencyManager
|
||||
import xyz.alexcrea.cuanvil.dependency.MinecraftVersionUtil
|
||||
import xyz.alexcrea.cuanvil.dependency.economy.EconomyManager
|
||||
import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil
|
||||
import xyz.alexcrea.cuanvil.enchant.CAEnchantmentRegistry
|
||||
import xyz.alexcrea.cuanvil.gui.config.MainConfigGui
|
||||
|
|
@ -259,9 +260,11 @@ open class CustomAnvil : JavaPlugin() {
|
|||
MainConfigGui.getInstance().init(DependencyManager.packetManager)
|
||||
GuiSharedConstant.loadConstants()
|
||||
|
||||
// Prepare economy if possible
|
||||
EconomyManager.setupEconomy(this)
|
||||
|
||||
// Finally, re add default we may be missing
|
||||
PluginSetDefault.reAddMissingDefault()
|
||||
|
||||
}
|
||||
|
||||
fun reloadResource(
|
||||
|
|
|
|||
|
|
@ -3,12 +3,16 @@ package io.delilaheve.util
|
|||
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.util.AnvilUseType
|
||||
import xyz.alexcrea.cuanvil.util.dialog.AnvilRenameDialogUtil
|
||||
import java.math.BigDecimal
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
|
|
@ -72,6 +76,11 @@ object ConfigOptions {
|
|||
|
||||
const val IMMUTABLE_ENCHANTMENT_LIST = "immutable_enchantments"
|
||||
|
||||
// Monetary configs
|
||||
const val MONETARY_USAGE_ROOT = "monetary_cost"
|
||||
const val SHOULD_USE_MONEY = "$MONETARY_USAGE_ROOT.enabled"
|
||||
const val MONEY_CURRENCY = "$MONETARY_USAGE_ROOT.currency"
|
||||
const val MONETARY_MULTIPLIER_ROOT = "$MONETARY_USAGE_ROOT.multipliers"
|
||||
|
||||
// Keys for specific enchantment values
|
||||
private const val KEY_BOOK = "book"
|
||||
|
|
@ -110,6 +119,11 @@ object ConfigOptions {
|
|||
|
||||
const val DEFAULT_PER_COLOR_CODE_PERMISSION = false
|
||||
|
||||
// Monetary configs
|
||||
const val DEFAULT_SHOULD_USE_MONEY = false
|
||||
const val DEFAULT_MONEY_CURRENCY = "default"
|
||||
const val DEFAULT_MONEY_MULTIPLIER = 1.0
|
||||
|
||||
// Debug flag
|
||||
private const val DEFAULT_DEBUG_LOG = false
|
||||
private const val DEFAULT_VERBOSE_DEBUG_LOG = false
|
||||
|
|
@ -164,6 +178,11 @@ object ConfigOptions {
|
|||
// Default max before merge disabled (negative mean enabled)
|
||||
const val DEFAULT_MAX_BEFORE_MERGE_DISABLED = -1
|
||||
|
||||
// -----------
|
||||
// Permissions
|
||||
// -----------
|
||||
private const val RENAME_DIALOG_PERMISSION = "ca.rename.dialog"
|
||||
|
||||
// -------------
|
||||
// Get methods
|
||||
// -------------
|
||||
|
|
@ -452,6 +471,13 @@ object ConfigOptions {
|
|||
.getBoolean(DIALOG_RENAME_USE_PERMISSION, DEFAULT_DIALOG_RENAME_USE_PERMISSION)
|
||||
}
|
||||
|
||||
fun canUseDialogRename(player: HumanEntity): Boolean {
|
||||
if(!doRenameDialog || !AnvilRenameDialogUtil.anvilRenameDialog.canSendDialog()) return false
|
||||
if(doRenameDialogUsePermission && !player.hasPermission(RENAME_DIALOG_PERMISSION)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the dialog menu require permission
|
||||
*/
|
||||
|
|
@ -625,4 +651,29 @@ object ConfigOptions {
|
|||
return false
|
||||
}
|
||||
|
||||
/*
|
||||
* Monetary configs (only for 1.21.6+)
|
||||
* Also require dialog rename
|
||||
*/
|
||||
fun shouldUseMoney(player: HumanEntity): Boolean {
|
||||
return EconomyManager.economy?.initialized() == true &&
|
||||
canUseDialogRename(player) &&
|
||||
ConfigHolder.DEFAULT_CONFIG
|
||||
.config
|
||||
.getBoolean(SHOULD_USE_MONEY, DEFAULT_SHOULD_USE_MONEY)
|
||||
}
|
||||
|
||||
val usedCurrency: String
|
||||
get() {
|
||||
return ConfigHolder.DEFAULT_CONFIG
|
||||
.config
|
||||
.getString(MONEY_CURRENCY, DEFAULT_MONEY_CURRENCY)!!
|
||||
}
|
||||
|
||||
fun getMonetaryMultiplier(type: String): BigDecimal {
|
||||
return BigDecimal(ConfigHolder.DEFAULT_CONFIG
|
||||
.config
|
||||
.getDouble("$MONETARY_MULTIPLIER_ROOT.$type", DEFAULT_MONEY_MULTIPLIER))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
55
src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilCost.kt
Normal file
55
src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilCost.kt
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
package xyz.alexcrea.cuanvil.anvil
|
||||
|
||||
import java.math.BigDecimal
|
||||
import io.delilaheve.util.ConfigOptions.getMonetaryMultiplier as moneyMultiplier
|
||||
|
||||
open class AnvilCost {
|
||||
private val isAlone: Boolean
|
||||
var valid = true // Get set as invalid if cost can be satisfied
|
||||
var isMonetary = false
|
||||
|
||||
var generic = 0
|
||||
var enchantment = 0
|
||||
var repair = 0
|
||||
var rename = 0
|
||||
var lore = 0
|
||||
var illegalPenalty = 0
|
||||
var workPenalty = 0
|
||||
var recipe = 0
|
||||
|
||||
constructor(generic: Int) {
|
||||
this.generic = generic
|
||||
isAlone = true
|
||||
}
|
||||
|
||||
constructor() {
|
||||
isAlone = false
|
||||
}
|
||||
|
||||
fun asXpCost(): Int {
|
||||
return generic + enchantment + repair + rename + lore + illegalPenalty + workPenalty + recipe
|
||||
}
|
||||
|
||||
open fun asMonetaryCost(): BigDecimal {
|
||||
// multiply by per use type multipliers
|
||||
return BigDecimal(generic)
|
||||
.add(BigDecimal(enchantment).multiply(moneyMultiplier("enchantment")))
|
||||
.add(BigDecimal(repair).multiply(moneyMultiplier("repair")))
|
||||
.add(BigDecimal(rename).multiply(moneyMultiplier("rename")))
|
||||
.add(BigDecimal(lore).multiply(moneyMultiplier("lore_edit")))
|
||||
.add(BigDecimal(enchantment).multiply(moneyMultiplier("enchantment")))
|
||||
.add(BigDecimal(illegalPenalty).multiply(moneyMultiplier("work_penalty")))
|
||||
.add(BigDecimal(workPenalty).multiply(moneyMultiplier("work_penalty")))
|
||||
.add(BigDecimal(recipe).multiply(moneyMultiplier("recipe")))
|
||||
.multiply(moneyMultiplier("global"))
|
||||
}
|
||||
}
|
||||
|
||||
class CustomCraftCost(val rawCost: Int): AnvilCost() {
|
||||
|
||||
override fun asMonetaryCost(): BigDecimal {
|
||||
return BigDecimal(rawCost)
|
||||
.multiply(moneyMultiplier("global"))
|
||||
}
|
||||
|
||||
}
|
||||
331
src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt
Normal file
331
src/main/kotlin/xyz/alexcrea/cuanvil/anvil/AnvilMergeLogic.kt
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
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.InventoryView
|
||||
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.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
|
||||
|
||||
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: InventoryView, //TODO use anvil view
|
||||
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(view, inventory, 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, 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(
|
||||
view: InventoryView, //TODO use anvil view instead
|
||||
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(view, inventory, 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: InventoryView, //TODO use anvil view instead
|
||||
inventory: AnvilInventory,
|
||||
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, inventory, player, resultItem, AnvilUseType.CUSTOM_CRAFT, cost)
|
||||
return CustomCraftResult(result, cost, amount, recipe)
|
||||
}
|
||||
|
||||
fun testUnitRepair(
|
||||
view: InventoryView, //TODO use anvil view
|
||||
inventory: AnvilInventory,
|
||||
player: Player,
|
||||
first: ItemStack, second: ItemStack
|
||||
): UnitRepairResult {
|
||||
val unitRepairAmount = first.getRepair(second) ?: return UnitRepairResult.EMPTY
|
||||
|
||||
return testUnitRepair(view, inventory, player, first, second, unitRepairAmount)
|
||||
}
|
||||
|
||||
fun testUnitRepair(
|
||||
view: InventoryView, //TODO use anvil view instead
|
||||
inventory: AnvilInventory,
|
||||
player: Player,
|
||||
first: ItemStack, second: ItemStack,
|
||||
unitRepairAmount: Double
|
||||
): UnitRepairResult {
|
||||
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(view, inventory, 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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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(
|
||||
|
|
@ -7,15 +7,19 @@ import org.bukkit.Bukkit
|
|||
import org.bukkit.ChatColor
|
||||
import org.bukkit.command.CommandSender
|
||||
import org.bukkit.entity.HumanEntity
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.event.inventory.InventoryClickEvent
|
||||
import org.bukkit.event.inventory.PrepareAnvilEvent
|
||||
import org.bukkit.inventory.AnvilInventory
|
||||
import org.bukkit.inventory.Inventory
|
||||
import org.bukkit.inventory.InventoryView
|
||||
import org.bukkit.inventory.ItemStack
|
||||
import xyz.alexcrea.cuanvil.anvil.AnvilCost
|
||||
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
|
||||
import xyz.alexcrea.cuanvil.api.event.listener.CATreatAnvilResultEvent
|
||||
import xyz.alexcrea.cuanvil.api.event.listener.CATreatAnvilResult2Event
|
||||
import xyz.alexcrea.cuanvil.config.ConfigHolder
|
||||
import xyz.alexcrea.cuanvil.dependency.datapack.DataPackDependency
|
||||
import xyz.alexcrea.cuanvil.dependency.gui.GenericExternGuiTester
|
||||
|
|
@ -29,7 +33,6 @@ 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.MetricsUtil.trackError
|
||||
import java.util.logging.Level
|
||||
|
||||
|
|
@ -198,7 +201,7 @@ object DependencyManager {
|
|||
}
|
||||
|
||||
// Return true if should bypass (either by a dependency or error)
|
||||
fun tryEventPreAnvilBypass(event: PrepareAnvilEvent, player: HumanEntity): Boolean {
|
||||
fun tryEventPreAnvilBypass(event: PrepareAnvilEvent, player: Player): Boolean {
|
||||
try {
|
||||
return unsafeTryEventPreAnvilBypass(event, player)
|
||||
} catch (e: Exception) {
|
||||
|
|
@ -207,7 +210,7 @@ object DependencyManager {
|
|||
}
|
||||
}
|
||||
|
||||
private fun unsafeTryEventPreAnvilBypass(event: PrepareAnvilEvent, player: HumanEntity): Boolean {
|
||||
private fun unsafeTryEventPreAnvilBypass(event: PrepareAnvilEvent, player: Player): Boolean {
|
||||
// Run the event
|
||||
val bypassEvent = CAPreAnvilBypassEvent(event)
|
||||
Bukkit.getPluginManager().callEvent(bypassEvent)
|
||||
|
|
@ -232,22 +235,24 @@ object DependencyManager {
|
|||
|
||||
// Return null if there was an issue
|
||||
fun tryTreatAnvilResult(
|
||||
event: PrepareAnvilEvent,
|
||||
view: InventoryView,
|
||||
inventory: Inventory, // TODO REMOVE, use view instead on legacy removal
|
||||
player: HumanEntity,
|
||||
result: ItemStack,
|
||||
useType: AnvilUseType,
|
||||
cost: Int
|
||||
): CATreatAnvilResultEvent? {
|
||||
val treatEvent = CATreatAnvilResultEvent(event, useType, result, cost)
|
||||
cost: AnvilCost
|
||||
): ItemStack? {
|
||||
val treatEvent = CATreatAnvilResult2Event(view, inventory, useType, result, cost)
|
||||
try {
|
||||
unsafeTryTreatAnvilResult(treatEvent)
|
||||
return treatEvent;
|
||||
return treatEvent.result
|
||||
} catch (e: Exception) {
|
||||
logExceptionAndClear(event.view.player, event.inventory, e)
|
||||
logExceptionAndClear(player, inventory, e)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private fun unsafeTryTreatAnvilResult(event: CATreatAnvilResultEvent) {
|
||||
private fun unsafeTryTreatAnvilResult(event: CATreatAnvilResult2Event) {
|
||||
Bukkit.getPluginManager().callEvent(event)
|
||||
|
||||
excellentEnchantsCompatibility?.treatAnvilResult(event)
|
||||
|
|
@ -293,11 +298,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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
package xyz.alexcrea.cuanvil.dependency.economy
|
||||
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.plugin.Plugin
|
||||
import java.math.BigDecimal
|
||||
|
||||
interface EconomyManager {
|
||||
|
||||
companion object {
|
||||
var economy: EconomyManager? = null
|
||||
|
||||
fun setupEconomy(plugin: Plugin) {
|
||||
if (plugin.server.pluginManager.getPlugin("Vault") == null)
|
||||
return
|
||||
if (UnlockedEconomyManager.unlockedAvailable())
|
||||
economy = UnlockedEconomyManager(plugin)
|
||||
|
||||
if (economy == null || !economy!!.initialized())
|
||||
economy = VaultEconomyManager(plugin)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun initialized(): Boolean
|
||||
|
||||
// We assume "initialized" got checked before these function get called
|
||||
fun has(player: Player, money: BigDecimal): Boolean
|
||||
fun remove(player: Player, money: BigDecimal): Boolean
|
||||
|
||||
fun format(money: BigDecimal): String;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
package xyz.alexcrea.cuanvil.dependency.economy
|
||||
|
||||
import io.delilaheve.util.ConfigOptions
|
||||
import net.milkbowl.vault2.economy.Economy
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.plugin.Plugin
|
||||
import java.math.BigDecimal
|
||||
|
||||
class UnlockedEconomyManager : EconomyManager {
|
||||
|
||||
val plugin: String
|
||||
val economy: Economy?
|
||||
|
||||
companion object {
|
||||
fun unlockedAvailable(): Boolean {
|
||||
try {
|
||||
Class.forName("net.milkbowl.vault2.economy.Economy")
|
||||
return true
|
||||
} catch (_: ClassNotFoundException) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constructor(plugin: Plugin) {
|
||||
this.plugin = plugin.name
|
||||
|
||||
val rsp = plugin.server.servicesManager.getRegistration(Economy::class.java)
|
||||
economy = rsp?.getProvider()
|
||||
}
|
||||
|
||||
override fun initialized(): Boolean {
|
||||
return economy != null
|
||||
}
|
||||
|
||||
private fun currency(): String {
|
||||
val configured = ConfigOptions.usedCurrency
|
||||
|
||||
return if ("default".equals(configured, true))
|
||||
economy!!.getDefaultCurrency(plugin)
|
||||
else configured
|
||||
}
|
||||
|
||||
override fun has(player: Player, money: BigDecimal): Boolean {
|
||||
if (money.signum() <= 0) return true
|
||||
|
||||
return economy!!.has(
|
||||
plugin,
|
||||
player.uniqueId,
|
||||
player.world.name,
|
||||
currency(),
|
||||
money
|
||||
)
|
||||
}
|
||||
|
||||
override fun remove(player: Player, money: BigDecimal): Boolean {
|
||||
if (money.signum() <= 0) return true
|
||||
|
||||
return economy!!.withdraw(
|
||||
plugin,
|
||||
player.uniqueId,
|
||||
player.world.name,
|
||||
currency(),
|
||||
money
|
||||
)
|
||||
.transactionSuccess()
|
||||
}
|
||||
|
||||
override fun format(money: BigDecimal): String {
|
||||
return economy!!.format(plugin, money, currency())
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package xyz.alexcrea.cuanvil.dependency.economy
|
||||
|
||||
import net.milkbowl.vault.economy.Economy
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.plugin.Plugin
|
||||
import java.math.BigDecimal
|
||||
|
||||
class VaultEconomyManager : EconomyManager {
|
||||
|
||||
val economy: Economy?
|
||||
|
||||
constructor(plugin: Plugin) {
|
||||
val rsp = plugin.server.servicesManager.getRegistration(Economy::class.java)
|
||||
economy = rsp?.getProvider()
|
||||
}
|
||||
|
||||
override fun initialized(): Boolean {
|
||||
return economy != null
|
||||
}
|
||||
|
||||
override fun has(player: Player, money: BigDecimal): Boolean {
|
||||
if (money.signum() <= 0) return true
|
||||
|
||||
return economy!!.has(player, money.toDouble())
|
||||
}
|
||||
|
||||
override fun remove(player: Player, money: BigDecimal): Boolean {
|
||||
if (money.signum() <= 0) return true
|
||||
|
||||
return economy!!.withdrawPlayer(player, money.toDouble()).transactionSuccess()
|
||||
}
|
||||
|
||||
override fun format(money: BigDecimal): String {
|
||||
return economy!!.format(money.toDouble())
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -8,15 +8,16 @@ import com.jankominek.disenchantment.events.ShatterEvent
|
|||
import com.jankominek.disenchantment.listeners.DisenchantClickListener
|
||||
import com.jankominek.disenchantment.listeners.ShatterClickListener
|
||||
import io.delilaheve.CustomAnvil
|
||||
import org.bukkit.entity.HumanEntity
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.event.Listener
|
||||
import org.bukkit.event.inventory.InventoryClickEvent
|
||||
import org.bukkit.event.inventory.PrepareAnvilEvent
|
||||
import org.bukkit.inventory.AnvilInventory
|
||||
import org.bukkit.inventory.ItemStack
|
||||
import xyz.alexcrea.cuanvil.anvil.AnvilCost
|
||||
import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener
|
||||
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
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
|
|
@ -50,7 +51,7 @@ class DisenchantmentDependency {
|
|||
InventoryClickEvent.getHandlerList().unregister(listener)
|
||||
}
|
||||
|
||||
fun testPrepareAnvil(event: PrepareAnvilEvent, player: HumanEntity): Boolean {
|
||||
fun testPrepareAnvil(event: PrepareAnvilEvent, player: Player): Boolean {
|
||||
val previousResult = event.result
|
||||
event.result = null
|
||||
|
||||
|
|
@ -58,14 +59,14 @@ class DisenchantmentDependency {
|
|||
DisenchantEvent.onEvent(event)
|
||||
if (event.result != null) {
|
||||
CustomAnvil.log("Detected pre anvil item extract bypass.")
|
||||
AnvilXpUtil.setAnvilInvXp(event.inventory, event.view, player, event.inventory.repairCost)
|
||||
AnvilXpUtil.setAnvilInvCost(event.inventory, event.view, player, AnvilCost(event.inventory.repairCost))
|
||||
return true
|
||||
}
|
||||
|
||||
ShatterEvent.onEvent(event)
|
||||
if (event.result != null) {
|
||||
CustomAnvil.log("Detected pre anvil split enchant bypass.")
|
||||
AnvilXpUtil.setAnvilInvXp(event.inventory, event.view, player, event.inventory.repairCost)
|
||||
AnvilXpUtil.setAnvilInvCost(event.inventory, event.view, player, AnvilCost(event.inventory.repairCost))
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,11 +8,12 @@ import org.bukkit.event.inventory.PrepareAnvilEvent
|
|||
import org.bukkit.inventory.ItemStack
|
||||
import org.bukkit.plugin.RegisteredListener
|
||||
import xyz.alexcrea.cuanvil.api.EnchantmentApi
|
||||
import xyz.alexcrea.cuanvil.api.event.listener.CATreatAnvilResultEvent
|
||||
import xyz.alexcrea.cuanvil.api.event.listener.CATreatAnvilResult2Event
|
||||
import xyz.alexcrea.cuanvil.enchant.wrapped.CAEEPreV5Enchantment
|
||||
import xyz.alexcrea.cuanvil.enchant.wrapped.CAEEV5Enchantment
|
||||
import xyz.alexcrea.cuanvil.enchant.wrapped.CAEEV5_4Enchantment
|
||||
import xyz.alexcrea.cuanvil.enchant.wrapped.CALegacyEEEnchantment
|
||||
import xyz.alexcrea.cuanvil.util.ModernPrepareAnvilCreator
|
||||
import java.lang.reflect.Method
|
||||
import su.nightexpress.excellentenchants.api.EnchantRegistry as V5EnchantRegistry
|
||||
import su.nightexpress.excellentenchants.enchantment.impl.universal.CurseOfFragilityEnchant as LegacyCurseOfFragilityEnchant
|
||||
|
|
@ -218,14 +219,20 @@ class ExcellentEnchantsDependency {
|
|||
return handleRechargeMethod.invoke(this.usedAnvilListener, event, first, second) as Boolean
|
||||
}
|
||||
|
||||
fun treatAnvilResult(event: CATreatAnvilResultEvent) {
|
||||
val result = event.result
|
||||
if (result == null) return
|
||||
fun treatAnvilResult(event: CATreatAnvilResult2Event) {
|
||||
val result = event.result ?: return
|
||||
|
||||
val first: ItemStack = treatInput(event.event.inventory.getItem(0))
|
||||
val second: ItemStack = treatInput(event.event.inventory.getItem(1))
|
||||
val first: ItemStack = treatInput(event.leftItem)
|
||||
val second: ItemStack = treatInput(event.rightItem)
|
||||
val fakeEvent: PrepareAnvilEvent = try {
|
||||
//TODO remove this on legacy removal
|
||||
PrepareAnvilEvent(event.view, result)
|
||||
} catch (_: NoSuchMethodError) {
|
||||
ModernPrepareAnvilCreator.createPrepareAnvil(event.view, result)
|
||||
}
|
||||
handleCombineMethod.invoke(this.usedAnvilListener, fakeEvent, first, second, result)
|
||||
|
||||
handleCombineMethod.invoke(this.usedAnvilListener, event.event, first, second, result)
|
||||
event.result = fakeEvent.result
|
||||
}
|
||||
|
||||
fun testAnvilResult(event: InventoryClickEvent): Any {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package xyz.alexcrea.cuanvil.dependency.plugins
|
||||
|
||||
import io.delilaheve.CustomAnvil
|
||||
import org.bukkit.entity.HumanEntity
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.event.inventory.InventoryClickEvent
|
||||
import org.bukkit.event.inventory.PrepareAnvilEvent
|
||||
import org.bukkit.inventory.AnvilInventory
|
||||
|
|
@ -9,8 +9,9 @@ import org.bukkit.plugin.RegisteredListener
|
|||
import valorless.havenbags.HavenBags
|
||||
import valorless.havenbags.features.BagSkin
|
||||
import valorless.havenbags.features.BagUpgrade
|
||||
import xyz.alexcrea.cuanvil.anvil.AnvilCost
|
||||
import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener
|
||||
import xyz.alexcrea.cuanvil.util.AnvilXpUtil
|
||||
import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil
|
||||
|
||||
class HavenBagsDependency {
|
||||
|
||||
|
|
@ -45,7 +46,7 @@ class HavenBagsDependency {
|
|||
|
||||
}
|
||||
|
||||
fun testPrepareAnvil(event: PrepareAnvilEvent, player: HumanEntity): Boolean {
|
||||
fun testPrepareAnvil(event: PrepareAnvilEvent, player: Player): Boolean {
|
||||
val previousResult = event.result
|
||||
event.result = null
|
||||
|
||||
|
|
@ -53,14 +54,14 @@ class HavenBagsDependency {
|
|||
bagSkin.onPrepareAnvil(event)
|
||||
if (event.result != null) {
|
||||
CustomAnvil.log("Detected pre anvil heaven bag anvil skin.")
|
||||
AnvilXpUtil.setAnvilInvXp(event.inventory, event.view, player, event.inventory.repairCost)
|
||||
AnvilXpUtil.setAnvilInvCost(event.inventory, event.view, player, AnvilCost(event.inventory.repairCost))
|
||||
return true
|
||||
}
|
||||
|
||||
bagUpgrade.onPrepareAnvil(event)
|
||||
if (event.result != null) {
|
||||
CustomAnvil.log("Detected pre anvil heaven bag anvil upgrade.")
|
||||
AnvilXpUtil.setAnvilInvXp(event.inventory, event.view, player, event.inventory.repairCost)
|
||||
AnvilXpUtil.setAnvilInvCost(event.inventory, event.view, player, AnvilCost(event.inventory.repairCost))
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ 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
|
||||
|
|
@ -16,22 +15,25 @@ 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.AnvilCost
|
||||
import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic
|
||||
import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.AnvilResult
|
||||
import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.CustomCraftResult
|
||||
import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.LoreEditResult
|
||||
import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.UnitRepairResult
|
||||
import xyz.alexcrea.cuanvil.dependency.DependencyManager
|
||||
import xyz.alexcrea.cuanvil.dependency.economy.EconomyManager
|
||||
import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil.setComponentDisplayName
|
||||
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.AnvilLoreEditUtil
|
||||
import xyz.alexcrea.cuanvil.util.AnvilUseType
|
||||
import xyz.alexcrea.cuanvil.util.AnvilXpUtil
|
||||
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.config.LoreEditConfigUtil
|
||||
import xyz.alexcrea.cuanvil.util.config.LoreEditType
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import kotlin.math.min
|
||||
|
||||
|
|
@ -50,6 +52,7 @@ class AnvilResultListener : Listener {
|
|||
fun anvilExtractionCheck(event: InventoryClickEvent) {
|
||||
val player = event.whoClicked as? Player ?: return
|
||||
val inventory = event.inventory as? AnvilInventory ?: return
|
||||
val view = event.view
|
||||
|
||||
if (event.rawSlot != ANVIL_OUTPUT_SLOT) {
|
||||
return
|
||||
|
|
@ -64,84 +67,104 @@ class AnvilResultListener : Listener {
|
|||
val leftItem = inventory.getItem(ANVIL_INPUT_LEFT) ?: return
|
||||
val rightItem = inventory.getItem(ANVIL_INPUT_RIGHT)
|
||||
|
||||
// Deny by default. allow if working
|
||||
event.result = Event.Result.DENY
|
||||
if (GameMode.CREATIVE != player.gameMode && inventory.repairCost >= inventory.maximumRepairCost) {
|
||||
event.result = Event.Result.DENY
|
||||
return
|
||||
}
|
||||
|
||||
// Test custom recipe
|
||||
val recipe = CustomRecipeUtil.getCustomRecipe(leftItem, rightItem)
|
||||
if (recipe != null) {
|
||||
event.result = Event.Result.ALLOW
|
||||
val customRecipeResult = AnvilMergeLogic.testCustomRecipe(view, inventory, player, leftItem, rightItem)
|
||||
if (!customRecipeResult.isEmpty()) {
|
||||
onCustomCraft(
|
||||
event, recipe, player,
|
||||
leftItem, rightItem, output, inventory
|
||||
event, player, inventory,
|
||||
leftItem, rightItem, customRecipeResult
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Do not continue if there was no change
|
||||
if ((output == inventory.getItem(ANVIL_INPUT_LEFT))) {
|
||||
event.result = Event.Result.DENY
|
||||
return
|
||||
}
|
||||
|
||||
// Rename
|
||||
if (rightItem == null) {
|
||||
event.result = Event.Result.ALLOW
|
||||
val result = AnvilMergeLogic.doRenaming(view, inventory, player, leftItem)
|
||||
if (result.isEmpty()) return
|
||||
|
||||
extractAnvilResult(
|
||||
event, player, inventory,
|
||||
null, 0,
|
||||
null, 0,
|
||||
result
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Merge
|
||||
val canMerge = leftItem.canMergeWith(rightItem)
|
||||
if (canMerge) {
|
||||
event.result = Event.Result.ALLOW
|
||||
val result = AnvilMergeLogic.doMerge(view, inventory, player, leftItem, rightItem)
|
||||
|
||||
extractAnvilResult(
|
||||
event, player, inventory,
|
||||
null, 0,
|
||||
null, 0,
|
||||
result
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Unit repair
|
||||
val unitRepairResult = leftItem.getRepair(rightItem)
|
||||
if (unitRepairResult != null) {
|
||||
val unitRepairResult = AnvilMergeLogic.testUnitRepair(
|
||||
view, inventory, player,
|
||||
leftItem, rightItem
|
||||
)
|
||||
if (unitRepairResult.isEmpty()) {
|
||||
onUnitRepairExtract(
|
||||
leftItem, rightItem, output,
|
||||
unitRepairResult, event, player, inventory
|
||||
rightItem, event, player, inventory,
|
||||
unitRepairResult
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// For lore edit
|
||||
if (handleBookLoreEdit(event, inventory, player, leftItem, rightItem, output)) {
|
||||
return
|
||||
} else if (handlePaperLoreEdit(event, inventory, player, leftItem, rightItem, output)) {
|
||||
val loreResult = AnvilMergeLogic.testLoreEdit(player, leftItem, rightItem)
|
||||
if (!loreResult.isEmpty()) {
|
||||
if (loreResult.type.isBook)
|
||||
handleBookLoreEdit(event, inventory, player, leftItem, rightItem, loreResult)
|
||||
else
|
||||
handlePaperLoreEdit(event, inventory, player, leftItem, rightItem, loreResult)
|
||||
return
|
||||
}
|
||||
|
||||
// Else there was no working situation somehow so we deny
|
||||
event.result = Event.Result.DENY
|
||||
}
|
||||
|
||||
private fun onCustomCraft(
|
||||
event: InventoryClickEvent,
|
||||
recipe: AnvilCustomRecipe,
|
||||
player: Player,
|
||||
inventory: AnvilInventory,
|
||||
leftItem: ItemStack,
|
||||
rightItem: ItemStack?,
|
||||
output: ItemStack,
|
||||
inventory: AnvilInventory
|
||||
result: CustomCraftResult,
|
||||
) {
|
||||
event.result = Event.Result.DENY
|
||||
|
||||
if (recipe.leftItem == null) return // in case it changed
|
||||
|
||||
val amount = CustomRecipeUtil.getCustomRecipeAmount(recipe, leftItem, rightItem)
|
||||
val xpCost = recipe.determineCost(amount, leftItem, output)
|
||||
val recipe = result.recipe!!
|
||||
val rawCost = result.customCraftCost.rawCost
|
||||
val finalCost =
|
||||
if (recipe.removeExactLinearXp) xpCost
|
||||
else AnvilXpUtil.calculateLevelForXp(xpCost)
|
||||
if (recipe.removeExactLinearXp) rawCost
|
||||
else AnvilXpUtil.calculateLevelForXp(rawCost)
|
||||
|
||||
CustomAnvil.log(
|
||||
"gamemode: ${player.gameMode != GameMode.CREATIVE}, " +
|
||||
"cost: $finalCost, level: ${player.level}, " +
|
||||
"result: ${player.totalExperience < finalCost} ${player.level < finalCost}"
|
||||
)
|
||||
|
||||
CustomAnvil.log("gamemode: ${player.gameMode != GameMode.CREATIVE}, cost: $finalCost, level: ${player.level}, result: ${player.totalExperience < finalCost} ${player.level < finalCost}")
|
||||
if (player.gameMode != GameMode.CREATIVE) {
|
||||
if (recipe.removeExactLinearXp) {
|
||||
if (ConfigOptions.shouldUseMoney(player)) {
|
||||
result.cost.isMonetary = true
|
||||
if (!EconomyManager.economy!!.has(player, result.cost.asMonetaryCost())) return
|
||||
} else if (recipe.removeExactLinearXp) {
|
||||
val levelXp = AnvilXpUtil.calculateXpForLevel(player.level)
|
||||
val delta = AnvilXpUtil.calculateXpForLevel(player.level + 1) - levelXp
|
||||
val totalXp = levelXp + player.exp * delta
|
||||
|
|
@ -158,31 +181,31 @@ class AnvilResultListener : Listener {
|
|||
if (event.click != ClickType.MIDDLE &&
|
||||
!handleCustomCraftClick(
|
||||
event,
|
||||
recipe,
|
||||
inventory,
|
||||
player,
|
||||
leftItem,
|
||||
rightItem,
|
||||
amount,
|
||||
finalCost,
|
||||
recipe.removeExactLinearXp
|
||||
result
|
||||
)
|
||||
) return
|
||||
|
||||
// Finally, we add the item to the player
|
||||
if (slotDestination.type == SlotType.CURSOR) {
|
||||
player.setItemOnCursor(output)
|
||||
player.setItemOnCursor(result.item)
|
||||
} else {// We assume SlotType == SlotType.INVENTORY
|
||||
player.inventory.setItem(slotDestination.slot, output)
|
||||
player.inventory.setItem(slotDestination.slot, result.item)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCustomCraftClick(
|
||||
event: InventoryClickEvent, recipe: AnvilCustomRecipe,
|
||||
event: InventoryClickEvent,
|
||||
inventory: AnvilInventory, player: Player,
|
||||
leftItem: ItemStack, rightItem: ItemStack?,
|
||||
amount: Int, xpCost: Int, linearCost: Boolean = false
|
||||
result: CustomCraftResult
|
||||
): Boolean {
|
||||
val amount = result.amount
|
||||
val recipe = result.recipe!!
|
||||
|
||||
// We remove what should be removed
|
||||
if (rightItem != null) {
|
||||
if (recipe.rightItem == null) return false// in case it changed
|
||||
|
|
@ -194,25 +217,7 @@ class AnvilResultListener : Listener {
|
|||
leftItem.amount -= amount * recipe.leftItem!!.amount
|
||||
inventory.setItem(ANVIL_INPUT_LEFT, leftItem)
|
||||
|
||||
if (player.gameMode != GameMode.CREATIVE) {
|
||||
if (linearCost) {
|
||||
val levelXp = AnvilXpUtil.calculateXpForLevel(player.level)
|
||||
val delta = AnvilXpUtil.calculateXpForLevel(player.level + 1) - levelXp
|
||||
var totalXp = levelXp + player.exp * delta
|
||||
totalXp -= xpCost
|
||||
|
||||
val newLevel = AnvilXpUtil.calculateLevelForXp(totalXp.toInt())
|
||||
|
||||
val newLevelXp = AnvilXpUtil.calculateXpForLevel(newLevel)
|
||||
val newDelta = AnvilXpUtil.calculateXpForLevel(newLevel + 1) - newLevelXp
|
||||
val xp = (totalXp - newLevelXp) / newDelta
|
||||
|
||||
player.level = newLevel
|
||||
player.exp = xp / newDelta
|
||||
} else {
|
||||
player.level -= xpCost
|
||||
}
|
||||
}
|
||||
removeCustomCraftCost(player, result)
|
||||
|
||||
// Then we try to find the new values for the anvil
|
||||
val newAmount = CustomRecipeUtil.getCustomRecipeAmount(recipe, leftItem, rightItem)
|
||||
|
|
@ -236,6 +241,47 @@ class AnvilResultListener : Listener {
|
|||
return true
|
||||
}
|
||||
|
||||
private fun removeCustomCraftCost(player: Player, result: CustomCraftResult) {
|
||||
if (player.gameMode == GameMode.CREATIVE) return
|
||||
|
||||
val rawCost = result.customCraftCost.rawCost
|
||||
if (result.cost.isMonetary) {
|
||||
EconomyManager.economy!!.remove(player, result.cost.asMonetaryCost())
|
||||
return
|
||||
}
|
||||
|
||||
if (result.recipe!!.removeExactLinearXp) {
|
||||
val levelXp = AnvilXpUtil.calculateXpForLevel(player.level)
|
||||
val delta = AnvilXpUtil.calculateXpForLevel(player.level + 1) - levelXp
|
||||
var totalXp = levelXp + player.exp * delta
|
||||
totalXp -= rawCost
|
||||
|
||||
val newLevel = AnvilXpUtil.calculateLevelForXp(totalXp.toInt())
|
||||
|
||||
val newLevelXp = AnvilXpUtil.calculateXpForLevel(newLevel)
|
||||
val newDelta = AnvilXpUtil.calculateXpForLevel(newLevel + 1) - newLevelXp
|
||||
val xp = (totalXp - newLevelXp) / newDelta
|
||||
|
||||
player.level = newLevel
|
||||
player.exp = xp / newDelta
|
||||
} else {
|
||||
player.level -= AnvilXpUtil.calculateLevelForXp(rawCost)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun tryRemoveCost(player: Player, cost: AnvilCost): Boolean {
|
||||
if (player.gameMode == GameMode.CREATIVE) return true
|
||||
if (cost.isMonetary) {
|
||||
val result = EconomyManager.economy!!.remove(player, cost.asMonetaryCost())
|
||||
if (!result) return false
|
||||
} else {
|
||||
player.level -= cost.asXpCost()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun extractAnvilResult(
|
||||
event: InventoryClickEvent,
|
||||
player: Player,
|
||||
|
|
@ -244,15 +290,17 @@ class AnvilResultListener : Listener {
|
|||
leftRemoveCount: Int,
|
||||
rightItem: ItemStack?,
|
||||
rightRemoveCount: Int,
|
||||
output: ItemStack,
|
||||
repairCost: Int,
|
||||
result: AnvilResult
|
||||
): Boolean {
|
||||
if (result.isEmpty()) return false
|
||||
|
||||
// To avoid vanilla, we cancel the event
|
||||
event.result = Event.Result.DENY
|
||||
event.isCancelled = true
|
||||
val cost = result.cost
|
||||
|
||||
// Assumed if player do not have enough xp then it returned MIN_VALUE
|
||||
if (repairCost == Int.MIN_VALUE) return false
|
||||
processCost(inventory, player, cost)
|
||||
if (!cost.valid && player.gameMode != GameMode.CREATIVE) return false
|
||||
|
||||
// Where should we get the item
|
||||
val slotDestination = getActionSlot(event, player)
|
||||
|
|
@ -260,6 +308,8 @@ class AnvilResultListener : Listener {
|
|||
|
||||
// If not creative middle click...
|
||||
if (event.click != ClickType.MIDDLE) {
|
||||
if (!tryRemoveCost(player, cost)) return false
|
||||
|
||||
// We remove what should be removed
|
||||
if (leftItem != null) leftItem.amount -= leftRemoveCount
|
||||
inventory.setItem(ANVIL_INPUT_LEFT, leftItem)
|
||||
|
|
@ -268,99 +318,58 @@ class AnvilResultListener : Listener {
|
|||
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)
|
||||
player.setItemOnCursor(result.item)
|
||||
} else {// We assume SlotType == SlotType.INVENTORY
|
||||
player.inventory.setItem(slotDestination.slot, output)
|
||||
player.inventory.setItem(slotDestination.slot, result.item)
|
||||
}
|
||||
|
||||
// TODO probably anvil damage & sound here ??
|
||||
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
|
||||
)
|
||||
|
||||
// Get repair cost
|
||||
val repairCost = getUnitRepairCost(inventory, player, leftItem, output, resultCopy, resultAmount)
|
||||
|
||||
// And then we give the item manually
|
||||
extractAnvilResult(
|
||||
event, player, inventory,
|
||||
null, 0,
|
||||
rightItem, resultAmount,
|
||||
resultCopy, repairCost
|
||||
)
|
||||
}
|
||||
|
||||
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, AnvilUseType.UNIT_REPAIR)
|
||||
repairCost += resultAmount * ConfigOptions.unitRepairCost
|
||||
private fun processCost(inventory: AnvilInventory, player: Player, cost: AnvilCost) {
|
||||
var sum = cost.repair
|
||||
|
||||
if (
|
||||
!ConfigOptions.doRemoveCostLimit &&
|
||||
ConfigOptions.doCapCost
|
||||
) {
|
||||
repairCost = min(repairCost, ConfigOptions.maxAnvilCost)
|
||||
val final = min(sum, ConfigOptions.maxAnvilCost)
|
||||
cost.generic += (final - sum)
|
||||
|
||||
sum = final
|
||||
}
|
||||
|
||||
if ((inventory.maximumRepairCost <= repairCost)
|
||||
|| (player.level < repairCost)
|
||||
) return Int.MIN_VALUE
|
||||
|
||||
return repairCost
|
||||
if (ConfigOptions.shouldUseMoney(player)) {
|
||||
cost.isMonetary = true
|
||||
if (!EconomyManager.economy!!.has(player, cost.asMonetaryCost()))
|
||||
cost.valid = false
|
||||
} else {
|
||||
if ((inventory.maximumRepairCost <= sum)
|
||||
|| (player.level < sum)
|
||||
) cost.valid = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFromLoreEditXpCost(
|
||||
xpCost: AtomicInteger,
|
||||
private fun onUnitRepairExtract(
|
||||
rightItem: ItemStack,
|
||||
event: InventoryClickEvent,
|
||||
player: Player,
|
||||
inventory: AnvilInventory,
|
||||
): Int {
|
||||
if (GameMode.CREATIVE == player.gameMode) return 0
|
||||
|
||||
val repairCost = xpCost.get()
|
||||
return if ((inventory.maximumRepairCost <= repairCost)
|
||||
|| (player.level < repairCost)
|
||||
) Int.MIN_VALUE
|
||||
else repairCost
|
||||
result: UnitRepairResult,
|
||||
) {
|
||||
// We give the item manually
|
||||
extractAnvilResult(
|
||||
event, player, inventory,
|
||||
null, 0,
|
||||
rightItem, result.repairAmount,
|
||||
result
|
||||
)
|
||||
}
|
||||
|
||||
private fun handleBookLoreEdit(
|
||||
|
|
@ -369,70 +378,84 @@ class AnvilResultListener : Listener {
|
|||
player: Player,
|
||||
leftItem: ItemStack,
|
||||
rightItem: ItemStack,
|
||||
output: ItemStack,
|
||||
): Boolean {
|
||||
if (Material.WRITABLE_BOOK != rightItem.type) return false
|
||||
val bookMeta = rightItem.itemMeta as BookMeta? ?: return false
|
||||
result: LoreEditResult
|
||||
) {
|
||||
if (result.type.isAppend)
|
||||
handleBookLoreAppend(event, inventory, player, rightItem, result)
|
||||
else
|
||||
handleBookLoreRemove(event, inventory, player, leftItem, rightItem, result)
|
||||
}
|
||||
|
||||
val editType = AnvilLoreEditUtil.bookLoreEditIsAppend(leftItem, rightItem) ?: return false
|
||||
private fun handleBookLoreAppend(
|
||||
event: InventoryClickEvent,
|
||||
inventory: AnvilInventory,
|
||||
player: Player,
|
||||
rightItem: ItemStack,
|
||||
result: LoreEditResult
|
||||
) {
|
||||
val bookMeta = rightItem.itemMeta as BookMeta? ?: return
|
||||
|
||||
val xpCost = AtomicInteger()
|
||||
if (editType) {
|
||||
if (output != AnvilLoreEditUtil.handleLoreAppendByBook(player, leftItem, bookMeta, xpCost)) return false
|
||||
|
||||
// Remove pages to book
|
||||
val clearedBook: ItemStack?
|
||||
if (LoreEditType.APPEND_BOOK.doConsume) {
|
||||
clearedBook = null
|
||||
} else {
|
||||
clearedBook = rightItem.clone()
|
||||
bookMeta.pages = Collections.emptyList()
|
||||
clearedBook.itemMeta = bookMeta
|
||||
}
|
||||
|
||||
return extractAnvilResult(
|
||||
event, player, inventory,
|
||||
null, 0,
|
||||
clearedBook, 0,
|
||||
output, getFromLoreEditXpCost(xpCost, player, inventory)
|
||||
)
|
||||
// Remove pages to book
|
||||
val clearedBook: ItemStack?
|
||||
if (LoreEditType.APPEND_BOOK.doConsume) {
|
||||
clearedBook = null
|
||||
} else {
|
||||
if (output != AnvilLoreEditUtil.handleLoreRemoveByBook(player, leftItem, xpCost)) return false
|
||||
clearedBook = rightItem.clone()
|
||||
bookMeta.pages = Collections.emptyList()
|
||||
clearedBook.itemMeta = bookMeta
|
||||
}
|
||||
|
||||
// fill book meta
|
||||
val lore = DependencyManager.stripLore(leftItem)
|
||||
if (lore.isEmpty()) return false
|
||||
extractAnvilResult(
|
||||
event, player, inventory,
|
||||
null, 0,
|
||||
clearedBook, 0,
|
||||
result
|
||||
)
|
||||
}
|
||||
|
||||
val rightCopy: ItemStack?
|
||||
if (LoreEditType.REMOVE_BOOK.doConsume) {
|
||||
rightCopy = null
|
||||
} else {
|
||||
// Uncolor the page
|
||||
AnvilLoreEditUtil.uncolorLines(player, lore, LoreEditType.REMOVE_BOOK)
|
||||
private fun handleBookLoreRemove(
|
||||
event: InventoryClickEvent,
|
||||
inventory: AnvilInventory,
|
||||
player: Player,
|
||||
leftItem: ItemStack,
|
||||
rightItem: ItemStack,
|
||||
result: LoreEditResult
|
||||
) {
|
||||
val bookMeta = rightItem.itemMeta as BookMeta? ?: return
|
||||
|
||||
val bookPage = StringBuilder()
|
||||
lore.forEach {
|
||||
if (bookPage.isNotEmpty()) bookPage.append('\n')
|
||||
if(it == null) return@forEach
|
||||
// fill book meta
|
||||
val lore = DependencyManager.stripLore(leftItem)
|
||||
if (lore.isEmpty()) return
|
||||
|
||||
bookPage.append(MiniMessageUtil.plain_text_mm.serialize(it))
|
||||
}
|
||||
val rightCopy: ItemStack?
|
||||
if (LoreEditType.REMOVE_BOOK.doConsume) {
|
||||
rightCopy = null
|
||||
} else {
|
||||
// Uncolor the page
|
||||
AnvilLoreEditUtil.uncolorLines(player, lore, LoreEditType.REMOVE_BOOK)
|
||||
|
||||
val resultPage = bookPage.toString()
|
||||
//TODO maybe check page size ? bc it may be too big ???
|
||||
val bookPage = StringBuilder()
|
||||
lore.forEach {
|
||||
if (bookPage.isNotEmpty()) bookPage.append('\n')
|
||||
if (it == null) return@forEach
|
||||
|
||||
rightCopy = rightItem.clone()
|
||||
bookMeta.setPages(resultPage)
|
||||
rightCopy.itemMeta = bookMeta
|
||||
bookPage.append(MiniMessageUtil.plain_text_mm.serialize(it))
|
||||
}
|
||||
|
||||
return extractAnvilResult(
|
||||
event, player, inventory,
|
||||
null, 0,
|
||||
rightCopy, 0,
|
||||
output, getFromLoreEditXpCost(xpCost, player, inventory)
|
||||
)
|
||||
val resultPage = bookPage.toString()
|
||||
//TODO maybe check page size ? bc it may be too big ???
|
||||
|
||||
rightCopy = rightItem.clone()
|
||||
bookMeta.setPages(resultPage)
|
||||
rightCopy.itemMeta = bookMeta
|
||||
}
|
||||
|
||||
extractAnvilResult(
|
||||
event, player, inventory,
|
||||
null, 0,
|
||||
rightCopy, 0,
|
||||
result
|
||||
)
|
||||
}
|
||||
|
||||
private fun handlePaperLoreEdit(
|
||||
|
|
@ -441,89 +464,106 @@ class AnvilResultListener : Listener {
|
|||
player: Player,
|
||||
leftItem: ItemStack,
|
||||
rightItem: ItemStack,
|
||||
output: ItemStack,
|
||||
): Boolean {
|
||||
if (Material.PAPER != rightItem.type) return false
|
||||
val paperMeta = rightItem.itemMeta ?: return false
|
||||
result: LoreEditResult
|
||||
) {
|
||||
if (result.type.isAppend)
|
||||
handlePaperLoreAppend(event, inventory, player, rightItem, result)
|
||||
else
|
||||
handlePaperLoreRemove(event, inventory, player, leftItem, rightItem, result)
|
||||
}
|
||||
|
||||
val editTypeIsAppend = AnvilLoreEditUtil.paperLoreEditIsAppend(leftItem, rightItem) ?: return false
|
||||
private fun handlePaperLoreAppend(
|
||||
event: InventoryClickEvent,
|
||||
inventory: AnvilInventory,
|
||||
player: Player,
|
||||
rightItem: ItemStack,
|
||||
result: LoreEditResult
|
||||
) {
|
||||
val paperMeta = rightItem.itemMeta ?: return
|
||||
|
||||
val xpCost = AtomicInteger()
|
||||
if (editTypeIsAppend) {
|
||||
if (output != AnvilLoreEditUtil.handleLoreAppendByPaper(player, leftItem, rightItem, xpCost)) return false
|
||||
|
||||
val paperCopy: ItemStack?
|
||||
if (LoreEditType.APPEND_PAPER.doConsume) {
|
||||
paperCopy = null
|
||||
} else {
|
||||
// Remove custom name to paper
|
||||
paperCopy = rightItem.clone()
|
||||
paperCopy.amount = 1
|
||||
paperMeta.setComponentDisplayName(null)
|
||||
paperCopy.itemMeta = paperMeta
|
||||
}
|
||||
|
||||
return if (rightItem.amount > 1) {
|
||||
extractAnvilResult(
|
||||
event, player, inventory,
|
||||
paperCopy, 0,
|
||||
rightItem, 1,
|
||||
output, getFromLoreEditXpCost(xpCost, player, inventory)
|
||||
)
|
||||
} else {
|
||||
extractAnvilResult(
|
||||
event, player, inventory,
|
||||
null, 0,
|
||||
paperCopy, 0,
|
||||
output, getFromLoreEditXpCost(xpCost, player, inventory)
|
||||
)
|
||||
}
|
||||
val paperCopy: ItemStack?
|
||||
if (LoreEditType.APPEND_PAPER.doConsume) {
|
||||
paperCopy = null
|
||||
} else {
|
||||
if (output != AnvilLoreEditUtil.handleLoreRemoveByPaper(player, leftItem, xpCost)) return false
|
||||
// Remove custom name to paper
|
||||
paperCopy = rightItem.clone()
|
||||
paperCopy.amount = 1
|
||||
paperMeta.setComponentDisplayName(null)
|
||||
|
||||
val leftMeta = leftItem.itemMeta
|
||||
if (leftMeta == null || !leftMeta.hasLore()) return false
|
||||
val lore = DependencyManager.stripLore(leftItem)
|
||||
if (lore.isEmpty()) return false
|
||||
// Remove pcd name
|
||||
AnvilMergeLogic.processPCD(paperMeta, player, null)
|
||||
|
||||
// Create result item
|
||||
val rightClone: ItemStack?
|
||||
if (LoreEditType.REMOVE_PAPER.doConsume) {
|
||||
rightClone = null
|
||||
} else {
|
||||
val removeEnd = LoreEditConfigUtil.paperLoreOrderIsEnd
|
||||
val line = if (removeEnd) lore[lore.size - 1]
|
||||
else lore[0]
|
||||
|
||||
// uncolor the line
|
||||
val ref = AtomicReference(line)
|
||||
AnvilLoreEditUtil.uncolorLine(player, ref, LoreEditType.REMOVE_PAPER)
|
||||
|
||||
rightClone = rightItem.clone()
|
||||
rightClone.amount = 1
|
||||
|
||||
val resultMeta = rightClone.itemMeta ?: return false
|
||||
resultMeta.setComponentDisplayName(ref.get())
|
||||
rightClone.itemMeta = resultMeta
|
||||
}
|
||||
|
||||
return if (rightItem.amount > 1) {
|
||||
extractAnvilResult(
|
||||
event, player, inventory,
|
||||
rightClone, 0,
|
||||
rightItem, 1,
|
||||
output, getFromLoreEditXpCost(xpCost, player, inventory)
|
||||
)
|
||||
} else {
|
||||
extractAnvilResult(
|
||||
event, player, inventory,
|
||||
null, 0,
|
||||
rightClone, 0,
|
||||
output, getFromLoreEditXpCost(xpCost, player, inventory)
|
||||
)
|
||||
}
|
||||
paperCopy.itemMeta = paperMeta
|
||||
}
|
||||
|
||||
if (rightItem.amount > 1) {
|
||||
extractAnvilResult(
|
||||
event, player, inventory,
|
||||
paperCopy, 0,
|
||||
rightItem, 1,
|
||||
result
|
||||
)
|
||||
} else {
|
||||
extractAnvilResult(
|
||||
event, player, inventory,
|
||||
null, 0,
|
||||
paperCopy, 0,
|
||||
result
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handlePaperLoreRemove(
|
||||
event: InventoryClickEvent,
|
||||
inventory: AnvilInventory,
|
||||
player: Player,
|
||||
leftItem: ItemStack,
|
||||
rightItem: ItemStack,
|
||||
result: LoreEditResult
|
||||
) {
|
||||
val leftMeta = leftItem.itemMeta
|
||||
if (leftMeta == null || !leftMeta.hasLore()) return
|
||||
|
||||
val lore = DependencyManager.stripLore(leftItem)
|
||||
if (lore.isEmpty()) return
|
||||
|
||||
// Create result item
|
||||
val rightClone: ItemStack?
|
||||
if (LoreEditType.REMOVE_PAPER.doConsume) {
|
||||
rightClone = null
|
||||
} else {
|
||||
val removeEnd = LoreEditConfigUtil.paperLoreOrderIsEnd
|
||||
val line = if (removeEnd) lore[lore.size - 1]
|
||||
else lore[0]
|
||||
|
||||
// uncolor the line
|
||||
val ref = AtomicReference(line)
|
||||
AnvilLoreEditUtil.uncolorLine(player, ref, LoreEditType.REMOVE_PAPER)
|
||||
|
||||
rightClone = rightItem.clone()
|
||||
rightClone.amount = 1
|
||||
|
||||
val resultMeta = rightClone.itemMeta ?: return
|
||||
resultMeta.setComponentDisplayName(ref.get())
|
||||
rightClone.itemMeta = resultMeta
|
||||
}
|
||||
|
||||
if (rightItem.amount > 1) {
|
||||
extractAnvilResult(
|
||||
event, player, inventory,
|
||||
rightClone, 0,
|
||||
rightItem, 1,
|
||||
result
|
||||
)
|
||||
} else {
|
||||
extractAnvilResult(
|
||||
event, player, inventory,
|
||||
null, 0,
|
||||
rightClone, 0,
|
||||
result
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -3,33 +3,29 @@ package xyz.alexcrea.cuanvil.listener
|
|||
import com.github.stefvanschie.inventoryframework.util.InventoryViewUtil
|
||||
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
|
||||
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.AnvilCost
|
||||
import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.AnvilResult
|
||||
import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.doMerge
|
||||
import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.doRenaming
|
||||
import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.testCustomRecipe
|
||||
import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.testLoreEdit
|
||||
import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.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.MaterialUtil.isAir
|
||||
import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair
|
||||
import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil
|
||||
import xyz.alexcrea.cuanvil.util.dialog.AnvilRenameDialogUtil
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
/**
|
||||
* Listener for anvil events
|
||||
|
|
@ -44,8 +40,6 @@ class PrepareAnvilListener : Listener {
|
|||
const val ANVIL_OUTPUT_SLOT = 2
|
||||
|
||||
var IS_EMPTY_TEST = false
|
||||
|
||||
private const val RENAME_DIALOG_PERMISSION = "ca.rename.dialog"
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -53,23 +47,27 @@ class PrepareAnvilListener : Listener {
|
|||
*/
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
fun anvilCombineCheck(event: PrepareAnvilEvent) {
|
||||
// Should find player
|
||||
val player: HumanEntity = InventoryViewUtil.getInstance().getPlayer(event.view)
|
||||
val view = event.view
|
||||
val inventory = event.inventory
|
||||
|
||||
val player = InventoryViewUtil.getInstance().getPlayer(view)
|
||||
if(player !is Player) return
|
||||
|
||||
tryRenameDialog(player, event)
|
||||
|
||||
// Test if custom anvil is bypassed before immutability test
|
||||
if (DependencyManager.earlyTryEventPreAnvilBypass(event, player)) {
|
||||
// even if we got bypassed we still want to set price
|
||||
AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, event.inventory.repairCost)
|
||||
AnvilXpUtil.setAnvilInvCost(inventory, view, player, AnvilCost(event.inventory.repairCost))
|
||||
return
|
||||
}
|
||||
|
||||
val first = inventory.getItem(ANVIL_INPUT_LEFT) ?: return
|
||||
val first = inventory.getItem(ANVIL_INPUT_LEFT)
|
||||
val second = inventory.getItem(ANVIL_INPUT_RIGHT)
|
||||
|
||||
if(IS_EMPTY_TEST) {
|
||||
event.result = null
|
||||
IS_EMPTY_TEST = false
|
||||
applyResult(event, player, AnvilResult.EMPTY)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -81,79 +79,69 @@ class PrepareAnvilListener : Listener {
|
|||
if (isImmutable(first) || isImmutable(second)) {
|
||||
CustomAnvil.verboseLog("Skipping anvil process as one of the two item is immutable")
|
||||
|
||||
event.result = null
|
||||
applyResult(event, player, AnvilResult.EMPTY)
|
||||
return
|
||||
}
|
||||
|
||||
tryRenameDialog(player, event)
|
||||
|
||||
// Test if the event should bypass custom anvil.
|
||||
if (DependencyManager.tryEventPreAnvilBypass(event, player)) {
|
||||
// even if we got bypassed we still want to set price
|
||||
AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, event.inventory.repairCost)
|
||||
AnvilXpUtil.setAnvilInvCost(inventory, view, player, AnvilCost(event.inventory.repairCost))
|
||||
return
|
||||
}
|
||||
|
||||
if (!player.hasPermission(CustomAnvil.affectedByPluginPermission)) return
|
||||
|
||||
val result = getResult(view, inventory, player, first, second)
|
||||
applyResult(event, player, result)
|
||||
}
|
||||
|
||||
fun getResult(
|
||||
view: InventoryView, //TODO use anvil view
|
||||
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: AnvilResult = testCustomRecipe(view, inventory, player, first, second)
|
||||
if (!result.isEmpty())
|
||||
return result
|
||||
|
||||
// Test rename lonely item
|
||||
val isAir = second.isAir
|
||||
CustomAnvil.verboseLog("checking air in main logic: $isAir")
|
||||
if (isAir) {
|
||||
doRenaming(event, inventory, player, first)
|
||||
return
|
||||
}
|
||||
val shouldTryRename = second.isAir
|
||||
CustomAnvil.verboseLog("checking air in main logic: $shouldTryRename")
|
||||
if (shouldTryRename)
|
||||
return doRenaming(view, inventory, player, first)
|
||||
|
||||
// Test for merge
|
||||
if (first.canMergeWith(second!!)) {
|
||||
doMerge(event, inventory, player, first, second)
|
||||
return
|
||||
}
|
||||
if (first.canMergeWith(second!!))
|
||||
return doMerge(view, inventory, player, first, second)
|
||||
|
||||
// Test for unit repair
|
||||
if (testUnitRepair(event, inventory, player, first, second)) return
|
||||
result = testUnitRepair(view, inventory, player, first, second)
|
||||
if (!result.isEmpty())
|
||||
return result
|
||||
|
||||
// Test for lore edit
|
||||
if (testLoreEdit(event, inventory, player, first, second)) return
|
||||
|
||||
CustomAnvil.log("no anvil fuse type found")
|
||||
event.result = null
|
||||
result = testLoreEdit(player, first, second)
|
||||
if (!result.isEmpty())
|
||||
return result
|
||||
|
||||
return AnvilResult.EMPTY
|
||||
}
|
||||
|
||||
private fun tryRenameDialog(
|
||||
player: HumanEntity,
|
||||
event: PrepareAnvilEvent
|
||||
) {
|
||||
if(!canUseRenameDialog(player)) return
|
||||
if(!ConfigOptions.canUseDialogRename(player)) return
|
||||
|
||||
AnvilRenameDialogUtil.anvilRenameDialog.tryShowDialog(player, event)
|
||||
}
|
||||
|
||||
private fun canUseRenameDialog(player: HumanEntity): Boolean {
|
||||
if(!ConfigOptions.doRenameDialog || !AnvilRenameDialogUtil.anvilRenameDialog.canSendDialog()) return false
|
||||
if(ConfigOptions.doRenameDialogUsePermission && !player.hasPermission(RENAME_DIALOG_PERMISSION)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun processDialogPCD(it: ItemMeta, player: HumanEntity) {
|
||||
val keepDialog = canUseRenameDialog(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
|
||||
|
||||
|
|
@ -180,221 +168,14 @@ class PrepareAnvilListener : Listener {
|
|||
return false
|
||||
}
|
||||
|
||||
// return true if a custom recipe exist with these ingredients
|
||||
private fun testCustomRecipe(
|
||||
event: PrepareAnvilEvent, inventory: AnvilInventory,
|
||||
player: HumanEntity,
|
||||
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 levelCost =
|
||||
if (recipe.removeExactLinearXp) AnvilXpUtil.calculateMinimumLevelForXp(xpCost)
|
||||
else AnvilXpUtil.calculateLevelForXp(xpCost)
|
||||
|
||||
val finalResult = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.CUSTOM_CRAFT, levelCost)
|
||||
if (finalResult == null) return false
|
||||
|
||||
event.result = finalResult.result
|
||||
if (finalResult.result.isAir) return false
|
||||
|
||||
AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, finalResult.levelCost, true)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun doRenaming(
|
||||
event: PrepareAnvilEvent, inventory: AnvilInventory,
|
||||
player: HumanEntity, first: ItemStack
|
||||
) {
|
||||
val resultItem = DependencyManager.cloneItem(event, first)
|
||||
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
|
||||
if(result.item == null) {
|
||||
AnvilXpUtil.onNoResult(player, event.view)
|
||||
return
|
||||
}
|
||||
|
||||
anvilCost += AnvilXpUtil.calculatePenalty(first, null, resultItem, AnvilUseType.RENAME_ONLY)
|
||||
|
||||
val finalResult = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.RENAME_ONLY, anvilCost)
|
||||
if (finalResult == null) return
|
||||
|
||||
event.result = finalResult.result
|
||||
if (finalResult.result.isAir) return
|
||||
|
||||
AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, finalResult.levelCost)
|
||||
AnvilXpUtil.setAnvilInvCost(event.inventory, event.view, player, result.cost, result.ignoreXpRules)
|
||||
}
|
||||
|
||||
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: HumanEntity,
|
||||
first: ItemStack, second: ItemStack
|
||||
) {
|
||||
val newEnchants = first.findEnchantments()
|
||||
.combineWith(second.findEnchantments(), first, player)
|
||||
var hasChanged = !isIdentical(first.findEnchantments(), newEnchants)
|
||||
|
||||
val resultItem = DependencyManager.cloneItem(event, first)
|
||||
var anvilCost = 0
|
||||
if(hasChanged){
|
||||
resultItem.setEnchantmentsUnsafe(newEnchants)
|
||||
// Calculate enchantment cost
|
||||
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
|
||||
hasChanged = hasChanged || repaired
|
||||
}
|
||||
|
||||
// Test/stop if nothing changed.
|
||||
if (!hasChanged) {
|
||||
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, AnvilUseType.MERGE)
|
||||
// Calculate rename cost
|
||||
anvilCost += handleRename(resultItem, inventory, player)
|
||||
|
||||
// Finally, we set result
|
||||
val finalResult = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.MERGE, anvilCost)
|
||||
if (finalResult == null) return
|
||||
|
||||
event.result = finalResult.result
|
||||
if (finalResult.result.isAir) return
|
||||
|
||||
AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, finalResult.levelCost)
|
||||
}
|
||||
|
||||
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: HumanEntity,
|
||||
first: ItemStack, second: ItemStack
|
||||
): Boolean {
|
||||
val unitRepairAmount = first.getRepair(second) ?: return false
|
||||
|
||||
val resultItem = DependencyManager.cloneItem(event, first)
|
||||
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, 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
|
||||
}
|
||||
|
||||
val finalResult = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.UNIT_REPAIR, anvilCost)
|
||||
if (finalResult == null) return false
|
||||
|
||||
event.result = finalResult.result
|
||||
if (finalResult.result.isAir) return false
|
||||
|
||||
AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, finalResult.levelCost)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun testLoreEdit(
|
||||
event: PrepareAnvilEvent, inventory: AnvilInventory, player: HumanEntity,
|
||||
first: ItemStack, second: ItemStack
|
||||
): Boolean {
|
||||
val type = second.type
|
||||
var result: ItemStack? = null
|
||||
|
||||
val xpCost = AtomicInteger()
|
||||
if (Material.WRITABLE_BOOK == type) {
|
||||
result = AnvilLoreEditUtil.tryLoreEditByBook(player, first, second, xpCost)
|
||||
} else if (Material.PAPER == type) {
|
||||
result = AnvilLoreEditUtil.tryLoreEditByPaper(player, first, second, xpCost)
|
||||
}
|
||||
|
||||
if (result.isAir || first == result) {
|
||||
CustomAnvil.log("lore edit, But input is same as output")
|
||||
event.result = null
|
||||
return false
|
||||
}
|
||||
|
||||
event.result = result
|
||||
AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, xpCost.get())
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -1,17 +1,19 @@
|
|||
package xyz.alexcrea.cuanvil.util
|
||||
package xyz.alexcrea.cuanvil.util.anvil
|
||||
|
||||
import net.kyori.adventure.text.Component
|
||||
import org.bukkit.entity.HumanEntity
|
||||
import org.bukkit.inventory.ItemStack
|
||||
import org.bukkit.inventory.meta.BookMeta
|
||||
import org.bukkit.permissions.Permissible
|
||||
import xyz.alexcrea.cuanvil.anvil.AnvilCost
|
||||
import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.LoreEditResult
|
||||
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.MiniMessageUtil
|
||||
import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil
|
||||
import xyz.alexcrea.cuanvil.util.config.LoreEditType
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
object AnvilLoreEditUtil {
|
||||
|
|
@ -31,7 +33,7 @@ object AnvilLoreEditUtil {
|
|||
player: Permissible,
|
||||
first: ItemStack,
|
||||
book: BookMeta,
|
||||
xpCost: AtomicInteger
|
||||
cost: AnvilCost
|
||||
): ItemStack? {
|
||||
if (!hasLoreEditByBookPermission(player)) return null
|
||||
|
||||
|
|
@ -42,8 +44,10 @@ object AnvilLoreEditUtil {
|
|||
val page = book.pages[0]
|
||||
val lines = ArrayList<String>(page.split("\n"))
|
||||
val outLines = ArrayList<Component>(lines.size)
|
||||
val colorCost = colorLines(player, LoreEditType.APPEND_BOOK,
|
||||
lines, outLines)
|
||||
val colorCost = colorLines(
|
||||
player, LoreEditType.APPEND_BOOK,
|
||||
lines, outLines
|
||||
)
|
||||
|
||||
lore.addAll(outLines)
|
||||
|
||||
|
|
@ -53,14 +57,14 @@ object AnvilLoreEditUtil {
|
|||
if (result == first) return null
|
||||
|
||||
// Handle xp
|
||||
xpCost.addAndGet(colorCost) // Cost of using color
|
||||
xpCost.addAndGet(outLines.size * LoreEditType.APPEND_BOOK.perLineCost) // per line cost
|
||||
xpCost.addAndGet(baseEditLoreXpCost(first, result, LoreEditType.APPEND_BOOK)) // Fixed cost and work penalty
|
||||
cost.lore = colorCost // Cost of using color
|
||||
cost.lore += outLines.size * LoreEditType.APPEND_BOOK.perLineCost // per line cost
|
||||
baseEditLoreXpCost(cost, first, result, LoreEditType.APPEND_BOOK) // Fixed cost and work penalty
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
fun handleLoreRemoveByBook(player: Permissible, first: ItemStack, xpCost: AtomicInteger): ItemStack? {
|
||||
fun handleLoreRemoveByBook(player: Permissible, first: ItemStack, cost: AnvilCost): ItemStack? {
|
||||
if (!hasLoreEditByBookPermission(player)) return null
|
||||
|
||||
// remove lore
|
||||
|
|
@ -78,9 +82,9 @@ object AnvilLoreEditUtil {
|
|||
if (result == first) return null
|
||||
|
||||
// Handle xp
|
||||
xpCost.addAndGet(uncolorCost)
|
||||
xpCost.addAndGet(currentLore.size * LoreEditType.REMOVE_BOOK.perLineCost)
|
||||
xpCost.addAndGet(baseEditLoreXpCost(first, result, LoreEditType.REMOVE_BOOK))
|
||||
cost.lore = uncolorCost
|
||||
cost.lore += currentLore.size * LoreEditType.REMOVE_BOOK.perLineCost
|
||||
baseEditLoreXpCost(cost, first, result, LoreEditType.REMOVE_BOOK)
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
@ -116,12 +120,17 @@ object AnvilLoreEditUtil {
|
|||
return null
|
||||
}
|
||||
|
||||
fun tryLoreEditByBook(player: HumanEntity, first: ItemStack, second: ItemStack, xpCost: AtomicInteger): ItemStack? {
|
||||
val isAppend = bookLoreEditIsAppend(first, second) ?: return null
|
||||
fun tryLoreEditByBook(player: HumanEntity, first: ItemStack, second: ItemStack): LoreEditResult {
|
||||
val isAppend = bookLoreEditIsAppend(first, second) ?: return LoreEditResult.EMPTY
|
||||
val type = if (isAppend) LoreEditType.APPEND_BOOK else LoreEditType.REMOVE_BOOK
|
||||
|
||||
val meta = second.itemMeta as BookMeta
|
||||
return if (isAppend) handleLoreAppendByBook(player, first, meta, xpCost)
|
||||
else handleLoreRemoveByBook(player, first, xpCost)
|
||||
val cost = AnvilCost()
|
||||
val item = if (isAppend)
|
||||
handleLoreAppendByBook(player, first, meta, cost)
|
||||
else handleLoreRemoveByBook(player, first, cost)
|
||||
|
||||
return LoreEditResult(item, cost, type)
|
||||
}
|
||||
|
||||
// Return true if appended, false if removed, null if neither
|
||||
|
|
@ -147,7 +156,7 @@ object AnvilLoreEditUtil {
|
|||
player: Permissible,
|
||||
first: ItemStack,
|
||||
second: ItemStack,
|
||||
xpCost: AtomicInteger
|
||||
cost: AnvilCost
|
||||
): ItemStack? {
|
||||
if (!hasLoreEditByPaperPermission(player)) return null
|
||||
|
||||
|
|
@ -159,9 +168,11 @@ object AnvilLoreEditUtil {
|
|||
|
||||
// A bit overdone to color 1 line but hey
|
||||
val outList = ArrayList<Component>(1)
|
||||
val colorCost = colorLines(player, LoreEditType.APPEND_PAPER,
|
||||
val colorCost = colorLines(
|
||||
player, LoreEditType.APPEND_PAPER,
|
||||
Collections.singletonList(second.itemMeta!!.displayName),
|
||||
outList)
|
||||
outList
|
||||
)
|
||||
|
||||
val line = outList[0]
|
||||
if (appendEnd)
|
||||
|
|
@ -175,13 +186,13 @@ object AnvilLoreEditUtil {
|
|||
if (result == first) return null
|
||||
|
||||
// Handle xp
|
||||
xpCost.addAndGet(colorCost)
|
||||
xpCost.addAndGet(baseEditLoreXpCost(first, result, LoreEditType.APPEND_PAPER))
|
||||
cost.lore = colorCost
|
||||
baseEditLoreXpCost(cost, first, result, LoreEditType.APPEND_PAPER)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
fun handleLoreRemoveByPaper(player: Permissible, first: ItemStack, xpCost: AtomicInteger): ItemStack? {
|
||||
fun handleLoreRemoveByPaper(player: Permissible, first: ItemStack, cost: AnvilCost): ItemStack? {
|
||||
if (!hasLoreEditByPaperPermission(player)) return null
|
||||
|
||||
// remove lore line
|
||||
|
|
@ -213,8 +224,8 @@ object AnvilLoreEditUtil {
|
|||
val uncolorCost = uncolorLine(player, line, LoreEditType.REMOVE_PAPER)
|
||||
|
||||
// Handle other xp
|
||||
xpCost.addAndGet(uncolorCost)
|
||||
xpCost.addAndGet(baseEditLoreXpCost(first, result, LoreEditType.REMOVE_PAPER))
|
||||
cost.lore = uncolorCost
|
||||
baseEditLoreXpCost(cost, first, result, LoreEditType.REMOVE_PAPER)
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
@ -222,33 +233,39 @@ object AnvilLoreEditUtil {
|
|||
fun tryLoreEditByPaper(
|
||||
player: HumanEntity,
|
||||
first: ItemStack,
|
||||
second: ItemStack,
|
||||
xpCost: AtomicInteger
|
||||
): ItemStack? {
|
||||
val isAppend = paperLoreEditIsAppend(first, second) ?: return null
|
||||
second: ItemStack
|
||||
): LoreEditResult {
|
||||
val isAppend = paperLoreEditIsAppend(first, second) ?: return LoreEditResult.EMPTY
|
||||
val type = if (isAppend) LoreEditType.APPEND_BOOK else LoreEditType.REMOVE_BOOK
|
||||
|
||||
return if (isAppend) handleLoreAppendByPaper(player, first, second, xpCost)
|
||||
else handleLoreRemoveByPaper(player, first, xpCost)
|
||||
val cost = AnvilCost()
|
||||
val item = if (isAppend)
|
||||
handleLoreAppendByPaper(player, first, second, cost)
|
||||
else handleLoreRemoveByPaper(player, first, cost)
|
||||
|
||||
return LoreEditResult(item, cost, type)
|
||||
}
|
||||
|
||||
private fun baseEditLoreXpCost(
|
||||
cost: AnvilCost,
|
||||
first: ItemStack,
|
||||
result: ItemStack,
|
||||
editType: LoreEditType
|
||||
): Int {
|
||||
var xpCost = editType.fixedCost
|
||||
) {
|
||||
cost.lore += editType.fixedCost
|
||||
|
||||
xpCost += AnvilXpUtil.calculatePenalty(first, null, result, editType.useType)
|
||||
return xpCost
|
||||
cost.workPenalty = AnvilXpUtil.calculatePenalty(first, null, result, editType.useType)
|
||||
}
|
||||
|
||||
fun colorPermission(player: Permissible, editType: LoreEditType): AnvilColorUtil.ColorPermissions {
|
||||
return AnvilColorUtil.calculatePermissions(player,
|
||||
return AnvilColorUtil.calculatePermissions(
|
||||
player,
|
||||
false,
|
||||
editType.allowColorCode,
|
||||
editType.allowHexColor,
|
||||
editType.allowMinimessage,
|
||||
AnvilColorUtil.ColorUseType.LORE_EDIT)
|
||||
AnvilColorUtil.ColorUseType.LORE_EDIT
|
||||
)
|
||||
}
|
||||
|
||||
private fun colorLine(line: String, permission: AnvilColorUtil.ColorPermissions): Component? {
|
||||
|
|
@ -258,8 +275,10 @@ object AnvilLoreEditUtil {
|
|||
)
|
||||
}
|
||||
|
||||
private fun colorLines(player: Permissible, editType: LoreEditType,
|
||||
lines: List<String>, outLines: MutableList<Component>): Int {
|
||||
private fun colorLines(
|
||||
player: Permissible, editType: LoreEditType,
|
||||
lines: List<String>, outLines: MutableList<Component>
|
||||
): Int {
|
||||
val permission = colorPermission(player, editType)
|
||||
val colorCost = editType.useColorCost
|
||||
|
||||
|
|
@ -286,7 +305,7 @@ object AnvilLoreEditUtil {
|
|||
// Now handle color of each lines
|
||||
var hasUndidColor = false
|
||||
for ((index, line) in lines.withIndex()) {
|
||||
if(line == null){
|
||||
if (line == null) {
|
||||
lines[index] = null
|
||||
continue
|
||||
}
|
||||
|
|
@ -301,7 +320,7 @@ object AnvilLoreEditUtil {
|
|||
hasUndidColor = true
|
||||
result = clearedLine
|
||||
} else {
|
||||
result = MiniMessageUtil.plain_text_mm.serialize(line)
|
||||
result = MiniMessageUtil.plain_text_mm.serialize(line)
|
||||
}
|
||||
|
||||
lines[index] = MiniMessageUtil.plain_text_mm.deserialize(result)
|
||||
|
|
@ -330,7 +349,7 @@ object AnvilLoreEditUtil {
|
|||
|
||||
var hasUndidColor = false
|
||||
val result: String
|
||||
if(clearedLine != null){
|
||||
if (clearedLine != null) {
|
||||
hasUndidColor = true
|
||||
result = clearedLine
|
||||
} else {
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package xyz.alexcrea.cuanvil.util
|
||||
package xyz.alexcrea.cuanvil.util.anvil
|
||||
|
||||
import io.delilaheve.util.ConfigOptions
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package xyz.alexcrea.cuanvil.util
|
||||
package xyz.alexcrea.cuanvil.util.anvil
|
||||
|
||||
import io.delilaheve.CustomAnvil
|
||||
import io.delilaheve.util.ConfigOptions
|
||||
|
|
@ -14,25 +14,48 @@ 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.AnvilCost
|
||||
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 kotlin.math.min
|
||||
|
||||
object AnvilXpUtil {
|
||||
|
||||
const val EXCLUSIVE_PENALTY_PREFIX = "repair_cost"
|
||||
|
||||
/**
|
||||
* Display the required cost (either as xp or as )
|
||||
*/
|
||||
fun setAnvilInvCost(
|
||||
inventory: AnvilInventory,
|
||||
view: InventoryView,
|
||||
player: Player,
|
||||
cost: AnvilCost,
|
||||
ignoreRules: Boolean = false
|
||||
) {
|
||||
if (ConfigOptions.shouldUseMoney(player)) {
|
||||
cost.isMonetary = true
|
||||
setAnvilPrice(inventory, view, player, cost)
|
||||
} else
|
||||
setAnvilInvXp(inventory, view, player, cost.asXpCost(), ignoreRules)
|
||||
}
|
||||
|
||||
/**
|
||||
* Display xp needed for the work on the anvil inventory
|
||||
*/
|
||||
fun setAnvilInvXp(
|
||||
private fun setAnvilInvXp(
|
||||
inventory: AnvilInventory,
|
||||
view: InventoryView,
|
||||
player: HumanEntity,
|
||||
anvilCost: Int,
|
||||
ignoreRules: Boolean = false
|
||||
) {
|
||||
|
||||
// Test repair cost limit
|
||||
val finalAnvilCost = if (
|
||||
!ignoreRules &&
|
||||
|
|
@ -78,7 +101,51 @@ object AnvilXpUtil {
|
|||
}
|
||||
|
||||
player.updateInventory()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display monetary cost needed for the work on the anvil inventory
|
||||
*/
|
||||
private fun setAnvilPrice(
|
||||
inventory: AnvilInventory,
|
||||
view: InventoryView,
|
||||
player: Player,
|
||||
cost: AnvilCost,
|
||||
) {
|
||||
val finalCost = cost.asMonetaryCost()
|
||||
|
||||
val has = player.gameMode == GameMode.CREATIVE ||
|
||||
EconomyManager.economy!!.has(player, finalCost)
|
||||
|
||||
val text = "Cost: " + (if (has) "§2" else "§4") +
|
||||
EconomyManager.economy!!.format(finalCost)
|
||||
AnvilTitleUtil.rename(
|
||||
view, text,
|
||||
player,
|
||||
AnvilRenameDialogUtil.anvilRenameDialog,
|
||||
CustomAnvil.instance
|
||||
)
|
||||
|
||||
clearAnvilXpCost(inventory, view, player)
|
||||
}
|
||||
|
||||
private fun clearAnvilXpCost(
|
||||
inventory: AnvilInventory,
|
||||
view: InventoryView,
|
||||
player: HumanEntity,
|
||||
) {
|
||||
// TODO for 2.x.x use anvil view & set directly there
|
||||
inventory.repairCost = 0
|
||||
|
||||
// retry after a tick
|
||||
DependencyManager.scheduler.scheduleOnEntity(
|
||||
CustomAnvil.instance, player
|
||||
) {
|
||||
inventory.repairCost = 0
|
||||
|
||||
if (player !is Player) return@scheduleOnEntity
|
||||
player.updateInventory()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -128,6 +195,16 @@ object AnvilXpUtil {
|
|||
return resultSum
|
||||
}
|
||||
|
||||
fun onNoResult(player: HumanEntity, view: InventoryView) {
|
||||
if (ConfigOptions.shouldUseMoney(player))
|
||||
AnvilTitleUtil.rename(
|
||||
view, "Repair & Name",
|
||||
player,
|
||||
AnvilRenameDialogUtil.anvilRenameDialog,
|
||||
CustomAnvil.instance
|
||||
)
|
||||
}
|
||||
|
||||
private fun exclusivePenaltyKey(useType: AnvilUseType): NamespacedKey {
|
||||
return NamespacedKey(CustomAnvil.instance, "${EXCLUSIVE_PENALTY_PREFIX}_${useType.typeName}")
|
||||
}
|
||||
|
|
@ -159,10 +236,8 @@ object AnvilXpUtil {
|
|||
* Function to calculate right enchantment values
|
||||
* it include enchantment placed on final item and conflicting enchantment
|
||||
*/
|
||||
fun getRightValues(right: ItemStack, result: ItemStack): Int {
|
||||
fun getRightValues(right: ItemStack, result: ItemStack, cost: AnvilCost) {
|
||||
// Calculate right value and illegal enchant penalty
|
||||
var illegalPenalty = 0
|
||||
var rightValue = 0
|
||||
|
||||
val rightIsFormBook = right.isEnchantedBook()
|
||||
val resultEnchs = result.findEnchantments()
|
||||
|
|
@ -180,7 +255,7 @@ object AnvilXpUtil {
|
|||
resultEnchsKeys.remove(enchantment.key)
|
||||
|
||||
if (ConflictType.ENCHANTMENT_CONFLICT == conflictType) {
|
||||
illegalPenalty += ConfigOptions.sacrificeIllegalCost
|
||||
cost.illegalPenalty += ConfigOptions.sacrificeIllegalCost
|
||||
CustomAnvil.verboseLog("Big conflict. Adding illegal price penalty")
|
||||
}
|
||||
continue
|
||||
|
|
@ -191,16 +266,14 @@ object AnvilXpUtil {
|
|||
val enchantmentMultiplier = ConfigOptions.enchantmentValue(enchantment.key, rightIsFormBook)
|
||||
val value = resultLevel * enchantmentMultiplier
|
||||
CustomAnvil.log("Value for ${enchantment.key.enchantmentName} level ${enchantment.value} is $value ($resultLevel * $enchantmentMultiplier)")
|
||||
rightValue += value
|
||||
cost.enchantment += value
|
||||
|
||||
}
|
||||
CustomAnvil.log(
|
||||
"Calculated right values: " +
|
||||
"rightValue: $rightValue, " +
|
||||
"illegalPenalty: $illegalPenalty"
|
||||
"rightValue: ${cost.enchantment}, " +
|
||||
"illegalPenalty: ${cost.illegalPenalty}"
|
||||
)
|
||||
|
||||
return rightValue + illegalPenalty
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -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
|
||||
|
|
@ -18,20 +18,23 @@ import xyz.alexcrea.cuanvil.config.ConfigHolder.DEFAULT_CONFIG as CONFIG
|
|||
enum class LoreEditType(
|
||||
val rootPath: String,
|
||||
val useType: AnvilUseType,
|
||||
val isBook: Boolean,
|
||||
val isAppend: Boolean,
|
||||
val isMultiLine: Boolean,
|
||||
) {
|
||||
APPEND_BOOK(AnvilUseType.LORE_EDIT_BOOK_APPEND, true, true),
|
||||
REMOVE_BOOK(AnvilUseType.LORE_EDIT_BOOK_REMOVE, false, true),
|
||||
APPEND_PAPER(AnvilUseType.LORE_EDIT_PAPER_APPEND, true, false),
|
||||
REMOVE_PAPER(AnvilUseType.LORE_EDIT_PAPER_REMOVE, false, false),
|
||||
APPEND_BOOK(AnvilUseType.LORE_EDIT_BOOK_APPEND, true, true, true),
|
||||
REMOVE_BOOK(AnvilUseType.LORE_EDIT_BOOK_REMOVE, true, false, true),
|
||||
APPEND_PAPER(AnvilUseType.LORE_EDIT_PAPER_APPEND, false, true, false),
|
||||
REMOVE_PAPER(AnvilUseType.LORE_EDIT_PAPER_REMOVE, false, false, false),
|
||||
;
|
||||
|
||||
constructor(
|
||||
useType: AnvilUseType,
|
||||
isPaper: Boolean,
|
||||
isAppend: Boolean,
|
||||
isMultiLine: Boolean,
|
||||
) : this(useType.path, useType, isAppend, isMultiLine)
|
||||
) : this(useType.path, useType,
|
||||
isPaper, isAppend, isMultiLine)
|
||||
|
||||
/**
|
||||
* If this edit type is enabled
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
||||
|
|
@ -47,5 +47,9 @@ object AnvilRenameDialogUtil {
|
|||
return null
|
||||
}
|
||||
|
||||
override fun isOpenFor(player: HumanEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -432,6 +432,34 @@ lore_edit:
|
|||
allow_hexadecimal_color: false
|
||||
allow_minimessage: true
|
||||
|
||||
# Allow to replace the xp cost by a monetary cost
|
||||
# If enabled it will not be bound to the experience level limits
|
||||
#
|
||||
# It also requires to enable dialog rename (set "enable_dialog_rename: false" a bit higher)
|
||||
# If dialog rename permission is enabled and player do not have the permission merge will fall back to vanilla xp cost
|
||||
#
|
||||
# If you are using custom craft I recommend using Linear Xp Cost with Exact Linear Xp as normal Xp Cost will act "weird"
|
||||
# But Linear Xp will act as 1$ time global multiplier. In other word: like you expect
|
||||
#
|
||||
# As this feature require dialog rename, it can only be enabled starting with paper 1.21.6 and later
|
||||
monetary_cost:
|
||||
enabled: false
|
||||
# If using vault unlocked this allow to specify what currency should be used for anvil usage
|
||||
# default being the default currency
|
||||
currency: default
|
||||
# multiply the anvil cost by a value to allow to have price a big bigger than like 40
|
||||
multipliers:
|
||||
# global multipliers. all usage type will be multiplied by this value
|
||||
global: 1.0
|
||||
# usage specific type. it will only apply for specific xp "reason"
|
||||
enchantment: 1.0 # related to enchantments level
|
||||
repair: 1.0 # for repairing via unit repair (per unit)
|
||||
rename: 1.0 # for renaming the item
|
||||
lore_edit: 1.0 # for changing the lore of the item (only if lore edit is enabled)
|
||||
illegal_penalty: 1.0 # for trying to combine illegal enchantment
|
||||
work_penalty: 1.0 # for work penalty (aka use penalty)
|
||||
recipe: 1.0 # for custom anvil recipe cost
|
||||
|
||||
# Whether to show debug logging
|
||||
debug_log: false
|
||||
|
||||
|
|
|
|||
|
|
@ -195,7 +195,7 @@ public class AnvilFuseTestUtil {
|
|||
|
||||
simulateClick(anvil, player, data.expectedResult());
|
||||
|
||||
// Should have similated the click
|
||||
// Should have simulated the click
|
||||
assertEqual(data.leftItem(), anvil.getFirstItem());
|
||||
assertEqual(data.rightItem(), anvil.getSecondItem());
|
||||
assertEqual(data.resultSlotItem(), anvil.getResult());
|
||||
|
|
@ -260,7 +260,7 @@ public class AnvilFuseTestUtil {
|
|||
}
|
||||
|
||||
public static boolean isAir(@Nullable ItemStack item) {
|
||||
return item == null || item.isEmpty();
|
||||
return item == null || item.isEmpty() || item.getAmount() == 0;
|
||||
}
|
||||
|
||||
public static void assertPriceEqual(Integer expectedPrice, int price) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue