This will throw an exception if used for a non-double setting
- * - * @param settingThe setting to get the value of
- * @returnThe value of the given setting as a double
- */ - public double asDouble(@NotNull ScrapperSetting setting) { - return ConfigHelper.asDouble(getValue(setting)); - } - /** * Gets the value of a setting, using the default if not set * @@ -190,8 +179,9 @@ public class GlobalScrapperSettings implements SettingsThe cost of using a scrapper to salvage an item
*/ - public double getSalvageCost() { - return asDouble(ScrapperSetting.SALVAGE_COST); + @NotNull + public ActionCost getSalvageCost() { + return getCost(ScrapperSetting.SALVAGE_COST, "salvage"); } /** @@ -199,8 +189,9 @@ public class GlobalScrapperSettings implements SettingsThe cost of using a scrapper to remove armor trim
*/ - public double getArmorTrimSalvageCost() { - return asDouble(ScrapperSetting.ARMOR_TRIM_SALVAGE_COST); + @NotNull + public ActionCost getArmorTrimSalvageCost() { + return getCost(ScrapperSetting.ARMOR_TRIM_SALVAGE_COST, "armor trim salvage"); } /** @@ -208,8 +199,19 @@ public class GlobalScrapperSettings implements SettingsThe cost of using a scrapper to remove netherite from an item
*/ - public double getNetheriteSalvageCost() { - return asDouble(ScrapperSetting.NETHERITE_SALVAGE_COST); + @NotNull + public ActionCost getNetheriteSalvageCost() { + return getCost(ScrapperSetting.NETHERITE_SALVAGE_COST, "netherite salvage"); + } + + /** + * Gets the cost of using a scrapper to split an enchanted book + * + * @returnThe cost of using a scrapper to split an enchanted book
+ */ + @NotNull + public ActionCost getEnchantedBookSalvageCost() { + return getCost(ScrapperSetting.ENCHANTED_BOOK_SALVAGE_COST, "enchanted book salvage"); } /** @@ -230,6 +232,15 @@ public class GlobalScrapperSettings implements SettingsWhether the enchanted book salvage price should be multiplied with the number of enchantments
+ */ + public boolean multiplyEnchantedBokSalvagePrice() { + return asBoolean(ScrapperSetting.MULTIPLY_ENCHANTED_BOOK_SALVAGE_COST); + } + /** * Gets trash salvage for the given material * @@ -304,4 +315,24 @@ public class GlobalScrapperSettings implements SettingsThe setting to get cost from
+ * @param costTypeCost identifier for error message
+ * @returnThe cost of the action
+ */ + @NotNull + private ActionCost getCost(@NotNull ScrapperSetting setting, @NotNull String costType) { + Object value = getValue(setting); + if (value instanceof ActionCost actionCost) { + return actionCost; + } else if (value instanceof Double number) { + return new ActionCost(number); + } else { + BlacksmithPlugin.error("Unable to load " + costType + " cost! Cost set to free!"); + return new ActionCost(); + } + } + } 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 3f1d849..70865b0 100644 --- a/src/main/java/net/knarcraft/blacksmith/config/scrapper/ScrapperSetting.java +++ b/src/main/java/net/knarcraft/blacksmith/config/scrapper/ScrapperSetting.java @@ -276,21 +276,33 @@ public enum ScrapperSetting implements Setting { /** * The setting for the salvage cost */ - SALVAGE_COST("salvagePrice", SettingValueType.POSITIVE_DOUBLE, 0, + SALVAGE_COST("salvagePrice", SettingValueType.ADVANCED_COST, 0, "The cost of using a scrapper to salvage an item", false, false), /** * The setting for the armor trim salvage cost */ - ARMOR_TRIM_SALVAGE_COST("armorTrimSalvagePrice", SettingValueType.POSITIVE_DOUBLE, 5, + ARMOR_TRIM_SALVAGE_COST("armorTrimSalvagePrice", SettingValueType.ADVANCED_COST, 5, "The cost of using the scrapper to remove armor trim", false, false), /** * The setting for the netherite salvage cost */ - NETHERITE_SALVAGE_COST("netheriteSalvagePrice", SettingValueType.POSITIVE_DOUBLE, 15, + NETHERITE_SALVAGE_COST("netheriteSalvagePrice", SettingValueType.ADVANCED_COST, 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_COST("enchantedBookSalvagePrice", SettingValueType.ADVANCED_COST, 15, + "The cost of using the scrapper to split an enchanted book", false, false), + + /** + * The setting for whether to multiply enchanted book salvage cost + */ + MULTIPLY_ENCHANTED_BOOK_SALVAGE_COST("multiplyEnchantedBookSalvageCost", SettingValueType.BOOLEAN, false, + "Whether to multiply the enchanted book salvage cost with the number of enchantments", false, false), + /** * The mathematical formula for increasing salvage cost */ diff --git a/src/main/java/net/knarcraft/blacksmith/container/ActionCost.java b/src/main/java/net/knarcraft/blacksmith/container/ActionCost.java index 3d72942..3f352f9 100644 --- a/src/main/java/net/knarcraft/blacksmith/container/ActionCost.java +++ b/src/main/java/net/knarcraft/blacksmith/container/ActionCost.java @@ -4,11 +4,9 @@ import net.knarcraft.blacksmith.BlacksmithPlugin; import net.knarcraft.blacksmith.manager.EconomyManager; import net.md_5.bungee.api.ChatColor; import org.bukkit.NamespacedKey; -import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.serialization.ConfigurationSerializable; 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; @@ -30,6 +28,22 @@ import java.util.Set; public record ActionCost(double monetaryCost, int expCost, @Nullable ItemStack itemCost, @NotNull SetThe monetary cost to set
+ */ + public ActionCost(double monetaryCost) { + this(monetaryCost, 0, null, Set.of()); + } + /** * Changes the monetary cost of this action * @@ -71,13 +85,13 @@ public record ActionCost(double monetaryCost, int expCost, @Nullable ItemStack i } /** - * Displays this action cost as a string + * Gets this action cost's costs as a string * * @param playerThe player to calculate the cost for
* @returnThe string representation of this action cost
*/ @NotNull - public String displayCost(@NotNull Player player) { + public String getCostString(@NotNull Player player) { StringBuilder builder = new StringBuilder(); if (monetaryCost > 0) { builder.append(EconomyManager.format(monetaryCost)).append(", ").append("\n"); @@ -174,7 +188,6 @@ public record ActionCost(double monetaryCost, int expCost, @Nullable ItemStack i return new HashMap<>(); } - PlayerInventory playerInventory = player.getInventory(); ItemMeta targetMeta = this.itemCost.getItemMeta(); String displayName = null; ListThe configuration section to load from
- * @param keyThe key of the cost to load
- * @returnThe loaded cost
- */ - private static ActionCost loadActionCost(@NotNull ConfigurationSection configurationSection, @NotNull String key) { - double cost = configurationSection.getDouble(key, Double.MIN_VALUE); - if (cost != Double.MIN_VALUE) { - return new ActionCost(cost, 0, null, Set.of()); - } else { - return (ActionCost) configurationSection.get(key); - } - } - /** * Deserializes an action cost * diff --git a/src/main/java/net/knarcraft/blacksmith/manager/EconomyManager.java b/src/main/java/net/knarcraft/blacksmith/manager/EconomyManager.java index 9e137ee..5b8d03c 100644 --- a/src/main/java/net/knarcraft/blacksmith/manager/EconomyManager.java +++ b/src/main/java/net/knarcraft/blacksmith/manager/EconomyManager.java @@ -3,8 +3,10 @@ package net.knarcraft.blacksmith.manager; import net.knarcraft.blacksmith.BlacksmithPlugin; import net.knarcraft.blacksmith.config.blacksmith.GlobalBlacksmithSettings; import net.knarcraft.blacksmith.config.scrapper.GlobalScrapperSettings; +import net.knarcraft.blacksmith.container.ActionCost; import net.knarcraft.blacksmith.property.SalvageMethod; import net.knarcraft.blacksmith.util.ItemHelper; +import net.knarcraft.blacksmith.util.SalvageHelper; import net.knarcraft.knarlib.formatting.StringReplacer; import net.milkbowl.vault.economy.Economy; import net.objecthunter.exp4j.Expression; @@ -60,9 +62,9 @@ public class EconomyManager { * @param itemThe item to be salvaged
* @returnWhether the player cannot pay for the salvage
*/ - public static boolean cannotPayForSalvage(@NotNull Player player, @NotNull SalvageMethod salvageMethod, @NotNull ItemStack item) { - // TODO: Account for advanced cost options - return economy.getBalance(player) - getSalvageCost(salvageMethod, item, player) < 0; + public static boolean cannotPayForSalvage(@NotNull Player player, @NotNull SalvageMethod salvageMethod, + @NotNull ItemStack item) { + return !getSalvageCost(salvageMethod, item, player).canPay(player); } /** @@ -88,7 +90,7 @@ public class EconomyManager { @NotNull public static String formatSalvageCost(@NotNull SalvageMethod salvageMethod, @NotNull ItemStack item, @NotNull Player player) { - return economy.format(getSalvageCost(salvageMethod, item, player)); + return getSalvageCost(salvageMethod, item, player).getCostString(player); } /** @@ -120,10 +122,11 @@ public class EconomyManager { * @param itemThe item to be salvaged
* @returnThe salvage cost
*/ - private static double getSalvageCost(@NotNull SalvageMethod salvageMethod, @NotNull ItemStack item, - @NotNull Player player) { + @NotNull + private static ActionCost getSalvageCost(@NotNull SalvageMethod salvageMethod, @NotNull ItemStack item, + @NotNull Player player) { GlobalScrapperSettings settings = BlacksmithPlugin.getInstance().getGlobalScrapperSettings(); - double baseCost = switch (salvageMethod) { + ActionCost cost = switch (salvageMethod) { case SALVAGE, EXTENDED_SALVAGE -> settings.getSalvageCost(); case NETHERITE -> settings.getNetheriteSalvageCost(); case ARMOR_TRIM -> settings.getArmorTrimSalvageCost(); @@ -131,11 +134,12 @@ public class EconomyManager { }; StringReplacer replacer = new StringReplacer(settings.getSalvageCostIncrease()); - replacer.add("{cost}", String.valueOf(baseCost)); + replacer.add("{cost}", String.valueOf(cost.monetaryCost())); replacer.add("{timesUsed}", String.valueOf(PlayerUsageManager.getUsages(player, System.currentTimeMillis() - 3600000))); Expression expression = new ExpressionBuilder(replacer.replace()).build(); - return Math.max(expression.evaluate(), baseCost); + cost.changeMonetaryCost(Math.max(cost.monetaryCost(), expression.evaluate())); + return cost; } /** @@ -158,10 +162,13 @@ public class EconomyManager { * @param playerThe player to withdraw from
* @param salvageMethodThe salvage method to withdraw for
*/ - public static void withdrawScrapper(@NotNull Player player, @NotNull SalvageMethod salvageMethod) { - double cost = getSalvageCost(salvageMethod, player.getInventory().getItemInMainHand(), player); - if (cost > 0) { - economy.withdrawPlayer(player, cost); + public static boolean withdrawScrapper(@NotNull Player player, @NotNull SalvageMethod salvageMethod) { + ActionCost cost = getSalvageCost(salvageMethod, player.getInventory().getItemInMainHand(), player); + if (cost.canPay(player)) { + cost.takePayment(player); + return true; + } else { + return false; } } @@ -243,16 +250,15 @@ public class EconomyManager { * @param itemThe enchanted book to calculate cost for
* @returnThe cost of scrapping the enchanted book
*/ - private static double getEnchantedBookSalvageCost(@NotNull ItemStack item) { - // TODO: Properly implement this - /*GlobalScrapperSettings settings = BlacksmithPlugin.getInstance().getGlobalScrapperSettings(); - - double cost = settings.getEnchantedBookSalvageCost(); - if (settings.multiplyEnchantedBookSalvageCost()) { - cost *= SalvageHelper.getEnchantmentCount(item) - 1; + @NotNull + private static ActionCost getEnchantedBookSalvageCost(@NotNull ItemStack item) { + GlobalScrapperSettings settings = BlacksmithPlugin.getInstance().getGlobalScrapperSettings(); + + ActionCost cost = settings.getEnchantedBookSalvageCost(); + if (settings.multiplyEnchantedBokSalvagePrice()) { + cost.changeMonetaryCost(cost.monetaryCost() * Math.max(SalvageHelper.getEnchantmentCount(item) - 1, 1)); } - return SalvageHelper.getEnchantmentCount(item) * cost;*/ - return 1000000; + return cost; } /** diff --git a/src/main/java/net/knarcraft/blacksmith/property/CostModifyAction.java b/src/main/java/net/knarcraft/blacksmith/property/CostModifyAction.java new file mode 100644 index 0000000..00434b8 --- /dev/null +++ b/src/main/java/net/knarcraft/blacksmith/property/CostModifyAction.java @@ -0,0 +1,43 @@ +package net.knarcraft.blacksmith.property; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * An action for modifying cost + */ +public enum CostModifyAction { + + /** + * Adds some cost to the existing cost, effectively modifying the total cost + */ + ADD, + + /** + * Clears the cost completely + */ + CLEAR, + + /** + * Sets the cost to the new cost, clearing any other existing costs + */ + SET, + ; + + /** + * Gets the action specified by the given string + * + * @param inputThe input string to parse
+ * @returnThe action, or null if not matched
+ */ + @Nullable + public static CostModifyAction fromString(@NotNull String input) { + for (CostModifyAction action : CostModifyAction.values()) { + if (input.toUpperCase().equals(action.name())) { + return action; + } + } + return null; + } + +} diff --git a/src/main/java/net/knarcraft/blacksmith/trait/CustomTrait.java b/src/main/java/net/knarcraft/blacksmith/trait/CustomTrait.java index 3e1a3bb..8dcf4d4 100644 --- a/src/main/java/net/knarcraft/blacksmith/trait/CustomTrait.java +++ b/src/main/java/net/knarcraft/blacksmith/trait/CustomTrait.java @@ -187,7 +187,10 @@ public abstract class CustomTraitThe arguments given by the user
+ * @param argumentsThe arguments given by the user
* @param detectedSettingThe setting recognized from the input, if any
* @param settingsThe global settings object to get settings from
* @param senderThe command sender to display any output to
*/ - public staticThe command sender to notify of any problems
* @param costStringThe string to parse into a cost
- * @returnThe parsed cost
- * @throws IllegalArgumentExceptionIf the specified cost is invalid
+ * @returnThe parsed cost, or null if not a valid number
*/ - public static double parseSimpleCost(@NotNull CommandSender commandSender, + @Nullable + public static Double parseSimpleCost(@NotNull CommandSender commandSender, @NotNull String costString) throws IllegalArgumentException { try { double cost = Double.parseDouble(costString); @@ -51,7 +44,7 @@ public final class CostHelper { return cost; } catch (NumberFormatException exception) { new FormatBuilder(Translatable.DOUBLE_COST_REQUIRED).error(commandSender); - throw new IllegalArgumentException("Invalid cost given"); + return null; } } @@ -62,23 +55,19 @@ public final class CostHelper { * @returnThe available tab-complete options
*/ public static @Nullable ListThe arguments given by a player
- * @param oldCostThe previous cost of the action
- * @param playerThe player attempting to alter the action cost
+ * @param costActionThe action to perform on the cost
+ * @param costTypeNameThe name of the cost to modify, or null if clearing costs
+ * @param costValueThe value of the new cost, or null if clearing costs
+ * @param oldCostThe previous cost of the action
+ * @param playerThe player attempting to alter the action cost
* @returnThe modified action cost, or null if something went wrong
*/ @Nullable - public static ActionCost modifyActionCost(@NotNull String[] arguments, @NotNull ActionCost oldCost, + public static ActionCost modifyActionCost(@NotNull String costAction, @Nullable String costTypeName, + @Nullable String costValue, @NotNull ActionCost oldCost, @NotNull Player player) { CostType costType; - if (arguments.length > 2) { - costType = CostType.parse(arguments[2]); + if (costTypeName != null) { + costType = CostType.parse(costTypeName); } else { costType = null; } - switch (arguments[1].toLowerCase()) { - case "add": - if (costType == null) { - player.sendMessage("Invalid cost type specified!"); - return null; - } - return parseCost(costType, Arrays.copyOfRange(arguments, 2, arguments.length), oldCost, player); - case "clear": - // Clears one or all fields of a cost - if (costType == null) { - return new ActionCost(0, 0, null, Set.of()); - } else { - return switch (costType) { - case EXP -> oldCost.changeExpCost(0); - case ITEM -> oldCost.changeItemCost(null); - case ECONOMY -> oldCost.changeMonetaryCost(0); - case PERMISSION -> oldCost.changePermissionCost(Set.of()); - }; - } - case "set": - if (costType == null) { - player.sendMessage("Invalid cost type specified!"); - return null; - } - return parseCost(costType, Arrays.copyOfRange(arguments, 2, arguments.length), - new ActionCost(0, 0, null, Set.of()), player); - default: - return null; + CostModifyAction action = CostModifyAction.fromString(costAction); + if (action == null) { + return null; + } + + if (action != CostModifyAction.CLEAR && (costType == null || costValue == null)) { + player.sendMessage("Invalid cost type or value specified!"); + return null; + } + + return switch (action) { + case CLEAR -> clearActionCost(costType, oldCost); + case ADD -> addActionCost(costType, costValue, oldCost, player); + case SET -> setActionCost(costType, costValue, player); + }; + } + + /** + * Sets the cost to the specified value + * + * @param costTypeThe type of cost to set
+ * @param costValueThe value of the new cost
+ * @param playerThe player to alert if the cost cannot be properly set
+ * @returnThe new action cost
+ */ + public static ActionCost setActionCost(@NotNull CostType costType, @NotNull String costValue, + @NotNull Player player) { + return parseCost(costType, costValue, new ActionCost(), player); + } + + /** + * Modifies a value for the specified action cost + * + * @param costTypeThe type of cost to modify
+ * @param costValueThe new value of the cost
+ * @param oldCostThe cost to modify
+ * @param playerThe player to alert if the cost cannot be properly set
+ * @returnThe new action cost
+ */ + public static ActionCost addActionCost(@NotNull CostType costType, @NotNull String costValue, + @NotNull ActionCost oldCost, @NotNull Player player) { + return parseCost(costType, costValue, oldCost, player); + } + + /** + * Clears an action cost + * + * @param costTypeThe type of action cost to clear, or null if clearing all action costs
+ * @param oldCostThe old cost to modify, or null if clearing all action costs
+ * @returnThe modified cost
+ */ + public static ActionCost clearActionCost(@Nullable CostType costType, @Nullable ActionCost oldCost) { + // Clears one or all fields of a cost + if (costType == null || oldCost == null) { + return new ActionCost(); + } else { + return switch (costType) { + case EXP -> oldCost.changeExpCost(0); + case ITEM -> oldCost.changeItemCost(null); + case ECONOMY -> oldCost.changeMonetaryCost(0); + case PERMISSION -> oldCost.changePermissionCost(Set.of()); + }; } } /** * Parses an advanced action cost * - * @param costTypeThe type of cost to parse
- * @param argumentsThe arguments given by the player
- * @param oldCostThe old cost to modify
- * @param playerThe player changing the value
+ * @param costTypeThe type of cost to parse
+ * @param valueThe value to parse
+ * @param oldCostThe old cost to modify
+ * @param playerThe player changing the value
* @returnThe new action cost, or null if the process failed.
*/ @Nullable - private static ActionCost parseCost(@NotNull CostType costType, @NotNull String[] arguments, @NotNull ActionCost oldCost, + private static ActionCost parseCost(@NotNull CostType costType, @NotNull String value, @NotNull ActionCost oldCost, @NotNull Player player) { switch (costType) { case ECONOMY: - double economyCost = Double.parseDouble(arguments[0]); + double economyCost = Double.parseDouble(value); if (economyCost < 0) { - player.sendMessage("Cost cannot be negative!"); + new FormatBuilder("Cost cannot be negative!").error(player); return null; } return oldCost.changeMonetaryCost(economyCost); case ITEM: ItemStack itemCost = player.getInventory().getItemInMainHand(); if (itemCost.getType().isAir()) { - player.sendMessage("You have no item in your main hand"); + new FormatBuilder("You have no item in your main hand").error(player); return null; } return oldCost.changeItemCost(itemCost); case PERMISSION: - SetThe full permission node typed by the player
- * @returnAll known valid auto-complete options
- */ - private static @NotNull ListThe values to filter
- * @param typedTextThe text the player has started typing
- * @returnThe given string values which start with the player's typed text
- */ - private static @NotNull ListThe permission to load
- */ - private static void loadPermission(@NotNull String permissionName) { - String[] permissionParts = permissionName.split("\\."); - if (permissionParts.length == 1 && !plugins.contains(permissionParts[0])) { - plugins.add(permissionParts[0]); - } else if (permissionParts.length > 1) { - StringJoiner pathJoiner = new StringJoiner("."); - for (int j = 0; j < permissionParts.length - 1; j++) { - pathJoiner.add(permissionParts[j]); - } - String path = pathJoiner.toString(); - List