Writes a lot of code necessary for the scrapper

Adds the classes necessary for the new scrapper
Partly implements some scrapper functionality
Restructures some classes to reduce code duplication
Moves some classes to make some classes easier to find
Adds a bunch of TODOs where things have an unfinished implementation
This commit is contained in:
Kristian Knarvik 2023-11-14 16:04:48 +01:00
parent 1938a690c6
commit f3f3f66c38
40 changed files with 1788 additions and 594 deletions

16
pom.xml
View File

@ -19,10 +19,6 @@
<!-- Repositories -->
<repositories>
<repository>
<id>knarcraft-repo</id>
<url>https://git.knarcraft.net/api/packages/EpicKnarvik97/maven</url>
</repository>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
@ -39,6 +35,10 @@
<id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
<repository>
<id>knarcraft-repo</id>
<url>https://git.knarcraft.net/api/packages/EpicKnarvik97/maven</url>
</repository>
</repositories>
<distributionManagement>
<repository>
@ -69,7 +69,7 @@
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.20.1-R0.1-SNAPSHOT</version>
<version>1.20.2-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
@ -87,7 +87,7 @@
<dependency>
<groupId>net.knarcraft</groupId>
<artifactId>knarlib</artifactId>
<version>1.2.3</version>
<version>1.2.4</version>
<scope>compile</scope>
</dependency>
<dependency>
@ -98,8 +98,8 @@
</dependency>
<dependency>
<groupId>com.github.seeseemelk</groupId>
<artifactId>MockBukkit-v1.19</artifactId>
<version>2.145.0</version>
<artifactId>MockBukkit-v1.20</artifactId>
<version>3.9.0</version>
<scope>test</scope>
</dependency>
</dependencies>

View File

