diff --git a/src/main/java/net/knarcraft/blacksmith/config/scrapper/GlobalScrapperSettings.java b/src/main/java/net/knarcraft/blacksmith/config/scrapper/GlobalScrapperSettings.java index 27ebd9e..6bfb075 100644 --- a/src/main/java/net/knarcraft/blacksmith/config/scrapper/GlobalScrapperSettings.java +++ b/src/main/java/net/knarcraft/blacksmith/config/scrapper/GlobalScrapperSettings.java @@ -7,6 +7,7 @@ import net.knarcraft.blacksmith.util.ConfigHelper; import net.knarcraft.blacksmith.util.ItemHelper; import org.bukkit.Material; import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -212,6 +213,42 @@ public class GlobalScrapperSettings implements Settings { return asDouble(ScrapperSetting.NETHERITE_SALVAGE_COST); } + /** + * Gets the base cost of salvaging an enchanted book + * + * @return

The enchanted book salvage base cost

+ */ + public double getEnchantedBookSalvageCost() { + return asDouble(ScrapperSetting.ENCHANTED_BOOK_SALVAGE_BASE_COST); + } + + /** + * Gets the item cost of salvaging an enchanted book + * + * @return

The enchanted book salvage cost

+ */ + public ItemStack getEnchantedBookItemCost() { + return ScrapperSetting.ENCHANTED_BOOK_SALVAGE_ITEM_COST; + } + + /** + * Whether to multiply the cost of salvaging enchanted books based on the number of enchantments + * + * @return

Whether to multiply the cost of salvaging enchanted books based on the number of enchantments

+ */ + public boolean multiplyEnchantedBookSalvageCost() { + return asBoolean(ScrapperSetting.ENCHANTED_BOOK_SALVAGE_MULTIPLY); + } + + /** + * Whether to require both a monetary and item-based cost when salvaging enchanted books + * + * @return

Whether to require both a monetary and item-based cost when salvaging enchanted books

+ */ + public boolean requireMoneyAndItemForEnchantedBookSalvage() { + return asBoolean(ScrapperSetting.ENCHANTED_BOOK_SALVAGE_REQUIRE_BOTH); + } + /** * Gets trash salvage for the given material * diff --git a/src/main/java/net/knarcraft/blacksmith/config/scrapper/ScrapperNPCSettings.java b/src/main/java/net/knarcraft/blacksmith/config/scrapper/ScrapperNPCSettings.java index 0f435c2..af2541e 100644 --- a/src/main/java/net/knarcraft/blacksmith/config/scrapper/ScrapperNPCSettings.java +++ b/src/main/java/net/knarcraft/blacksmith/config/scrapper/ScrapperNPCSettings.java @@ -83,6 +83,15 @@ public class ScrapperNPCSettings implements TraitSettings { } } + /** + * Gets whether this scrapper is able to split an enchanted book + * + * @return

True if this scrapper is able to split an enchanted book

+ */ + public boolean splitEnchantedBook() { + return asBoolean(ScrapperSetting.SPLIT_ENCHANTED_BOOK); + } + /** * Gets the raw current value of a setting * @@ -171,6 +180,15 @@ public class ScrapperNPCSettings implements TraitSettings { return asString(ScrapperSetting.COST_MESSAGE_NETHERITE); } + /** + * Gets the message to use for displaying enchanted book salvage cost + * + * @return

The message to use for displaying enchanted book salvage cost

+ */ + public String getEnchantedBookCostMessage() { + return asString(ScrapperSetting.COST_MESSAGE_ENCHANTED_BOOK); + } + @Override @NotNull public String getCoolDownUnexpiredMessage() { @@ -466,4 +484,34 @@ public class ScrapperNPCSettings implements TraitSettings { return asString(ScrapperSetting.CANNOT_SALVAGE_NETHERITE_MESSAGE); } + /** + * Gets the message to display when explaining that this scrapper is unable to salvage enchanted books + * + * @return

The message to display when explaining that this scrapper is unable to salvage enchanted books

+ */ + @NotNull + public String getCannotSalvageEnchantedBookMessage() { + return asString(ScrapperSetting.CANNOT_SALVAGE_ENCHANTED_BOOK_MESSAGE); + } + + /** + * Gets the message to display when explaining that the scrapper needs to be provided more items for enchanted book salvage + * + * @return

The message to display when explaining that the scrapper needs to be provided more items for enchanted book salvage

+ */ + @NotNull + public String getNotEnoughItemsMessage() { + return asString(ScrapperSetting.NOT_ENOUGH_ITEMS_MESSAGE); + } + + /** + * Gets the message to display when explaining that the scrapper cannot salvage an enchanted book with a single enchantment + * + * @return

The message to display when explaining that the scrapper cannot salvage an enchanted book with a single enchantment

+ */ + @NotNull + public String getCannotSplitEnchantedBookFurtherMessage() { + return asString(ScrapperSetting.CANNOT_SPLIT_ENCHANTED_BOOK_FURTHER_MESSAGE); + } + } diff --git a/src/main/java/net/knarcraft/blacksmith/config/scrapper/ScrapperSetting.java b/src/main/java/net/knarcraft/blacksmith/config/scrapper/ScrapperSetting.java index d24a43f..dad439a 100644 --- a/src/main/java/net/knarcraft/blacksmith/config/scrapper/ScrapperSetting.java +++ b/src/main/java/net/knarcraft/blacksmith/config/scrapper/ScrapperSetting.java @@ -91,6 +91,12 @@ public enum ScrapperSetting implements Setting { */ SALVAGE_NETHERITE("salvageNetherite", SettingValueType.BOOLEAN, true, "Whether to enable salvaging of netherite items", true, false), + + /** + * The setting for whether the NPC should allow salvaging an enchanted books with several enchantments into several books with one enchantment + */ + SPLIT_ENCHANTED_BOOK("splitEnchantedBook", SettingValueType.BOOLEAN, false, "Whether to enable " + + "splitting of enchanted books", true, false), /*----------- | Messages | @@ -194,6 +200,13 @@ public enum ScrapperSetting implements Setting { "&eIt will cost &a{cost}&e to salvage that &a{item}&e into diamond!", "The message to display when explaining the shown item's netherite salvage cost", true, true), + /** + * The message displayed when displaying the cost of salvaging the player's held enchanted book + */ + COST_MESSAGE_ENCHANTED_BOOK("costMessageEnchantedBook", SettingValueType.STRING, + "&eIt will cost &a{cost}&e to salvage that enchanted book!", + "The message to display when explaining the shown enchanted book's salvage cost", true, true), + /** * The message displayed when explaining that all items will be returned as salvage */ @@ -236,6 +249,27 @@ public enum ScrapperSetting implements Setting { "&cI'm sorry, but I'm unable to salvage netherite items!", "The message to display when asked to salvage netherite items, and that option is disabled", true, true), + /** + * The message displayed when explaining that enchanted book salvage is disabled + */ + CANNOT_SALVAGE_ENCHANTED_BOOK_MESSAGE("cannotSalvageEnchantedBookMessage", SettingValueType.STRING, + "&cI'm sorry, but I'm unable to salvage enchanted books!", + "The message to display when asked to salvage enchanted books, and the option is disabled", true, true), + + /** + * The message displayed when explaining that a player needs to provide items to salvage an enchanted book + */ + NOT_ENOUGH_ITEMS_MESSAGE("notEnoughItemsMessage", SettingValueType.STRING, + "&cI'm sorry, but you need to provide enough items for salvaging the enchanted book", + "The message displayed when a player attempts to salvage an enchanted book without providing enough items", true, true), + + /** + * The message displayed when explaining that a player cannot salvage an enchanted book containing a single enchantment + */ + CANNOT_SPLIT_ENCHANTED_BOOK_FURTHER_MESSAGE("cannotSplitEnchantedBookFurtherMessage", SettingValueType.STRING, + "&cI'm sorry, but I cannot salvage that enchanted book any further", + "The message displayed when a player attempts to salvage an enchanted book with a single enchantment", true, true), + /** * The message displayed when clicking a scrapper with an empty hand */ @@ -264,6 +298,30 @@ public enum ScrapperSetting implements Setting { NETHERITE_SALVAGE_COST("netheriteSalvagePrice", SettingValueType.POSITIVE_DOUBLE, 15, "The cost of using the scrapper to remove netherite from an item", false, false), + /** + * The setting for the enchanted book salvage cost + */ + ENCHANTED_BOOK_SALVAGE_BASE_COST("enchantedBookSalvageBasePrice", SettingValueType.POSITIVE_DOUBLE, 10, + "The per-enchantment cost of splitting an enchanted book", false, false), + + /** + * The setting for the enchanted book item cost + */ + ENCHANTED_BOOK_SALVAGE_ITEM_COST("enchantedBookSalvageItemCost", SettingValueType.ITEM_STACK, null, + "The item that needs to be provided in order to salvage an enchanted book", false, false), + + /** + * The setting for whether to multiply enchanted book cost + */ + ENCHANTED_BOOK_SALVAGE_MULTIPLY("enchantedBookSalvageMultiplyByEnchantmentNumber", SettingValueType.BOOLEAN, + true, "Whether to multiply the cost of salvaging an enchanted book by the number of enchantments on the book", false, false), + + /** + * The setting for whether to require both monetary and item payment for salvaging enchanted books + */ + ENCHANTED_BOOK_SALVAGE_REQUIRE_BOTH("enchantedBookSalvageRequireMoneyAndItem", SettingValueType.BOOLEAN, + true, "Whether salvaging an enchanted book will cost both the configured money amount and the configured item", false, false), + /** * Whether to display exact time in minutes and seconds when displaying a remaining cool-down */ diff --git a/src/main/java/net/knarcraft/blacksmith/container/ActionCost.java b/src/main/java/net/knarcraft/blacksmith/container/ActionCost.java new file mode 100644 index 0000000..cb67455 --- /dev/null +++ b/src/main/java/net/knarcraft/blacksmith/container/ActionCost.java @@ -0,0 +1,99 @@ +package net.knarcraft.blacksmith.container; + +import net.knarcraft.blacksmith.manager.EconomyManager; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** + * The cost of performing an action + * + * @param monetaryCost

The monetary cost of the action

+ * @param expCost

The experience cost of the action

+ * @param itemCost

The item-based cost of the action

+ * @param requiredPermissions

The permission required for the action

+ */ +public record ActionCost(double monetaryCost, int expCost, @Nullable ItemStack itemCost, + @NotNull Set requiredPermissions) { + + /** + * Checks whether the given player is able to pay this action cost + * + * @param player

The player to check

+ * @return

True if the player is able to pay

+ */ + public boolean canPay(@NotNull Player player) { + for (String permission : requiredPermissions) { + if (!player.hasPermission(permission)) { + return false; + } + } + + if (player.getExp() < expCost || !EconomyManager.hasEnough(player, monetaryCost)) { + return false; + } + + return itemCost == null || player.getInventory().contains(itemCost); + } + + public void takePayment(@NotNull Player player) { + player.giveExp(-expCost); + EconomyManager.withdraw(monetaryCost); + + } + + /** + * Takes payment for printing a number of books by withdrawing the correct item + * + * @param player

The player which needs to pay

+ * @param item

The item to pay

+ */ + private static void payForBookPrintingItem(Player player, ItemStack item) { + PlayerInventory playerInventory = player.getInventory(); + ItemMeta targetMeta = item.getItemMeta(); + String displayName = null; + List lore = null; + if (targetMeta != null) { + displayName = targetMeta.hasDisplayName() ? targetMeta.getDisplayName() : null; + lore = targetMeta.hasLore() ? targetMeta.getLore() : null; + } + + HashMap itemsOfType = playerInventory.all(item.getType()); + + int clearedAmount = 0; + for (Map.Entry entry : itemsOfType.entrySet()) { + if (targetMeta == null) { + if (Objects.requireNonNull(entry.getValue()).getAmount() <= item.getAmount() - clearedAmount) { + clearedAmount += entry.getValue().getAmount(); + player.getInventory().clear(entry.getKey()); + } else { + clearedAmount = item.getAmount(); + entry.getValue().setAmount(entry.getValue().getAmount() - (clearedAmount)); + } + } else { + // TODO: Only consider item if the name and lore is the same + ItemMeta meta = entry.getValue().getItemMeta(); + if (meta == null) { + break; + } + if (displayName != null && (!meta.hasDisplayName() || !meta.getDisplayName().equals(displayName))) { + break; + } + } + + if (clearedAmount <= item.getAmount()) { + break; + } + } + } + +} diff --git a/src/main/java/net/knarcraft/blacksmith/manager/EconomyManager.java b/src/main/java/net/knarcraft/blacksmith/manager/EconomyManager.java index 717e58c..8b6fd5c 100644 --- a/src/main/java/net/knarcraft/blacksmith/manager/EconomyManager.java +++ b/src/main/java/net/knarcraft/blacksmith/manager/EconomyManager.java @@ -5,6 +5,7 @@ import net.knarcraft.blacksmith.config.blacksmith.GlobalBlacksmithSettings; import net.knarcraft.blacksmith.config.scrapper.GlobalScrapperSettings; import net.knarcraft.blacksmith.property.SalvageMethod; import net.knarcraft.blacksmith.util.ItemHelper; +import net.knarcraft.blacksmith.util.SalvageHelper; import net.milkbowl.vault.economy.Economy; import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; @@ -54,10 +55,11 @@ public class EconomyManager { * * @param player

The player holding an item

* @param salvageMethod

The salvage method to check

+ * @param item

The item to be salvaged

* @return

Whether the player cannot pay for the salvage

*/ - public static boolean cannotPayForSalvage(@NotNull Player player, @NotNull SalvageMethod salvageMethod) { - return economy.getBalance(player) - getSalvageCost(salvageMethod) < 0; + public static boolean cannotPayForSalvage(@NotNull Player player, @NotNull SalvageMethod salvageMethod, @NotNull ItemStack item) { + return economy.getBalance(player) - getSalvageCost(salvageMethod, item) < 0; } /** @@ -76,11 +78,34 @@ public class EconomyManager { * Gets the human-readable cost of salvaging an item * * @param salvageMethod

The salvage method to get the cost for

+ * @param item

The item to be salvaged

* @return

The formatted cost

*/ @NotNull - public static String formatSalvageCost(@NotNull SalvageMethod salvageMethod) { - return economy.format(getSalvageCost(salvageMethod)); + public static String formatSalvageCost(@NotNull SalvageMethod salvageMethod, @NotNull ItemStack item) { + return economy.format(getSalvageCost(salvageMethod, item)); + } + + /** + * Gets whether the given player has enough money to pay the given cost + * + * @param player

The player to check

+ * @param cost

The required cost

+ * @return

True if the player has enough money to cover the cost

+ */ + public static boolean hasEnough(@NotNull Player player, double cost) { + return economy.getBalance(player) >= cost; + } + + /** + * Formats a number as an economy cost + * + * @param cost

The cost to format

+ * @return

The formatted cost

+ */ + @NotNull + public static String format(double cost) { + return economy.format(cost); } /** @@ -89,12 +114,13 @@ public class EconomyManager { * @param salvageMethod

The salvage method to get cost for

* @return

The salvage cost

*/ - private static double getSalvageCost(@NotNull SalvageMethod salvageMethod) { + private static double getSalvageCost(@NotNull SalvageMethod salvageMethod, @NotNull ItemStack item) { GlobalScrapperSettings settings = BlacksmithPlugin.getInstance().getGlobalScrapperSettings(); return switch (salvageMethod) { case SALVAGE, EXTENDED_SALVAGE -> settings.getSalvageCost(); case NETHERITE -> settings.getNetheriteSalvageCost(); case ARMOR_TRIM -> settings.getArmorTrimSalvageCost(); + case ENCHANTED_BOOK -> getEnchantedBookSalvageCost(item); }; } @@ -119,7 +145,7 @@ public class EconomyManager { * @param salvageMethod

The salvage method to withdraw for

*/ public static void withdrawScrapper(@NotNull Player player, @NotNull SalvageMethod salvageMethod) { - double cost = getSalvageCost(salvageMethod); + double cost = getSalvageCost(salvageMethod, player.getInventory().getItemInMainHand()); if (cost > 0) { economy.withdrawPlayer(player, cost); } @@ -183,6 +209,21 @@ public class EconomyManager { return price; } + /** + * Gets the cost of scrapping an enchanted book + * + * @param item

The enchanted book to calculate cost for

+ * @return

The cost of scrapping the enchanted book

+ */ + private static double getEnchantedBookSalvageCost(@NotNull ItemStack item) { + GlobalScrapperSettings settings = BlacksmithPlugin.getInstance().getGlobalScrapperSettings(); + double cost = settings.getEnchantedBookSalvageCost(); + if (settings.multiplyEnchantedBookSalvageCost()) { + cost *= SalvageHelper.getEnchantmentCount(item) - 1; + } + return SalvageHelper.getEnchantmentCount(item) * cost; + } + /** * Sets up Vault for economy * diff --git a/src/main/java/net/knarcraft/blacksmith/property/SalvageMethod.java b/src/main/java/net/knarcraft/blacksmith/property/SalvageMethod.java index 581f6b9..33670d6 100644 --- a/src/main/java/net/knarcraft/blacksmith/property/SalvageMethod.java +++ b/src/main/java/net/knarcraft/blacksmith/property/SalvageMethod.java @@ -25,4 +25,9 @@ public enum SalvageMethod { */ NETHERITE, + /** + * Splitting enchantments of an enchanted book + */ + ENCHANTED_BOOK, + } diff --git a/src/main/java/net/knarcraft/blacksmith/trait/CustomTrait.java b/src/main/java/net/knarcraft/blacksmith/trait/CustomTrait.java index 1312ecd..727c007 100644 --- a/src/main/java/net/knarcraft/blacksmith/trait/CustomTrait.java +++ b/src/main/java/net/knarcraft/blacksmith/trait/CustomTrait.java @@ -190,10 +190,9 @@ public abstract class CustomTrait extends Trait { } else if (this.session instanceof SalvageSession salvageSession) { EconomyManager.withdrawScrapper(player, salvageSession.salvageMethod); } - - session.scheduleAction(); PlayerInventory playerInventory = player.getInventory(); ItemStack heldItem = player.getInventory().getItemInMainHand(); + session.scheduleAction(); //Display the item in the NPC's hand Entity entity = npc.getEntity(); diff --git a/src/main/java/net/knarcraft/blacksmith/trait/SalvageSession.java b/src/main/java/net/knarcraft/blacksmith/trait/SalvageSession.java index b262aea..7adb8d3 100644 --- a/src/main/java/net/knarcraft/blacksmith/trait/SalvageSession.java +++ b/src/main/java/net/knarcraft/blacksmith/trait/SalvageSession.java @@ -70,7 +70,9 @@ public class SalvageSession extends Session implements Runnable { return true; } - if (EconomyManager.cannotPayForSalvage(this.player, this.salvageMethod)) { + // TODO: Check item cost. If + + if (EconomyManager.cannotPayForSalvage(this.player, this.salvageMethod, this.itemToSalvage)) { sendNPCMessage(this.npc, this.player, this.config.getInsufficientFundsMessage()); return true; } @@ -94,6 +96,7 @@ public class SalvageSession extends Session implements Runnable { case EXTENDED_SALVAGE -> Material.CRAFTING_TABLE; case SALVAGE -> Material.ANVIL; case NETHERITE, ARMOR_TRIM -> Material.SMITHING_TABLE; + case ENCHANTED_BOOK -> Material.ENCHANTING_TABLE; }; } diff --git a/src/main/java/net/knarcraft/blacksmith/trait/ScrapperTrait.java b/src/main/java/net/knarcraft/blacksmith/trait/ScrapperTrait.java index e8138d9..d5511e8 100644 --- a/src/main/java/net/knarcraft/blacksmith/trait/ScrapperTrait.java +++ b/src/main/java/net/knarcraft/blacksmith/trait/ScrapperTrait.java @@ -4,6 +4,7 @@ import net.citizensnpcs.api.util.DataKey; import net.knarcraft.blacksmith.BlacksmithPlugin; import net.knarcraft.blacksmith.config.SmithPreset; import net.knarcraft.blacksmith.config.SmithPresetFilter; +import net.knarcraft.blacksmith.config.scrapper.GlobalScrapperSettings; import net.knarcraft.blacksmith.config.scrapper.ScrapperNPCSettings; import net.knarcraft.blacksmith.config.scrapper.ScrapperSetting; import net.knarcraft.blacksmith.container.RecipeResult; @@ -20,6 +21,7 @@ import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ArmorMeta; +import org.bukkit.inventory.meta.EnchantmentStorageMeta; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -117,7 +119,7 @@ public class ScrapperTrait extends CustomTrait { return; } // Print the cost to the player - printCostMessage(player, itemInHand, EconomyManager.formatSalvageCost(result.salvageMethod()), + printCostMessage(player, itemInHand, EconomyManager.formatSalvageCost(result.salvageMethod(), itemInHand), result.salvageMethod()); } @@ -145,6 +147,13 @@ public class ScrapperTrait extends CustomTrait { return result; } + result = isEnchantedBookSalvage(player, itemInHand); + if (result.salvageState() == SalvageState.NO_SALVAGE) { + return null; + } else if (result.salvageState() == SalvageState.FOUND_SALVAGE) { + return result; + } + result = isNormalSalvage(player, itemInHand, extended); if (result.salvageState() == SalvageState.NO_SALVAGE) { return null; @@ -174,7 +183,7 @@ public class ScrapperTrait extends CustomTrait { return new SalvageResult(SalvageMethod.SALVAGE, new ArrayList<>(), SalvageState.NO_SALVAGE, 0); } - // Check if the item is enchanted, and whether this blacksmith can salvage it + // Check if the item is enchanted, and whether this scrapper can salvage it if (!itemInHand.getEnchantments().isEmpty() && !getSettings().salvageEnchanted()) { sendNPCMessage(this.npc, player, getSettings().getCannotSalvageEnchantedMessage()); return new SalvageResult(SalvageMethod.SALVAGE, new ArrayList<>(), SalvageState.NO_SALVAGE, 0); @@ -193,6 +202,37 @@ public class ScrapperTrait extends CustomTrait { } } + /** + * Gets the result of trying to salvage the item as an enchanted book + * + * @param player

The player trying to salvage the item

+ * @param itemInHand

The item to be salvaged

+ * @return

The result of attempting the salvage

+ */ + private SalvageResult isEnchantedBookSalvage(@NotNull Player player, @NotNull ItemStack itemInHand) { + if (itemInHand.getType() != Material.ENCHANTED_BOOK || + !(itemInHand.getItemMeta() instanceof EnchantmentStorageMeta)) { + return new SalvageResult(SalvageMethod.ENCHANTED_BOOK, new ArrayList<>(), SalvageState.INCORRECT_METHOD, 0); + } + + if (!getSettings().splitEnchantedBook()) { + sendNPCMessage(this.npc, player, getSettings().getCannotSalvageEnchantedBookMessage()); + return new SalvageResult(SalvageMethod.ENCHANTED_BOOK, new ArrayList<>(), SalvageState.NO_SALVAGE, 0); + } + + if (SalvageHelper.getEnchantmentCount(itemInHand) <= 1) { + sendNPCMessage(this.npc, player, getSettings().getCannotSplitEnchantedBookFurtherMessage()); + return new SalvageResult(SalvageMethod.ENCHANTED_BOOK, new ArrayList<>(), SalvageState.NO_SALVAGE, 0); + } + + List salvage = SalvageHelper.getEnchantedBookSalvage(itemInHand); + boolean noUsefulSalvage = salvage == null || salvage.isEmpty(); + if (noUsefulSalvage) { + return new SalvageResult(SalvageMethod.ENCHANTED_BOOK, new ArrayList<>(), SalvageState.NO_SALVAGE, 0); + } + return new SalvageResult(SalvageMethod.ENCHANTED_BOOK, salvage, SalvageState.FOUND_SALVAGE, 1); + } + /** * Gets the result of trying to salvage the item as a netherite applied item * @@ -272,10 +312,30 @@ public class ScrapperTrait extends CustomTrait { sendNPCMessage(this.npc, player, replacer.replace(getSettings().getCostMessage())); } else if (salvageMethod == SalvageMethod.NETHERITE) { sendNPCMessage(this.npc, player, replacer.replace(getSettings().getNetheriteCostMessage())); + } else if (salvageMethod == SalvageMethod.ENCHANTED_BOOK) { + StringReplacer replacer2 = new StringReplacer(); + replacer2.add("{cost}", getEnchantedBookCost()); + sendNPCMessage(this.npc, player, replacer2.replace(getSettings().getEnchantedBookCostMessage())); } else { BlacksmithPlugin.error("Unrecognized salvage method " + salvageMethod); } } + + private String getEnchantedBookCost() { + // TODO: If both an item and money is required, print both requirements + // TODO: If only money is required, print the money requirement + // TODO: If an item requirement is set, print the item requirement + + GlobalScrapperSettings scrapperSettings = BlacksmithPlugin.getInstance().getGlobalScrapperSettings(); + String moneyCost = EconomyManager.format(scrapperSettings.getEnchantedBookSalvageCost()); + ItemStack itemCost = scrapperSettings. + if (scrapperSettings.requireMoneyAndItemForEnchantedBookSalvage()) { + // TODO: Print both with a + between them (if item has a special name, use that instead of the material name) + + } else { + // TODO: If the item is not null, print it, otherwise print the monetary cost + } + } @Override protected boolean showExactTime() { @@ -291,8 +351,8 @@ public class ScrapperTrait extends CustomTrait { * @return

True if the item can be theoretically salvaged

*/ private boolean canBeSalvaged(@NotNull ItemStack item, @NotNull List salvageAbleItems, boolean extended) { - return (extended || ItemHelper.isRepairable(item)) && - (salvageAbleItems.isEmpty() || salvageAbleItems.contains(item.getType())); + return item.getType() == Material.ENCHANTED_BOOK || ((extended || ItemHelper.isRepairable(item)) && + (salvageAbleItems.isEmpty() || salvageAbleItems.contains(item.getType()))); } /** diff --git a/src/main/java/net/knarcraft/blacksmith/util/SalvageHelper.java b/src/main/java/net/knarcraft/blacksmith/util/SalvageHelper.java index 467e366..2bf3f26 100644 --- a/src/main/java/net/knarcraft/blacksmith/util/SalvageHelper.java +++ b/src/main/java/net/knarcraft/blacksmith/util/SalvageHelper.java @@ -2,6 +2,7 @@ package net.knarcraft.blacksmith.util; import net.knarcraft.blacksmith.BlacksmithPlugin; import net.knarcraft.blacksmith.container.RecipeResult; +import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Server; import org.bukkit.enchantments.Enchantment; @@ -10,6 +11,8 @@ import org.bukkit.inventory.Recipe; import org.bukkit.inventory.ShapedRecipe; import org.bukkit.inventory.ShapelessRecipe; import org.bukkit.inventory.meta.ArmorMeta; +import org.bukkit.inventory.meta.EnchantmentStorageMeta; +import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.trim.ArmorTrim; import org.bukkit.inventory.meta.trim.TrimMaterial; import org.jetbrains.annotations.NotNull; @@ -44,6 +47,51 @@ public final class SalvageHelper { trimMaterialToMaterial.put(TrimMaterial.REDSTONE, Material.REDSTONE); } + /** + * Gets salvage for an enchanted book + * + * @param itemStack

The enchanted book(s) to salvage

+ * @return

The resulting salvage

+ * @throws RuntimeException

If generated enchanted book metadata is null

+ */ + @Nullable + public static List getEnchantedBookSalvage(@NotNull ItemStack itemStack) { + ItemMeta meta = itemStack.getItemMeta(); + List output = new ArrayList<>(); + + if (!(meta instanceof EnchantmentStorageMeta enchantmentStorageMeta)) { + return null; + } + + Map enchantmentMap = enchantmentStorageMeta.getStoredEnchants(); + for (Map.Entry enchantmentEntry : enchantmentMap.entrySet()) { + EnchantmentStorageMeta enchantmentMeta = (EnchantmentStorageMeta) Bukkit.getServer().getItemFactory().getItemMeta(Material.ENCHANTED_BOOK); + if (enchantmentMeta == null) { + throw new RuntimeException("Unable to create enchanted book metadata."); + } + enchantmentMeta.addStoredEnchant(enchantmentEntry.getKey(), enchantmentEntry.getValue(), true); + ItemStack newBook = new ItemStack(Material.ENCHANTED_BOOK, 1); + newBook.setItemMeta(enchantmentMeta); + output.add(newBook); + } + + return output; + } + + /** + * Gets the number of enchantments applied to an enchanted book + * + * @param itemStack

The enchanted book to check

+ * @return

The number of enchantments, or -1 if not an enchanted book

+ */ + public static int getEnchantmentCount(@NotNull ItemStack itemStack) { + ItemMeta meta = itemStack.getItemMeta(); + if (!(meta instanceof EnchantmentStorageMeta enchantmentStorageMeta)) { + return -1; + } + return enchantmentStorageMeta.getStoredEnchants().size(); + } + /** * Gets salvage for a given netherite item * diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 92e720f..0e01af2 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -141,6 +141,18 @@ scrapper: # The cost of using the scrapper to remove netherite from an item netheriteSalvagePrice: 15 + + # The per-enchantment cost of splitting an enchanted book + enchantedBookSalvageBasePrice: 10 + + # The item that needs to be provided to pay for splitting an enchanted book + enchantedBookSalvageItemCost: null + + # Whether to multiply the initial cost with the number of enchantments on the book + enchantedBookSalvageMultiplyByEnchantmentNumber: true + + # Whether to require both a monetary sum and providing an item in order to pay for salvaging an enchanted book + enchantedBookSalvageRequireMoneyAndItem: false # The settings which are set to any new scrapper NPC. To change any of these settings for an existing NPC, you must # change the Citizens NPC file, or use the /scrapper command @@ -182,6 +194,9 @@ scrapper: # Whether to enable salvaging of netherite items salvageNetherite: true + # Whether to enable salvaging an enchanted books with several enchantments into several books with one enchantment + splitEnchantedBook: false + # Default values for messages used by NPCs messages: # The message to display when another player is using the scrapper @@ -226,6 +241,9 @@ scrapper: # The message to display when explaining the shown item's netherite salvage cost costMessageNetherite: "&eIt will cost &a{cost}&e to salvage that &a{item}&e into diamond!" + # The message to display when explaining the shown enchanted book's salvage cost + costMessageEnchantedBook: "&eIt will cost &a{cost}&e to salvage that enchanted book!" + # The yield message to display if trying to salvage a non-damaged item fullSalvageMessage: "&aI should be able to extract all components from that pristine item.&r" @@ -244,5 +262,14 @@ scrapper: # The message to display when asked to salvage netherite items, and that option is disabled cannotSalvageNetheriteMessage: "&cI'm sorry, but I'm unable to salvage netherite items!" + # The message displayed when explaining that enchanted book salvage is disabled + cannotSalvageEnchantedBookMessage: "&cI'm sorry, but I'm unable to salvage enchanted books!" + + # The message displayed when a player attempts to salvage an enchanted book without providing enough normal books + notEnoughItemsMessage: "&cI'm sorry, but you need to provide {number} {item} to salvage the enchanted book" + + # The message displayed when a player attempts to salvage an enchanted book with a single enchantment + cannotSplitEnchantedBookFurtherMessage: "&cI'm sorry, but I cannot salvage that enchanted book any further" + # The message to display when a scrapper is clicked with an empty hand noItemMessage: "Please present the item you want to salvage" \ No newline at end of file