diff --git a/src/main/java/net/knarcraft/blacksmith/BlacksmithPlugin.java b/src/main/java/net/knarcraft/blacksmith/BlacksmithPlugin.java index 9cb62ce..1dc7cf3 100644 --- a/src/main/java/net/knarcraft/blacksmith/BlacksmithPlugin.java +++ b/src/main/java/net/knarcraft/blacksmith/BlacksmithPlugin.java @@ -1,8 +1,10 @@ package net.knarcraft.blacksmith; import net.citizensnpcs.api.CitizensAPI; -import net.knarcraft.blacksmith.command.BlackSmithCommand; -import net.knarcraft.blacksmith.command.BlackSmithTabCompleter; +import net.knarcraft.blacksmith.command.BlackSmithConfigCommand; +import net.knarcraft.blacksmith.command.BlackSmithConfigTabCompleter; +import net.knarcraft.blacksmith.command.BlackSmithEditCommand; +import net.knarcraft.blacksmith.command.BlackSmithEditTabCompleter; import net.knarcraft.blacksmith.config.GlobalSettings; import net.knarcraft.blacksmith.listener.NPCClickListener; import net.knarcraft.blacksmith.listener.PlayerListener; @@ -57,6 +59,7 @@ public class BlacksmithPlugin extends JavaPlugin { public void onEnable() { instance = this; + //Copy default config to disk FileConfiguration fileConfiguration = this.getConfig(); this.saveDefaultConfig(); fileConfiguration.options().copyDefaults(true); @@ -65,29 +68,65 @@ public class BlacksmithPlugin extends JavaPlugin { config = new GlobalSettings(this); config.load(); + //Set up Vault integration + if (!setUpVault()) { + return; + } + + //Register the blacksmith trait with Citizens + CitizensAPI.getTraitFactory().registerTrait( + net.citizensnpcs.api.trait.TraitInfo.create(BlacksmithTrait.class).withName("blacksmith")); + + //Register all commands + registerCommands(); + //Register all listeners + registerListeners(); + + getLogger().log(Level.INFO, " v" + getDescription().getVersion() + " enabled."); + } + + /** + * Tries to set up Vault + * + * @return
True if Vault setup/integration succeeded
+ */ + private boolean setUpVault() { getLogger().log(Level.INFO, "Setting Up Vault now...."); boolean canLoad = EconomyManager.setUp(getServer().getServicesManager(), getLogger()); if (!canLoad) { getLogger().log(Level.SEVERE, "Vault Integration Failed...."); getServer().getPluginManager().disablePlugin(this); - return; - } - //Register the blacksmith trait with Citizens - CitizensAPI.getTraitFactory().registerTrait( - net.citizensnpcs.api.trait.TraitInfo.create(BlacksmithTrait.class).withName("blacksmith")); - - //Register the blacksmith main-command - PluginCommand blacksmithCommand = this.getCommand("blacksmith"); - if (blacksmithCommand != null) { - blacksmithCommand.setExecutor(new BlackSmithCommand()); - blacksmithCommand.setTabCompleter(new BlackSmithTabCompleter()); + return false; } + return true; + } + /** + * Registers all listeners used by this plugin + */ + private void registerListeners() { PluginManager pluginManager = getServer().getPluginManager(); pluginManager.registerEvents(new PlayerListener(), this); pluginManager.registerEvents(new NPCClickListener(), this); + } - getLogger().log(Level.INFO, " v" + getDescription().getVersion() + " enabled."); + /** + * Registers all commands used by this plugin + */ + private void registerCommands() { + //Register the blacksmith NPC edit main-command + PluginCommand blacksmithCommand = this.getCommand("blacksmith"); + if (blacksmithCommand != null) { + blacksmithCommand.setExecutor(new BlackSmithEditCommand()); + blacksmithCommand.setTabCompleter(new BlackSmithEditTabCompleter()); + } + + //Register the global config edit command + PluginCommand blacksmithConfigCommand = this.getCommand("blacksmithConfig"); + if (blacksmithConfigCommand != null) { + blacksmithConfigCommand.setExecutor(new BlackSmithConfigCommand()); + blacksmithConfigCommand.setTabCompleter(new BlackSmithConfigTabCompleter()); + } } } diff --git a/src/main/java/net/knarcraft/blacksmith/command/BlackSmithCommand.java b/src/main/java/net/knarcraft/blacksmith/command/BlackSmithCommand.java deleted file mode 100644 index 3bee842..0000000 --- a/src/main/java/net/knarcraft/blacksmith/command/BlackSmithCommand.java +++ /dev/null @@ -1,36 +0,0 @@ -package net.knarcraft.blacksmith.command; - -import net.md_5.bungee.api.ChatColor; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.jetbrains.annotations.NotNull; - -/** - * The main command used for everything blacksmith related - */ -public class BlackSmithCommand implements CommandExecutor { - - @Override - public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, - @NotNull String[] args) { - if (!sender.hasPermission("blacksmith.admin")) { - sender.sendMessage(ChatColor.DARK_RED + "You don't have the necessary permission for using this command."); - return true; - } - - //TODO: This command should have one config sub-command which changes the default config values, and all - // setting names which can be changed for each NPC. - if (args.length > 0) { - if (args[0].equalsIgnoreCase("reload")) { - return new ReloadCommand().onCommand(sender, command, label, args); - } else if (args[0].equalsIgnoreCase("config")) { - //TODO: Allow changing any global/default setting + reloading here - } else { - return new NPCSettingCommand().onCommand(sender, command, label, args); - } - } - return false; - } - -} diff --git a/src/main/java/net/knarcraft/blacksmith/command/BlackSmithConfigCommand.java b/src/main/java/net/knarcraft/blacksmith/command/BlackSmithConfigCommand.java new file mode 100644 index 0000000..4a907b4 --- /dev/null +++ b/src/main/java/net/knarcraft/blacksmith/command/BlackSmithConfigCommand.java @@ -0,0 +1,85 @@ +package net.knarcraft.blacksmith.command; + +import net.knarcraft.blacksmith.BlacksmithPlugin; +import net.knarcraft.blacksmith.config.GlobalSetting; +import net.knarcraft.blacksmith.config.GlobalSettings; +import net.knarcraft.blacksmith.config.NPCSetting; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.enchantments.Enchantment; +import org.jetbrains.annotations.NotNull; + +/** + * The command used for changing global configuration options + */ +public class BlackSmithConfigCommand implements CommandExecutor { + + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, + @NotNull String[] args) { + if (args.length > 0) { + String commandName = args[0]; + if (commandName.equalsIgnoreCase("reload")) { + return new ReloadCommand().onCommand(sender, command, label, args); + } + GlobalSettings settings = BlacksmithPlugin.getInstance().getSettings(); + + //Changing reforge-able items' default isn't recommended + if (commandName.equalsIgnoreCase(NPCSetting.REFORGE_ABLE_ITEMS.getCommandName())) { + sender.sendMessage(ChatColor.DARK_RED + "Changing reforge-able items globally will make every new " + + "blacksmith unable to re-forge anything not in the list, unless it's changed for the " + + "individual NPC. If you really want to change this, change it manually."); + return false; + } + + if (isSpecialCase(settings, commandName, args)) { + return true; + } + + for (GlobalSetting globalSetting : GlobalSetting.values()) { + if (commandName.equalsIgnoreCase(globalSetting.getCommandName())) { + settings.changeValue(globalSetting, args[1]); + return true; + } + } + for (NPCSetting npcSetting : NPCSetting.values()) { + if (commandName.equalsIgnoreCase(npcSetting.getCommandName())) { + settings.changeValue(npcSetting, args[1]); + } + } + } + return false; + } + + /** + * Gets whether the command could be processed as one of the three special cases + * + * @param settingsThe settings to modify
+ * @param commandNameThe sub-command the player specified
+ * @param argsAll arguments given
+ * @returnTrue if already handled as a special case
+ */ + private boolean isSpecialCase(GlobalSettings settings, String commandName, String[] args) { + if (commandName.equalsIgnoreCase(GlobalSetting.BASE_PRICE.getCommandName())) { + settings.setBasePrice(Material.matchMaterial(args[1]), Double.parseDouble(args[2])); + return true; + } + + if (commandName.equalsIgnoreCase(GlobalSetting.PRICE_PER_DURABILITY_POINT.getCommandName())) { + settings.setPricePerDurabilityPoint(Material.matchMaterial(args[1]), Double.parseDouble(args[2])); + return true; + } + + if (commandName.equalsIgnoreCase(GlobalSetting.ENCHANTMENT_COST.getCommandName())) { + settings.setEnchantmentCost(Enchantment.getByKey(NamespacedKey.minecraft(args[1])), Double.parseDouble(args[2])); + return true; + } + + return false; + } + +} diff --git a/src/main/java/net/knarcraft/blacksmith/command/BlackSmithConfigTabCompleter.java b/src/main/java/net/knarcraft/blacksmith/command/BlackSmithConfigTabCompleter.java new file mode 100644 index 0000000..d11b1ff --- /dev/null +++ b/src/main/java/net/knarcraft/blacksmith/command/BlackSmithConfigTabCompleter.java @@ -0,0 +1,64 @@ +package net.knarcraft.blacksmith.command; + +import net.knarcraft.blacksmith.config.GlobalSetting; +import net.knarcraft.blacksmith.config.NPCSetting; +import net.knarcraft.blacksmith.util.TabCompleteValuesHelper; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +/** + * The tab completer for the command used for changing global configuration options + */ +public class BlackSmithConfigTabCompleter implements TabCompleter { + + @Nullable + @Override + public ListThe name of the used command
+ * @returnSome valid options for the command's argument
+ */ + private ListThe blacksmith trait belonging to the selected NPC
+ * @param npcSettingThe NPC setting to change
+ * @param newValueThe value to change the setting to
+ * @param senderThe command sender to notify about results
+ * @returnTrue if everything went successfully
+ */ + private boolean changeNPCSetting(BlacksmithTrait blacksmithTrait, NPCSetting npcSetting, String newValue, + CommandSender sender) { + if (newValue == null) { + //Display the current value of the setting + sender.sendMessage(ChatColor.GREEN + "Current value of " + npcSetting.getCommandName() + ": " + + ChatColor.GOLD + blacksmithTrait.getSettings().getRawValue(npcSetting)); + return true; + } else { + boolean isValidType = TypeValidationHelper.isValid(npcSetting.getValueType(), newValue, sender); + if (isValidType) { + //Change the setting + blacksmithTrait.getSettings().changeSetting(npcSetting, + ChatColor.translateAlternateColorCodes('&', newValue)); + sender.sendMessage(ChatColor.GREEN + npcSetting.getNodeName() + " set to " + ChatColor.GOLD + newValue); + return true; + } else { + return false; + } + } + } + +} diff --git a/src/main/java/net/knarcraft/blacksmith/command/BlackSmithEditTabCompleter.java b/src/main/java/net/knarcraft/blacksmith/command/BlackSmithEditTabCompleter.java new file mode 100644 index 0000000..7bb9464 --- /dev/null +++ b/src/main/java/net/knarcraft/blacksmith/command/BlackSmithEditTabCompleter.java @@ -0,0 +1,57 @@ +package net.knarcraft.blacksmith.command; + +import net.knarcraft.blacksmith.config.NPCSetting; +import net.knarcraft.blacksmith.util.TabCompleteValuesHelper; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +/** + * The tab completer for the blacksmith editing command + */ +public class BlackSmithEditTabCompleter implements TabCompleter { + + @Nullable + @Override + public ListThe name of the used command
+ * @returnSome valid options for the command's argument
+ */ + private ListThis allows specifying a price for each item, by setting base-price.item_name.
*/ - BASE_PRICE("global.base-price.default", 10.0), + BASE_PRICE("global.base-price.default", SettingValueType.POSITIVE_DOUBLE, 10.0, "basePrice"), /** * The base price for each durability point @@ -18,33 +18,39 @@ public enum GlobalSetting { * this is the cost each present durability point will add to the cost. This allows specifying a price per * durability point value for each item, by setting price-per-durability-point.item_name */ - PRICE_PER_DURABILITY_POINT("global.price-per-durability-point.default", 0.005), + PRICE_PER_DURABILITY_POINT("global.price-per-durability-point.default", SettingValueType.POSITIVE_DOUBLE, 0.005, "pricePerDurabilityPoint"), /** * The price increase for each level of each present enchantment * *This can be specified for each possible enchantment by setting enchantment-cost.enchantment_name
*/ - ENCHANTMENT_COST("global.enchantment-cost.default", 5), + ENCHANTMENT_COST("global.enchantment-cost.default", SettingValueType.POSITIVE_DOUBLE, 5.0, "enchantmentCost"), /** * Whether the cost should increase for damage taken, as opposed to increase for durability present */ - NATURAL_COST("global.natural-cost", true); + NATURAL_COST("global.natural-cost", SettingValueType.BOOLEAN, true, "useNaturalCost"); private final String path; private final String parent; + private final String commandName; private final Object value; + private final SettingValueType valueType; /** * Instantiates a new setting * - * @param pathThe full config path for this setting
- * @param valueThe default value of this setting
+ * @param pathThe full config path for this setting
+ * @param valueTypeThe type of value used by this setting
+ * @param valueThe default value of this setting
+ * @param commandNameThe name of the command used to change this setting
*/ - GlobalSetting(String path, Object value) { + GlobalSetting(String path, SettingValueType valueType, Object value, String commandName) { this.path = path; this.value = value; + this.commandName = commandName; + this.valueType = valueType; String[] pathParts = path.split("\\."); this.parent = String.join(".", Arrays.copyOfRange(pathParts, 0, pathParts.length - 1)); } @@ -72,8 +78,26 @@ public enum GlobalSetting { * * @returnThe value of this setting
*/ - Object getDefaultValue() { + public Object getDefaultValue() { return value; } + /** + * The name of the command used to change this setting + * + * @returnThe name of this setting's command
+ */ + public String getCommandName() { + return commandName; + } + + /** + * Gets the value type for this setting + * + * @returnThe value type for this setting
+ */ + public SettingValueType getValueType() { + return this.valueType; + } + } diff --git a/src/main/java/net/knarcraft/blacksmith/config/GlobalSettings.java b/src/main/java/net/knarcraft/blacksmith/config/GlobalSettings.java index 89c2119..f67f3e8 100644 --- a/src/main/java/net/knarcraft/blacksmith/config/GlobalSettings.java +++ b/src/main/java/net/knarcraft/blacksmith/config/GlobalSettings.java @@ -3,6 +3,7 @@ package net.knarcraft.blacksmith.config; import net.citizensnpcs.api.util.DataKey; import net.citizensnpcs.api.util.YamlStorage; import net.knarcraft.blacksmith.BlacksmithPlugin; +import net.knarcraft.blacksmith.util.ConfigHelper; import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.enchantments.Enchantment; @@ -33,8 +34,8 @@ public class GlobalSettings { */ public GlobalSettings(BlacksmithPlugin plugin) { defaultConfig = new YamlStorage(new File(plugin.getDataFolder() + File.separator + "config.yml"), - "Blacksmith Configuration\nWarning: The values under defaults are the values set for a blacksmith" + - "upon creation. To change any values for existing NPCs, edit the citizens NPC file."); + "Blacksmith Configuration\nWarning: The values under defaults are the values set for a " + + "blacksmith upon creation. To change any values for existing NPCs, edit the citizens NPC file."); } /** @@ -62,6 +63,82 @@ public class GlobalSettings { defaultConfig.save(); } + /** + * Changes the value of the given setting + * + * @param globalSettingThe global setting to change
+ * @param newValueThe new value of the setting
+ */ + public void changeValue(GlobalSetting globalSetting, Object newValue) { + globalSettings.put(globalSetting, newValue); + save(); + } + + /** + * Changes the value of the given setting + * + * @param npcSettingThe default NPC setting to change
+ * @param newValueThe new value for the setting
+ */ + public void changeValue(NPCSetting npcSetting, Object newValue) { + defaultNPCSettings.put(npcSetting, newValue); + save(); + } + + /** + * Sets the enchantment cost for the given enchantment + * + * @param enchantmentThe enchantment to set the enchantment cost for
+ * @param newEnchantmentCostThe new enchantment cost
+ */ + public void setEnchantmentCost(Enchantment enchantment, double newEnchantmentCost) { + if (newEnchantmentCost < 0) { + throw new IllegalArgumentException("Enchantment cost cannot be negative!"); + } + if (enchantment == null) { + globalSettings.put(GlobalSetting.ENCHANTMENT_COST, newEnchantmentCost); + } else { + enchantmentCosts.put(enchantment, newEnchantmentCost); + } + save(); + } + + /** + * Sets the price per durability point for the given material + * + * @param materialThe material to set the price per durability point price for
+ * @param newPriceThe new price per durability point price
+ */ + public void setPricePerDurabilityPoint(Material material, double newPrice) { + if (newPrice < 0) { + throw new IllegalArgumentException("Price per durability point cannot be negative!"); + } + if (material == null) { + globalSettings.put(GlobalSetting.PRICE_PER_DURABILITY_POINT, newPrice); + } else { + materialPricePerDurabilityPoints.put(material, newPrice); + } + save(); + } + + /** + * Sets the base price for the given material + * + * @param materialThe material to set the base price for
+ * @param newBasePriceThe new base price
+ */ + public void setBasePrice(Material material, double newBasePrice) { + if (newBasePrice < 0) { + throw new IllegalArgumentException("Base price cannot be negative!"); + } + if (material == null) { + globalSettings.put(GlobalSetting.BASE_PRICE, newBasePrice); + } else { + materialBasePrices.put(material, newBasePrice); + } + save(); + } + /** * Gets the current value of the default NPC settings * @@ -135,12 +212,7 @@ public class GlobalSettings { * @returnThe value of the given setting as a boolean
*/ public boolean asBoolean(GlobalSetting setting) { - Object value = getValue(setting); - if (value instanceof String) { - return Boolean.parseBoolean((String) value); - } else { - return (Boolean) value; - } + return ConfigHelper.asBoolean(getValue(setting)); } /** @@ -152,14 +224,7 @@ public class GlobalSettings { * @returnThe value of the given setting as a double
*/ public double asDouble(GlobalSetting setting) { - Object value = getValue(setting); - if (value instanceof String) { - return Double.parseDouble((String) value); - } else if (value instanceof Integer) { - return (Integer) value; - } else { - return (Double) value; - } + return ConfigHelper.asDouble(getValue(setting)); } /** @@ -256,6 +321,16 @@ public class GlobalSettings { return relevant; } + /** + * Converts a normalized material name to the format used in the config file + * + * @param normalizedNameThe normalized name to un-normalize
+ * @returnThe un-normalized name
+ */ + private String unNormalizeName(String normalizedName) { + return normalizedName.toLowerCase().replace("_", "-"); + } + /** * Loads all default NPC settings * @@ -273,4 +348,41 @@ public class GlobalSettings { } } + /** + * Saves all current settings to the config file + */ + private void save() { + DataKey root = defaultConfig.getKey(""); + //Save all default NPC settings + for (NPCSetting setting : NPCSetting.values()) { + root.setRaw(setting.getPath(), defaultNPCSettings.get(setting)); + } + + //Save all normal global settings + for (GlobalSetting globalSetting : GlobalSetting.values()) { + root.setRaw(globalSetting.getPath(), globalSettings.get(globalSetting)); + } + + //Save all base prices + DataKey basePriceNode = root.getRelative(GlobalSetting.BASE_PRICE.getParent()); + for (Material material : materialBasePrices.keySet()) { + basePriceNode.setRaw(unNormalizeName(material.name()), materialBasePrices.get(material)); + } + + //Save all per-durability-point prices + DataKey basePerDurabilityPriceNode = root.getRelative(GlobalSetting.PRICE_PER_DURABILITY_POINT.getParent()); + for (Material material : materialPricePerDurabilityPoints.keySet()) { + basePerDurabilityPriceNode.setRaw(unNormalizeName(material.name()), materialPricePerDurabilityPoints.get(material)); + } + + //Load all enchantment prices + DataKey enchantmentCostNode = root.getRelative(GlobalSetting.ENCHANTMENT_COST.getParent()); + for (Enchantment enchantment : enchantmentCosts.keySet()) { + enchantmentCostNode.setRaw(unNormalizeName(enchantment.getKey().toString()), enchantmentCosts.get(enchantment)); + } + + //Perform the actual save to disk + defaultConfig.save(); + } + } diff --git a/src/main/java/net/knarcraft/blacksmith/config/NPCSetting.java b/src/main/java/net/knarcraft/blacksmith/config/NPCSetting.java index 44ac759..19b22b3 100644 --- a/src/main/java/net/knarcraft/blacksmith/config/NPCSetting.java +++ b/src/main/java/net/knarcraft/blacksmith/config/NPCSetting.java @@ -7,58 +7,62 @@ import java.util.Arrays; */ public enum NPCSetting { - DROP_ITEM("defaults.drop-item", true, "dropItem"), - DISABLE_COOL_DOWN("defaults.disable-cool-down", false, "disableCoolDown"), - DISABLE_DELAY("defaults.disable-delay", false, "disableDelay"), - FAIL_CHANCE("defaults.percent-chance-to-fail-reforge", 10, "failReforgeChance"), - EXTRA_ENCHANTMENT_CHANCE("defaults.percent-chance-for-extra-enchantment", 5, + DROP_ITEM("defaults.drop-item", SettingValueType.BOOLEAN, true, "dropItem"), + DISABLE_COOL_DOWN("defaults.disable-cool-down", SettingValueType.BOOLEAN, false, "disableCoolDown"), + DISABLE_DELAY("defaults.disable-delay", SettingValueType.BOOLEAN, false, "disableDelay"), + FAIL_CHANCE("defaults.percent-chance-to-fail-reforge", SettingValueType.PERCENTAGE, 10, "failReforgeChance"), + EXTRA_ENCHANTMENT_CHANCE("defaults.percent-chance-for-extra-enchantment", SettingValueType.PERCENTAGE, 5, "extraEnchantmentChance"), - MAX_ENCHANTMENTS("defaults.maximum-enchantments", 3, "maxEnchantments"), - MAX_REFORGE_DELAY("defaults.delays-in-seconds.maximum", 30, "maxReforgeDelay"), - MIN_REFORGE_DELAY("defaults.delays-in-seconds.minimum", 5, "minReforgeDelay"), - REFORGE_COOL_DOWN("defaults.delays-in-seconds.reforge-cool-down", 60, "reforgeCoolDown"), - REFORGE_ABLE_ITEMS("defaults.reforge-able-items", new String[]{}, "reforgeAbleItems"), + MAX_ENCHANTMENTS("defaults.maximum-enchantments", SettingValueType.POSITIVE_INTEGER, 3, "maxEnchantments"), + MAX_REFORGE_DELAY("defaults.delays-in-seconds.maximum", SettingValueType.POSITIVE_INTEGER, 30, "maxReforgeDelay"), + MIN_REFORGE_DELAY("defaults.delays-in-seconds.minimum", SettingValueType.POSITIVE_INTEGER, 5, "minReforgeDelay"), + REFORGE_COOL_DOWN("defaults.delays-in-seconds.reforge-cool-down", SettingValueType.POSITIVE_INTEGER, 60, "reforgeCoolDown"), + REFORGE_ABLE_ITEMS("defaults.reforge-able-items", SettingValueType.STRING_LIST, new String[]{}, "reforgeAbleItems"), /*----------- | Messages | -----------*/ - BUSY_WITH_PLAYER_MESSAGE("defaults.messages.busy-with-player", + BUSY_WITH_PLAYER_MESSAGE("defaults.messages.busy-with-player", SettingValueType.STRING, "§cI'm busy at the moment. Come back later!", "busyPlayerMessage"), - BUSY_WITH_REFORGE_MESSAGE("defaults.messages.busy-with-reforge", "§cI'm working on it. Be patient!", + BUSY_WITH_REFORGE_MESSAGE("defaults.messages.busy-with-reforge", SettingValueType.STRING, "§cI'm working on it. Be patient!", "busyReforgeMessage"), - COOL_DOWN_UNEXPIRED_MESSAGE("defaults.messages.cool-down-not-expired", + COOL_DOWN_UNEXPIRED_MESSAGE("defaults.messages.cool-down-not-expired", SettingValueType.STRING, "§cYou've already had your chance! Give me a break!", "coolDownUnexpiredMessage"), COST_MESSAGE( - "defaults.messages.cost", + "defaults.messages.cost", SettingValueType.STRING, "§eIt will cost §aThe full config path for this setting
+ * @param valueTypeThe type of value used by this setting
* @param valueThe default value of this setting
* @param commandNameThe name of the command used to change this setting
*/ - NPCSetting(String path, Object value, String commandName) { + NPCSetting(String path, SettingValueType valueType, Object value, String commandName) { this.path = path; this.value = value; + this.valueType = valueType; String[] pathParts = path.split("\\."); this.childPath = String.join(".", Arrays.copyOfRange(pathParts, 1, pathParts.length)); this.commandName = commandName; @@ -114,4 +118,13 @@ public enum NPCSetting { return nodeName; } + /** + * Gets the value type for this setting + * + * @returnThe value type for this setting
+ */ + public SettingValueType getValueType() { + return this.valueType; + } + } diff --git a/src/main/java/net/knarcraft/blacksmith/config/NPCSettings.java b/src/main/java/net/knarcraft/blacksmith/config/NPCSettings.java index 4209bec..54f047c 100644 --- a/src/main/java/net/knarcraft/blacksmith/config/NPCSettings.java +++ b/src/main/java/net/knarcraft/blacksmith/config/NPCSettings.java @@ -3,6 +3,7 @@ package net.knarcraft.blacksmith.config; import net.citizensnpcs.api.util.DataKey; import net.knarcraft.blacksmith.BlacksmithPlugin; import net.knarcraft.blacksmith.trait.BlacksmithTrait; +import net.knarcraft.blacksmith.util.ConfigHelper; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; @@ -61,6 +62,9 @@ public class NPCSettings { * @param newValueThe new value of the setting
*/ public void changeSetting(NPCSetting setting, Object newValue) { + if (setting == NPCSetting.REFORGE_ABLE_ITEMS) { + newValue = replaceReforgeAblePlaceholders(newValue); + } currentValues.put(setting, newValue); } @@ -265,12 +269,7 @@ public class NPCSettings { * @returnThe value of the given setting as a boolean
*/ private boolean asBoolean(NPCSetting setting) { - Object value = getValue(setting); - if (value instanceof String) { - return Boolean.parseBoolean((String) value); - } else { - return (Boolean) value; - } + return ConfigHelper.asBoolean(getValue(setting)); } /** @@ -282,12 +281,7 @@ public class NPCSettings { * @returnThe value of the given setting as an integer
*/ private int asInt(NPCSetting setting) { - Object value = getValue(setting); - if (value instanceof String) { - return Integer.parseInt((String) value); - } else { - return (Integer) value; - } + return ConfigHelper.asInt(getValue(setting)); } /** @@ -322,6 +316,31 @@ public class NPCSettings { return value; } + /** + * Replaces placeholders in the given reforge-able value + * + * @param valueThe value specified by a user
+ * @returnThe value with placeholders replaced
+ */ + private Object replaceReforgeAblePlaceholders(Object value) { + if (value instanceof String string) { + String[] list = string.split(","); + ListThe string that might be a placeholder
+ * @returnThe string, possibly with the placeholder replaced
+ */ + public static String replacePlaceholder(String possiblePlaceholder) { + for (SmithPreset smithPreset : SmithPreset.values()) { + if (possiblePlaceholder.equalsIgnoreCase("preset:" + smithPreset.name())) { + return String.join(",", smithPreset.getMaterialNames()); + } + } + return possiblePlaceholder; + } + + /** + * Gets all materials included in this preset + * + * @returnAll materials in this preset
+ */ + public ListAll ranged weapon materials
+ */ + private ListAll tool materials
+ */ + private ListAll weapon materials
+ */ + private ListAll sword materials
+ */ + private ListThe string to look for
+ * @returnThe resulting materials
+ */ + private ListAll material names for this smith
+ */ + private ListTrue if the given item is a type of armor
*/ public static boolean isArmor(ItemStack item) { - return EnchantmentTarget.WEARABLE.includes(item); - //TODO: Remove this commented-out code if the above line works - /*return switch (item.getType()) { - case LEATHER_HELMET, LEATHER_CHESTPLATE, LEATHER_LEGGINGS, LEATHER_BOOTS, CHAINMAIL_HELMET, - CHAINMAIL_CHESTPLATE, CHAINMAIL_LEGGINGS, CHAINMAIL_BOOTS, GOLDEN_HELMET, GOLDEN_CHESTPLATE, - GOLDEN_LEGGINGS, GOLDEN_BOOTS, IRON_HELMET, IRON_CHESTPLATE, IRON_LEGGINGS, IRON_BOOTS, - DIAMOND_HELMET, DIAMOND_CHESTPLATE, DIAMOND_LEGGINGS, DIAMOND_BOOTS, TURTLE_HELMET, ELYTRA, - NETHERITE_HELMET, NETHERITE_CHESTPLATE, NETHERITE_LEGGINGS, NETHERITE_BOOTS -> true; - default -> false; - };*/ + return EnchantmentTarget.WEARABLE.includes(item) || item.getType() == Material.ELYTRA; } /** diff --git a/src/main/java/net/knarcraft/blacksmith/trait/ReforgeSession.java b/src/main/java/net/knarcraft/blacksmith/trait/ReforgeSession.java index fcdc05e..ace17bc 100644 --- a/src/main/java/net/knarcraft/blacksmith/trait/ReforgeSession.java +++ b/src/main/java/net/knarcraft/blacksmith/trait/ReforgeSession.java @@ -68,13 +68,14 @@ public class ReforgeSession implements Runnable { //Give the item back to the player if (!config.getDisableDelay()) { - //If the player isn't online, drop the item to prevent it from disappearing - if (config.getDropItem() || !player.isOnline()) { + //If the player isn't online, or the player cannot fit the item, drop the item to prevent it from disappearing + if (config.getDropItem() || !player.isOnline() || player.getInventory().firstEmpty() == -1) { player.getWorld().dropItemNaturally(npc.getEntity().getLocation(), itemToReforge); } else { player.getInventory().addItem(itemToReforge); } } else { + //It can be assumed as this happens instantly, that the player still has the item's previous slot selected player.getInventory().setItemInMainHand(itemToReforge); } diff --git a/src/main/java/net/knarcraft/blacksmith/util/ConfigHelper.java b/src/main/java/net/knarcraft/blacksmith/util/ConfigHelper.java new file mode 100644 index 0000000..e303630 --- /dev/null +++ b/src/main/java/net/knarcraft/blacksmith/util/ConfigHelper.java @@ -0,0 +1,62 @@ +package net.knarcraft.blacksmith.util; + +/** + * A helper class for getting an object value as the correct type + */ +public final class ConfigHelper { + + private ConfigHelper() { + + } + + /** + * Gets the given value as a double + * + *This will throw an exception if used for a non-double setting
+ * + * @param valueThe object value to get
+ * @returnThe value of the given object as a double
+ */ + public static double asDouble(Object value) { + if (value instanceof String) { + return Double.parseDouble((String) value); + } else if (value instanceof Integer) { + return (Integer) value; + } else { + return (Double) value; + } + } + + /** + * Gets the given value as a boolean + * + *This will throw an exception if used for a non-boolean value
+ * + * @param valueThe object value to get
+ * @returnThe value of the given object as a boolean
+ */ + public static boolean asBoolean(Object value) { + if (value instanceof String) { + return Boolean.parseBoolean((String) value); + } else { + return (Boolean) value; + } + } + + /** + * Gets the given value as an integer + * + *This will throw an exception if used for a non-integer setting
+ * + * @param valueThe object value to get
+ * @returnThe value of the given object as an integer
+ */ + public static int asInt(Object value) { + if (value instanceof String) { + return Integer.parseInt((String) value); + } else { + return (Integer) value; + } + } + +} diff --git a/src/main/java/net/knarcraft/blacksmith/util/TabCompleteValuesHelper.java b/src/main/java/net/knarcraft/blacksmith/util/TabCompleteValuesHelper.java new file mode 100644 index 0000000..572cd7c --- /dev/null +++ b/src/main/java/net/knarcraft/blacksmith/util/TabCompleteValuesHelper.java @@ -0,0 +1,121 @@ +package net.knarcraft.blacksmith.util; + +import net.knarcraft.blacksmith.config.SettingValueType; + +import java.util.ArrayList; +import java.util.List; + +/** + * A helper class for getting valid values for tab-completion + */ +public final class TabCompleteValuesHelper { + + private TabCompleteValuesHelper() { + + } + + /** + * Gets tab-completion values for the given value type + * + * @param valueTypeThe value type to get possible values for
+ * @returnThe values to show the user
+ */ + public static ListSome example possible values for reforge-able materials
+ */ + private static ListSome example string values
+ */ + private static ListSome example percentage values
+ */ + private static ListSome possible positive doubles
+ */ + private static ListSome example positive integers
+ */ + private static ListThe possible boolean values
+ */ + private static ListThe value type required
+ * @param valueThe value given
+ * @param senderThe command sender to use for printing error messages
+ * @returnTrue if the value is valid
+ */ + public static boolean isValid(SettingValueType valueType, Object value, CommandSender sender) { + try { + return switch (valueType) { + case POSITIVE_DOUBLE -> isPositiveDouble(value, sender); + case STRING -> isNonEmptyString(value, sender); + case POSITIVE_INTEGER -> isPositiveInteger(value, sender); + case PERCENTAGE -> isPercentage(value, sender); + case BOOLEAN -> true; + case STRING_LIST -> isStringList(value, sender); + }; + } catch (ClassCastException exception) { + //This error signifies that an object is not a string, and of the wrong class + return false; + } + } + + /** + * Checks whether the given value is a string list + * + * @param valueThe value to check
+ * @param senderThe command sender to use for printing error messages
+ * @returnTrue if the value is a string list
+ */ + private static boolean isStringList(Object value, CommandSender sender) { + boolean isStringList = value instanceof String[] || value instanceof String; + if (!isStringList && sender != null) { + sender.sendMessage(ChatColor.DARK_RED + "A string list is required!"); + } + return isStringList; + } + + /** + * Checks whether the given value is a valid percentage + * + * @param valueThe value to check
+ * @param senderThe command sender to use for printing error messages
+ * @returnTrue if the value is a percentage
+ */ + private static boolean isPercentage(Object value, CommandSender sender) { + try { + int intValue = ConfigHelper.asInt(value); + return intValue > 0 && intValue <= 100; + } catch (NumberFormatException | NullPointerException exception) { + if (sender != null) { + sender.sendMessage(ChatColor.DARK_RED + "You specified a value which isn't between 0 and 100!"); + } + return false; + } + } + + /** + * Checks whether the given value is a non-empty string + * + * @param valueThe value to check
+ * @param senderThe command sender to use for printing error messages
+ * @returnTrue if the value is a non-empty string
+ */ + private static boolean isNonEmptyString(Object value, CommandSender sender) { + boolean isString = value instanceof String string && !string.strip().isEmpty(); + if (!isString && sender != null) { + sender.sendMessage(ChatColor.DARK_RED + "A non-empty string is required!"); + } + return isString; + } + + /** + * Checks whether the given value is a positive double + * + * @param valueThe value to check
+ * @param senderThe command sender to use for printing error messages
+ * @returnTrue if the value is a positive double
+ */ + private static boolean isPositiveDouble(Object value, CommandSender sender) { + try { + return ConfigHelper.asDouble(value) > 0.0; + } catch (NumberFormatException | NullPointerException exception) { + if (sender != null) { + sender.sendMessage(ChatColor.DARK_RED + "You specified a value which isn't a positive double!"); + } + return false; + } + } + + /** + * Checks whether the given value is a positive integer + * + * @param valueThe value to check
+ * @param senderThe command sender to use for printing error messages
+ * @returnTrue if the value is a positive integer
+ */ + private static boolean isPositiveInteger(Object value, CommandSender sender) { + try { + return ConfigHelper.asInt(value) > 0; + } catch (NumberFormatException | NullPointerException exception) { + if (sender != null) { + sender.sendMessage(ChatColor.DARK_RED + "You specified a value which isn't a positive integer!"); + } + return false; + } + } + +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 91b1228..ac72b0a 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -9,9 +9,19 @@ api-version: 1.18 commands: blacksmith: + permission: blacksmith.edit + usage: /