@ -1,13 +1,18 @@
package net.knarcraft.blacksmith;
import net.citizensnpcs.api.CitizensAPI;
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.command.PresetCommand;
import net.knarcraft.blacksmith.command.PresetTabCompleter;
import net.knarcraft.blacksmith.command.blacksmith.BlackSmithConfigCommand;
import net.knarcraft.blacksmith.command.blacksmith.BlackSmithConfigTabCompleter;
import net.knarcraft.blacksmith.command.blacksmith.BlackSmithEditCommand;
import net.knarcraft.blacksmith.command.blacksmith.BlackSmithEditTabCompleter;
import net.knarcraft.blacksmith.command.scrapper.ScrapperConfigCommand;
import net.knarcraft.blacksmith.command.scrapper.ScrapperConfigTabCompleter;
import net.knarcraft.blacksmith.command.scrapper.ScrapperEditCommand;
import net.knarcraft.blacksmith.command.scrapper.ScrapperEditTabCompleter;
import net.knarcraft.blacksmith.config.blacksmith.GlobalBlacksmithSettings;
import net.knarcraft.blacksmith.config.scrapper.GlobalScrapperSettings;
import net.knarcraft.blacksmith.formatting.BlacksmithTranslatableMessage;
import net.knarcraft.blacksmith.listener.NPCClickListener;
import net.knarcraft.blacksmith.listener.PlayerListener;
@ -17,12 +22,16 @@ import net.knarcraft.knarlib.formatting.StringFormatter;
import net.knarcraft.knarlib.formatting.TranslatableTimeUnit;
import net.knarcraft.knarlib.formatting.Translator;
import net.knarcraft.knarlib.util.UpdateChecker;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.PluginCommand;
import org.bukkit.command.TabCompleter;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.plugin.java.JavaPluginLoader;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.logging.Level;
@ -33,7 +42,8 @@ import java.util.logging.Level;
public class BlacksmithPlugin extends JavaPlugin {
private static BlacksmithPlugin instance;
private GlobalBlacksmithSettings config;
private GlobalBlacksmithSettings blacksmithConfig;
private GlobalScrapperSettings scrapperConfig;
private static Translator translator;
private static StringFormatter stringFormatter;
@ -49,7 +59,8 @@ public class BlacksmithPlugin extends JavaPlugin {
* Constructor required for MockBukkit
*/
@SuppressWarnings("unused")
protected BlacksmithPlugin(JavaPluginLoader loader, PluginDescriptionFile descriptionFile, File dataFolder, File file) {
protected BlacksmithPlugin(@NotNull JavaPluginLoader loader, @NotNull PluginDescriptionFile descriptionFile,
@NotNull File dataFolder, @NotNull File file) {
super(loader, descriptionFile, dataFolder, file);
}
@ -58,17 +69,26 @@ public class BlacksmithPlugin extends JavaPlugin {
*
* @return <p>An instance of the blacksmith plugin</p>
*/
public static BlacksmithPlugin getInstance() {
public static @NotNull BlacksmithPlugin getInstance() {
return instance;
}
/**
* Gets settings for the blacksmith plugin
* Gets settings for the blacksmith plugin's blacksmiths
*
* @return <p>Settings for the blacksmith plugin</p>
* @return <p>Settings for the blacksmith plugin's blacksmith</p>
*/
public GlobalBlacksmithSettings getSettings() {
return config;
public @NotNull GlobalBlacksmithSettings getGlobalBlacksmithSettings() {
return blacksmithConfig;
}
/**
* Gets settings for the blacksmith plugin's scrapper
*
* @return <p>Settings for the blacksmith plugin's scrapper</p>
*/
public @NotNull GlobalScrapperSettings getGlobalScrapperSettings() {
return scrapperConfig;
}
/**
@ -76,8 +96,10 @@ public class BlacksmithPlugin extends JavaPlugin {
*/
public void reload() {
this.reloadConfig();
config.load();
translator.loadLanguages(this.getDataFolder(), "en", this.getConfig().getString("language", "en"));
blacksmithConfig.load();
scrapperConfig.load();
translator.loadLanguages(this.getDataFolder(), "en",
this.getConfig().getString("language", "en"));
}
/**
@ -85,7 +107,7 @@ public class BlacksmithPlugin extends JavaPlugin {
*
* @return <p>The translator to use</p>
*/
public static Translator getTranslator() {
public static @NotNull Translator getTranslator() {
return BlacksmithPlugin.translator;
}
@ -94,7 +116,7 @@ public class BlacksmithPlugin extends JavaPlugin {
*
* @return <p>The string formatter to use</p>
*/
public static StringFormatter getStringFormatter() {
public static @NotNull StringFormatter getStringFormatter() {
return BlacksmithPlugin.stringFormatter;
}
@ -114,23 +136,8 @@ public class BlacksmithPlugin extends JavaPlugin {
this.reloadConfig();
this.saveConfig();
//Load settings
config = new GlobalBlacksmithSettings(this);
config.load();
//Prepare the translator
translator = new Translator();
translator.registerMessageCategory(TranslatableTimeUnit.UNIT_SECOND);
translator.registerMessageCategory(BlacksmithTranslatableMessage.ITEM_TYPE_ENCHANTMENT);
translator.loadLanguages(this.getDataFolder(), "en", fileConfiguration.getString("language", "en"));
PluginDescriptionFile description = this.getDescription();
String prefix;
if (description.getPrefix() == null) {
prefix = "Blacksmith";
} else {
prefix = description.getPrefix();
}
BlacksmithPlugin.stringFormatter = new StringFormatter(prefix, translator);
// Initialize custom configuration files
initializeConfigurations(fileConfiguration);
//Set up Vault integration
if (!setUpVault()) {
@ -153,6 +160,34 @@ public class BlacksmithPlugin extends JavaPlugin {
() -> this.getDescription().getVersion(), null);
}
/**
* Initializes custom configuration and translation
*
* @param fileConfiguration <p>The configuration file to get values from</p>
*/
private void initializeConfigurations(@NotNull FileConfiguration fileConfiguration) {
//Load settings
blacksmithConfig = new GlobalBlacksmithSettings(this);
blacksmithConfig.load();
scrapperConfig = new GlobalScrapperSettings(this);
scrapperConfig.load();
//Prepare the translator
translator = new Translator();
translator.registerMessageCategory(TranslatableTimeUnit.UNIT_SECOND);
translator.registerMessageCategory(BlacksmithTranslatableMessage.ITEM_TYPE_ENCHANTMENT);
translator.loadLanguages(this.getDataFolder(), "en",
fileConfiguration.getString("language", "en"));
PluginDescriptionFile description = this.getDescription();
String prefix;
if (description.getPrefix() == null) {
prefix = "Blacksmith";
} else {
prefix = description.getPrefix();
}
BlacksmithPlugin.stringFormatter = new StringFormatter(prefix, translator);
}
/**
* Tries to set up Vault
*
@ -182,24 +217,28 @@ public class BlacksmithPlugin extends JavaPlugin {
* 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());
}
registerCommand("blacksmith", new BlackSmithEditCommand(), new BlackSmithEditTabCompleter());
registerCommand("blacksmithConfig", new BlackSmithConfigCommand(), new BlackSmithConfigTabCompleter());
registerCommand("scrapper", new ScrapperEditCommand(), new ScrapperEditTabCompleter());
registerCommand("scrapperConfig", new ScrapperConfigCommand(), new ScrapperConfigTabCompleter());
registerCommand("preset", new PresetCommand(), new PresetTabCompleter());
}
//Register the global config edit command
PluginCommand blacksmithConfigCommand = this.getCommand("blacksmithConfig");
if (blacksmithConfigCommand != null) {
blacksmithConfigCommand.setExecutor(new BlackSmithConfigCommand());
blacksmithConfigCommand.setTabCompleter(new BlackSmithConfigTabCompleter());
}
PluginCommand presetCommand = this.getCommand("preset");
if (presetCommand != null) {
presetCommand.setExecutor(new PresetCommand());
presetCommand.setTabCompleter(new PresetTabCompleter());
/**
* Registers a command
*
* @param commandName <p>The name of the command</p>
* @param executor <p>The executor to bind to the command</p>
* @param tabCompleter <p>The tab completer to bind to the command, or null</p>
*/
private void registerCommand(@NotNull String commandName, @NotNull CommandExecutor executor,
@Nullable TabCompleter tabCompleter) {
PluginCommand command = this.getCommand(commandName);
if (command != null) {
command.setExecutor(executor);
if (tabCompleter != null) {
command.setTabCompleter(tabCompleter);
}
}
}

View File

@ -24,10 +24,9 @@ public class ReloadCommand implements TabExecutor {
return true;
}
@Nullable
@Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] args) {
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
@NotNull String label, @NotNull String[] args) {
return new ArrayList<>();
}

View File

@ -1,10 +1,11 @@
package net.knarcraft.blacksmith.command;
package net.knarcraft.blacksmith.command.blacksmith;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.command.ReloadCommand;
import net.knarcraft.blacksmith.config.SettingValueType;
import net.knarcraft.blacksmith.config.blacksmith.BlacksmithNPCSetting;
import net.knarcraft.blacksmith.config.blacksmith.GlobalBlacksmithSetting;
import net.knarcraft.blacksmith.config.blacksmith.GlobalBlacksmithSettings;
import net.knarcraft.blacksmith.config.blacksmith.BlacksmithNPCSetting;
import net.knarcraft.blacksmith.config.SettingValueType;
import net.knarcraft.blacksmith.formatting.BlacksmithTranslatableMessage;
import net.knarcraft.blacksmith.formatting.ItemType;
import net.knarcraft.blacksmith.util.InputParsingHelper;
@ -17,6 +18,7 @@ import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.enchantments.Enchantment;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
@ -38,7 +40,7 @@ public class BlackSmithConfigCommand implements CommandExecutor {
if (commandName.equalsIgnoreCase("reload")) {
return new ReloadCommand().onCommand(sender, command, label, args);
}
GlobalBlacksmithSettings settings = BlacksmithPlugin.getInstance().getSettings();
GlobalBlacksmithSettings settings = BlacksmithPlugin.getInstance().getGlobalBlacksmithSettings();
//Find which global setting the user has specified, if any
GlobalBlacksmithSetting detectedGlobalBlacksmithSetting = null;
@ -86,15 +88,16 @@ public class BlackSmithConfigCommand implements CommandExecutor {
/**
* Changes the value of the setting defined in the user's input
*
* @param args <p>The arguments given by the user</p>
* @param args <p>The arguments given by the user</p>
* @param detectedGlobalBlacksmithSetting <p>The global setting recognized from the input, if any</p>
* @param detectedBlacksmithNPCSetting <p>The NPC setting recognized from the input, if any</p>
* @param settings <p>The global settings object to get settings from</p>
* @param sender <p>The command sender to display any output to</p>
* @param settings <p>The global settings object to get settings from</p>
* @param sender <p>The command sender to display any output to</p>
* @return <p>True if the value was successfully changed</p>
*/
private boolean changeValue(String[] args, GlobalBlacksmithSetting detectedGlobalBlacksmithSetting, BlacksmithNPCSetting detectedBlacksmithNPCSetting,
GlobalBlacksmithSettings settings, CommandSender sender) {
private boolean changeValue(@NotNull String[] args, @Nullable GlobalBlacksmithSetting detectedGlobalBlacksmithSetting,
@Nullable BlacksmithNPCSetting detectedBlacksmithNPCSetting,
@NotNull GlobalBlacksmithSettings settings, @NotNull CommandSender sender) {
String newValue = args[1];
if (detectedGlobalBlacksmithSetting != null) {
settings.changeValue(detectedGlobalBlacksmithSetting, newValue);
@ -120,12 +123,13 @@ public class BlackSmithConfigCommand implements CommandExecutor {
*
* @param detectedGlobalBlacksmithSetting <p>The global setting recognized from the input, if any</p>
* @param detectedBlacksmithNPCSetting <p>The NPC setting recognized from the input, if any</p>
* @param settings <p>The global settings object to get settings from</p>
* @param sender <p>The command sender to display any output to</p>
* @param settings <p>The global settings object to get settings from</p>
* @param sender <p>The command sender to display any output to</p>
* @return <p>True if a settings was successfully displayed</p>
*/
private boolean displayCurrentValue(GlobalBlacksmithSetting detectedGlobalBlacksmithSetting, BlacksmithNPCSetting detectedBlacksmithNPCSetting,
GlobalBlacksmithSettings settings, CommandSender sender) {
private boolean displayCurrentValue(@Nullable GlobalBlacksmithSetting detectedGlobalBlacksmithSetting,
@Nullable BlacksmithNPCSetting detectedBlacksmithNPCSetting,
@NotNull GlobalBlacksmithSettings settings, @NotNull CommandSender sender) {
String settingValue;
String correctCommandName;
boolean printRawValue = false;
@ -165,8 +169,9 @@ public class BlackSmithConfigCommand implements CommandExecutor {
* @param settings <p>The settings object to query</p>
* @return <p>True if the value was successfully displayed</p>
*/
private boolean displaySpecialCaseValue(String selector, CommandSender sender, GlobalBlacksmithSetting setting,
GlobalBlacksmithSettings settings) {
private boolean displaySpecialCaseValue(@NotNull String selector, @NotNull CommandSender sender,
@Nullable GlobalBlacksmithSetting setting,
@NotNull GlobalBlacksmithSettings settings) {
if (setting == GlobalBlacksmithSetting.BASE_PRICE || setting == GlobalBlacksmithSetting.PRICE_PER_DURABILITY_POINT) {
Material material = InputParsingHelper.matchMaterial(selector);
if (material == null) {
@ -203,7 +208,7 @@ public class BlackSmithConfigCommand implements CommandExecutor {
* @param commandName <p>The command specified</p>
* @return <p>True if the command is a special case</p>
*/
private boolean isSpecialCase(String commandName) {
private boolean isSpecialCase(@NotNull String commandName) {
return commandName.equalsIgnoreCase(GlobalBlacksmithSetting.BASE_PRICE.getCommandName()) ||
commandName.equalsIgnoreCase(GlobalBlacksmithSetting.PRICE_PER_DURABILITY_POINT.getCommandName()) ||
commandName.equalsIgnoreCase(GlobalBlacksmithSetting.ENCHANTMENT_COST.getCommandName());
@ -212,13 +217,15 @@ public class BlackSmithConfigCommand implements CommandExecutor {
/**
* Updates a special-case configuration value if a special-case is encountered
*
* @param settings <p>The settings to modify</p>
* @param settings <p>The settings to modify</p>
* @param detectedGlobalBlacksmithSetting <p>The global setting specified</p>
* @param args <p>All arguments given</p>
* @param sender <p>The command sender to notify if successful</p>
* @param args <p>All arguments given</p>
* @param sender <p>The command sender to notify if successful</p>
* @return <p>True if already handled as a special case</p>
*/
private boolean updateSpecialCase(GlobalBlacksmithSettings settings, GlobalBlacksmithSetting detectedGlobalBlacksmithSetting, String[] args,
private boolean updateSpecialCase(@NotNull GlobalBlacksmithSettings settings,
@Nullable GlobalBlacksmithSetting detectedGlobalBlacksmithSetting,
@NotNull String[] args,
CommandSender sender) {
if (InputParsingHelper.isEmpty(args[2])) {
args[2] = "-1";
@ -252,15 +259,17 @@ public class BlackSmithConfigCommand implements CommandExecutor {
/**
* Updates a special case price configuration value if a special case is encountered
*
* @param settings <p>The settings to modify</p>
* @param settings <p>The settings to modify</p>
* @param detectedGlobalBlacksmithSetting <p>The global setting specified</p>
* @param materialName <p>The material name to update the price for</p>
* @param newPrice <p>The new price to update to</p>
* @param sender <p>The command sender to respond to</p>
* @param materialName <p>The material name to update the price for</p>
* @param newPrice <p>The new price to update to</p>
* @param sender <p>The command sender to respond to</p>
* @return <p>True if the input was valid, and the item(s) was/were updated</p>
*/
private boolean updatePriceSpecialCase(GlobalBlacksmithSettings settings, GlobalBlacksmithSetting detectedGlobalBlacksmithSetting,
String materialName, double newPrice, CommandSender sender) {
private boolean updatePriceSpecialCase(@NotNull GlobalBlacksmithSettings settings,
@NotNull GlobalBlacksmithSetting detectedGlobalBlacksmithSetting,
@NotNull String materialName, double newPrice,
@NotNull CommandSender sender) {
ItemType itemType = ItemType.MATERIAL;
String itemChanged;
//Update base price or price per durability point for an item
@ -289,13 +298,14 @@ public class BlackSmithConfigCommand implements CommandExecutor {
/**
* Updates all materials matching the material name wildcard
*
* @param settings <p>The settings to modify</p>
* @param settings <p>The settings to modify</p>
* @param detectedGlobalBlacksmithSetting <p>The global setting specified</p>
* @param materialName <p>The wildcard material name to update cost for</p>
* @param newPrice <p>The new cost for the matched items</p>
* @param materialName <p>The wildcard material name to update cost for</p>
* @param newPrice <p>The new cost for the matched items</p>
*/
private void updateAllMatchedPrices(GlobalBlacksmithSettings settings, GlobalBlacksmithSetting detectedGlobalBlacksmithSetting,
String materialName, double newPrice) {
private void updateAllMatchedPrices(@NotNull GlobalBlacksmithSettings settings,
@NotNull GlobalBlacksmithSetting detectedGlobalBlacksmithSetting,
@NotNull String materialName, double newPrice) {
String search = InputParsingHelper.regExIfy(materialName);
for (Material material : ItemHelper.getAllReforgeAbleMaterials()) {
if (!material.name().matches(search)) {

View File

@ -1,8 +1,8 @@
package net.knarcraft.blacksmith.command;
package net.knarcraft.blacksmith.command.blacksmith;
import net.knarcraft.blacksmith.config.blacksmith.GlobalBlacksmithSetting;
import net.knarcraft.blacksmith.config.blacksmith.BlacksmithNPCSetting;
import net.knarcraft.blacksmith.config.SettingValueType;
import net.knarcraft.blacksmith.config.blacksmith.BlacksmithNPCSetting;
import net.knarcraft.blacksmith.config.blacksmith.GlobalBlacksmithSetting;
import net.knarcraft.blacksmith.util.InputParsingHelper;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
@ -66,7 +66,7 @@ public class BlackSmithConfigTabCompleter implements TabCompleter {
* @param args <p>The arguments given by the user</p>
* @return <p>Null if not writing a spaced message</p>
*/
private List<String> skipCompletionForSpacedMessage(String[] args) {
private List<String> skipCompletionForSpacedMessage(@NotNull String[] args) {
if (args.length > 2) {
BlacksmithNPCSetting blacksmithNpcSetting = null;
for (BlacksmithNPCSetting setting : BlacksmithNPCSetting.values()) {
@ -86,10 +86,11 @@ public class BlackSmithConfigTabCompleter implements TabCompleter {
* Gets tab-completions for a selected material or enchantment
*
* @param globalBlacksmithSetting <p>The global setting to get tab-completions for</p>
* @param args <p>The arguments given by the user</p>
* @param args <p>The arguments given by the user</p>
* @return <p>The tab-completions to show to the user</p>
*/
private List<String> getPerTypeTabCompletions(GlobalBlacksmithSetting globalBlacksmithSetting, String[] args) {
private List<String> getPerTypeTabCompletions(@NotNull GlobalBlacksmithSetting globalBlacksmithSetting,
@NotNull String[] args) {
//Display possible tab-completions only if a valid enchantment or material is provided
if (((globalBlacksmithSetting == GlobalBlacksmithSetting.BASE_PRICE ||
globalBlacksmithSetting == GlobalBlacksmithSetting.PRICE_PER_DURABILITY_POINT) &&
@ -109,7 +110,7 @@ public class BlackSmithConfigTabCompleter implements TabCompleter {
* @param commandValue <p>The command value used to filter tab-completions</p>
* @return <p>Some valid options for the command's argument</p>
*/
private List<String> tabCompleteCommandValues(String commandName, String commandValue) {
private List<String> tabCompleteCommandValues(@NotNull String commandName, @NotNull String commandValue) {
if (commandName.equalsIgnoreCase("reload")) {
return new ArrayList<>();
}
@ -131,12 +132,15 @@ public class BlackSmithConfigTabCompleter implements TabCompleter {
* Gets tab-completions for the given global setting and filters on the command value
*
* @param globalBlacksmithSetting <p>The global setting to get tab-completions for</p>
* @param commandValue <p>The command value used to filter between available tab-completions</p>
* @param commandValue <p>The command value used to filter between available tab-completions</p>
* @return <p>The available tab-completions</p>
*/
private List<String> getCompletions(GlobalBlacksmithSetting globalBlacksmithSetting, String commandValue) {
List<String> returnValues = filterMatchingContains(getTabCompletions(globalBlacksmithSetting.getValueType()), commandValue);
if (globalBlacksmithSetting == GlobalBlacksmithSetting.BASE_PRICE || globalBlacksmithSetting == GlobalBlacksmithSetting.PRICE_PER_DURABILITY_POINT) {
private List<String> getCompletions(@NotNull GlobalBlacksmithSetting globalBlacksmithSetting,
@NotNull String commandValue) {
List<String> returnValues = filterMatchingContains(
getTabCompletions(globalBlacksmithSetting.getValueType()), commandValue);
if (globalBlacksmithSetting == GlobalBlacksmithSetting.BASE_PRICE ||
globalBlacksmithSetting == GlobalBlacksmithSetting.PRICE_PER_DURABILITY_POINT) {
returnValues.addAll(filterMatchingContains(getTabCompletions(SettingValueType.MATERIAL), commandValue));
} else if (globalBlacksmithSetting == GlobalBlacksmithSetting.ENCHANTMENT_COST) {
returnValues.addAll(filterMatchingContains(getTabCompletions(SettingValueType.ENCHANTMENT), commandValue));

View File

@ -1,10 +1,10 @@
package net.knarcraft.blacksmith.command;
package net.knarcraft.blacksmith.command.blacksmith;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.npc.NPC;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.config.blacksmith.BlacksmithNPCSetting;
import net.knarcraft.blacksmith.config.SettingValueType;
import net.knarcraft.blacksmith.config.blacksmith.BlacksmithNPCSetting;
import net.knarcraft.blacksmith.formatting.BlacksmithTranslatableMessage;
import net.knarcraft.blacksmith.trait.BlacksmithTrait;
import net.knarcraft.blacksmith.util.InputParsingHelper;
@ -14,6 +14,7 @@ import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
@ -58,14 +59,16 @@ public class BlackSmithEditCommand implements CommandExecutor {
/**
* Changes the given NPC setting, or displays the current value if a new value isn't specified
*
* @param blacksmithTrait <p>The blacksmith trait belonging to the selected NPC</p>
* @param blacksmithNpcSetting <p>The NPC setting to change</p>
* @param newValue <p>The value to change the setting to</p>
* @param sender <p>The command sender to notify about results</p>
* @param blacksmithTrait <p>The blacksmith trait belonging to the selected NPC</p>
* @param blacksmithNpcSetting <p>The NPC setting to change</p>
* @param newValue <p>The value to change the setting to</p>
* @param sender <p>The command sender to notify about results</p>
* @return <p>True if everything went successfully</p>
*/
private boolean displayOrChangeNPCSetting(BlacksmithTrait blacksmithTrait, BlacksmithNPCSetting blacksmithNpcSetting, String newValue,
CommandSender sender) {
private boolean displayOrChangeNPCSetting(@NotNull BlacksmithTrait blacksmithTrait,
@NotNull BlacksmithNPCSetting blacksmithNpcSetting,
@Nullable String newValue,
@NotNull CommandSender sender) {
if (newValue == null) {
//Display the current value of the setting
displayNPCSetting(blacksmithTrait, blacksmithNpcSetting, sender);
@ -95,15 +98,17 @@ public class BlackSmithEditCommand implements CommandExecutor {
/**
* Displays the current value of the given NPC setting
*
* @param blacksmithTrait <p>The blacksmith trait of the NPC to get the value from</p>
* @param blacksmithNpcSetting <p>The NPC setting to see the value of</p>
* @param sender <p>The command sender to display the value to</p>
* @param blacksmithTrait <p>The blacksmith trait of the NPC to get the value from</p>
* @param blacksmithNpcSetting <p>The NPC setting to see the value of</p>
* @param sender <p>The command sender to display the value to</p>
*/
private void displayNPCSetting(BlacksmithTrait blacksmithTrait, BlacksmithNPCSetting blacksmithNpcSetting, CommandSender sender) {
private void displayNPCSetting(@NotNull BlacksmithTrait blacksmithTrait,
@NotNull BlacksmithNPCSetting blacksmithNpcSetting, @NotNull CommandSender sender) {
String rawValue = String.valueOf(blacksmithTrait.getSettings().getRawValue(blacksmithNpcSetting));
if (InputParsingHelper.isEmpty(rawValue)) {
//Display the default value, if no custom value has been specified
rawValue = String.valueOf(BlacksmithPlugin.getInstance().getSettings().getRawValue(blacksmithNpcSetting));
rawValue = String.valueOf(BlacksmithPlugin.getInstance().getGlobalBlacksmithSettings().getRawValue(
blacksmithNpcSetting));
BlacksmithPlugin.getStringFormatter().displaySuccessMessage(sender,
getCurrentValueMessage(blacksmithNpcSetting.getCommandName(), rawValue));
} else {

View File

@ -1,4 +1,4 @@
package net.knarcraft.blacksmith.command;
package net.knarcraft.blacksmith.command.blacksmith;
import net.knarcraft.blacksmith.config.blacksmith.BlacksmithNPCSetting;
import net.knarcraft.blacksmith.util.TabCompleteValuesHelper;
@ -17,10 +17,9 @@ import java.util.List;
*/
public class BlackSmithEditTabCompleter implements TabCompleter {
@Nullable
@Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] args) {
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
@NotNull String label, @NotNull String[] args) {
if (!sender.hasPermission("blacksmith.edit")) {
return new ArrayList<>();
}
@ -48,7 +47,7 @@ public class BlackSmithEditTabCompleter implements TabCompleter {
* @param commandValue <p>The command value used to filter tab-completions</p>
* @return <p>Some valid options for the command's argument</p>
*/
private List<String> tabCompleteCommandValues(String commandName, String commandValue) {
private @Nullable List<String> tabCompleteCommandValues(@NotNull String commandName, @NotNull String commandValue) {
for (BlacksmithNPCSetting blacksmithNpcSetting : BlacksmithNPCSetting.values()) {
if (blacksmithNpcSetting.getCommandName().equalsIgnoreCase(commandName)) {
return TabCompletionHelper.filterMatchingContains(TabCompleteValuesHelper.getTabCompletions(

View File

@ -0,0 +1,17 @@
package net.knarcraft.blacksmith.command.scrapper;
import org.apache.commons.lang.NotImplementedException;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
public class ScrapperConfigCommand implements CommandExecutor {
@Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] strings) {
throw new NotImplementedException();
}
}

View File

@ -0,0 +1,20 @@
package net.knarcraft.blacksmith.command.scrapper;
import org.apache.commons.lang.NotImplementedException;
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.List;
public class ScrapperConfigTabCompleter implements TabCompleter {
@Override
public @Nullable List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command,
@NotNull String s, @NotNull String[] strings) {
throw new NotImplementedException();
}
}

View File

@ -0,0 +1,15 @@
package net.knarcraft.blacksmith.command.scrapper;
import org.apache.commons.lang.NotImplementedException;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
public class ScrapperEditCommand implements CommandExecutor {
@Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] strings) {
throw new NotImplementedException();
}
}

View File

@ -0,0 +1,18 @@
package net.knarcraft.blacksmith.command.scrapper;
import org.apache.commons.lang.NotImplementedException;
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.List;
public class ScrapperEditTabCompleter implements TabCompleter {
@Override
public @Nullable List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command,
@NotNull String s, @NotNull String[] strings) {
throw new NotImplementedException();
}
}

View File

@ -0,0 +1,45 @@
package net.knarcraft.blacksmith.config;
import org.jetbrains.annotations.NotNull;
/**
* An interface describing a global setting
*/
public interface GlobalSetting {
/**
* Gets the full config path for this setting
*
* @return <p>The full config path for this setting</p>
*/
@NotNull String getPath();
/**
* Gets the parent item of the defined path
*
* @return <p>The parent node</p>
*/
@NotNull String getParent();
/**
* Gets the value of this setting
*
* @return <p>The value of this setting</p>
*/
@NotNull Object getDefaultValue();
/**
* The name of the command used to change this setting
*
* @return <p>The name of this setting's command</p>
*/
@NotNull String getCommandName();
/**
* Gets the value type for this setting
*
* @return <p>The value type for this setting</p>
*/
@NotNull SettingValueType getValueType();
}

View File

@ -1,5 +1,7 @@
package net.knarcraft.blacksmith.config;
import org.jetbrains.annotations.NotNull;
/**
* An interface describing an NPC setting
*/
@ -10,34 +12,34 @@ public interface NPCSetting {
*
* @return <p>The full config path for this setting</p>
*/
String getPath();
@NotNull String getPath();
/**
* Gets the config path without the root node
*
* @return <p>The config path without the root node</p>
*/
String getChildPath();
@NotNull String getChildPath();
/**
* Gets the value of this setting
*
* @return <p>The value of this setting</p>
*/
Object getDefaultValue();
@NotNull Object getDefaultValue();
/**
* The name of the command used to change this setting
*
* @return <p>The name of this setting's command</p>
*/
String getCommandName();
@NotNull String getCommandName();
/**
* Gets the value type for this setting
*
* @return <p>The value type for this setting</p>
*/
SettingValueType getValueType();
@NotNull SettingValueType getValueType();
}

View File

@ -0,0 +1,40 @@
package net.knarcraft.blacksmith.config;
public interface TraitSettings {
/**
* Gets whether to disable the action cool-down
*
* @return <p>Whether to disable the action cool-down</p>
*/
boolean getDisableCoolDown();
/**
* Gets the message to display when a blacksmith or scrapper is still affected by a cool-down
*
* @return <p>The cool down unexpired message</p>
*/
String getCoolDownUnexpiredMessage();
/**
* Gets the message to display when the blacksmith is busy with another player
*
* @return <p>The busy with player message</p>
*/
String getBusyWithPlayerMessage();
/**
* Gets the message to display when the blacksmith is busy with reforging or salvaging an item
*
* @return <p>The busy working message</p>
*/
String getBusyWorkingMessage();
/**
* Gets the message to display when a blacksmith starts reforging or salvaging an item
*
* @return <p>The start working message</p>
*/
String getStartWorkingMessage();
}

View File

@ -2,6 +2,7 @@ package net.knarcraft.blacksmith.config.blacksmith;
import net.knarcraft.blacksmith.config.NPCSetting;
import net.knarcraft.blacksmith.config.SettingValueType;
import org.jetbrains.annotations.NotNull;
/**
* An enum representing all of Blacksmith's settings
@ -162,7 +163,7 @@ public enum BlacksmithNPCSetting implements NPCSetting {
* @param commandName <p>The name of the command used to change this setting</p>
*/
BlacksmithNPCSetting(String path, SettingValueType valueType, Object value, String commandName) {
this.path = "defaults." + path;
this.path = "blacksmith.defaults." + path;
this.value = value;
this.valueType = valueType;
this.childPath = path;
@ -170,27 +171,27 @@ public enum BlacksmithNPCSetting implements NPCSetting {
}
@Override
public String getPath() {
public @NotNull String getPath() {
return path;
}
@Override
public String getChildPath() {
public @NotNull String getChildPath() {
return childPath;
}
@Override
public Object getDefaultValue() {
public @NotNull Object getDefaultValue() {
return value;
}
@Override
public String getCommandName() {
public @NotNull String getCommandName() {
return commandName;
}
@Override
public SettingValueType getValueType() {
public @NotNull SettingValueType getValueType() {
return this.valueType;
}

View File

@ -4,6 +4,7 @@ import net.citizensnpcs.api.util.DataKey;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.config.SettingValueType;
import net.knarcraft.blacksmith.config.SmithPreset;
import net.knarcraft.blacksmith.config.TraitSettings;
import net.knarcraft.blacksmith.util.ConfigHelper;
import net.knarcraft.blacksmith.util.InputParsingHelper;
import net.knarcraft.blacksmith.util.ItemHelper;
@ -22,7 +23,7 @@ import java.util.logging.Level;
/**
* A class which keeps track of all Blacksmith settings/config values for one NPC
*/
public class BlacksmithNPCSettings {
public class BlacksmithNPCSettings implements TraitSettings {
private final List<Material> reforgeAbleItems = new ArrayList<>();
private final List<Enchantment> enchantmentBlocklist = new ArrayList<>();
@ -95,11 +96,7 @@ public class BlacksmithNPCSettings {
return currentValues.get(setting);
}
/**
* Gets the message to display when the blacksmith is busy with another player
*
* @return <p>The busy with player message</p>
*/
@Override
public String getBusyWithPlayerMessage() {
return asString(BlacksmithNPCSetting.BUSY_WITH_PLAYER_MESSAGE);
}
@ -109,7 +106,7 @@ public class BlacksmithNPCSettings {
*
* @return <p>The busy reforging message</p>
*/
public String getBusyReforgingMessage() {
public String getBusyWorkingMessage() {
return asString(BlacksmithNPCSetting.BUSY_WITH_REFORGE_MESSAGE);
}
@ -140,12 +137,8 @@ public class BlacksmithNPCSettings {
return asString(BlacksmithNPCSetting.NOT_DAMAGED_MESSAGE);
}
/**
* Gets the message to display when a blacksmith starts reforging an item
*
* @return <p>The start reforge message</p>
*/
public String getStartReforgeMessage() {
@Override
public String getStartWorkingMessage() {
return asString(BlacksmithNPCSetting.START_REFORGE_MESSAGE);
}
@ -176,11 +169,7 @@ public class BlacksmithNPCSettings {
return asString(BlacksmithNPCSetting.INSUFFICIENT_FUNDS_MESSAGE);
}
/**
* Gets the message to display when a blacksmith is still affected by a cool-down
*
* @return <p>The cool down unexpired message</p>
*/
@Override
public String getCoolDownUnexpiredMessage() {
return asString(BlacksmithNPCSetting.COOL_DOWN_UNEXPIRED_MESSAGE);
}
@ -305,24 +294,11 @@ public class BlacksmithNPCSettings {
return asString(BlacksmithNPCSetting.BLACKSMITH_TITLE);
}
/**
* Gets whether to disable the reforge-cool-down
*
* @return <p>Whether to disable the reforge-cool-down</p>
*/
@Override
public boolean getDisableCoolDown() {
return asInt(BlacksmithNPCSetting.REFORGE_COOL_DOWN) <= 0;
}
/**
* Gets whether to disable the delay between starting reforging and the reforging finishing
*
* @return <p>Whether to disable the reforge delay</p>
*/
public boolean getDisableDelay() {
return asInt(BlacksmithNPCSetting.MAX_REFORGE_DELAY) <= 0;
}
/**
* Gets whether this blacksmith is able to repair anvils
*
@ -413,8 +389,10 @@ public class BlacksmithNPCSettings {
*/
private void updateEnchantmentBlocklist() {
this.enchantmentBlocklist.clear();
this.enchantmentBlocklist.addAll(getEnchantmentBlocklist(ConfigHelper.asStringList(getValue(
BlacksmithNPCSetting.ENCHANTMENT_BLOCKLIST))));
List<String> enchantments = ConfigHelper.asStringList(getValue(BlacksmithNPCSetting.ENCHANTMENT_BLOCKLIST));
if (enchantments != null) {
this.enchantmentBlocklist.addAll(getEnchantmentBlocklist(enchantments));
}
}
/**
@ -447,8 +425,10 @@ public class BlacksmithNPCSettings {
*/
private void updateReforgeAbleItems() {
this.reforgeAbleItems.clear();
this.reforgeAbleItems.addAll(getReforgeAbleItems(ConfigHelper.asStringList(getValue(
BlacksmithNPCSetting.REFORGE_ABLE_ITEMS))));
List<String> materialStrings = ConfigHelper.asStringList(getValue(BlacksmithNPCSetting.REFORGE_ABLE_ITEMS));
if (materialStrings != null) {
this.reforgeAbleItems.addAll(getReforgeAbleItems(materialStrings));
}
}
/**

View File

@ -1,17 +1,22 @@
package net.knarcraft.blacksmith.config.blacksmith;
import net.knarcraft.blacksmith.config.GlobalSetting;
import net.knarcraft.blacksmith.config.SettingValueType;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
public enum GlobalBlacksmithSetting {
/**
* Settings which are the same for every blacksmith
*/
public enum GlobalBlacksmithSetting implements GlobalSetting {
/**
* The base price for repairing, regardless of durability
*
* <p>This allows specifying a price for each item, by setting basePrice.item_name.</p>
*/
BASE_PRICE("global.basePrice.default", SettingValueType.POSITIVE_DOUBLE, 10.0, "basePrice"),
BASE_PRICE("basePrice.default", SettingValueType.POSITIVE_DOUBLE, 10.0, "basePrice"),
/**
* The base price for each durability point
@ -20,7 +25,7 @@ public enum GlobalBlacksmithSetting {
* 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 pricePerDurabilityPoint.item_name</p>
*/
PRICE_PER_DURABILITY_POINT("global.pricePerDurabilityPoint.default", SettingValueType.POSITIVE_DOUBLE,
PRICE_PER_DURABILITY_POINT("pricePerDurabilityPoint.default", SettingValueType.POSITIVE_DOUBLE,
0.005, "pricePerDurabilityPoint"),
/**
@ -28,29 +33,29 @@ public enum GlobalBlacksmithSetting {
*
* <p>This can be specified for each possible enchantment by setting enchantment-cost.enchantment_name</p>
*/
ENCHANTMENT_COST("global.enchantmentCost.default", SettingValueType.POSITIVE_DOUBLE, 5.0,
ENCHANTMENT_COST("enchantmentCost.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.useNaturalCost", SettingValueType.BOOLEAN, true, "useNaturalCost"),
NATURAL_COST("useNaturalCost", SettingValueType.BOOLEAN, true, "useNaturalCost"),
/**
* Whether to show exact time when displaying the wait time for a reforging or the cool-down
*/
SHOW_EXACT_TIME("global.showExactTime", SettingValueType.BOOLEAN, false, "showExactTime"),
SHOW_EXACT_TIME("showExactTime", SettingValueType.BOOLEAN, false, "showExactTime"),
/**
* The cost for repairing a chipped anvil
*/
ANVIL_CHIPPED_COST("global.chippedAnvilReforgingCost", SettingValueType.POSITIVE_DOUBLE, 10.0,
ANVIL_CHIPPED_COST("chippedAnvilReforgingCost", SettingValueType.POSITIVE_DOUBLE, 10.0,
"chippedAnvilReforgingCost"),
/**
* The cost for repairing a damaged anvil
*/
ANVIL_DAMAGED_COST("global.damagedAnvilReforgingCost", SettingValueType.POSITIVE_DOUBLE, 20.0,
ANVIL_DAMAGED_COST("damagedAnvilReforgingCost", SettingValueType.POSITIVE_DOUBLE, 20.0,
"damagedAnvilReforgingCost");
private final String path;
@ -68,7 +73,7 @@ public enum GlobalBlacksmithSetting {
* @param commandName <p>The name of the command used to change this setting</p>
*/
GlobalBlacksmithSetting(String path, SettingValueType valueType, Object value, String commandName) {
this.path = path;
this.path = "blacksmith.global." + path;
this.value = value;
this.commandName = commandName;
this.valueType = valueType;
@ -76,48 +81,28 @@ public enum GlobalBlacksmithSetting {
this.parent = String.join(".", Arrays.copyOfRange(pathParts, 0, pathParts.length - 1));
}
/**
* Gets the full config path for this setting
*
* @return <p>The full config path for this setting</p>
*/
public String getPath() {
@Override
public @NotNull String getPath() {
return path;
}
/**
* Gets the parent item of the defined path
*
* @return <p>The parent node</p>
*/
public String getParent() {
@Override
public @NotNull String getParent() {
return parent;
}
/**
* Gets the value of this setting
*
* @return <p>The value of this setting</p>
*/
public Object getDefaultValue() {
@Override
public @NotNull Object getDefaultValue() {
return value;
}
/**
* The name of the command used to change this setting
*
* @return <p>The name of this setting's command</p>
*/
public String getCommandName() {
@Override
public @NotNull String getCommandName() {
return commandName;
}
/**
* Gets the value type for this setting
*
* @return <p>The value type for this setting</p>
*/
public SettingValueType getValueType() {
@Override
public @NotNull SettingValueType getValueType() {
return this.valueType;
}

View File

@ -72,7 +72,7 @@ public class GlobalBlacksmithSettings {
* Changes the value of the given setting
*
* @param globalBlacksmithSetting <p>The global setting to change</p>
* @param newValue <p>The new value of the setting</p>
* @param newValue <p>The new value of the setting</p>
*/
public void changeValue(GlobalBlacksmithSetting globalBlacksmithSetting, Object newValue) {
globalSettings.put(globalBlacksmithSetting, newValue);
@ -83,7 +83,7 @@ public class GlobalBlacksmithSettings {
* Changes the value of the given setting
*
* @param blacksmithNpcSetting <p>The default NPC setting to change</p>
* @param newValue <p>The new value for the setting</p>
* @param newValue <p>The new value for the setting</p>
*/
public void changeValue(BlacksmithNPCSetting blacksmithNpcSetting, Object newValue) {
if (blacksmithNpcSetting.getValueType() == SettingValueType.STRING_LIST ||
@ -498,8 +498,11 @@ public class GlobalBlacksmithSettings {
*/
private void loadReforgeAbleItems() {
defaultReforgeAbleMaterials.clear();
defaultReforgeAbleMaterials.addAll(BlacksmithNPCSettings.getReforgeAbleItems(ConfigHelper.asStringList(
defaultNPCSettings.get(BlacksmithNPCSetting.REFORGE_ABLE_ITEMS))));
List<String> materialNames = ConfigHelper.asStringList(defaultNPCSettings.get(
BlacksmithNPCSetting.REFORGE_ABLE_ITEMS));
if (materialNames != null) {
defaultReforgeAbleMaterials.addAll(BlacksmithNPCSettings.getReforgeAbleItems(materialNames));
}
}
/**
@ -507,8 +510,11 @@ public class GlobalBlacksmithSettings {
*/
private void loadEnchantmentBlocklist() {
defaultEnchantmentBlocklist.clear();
defaultEnchantmentBlocklist.addAll(BlacksmithNPCSettings.getEnchantmentBlocklist(ConfigHelper.asStringList(
defaultNPCSettings.get(BlacksmithNPCSetting.ENCHANTMENT_BLOCKLIST))));
List<String> enchantmentNames = ConfigHelper.asStringList(defaultNPCSettings.get(
BlacksmithNPCSetting.ENCHANTMENT_BLOCKLIST));
if (enchantmentNames != null) {
defaultEnchantmentBlocklist.addAll(BlacksmithNPCSettings.getEnchantmentBlocklist(enchantmentNames));
}
}
/**

View File

@ -0,0 +1,75 @@
package net.knarcraft.blacksmith.config.scrapper;
import net.knarcraft.blacksmith.config.GlobalSetting;
import net.knarcraft.blacksmith.config.SettingValueType;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
/**
* Settings which are the same for every scrapper
*/
public enum GlobalScrapperSetting implements GlobalSetting {
/**
* Whether to display exact time in minutes and seconds when displaying a remaining cool-down
*/
SHOW_EXACT_TIME("scrapper.global.showExactTime", SettingValueType.BOOLEAN, "false",
"showExactTime"),
/**
* Whether to give experience back when salvaging an enchanted item
*/
GIVE_EXPERIENCE("scrapper.global.giveExperience", SettingValueType.BOOLEAN, "true",
"giveExperience"),
;
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 path <p>The full config path for this setting</p>
* @param valueType <p>The type of value used by this setting</p>
* @param value <p>The default value of this setting</p>
* @param commandName <p>The name of the command used to change this setting</p>
*/
GlobalScrapperSetting(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));
}
@Override
public @NotNull String getPath() {
return path;
}
@Override
public @NotNull String getParent() {
return parent;
}
@Override
public @NotNull Object getDefaultValue() {
return value;
}
@Override
public @NotNull String getCommandName() {
return commandName;
}
@Override
public @NotNull SettingValueType getValueType() {
return this.valueType;
}
}

View File

@ -0,0 +1,211 @@
package net.knarcraft.blacksmith.config.scrapper;
import net.citizensnpcs.api.util.DataKey;
import net.citizensnpcs.api.util.YamlStorage;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.config.SettingValueType;
import net.knarcraft.blacksmith.util.ConfigHelper;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
public class GlobalScrapperSettings {
private final Map<ScrapperNPCSetting, Object> defaultNPCSettings = new HashMap<>();
private final Map<GlobalScrapperSetting, Object> globalSettings = new HashMap<>();
private final YamlStorage defaultConfig;
/**
* Instantiates a new "Settings"
*
* @param plugin <p>A reference to the blacksmith plugin</p>
*/
public GlobalScrapperSettings(BlacksmithPlugin plugin) {
defaultConfig = new YamlStorage(new File(plugin.getDataFolder() + File.separator + "config.yml"),
"Scrapper Configuration\nWarning: The values under defaults are the values set for a " +
"scrapper upon creation. To change any values for existing NPCs, edit the citizens NPC file.");
}
/**
* Loads all configuration values from the config file
*/
public void load() {
//Load the config from disk
defaultConfig.load();
DataKey root = defaultConfig.getKey("");
//Just in case, clear existing values
defaultNPCSettings.clear();
globalSettings.clear();
//Load/Save NPC default settings
loadDefaultNPCSettings(root);
//Load/Save global settings
loadGlobalSettings(root);
//Save any modified values to disk
defaultConfig.save();
}
/**
* Changes the value of the given setting
*
* @param globalScrapperSetting <p>The global setting to change</p>
* @param newValue <p>The new value of the setting</p>
*/
public void changeValue(GlobalScrapperSetting globalScrapperSetting, Object newValue) {
globalSettings.put(globalScrapperSetting, newValue);
save();
}
/**
* Changes the value of the given setting
*
* @param scrapperNPCSetting <p>The default NPC setting to change</p>
* @param newValue <p>The new value for the setting</p>
*/
public void changeValue(ScrapperNPCSetting scrapperNPCSetting, Object newValue) {
if (scrapperNPCSetting.getValueType() == SettingValueType.STRING_LIST ||
scrapperNPCSetting.getValueType() == SettingValueType.REFORGE_ABLE_ITEMS) {
//Workaround to make sure it's treated as the correct type
defaultNPCSettings.put(scrapperNPCSetting, newValue == null ? null : ConfigHelper.asStringList(newValue));
} else {
defaultNPCSettings.put(scrapperNPCSetting, newValue);
}
save();
}
/**
* Gets the current raw value of the given global setting
*
* @param globalBlacksmithSetting <p>The setting to get</p>
* @return <p>The current raw setting value</p>
*/
public Object getRawValue(GlobalScrapperSetting globalBlacksmithSetting) {
return globalSettings.get(globalBlacksmithSetting);
}
/**
* Gets the current raw value of the given default NPC setting
*
* @param scrapperNPCSetting <p>The setting to get</p>
* @return <p>The current raw setting value</p>
*/
public Object getRawValue(ScrapperNPCSetting scrapperNPCSetting) {
return defaultNPCSettings.get(scrapperNPCSetting);
}
/**
* Gets the current value of the default NPC settings
*
* @return <p>The current value of the default NPC settings</p>
*/
public Map<ScrapperNPCSetting, Object> getDefaultNPCSettings() {
return new HashMap<>(this.defaultNPCSettings);
}
/**
* Gets whether to show exact time for reforging wait-time, and for wait-time between sessions
*
* @return <p>Whether to show exact time</p>
*/
public boolean getShowExactTime() {
return asBoolean(GlobalScrapperSetting.SHOW_EXACT_TIME);
}
/**
* Gets the given value as a boolean
*
* <p>This will throw an exception if used for a non-boolean value</p>
*
* @param setting <p>The setting to get the value of</p>
* @return <p>The value of the given setting as a boolean</p>
*/
public boolean asBoolean(GlobalScrapperSetting setting) {
return ConfigHelper.asBoolean(getValue(setting));
}
/**
* Gets the given value as a double
*
* <p>This will throw an exception if used for a non-double setting</p>
*
* @param setting <p>The setting to get the value of</p>
* @return <p>The value of the given setting as a double</p>
*/
public double asDouble(GlobalScrapperSetting setting) {
return ConfigHelper.asDouble(getValue(setting));
}
/**
* Gets the value of a setting, using the default if not set
*
* @param setting <p>The setting to get the value of</p>
* @return <p>The current value</p>
*/
private Object getValue(GlobalScrapperSetting setting) {
Object value = globalSettings.get(setting);
//If not set in config.yml, use the default value from the enum
if (value == null) {
value = setting.getDefaultValue();
}
return value;
}
/**
* Loads all global settings
*
* @param root <p>The root node of all global settings</p>
*/
private void loadGlobalSettings(DataKey root) {
for (GlobalScrapperSetting globalScrapperSetting : GlobalScrapperSetting.values()) {
if (!root.keyExists(globalScrapperSetting.getPath())) {
//If the setting does not exist in the config file, add it
root.setRaw(globalScrapperSetting.getPath(), globalScrapperSetting.getDefaultValue());
} else {
//Set the setting to the value found in the path
globalSettings.put(globalScrapperSetting, root.getRaw(globalScrapperSetting.getPath()));
}
}
}
/**
* Loads all default NPC settings
*
* @param root <p>The root node of all default NPC settings</p>
*/
private void loadDefaultNPCSettings(DataKey root) {
for (ScrapperNPCSetting setting : ScrapperNPCSetting.values()) {
if (!root.keyExists(setting.getPath())) {
//If the setting does not exist in the config file, add it
root.setRaw(setting.getPath(), setting.getDefaultValue());
} else {
//Set the setting to the value found in the path
defaultNPCSettings.put(setting, root.getRaw(setting.getPath()));
}
}
}
/**
* Saves all current settings to the config file
*/
private void save() {
DataKey root = defaultConfig.getKey("");
//Save all default NPC settings
for (ScrapperNPCSetting setting : ScrapperNPCSetting.values()) {
root.setRaw(setting.getPath(), defaultNPCSettings.get(setting));
}
//Save all normal global settings
for (GlobalScrapperSetting globalBlacksmithSetting : GlobalScrapperSetting.values()) {
root.setRaw(globalBlacksmithSetting.getPath(), globalSettings.get(globalBlacksmithSetting));
}
//Perform the actual save to disk
defaultConfig.save();
}
}

View File

@ -2,6 +2,7 @@ package net.knarcraft.blacksmith.config.scrapper;
import net.knarcraft.blacksmith.config.NPCSetting;
import net.knarcraft.blacksmith.config.SettingValueType;
import org.jetbrains.annotations.NotNull;
public enum ScrapperNPCSetting implements NPCSetting {
@ -21,6 +22,21 @@ public enum ScrapperNPCSetting implements NPCSetting {
* The setting for which items a scrapper is able to salvage
*/
SALVAGE_ABLE_ITEMS("salvageAbleItems", SettingValueType.REFORGE_ABLE_ITEMS, "", "salvageAbleItems"),
/**
* The maximum amount of seconds a player may need to wait for the reforging to finish
*/
MAX_SALVAGE_DELAY("delaysInSeconds.maximum", SettingValueType.POSITIVE_INTEGER, 30, "maxReforgeDelay"),
/**
* The minimum amount of seconds a player may need to wait for the reforging to finish
*/
MIN_SALVAGE_DELAY("delaysInSeconds.minimum", SettingValueType.POSITIVE_INTEGER, 5, "minReforgeDelay"),
/**
* The setting for number of seconds a player has to wait between each usage of the blacksmith
*/
SALVAGE_COOL_DOWN("delaysInSeconds.reforgeCoolDown", SettingValueType.POSITIVE_INTEGER, 60, "reforgeCoolDown"),
/*-----------
| Messages |
@ -31,6 +47,50 @@ public enum ScrapperNPCSetting implements NPCSetting {
*/
BUSY_WITH_PLAYER_MESSAGE("messages.busyPlayerMessage", SettingValueType.STRING,
"&cI'm busy at the moment. Come back later!", "busyPlayerMessage"),
/**
* The message displayed when the scrapper is busy salvaging the player's item
*/
BUSY_WITH_SALVAGE_MESSAGE("messages.busySalvageMessage", SettingValueType.STRING,
"&cI'm working on it. Be patient! I'll finish {time}!", "busySalvageMessage"),
/**
* The message displayed if the player needs to wait for the cool-down to expire
*/
COOL_DOWN_UNEXPIRED_MESSAGE("messages.coolDownUnexpiredMessage", SettingValueType.STRING,
"&cYou've already had your chance! Give me a break! I'll be ready {time}!",
"coolDownUnexpiredMessage"),
/**
* The message displayed if presented with an item that cannot be salvaged by the NPC
*/
CANNOT_SALVAGE_MESSAGE("messages.cannotSalvageMessage", SettingValueType.STRING,
"&cI'm unable to salvage that item", "cannotSalvageMessage"),
/**
* The message displayed if salvaging an item would return in no items
*/
TOO_DAMAGED_FOR_SALVAGE_MESSAGE("messages.tooDamagedForSalvageMessage", SettingValueType.STRING,
"&cThat item is too damaged to be salvaged into anything useful",
"tooDamagedForSalvageMessage"),
/**
* The message displayed if a salvage is successful
*/
SUCCESS_SALVAGE_MESSAGE("messages.successSalvagedMessage", SettingValueType.STRING, "&cThere you go!",
"successSalvagedMessage"),
/**
* The message displayed if a player presents a different item after seeing the price to reforge an item
*/
ITEM_UNEXPECTEDLY_CHANGED_MESSAGE("messages.itemChangedMessage", SettingValueType.STRING,
"&cThat's not the item you wanted to reforge before!", "itemChangedMessage"),
/**
* The message displayed when the scrapper starts salvaging an item
*/
START_SALVAGE_MESSAGE("messages.startSalvageMessage", SettingValueType.STRING,
"&eOk, let's see what I can do...", "startSalvageMessage"),
;
private final String path;
@ -48,7 +108,7 @@ public enum ScrapperNPCSetting implements NPCSetting {
* @param commandName <p>The name of the command used to change this setting</p>
*/
ScrapperNPCSetting(String path, SettingValueType valueType, Object value, String commandName) {
this.path = "defaults." + path;
this.path = "scrapper.defaults." + path;
this.value = value;
this.valueType = valueType;
this.childPath = path;
@ -56,28 +116,28 @@ public enum ScrapperNPCSetting implements NPCSetting {
}
@Override
public String getPath() {
public @NotNull String getPath() {
return path;
}
@Override
public String getChildPath() {
public @NotNull String getChildPath() {
return childPath;
}
@Override
public Object getDefaultValue() {
public @NotNull Object getDefaultValue() {
return value;
}
@Override
public String getCommandName() {
public @NotNull String getCommandName() {
return commandName;
}
@Override
public SettingValueType getValueType() {
public @NotNull SettingValueType getValueType() {
return this.valueType;
}
}

View File

@ -1,4 +1,239 @@
package net.knarcraft.blacksmith.config.scrapper;
public class ScrapperNPCSettings {
import net.citizensnpcs.api.util.DataKey;
import net.knarcraft.blacksmith.config.SettingValueType;
import net.knarcraft.blacksmith.config.SmithPreset;
import net.knarcraft.blacksmith.config.TraitSettings;
import net.knarcraft.blacksmith.util.ConfigHelper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ScrapperNPCSettings implements TraitSettings {
private final Map<ScrapperNPCSetting, Object> currentValues = new HashMap<>();
private final GlobalScrapperSettings globalScrapperSettings;
/**
* Instantiates a new scrapper NPC settings object
*
* @param globalScrapperSettings <p>The global settings object to get default values from</p>
*/
public ScrapperNPCSettings(GlobalScrapperSettings globalScrapperSettings) {
this.globalScrapperSettings = globalScrapperSettings;
}
/**
* Loads variables from the given data key
*
* @param key <p>The data key to load variables from</p>
*/
public void loadVariables(DataKey key) {
for (ScrapperNPCSetting setting : ScrapperNPCSetting.values()) {
if (key.keyExists(setting.getChildPath())) {
currentValues.put(setting, key.getRaw(setting.getChildPath()));
}
}
}
/**
* Saves variables to the given data key
*
* @param key <p>The data key to save variables to</p>
*/
public void saveVariables(DataKey key) {
for (ScrapperNPCSetting setting : ScrapperNPCSetting.values()) {
key.setRaw(setting.getChildPath(), currentValues.get(setting));
}
}
/**
* Changes one setting to the given value
*
* @param setting <p>The setting to change</p>
* @param newValue <p>The new value of the setting</p>
*/
public void changeSetting(ScrapperNPCSetting setting, Object newValue) {
if (setting.getValueType() == SettingValueType.STRING_LIST ||
setting.getValueType() == SettingValueType.REFORGE_ABLE_ITEMS) {
//Workaround to make sure it's treated as the correct type
currentValues.put(setting, newValue == null ? null : ConfigHelper.asStringList(newValue));
} else {
currentValues.put(setting, newValue);
}
}
/**
* Gets the raw current value of a setting
*
* @param setting <p>The setting to get the value of</p>
* @return <p>The current value of the setting</p>
*/
public Object getRawValue(ScrapperNPCSetting setting) {
return currentValues.get(setting);
}
@Override
public String getBusyWithPlayerMessage() {
return asString(ScrapperNPCSetting.BUSY_WITH_PLAYER_MESSAGE);
}
@Override
public String getBusyWorkingMessage() {
return asString(ScrapperNPCSetting.BUSY_WITH_SALVAGE_MESSAGE);
}
@Override
public String getStartWorkingMessage() {
return asString(ScrapperNPCSetting.START_SALVAGE_MESSAGE);
}
/**
* Gets the message to display when a blacksmith has successfully repaired an item
*
* @return <p>The reforge success message</p>
*/
public String getSuccessMessage() {
return asString(ScrapperNPCSetting.SUCCESS_SALVAGE_MESSAGE);
}
@Override
public String getCoolDownUnexpiredMessage() {
return asString(ScrapperNPCSetting.COOL_DOWN_UNEXPIRED_MESSAGE);
}
/**
* The message displayed if a player presents a different item after seeing the price to salvage an item
*/
public String getItemChangedMessage() {
return asString(ScrapperNPCSetting.ITEM_UNEXPECTEDLY_CHANGED_MESSAGE);
}
/**
* Gets the minimum delay used to wait for a salvaging to finish.
*
* @return <p>The minimum salvage delay</p>
*/
public int getMinSalvageDelay() {
return asInt(ScrapperNPCSetting.MIN_SALVAGE_DELAY);
}
/**
* Gets the maximum delay used to wait for a salvaging to finish
*
* @return <p>The maximum salvage delay</p>
*/
public int getMaxSalvageDelay() {
return asInt(ScrapperNPCSetting.MAX_SALVAGE_DELAY);
}
/**
* Gets the cool-down between each salvaging
*
* @return <p>The salvage cool-down</p>
*/
public int getSalvageCoolDown() {
return asInt(ScrapperNPCSetting.SALVAGE_COOL_DOWN);
}
/**
* Gets whether an item should be dropped on the ground instead of being given to the player
*
* @return <p>Whether to drop reforged items on the ground</p>
*/
public boolean getDropItem() {
return asBoolean(ScrapperNPCSetting.DROP_ITEM);
}
@Override
public boolean getDisableCoolDown() {
return asInt(ScrapperNPCSetting.SALVAGE_COOL_DOWN) <= 0;
}
/**
* Gets whether to disable the delay between starting reforging and the reforging finishing
*
* @return <p>Whether to disable the reforge delay</p>
*/
public boolean getDisableDelay() {
return asInt(ScrapperNPCSetting.MAX_SALVAGE_DELAY) <= 0;
}
/**
* Gets the given value as an integer
*
* <p>This will throw an exception if used for a non-integer setting</p>
*
* @param setting <p>The setting to get the value of</p>
* @return <p>The value of the given setting as an integer</p>
*/
private int asInt(ScrapperNPCSetting setting) {
return ConfigHelper.asInt(getValue(setting));
}
/**
* Gets the string value of the given setting
*
* @param setting <p>The setting to get the value of</p>
* @return <p>The value of the given setting as a string</p>
*/
private String asString(ScrapperNPCSetting setting) {
return getValue(setting).toString();
}
/**
* Gets the boolean value of the given setting
*
* @param setting <p>The setting to get the value of</p>
* @return <p>The value of the given setting as a boolean</p>
*/
private boolean asBoolean(ScrapperNPCSetting setting) {
return ConfigHelper.asBoolean(getValue(setting));
}
/**
* Gets the value of a setting, using the default if not set
*
* @param setting <p>The setting to get the value of</p>
* @return <p>The current value</p>
*/
private Object getValue(ScrapperNPCSetting setting) {
Object value = currentValues.get(setting);
//If not set, use the default value from the config.yml file
if (value == null) {
Map<ScrapperNPCSetting, Object> defaultNPCSettings = globalScrapperSettings.getDefaultNPCSettings();
if (defaultNPCSettings.containsKey(setting)) {
value = defaultNPCSettings.get(setting);
}
}
//If not set in config.yml, use the default value from the enum
if (value == null) {
value = setting.getDefaultValue();
}
return value;
}
/**
* Replaces placeholders in the given reforge-able value
*
* @param stringList <p>The value specified by a user</p>
* @return <p>The value with placeholders replaced</p>
*/
private static List<String> replaceReforgeAblePresets(List<String> stringList) {
List<String> newStrings = new ArrayList<>();
for (String item : stringList) {
if (item == null) {
continue;
}
String replaced = SmithPreset.replacePreset(item);
if (!replaced.equals(item)) {
newStrings.addAll(List.of(replaced.split(",")));
} else {
newStrings.add(item);
}
}
return newStrings;
}
}

View File

@ -1,11 +1,18 @@
package net.knarcraft.blacksmith.listener;
import net.citizensnpcs.api.event.NPCRightClickEvent;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.formatting.BlacksmithTranslatableMessage;
import net.knarcraft.blacksmith.trait.BlacksmithTrait;
import net.knarcraft.blacksmith.trait.CustomTrait;
import net.knarcraft.blacksmith.trait.ScrapperTrait;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.logging.Level;
/**
* A listener for detecting and handling a Blacksmith being right-clicked
@ -13,12 +20,26 @@ import org.bukkit.event.Listener;
public class NPCClickListener implements Listener {
@EventHandler
public void onRightClick(net.citizensnpcs.api.event.NPCRightClickEvent event) {
public void onRightClick(@NotNull NPCRightClickEvent event) {
//We only care about blacksmiths
if (!event.getNPC().hasTrait(BlacksmithTrait.class)) {
if (event.getNPC().hasTrait(BlacksmithTrait.class)) {
handleNPCClick(event, event.getNPC().getTraitNullable(BlacksmithTrait.class));
} else if (event.getNPC().hasTrait(ScrapperTrait.class)) {
handleNPCClick(event, event.getNPC().getTraitNullable(ScrapperTrait.class));
}
}
/**
* Handles a player clicking on a blacksmith or a scrapper NPC
*
* @param event <p>The NPC click event performed</p>
* @param customTrait <p>The NPC's custom trait</p>
*/
private void handleNPCClick(@NotNull NPCRightClickEvent event, @Nullable CustomTrait customTrait) {
if (customTrait == null) {
BlacksmithPlugin.getInstance().getLogger().log(Level.WARNING, "Could not get trait from NPC!");
return;
}
BlacksmithTrait blacksmithTrait = event.getNPC().getTraitNullable(BlacksmithTrait.class);
//Perform any necessary pre-session work
Player player = event.getClicker();
@ -30,16 +51,16 @@ public class NPCClickListener implements Listener {
return;
}
if (!blacksmithTrait.prepareForSession(player)) {
if (!customTrait.prepareForSession(player)) {
return;
}
if (blacksmithTrait.hasSession()) {
if (customTrait.hasSession()) {
//Continue the existing session
blacksmithTrait.continueSession(player);
customTrait.continueSession(player);
} else {
//Start a new session
blacksmithTrait.startSession(player);
customTrait.startSession(player);
}
}

View File

@ -1,7 +1,9 @@
package net.knarcraft.blacksmith.listener;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.npc.NPCRegistry;
import net.knarcraft.blacksmith.trait.BlacksmithTrait;
import net.knarcraft.blacksmith.trait.ScrapperTrait;
import org.bukkit.Material;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.Entity;
@ -13,6 +15,8 @@ import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PlayerListener implements Listener {
@ -22,19 +26,21 @@ public class PlayerListener implements Listener {
* @param event <p>The triggered event</p>
*/
@EventHandler(priority = EventPriority.HIGHEST)
public void onClick(PlayerInteractEvent event) {
public void onClick(@NotNull PlayerInteractEvent event) {
if (event.getHand() == null || (event.getAction() != Action.RIGHT_CLICK_AIR && event.getAction() !=
Action.RIGHT_CLICK_BLOCK)) {
return;
}
//If the player is not looking at a blacksmith, there is no need to do anything
//If the player is not looking at a blacksmith or scrapper, there is no need to do anything
Entity target = getTarget(event.getPlayer(), event.getPlayer().getNearbyEntities(15, 10, 15));
if (!CitizensAPI.getNPCRegistry().isNPC(target) ||
!CitizensAPI.getNPCRegistry().getNPC(target).hasTrait(BlacksmithTrait.class)) {
NPCRegistry registry = CitizensAPI.getNPCRegistry();
if (!registry.isNPC(target) ||
(!registry.getNPC(target).hasTrait(BlacksmithTrait.class) &&
!registry.getNPC(target).hasTrait(ScrapperTrait.class))) {
return;
}
//Block armor equip if interacting with a blacksmith
//Block armor equip if interacting with a blacksmith or a scrapper
ItemStack usedItem = event.getPlayer().getInventory().getItem(event.getHand());
if (usedItem != null && isArmor(usedItem)) {
event.setUseItemInHand(Event.Result.DENY);
@ -47,7 +53,7 @@ public class PlayerListener implements Listener {
* @param item <p>The item to check if is armor or not</p>
* @return <p>True if the given item is a type of armor</p>
*/
public static boolean isArmor(ItemStack item) {
public static boolean isArmor(@NotNull ItemStack item) {
return EnchantmentTarget.WEARABLE.includes(item) || item.getType() == Material.ELYTRA;
}
@ -59,7 +65,7 @@ public class PlayerListener implements Listener {
* @param <T> <p>The type of entity the player is looking at</p>
* @return <p>The entity the player is looking at, or null if no such entity exists</p>
*/
private static <T extends Entity> T getTarget(final Entity entity, final Iterable<T> entities) {
private static <T extends Entity> T getTarget(final @Nullable Entity entity, final @NotNull Iterable<T> entities) {
if (entity == null) {
return null;
}
@ -67,8 +73,10 @@ public class PlayerListener implements Listener {
final double threshold = 1;
for (final T other : entities) {
final Vector n = other.getLocation().toVector().subtract(entity.getLocation().toVector());
if (entity.getLocation().getDirection().normalize().crossProduct(n).lengthSquared() < threshold &&
n.normalize().dot(entity.getLocation().getDirection().normalize()) >= 0) {
// Replace if the other entity is nearer, or no target has been found
if (target == null ||
target.getLocation().distanceSquared(entity.getLocation()) >
other.getLocation().distanceSquared(entity.getLocation())) {

View File

@ -10,6 +10,7 @@ import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.ServicesManager;
import org.jetbrains.annotations.NotNull;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -32,7 +33,7 @@ public class EconomyManager {
* @param logger <p>The logger to use for logging</p>
* @return <p>True if Vault was successfully set up</p>
*/
public static boolean setUp(ServicesManager servicesManager, Logger logger) {
public static boolean setUp(@NotNull ServicesManager servicesManager, @NotNull Logger logger) {
//If already set up, there is nothing to do
if (economy != null) {
return true;
@ -41,13 +42,13 @@ public class EconomyManager {
}
/**
* Gets whether the given player can pay for re-forging their held item
* Gets whether the given player cannot pay for re-forging their held item
*
* @param player <p>The player holding an item</p>
* @return <p>Whether the player can pay for the reforge</p>
* @return <p>Whether the player cannot pay for the reforge</p>
*/
public static boolean canPay(Player player) {
return economy.getBalance(player) - getHeldItemCost(player) >= 0;
public static boolean cannotPayForHeldItemReforge(@NotNull Player player) {
return !(economy.getBalance(player) - getHeldItemCost(player) >= 0);
}
/**
@ -56,7 +57,7 @@ public class EconomyManager {
* @param player <p>The player holding an item</p>
* @return <p>The formatted cost</p>
*/
public static String formatCost(Player player) {
public static String formatCost(@NotNull Player player) {
double cost = getHeldItemCost(player);
return economy.format(cost);
}
@ -68,7 +69,7 @@ public class EconomyManager {
*
* @param player <p>The player to withdraw from</p>
*/
public static void withdraw(Player player) {
public static void withdraw(@NotNull Player player) {
economy.withdrawPlayer(player, getHeldItemCost(player));
}
@ -78,7 +79,7 @@ public class EconomyManager {
* @param player <p>The player to calculate the cost for</p>
* @return <p>The calculated cost</p>
*/
private static double getHeldItemCost(Player player) {
private static double getHeldItemCost(@NotNull Player player) {
return getCost(player.getInventory().getItemInMainHand());
}
@ -88,8 +89,8 @@ public class EconomyManager {
* @param item <p>The item to be repaired</p>
* @return <p>The cost of the repair</p>
*/
private static double getCost(ItemStack item) {
GlobalBlacksmithSettings globalBlacksmithSettings = BlacksmithPlugin.getInstance().getSettings();
private static double getCost(@NotNull ItemStack item) {
GlobalBlacksmithSettings globalBlacksmithSettings = BlacksmithPlugin.getInstance().getGlobalBlacksmithSettings();
Material material = item.getType();
//Calculate the base price
@ -121,8 +122,8 @@ public class EconomyManager {
* @param item <p>The item to calculate enchantment cost for</p>
* @return <p>The resulting enchantment cost</p>
*/
private static double getEnchantmentCost(ItemStack item) {
GlobalBlacksmithSettings settings = BlacksmithPlugin.getInstance().getSettings();
private static double getEnchantmentCost(@NotNull ItemStack item) {
GlobalBlacksmithSettings settings = BlacksmithPlugin.getInstance().getGlobalBlacksmithSettings();
double price = 0;
for (Enchantment enchantment : item.getEnchantments().keySet()) {
price += settings.getEnchantmentCost(enchantment) * item.getEnchantmentLevel(enchantment);
@ -137,7 +138,7 @@ public class EconomyManager {
* @param logger <p>The logger to use for logging</p>
* @return <p>True if Vault was successfully set up</p>
*/
private static boolean setupVault(ServicesManager servicesManager, Logger logger) {
private static boolean setupVault(@NotNull ServicesManager servicesManager, @NotNull Logger logger) {
// Setup Vault
RegisteredServiceProvider<Economy> economyProvider = servicesManager.getRegistration(Economy.class);
if (economyProvider != null) {

View File

@ -1,38 +1,26 @@
package net.knarcraft.blacksmith.trait;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.trait.Trait;
import net.citizensnpcs.api.util.DataKey;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.config.blacksmith.BlacksmithNPCSettings;
import net.knarcraft.blacksmith.formatting.TimeFormatter;
import net.knarcraft.blacksmith.manager.EconomyManager;
import net.knarcraft.blacksmith.util.ItemHelper;
import net.knarcraft.knarlib.formatting.StringFormatter;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import static net.knarcraft.blacksmith.formatting.BlacksmithStringFormatter.sendNPCMessage;
/**
* The class representing the Blacksmith NPC trait
*/
public class BlacksmithTrait extends Trait {
private final Map<UUID, Calendar> coolDowns = new HashMap<>();
private ReforgeSession session;
public class BlacksmithTrait extends CustomTrait {
private final BlacksmithNPCSettings config;
private long _sessionStart = System.currentTimeMillis();
/**
* Instantiates a new blacksmith trait
@ -41,7 +29,8 @@ public class BlacksmithTrait extends Trait {
super("blacksmith");
//This should crash if the blacksmith plugin hasn't been properly registered
Bukkit.getServer().getPluginManager().getPlugin("Blacksmith");
this.config = new BlacksmithNPCSettings(BlacksmithPlugin.getInstance().getSettings());
this.config = new BlacksmithNPCSettings(BlacksmithPlugin.getInstance().getGlobalBlacksmithSettings());
super.setTraitSettings(this.config);
}
/**
@ -49,43 +38,17 @@ public class BlacksmithTrait extends Trait {
*
* @return <p>The current settings for this NPC</p>
*/
public BlacksmithNPCSettings getSettings() {
public @NotNull BlacksmithNPCSettings getSettings() {
return config;
}
/**
* Gets whether this blacksmith is already in a reforging session
*
* @return <p>Whether already in a reforge session</p>
*/
public boolean hasSession() {
return this.session != null;
}
/**
* Adds a cool-down for the given player's next blacksmith reforge
*
* @param playerUUID <p>The ID of the player to add the cool-down for</p>
* @param waitUntil <p>The time when the player can interact again</p>
*/
public void addCoolDown(UUID playerUUID, Calendar waitUntil) {
coolDowns.put(playerUUID, waitUntil);
}
/**
* Unsets the session of this blacksmith, making it ready for another round
*/
public void unsetSession() {
this.session = null;
}
/**
* Loads all config values stored in citizens' config file for this NPC
*
* @param key <p>The data key used for the config root</p>
*/
@Override
public void load(DataKey key) {
public void load(@NotNull DataKey key) {
config.loadVariables(key);
}
@ -95,85 +58,12 @@ public class BlacksmithTrait extends Trait {
* @param key <p>The data key used for the config root</p>
*/
@Override
public void save(DataKey key) {
public void save(@NotNull DataKey key) {
config.saveVariables(key);
}
/**
* Performs necessary work before the session is started or continued
*
* @param player <p>The player to prepare the session for</p>
* @return <p>True if preparations were successful. False if a session shouldn't be started</p>
*/
public boolean prepareForSession(Player player) {
UUID playerId = player.getUniqueId();
//If cool-down has been disabled after it was set for this player, remove the cool-down
if (config.getDisableCoolDown() && coolDowns.get(playerId) != null) {
coolDowns.remove(playerId);
}
//Deny if permission is missing
if (!player.hasPermission("blacksmith.use")) {
return false;
}
//Deny if on cool-down, or remove cool-down if expired
if (coolDowns.get(playerId) != null) {
Calendar calendar = Calendar.getInstance();
if (!calendar.after(coolDowns.get(playerId))) {
int secondDifference = (int) (coolDowns.get(playerId).getTimeInMillis() - calendar.getTimeInMillis()) / 1000;
boolean exactTime = BlacksmithPlugin.getInstance().getSettings().getShowExactTime();
sendNPCMessage(this.npc, player, StringFormatter.replacePlaceholder(config.getCoolDownUnexpiredMessage(),
"{time}", TimeFormatter.formatTime(exactTime, secondDifference)));
return false;
}
coolDowns.remove(playerId);
}
//If already in a session, but the player has failed to interact, and left the blacksmith, allow a new session
if (session != null) {
if (!session.isRunning() && (System.currentTimeMillis() > _sessionStart + 10 * 1000 ||
this.npc.getEntity().getLocation().distance(session.getPlayer().getLocation()) > 20)) {
session = null;
}
}
return true;
}
/**
* Tries to continue the session for the given player
*
* @param player <p>The player to continue the session for</p>
*/
public void continueSession(Player player) {
//Another player is using the blacksmith
if (!session.isInSession(player)) {
sendNPCMessage(this.npc, player, config.getBusyWithPlayerMessage());
return;
}
//The blacksmith is already reforging for the player
if (session.isRunning()) {
int timeRemaining = (int) ((session.getFinishTime() - System.currentTimeMillis()) / 1000);
boolean showExactTime = BlacksmithPlugin.getInstance().getSettings().getShowExactTime();
sendNPCMessage(this.npc, player, StringFormatter.replacePlaceholder(config.getBusyReforgingMessage(),
"{time}", TimeFormatter.formatTime(showExactTime, timeRemaining)));
return;
}
if (session.endSession()) {
//Quit if the player cannot afford, or has changed their item
session = null;
} else {
//Start reforging for the player
reforge(npc, player);
}
}
/**
* Starts a new session, and prepares to repair the player's item
*
* @param player <p>The player to start the session for</p>
*/
public void startSession(Player player) {
@Override
public void startSession(@NotNull Player player) {
ItemStack hand = player.getInventory().getItemInMainHand();
//Refuse if not repairable, or if reforge-able items is set, but doesn't include the held item
List<Material> reforgeAbleItems = config.getReforgeAbleItems();
@ -195,7 +85,7 @@ public class BlacksmithTrait extends Trait {
}
//Start a new reforge session for the player
_sessionStart = System.currentTimeMillis();
currentSessionStartTime = System.currentTimeMillis();
session = new ReforgeSession(this, player, npc, config);
//Tell the player the cost of repairing the item
@ -205,26 +95,9 @@ public class BlacksmithTrait extends Trait {
itemName));
}
/**
* Starts reforging the player's item
*
* @param npc <p>The NPC performing the reforge</p>
* @param player <p>The player that initiated the reforge</p>
*/
private void reforge(NPC npc, Player player) {
sendNPCMessage(this.npc, player, config.getStartReforgeMessage());
EconomyManager.withdraw(player);
session.beginReforge();
ItemStack heldItem = player.getInventory().getItemInMainHand();
//Display the item in the NPC's hand
if (npc.getEntity() instanceof Player) {
((Player) npc.getEntity()).getInventory().setItemInMainHand(heldItem);
} else {
Objects.requireNonNull(((LivingEntity) npc.getEntity()).getEquipment()).setItemInMainHand(heldItem);
}
//Remove the item from the player's inventory
player.getInventory().setItemInMainHand(null);
@Override
protected boolean showExactTime() {
return BlacksmithPlugin.getInstance().getGlobalBlacksmithSettings().getShowExactTime();
}
}

View File

@ -0,0 +1,180 @@
package net.knarcraft.blacksmith.trait;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.trait.Trait;
import net.knarcraft.blacksmith.config.TraitSettings;
import net.knarcraft.blacksmith.formatting.TimeFormatter;
import net.knarcraft.blacksmith.manager.EconomyManager;
import net.knarcraft.knarlib.formatting.StringFormatter;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import static net.knarcraft.blacksmith.formatting.BlacksmithStringFormatter.sendNPCMessage;
/**
* A custom trait that utilizes sessions
*/
public abstract class CustomTrait extends Trait {
protected Session session;
protected TraitSettings config;
protected final Map<UUID, Calendar> coolDowns = new HashMap<>();
protected long currentSessionStartTime = System.currentTimeMillis();
/**
* Instantiates a new custom trait
*
* @param name <p>The name of the new trait</p>
*/
protected CustomTrait(String name) {
super(name);
}
/**
* Sets the trait settings object containing this trait's configuration
*
* @param config <p>The trait settings to use</p>
*/
protected void setTraitSettings(TraitSettings config) {
this.config = config;
}
/**
* Starts a new session, and prepares to repair the player's item
*
* @param player <p>The player to start the session for</p>
*/
public abstract void startSession(@NotNull Player player);
/**
* Tries to continue the session for the given player
*
* @param player <p>The player to continue the session for</p>
*/
public void continueSession(@NotNull Player player) {
//Another player is using the blacksmith
if (session.isNotInSession(player)) {
sendNPCMessage(this.npc, player, config.getBusyWithPlayerMessage());
return;
}
//The blacksmith is already reforging for the player
if (session.isRunning()) {
int timeRemaining = (int) ((session.getFinishTime() - System.currentTimeMillis()) / 1000);
boolean showExactTime = showExactTime();
sendNPCMessage(this.npc, player, StringFormatter.replacePlaceholder(config.getBusyWorkingMessage(),
"{time}", TimeFormatter.formatTime(showExactTime, timeRemaining)));
return;
}
if (session.isSessionInvalid()) {
//Quit if the player cannot afford, or has changed their item
session = null;
} else {
//Start reforging for the player
startMainAction(npc, player);
}
}
/**
* Gets whether this blacksmith is already in a reforging session
*
* @return <p>Whether already in a salvage session</p>
*/
public boolean hasSession() {
return this.session != null;
}
/**
* Adds a cool-down for the given player's next blacksmith reforge
*
* @param playerUUID <p>The ID of the player to add the cool-down for</p>
* @param waitUntil <p>The time when the player can interact again</p>
*/
public void addCoolDown(@NotNull UUID playerUUID, @NotNull Calendar waitUntil) {
coolDowns.put(playerUUID, waitUntil);
}
/**
* Unsets the session of this scrapper, making it ready for another round
*/
public void unsetSession() {
this.session = null;
}
/**
* Performs necessary work before the session is started or continued
*
* @param player <p>The player to prepare the session for</p>
* @return <p>True if preparations were successful. False if a session shouldn't be started</p>
*/
public boolean prepareForSession(@NotNull Player player) {
UUID playerId = player.getUniqueId();
//If cool-down has been disabled after it was set for this player, remove the cool-down
if (config.getDisableCoolDown() && coolDowns.get(playerId) != null) {
coolDowns.remove(playerId);
}
//Deny if permission is missing
if (!player.hasPermission("blacksmith.use")) {
return false;
}
//Deny if on cool-down, or remove cool-down if expired
if (coolDowns.get(playerId) != null) {
Calendar calendar = Calendar.getInstance();
if (!calendar.after(coolDowns.get(playerId))) {
int secondDifference = (int) (coolDowns.get(playerId).getTimeInMillis() - calendar.getTimeInMillis()) / 1000;
boolean exactTime = showExactTime();
sendNPCMessage(this.npc, player, StringFormatter.replacePlaceholder(config.getCoolDownUnexpiredMessage(),
"{time}", TimeFormatter.formatTime(exactTime, secondDifference)));
return false;
}
coolDowns.remove(playerId);
}
//If already in a session, but the player has failed to interact, and left the blacksmith, allow a new session
if (session != null && !session.isRunning() && (System.currentTimeMillis() > currentSessionStartTime + 10 * 1000 ||
this.npc.getEntity().getLocation().distance(session.getPlayer().getLocation()) > 20)) {
session = null;
}
return true;
}
/**
* Gets whether to show exact time when displaying the remaining time left
*
* @return <p>Whether to show exact time, or vague words</p>
*/
protected abstract boolean showExactTime();
/**
* Starts reforging the player's item
*
* @param npc <p>The NPC performing the reforge</p>
* @param player <p>The player that initiated the reforge</p>
*/
private void startMainAction(@NotNull NPC npc, @NotNull Player player) {
sendNPCMessage(this.npc, player, config.getStartWorkingMessage());
// TODO: Differentiate between blacksmiths and scrappers when withdrawing money
EconomyManager.withdraw(player);
session.scheduleAction();
ItemStack heldItem = player.getInventory().getItemInMainHand();
//Display the item in the NPC's hand
if (npc.getEntity() instanceof Player) {
((Player) npc.getEntity()).getInventory().setItemInMainHand(heldItem);
} else {
Objects.requireNonNull(((LivingEntity) npc.getEntity()).getEquipment()).setItemInMainHand(heldItem);
}
//Remove the item from the player's inventory
player.getInventory().setItemInMainHand(null);
}
}

View File

@ -11,7 +11,7 @@ import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitScheduler;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Calendar;
@ -25,14 +25,11 @@ import static net.knarcraft.blacksmith.formatting.BlacksmithStringFormatter.send
/**
* A representation of the session between a player and a blacksmith
*/
public class ReforgeSession implements Runnable {
public class ReforgeSession extends Session implements Runnable {
private final BlacksmithTrait blacksmithTrait;
private final Player player;
private final NPC npc;
private final ItemStack itemToReforge;
private int taskId;
private long finishTime = 0;
private final BlacksmithNPCSettings config;
private static final String[] enchantments = new String[Enchantment.values().length];
private static final Random random = new Random();
@ -45,9 +42,10 @@ public class ReforgeSession implements Runnable {
* @param npc <p>The Blacksmith NPC involved in the session</p>
* @param config <p>The config to use for the session</p>
*/
ReforgeSession(BlacksmithTrait blacksmithTrait, Player player, NPC npc, BlacksmithNPCSettings config) {
ReforgeSession(@NotNull BlacksmithTrait blacksmithTrait, @NotNull Player player, @NotNull NPC npc,
@NotNull BlacksmithNPCSettings config) {
super(player);
this.blacksmithTrait = blacksmithTrait;
this.player = player;
this.npc = npc;
this.itemToReforge = player.getInventory().getItemInMainHand();
this.config = config;
@ -61,13 +59,31 @@ public class ReforgeSession implements Runnable {
}
}
/**
* Gets the time in milliseconds when this reforging session will finish
*
* @return <p>The time the reforging will finish</p>
*/
public long getFinishTime() {
return this.finishTime;
@Override
public boolean isSessionInvalid() {
// Prevent player from switching items during session
ItemStack itemInHand = player.getInventory().getItemInMainHand();
if (!itemToReforge.equals(itemInHand)) {
sendNPCMessage(this.npc, player, config.getItemChangedMessage());
return true;
}
// The player is unable to pay
if (EconomyManager.cannotPayForHeldItemReforge(player)) {
sendNPCMessage(this.npc, player, config.getInsufficientFundsMessage());
return true;
}
return false;
}
@Override
protected int getActionDelay() {
if (config.getMaxReforgeDelay() > 0) {
//Finish the reforging after a random delay between the max and min
return new Random().nextInt(config.getMaxReforgeDelay()) + config.getMinReforgeDelay();
} else {
//Finish the salvaging as soon as possible
return 0;
}
}
/**
@ -85,7 +101,22 @@ public class ReforgeSession implements Runnable {
}
//Give the item back to the player
if (!config.getDisableDelay()) {
giveReforgedItem();
//Mark this blacksmith as available
blacksmithTrait.unsetSession();
// Start cool-down
Calendar wait = Calendar.getInstance();
wait.add(Calendar.SECOND, config.getReforgeCoolDown());
blacksmithTrait.addCoolDown(player.getUniqueId(), wait);
}
/**
* Gives the reforged item back to the player
*/
private void giveReforgedItem() {
if (config.getMaxReforgeDelay() > 0) {
//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);
@ -96,14 +127,6 @@ public class ReforgeSession implements Runnable {
//It can be assumed as this happens instantly, that the player still has the item's previous slot selected
player.getInventory().setItemInMainHand(itemToReforge);
}
//Mark this blacksmith as available
blacksmithTrait.unsetSession();
// Start cool-down
Calendar wait = Calendar.getInstance();
wait.add(Calendar.SECOND, config.getReforgeCoolDown());
blacksmithTrait.addCoolDown(player.getUniqueId(), wait);
}
/**
@ -225,71 +248,4 @@ public class ReforgeSession implements Runnable {
}
}
/**
* Gets whether to end the current session
*
* <p>If the player has switched their item, or the player cannot pay, this returns true.</p>
*
* @return <p>True if the current session should end</p>
*/
public boolean endSession() {
// Prevent player from switching items during session
ItemStack itemInHand = player.getInventory().getItemInMainHand();
if (!itemToReforge.equals(itemInHand)) {
sendNPCMessage(this.npc, player, config.getItemChangedMessage());
return true;
}
// The player is unable to pay
if (!EconomyManager.canPay(player)) {
sendNPCMessage(this.npc, player, config.getInsufficientFundsMessage());
return true;
}
return false;
}
/**
* Gets whether the current session is still running
*
* @return <p>True if the current session is still running</p>
*/
public boolean isRunning() {
return BlacksmithPlugin.getInstance().getServer().getScheduler().isQueued(taskId);
}
/**
* Gets whether the given player is currently in a reforging session
*
* @param other <p>The player to check if is in session</p>
* @return <p>True if the given player is in a reforge session</p>
*/
public boolean isInSession(Player other) {
return player.getName().equals(other.getName());
}
/**
* Begins the actual item reforging
*/
public void beginReforge() {
BukkitScheduler scheduler = BlacksmithPlugin.getInstance().getServer().getScheduler();
int reforgeDelay;
if (!config.getDisableCoolDown()) {
//Finish the reforging after a random delay between the max and min
reforgeDelay = new Random().nextInt(config.getMaxReforgeDelay()) + config.getMinReforgeDelay();
} else {
//Finish the reforging as soon as possible
reforgeDelay = 0;
}
this.finishTime = System.currentTimeMillis() + (reforgeDelay * 1000L);
taskId = scheduler.scheduleSyncDelayedTask(BlacksmithPlugin.getInstance(), this, reforgeDelay * 20L);
}
/**
* Gets the player currently in this reforge session
*
* @return <p>The player currently in this reforge session</p>
*/
public Player getPlayer() {
return player;
}
}

View File

@ -0,0 +1,128 @@
package net.knarcraft.blacksmith.trait;
import net.citizensnpcs.api.npc.NPC;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.config.scrapper.ScrapperNPCSettings;
import net.knarcraft.blacksmith.util.ItemHelper;
import net.knarcraft.blacksmith.util.SalvageHelper;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import static net.knarcraft.blacksmith.formatting.BlacksmithStringFormatter.sendNPCMessage;
/**
* A representation of the session between a player and a blacksmith
*/
public class SalvageSession extends Session implements Runnable {
private final ScrapperTrait scrapperTrait;
private final NPC npc;
private final ItemStack itemToSalvage;
private final ScrapperNPCSettings config;
private final List<ItemStack> salvage;
private final int enchantmentLevels;
/**
* Instantiates a new session
*
* @param scrapperTrait <p>A reference to the blacksmith trait</p>
* @param player <p>The player initiating the session</p>
* @param npc <p>The Blacksmith NPC involved in the session</p>
* @param config <p>The config to use for the session</p>
*/
SalvageSession(@NotNull ScrapperTrait scrapperTrait, @NotNull Player player, @NotNull NPC npc,
@NotNull ScrapperNPCSettings config) {
super(player);
this.scrapperTrait = scrapperTrait;
this.npc = npc;
this.itemToSalvage = player.getInventory().getItemInMainHand();
this.config = config;
// TODO: Implement the list of items to ignore when calculating salvage
this.salvage = SalvageHelper.getSalvage(BlacksmithPlugin.getInstance().getServer(), this.itemToSalvage,
new ArrayList<>());
this.enchantmentLevels = SalvageHelper.getTotalEnchantmentLevels(this.itemToSalvage);
}
@Override
public boolean isSessionInvalid() {
// Prevent player from switching items during session
ItemStack itemInHand = player.getInventory().getItemInMainHand();
if (!itemToSalvage.equals(itemInHand)) {
sendNPCMessage(this.npc, player, config.getItemChangedMessage());
return true;
}
// TODO: Add economy check
return false;
}
@Override
protected int getActionDelay() {
if (config.getMaxSalvageDelay() > 0) {
//Finish the salvaging after a random delay between the max and min
return new Random().nextInt(config.getMaxSalvageDelay()) + config.getMinSalvageDelay();
} else {
//Finish the salvaging as soon as possible
return 0;
}
}
/**
* Runs the actual salvage which fixes the item and gives it back to the player
*/
@Override
public void run() {
// TODO: Tell the player that the salvaging was successful, or cancel giveSalvage + give the original item back?
// Give the player the result of the salvage
giveSalvage();
//Stop the reforged item from displaying in the blacksmith's hand
if (npc.getEntity() instanceof Player) {
((Player) npc.getEntity()).getInventory().setItemInMainHand(null);
} else {
Objects.requireNonNull(((LivingEntity) npc.getEntity()).getEquipment()).setItemInMainHand(null);
}
//Mark this scrapper as available
scrapperTrait.unsetSession();
// Start cool-down
Calendar wait = Calendar.getInstance();
wait.add(Calendar.SECOND, config.getSalvageCoolDown());
scrapperTrait.addCoolDown(player.getUniqueId(), wait);
}
/**
* Gives the player the calculated salvage
*/
private void giveSalvage() {
// TODO: Find a better calculation than 1 enchantment level = 1 exp level
// Gives the player back some of the XP used on an item
player.giveExpLevels(enchantmentLevels);
if (config.getDropItem() || !player.isOnline() || player.getInventory().firstEmpty() == -1) {
// If the player isn't online, or the player cannot fit the item, drop the item to prevent it from
// disappearing, even if drop item is disabled.
for (ItemStack item : salvage) {
player.getWorld().dropItemNaturally(npc.getEntity().getLocation(), item);
}
} else {
for (ItemStack item : salvage) {
if (ItemHelper.canFitItem(player.getInventory(), item)) {
player.getInventory().addItem(item);
} else {
// If the player cannot fit all the salvage, drop it on the ground
player.getWorld().dropItemNaturally(player.getLocation(), item);
}
}
}
}
}

View File

@ -1,17 +1,80 @@
package net.knarcraft.blacksmith.trait;
import net.citizensnpcs.api.trait.Trait;
import net.citizensnpcs.api.util.DataKey;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.config.scrapper.ScrapperNPCSettings;
import org.apache.commons.lang.NotImplementedException;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* The class representing a scrapper NPC trait
*/
public class ScrapperTrait extends Trait {
protected ScrapperTrait(String name) {
super(name);
}
public class ScrapperTrait extends CustomTrait {
//TODO: A scrapper will take items and turn them into the base ingredients
//TODO: If an item is enchanted, give an appropriate amount of exp
private final ScrapperNPCSettings config;
/**
* Instantiates a new blacksmith trait
*/
public ScrapperTrait() {
super("scrapper");
//This should crash if the blacksmith plugin hasn't been properly registered
Bukkit.getServer().getPluginManager().getPlugin("Blacksmith");
this.config = new ScrapperNPCSettings(BlacksmithPlugin.getInstance().getGlobalScrapperSettings());
super.setTraitSettings(this.config);
}
/**
* Gets the current settings for this NPC
*
* @return <p>The current settings for this NPC</p>
*/
public ScrapperNPCSettings getSettings() {
return config;
}
/**
* Loads all config values stored in citizens' config file for this NPC
*
* @param key <p>The data key used for the config root</p>
*/
@Override
public void load(@NotNull DataKey key) {
config.loadVariables(key);
}
/**
* Saves all config values for this NPC
*
* @param key <p>The data key used for the config root</p>
*/
@Override
public void save(@NotNull DataKey key) {
config.saveVariables(key);
}
/**
* Starts a new session, and prepares to repair the player's item
*
* @param player <p>The player to start the session for</p>
*/
public void startSession(@NotNull Player player) {
// TODO: Need some method to check which items are salvageable by the specific NPC. It should be similar to
// reforge-able items. Optionally, extended mode can be enabled, which supports any recipe?
// TODO: Check if the held item is salvageable, and give appropriate messages
// TODO: Mark the session start
// TODO: Initialize a new session
// TODO: Tell player about the required cost
throw new NotImplementedException();
}
@Override
protected boolean showExactTime() {
return BlacksmithPlugin.getInstance().getGlobalScrapperSettings().getShowExactTime();
}
}

View File

@ -0,0 +1,95 @@
package net.knarcraft.blacksmith.trait;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitScheduler;
import org.jetbrains.annotations.NotNull;
/**
* A runnable session for performing a reforging/salvage task
*/
public abstract class Session implements Runnable {
protected final Player player;
protected long finishTime;
protected int taskId;
/**
* Instantiates a new session
*
* @param player <p>The player the session belongs to</p>
*/
public Session(@NotNull Player player) {
this.player = player;
}
/**
* Gets whether the given player is currently in a reforging session
*
* @param other <p>The player to check if is in session</p>
* @return <p>False if the given player is in a reforge session</p>
*/
public boolean isNotInSession(@NotNull Player other) {
return !player.getUniqueId().equals(other.getUniqueId());
}
/**
* Gets the player currently in this reforge session
*
* @return <p>The player currently in this reforge session</p>
*/
public @NotNull Player getPlayer() {
return player;
}
/**
* Gets whether the current session is invalid, and should be ended
*
* <p>If the player has switched their item, the player cannot pay, or some other condition fails,
* this returns true.</p>
*
* @return <p>True if the current session should end</p>
*/
public abstract boolean isSessionInvalid();
/**
* Gets whether the current session is still running
*
* @return <p>True if the current session is still running</p>
*/
public boolean isRunning() {
return BlacksmithPlugin.getInstance().getServer().getScheduler().isQueued(taskId);
}
/**
* Gets the time in milliseconds when this session's action will finish
*
* <p>This returns 0 if the action hasn't been run yet.</p>
*
* @return <p>The timestamp for when the action will finish</p>
*/
public long getFinishTime() {
return this.finishTime;
}
/**
* Schedules this session's main task for after the action delay has passed
*/
public void scheduleAction() {
if (isRunning()) {
throw new IllegalStateException("Session action tried to run twice!");
}
BukkitScheduler scheduler = BlacksmithPlugin.getInstance().getServer().getScheduler();
int actionDelay = getActionDelay();
this.finishTime = System.currentTimeMillis() + (actionDelay * 1000L);
taskId = scheduler.scheduleSyncDelayedTask(BlacksmithPlugin.getInstance(), this, actionDelay * 20L);
}
/**
* Gets the delay for this session's action to finish
*
* @return <p>The delay to wait for this session's action to finish</p>
*/
protected abstract int getActionDelay();
}

View File

@ -1,5 +1,8 @@
package net.knarcraft.blacksmith.util;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
@ -18,7 +21,7 @@ public final class ConfigHelper {
* @param value <p>The raw string list value</p>
* @return <p>The value as a string list, or null if not compatible</p>
*/
public static List<String> asStringList(Object value) {
public static @Nullable List<String> asStringList(@Nullable Object value) {
if (value == null) {
return new ArrayList<>();
}
@ -42,8 +45,10 @@ public final class ConfigHelper {
*
* @param value <p>The object value to get</p>
* @return <p>The value of the given object as a double</p>
* @throws ClassCastException <p>If a non-double is given as input</p>
* @throws NumberFormatException <p>If a non-double is given as input</p>
*/
public static double asDouble(Object value) {
public static double asDouble(@NotNull Object value) throws ClassCastException, NumberFormatException {
if (value instanceof String) {
return Double.parseDouble((String) value);
} else if (value instanceof Integer) {
@ -60,12 +65,15 @@ public final class ConfigHelper {
*
* @param value <p>The object value to get</p>
* @return <p>The value of the given object as a boolean</p>
* @throws ClassCastException <p>If the given value is not a boolean</p>
*/
public static boolean asBoolean(Object value) {
if (value instanceof String) {
public static boolean asBoolean(@NotNull Object value) throws ClassCastException {
if (value instanceof Boolean booleanValue) {
return booleanValue;
} else if (value instanceof String) {
return Boolean.parseBoolean((String) value);
} else {
return (Boolean) value;
throw new ClassCastException();
}
}
@ -76,8 +84,9 @@ public final class ConfigHelper {
*
* @param value <p>The object value to get</p>
* @return <p>The value of the given object as an integer</p>
* @throws ClassCastException <p>If the given value is not an integer</p>
*/
public static int asInt(Object value) {
public static int asInt(@NotNull Object value) throws ClassCastException {
if (value instanceof String) {
return Integer.parseInt((String) value);
} else {

View File

@ -3,6 +3,8 @@ package net.knarcraft.blacksmith.util;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.enchantments.Enchantment;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A helper class for parsing input into proper object types
@ -19,7 +21,7 @@ public final class InputParsingHelper {
* @param input <p>The input to check</p>
* @return <p>True if the value is empty</p>
*/
public static boolean isEmpty(String input) {
public static boolean isEmpty(@Nullable String input) {
return input == null || input.equalsIgnoreCase("null") || input.equals("\"\"") ||
input.isBlank() || input.equals("-1");
}
@ -30,7 +32,7 @@ public final class InputParsingHelper {
* @param input <p>The string to match to a material</p>
* @return <p>The material matching the string, or null if not found</p>
*/
public static Material matchMaterial(String input) {
public static @Nullable Material matchMaterial(@NotNull String input) {
return Material.matchMaterial(input.replace("-", "_"));
}
@ -40,7 +42,7 @@ public final class InputParsingHelper {
* @param input <p>The string to match to an enchantment</p>
* @return <p>The enchantment matching the string, or null if not found</p>
*/
public static Enchantment matchEnchantment(String input) {
public static @Nullable Enchantment matchEnchantment(@NotNull String input) {
try {
return Enchantment.getByKey(NamespacedKey.minecraft(
input.replace("-", "_").replace(" ", "_").toLowerCase()));
@ -56,7 +58,7 @@ public final class InputParsingHelper {
* @param input <p>The input to RegExIfy</p>
* @return <p>The converted input</p>
*/
public static String regExIfy(String input) {
public static @NotNull String regExIfy(@NotNull String input) {
return input.replace("*", ".*").toUpperCase().replace("-", "_");
}

View File

@ -1,9 +1,11 @@
package net.knarcraft.blacksmith.util;
import org.bukkit.Material;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.Damageable;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
@ -23,7 +25,7 @@ public final class ItemHelper {
* @param item <p>The item to check</p>
* @return <p>True if the item is repairable</p>
*/
public static boolean isRepairable(ItemStack item) {
public static boolean isRepairable(@NotNull ItemStack item) {
return item.getItemMeta() instanceof Damageable && getMaxDurability(item) > 0;
}
@ -33,7 +35,7 @@ public final class ItemHelper {
* @param itemStack <p>The item to get the durability of</p>
* @return <p>The max durability of the item</p>
*/
public static short getMaxDurability(ItemStack itemStack) {
public static short getMaxDurability(@NotNull ItemStack itemStack) {
return itemStack.getType().getMaxDurability();
}
@ -43,7 +45,7 @@ public final class ItemHelper {
* @param itemStack <p>The item to get the durability of</p>
* @return <p>The durability of the item</p>
*/
public static short getDurability(ItemStack itemStack) {
public static short getDurability(@NotNull ItemStack itemStack) {
Damageable damageable = (Damageable) itemStack.getItemMeta();
int maxDurability = getMaxDurability(itemStack);
if (damageable != null) {
@ -59,7 +61,7 @@ public final class ItemHelper {
* @param itemStack <p>The damage done to the item</p>
* @return <p>The damage done to the item</p>
*/
public static short getDamage(ItemStack itemStack) {
public static short getDamage(@NotNull ItemStack itemStack) {
Damageable damageable = (Damageable) itemStack.getItemMeta();
if (damageable != null) {
return (short) damageable.getDamage();
@ -75,7 +77,7 @@ public final class ItemHelper {
* @param newDamage <p>The new damage done</p>
* @return <p>True if the damage was updated. False if not damageable.</p>
*/
public static boolean updateDamage(ItemStack item, int newDamage) {
public static boolean updateDamage(@NotNull ItemStack item, int newDamage) {
ItemMeta meta = item.getItemMeta();
if (!(meta instanceof Damageable damageable)) {
return false;
@ -90,7 +92,7 @@ public final class ItemHelper {
*
* @return <p>A complete list of reforge-able materials</p>
*/
public static List<Material> getAllReforgeAbleMaterials() {
public static @NotNull List<Material> getAllReforgeAbleMaterials() {
List<Material> reforgeAbleMaterials = new ArrayList<>();
for (Material material : Material.values()) {
ItemStack item = new ItemStack(material);
@ -108,10 +110,49 @@ public final class ItemHelper {
* @param requireDamaged <p>Whether only a damaged anvil should count</p>
* @return <p>True if the given material is an anvil</p>
*/
public static boolean isAnvil(Material material, boolean requireDamaged) {
public static boolean isAnvil(@NotNull Material material, boolean requireDamaged) {
boolean isDamagedAnvil = material == Material.CHIPPED_ANVIL || material == Material.DAMAGED_ANVIL;
boolean isAnvil = isDamagedAnvil || material == Material.ANVIL;
return (requireDamaged && isDamagedAnvil) || (!requireDamaged && isAnvil);
}
/**
* Checks whether the given inventory is able to fit the given item
*
* @param inventory <p>The inventory to check</p>
* @param item <p>The item to check</p>
* @return <p>True if the inventory can fit the item</p>
*/
public static boolean canFitItem(@NotNull Inventory inventory, @NotNull ItemStack item) {
// If a slot is available, there is no problem
if (inventory.firstEmpty() != -1) {
return true;
}
// If the inventory doesn't contain the correct type of item, stacking is impossible
if (!inventory.contains(item.getType())) {
return false;
}
// Check if the item stack can fit in the inventory if stacked with existing items
int availableSlots = 0;
for (ItemStack itemStack : inventory.getStorageContents()) {
ItemMeta itemMeta = itemStack.getItemMeta();
ItemMeta targetMeta = item.getItemMeta();
// Skip items of a different type, or with metadata that would prevent stacking
if (itemStack.getType() != item.getType() ||
(itemMeta != null && targetMeta != null && !itemMeta.equals(targetMeta))) {
continue;
}
availableSlots += itemStack.getMaxStackSize() - itemStack.getAmount();
if (availableSlots < item.getAmount()) {
return true;
}
}
return false;
}
}

View File

@ -2,10 +2,13 @@ package net.knarcraft.blacksmith.util;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.Recipe;
import org.bukkit.inventory.ShapedRecipe;
import org.bukkit.inventory.ShapelessRecipe;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
@ -18,6 +21,20 @@ import java.util.Random;
*/
public final class SalvageHelper {
/**
* Gets the sum of all enchantment levels for the given item
*
* @param item <p>The item to check</p>
* @return <p>The total amount of enchantment levels on the item</p>
*/
public static int getTotalEnchantmentLevels(@NotNull ItemStack item) {
int expLevels = 0;
for (Enchantment enchantment : item.getEnchantments().keySet()) {
expLevels += item.getEnchantmentLevel(enchantment);
}
return expLevels;
}
/**
* Gets the items to return if salvaging the given item stack
*
@ -28,7 +45,8 @@ public final class SalvageHelper {
* @param ignoredSalvage <p>Any material which should not be returned as part of the salvage.</p>
* @return <p>The items to return to the user, or null if not salvageable</p>
*/
public static List<ItemStack> getSalvage(Server server, ItemStack salvagedItem, List<Material> ignoredSalvage) {
public static @Nullable List<ItemStack> getSalvage(@NotNull Server server, @Nullable ItemStack salvagedItem,
@NotNull List<Material> ignoredSalvage) {
if (salvagedItem == null || salvagedItem.getAmount() < 1 ||
!ItemHelper.isRepairable(salvagedItem)) {
return null;
@ -52,8 +70,8 @@ public final class SalvageHelper {
* @param ignoredSalvage <p>Any material which should not be returned as part of the salvage.</p>
* @return <p>A list of items, or null if not a valid type of recipe</p>
*/
private static List<ItemStack> getRecipeSalvage(Recipe recipe, ItemStack salvagedItem,
List<Material> ignoredSalvage) {
private static @Nullable List<ItemStack> getRecipeSalvage(@NotNull Recipe recipe, @NotNull ItemStack salvagedItem,
@NotNull List<Material> ignoredSalvage) {
List<ItemStack> ingredients;
if (recipe instanceof ShapedRecipe shapedRecipe) {
ingredients = getIngredients(shapedRecipe);
@ -67,9 +85,7 @@ public final class SalvageHelper {
ingredients = combineStacks(ingredients);
//Purge any ignored salvage to only calculate salvage using the remaining items
if (ignoredSalvage != null) {
ingredients.removeIf((item) -> ignoredSalvage.contains(item.getType()));
}
ingredients.removeIf((item) -> ignoredSalvage.contains(item.getType()));
Random random = new Random();
@ -90,7 +106,7 @@ public final class SalvageHelper {
* @param itemsToCopy <p>The items to make a copy of</p>
* @return <p>A copy of the given items</p>
*/
private static List<ItemStack> copyItems(List<ItemStack> itemsToCopy) {
private static @NotNull List<ItemStack> copyItems(@NotNull List<ItemStack> itemsToCopy) {
List<ItemStack> copies = new ArrayList<>(itemsToCopy.size());
for (ItemStack itemStack : itemsToCopy) {
copies.add(new ItemStack(itemStack.getType(), itemStack.getAmount()));
@ -106,10 +122,11 @@ public final class SalvageHelper {
* @param random <p>The randomness generator to use</p>
* @return <p>The items to be returned to the user as salvage</p>
*/
private static List<ItemStack> getSalvage(List<ItemStack> recipeItems, ItemStack salvagedItem, Random random) {
private static @NotNull List<ItemStack> getSalvage(@NotNull List<ItemStack> recipeItems,
@NotNull ItemStack salvagedItem, @NotNull Random random) {
double percentageRemaining = (ItemHelper.getDurability(salvagedItem) /
(double) ItemHelper.getMaxDurability(salvagedItem));
int totalItems = totalItems(recipeItems);
int totalItems = totalItemAmount(recipeItems);
//Get the amount of recipe items to be returned
int itemsToReturn = (int) Math.floor(percentageRemaining * totalItems);
int bound = recipeItems.size();
@ -137,7 +154,7 @@ public final class SalvageHelper {
* @param items <p>The items to get the sum of</p>
* @return <p>The total number of items</p>
*/
private static int totalItems(List<ItemStack> items) {
private static int totalItemAmount(@NotNull List<ItemStack> items) {
int sum = 0;
for (ItemStack itemStack : items) {
sum += itemStack.getAmount();
@ -154,7 +171,7 @@ public final class SalvageHelper {
* @param items <p>The items to combine</p>
* @return <p>The given items, but combined</p>
*/
private static List<ItemStack> combineStacks(List<ItemStack> items) {
private static @NotNull List<ItemStack> combineStacks(@NotNull List<ItemStack> items) {
Map<Material, Integer> itemAmounts = new HashMap<>();
for (ItemStack item : items) {
Material itemType = item.getType();
@ -174,7 +191,7 @@ public final class SalvageHelper {
* @param shapedRecipe <p>The shaped recipe to get ingredients for</p>
* @return <p>The items contained in the recipe</p>
*/
private static List<ItemStack> getIngredients(ShapedRecipe shapedRecipe) {
private static @NotNull List<ItemStack> getIngredients(@NotNull ShapedRecipe shapedRecipe) {
List<ItemStack> ingredients = new ArrayList<>();
Map<Character, ItemStack> ingredientMap = shapedRecipe.getIngredientMap();
//The shape is a list of the three rows' strings. Each string contains 3 characters.

View File

@ -5,6 +5,7 @@ import net.knarcraft.blacksmith.config.SmithPreset;
import net.knarcraft.blacksmith.config.SmithPresetFilter;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
@ -24,7 +25,7 @@ public final class TabCompleteValuesHelper {
* @param valueType <p>The value type to get possible values for</p>
* @return <p>The values to show the user</p>
*/
public static List<String> getTabCompletions(SettingValueType valueType) {
public static @NotNull List<String> getTabCompletions(@NotNull SettingValueType valueType) {
return switch (valueType) {
case POSITIVE_INTEGER -> getPositiveIntegers();
case BOOLEAN -> getBooleans();
@ -43,7 +44,7 @@ public final class TabCompleteValuesHelper {
*
* @return <p>Some example enchantment block lists</p>
*/
private static List<String> getExampleEnchantmentBlockLists() {
private static @NotNull List<String> getExampleEnchantmentBlockLists() {
List<String> exampleBlockLists = new ArrayList<>();
exampleBlockLists.add(Enchantment.VANISHING_CURSE.getKey().getKey() + "," +
Enchantment.BINDING_CURSE.getKey().getKey() + "," + Enchantment.MENDING.getKey().getKey());
@ -58,7 +59,7 @@ public final class TabCompleteValuesHelper {
*
* @return <p>A complete list of reforge-able material names</p>
*/
private static List<String> getAllReforgeAbleMaterialNames() {
private static @NotNull List<String> getAllReforgeAbleMaterialNames() {
List<String> reforgeAbleMaterials = new ArrayList<>();
for (Material material : ItemHelper.getAllReforgeAbleMaterials()) {
reforgeAbleMaterials.add(material.name());
@ -80,7 +81,7 @@ public final class TabCompleteValuesHelper {
*
* @return <p>A complete list of enchantments</p>
*/
private static List<String> getAllEnchantments() {
private static @NotNull List<String> getAllEnchantments() {
List<String> enchantments = new ArrayList<>();
for (Enchantment enchantment : Enchantment.values()) {
enchantments.add(enchantment.getKey().getKey());
@ -93,7 +94,7 @@ public final class TabCompleteValuesHelper {
*
* @return <p>Some example possible values for reforge-able materials</p>
*/
private static List<String> getReforgeAbleMaterials() {
private static @NotNull List<String> getReforgeAbleMaterials() {
List<String> stringLists = new ArrayList<>();
for (SmithPreset preset : SmithPreset.values()) {
stringLists.add("preset:" + preset.name());
@ -114,7 +115,7 @@ public final class TabCompleteValuesHelper {
*
* @return <p>Some example string values</p>
*/
private static List<String> getStrings() {
private static @NotNull List<String> getStrings() {
List<String> strings = new ArrayList<>(1);
strings.add("&aExample message. Use & for color tags.");
return strings;
@ -125,7 +126,7 @@ public final class TabCompleteValuesHelper {
*
* @return <p>Some example percentage values</p>
*/
private static List<String> getPercentages() {
private static @NotNull List<String> getPercentages() {
List<String> percentages = new ArrayList<>(6);
percentages.add("0");
percentages.add("10");
@ -141,7 +142,7 @@ public final class TabCompleteValuesHelper {
*
* @return <p>Some possible positive doubles</p>
*/
private static List<String> getPositiveDoubles() {
private static @NotNull List<String> getPositiveDoubles() {
List<String> positiveDoubles = new ArrayList<>(4);
positiveDoubles.add("0.0");
positiveDoubles.add("0.0001");
@ -156,7 +157,7 @@ public final class TabCompleteValuesHelper {
*
* @return <p>Some example positive integers</p>
*/
private static List<String> getPositiveIntegers() {
private static @NotNull List<String> getPositiveIntegers() {
List<String> positiveIntegers = new ArrayList<>(6);
positiveIntegers.add("0");
positiveIntegers.add("5");
@ -173,7 +174,7 @@ public final class TabCompleteValuesHelper {
*
* @return <p>The possible boolean values</p>
*/
private static List<String> getBooleans() {
private static @NotNull List<String> getBooleans() {
List<String> booleans = new ArrayList<>(2);
booleans.add("True");
booleans.add("False");

View File

@ -6,6 +6,8 @@ import net.knarcraft.blacksmith.formatting.BlacksmithTranslatableMessage;
import org.bukkit.Material;
import org.bukkit.command.CommandSender;
import org.bukkit.enchantments.Enchantment;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
@ -26,7 +28,8 @@ public final class TypeValidationHelper {
* @param sender <p>The command sender to use for printing error messages</p>
* @return <p>True if the value is valid</p>
*/
public static boolean isValid(SettingValueType valueType, Object value, CommandSender sender) {
public static boolean isValid(@NotNull SettingValueType valueType, @Nullable Object value,
@Nullable CommandSender sender) {
try {
return switch (valueType) {
case POSITIVE_DOUBLE -> isPositiveDouble(value, sender);
@ -51,7 +54,7 @@ public final class TypeValidationHelper {
* @param sender <p>The command sender to use for printing error messages</p>
* @return <p>True if the value is a material</p>
*/
private static boolean isMaterial(Object value, CommandSender sender) {
private static boolean isMaterial(@Nullable Object value, @Nullable CommandSender sender) {
boolean isMaterial = value instanceof Material || (value instanceof String string &&
InputParsingHelper.matchMaterial(string) != null);
if (!isMaterial && sender != null) {
@ -68,7 +71,7 @@ public final class TypeValidationHelper {
* @param sender <p>The command sender to use for printing error messages</p>
* @return <p>True if the value is an enchantment</p>
*/
private static boolean isEnchantment(Object value, CommandSender sender) {
private static boolean isEnchantment(@Nullable Object value, @Nullable CommandSender sender) {
boolean isEnchantment = value instanceof Enchantment || (value instanceof String string &&
InputParsingHelper.matchEnchantment(string) != null);
if (!isEnchantment && sender != null) {
@ -85,7 +88,7 @@ public final class TypeValidationHelper {
* @param sender <p>The command sender to use for printing error messages</p>
* @return <p>True if the value is a string list</p>
*/
private static boolean isStringList(Object value, CommandSender sender) {
private static boolean isStringList(@Nullable Object value, @Nullable CommandSender sender) {
boolean isStringList = value instanceof String[] || value instanceof List<?> || value instanceof String;
if (!isStringList && sender != null) {
BlacksmithPlugin.getStringFormatter().displayErrorMessage(sender,
@ -101,9 +104,9 @@ public final class TypeValidationHelper {
* @param sender <p>The command sender to use for printing error messages</p>
* @return <p>True if the value is a percentage</p>
*/
private static boolean isPercentage(Object value, CommandSender sender) {
boolean isPercentage = isPositiveInteger(value, null) && ConfigHelper.asInt(value) >= 0 &&
ConfigHelper.asInt(value) <= 100;
private static boolean isPercentage(@Nullable Object value, @Nullable CommandSender sender) {
boolean isPercentage = value != null && isPositiveInteger(value, null) &&
ConfigHelper.asInt(value) >= 0 && ConfigHelper.asInt(value) <= 100;
if (!isPercentage && sender != null) {
BlacksmithPlugin.getStringFormatter().displayErrorMessage(sender,
BlacksmithTranslatableMessage.INPUT_PERCENTAGE_REQUIRED);
@ -118,7 +121,7 @@ public final class TypeValidationHelper {
* @param sender <p>The command sender to use for printing error messages</p>
* @return <p>True if the value is a non-empty string</p>
*/
private static boolean isNonEmptyString(Object value, CommandSender sender) {
private static boolean isNonEmptyString(@Nullable Object value, @Nullable CommandSender sender) {
boolean isString = value instanceof String string && !string.isBlank();
if (!isString && sender != null) {
BlacksmithPlugin.getStringFormatter().displayErrorMessage(sender,
@ -134,9 +137,9 @@ public final class TypeValidationHelper {
* @param sender <p>The command sender to use for printing error messages</p>
* @return <p>True if the value is a positive double</p>
*/
private static boolean isPositiveDouble(Object value, CommandSender sender) {
private static boolean isPositiveDouble(@Nullable Object value, @Nullable CommandSender sender) {
try {
return ConfigHelper.asDouble(value) >= 0.0;
return value != null && ConfigHelper.asDouble(value) >= 0.0;
} catch (NumberFormatException | NullPointerException exception) {
if (sender != null) {
BlacksmithPlugin.getStringFormatter().displayErrorMessage(sender,
@ -153,9 +156,9 @@ public final class TypeValidationHelper {
* @param sender <p>The command sender to use for printing error messages</p>
* @return <p>True if the value is a positive integer</p>
*/
private static boolean isPositiveInteger(Object value, CommandSender sender) {
private static boolean isPositiveInteger(@Nullable Object value, @Nullable CommandSender sender) {
try {
return ConfigHelper.asInt(value) >= 0;
return value != null && ConfigHelper.asInt(value) >= 0;
} catch (NumberFormatException | NullPointerException exception) {
if (sender != null) {
BlacksmithPlugin.getStringFormatter().displayErrorMessage(sender,

View File

@ -11,21 +11,21 @@ blacksmith:
basePrice:
# You can add, for example "diamond-sword: 15.3" to change the base cost for a specific item
default: 10.0
# The additional cost for each durability point missing (natural cost) or present (not natural cost)
pricePerDurabilityPoint:
# You can add, for example "diamond-sword: 0.09" to change the base cost for a specific item
default: 0.005
# The additional cost for each enchantment level present on an item
enchantmentCost:
# You can add, for example "arrow-infinite: 0.09" to change the enchantment cost for a specific enchantment
default: 5
# Natural cost makes re-forging more expensive the more damaged the item is. Disabling this will enable the legacy
# blacksmith behavior instead
useNaturalCost: true
# Exact time displays the exact number of seconds and minutes remaining as part of the reforging cool-down and
# reforging delay messages, instead of just vaguely hinting at the remaining time.
showExactTime: false
@ -41,76 +41,76 @@ blacksmith:
defaults:
# Whether the item will drop a reforged item on the ground, instead of putting it into the user's inventory
dropItem: true
# The items a blacksmith is able to reforge. Setting this only allows NPCs to repair the listed items. This should be
# set for each individual NPC, and should not be set here, unless you want to restrict NPCs which have not been set
# up yet.
reforgeAbleItems: [ ]
# The enchantments a blacksmith is denied from applying to an item. Disable anything you find too op or annoying.
enchantmentBlocklist: [ "binding_curse", "mending", "vanishing_curse" ]
# The chance to fail reforging an item, which only repairs the item a tiny bit or not at all (0-100)
failReforgeChance: 10 # Default = 10%
# Whether failed reforging should remove or downgrade the item's enchantments
failReforgeRemovesEnchantments: false # Default = false
# The chance that an enchantment will be added to the reforged item (0-100)
extraEnchantmentChance: 5 # Default = 5%
# The maximum number of enchantments the blacksmith will try to add
maxEnchantments: 3
# Whether the blacksmith will reforge anvils as a special case
reforgeAnvils: false
# All settable delays
delaysInSeconds:
# The maximum time for a reforging to finish
maximum: 30
# The minimum time for a reforging to finish
minimum: 5
# The cool-down period between each reforge
reforgeCoolDown: 60
# The title describing the blacksmith's usage/speciality
blacksmithTitle: "blacksmith"
# The title describing the blacksmith's usage/speciality
blacksmithTitle: "blacksmith"
# All messages used by the NPC
messages:
# The message to display when another player is using the blacksmith
busyPlayerMessage: "&cI'm busy at the moment. Come back later!"
# The message to display when the blacksmith is working on the reforging
busyReforgeMessage: "&cI'm working on it. Be patient! I'll finish {time}!"
# The message to display when the blacksmith is still on a cool-down from the previous re-forging
coolDownUnexpiredMessage: "&cYou've already had your chance! Give me a break! I'll be ready {time}!"
# The message to display when informing a player about the reforging cost
costMessage: "&eIt will cost &a{cost}&e to reforge that &a{item}&e! Click again to reforge!"
# The message to display when the blacksmith fails to reforge an item
failReforgeMessage: "&cWhoops! Didn't mean to do that! Maybe next time?"
# The message to display when a player cannot pay for the reforging
insufficientFundsMessage: "&cYou don't have enough money to reforge that item!"
# The message to display when holding an item the blacksmith is unable to reforge
invalidItemMessage: "&cI'm sorry, but I'm a/an {title}, I don't know how to reforge that!"
# The message to display when presenting a different item than the one just evaluated
itemChangedMessage: "&cThat's not the item you wanted to reforge before!"
# The message to display once the blacksmith starts re-forging
startReforgeMessage: "&eOk, let's see what I can do..."
# The message to display once the reforging has successfully finished
successMessage: "There you go! All better!"
# The message to display if a player is trying to reforge an item with full durability
notDamagedMessage: "&cThat item is not in need of repair"
@ -137,30 +137,41 @@ scrapper:
# set for each individual NPC, and should not be set here, unless you want to restrict NPCs which have not been set
# up yet.
salvageAbleItems: [ ]
messages:
# The message to display when another player is using the scrapper
busyPlayerMessage: "&cI'm busy at the moment. Come back later!"
# The message to display when the blacksmith is working on the salvaging
busySalvageMessage: "&cI'm working on it. Be patient! I'll finish {time}!"
# The message to display when the blacksmith is still on a cool-down from the previous re-forging
coolDownUnexpiredMessage: "&cYou've already had your chance! Give me a break! I'll be ready {time}!"
# The message to display if the player tries to salvage an item the blacksmith cannot salvage
cannotSalvageMessage: "&cI'm unable to salvage that item"
# All settable delays
delaysInSeconds:
# The maximum time for a salvaging to finish
maximum: 30
# The message to display if reforging the player's item would result in no salvage
tooDamagedForSalvageMessage: "&cThat item is too damaged to be salvaged into anything useful"
# The minimum time for a salvaging to finish
minimum: 5
# The message to display when an item is successfully salvaged
successSalvagedMessage: "&cThere you go!"
# The message to display when the blacksmith fails to reforge an item
failSalvageMessage: "&cWhoops! The item broke! Maybe next time?"
# The message to display when presenting a different item than the one just evaluated
itemChangedMessage: "&cThat's not the item you wanted to salvage before!"
# The message to display once the blacksmith starts re-forging
startSalvageMessage: "&eOk, let's see what I can do..."
# The cool-down period between each salvage
salvageCoolDown: 60
messages:
# The message to display when another player is using the scrapper
busyPlayerMessage: "&cI'm busy at the moment. Come back later!"
# The message to display when the blacksmith is working on the salvaging
busySalvageMessage: "&cI'm working on it. Be patient! I'll finish {time}!"
# The message to display when the blacksmith is still on a cool-down from the previous re-forging
coolDownUnexpiredMessage: "&cYou've already had your chance! Give me a break! I'll be ready {time}!"
# The message to display if the player tries to salvage an item the blacksmith cannot salvage
cannotSalvageMessage: "&cI'm unable to salvage that item"
# The message to display if reforging the player's item would result in no salvage
tooDamagedForSalvageMessage: "&cThat item is too damaged to be salvaged into anything useful"
# The message to display when an item is successfully salvaged
successSalvagedMessage: "&cThere you go!"
# The message to display when the blacksmith fails to reforge an item
failSalvageMessage: "&cWhoops! The item broke! Maybe next time?"
# The message to display when presenting a different item than the one just evaluated
itemChangedMessage: "&cThat's not the item you wanted to salvage before!"
# The message to display once the blacksmith starts re-forging
startSalvageMessage: "&eOk, let's see what I can do..."

View File

@ -16,7 +16,15 @@ commands:
blacksmithconfig:
permission: blacksmith.admin
usage: /<command> <option/reload> [new value]
description: Used for configuring default blacksmith settings, or global settings
description: Used for configuring default blacksmith settings, or global blacksmith settings
scrapper:
permission: blacksmith.edit
usage: /<command> <option> [new value]
description: Used for configuring the selected scrapper NPC
scrapperconfig:
permission: blacksmith.admin
usage: /<command> <option/reload> [new value]
description: Used for configuring default scrapper settings, or global scrapper settings
preset:
permission: blacksmith.preset
usage: /<command> <preset>[:filter]

View File

@ -19,6 +19,7 @@ import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.fail;
/**
* A test class to test salvaging
@ -40,14 +41,14 @@ public class SalvageHelperTest {
@Test
public void getNullForInvalidItemTest() {
//Assert that a non-reforge-able item will return null
assertNull(SalvageHelper.getSalvage(server, new ItemStack(Material.POTATO, 1), null));
assertNull(SalvageHelper.getSalvage(server, new ItemStack(Material.POTATO, 1), new ArrayList<>()));
}
@Test
public void getNullForLessThanOneItemTest() {
//Assert that 0 or 1 items will return null
assertNull(SalvageHelper.getSalvage(server, new ItemStack(Material.IRON_AXE, 0), null));
assertNull(SalvageHelper.getSalvage(server, new ItemStack(Material.IRON_SWORD, -1), null));
assertNull(SalvageHelper.getSalvage(server, new ItemStack(Material.IRON_AXE, 0), new ArrayList<>()));
assertNull(SalvageHelper.getSalvage(server, new ItemStack(Material.IRON_SWORD, -1), new ArrayList<>()));
}
@Test
@ -57,7 +58,12 @@ public class SalvageHelperTest {
expectedSalvage.add(new ItemStack(Material.STICK, 2));
ItemStack itemToSalvage = new ItemStack(Material.DIAMOND_PICKAXE, 1);
//Note: Conversion to sets makes sure the order doesn't matter
assertEquals(expectedSalvage, new HashSet<>(SalvageHelper.getSalvage(server, itemToSalvage, null)));
List<ItemStack> salvage = SalvageHelper.getSalvage(server, itemToSalvage, new ArrayList<>());
if (salvage == null) {
fail();
} else {
assertEquals(expectedSalvage, new HashSet<>(salvage));
}
}
@Test
@ -67,7 +73,12 @@ public class SalvageHelperTest {
expectedSalvage.add(new ItemStack(Material.STICK, 14));
ItemStack itemToSalvage = new ItemStack(Material.DIAMOND_PICKAXE, 7);
//Note: Conversion to sets makes sure the order doesn't matter
assertEquals(expectedSalvage, new HashSet<>(SalvageHelper.getSalvage(server, itemToSalvage, null)));
List<ItemStack> salvage = SalvageHelper.getSalvage(server, itemToSalvage, new ArrayList<>());
if (salvage == null) {
fail();
} else {
assertEquals(expectedSalvage, new HashSet<>(salvage));
}
}
@Test
@ -82,7 +93,7 @@ public class SalvageHelperTest {
damageable.setDamage(100);
}
itemToSalvage.setItemMeta(meta);
List<ItemStack> salvage = SalvageHelper.getSalvage(server, itemToSalvage, null);
List<ItemStack> salvage = SalvageHelper.getSalvage(server, itemToSalvage, new ArrayList<>());
//Assert that some items are given
assertNotEquals(salvage, new ArrayList<>());
//Assert that a damaged item won't give full salvage