34 Commits

Author SHA1 Message Date
0d077ccdbb Bumps version to 1.0.4 for release
All checks were successful
EpicKnarvik97/Blacksmith/pipeline/head This commit looks good
2023-01-11 14:38:35 +01:00
1bc03d7670 Improves some README descriptions 2023-01-11 14:29:59 +01:00
f192a5a2b5 Fixes a bug in the preset prefix logic
All checks were successful
EpicKnarvik97/Blacksmith/pipeline/head This commit looks good
2023-01-11 02:46:30 +01:00
bd00a59d08 Makes it possible to edit reforgeAbleItems globally #17 2023-01-11 01:54:52 +01:00
8423eabc57 Fixes handling of preset replacement 2023-01-11 01:32:48 +01:00
488d4c7589 Makes some settings inherit properly #16 2023-01-11 00:51:05 +01:00
753c7c6275 Adds optional ability to reforge anvils #15
All checks were successful
EpicKnarvik97/Blacksmith/pipeline/head This commit looks good
2023-01-09 23:47:17 +01:00
a856aa03e0 Implements material wildcards for costs #14
All checks were successful
EpicKnarvik97/Blacksmith/pipeline/head This commit looks good
2023-01-09 16:53:52 +01:00
913cc5736e Prevents filters from matching non-reforge-able materials 2023-01-09 15:25:05 +01:00
a5ae3cb295 Fixes a bug caused by "-" being consumed
All checks were successful
EpicKnarvik97/Blacksmith/pipeline/head This commit looks good
2023-01-09 15:13:55 +01:00
7d940ee334 Updates dependencies
All checks were successful
EpicKnarvik97/Blacksmith/pipeline/head This commit looks good
2023-01-09 05:06:33 +01:00
ea54492ccf Adds negation for material names and presets #13 2023-01-09 05:03:28 +01:00
30b8507b9f Improves the tab-completion values for reforgeAbleItems
All checks were successful
EpicKnarvik97/Blacksmith/pipeline/head This commit looks good
2023-01-06 14:52:30 +01:00
c5ffa2b0c4 Improves setup information 2023-01-06 00:03:33 +01:00
a9cfea2d32 Fixes a repo name
All checks were successful
EpicKnarvik97/Blacksmith/pipeline/head This commit looks good
2022-11-26 15:37:14 +01:00
1bab972c13 Improves Jenkinsfile 2022-11-26 15:14:53 +01:00
d1c549fd20 Adds auto deploy
All checks were successful
EpicKnarvik97/Blacksmith/pipeline/head This commit looks good
2022-11-26 14:51:52 +01:00
c9ff3af6ac Adds distribution management and Jenkinsfile
All checks were successful
EpicKnarvik97/Blacksmith/pipeline/head This commit looks good
2022-11-26 04:13:41 +01:00
5089a721a0 Fixes some minor issues
Fixes some repositories using http instead of https
Fixes formatting for tables
2022-11-14 02:44:18 +01:00
a036c39dc3 Updates code to account for KnarLib changes 2022-11-07 22:22:23 +01:00
a501a3cbb4 Changes things for the non-static Translator 2022-11-07 15:20:30 +01:00
7875e9a705 Adds missing time unit strings 2022-11-07 01:59:49 +01:00
ac5f032ce9 Uses a placeholder for the plugin version 2022-11-07 00:22:03 +01:00
7784ee46a5 Adds an alert if an update is available 2022-11-07 00:18:13 +01:00
3c805ee284 Uses KnarLib for common tasks 2022-11-07 00:07:32 +01:00
3c4394d6fa Bumps version to 1.0.3 2022-11-05 04:40:09 +01:00
d0f4ff11b7 Adds missing null checks before asStringList 2022-11-05 04:39:18 +01:00
ee2b503886 Bumps version to 1.0.2 2022-11-05 04:28:28 +01:00
89c1c4a56c Adds a new enchantmentBlocklist option
The enchantmentBlocklist allows specifying a list of enchantments blacksmiths should be blocked from adding to items. Especially curse of binding and curse of vanishing are problematic as the effect is quite bad. Mending is possibly undesirable as it's supposed to be quite rare.
2022-11-05 04:21:47 +01:00
012d1df099 Allows 0 for positive integers and doubles
It wasn't possible to set extra enchantment chance to 0. This fixes that problem.
2022-11-04 21:31:56 +01:00
10bfd29468 Removes outdated presets for the reforgeAbleItems list 2022-10-26 21:02:36 +02:00
849e9d7a9d Adds Economy plugin to dependencies to prevent confusion 2022-10-26 20:33:44 +02:00
89d1f79f7d Improves description of the reforge-able items format 2022-10-26 20:20:35 +02:00
36e254c0df Fixes README formatting creating an unintended space 2022-10-26 20:08:34 +02:00
37 changed files with 966 additions and 715 deletions

33
Jenkinsfile vendored Normal file
View File

@ -0,0 +1,33 @@
pipeline {
agent any
tools {
jdk 'JDK17'
}
stages {
stage('Build') {
steps {
echo 'Building...'
sh 'mvn clean & mvn validate & mvn compile'
}
}
stage('Test') {
steps {
echo 'Testing...'
sh 'mvn test'
}
}
stage('Verify') {
steps {
echo 'Verifying...'
sh 'mvn verify -Dmaven.test.skip=true'
}
}
stage('Deploy') {
steps {
echo 'Deploying...'
sh 'mvn deploy -Dmaven.install.skip=true -Dmaven.test.skip=true'
archiveArtifacts artifacts: '**/target/*.jar', fingerprint: true
}
}
}
}

119
README.md
View File

@ -10,7 +10,7 @@ fee. Costs are highly customizable.
- By default, natural cost is used. The original fork made it cheaper the more damaged an item is, but natural cost
makes the cost increase the more damaged the item is.
- EnchantmentTarget is used instead of a hard-coded list of repairable items
- All settings (except default reforge-able-items), both global and for each blacksmith, can be changed using commands,
- All settings, both global and for each blacksmith, can be changed using commands,
and support tab-completion.
- This plugin is not directly compatible with the original. If you are using the old one, you will need to set it up
again!
@ -19,12 +19,16 @@ fee. Costs are highly customizable.
- Citizens2
- Vault
- Any Vault-supported Economy plugin
## Basic usage
To create a new blacksmith, simply add the blacksmith trait to an NPC. Right-clicking the NPC will tell you if the
currently held item is repairable by the blacksmith. If it is, the blacksmith should give a price quote. Right-clicking
again starts the repair. The item should be given back or dropped after a random delay according to the set limits.
To create a new blacksmith, simply add the blacksmith trait to an NPC by selecting it with `/npc select`, and then using
`/trait add Blacksmith` (See Citizens' documentation for more details).
Right-clicking the NPC will tell you if the currently held item is repairable by the blacksmith. If it is, the
blacksmith should give a price quote. Right-clicking again starts the repair. The item should be given back or dropped
after a random delay according to the set limits.
While costs are always set globally (no blacksmith can do the same job for cheaper), the blacksmith's messages,
cool-down between each reforge, whether the item is dropped or given, the chance of failing or adding an enchantment,
@ -43,12 +47,13 @@ In addition to just being able to repair items, blacksmiths have some random fea
- There is a chance that blacksmiths fail to repair an item, leaving it at about the same durability as before. Use
failReforgeChance to control the chance. Set it to 0 to remove the feature.
- There is a chance a blacksmith may add an enchantment to a reforged item. You can control the probability using
extraEnchantmentChance, and set the maximum number of enchantments using maxEnchantments
extraEnchantmentChance, and set the maximum number of enchantments using maxEnchantments. EnchantmentBlocklist can be
used to block any enchantments you don't want to randomly grant.
## Commands
| Command | Arguments | Description |
| --- | --- | --- |
|-------------------|-------------------------------|----------------------------------------------------------------------------------------------|
| /blacksmith | \<option> \[new-value] | Changes a configuration option for the selected blacksmith (use Citizens' /npc select first) |
| /blacksmithconfig | \<reload/option> \[new-value] | Changes a default/global configuration value |
| /preset | \<preset>\[:filter] | Displays all materials included in the given preset, after applying the filter if set |
@ -65,45 +70,67 @@ for a specific material/enchantment. For /blacksmithconfig <basePrice/pricePerDu
material/enchantment>, using -1 or null as the value will clear the cost for the specified material/enchantment, using
the default one instead.
basePrice and pricePerDurabilityPoint support using a wildcard "*" to set the price for multiple groups of materials at
once. `/blacksmithconfig basePrice golden* 43` would set the price of any material starting with "GOLDEN" to 43. You can
also set `netherite*: 34` directly in the config file to set the price of all netherite materials to 34.
### Presets and filters:
Note: All of these can be used when specifying reforge-able items, such as "preset:weapon-smith:bow,preset:armor_smith:
gold,PRESET:TOOL_SMITH,shield"
**Presets are a nice way to make specialized blacksmiths by specifying categories of materials, instead of manually
listing every material manually. They can only be used for the `reforgeAbleItems` option.**
Note: All of these can be used when specifying reforge-able items, such as
"preset:weapon-smith:bow,preset:armor_smith:gold,PRESET:TOOL_SMITH,shield"
The format of reforge-able items requires each preset/material to be separated by a comma. If specifying a preset
instead of a material, start by typing "preset:". Then you can type BLACKSMITH, WEAPON_SMITH, ARMOR_SMITH or TOOL_SMITH.
If you want, you can add another colon and one of the filters supported by the preset.
For example: "preset:WEAPON_SMITH:BOW" to make the blacksmith only repair bows and crossbows. You can use the same
preset several times with different filters. For example: "preset:ARMOR_SMITH:DIAMOND,preset:ARMOR_SMITH:NETHERITE"
would allow the blacksmith to repair all diamond and netherite armor.
Presets and filters can also be negated by applying a "-" character in front of the material name. For example,
"-SHIELD" would make shields unrepairable, even if included in a listed preset. "preset:WEAPON_SMITH,-SHIELD"
would allow the blacksmith to repair any weapon included in the WEAPON_SMITH preset except shields.
"preset:BLACKSMITH,-ELYTRA"would allow the blacksmith to repair any repairable item, except elytra. You can also negate
entire presets or filters. For example: "preset:blacksmith,-preset:armor-smith:diamond" would allow a blacksmith to
repair all items, except diamond armor.
All currently supported presets, and available filters for each preset:
- WEAPON_SMITH:
- BOW
- SWORD
- RANGED
- ARMOR_SMITH:
- LEATHER
- IRON
- CHAINMAIL
- GOLD
- DIAMOND
- NETHERITE
- HELMET
- BOOTS
- LEGGINGS
- CHESTPLATE
- TOOL_SMITH
- WOOD
- STONE
- IRON
- GOLD
- DIAMOND
- NETHERITE
- PICKAXE
- AXE
- HOE
- SHOVEL
- MISC
- BLACKSMITH (WEAPON_SMITH + ARMOR_SMITH + TOOL_SMITH)
- WEAPON_SMITH: (RANGED + SWORD + SHIELD)
- BOW (bows and crossbows)
- SWORD (swords)
- RANGED (bows, crossbows and tridents)
- ARMOR_SMITH: (HELMET + BOOTS + LEGGINGS + CHESTPLATE + ELYTRA)
- LEATHER (all pieces of leather armor)
- IRON (all pieces of iron armor)
- CHAINMAIL (all pieces of chainmail armor)
- GOLD (all pieces of gold armor)
- DIAMOND (all pieces of diamond armor)
- NETHERITE (all pieces of netherite armor)
- HELMET (all helmets)
- BOOTS (all boots)
- LEGGINGS (all leggings)
- CHESTPLATE (all chest-plates)
- TOOL_SMITH: (PICKAXE + AXE + HOE + SHOVEL + MISC)
- WOOD (all wood tools)
- STONE (all stone tools)
- IRON (all iron tools)
- GOLD (all gold tools)
- DIAMOND (all diamond tools)
- NETHERITE (all netherite tools)
- PICKAXE (all pickaxes)
- AXE (all axes)
- HOE (all hoes)
- SHOVEL (all shovels)
- MISC (FISHING_ROD + SHEARS + FLINT_AND_STEEL)
## Permissions
| Permission node | Description |
| --- | --- |
|------------------|----------------------------------------------------------|
| blacksmith.admin | Allows overall blacksmith configuration |
| blacksmith.edit | Allows changing settings for the selected blacksmith NPC |
| blacksmith.use | Allows the player to repair items using blacksmiths |
@ -113,25 +140,27 @@ All currently supported presets, and available filters for each preset:
### Plugin Options
| Key | Value type | Description |
| --- | --- | --- |
|----------|------------|----------------------------------------------------------------------------------------------|
| language | string | The language used for this plugin. Only "en" is supported, unless you add a custom language. |
### Global-only options
| Key | Value type | Description |
| --- | --- | --- |
| basePrice | positive decimal number | The base price which has to be paid regardless of the durability remaining for an item. Setting this without specifying a material sets the basePrice for any item the basePrice has not been set for. |
| pricePerDurabilityPoint | positive decimal number | The price added for each durability point present/missing (depends on whether natural cost is set to true or false). Setting this without specifying a material sets the pricePerDurabilityPoint for any item the pricePerDurabilityPoint has not been set for. |
| enchantmentCost | positive decimal number | The added cost for each level of an enchantment present on the item. The cost can be set for specific enchantments. Not specifying an enchantment sets the value for all enchantments without a set value.
|---------------------------|-------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| basePrice | positive decimal number | The base price which has to be paid regardless of the durability remaining for an item. Setting this without specifying a material sets the basePrice for any item the basePrice has not been set for. You can use for example "netherite*: 10" to set the value for any material beginning with "netherite". |
| pricePerDurabilityPoint | positive decimal number | The price added for each durability point present/missing (depends on whether natural cost is set to true or false). Setting this without specifying a material sets the pricePerDurabilityPoint for any item the pricePerDurabilityPoint has not been set for. You can use for example "netherite*: 10" to set the value for any material beginning with "netherite". |
| enchantmentCost | positive decimal number | The added cost for each level of an enchantment present on the item. The cost can be set for specific enchantments. Not specifying an enchantment sets the value for all enchantments without a set value. |
| useNaturalCost | true/false | If true, each missing durability will add to the cost (price = basePrice + missingDurability * pricePerDurabilityPoint + enchantmentCost). If false, durability will be used to calculate the cost instead of missingDurability (this was the behavior before natural cost was added). |
| showExactTime | true/false | If true, blacksmiths will display exact time remaining in minutes and seconds, instead of vague expressions |
| chippedAnvilReforgingCost | positive decimal number | The price for reforging a chipped anvil (slightly damaged). No other costs apply! |
| damagedAnvilReforgingCost | positive decimal number | The price for reforging a damaged anvil (very damaged). No other costs apply! |
### Per-npc (with default values set in config.yml)
#### Configuration values
| Key | Value type | Description |
| --- | --- | --- |
|------------------------|-----------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| dropItem | true/false | Whether the blacksmith should drop the repaired item on the ground (instead of putting it into the player's inventory). |
| disableCoolDown | true/false | Whether to completely disable the cool-down between repairs. |
| disableDelay | true/false | Whether to completely disable the delay required to reforge an item. |
@ -141,13 +170,15 @@ All currently supported presets, and available filters for each preset:
| maxReforgeDelay | 0-3600 | The maximum number of seconds a player needs to wait for an item to be repaired. |
| minReforgeDelay | 0-3600 | The minimum number of seconds a player needs to wait for an item to be repaired. |
| reforgeCoolDown | 0-3600 | The cool-down, in seconds, a player has to wait between each time they use one specific blacksmith. |
| reforgeAbleItems | DIAMOND_LEGGINGS,GOLD-pickaxe,bow, etc. | Specifies which items this blacksmith is able to reforge. If set to "" or null, all normally repairable items can be repaired. If set to a list of items, only the items specified can be repaired. Some presets have been included for ease of use. Use a preset by specifying "preset:sword-smith" instead of a material such as "gold-pickaxe". Available presets: SWORD_SMITH, WEAPON_SMITH, ARMOR_SMITH, TOOL_SMITH, RANGED_SMITH. |
| reforgeAbleItems | DIAMOND_LEGGINGS,GOLD-pickaxe,bow, etc. | Specifies which items this blacksmith is able to reforge. If set to "" or null, all normally repairable items can be repaired. If set to a list of items, only the items specified can be repaired. Some presets have been included for ease of use. Use a preset by specifying "preset:sword-smith" instead of a material such as "gold-pickaxe". |
| blacksmithTitle | text string | The title displayed as part of the message explaining that a blacksmith doesn't recognize a player's held item |
| enchantmentBlocklist | string list | A string list of all enchantments a blacksmith should not be allowed to add to items. |
| reforgeAnvils | true/false | Whether to allow the blacksmith to reforge anvils. If enabled, chipped and damaged anvils will be replaced with a normal anvil. |
#### Messages
| Message Key | Explanation |
| --- | --- |
|--------------------------|-----------------------------------------------------------------------------------------------------------------|
| busyPlayerMessage | The message displayed when the blacksmith is serving another player |
| busyReforgeMessage | The message displayed when the blacksmith is busy reforging an item |
| coolDownUnexpiredMessage | The message displayed when the player has to wait for the cool-down to expire before using the blacksmith again |

85
pom.xml
View File

@ -6,31 +6,46 @@
<groupId>net.knarcraft</groupId>
<artifactId>blacksmith</artifactId>
<version>1.0.1-SNAPSHOT</version>
<version>1.0.4</version>
<name>Blacksmith</name>
<description>Blacksmith Character for the CitizensAPI</description>
<description>Blacksmith NPC for the Citizens API</description>
<!-- Properties -->
<properties>
<java.version>16</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<build.number>Unknown</build.number>
</properties>
<!-- 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>
</repository>
<repository>
<id>citizens-repo</id>
<url>http://repo.citizensnpcs.co/</url>
<url>https://repo.citizensnpcs.co/</url>
</repository>
<repository>
<id>vault-repo</id>
<url>http://nexus.hc.to/content/repositories/pub_releases</url>
<url>https://nexus.hc.to/content/repositories/pub_releases</url>
</repository>
</repositories>
<distributionManagement>
<repository>
<id>knarcraft-repo</id>
<url>https://git.knarcraft.net/api/packages/EpicKnarvik97/maven</url>
</repository>
<snapshotRepository>
<id>knarcraft-repo</id>
<url>https://git.knarcraft.net/api/packages/EpicKnarvik97/maven</url>
</snapshotRepository>
</distributionManagement>
<!-- Dependencies -->
<dependencies>
@ -50,7 +65,7 @@
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.19.2-R0.1-SNAPSHOT</version>
<version>1.19.3-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
@ -62,34 +77,66 @@
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>23.0.0</version>
<version>23.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.knarcraft</groupId>
<artifactId>knarlib</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
<!-- Build information -->
<build>
<defaultGoal>clean package install</defaultGoal>
<resources>
<resource>
<filtering>true</filtering>
<directory>${basedir}/src/main/resources</directory>
<includes>
<include>*.yml</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<version>3.8.1</version>
<configuration>
<source>16</source>
<target>16</target>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<filters>
<filter>
<artifact>net.knarcraft:knarlib</artifact>
<includes>
<include>net/knarcraft/knarlib/**</include>
</includes>
</filter>
<filter>
<excludes>
<exclude>*.MF</exclude>
<exclude>*.yml</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>

View File

@ -8,11 +8,15 @@ import net.knarcraft.blacksmith.command.BlackSmithEditTabCompleter;
import net.knarcraft.blacksmith.command.PresetCommand;
import net.knarcraft.blacksmith.command.PresetTabCompleter;
import net.knarcraft.blacksmith.config.GlobalSettings;
import net.knarcraft.blacksmith.formatting.Translator;
import net.knarcraft.blacksmith.formatting.BlacksmithTranslatableMessage;
import net.knarcraft.blacksmith.listener.NPCClickListener;
import net.knarcraft.blacksmith.listener.PlayerListener;
import net.knarcraft.blacksmith.manager.EconomyManager;
import net.knarcraft.blacksmith.trait.BlacksmithTrait;
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.PluginCommand;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.plugin.PluginManager;
@ -27,6 +31,8 @@ public class BlacksmithPlugin extends JavaPlugin {
private static BlacksmithPlugin instance;
private GlobalSettings config;
private static Translator translator;
private static StringFormatter stringFormatter;
/**
* Gets an instance of the Blacksmith plugin
@ -50,9 +56,27 @@ public class BlacksmithPlugin extends JavaPlugin {
* Reloads the configuration file from disk
*/
public void reload() {
config.load();
this.reloadConfig();
Translator.loadLanguages(this.getConfig().getString("language", "en"));
config.load();
translator.loadLanguages(this.getDataFolder(), this.getConfig().getString("language", "en"));
}
/**
* Gets the translator to use for translation
*
* @return <p>The translator to use</p>
*/
public static Translator getTranslator() {
return BlacksmithPlugin.translator;
}
/**
* Gets the string formatter to use for formatting
*
* @return <p>The string formatter to use</p>
*/
public static StringFormatter getStringFormatter() {
return BlacksmithPlugin.stringFormatter;
}
@Override
@ -75,7 +99,12 @@ public class BlacksmithPlugin extends JavaPlugin {
config = new GlobalSettings(this);
config.load();
Translator.loadLanguages(fileConfiguration.getString("language", "en"));
//Prepare the translator
translator = new Translator();
translator.registerMessageCategory(TranslatableTimeUnit.UNIT_SECOND);
translator.registerMessageCategory(BlacksmithTranslatableMessage.ITEM_TYPE_ENCHANTMENT);
translator.loadLanguages(this.getDataFolder(), fileConfiguration.getString("language", "en"));
BlacksmithPlugin.stringFormatter = new StringFormatter(this.getDescription().getPrefix(), translator);
//Set up Vault integration
if (!setUpVault()) {
@ -92,6 +121,10 @@ public class BlacksmithPlugin extends JavaPlugin {
registerListeners();
getLogger().log(Level.INFO, " v" + getDescription().getVersion() + " enabled.");
//Alert about an update in the console
UpdateChecker.checkForUpdate(this, "https://api.spigotmc.org/legacy/update.php?resource=105938",
() -> this.getDescription().getVersion(), null);
}
/**

View File

@ -5,9 +5,10 @@ import net.knarcraft.blacksmith.config.GlobalSetting;
import net.knarcraft.blacksmith.config.GlobalSettings;
import net.knarcraft.blacksmith.config.NPCSetting;
import net.knarcraft.blacksmith.config.SettingValueType;
import net.knarcraft.blacksmith.formatting.BlacksmithTranslatableMessage;
import net.knarcraft.blacksmith.formatting.ItemType;
import net.knarcraft.blacksmith.formatting.TranslatableMessage;
import net.knarcraft.blacksmith.util.InputParsingHelper;
import net.knarcraft.blacksmith.util.ItemHelper;
import net.knarcraft.blacksmith.util.TypeValidationHelper;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.Material;
@ -19,9 +20,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import static net.knarcraft.blacksmith.formatting.StringFormatter.displayErrorMessage;
import static net.knarcraft.blacksmith.formatting.StringFormatter.displaySuccessMessage;
import static net.knarcraft.blacksmith.formatting.TranslatableMessage.getValueChangedMessage;
import static net.knarcraft.blacksmith.formatting.BlacksmithTranslatableMessage.getValueChangedMessage;
/**
* The command used for changing global configuration options
@ -41,12 +40,6 @@ public class BlackSmithConfigCommand implements CommandExecutor {
}
GlobalSettings settings = BlacksmithPlugin.getInstance().getSettings();
//Changing reforge-able items' default isn't recommended
if (commandName.equalsIgnoreCase(NPCSetting.REFORGE_ABLE_ITEMS.getCommandName())) {
displayErrorMessage(sender, TranslatableMessage.DEFAULT_REFORGE_ABLE_ITEMS_UNCHANGEABLE);
return false;
}
//Find which global setting the user has specified, if any
GlobalSetting detectedGlobalSetting = null;
for (GlobalSetting globalSetting : GlobalSetting.values()) {
@ -105,7 +98,8 @@ public class BlackSmithConfigCommand implements CommandExecutor {
String newValue = args[1];
if (detectedGlobalSetting != null) {
settings.changeValue(detectedGlobalSetting, newValue);
displaySuccessMessage(sender, getValueChangedMessage(detectedGlobalSetting.getCommandName(), newValue));
BlacksmithPlugin.getStringFormatter().displaySuccessMessage(sender,
getValueChangedMessage(detectedGlobalSetting.getCommandName(), newValue));
return true;
} else if (detectedNPCSetting != null) {
//This makes sure all arguments are treated as a sentence
@ -113,7 +107,8 @@ public class BlackSmithConfigCommand implements CommandExecutor {
newValue = String.join(" ", Arrays.asList(args).subList(1, args.length));
}
settings.changeValue(detectedNPCSetting, newValue);
displaySuccessMessage(sender, getValueChangedMessage(detectedNPCSetting.getCommandName(), newValue));
BlacksmithPlugin.getStringFormatter().displaySuccessMessage(sender,
getValueChangedMessage(detectedNPCSetting.getCommandName(), newValue));
return true;
} else {
return false;
@ -151,10 +146,11 @@ public class BlackSmithConfigCommand implements CommandExecutor {
return false;
}
//Display the current value of the setting
displaySuccessMessage(sender, TranslatableMessage.getCurrentValueMessage(correctCommandName, settingValue));
BlacksmithPlugin.getStringFormatter().displaySuccessMessage(sender,
BlacksmithTranslatableMessage.getCurrentValueMessage(correctCommandName, settingValue));
//Print the value with any colors displayed as &a-f0-9
if (printRawValue) {
sender.sendMessage(TranslatableMessage.getRawValueMessage(
sender.sendMessage(BlacksmithTranslatableMessage.getRawValueMessage(
settingValue.replace(ChatColor.COLOR_CHAR, '&')));
}
return true;
@ -182,7 +178,8 @@ public class BlackSmithConfigCommand implements CommandExecutor {
} else {
currentValue = String.valueOf(settings.getPricePerDurabilityPoint(material));
}
displaySuccessMessage(sender, TranslatableMessage.getItemCurrentValueMessage(setting.getCommandName(),
BlacksmithPlugin.getStringFormatter().displaySuccessMessage(sender,
BlacksmithTranslatableMessage.getItemCurrentValueMessage(setting.getCommandName(),
ItemType.MATERIAL, material.name(), currentValue));
return true;
} else if (setting == GlobalSetting.ENCHANTMENT_COST) {
@ -190,7 +187,8 @@ public class BlackSmithConfigCommand implements CommandExecutor {
if (enchantment == null) {
return false;
}
displaySuccessMessage(sender, TranslatableMessage.getItemCurrentValueMessage(setting.getCommandName(),
BlacksmithPlugin.getStringFormatter().displaySuccessMessage(sender,
BlacksmithTranslatableMessage.getItemCurrentValueMessage(setting.getCommandName(),
ItemType.ENCHANTMENT, enchantment.getKey().getKey(),
String.valueOf(settings.getEnchantmentCost(enchantment))));
return true;
@ -228,38 +226,88 @@ public class BlackSmithConfigCommand implements CommandExecutor {
return true;
}
double newPrice = Double.parseDouble(args[2]);
String itemChanged;
ItemType itemType;
String newValue = String.valueOf(newPrice);
if (detectedGlobalSetting == GlobalSetting.BASE_PRICE ||
detectedGlobalSetting == GlobalSetting.PRICE_PER_DURABILITY_POINT) {
Material material = InputParsingHelper.matchMaterial(args[1]);
return updatePriceSpecialCase(settings, detectedGlobalSetting, args[1], newPrice, sender);
} else if (detectedGlobalSetting == GlobalSetting.ENCHANTMENT_COST) {
//Update enchantment cost for an item
Enchantment enchantment = InputParsingHelper.matchEnchantment(args[1]);
if (enchantment == null) {
return false;
}
ItemType itemType = ItemType.ENCHANTMENT;
String itemChanged = enchantment.getKey().getKey();
settings.setEnchantmentCost(enchantment, newPrice);
BlacksmithPlugin.getStringFormatter().displaySuccessMessage(sender,
BlacksmithTranslatableMessage.getItemValueChangedMessage(detectedGlobalSetting.getCommandName(),
itemType, itemChanged, newValue));
return true;
} else {
return false;
}
}
/**
* Updates a special case price configuration value if a special case is encountered
*
* @param settings <p>The settings to modify</p>
* @param detectedGlobalSetting <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>
* @return <p>True if the input was valid, and the item(s) was/were updated</p>
*/
private boolean updatePriceSpecialCase(GlobalSettings settings, GlobalSetting detectedGlobalSetting,
String materialName, double newPrice, CommandSender sender) {
ItemType itemType = ItemType.MATERIAL;
String itemChanged;
//Update base price or price per durability point for an item
if (materialName.contains("*")) {
itemChanged = materialName;
updateAllMatchedPrices(settings, detectedGlobalSetting, materialName, newPrice);
} else {
Material material = InputParsingHelper.matchMaterial(materialName);
if (material == null) {
return false;
}
itemType = ItemType.MATERIAL;
itemChanged = material.name();
if (detectedGlobalSetting == GlobalSetting.BASE_PRICE) {
settings.setBasePrice(material, newPrice);
} else {
settings.setPricePerDurabilityPoint(material, newPrice);
}
} else if (detectedGlobalSetting == GlobalSetting.ENCHANTMENT_COST) {
Enchantment enchantment = InputParsingHelper.matchEnchantment(args[1]);
if (enchantment == null) {
return false;
}
itemType = ItemType.ENCHANTMENT;
itemChanged = enchantment.getKey().getKey();
settings.setEnchantmentCost(enchantment, newPrice);
} else {
return false;
}
displaySuccessMessage(sender, TranslatableMessage.getItemValueChangedMessage(detectedGlobalSetting.getCommandName(),
itemType, itemChanged, newValue));
BlacksmithPlugin.getStringFormatter().displaySuccessMessage(sender,
BlacksmithTranslatableMessage.getItemValueChangedMessage(detectedGlobalSetting.getCommandName(),
itemType, itemChanged, String.valueOf(newPrice)));
return true;
}
/**
* Updates all materials matching the material name wildcard
*
* @param settings <p>The settings to modify</p>
* @param detectedGlobalSetting <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>
*/
private void updateAllMatchedPrices(GlobalSettings settings, GlobalSetting detectedGlobalSetting,
String materialName, double newPrice) {
String search = InputParsingHelper.regExIfy(materialName);
for (Material material : ItemHelper.getAllReforgeAbleMaterials()) {
if (!material.name().matches(search)) {
continue;
}
if (detectedGlobalSetting == GlobalSetting.BASE_PRICE) {
settings.setBasePrice(material, newPrice);
} else {
settings.setPricePerDurabilityPoint(material, newPrice);
}
}
}
}

View File

@ -14,7 +14,7 @@ import java.util.ArrayList;
import java.util.List;
import static net.knarcraft.blacksmith.util.TabCompleteValuesHelper.getTabCompletions;
import static net.knarcraft.blacksmith.util.TabCompletionHelper.filterMatchingContains;
import static net.knarcraft.knarlib.util.TabCompletionHelper.filterMatchingContains;
/**
* The tab completer for the command used for changing global configuration options

View File

@ -5,9 +5,7 @@ import net.citizensnpcs.api.npc.NPC;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.config.NPCSetting;
import net.knarcraft.blacksmith.config.SettingValueType;
import net.knarcraft.blacksmith.formatting.StringFormatter;
import net.knarcraft.blacksmith.formatting.TranslatableMessage;
import net.knarcraft.blacksmith.formatting.Translator;
import net.knarcraft.blacksmith.formatting.BlacksmithTranslatableMessage;
import net.knarcraft.blacksmith.trait.BlacksmithTrait;
import net.knarcraft.blacksmith.util.InputParsingHelper;
import net.knarcraft.blacksmith.util.TypeValidationHelper;
@ -19,9 +17,8 @@ import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import static net.knarcraft.blacksmith.formatting.StringFormatter.displaySuccessMessage;
import static net.knarcraft.blacksmith.formatting.TranslatableMessage.getCurrentValueMessage;
import static net.knarcraft.blacksmith.formatting.TranslatableMessage.getValueChangedMessage;
import static net.knarcraft.blacksmith.formatting.BlacksmithTranslatableMessage.getCurrentValueMessage;
import static net.knarcraft.blacksmith.formatting.BlacksmithTranslatableMessage.getValueChangedMessage;
/**
* The main command used for blacksmith editing
@ -33,7 +30,8 @@ public class BlackSmithEditCommand implements CommandExecutor {
@NotNull String[] args) {
NPC npc = CitizensAPI.getDefaultNPCSelector().getSelected(sender);
if (npc == null || !npc.hasTrait(BlacksmithTrait.class)) {
StringFormatter.displayErrorMessage(sender, TranslatableMessage.NO_NPC_SELECTED);
BlacksmithPlugin.getStringFormatter().displayErrorMessage(sender,
BlacksmithTranslatableMessage.NO_NPC_SELECTED);
return true;
}
@ -86,7 +84,8 @@ public class BlackSmithEditCommand implements CommandExecutor {
//Change the setting
blacksmithTrait.getSettings().changeSetting(npcSetting, newValue);
displaySuccessMessage(sender, getValueChangedMessage(npcSetting.getCommandName(), String.valueOf(newValue)));
BlacksmithPlugin.getStringFormatter().displaySuccessMessage(sender,
getValueChangedMessage(npcSetting.getCommandName(), String.valueOf(newValue)));
//Save the changes immediately to prevent data loss on server crash
CitizensAPI.getNPCRegistry().saveToStore();
}
@ -105,14 +104,18 @@ public class BlackSmithEditCommand implements CommandExecutor {
if (InputParsingHelper.isEmpty(rawValue)) {
//Display the default value, if no custom value has been specified
rawValue = String.valueOf(BlacksmithPlugin.getInstance().getSettings().getRawValue(npcSetting));
displaySuccessMessage(sender, getCurrentValueMessage(npcSetting.getCommandName(), rawValue));
BlacksmithPlugin.getStringFormatter().displaySuccessMessage(sender,
getCurrentValueMessage(npcSetting.getCommandName(), rawValue));
} else {
//Add a marker if the value has been customized
String marker = Translator.getTranslatedMessage(TranslatableMessage.SETTING_OVERRIDDEN_MARKER);
displaySuccessMessage(sender, getCurrentValueMessage(npcSetting.getCommandName(), rawValue) + marker);
String marker = BlacksmithPlugin.getTranslator().getTranslatedMessage(
BlacksmithTranslatableMessage.SETTING_OVERRIDDEN_MARKER);
BlacksmithPlugin.getStringFormatter().displaySuccessMessage(sender,
getCurrentValueMessage(npcSetting.getCommandName(), rawValue) + marker);
}
if (npcSetting.getPath().startsWith("defaults.messages")) {
sender.sendMessage(TranslatableMessage.getRawValueMessage(rawValue.replace(ChatColor.COLOR_CHAR, '&')));
sender.sendMessage(BlacksmithTranslatableMessage.getRawValueMessage(
rawValue.replace(ChatColor.COLOR_CHAR, '&')));
}
}

View File

@ -2,7 +2,7 @@ package net.knarcraft.blacksmith.command;
import net.knarcraft.blacksmith.config.NPCSetting;
import net.knarcraft.blacksmith.util.TabCompleteValuesHelper;
import net.knarcraft.blacksmith.util.TabCompletionHelper;
import net.knarcraft.knarlib.util.TabCompletionHelper;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
@ -33,7 +33,7 @@ public class BlackSmithEditTabCompleter implements TabCompleter {
if (args.length == 1) {
return TabCompletionHelper.filterMatchingContains(npcSettings, args[0]);
} else {
if (npcSettings.contains(args[0]) && args.length <= 2) {
if (npcSettings.contains(args[0]) && args.length == 2) {
return tabCompleteCommandValues(args[0], args[1]);
} else {
return new ArrayList<>();

View File

@ -1,10 +1,10 @@
package net.knarcraft.blacksmith.command;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.config.SmithPreset;
import net.knarcraft.blacksmith.config.SmithPresetFilter;
import net.knarcraft.blacksmith.formatting.StringFormatter;
import net.knarcraft.blacksmith.formatting.TranslatableMessage;
import net.knarcraft.blacksmith.formatting.Translator;
import net.knarcraft.blacksmith.formatting.BlacksmithTranslatableMessage;
import net.knarcraft.knarlib.formatting.StringFormatter;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
@ -14,9 +14,6 @@ import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import static net.knarcraft.blacksmith.formatting.StringFormatter.displayErrorMessage;
import static net.knarcraft.blacksmith.formatting.StringFormatter.displaySuccessMessage;
/**
* The command for displaying which materials are contained in a preset
*/
@ -40,7 +37,8 @@ public class PresetCommand implements CommandExecutor {
SmithPresetFilter filter = SmithPresetFilter.valueOf(parts[1]);
if (!smithPreset.supportsFilter(filter)) {
displayErrorMessage(sender, TranslatableMessage.INVALID_FILTER_FOR_PRESET);
BlacksmithPlugin.getStringFormatter().displayErrorMessage(sender,
BlacksmithTranslatableMessage.INVALID_FILTER_FOR_PRESET);
return false;
}
includedMaterials = smithPreset.getFilteredMaterials(filter);
@ -48,7 +46,8 @@ public class PresetCommand implements CommandExecutor {
includedMaterials = SmithPreset.valueOf(presetName).getMaterials();
}
} catch (IllegalArgumentException exception) {
displayErrorMessage(sender, TranslatableMessage.INVALID_PRESET_OR_FILTER);
BlacksmithPlugin.getStringFormatter().displayErrorMessage(sender,
BlacksmithTranslatableMessage.INVALID_PRESET_OR_FILTER);
return false;
}
@ -57,8 +56,9 @@ public class PresetCommand implements CommandExecutor {
for (Material material : includedMaterials) {
materialNames.add(material.name());
}
displaySuccessMessage(sender, StringFormatter.replacePlaceholder(Translator.getTranslatedMessage(
TranslatableMessage.PRESET_MATERIALS), "{materials}", String.join(", ", materialNames)));
BlacksmithPlugin.getStringFormatter().displaySuccessMessage(sender, StringFormatter.replacePlaceholder(
BlacksmithPlugin.getTranslator().getTranslatedMessage(BlacksmithTranslatableMessage.PRESET_MATERIALS),
"{materials}", String.join(", ", materialNames)));
return true;
}

View File

@ -2,7 +2,7 @@ package net.knarcraft.blacksmith.command;
import net.knarcraft.blacksmith.config.SmithPreset;
import net.knarcraft.blacksmith.config.SmithPresetFilter;
import net.knarcraft.blacksmith.util.TabCompletionHelper;
import net.knarcraft.knarlib.util.TabCompletionHelper;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;

View File

@ -1,7 +1,7 @@
package net.knarcraft.blacksmith.command;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.formatting.TranslatableMessage;
import net.knarcraft.blacksmith.formatting.BlacksmithTranslatableMessage;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
@ -11,8 +11,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import static net.knarcraft.blacksmith.formatting.StringFormatter.displaySuccessMessage;
/**
* The command for re-loading the plugin
*/
@ -22,7 +20,7 @@ public class ReloadCommand implements TabExecutor {
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] args) {
BlacksmithPlugin.getInstance().reload();
displaySuccessMessage(sender, TranslatableMessage.PLUGIN_RELOADED);
BlacksmithPlugin.getStringFormatter().displaySuccessMessage(sender, BlacksmithTranslatableMessage.PLUGIN_RELOADED);
return true;
}

View File

@ -26,7 +26,8 @@ public enum GlobalSetting {
*
* <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, "enchantmentCost"),
ENCHANTMENT_COST("global.enchantmentCost.default", SettingValueType.POSITIVE_DOUBLE, 5.0,
"enchantmentCost"),
/**
* Whether the cost should increase for damage taken, as opposed to increase for durability present
@ -36,7 +37,19 @@ public enum GlobalSetting {
/**
* 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("global.showExactTime", SettingValueType.BOOLEAN, false, "showExactTime"),
/**
* The cost for repairing a chipped anvil
*/
ANVIL_CHIPPED_COST("global.chippedAnvilReforgingCost", SettingValueType.POSITIVE_DOUBLE, 10.0,
"chippedAnvilReforgingCost"),
/**
* The cost for repairing a damaged anvil
*/
ANVIL_DAMAGED_COST("global.damagedAnvilReforgingCost", SettingValueType.POSITIVE_DOUBLE, 20.0,
"damagedAnvilReforgingCost");
private final String path;
private final String parent;

View File

@ -5,11 +5,14 @@ import net.citizensnpcs.api.util.YamlStorage;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.util.ConfigHelper;
import net.knarcraft.blacksmith.util.InputParsingHelper;
import net.knarcraft.blacksmith.util.ItemHelper;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
@ -21,8 +24,9 @@ public class GlobalSettings {
private final Map<Material, Double> materialBasePrices = new HashMap<>();
private final Map<Material, Double> materialPricePerDurabilityPoints = new HashMap<>();
private final Map<Enchantment, Double> enchantmentCosts = new HashMap<>();
private final Map<NPCSetting, Object> defaultNPCSettings = new HashMap<>();
private final List<Material> defaultReforgeAbleMaterials = new ArrayList<>();
private final List<Enchantment> defaultEnchantmentBlocklist = new ArrayList<>();
private final Map<GlobalSetting, Object> globalSettings = new HashMap<>();
private final YamlStorage defaultConfig;
@ -81,8 +85,19 @@ public class GlobalSettings {
* @param newValue <p>The new value for the setting</p>
*/
public void changeValue(NPCSetting npcSetting, Object newValue) {
if (npcSetting.getValueType() == SettingValueType.STRING_LIST ||
npcSetting.getValueType() == SettingValueType.REFORGE_ABLE_ITEMS) {
//Workaround to make sure it's treated as the correct type
defaultNPCSettings.put(npcSetting, newValue == null ? null : ConfigHelper.asStringList(newValue));
} else {
defaultNPCSettings.put(npcSetting, newValue);
}
save();
if (npcSetting == NPCSetting.REFORGE_ABLE_ITEMS) {
loadReforgeAbleItems();
} else if (npcSetting == NPCSetting.ENCHANTMENT_BLOCKLIST) {
loadEnchantmentBlocklist();
}
}
/**
@ -246,6 +261,40 @@ public class GlobalSettings {
}
}
/**
* Gets the value of reforgeAbleItems
*
* @return <p>The value of reforgeAbleItems</p>
*/
public List<Material> getReforgeAbleItems() {
return this.defaultReforgeAbleMaterials;
}
/**
* Gets the value of enchantmentBlocklist
*
* @return <p>The value of enchantmentBlocklist</p>
*/
public List<Enchantment> getEnchantmentBlocklist() {
return this.defaultEnchantmentBlocklist;
}
/**
* Gets the cost for repairing the given type of anvil
*
* @param material <p>The anvil material to repair</p>
* @return <p>The cost of repairing the anvil</p>
*/
public double getAnvilCost(Material material) {
if (material == Material.CHIPPED_ANVIL) {
return asDouble(GlobalSetting.ANVIL_CHIPPED_COST);
} else if (material == Material.DAMAGED_ANVIL) {
return asDouble(GlobalSetting.ANVIL_DAMAGED_COST);
} else {
throw new IllegalArgumentException("An unexpected item was encountered!");
}
}
/**
* Gets the given value as a boolean
*
@ -302,45 +351,95 @@ public class GlobalSettings {
}
//Load all base prices
DataKey basePriceNode = root.getRelative(GlobalSetting.BASE_PRICE.getParent());
Map<String, String> relevantKeys = getRelevantKeys(basePriceNode);
for (String key : relevantKeys.keySet()) {
String materialName = relevantKeys.get(key);
Material material = InputParsingHelper.matchMaterial(materialName);
if (material != null) {
materialBasePrices.put(material, basePriceNode.getDouble(key));
} else {
BlacksmithPlugin.getInstance().getLogger().log(Level.WARNING,
"Unable to find a material matching " + materialName);
}
}
loadBasePrices(root);
//Load all per-durability-point prices
DataKey basePerDurabilityPriceNode = root.getRelative(GlobalSetting.PRICE_PER_DURABILITY_POINT.getParent());
relevantKeys = getRelevantKeys(basePerDurabilityPriceNode);
for (String key : relevantKeys.keySet()) {
String materialName = relevantKeys.get(key);
Material material = InputParsingHelper.matchMaterial(materialName);
if (material != null) {
materialPricePerDurabilityPoints.put(material, basePerDurabilityPriceNode.getDouble(key));
} else {
BlacksmithPlugin.getInstance().getLogger().log(Level.WARNING,
"Unable to find a material matching " + materialName);
}
}
loadPricesPerDurabilityPoint(root);
//Load all enchantment prices
DataKey enchantmentCostNode = root.getRelative(GlobalSetting.ENCHANTMENT_COST.getParent());
relevantKeys = getRelevantKeys(basePerDurabilityPriceNode);
Map<String, String> relevantKeys = getRelevantKeys(enchantmentCostNode);
for (String key : relevantKeys.keySet()) {
String enchantmentName = relevantKeys.get(key);
Enchantment enchantment = InputParsingHelper.matchEnchantment(enchantmentName);
if (enchantment != null) {
enchantmentCosts.put(enchantment, enchantmentCostNode.getDouble(key));
setItemPrice(enchantmentCosts, enchantmentName, enchantment, enchantmentCostNode.getDouble(key));
}
}
/**
* Loads all prices per durability point for all materials
*
* @param root <p>The configuration root node to search from</p>
*/
private void loadPricesPerDurabilityPoint(DataKey root) {
DataKey basePerDurabilityPriceNode = root.getRelative(GlobalSetting.PRICE_PER_DURABILITY_POINT.getParent());
Map<String, String> relevantKeys = getRelevantKeys(basePerDurabilityPriceNode);
for (String key : relevantKeys.keySet()) {
String materialName = relevantKeys.get(key);
double price = basePerDurabilityPriceNode.getDouble(key);
if (materialName.contains("*")) {
//Treat *CHESTPLATE as a regular expression to match all chest-plates
setMatchedMaterialPrices(materialPricePerDurabilityPoints, materialName, price);
} else {
Material material = InputParsingHelper.matchMaterial(materialName);
setItemPrice(materialPricePerDurabilityPoints, materialName, material, price);
}
}
}
/**
* Loads base prices for all materials
*
* @param root <p>The configuration root node to search from</p>
*/
private void loadBasePrices(DataKey root) {
DataKey basePriceNode = root.getRelative(GlobalSetting.BASE_PRICE.getParent());
Map<String, String> relevantKeys = getRelevantKeys(basePriceNode);
for (String key : relevantKeys.keySet()) {
String materialName = relevantKeys.get(key);
double price = basePriceNode.getDouble(key);
if (materialName.contains("*")) {
//Treat *CHESTPLATE as a regular expression to match all chest-plates
setMatchedMaterialPrices(materialBasePrices, materialName, price);
} else {
Material material = InputParsingHelper.matchMaterial(materialName);
setItemPrice(materialBasePrices, materialName, material, price);
}
}
}
/**
* Sets the price for any materials matching the given wildcard material name
*
* @param prices <p>The map to store the prices in</p>
* @param materialName <p>The material name to match</p>
* @param price <p>The price to set for the matched materials</p>
*/
private void setMatchedMaterialPrices(Map<Material, Double> prices, String materialName, double price) {
String search = InputParsingHelper.regExIfy(materialName);
for (Material material : ItemHelper.getAllReforgeAbleMaterials()) {
if (material.name().matches(search)) {
setItemPrice(prices, material.name(), material, price);
}
}
}
/**
* Sets the price for the given material
*
* @param prices <p>The map to store the price in</p>
* @param itemName <p>The name of the material to add a price for</p>
* @param item <p>The material parsed from the name</p>
* @param price <p>The price to set</p>
*/
private <K> void setItemPrice(Map<K, Double> prices, String itemName, K item, double price) {
if (item != null) {
prices.put(item, price);
} else {
BlacksmithPlugin.getInstance().getLogger().log(Level.WARNING,
"Unable to find an enchantment matching " + enchantmentName);
}
"Unable to find a material/enchantment matching " + itemName);
}
}
@ -389,6 +488,26 @@ public class GlobalSettings {
defaultNPCSettings.put(setting, root.getRaw(setting.getPath()));
}
}
loadReforgeAbleItems();
loadEnchantmentBlocklist();
}
/**
* Loads reforgeAble items from the current value
*/
private void loadReforgeAbleItems() {
defaultReforgeAbleMaterials.clear();
defaultReforgeAbleMaterials.addAll(NPCSettings.getReforgeAbleItems(ConfigHelper.asStringList(
defaultNPCSettings.get(NPCSetting.REFORGE_ABLE_ITEMS))));
}
/**
* Loads the enchantment blocklist from the current value
*/
private void loadEnchantmentBlocklist() {
defaultEnchantmentBlocklist.clear();
defaultEnchantmentBlocklist.addAll(NPCSettings.getEnchantmentBlocklist(ConfigHelper.asStringList(
defaultNPCSettings.get(NPCSetting.ENCHANTMENT_BLOCKLIST))));
}
/**

View File

@ -46,7 +46,7 @@ public enum NPCSetting {
/**
* The setting for which items the blacksmith is able to reforge
*/
REFORGE_ABLE_ITEMS("reforgeAbleItems", SettingValueType.STRING_LIST, new String[]{}, "reforgeAbleItems"),
REFORGE_ABLE_ITEMS("reforgeAbleItems", SettingValueType.REFORGE_ABLE_ITEMS, "", "reforgeAbleItems"),
/**
* The setting for the title used to display which kind of blacksmith the NPC is
@ -56,6 +56,17 @@ public enum NPCSetting {
*/
BLACKSMITH_TITLE("blacksmithTitle", SettingValueType.STRING, "blacksmith", "blacksmithTitle"),
/**
* The setting for the enchantments a blacksmith cannot apply to items
*/
ENCHANTMENT_BLOCKLIST("enchantmentBlocklist", SettingValueType.STRING_LIST, new String[]{"binding_curse",
"mending", "vanishing_curse"}, "enchantmentBlocklist"),
/**
* Whether to allow this blacksmith to repair anvils
*/
REPAIR_ANVILS("reforgeAnvils", SettingValueType.BOOLEAN, false, "reforgeAnvils"),
/*-----------
| Messages |
-----------*/

View File

@ -6,12 +6,15 @@ import net.knarcraft.blacksmith.trait.BlacksmithTrait;
import net.knarcraft.blacksmith.util.ConfigHelper;
import net.knarcraft.blacksmith.util.InputParsingHelper;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
/**
@ -20,6 +23,7 @@ import java.util.logging.Level;
public class NPCSettings {
private final List<Material> reforgeAbleItems = new ArrayList<>();
private final List<Enchantment> enchantmentBlocklist = new ArrayList<>();
private final Map<NPCSetting, Object> currentValues = new HashMap<>();
private final GlobalSettings globalSettings;
@ -43,6 +47,7 @@ public class NPCSettings {
}
//Updates the list of reforge-able items/materials
updateReforgeAbleItems();
updateEnchantmentBlocklist();
}
/**
@ -63,10 +68,19 @@ public class NPCSettings {
* @param newValue <p>The new value of the setting</p>
*/
public void changeSetting(NPCSetting 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);
}
if (setting == NPCSetting.REFORGE_ABLE_ITEMS) {
updateReforgeAbleItems();
}
if (setting == NPCSetting.ENCHANTMENT_BLOCKLIST) {
updateEnchantmentBlocklist();
}
}
/**
@ -186,7 +200,26 @@ public class NPCSettings {
* @return <p>All items reforge-able by this NPC</p>
*/
public List<Material> getReforgeAbleItems() {
return new ArrayList<>(reforgeAbleItems);
Object currentValue = currentValues.get(NPCSetting.REFORGE_ABLE_ITEMS);
if (currentValue == null || String.valueOf(currentValue).isEmpty()) {
return globalSettings.getReforgeAbleItems();
} else {
return new ArrayList<>(this.reforgeAbleItems);
}
}
/**
* Gets the list of blocked enchantments
*
* @return <p>The list of blocked enchantments</p>
*/
public List<Enchantment> getEnchantmentBlocklist() {
Object currentValue = currentValues.get(NPCSetting.ENCHANTMENT_BLOCKLIST);
if (currentValue == null || String.valueOf(currentValue).isEmpty()) {
return globalSettings.getEnchantmentBlocklist();
} else {
return new ArrayList<>(this.enchantmentBlocklist);
}
}
/**
@ -279,6 +312,15 @@ public class NPCSettings {
return asInt(NPCSetting.MAX_REFORGE_DELAY) <= 0;
}
/**
* Gets whether this blacksmith is able to repair anvils
*
* @return <p>True if this blacksmith is able to repair anvils</p>
*/
public boolean getRepairAnvils() {
return ConfigHelper.asBoolean(getValue(NPCSetting.REPAIR_ANVILS));
}
/**
* Gets the given value as an integer
*
@ -326,53 +368,113 @@ public class NPCSettings {
/**
* Replaces placeholders in the given reforge-able value
*
* @param value <p>The value specified by a user</p>
* @param stringList <p>The value specified by a user</p>
* @return <p>The value with placeholders replaced</p>
*/
private Object replaceReforgeAblePresets(Object value) {
if (value instanceof String string) {
String[] list = string.split(",");
List<String> replaced = new ArrayList<>(list.length);
for (String item : list) {
replaced.add(SmithPreset.replacePreset(item));
}
return String.join(",", replaced);
} else if (value instanceof String[] stringList) {
List<String> replaced = new ArrayList<>(stringList.length);
private static List<String> replaceReforgeAblePresets(List<String> stringList) {
List<String> newStrings = new ArrayList<>();
for (String item : stringList) {
replaced.add(SmithPreset.replacePreset(item));
if (item == null) {
continue;
}
return replaced.toArray();
String replaced = SmithPreset.replacePreset(item);
if (!replaced.equals(item)) {
newStrings.addAll(List.of(replaced.split(",")));
} else {
throw new IllegalArgumentException("Unexpected object type encountered!");
newStrings.add(item);
}
}
return newStrings;
}
/**
* Updates the list of blocked enchantments
*/
private void updateEnchantmentBlocklist() {
this.enchantmentBlocklist.clear();
this.enchantmentBlocklist.addAll(getEnchantmentBlocklist(ConfigHelper.asStringList(getValue(
NPCSetting.ENCHANTMENT_BLOCKLIST))));
}
/**
* Gets the list of enchantments listed in the given string list
*
* @param enchantments <p>The enchantment names to block</p>
* @return <p>The enchantments to be blocked</p>
*/
public static List<Enchantment> getEnchantmentBlocklist(List<String> enchantments) {
List<Enchantment> enchantmentBlocklist = new ArrayList<>();
for (String item : enchantments) {
if (InputParsingHelper.isEmpty(item)) {
continue;
}
Enchantment enchantment = InputParsingHelper.matchEnchantment(item);
if (enchantment != null) {
enchantmentBlocklist.add(enchantment);
} else {
BlacksmithPlugin.getInstance().getLogger().log(Level.WARNING, "Unable to verify " + item +
" as a valid enchantment");
}
}
return enchantmentBlocklist;
}
/**
* Updates the reforge-able items according to the current value of the setting
*/
private void updateReforgeAbleItems() {
this.reforgeAbleItems.clear();
String newReforgeAbleItems = (String) currentValues.get(NPCSetting.REFORGE_ABLE_ITEMS);
if (newReforgeAbleItems == null) {
return;
this.reforgeAbleItems.addAll(getReforgeAbleItems(ConfigHelper.asStringList(getValue(
NPCSetting.REFORGE_ABLE_ITEMS))));
}
/**
* Gets a list of the reforgeAbleItems described in the given item list
*
* @param itemList <p>The list of items defined by the user</p>
* @return <p>The materials contained in the item list</p>
*/
public static List<Material> getReforgeAbleItems(List<String> itemList) {
List<Material> reforgeAbleItems = new ArrayList<>();
if (itemList == null) {
return null;
}
//Convert any presets with a list of materials
newReforgeAbleItems = (String) replaceReforgeAblePresets(newReforgeAbleItems);
itemList = replaceReforgeAblePresets(itemList);
for (String item : newReforgeAbleItems.split(",")) {
Set<Material> blacklisted = new HashSet<>();
//Parse every material, and add to reforgeAble items
for (String item : itemList) {
//Ignore ,,
if (InputParsingHelper.isEmpty(item)) {
continue;
}
boolean blacklist = false;
if (item.startsWith("-")) {
blacklist = true;
item = item.substring(1);
}
Material material = InputParsingHelper.matchMaterial(item);
if (material != null && BlacksmithTrait.isRepairable(new ItemStack(material, 1))) {
this.reforgeAbleItems.add(material);
if (!blacklist) {
reforgeAbleItems.add(material);
} else {
blacklisted.add(material);
}
} else {
BlacksmithPlugin.getInstance().getLogger().log(Level.WARNING, "Unable to verify " + item +
" as a valid reforge-able item");
}
}
//Remove any blacklisted materials at the end to make sure order of arguments won't matter
reforgeAbleItems.removeAll(blacklisted);
return reforgeAbleItems;
}
}

View File

@ -43,6 +43,11 @@ public enum SettingValueType {
/**
* An enchantment
*/
ENCHANTMENT
ENCHANTMENT,
/**
* A comma-separated list of reforge-able items
*/
REFORGE_ABLE_ITEMS,
}

View File

@ -1,6 +1,7 @@
package net.knarcraft.blacksmith.config;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.util.ItemHelper;
import org.bukkit.Material;
import java.util.ArrayList;
@ -12,6 +13,11 @@ import java.util.logging.Level;
*/
public enum SmithPreset {
/**
* A blacksmith capable of re-forging everything
*/
BLACKSMITH(new SmithPresetFilter[]{}),
/**
* A blacksmith capable of re-forging all weapons (including shields)
*/
@ -81,11 +87,23 @@ public enum SmithPreset {
* @return <p>The string, possibly with the preset replaced</p>
*/
public static String replacePreset(String possiblePreset) {
boolean negated = false;
String upperCasedPreset = possiblePreset.replace('-', '_').toUpperCase();
if (!upperCasedPreset.startsWith("PRESET:")) {
if (possiblePreset.startsWith("-")) {
negated = true;
}
if ((negated && !upperCasedPreset.startsWith("_PRESET:")) ||
(!negated && !upperCasedPreset.startsWith("PRESET:"))) {
return possiblePreset;
}
//Strip the "-" here to prevent stripping for material names
if (negated) {
upperCasedPreset = upperCasedPreset.substring(1);
}
//Parse the input
SmithPresetFilter filter = null;
SmithPreset preset;
@ -105,11 +123,16 @@ public enum SmithPreset {
}
//Return the list of materials included in the preset
List<String> materialNames;
if (filter != null) {
return String.join(",", preset.getMaterialNames(filter));
materialNames = preset.getMaterialNames(filter);
} else {
return String.join(",", preset.getMaterialNames());
materialNames = preset.getMaterialNames();
}
if (negated) {
materialNames = negateMaterials(materialNames);
}
return String.join(",", materialNames);
}
/**
@ -131,6 +154,7 @@ public enum SmithPreset {
*/
public List<Material> getMaterials() {
return switch (this) {
case BLACKSMITH -> ItemHelper.getAllReforgeAbleMaterials();
case WEAPON_SMITH -> getWeapons();
case ARMOR_SMITH -> getArmor();
case TOOL_SMITH -> getTools();
@ -205,13 +229,13 @@ public enum SmithPreset {
* @return <p>The resulting materials</p>
*/
private List<Material> getMaterialsEndingWith(String end) {
List<Material> swords = new ArrayList<>();
for (Material material : Material.values()) {
List<Material> matchedMaterials = new ArrayList<>();
for (Material material : ItemHelper.getAllReforgeAbleMaterials()) {
if (!material.name().startsWith("LEGACY") && material.name().endsWith(end)) {
swords.add(material);
matchedMaterials.add(material);
}
}
return swords;
return matchedMaterials;
}
/**
@ -247,4 +271,20 @@ public enum SmithPreset {
return items;
}
/**
* Negates the given material names
*
* @param materials <p>The material names to negate</p>
* @return <p>The negated material names</p>
*/
private static List<String> negateMaterials(List<String> materials) {
List<String> negatedMaterials = new ArrayList<>(materials.size());
materials.forEach((material) -> {
if (material != null && !material.isBlank()) {
negatedMaterials.add("-" + material);
}
});
return negatedMaterials;
}
}

View File

@ -0,0 +1,30 @@
package net.knarcraft.blacksmith.formatting;
import net.citizensnpcs.api.npc.NPC;
import net.knarcraft.knarlib.property.ColorConversion;
import net.knarcraft.knarlib.util.ColorHelper;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.entity.Player;
/**
* A formatter for formatting displayed messages
*/
public final class BlacksmithStringFormatter {
private BlacksmithStringFormatter() {
}
/**
* Sends a message from a blacksmith NPC to a player
*
* @param npc <p>The NPC sending the message</p>
* @param player <p>The player to send the message to</p>
* @param message <p>The message to send</p>
*/
public static void sendNPCMessage(NPC npc, Player player, String message) {
player.sendMessage(ChatColor.GREEN + "[" + npc.getName() + "] -> You:" + ChatColor.RESET + " " +
ColorHelper.translateColorCodes(message, ColorConversion.NORMAL));
}
}

View File

@ -1,6 +1,8 @@
package net.knarcraft.blacksmith.formatting;
import static net.knarcraft.blacksmith.formatting.StringFormatter.replacePlaceholders;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.knarlib.formatting.StringFormatter;
import net.knarcraft.knarlib.formatting.TranslatableMessage;
/**
* An enum containing all translatable global messages
@ -8,7 +10,7 @@ import static net.knarcraft.blacksmith.formatting.StringFormatter.replacePlaceho
* <p>This does not include NPC messages as they are configurable per-npc, and can be translated by changing the
* default message values in the main config file.</p>
*/
public enum TranslatableMessage {
public enum BlacksmithTranslatableMessage implements TranslatableMessage {
/**
* The message displayed when a configuration value has been successfully changed
@ -50,11 +52,6 @@ public enum TranslatableMessage {
*/
NO_NPC_SELECTED,
/**
* The message displayed if trying to change the default value of reforge-able item using commands
*/
DEFAULT_REFORGE_ABLE_ITEMS_UNCHANGEABLE,
/**
* The message displayed if a string list is required, but something else is given
*/
@ -105,36 +102,6 @@ public enum TranslatableMessage {
*/
PRESET_MATERIALS,
/**
* The format for displaying the exact duration of a blacksmith's cool-down or delay
*/
DURATION_FORMAT,
/**
* The text to display for 0 seconds
*/
UNIT_NOW,
/**
* The text to display for 1 second
*/
UNIT_SECOND,
/**
* The text to display for a number of seconds
*/
UNIT_SECONDS,
/**
* The text to display for 1 minute
*/
UNIT_MINUTE,
/**
* The text to display for a number of minutes
*/
UNIT_MINUTES,
/**
* The text to display when describing less than 10 seconds remaining
*/
@ -172,8 +139,8 @@ public enum TranslatableMessage {
* @return <p>The message to display</p>
*/
public static String getRawValueMessage(String rawValue) {
return StringFormatter.replacePlaceholder(Translator.getTranslatedMessage(TranslatableMessage.RAW_VALUE),
"{rawValue}", rawValue);
return StringFormatter.replacePlaceholder(BlacksmithPlugin.getTranslator().getTranslatedMessage(
BlacksmithTranslatableMessage.RAW_VALUE), "{rawValue}", rawValue);
}
/**
@ -184,8 +151,9 @@ public enum TranslatableMessage {
* @return <p>The string to display to a user</p>
*/
public static String getValueChangedMessage(String setting, String newValue) {
return replacePlaceholders(Translator.getTranslatedMessage(TranslatableMessage.VALUE_CHANGED),
new String[]{"{setting}", "{newValue}"}, new String[]{setting, newValue});
return StringFormatter.replacePlaceholders(BlacksmithPlugin.getTranslator().getTranslatedMessage(
BlacksmithTranslatableMessage.VALUE_CHANGED), new String[]{"{setting}", "{newValue}"},
new String[]{setting, newValue});
}
/**
@ -198,7 +166,8 @@ public enum TranslatableMessage {
* @return <p>The string to display to a user</p>
*/
public static String getItemValueChangedMessage(String setting, ItemType itemType, String item, String newValue) {
return replacePlaceholders(Translator.getTranslatedMessage(TranslatableMessage.VALUE_FOR_ITEM_CHANGED),
return StringFormatter.replacePlaceholders(BlacksmithPlugin.getTranslator().getTranslatedMessage(
BlacksmithTranslatableMessage.VALUE_FOR_ITEM_CHANGED),
new String[]{"{setting}", "{itemType}", "{item}", "{newValue}"},
new String[]{setting, itemType.getItemTypeName(), item, newValue});
}
@ -211,8 +180,9 @@ public enum TranslatableMessage {
* @return <p>The string to display to a user</p>
*/
public static String getCurrentValueMessage(String setting, String currentValue) {
return replacePlaceholders(Translator.getTranslatedMessage(TranslatableMessage.CURRENT_VALUE),
new String[]{"{setting}", "{currentValue}"}, new String[]{setting, currentValue});
return StringFormatter.replacePlaceholders(BlacksmithPlugin.getTranslator().getTranslatedMessage(
BlacksmithTranslatableMessage.CURRENT_VALUE), new String[]{"{setting}", "{currentValue}"},
new String[]{setting, currentValue});
}
/**
@ -225,9 +195,14 @@ public enum TranslatableMessage {
* @return <p>The string to display to a user</p>
*/
public static String getItemCurrentValueMessage(String setting, ItemType itemType, String item, String currentValue) {
return replacePlaceholders(Translator.getTranslatedMessage(TranslatableMessage.CURRENT_VALUE_FOR_ITEM),
return StringFormatter.replacePlaceholders(BlacksmithPlugin.getTranslator().getTranslatedMessage(
BlacksmithTranslatableMessage.CURRENT_VALUE_FOR_ITEM),
new String[]{"{setting}", "{itemType}", "{item}", "{currentValue}"},
new String[]{setting, itemType.getItemTypeName(), item, currentValue});
}
@Override
public TranslatableMessage[] getAllMessages() {
return BlacksmithTranslatableMessage.values();
}
}

View File

@ -1,5 +1,7 @@
package net.knarcraft.blacksmith.formatting;
import net.knarcraft.blacksmith.BlacksmithPlugin;
/**
* An enum representing all item types used in messages
*/
@ -15,8 +17,10 @@ public enum ItemType {
*/
public String getItemTypeName() {
return switch (this) {
case MATERIAL -> Translator.getTranslatedMessage(TranslatableMessage.ITEM_TYPE_MATERIAL);
case ENCHANTMENT -> Translator.getTranslatedMessage(TranslatableMessage.ITEM_TYPE_ENCHANTMENT);
case MATERIAL -> BlacksmithPlugin.getTranslator().getTranslatedMessage(
BlacksmithTranslatableMessage.ITEM_TYPE_MATERIAL);
case ENCHANTMENT -> BlacksmithPlugin.getTranslator().getTranslatedMessage(
BlacksmithTranslatableMessage.ITEM_TYPE_ENCHANTMENT);
};
}

View File

@ -1,128 +0,0 @@
package net.knarcraft.blacksmith.formatting;
import net.citizensnpcs.api.npc.NPC;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A formatter for formatting displayed messages
*/
public final class StringFormatter {
private final static String pluginName = BlacksmithPlugin.getInstance().getDescription().getName();
private StringFormatter() {
}
/**
* Sends a message from a blacksmith NPC to a player
*
* @param npc <p>The NPC sending the message</p>
* @param player <p>The player to send the message to</p>
* @param message <p>The message to send</p>
*/
public static void sendNPCMessage(NPC npc, Player player, String message) {
player.sendMessage(ChatColor.GREEN + "[" + npc.getName() + "] -> You:" + ChatColor.RESET + " " +
translateColors(message));
}
/**
* Displays a message signifying a successful action
*
* @param sender <p>The command sender to display the message to</p>
* @param message <p>The translatable message to display</p>
*/
public static void displaySuccessMessage(CommandSender sender, TranslatableMessage message) {
sender.sendMessage(ChatColor.GREEN + getFormattedMessage(Translator.getTranslatedMessage(message)));
}
/**
* Displays a message signifying a successful action
*
* @param sender <p>The command sender to display the message to</p>
* @param message <p>The raw message to display</p>
*/
public static void displaySuccessMessage(CommandSender sender, String message) {
sender.sendMessage(ChatColor.GREEN + getFormattedMessage(message));
}
/**
* Displays a message signifying an unsuccessful action
*
* @param sender <p>The command sender to display the message to</p>
* @param message <p>The translatable message to display</p>
*/
public static void displayErrorMessage(CommandSender sender, TranslatableMessage message) {
sender.sendMessage(ChatColor.DARK_RED + getFormattedMessage(Translator.getTranslatedMessage(message)));
}
/**
* Gets the formatted version of any chat message
*
* @param message <p>The message to format</p>
* @return <p>The formatted message</p>
*/
private static String getFormattedMessage(String message) {
return "[" + pluginName + "] " + ChatColor.RESET + translateColors(message);
}
/**
* Translates & color codes to proper colors
*
* @param input <p>The input string to translate colors for</p>
* @return <p>The input with color codes translated</p>
*/
private static String translateColors(String input) {
return ChatColor.translateAlternateColorCodes('&', input);
}
/**
* Replaces a placeholder in a string
*
* @param input <p>The input string to replace in</p>
* @param placeholder <p>The placeholder to replace</p>
* @param replacement <p>The replacement value</p>
* @return <p>The input string with the placeholder replaced</p>
*/
public static String replacePlaceholder(String input, String placeholder, String replacement) {
return input.replace(placeholder, replacement);
}
/**
* Replaces placeholders in a string
*
* @param input <p>The input string to replace in</p>
* @param placeholders <p>The placeholders to replace</p>
* @param replacements <p>The replacement values</p>
* @return <p>The input string with placeholders replaced</p>
*/
public static String replacePlaceholders(String input, String[] placeholders, String[] replacements) {
for (int i = 0; i < Math.min(placeholders.length, replacements.length); i++) {
input = replacePlaceholder(input, placeholders[i], replacements[i]);
}
return input;
}
/**
* Translates all found color codes to formatting in a string
*
* @param message <p>The string to search for color codes</p>
* @return <p>The message with color codes translated</p>
*/
public static String translateAllColorCodes(String message) {
message = ChatColor.translateAlternateColorCodes('&', message);
Pattern pattern = Pattern.compile("(#[a-fA-F0-9]{6})");
Matcher matcher = pattern.matcher(message);
while (matcher.find()) {
message = message.replace(matcher.group(), "" + ChatColor.of(matcher.group()));
}
return message;
}
}

View File

@ -1,23 +1,16 @@
package net.knarcraft.blacksmith.formatting;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import static net.knarcraft.blacksmith.formatting.StringFormatter.replacePlaceholder;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
/**
* A utility for formatting a string specifying an amount of time
*/
public final class TimeFormatter {
private static Map<Double, TranslatableMessage[]> timeUnits;
private static List<Double> sortedUnits;
private TimeFormatter() {
}
@ -31,7 +24,7 @@ public final class TimeFormatter {
*/
public static String formatTime(boolean exact, int seconds) {
if (exact) {
return getDurationString(seconds);
return net.knarcraft.knarlib.formatting.TimeFormatter.getDurationString(BlacksmithPlugin.getTranslator(), seconds);
} else {
return formatUnclearTime(seconds);
}
@ -60,7 +53,7 @@ public final class TimeFormatter {
* @return <p>Text describing the time interval</p>
*/
private static String getMessageFromInterval(TimeInterval interval) {
String text = Translator.getTranslatedMessage(TranslatableMessage.valueOf(interval.name()));
String text = BlacksmithPlugin.getTranslator().getTranslatedMessage(BlacksmithTranslatableMessage.valueOf(interval.name()));
//Choose a random entry if a comma-separated list is provided
if (text != null && text.contains(",")) {
@ -79,68 +72,4 @@ public final class TimeFormatter {
}
}
/**
* Gets the string used for displaying this sign's duration
*
* @return <p>The string used for displaying this sign's duration</p>
*/
public static String getDurationString(int duration) {
if (duration == 0) {
return Translator.getTranslatedMessage(TranslatableMessage.UNIT_NOW);
} else {
if (sortedUnits == null) {
initializeUnits();
}
for (Double unit : sortedUnits) {
if (duration / unit >= 1) {
double units = round(duration / unit);
return formatDurationString(units, timeUnits.get(unit)[units == 1 ? 0 : 1],
(units * 10) % 10 == 0);
}
}
return formatDurationString(duration, TranslatableMessage.UNIT_SECONDS, false);
}
}
/**
* Rounds a number to its last two digits
*
* @param number <p>The number to round</p>
* @return <p>The rounded number</p>
*/
private static double round(double number) {
return Math.round(number * 100.0) / 100.0;
}
/**
* Formats a duration string
*
* @param duration <p>The duration to display</p>
* @param translatableMessage <p>The time unit to display</p>
* @param castToInt <p>Whether to cast the duration to an int</p>
* @return <p>The formatted duration string</p>
*/
private static String formatDurationString(double duration, TranslatableMessage translatableMessage, boolean castToInt) {
String durationFormat = Translator.getTranslatedMessage(TranslatableMessage.DURATION_FORMAT);
durationFormat = replacePlaceholder(durationFormat, "{unit}",
Translator.getTranslatedMessage(translatableMessage));
return replacePlaceholder(durationFormat, "{time}", castToInt ? String.valueOf((int) duration) :
String.valueOf(duration));
}
/**
* Initializes the mapping of available time units for formatting permission sign duration
*/
private static void initializeUnits() {
double minute = 60;
timeUnits = new HashMap<>();
timeUnits.put(minute, new TranslatableMessage[]{TranslatableMessage.UNIT_MINUTE, TranslatableMessage.UNIT_MINUTES});
timeUnits.put(1D, new TranslatableMessage[]{TranslatableMessage.UNIT_SECOND, TranslatableMessage.UNIT_SECONDS});
sortedUnits = new ArrayList<>(timeUnits.keySet());
Collections.sort(sortedUnits);
Collections.reverse(sortedUnits);
}
}

View File

@ -1,120 +0,0 @@
package net.knarcraft.blacksmith.formatting;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.util.FileHelper;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
/**
* A tool to get strings translated to the correct language
*/
public final class Translator {
private static Map<TranslatableMessage, String> translatedMessages;
private static Map<TranslatableMessage, String> backupTranslatedMessages;
private Translator() {
}
/**
* Loads the languages used by this translator
*/
public static void loadLanguages(String selectedLanguage) {
backupTranslatedMessages = loadTranslatedMessages("en");
translatedMessages = loadCustomTranslatedMessages(selectedLanguage);
if (translatedMessages == null) {
translatedMessages = loadTranslatedMessages(selectedLanguage);
}
}
/**
* Gets a translated version of the given translatable message
*
* @param translatableMessage <p>The message to translate</p>
* @return <p>The translated message</p>
*/
public static String getTranslatedMessage(TranslatableMessage translatableMessage) {
if (translatedMessages == null) {
return "Translated strings not loaded";
}
String translatedMessage;
if (translatedMessages.containsKey(translatableMessage)) {
translatedMessage = translatedMessages.get(translatableMessage);
} else if (backupTranslatedMessages.containsKey(translatableMessage)) {
translatedMessage = backupTranslatedMessages.get(translatableMessage);
} else {
translatedMessage = translatableMessage.toString();
}
return StringFormatter.translateAllColorCodes(translatedMessage);
}
/**
* Loads all translated messages for the given language
*
* @param language <p>The language chosen by the user</p>
* @return <p>A mapping of all strings for the given language</p>
*/
public static Map<TranslatableMessage, String> loadTranslatedMessages(String language) {
try {
BufferedReader reader = FileHelper.getBufferedReaderForInternalFile("/strings.yml");
return loadTranslatableMessages(language, reader);
} catch (FileNotFoundException e) {
BlacksmithPlugin.getInstance().getLogger().log(Level.SEVERE, "Unable to load translated messages");
return null;
}
}
/**
* Tries to load translated messages from a custom strings.yml file
*
* @param language <p>The selected language</p>
* @return <p>The loaded translated strings, or null if no custom language file exists</p>
*/
public static Map<TranslatableMessage, String> loadCustomTranslatedMessages(String language) {
BlacksmithPlugin instance = BlacksmithPlugin.getInstance();
File strings = new File(instance.getDataFolder(), "strings.yml");
if (!strings.exists()) {
instance.getLogger().log(Level.FINEST, "Strings file not found");
return null;
}
try {
instance.getLogger().log(Level.INFO, "Loading custom strings...");
return loadTranslatableMessages(language, new BufferedReader(new InputStreamReader(new FileInputStream(strings))));
} catch (FileNotFoundException e) {
instance.getLogger().log(Level.WARNING, "Unable to load custom messages");
return null;
}
}
/**
* Loads translatable messages from the given reader
*
* @param language <p>The selected language</p>
* @param reader <p>The buffered reader to read from</p>
* @return <p>The loaded translated strings</p>
*/
private static Map<TranslatableMessage, String> loadTranslatableMessages(String language, BufferedReader reader) {
Map<TranslatableMessage, String> translatedMessages = new HashMap<>();
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(reader);
for (TranslatableMessage message : TranslatableMessage.values()) {
String translated = configuration.getString(language + "." + message.toString());
if (translated != null) {
translatedMessages.put(message, translated);
}
}
return translatedMessages;
}
}

View File

@ -1,7 +1,7 @@
package net.knarcraft.blacksmith.listener;
import net.knarcraft.blacksmith.formatting.StringFormatter;
import net.knarcraft.blacksmith.formatting.TranslatableMessage;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.formatting.BlacksmithTranslatableMessage;
import net.knarcraft.blacksmith.trait.BlacksmithTrait;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@ -25,7 +25,8 @@ public class NPCClickListener implements Listener {
//Permission check
if (!player.hasPermission("blacksmith.use")) {
StringFormatter.displayErrorMessage(player, TranslatableMessage.PERMISSION_DENIED);
BlacksmithPlugin.getStringFormatter().displayErrorMessage(player,
BlacksmithTranslatableMessage.PERMISSION_DENIED);
return;
}

View File

@ -91,6 +91,7 @@ public class EconomyManager {
private static double getCost(ItemStack item) {
GlobalSettings globalSettings = BlacksmithPlugin.getInstance().getSettings();
Material material = item.getType();
//Calculate the base price
double price = globalSettings.getBasePrice(material);
@ -106,6 +107,11 @@ public class EconomyManager {
//Increase price for any enchantments
price += getEnchantmentCost(item);
//Override the cost for anvils
if (ItemHelper.isAnvil(material, true)) {
price = globalSettings.getAnvilCost(material);
}
return price;
}

View File

@ -8,6 +8,7 @@ import net.knarcraft.blacksmith.config.NPCSettings;
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.enchantments.EnchantmentTarget;
@ -23,8 +24,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import static net.knarcraft.blacksmith.formatting.StringFormatter.replacePlaceholder;
import static net.knarcraft.blacksmith.formatting.StringFormatter.sendNPCMessage;
import static net.knarcraft.blacksmith.formatting.BlacksmithStringFormatter.sendNPCMessage;
/**
* The class representing the Blacksmith NPC trait
@ -114,7 +114,7 @@ public class BlacksmithTrait extends Trait {
coolDowns.remove(playerId);
}
//Deny if permission is missing
if (!player.hasPermission("blacksmith.reforge")) {
if (!player.hasPermission("blacksmith.use")) {
return false;
}
@ -124,7 +124,7 @@ public class BlacksmithTrait extends Trait {
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, replacePlaceholder(config.getCoolDownUnexpiredMessage(),
sendNPCMessage(this.npc, player, StringFormatter.replacePlaceholder(config.getCoolDownUnexpiredMessage(),
"{time}", TimeFormatter.formatTime(exactTime, secondDifference)));
return false;
}
@ -157,8 +157,8 @@ public class BlacksmithTrait extends Trait {
if (session.isRunning()) {
int timeRemaining = (int) ((session.getFinishTime() - System.currentTimeMillis()) / 1000);
boolean showExactTime = BlacksmithPlugin.getInstance().getSettings().getShowExactTime();
sendNPCMessage(this.npc, player, replacePlaceholder(config.getBusyReforgingMessage(), "{time}",
TimeFormatter.formatTime(showExactTime, timeRemaining)));
sendNPCMessage(this.npc, player, StringFormatter.replacePlaceholder(config.getBusyReforgingMessage(),
"{time}", TimeFormatter.formatTime(showExactTime, timeRemaining)));
return;
}
if (session.endSession()) {
@ -179,14 +179,15 @@ public class BlacksmithTrait extends Trait {
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();
if (!isRepairable(hand) || (!reforgeAbleItems.isEmpty() && !reforgeAbleItems.contains(hand.getType()))) {
String invalidMessage = replacePlaceholder(config.getInvalidItemMessage(),
if ((!this.config.getRepairAnvils() || !ItemHelper.isAnvil(hand.getType(), false)) &&
(!isRepairable(hand) || (!reforgeAbleItems.isEmpty() && !reforgeAbleItems.contains(hand.getType())))) {
String invalidMessage = StringFormatter.replacePlaceholder(config.getInvalidItemMessage(),
"{title}", config.getBlacksmithTitle());
sendNPCMessage(this.npc, player, invalidMessage);
return;
}
if (ItemHelper.getDamage(hand) == 0) {
if (ItemHelper.getDamage(hand) == 0 && !ItemHelper.isAnvil(hand.getType(), true)) {
sendNPCMessage(this.npc, player, config.getNotDamagedMessage());
return;
}

View File

@ -6,6 +6,7 @@ import net.knarcraft.blacksmith.config.NPCSettings;
import net.knarcraft.blacksmith.manager.EconomyManager;
import net.knarcraft.blacksmith.util.InputParsingHelper;
import net.knarcraft.blacksmith.util.ItemHelper;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
@ -21,7 +22,7 @@ import java.util.Objects;
import java.util.Random;
import java.util.logging.Level;
import static net.knarcraft.blacksmith.formatting.StringFormatter.sendNPCMessage;
import static net.knarcraft.blacksmith.formatting.BlacksmithStringFormatter.sendNPCMessage;
/**
* A representation of the session between a player and a blacksmith
@ -129,6 +130,11 @@ public class ReforgeSession implements Runnable {
// Remove any damage done to the item
updateDamage(itemToReforge, 0);
//Replace damaged anvils with a normal anvil
if (ItemHelper.isAnvil(itemToReforge.getType(), true)) {
itemToReforge.setType(Material.ANVIL);
}
// Add random enchantments
int roll = random.nextInt(100);
if (!(roll < config.getExtraEnchantmentChance() &&
@ -145,6 +151,13 @@ public class ReforgeSession implements Runnable {
usableEnchantments.add(enchantment);
}
}
//Remove any enchantments in the block list
usableEnchantments.removeAll(blacksmithTrait.getSettings().getEnchantmentBlocklist());
//In case all usable enchantments have been blocked, abort
if (usableEnchantments.isEmpty()) {
return;
}
//Choose a random enchantment
Enchantment randomEnchantment = usableEnchantments.get(random.nextInt(usableEnchantments.size()));

View File

@ -1,5 +1,8 @@
package net.knarcraft.blacksmith.util;
import java.util.ArrayList;
import java.util.List;
/**
* A helper class for getting an object value as the correct type
*/
@ -9,6 +12,25 @@ public final class ConfigHelper {
}
/**
* Gets the given value as a string list
*
* @param value <p>The raw string list value</p>
* @return <p>The value as a string list</p>
*/
public static List<String> asStringList(Object value) {
if (value instanceof String) {
return List.of(((String) value).split(","));
} else {
List<String> strings = new ArrayList<>();
List<?> list = (List<?>) value;
for (Object object : list) {
strings.add(String.valueOf(object));
}
return strings;
}
}
/**
* Gets the given value as a double
*

View File

@ -1,32 +0,0 @@
package net.knarcraft.blacksmith.util;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
/**
* A helper class for dealing with files
*/
public final class FileHelper {
private FileHelper() {
}
/**
* Gets a buffered reader for
*
* @return <p>A buffered read for reading the file</p>
* @throws FileNotFoundException <p>If unable to get an input stream for the given file</p>
*/
public static BufferedReader getBufferedReaderForInternalFile(String file) throws FileNotFoundException {
InputStream inputStream = FileHelper.class.getResourceAsStream(file);
if (inputStream == null) {
throw new FileNotFoundException("Unable to read the given file");
}
return new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
}
}

View File

@ -44,4 +44,14 @@ public final class InputParsingHelper {
return Enchantment.getByKey(NamespacedKey.minecraft(input.replace("-", "_")));
}
/**
* Converts a material name like "*helmet" into a regular expression ".*HELMET"
*
* @param input <p>The input to RegExIfy</p>
* @return <p>The converted input</p>
*/
public static String regExIfy(String input) {
return input.replace("*", ".*").toUpperCase().replace("-", "_");
}
}

View File

@ -1,8 +1,13 @@
package net.knarcraft.blacksmith.util;
import org.bukkit.Material;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.Damageable;
import java.util.ArrayList;
import java.util.List;
public final class ItemHelper {
private ItemHelper() {
@ -40,4 +45,33 @@ public final class ItemHelper {
}
}
/**
* Gets a complete list of all reforge-able materials
*
* @return <p>A complete list of reforge-able materials</p>
*/
public static List<Material> getAllReforgeAbleMaterials() {
List<Material> reforgeAbleMaterials = new ArrayList<>();
for (Material material : Material.values()) {
ItemStack item = new ItemStack(material);
if (item.getItemMeta() instanceof Damageable && EnchantmentTarget.BREAKABLE.includes(item)) {
reforgeAbleMaterials.add(material);
}
}
return reforgeAbleMaterials;
}
/**
* Checks whether the given material is an anvil
*
* @param material <p>The material to check</p>
* @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) {
boolean isDamagedAnvil = material == Material.CHIPPED_ANVIL || material == Material.DAMAGED_ANVIL;
boolean isAnvil = isDamagedAnvil || material == Material.ANVIL;
return (requireDamaged && isDamagedAnvil) || (!requireDamaged && isAnvil);
}
}

View File

@ -1,11 +1,10 @@
package net.knarcraft.blacksmith.util;
import net.knarcraft.blacksmith.config.SettingValueType;
import net.knarcraft.blacksmith.config.SmithPreset;
import net.knarcraft.blacksmith.config.SmithPresetFilter;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.Damageable;
import java.util.ArrayList;
import java.util.List;
@ -32,25 +31,47 @@ public final class TabCompleteValuesHelper {
case POSITIVE_DOUBLE -> getPositiveDoubles();
case STRING -> getStrings();
case PERCENTAGE -> getPercentages();
case STRING_LIST -> getReforgeAbleMaterials();
case MATERIAL -> getAllReforgeAbleMaterials();
case REFORGE_ABLE_ITEMS -> getReforgeAbleMaterials();
case MATERIAL -> getAllReforgeAbleMaterialNames();
case ENCHANTMENT -> getAllEnchantments();
case STRING_LIST -> getExampleEnchantmentBlockLists();
};
}
/**
* Gets a complete list of all reforge-able materials
* Gets example enchantment block lists
*
* @return <p>A complete list of reforge-able materials</p>
* @return <p>Some example enchantment block lists</p>
*/
private static List<String> getAllReforgeAbleMaterials() {
private static List<String> getExampleEnchantmentBlockLists() {
List<String> exampleBlockLists = new ArrayList<>();
exampleBlockLists.add(Enchantment.VANISHING_CURSE.getKey().getKey() + "," +
Enchantment.BINDING_CURSE.getKey().getKey() + "," + Enchantment.MENDING.getKey().getKey());
exampleBlockLists.add(Enchantment.VANISHING_CURSE.getKey().getKey() + "," +
Enchantment.BINDING_CURSE.getKey().getKey());
return exampleBlockLists;
}
/**
* Gets a complete list of all reforge-able material names
*
* @return <p>A complete list of reforge-able material names</p>
*/
private static List<String> getAllReforgeAbleMaterialNames() {
List<String> reforgeAbleMaterials = new ArrayList<>();
for (Material material : Material.values()) {
ItemStack item = new ItemStack(material);
if (item.getItemMeta() instanceof Damageable && EnchantmentTarget.BREAKABLE.includes(item)) {
for (Material material : ItemHelper.getAllReforgeAbleMaterials()) {
reforgeAbleMaterials.add(material.name());
}
}
reforgeAbleMaterials.add("NETHERITE_*");
reforgeAbleMaterials.add("DIAMOND_*");
reforgeAbleMaterials.add("GOLDEN_*");
reforgeAbleMaterials.add("IRON_*");
reforgeAbleMaterials.add("CHAINMAIL_*");
reforgeAbleMaterials.add("STONE_*");
reforgeAbleMaterials.add("*BOW");
reforgeAbleMaterials.add("LEATHER_*");
reforgeAbleMaterials.add("WOODEN_*");
return reforgeAbleMaterials;
}
@ -74,12 +95,17 @@ public final class TabCompleteValuesHelper {
*/
private static List<String> getReforgeAbleMaterials() {
List<String> stringLists = new ArrayList<>();
stringLists.add("preset:sword-smith");
stringLists.add("preset:weapon-smith");
stringLists.add("preset:armor-smith");
stringLists.add("preset:tool-smith");
stringLists.add("preset:ranged-smith");
stringLists.add("bow,crossbow,elytra");
for (SmithPreset preset : SmithPreset.values()) {
stringLists.add("preset:" + preset.name());
for (SmithPresetFilter filter : preset.getSupportedFilters()) {
stringLists.add("preset:" + preset.name() + ":" + filter.name());
}
}
stringLists.add("preset:WEAPON_SMITH:RANGED,SHIELD");
stringLists.add("preset:WEAPON_SMITH,preset:ARMOR_SMITH");
stringLists.add("preset:WEAPON_SMITH,preset:TOOL_SMITH");
stringLists.add("preset:ARMOR_SMITH,preset:TOOL_SMITH");
stringLists.add("BOW,CROSSBOW,ELYTRA");
return stringLists;
}

View File

@ -1,32 +0,0 @@
package net.knarcraft.blacksmith.util;
import java.util.ArrayList;
import java.util.List;
/**
* A helper class for tab-completion
*/
public final class TabCompletionHelper {
private TabCompletionHelper() {
}
/**
* Finds tab complete values that contain the typed text
*
* @param values <p>The values to filter</p>
* @param typedText <p>The text the player has started typing</p>
* @return <p>The given string values that contain the player's typed text</p>
*/
public static List<String> filterMatchingContains(List<String> values, String typedText) {
List<String> configValues = new ArrayList<>();
for (String value : values) {
if (value.toLowerCase().contains(typedText.toLowerCase())) {
configValues.add(value);
}
}
return configValues;
}
}

View File

@ -1,10 +1,11 @@
package net.knarcraft.blacksmith.util;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.config.SettingValueType;
import net.knarcraft.blacksmith.formatting.TranslatableMessage;
import net.knarcraft.blacksmith.formatting.BlacksmithTranslatableMessage;
import org.bukkit.command.CommandSender;
import static net.knarcraft.blacksmith.formatting.StringFormatter.displayErrorMessage;
import java.util.List;
/**
* A helper class for validating a value's type
@ -31,7 +32,7 @@ public final class TypeValidationHelper {
case POSITIVE_INTEGER -> isPositiveInteger(value, sender);
case PERCENTAGE -> isPercentage(value, sender);
case BOOLEAN -> true;
case STRING_LIST -> isStringList(value, sender);
case STRING_LIST, REFORGE_ABLE_ITEMS -> isStringList(value, sender);
case MATERIAL, ENCHANTMENT -> false;
};
} catch (ClassCastException exception) {
@ -48,9 +49,10 @@ public final class TypeValidationHelper {
* @return <p>True if the value is a string list</p>
*/
private static boolean isStringList(Object value, CommandSender sender) {
boolean isStringList = value instanceof String[] || value instanceof String;
boolean isStringList = value instanceof String[] || value instanceof List<?> || value instanceof String;
if (!isStringList && sender != null) {
displayErrorMessage(sender, TranslatableMessage.INPUT_STRING_LIST_REQUIRED);
BlacksmithPlugin.getStringFormatter().displayErrorMessage(sender,
BlacksmithTranslatableMessage.INPUT_STRING_LIST_REQUIRED);
}
return isStringList;
}
@ -65,10 +67,11 @@ public final class TypeValidationHelper {
private static boolean isPercentage(Object value, CommandSender sender) {
try {
int intValue = ConfigHelper.asInt(value);
return intValue > 0 && intValue <= 100;
return intValue >= 0 && intValue <= 100;
} catch (NumberFormatException | NullPointerException exception) {
if (sender != null) {
displayErrorMessage(sender, TranslatableMessage.INPUT_PERCENTAGE_REQUIRED);
BlacksmithPlugin.getStringFormatter().displayErrorMessage(sender,
BlacksmithTranslatableMessage.INPUT_PERCENTAGE_REQUIRED);
}
return false;
}
@ -82,9 +85,10 @@ public final class TypeValidationHelper {
* @return <p>True if the value is a non-empty string</p>
*/
private static boolean isNonEmptyString(Object value, CommandSender sender) {
boolean isString = value instanceof String string && !string.strip().isEmpty();
boolean isString = value instanceof String string && !string.isBlank();
if (!isString && sender != null) {
displayErrorMessage(sender, TranslatableMessage.INPUT_STRING_REQUIRED);
BlacksmithPlugin.getStringFormatter().displayErrorMessage(sender,
BlacksmithTranslatableMessage.INPUT_STRING_REQUIRED);
}
return isString;
}
@ -98,10 +102,11 @@ public final class TypeValidationHelper {
*/
private static boolean isPositiveDouble(Object value, CommandSender sender) {
try {
return ConfigHelper.asDouble(value) > 0.0;
return ConfigHelper.asDouble(value) >= 0.0;
} catch (NumberFormatException | NullPointerException exception) {
if (sender != null) {
displayErrorMessage(sender, TranslatableMessage.INPUT_POSITIVE_DOUBLE_REQUIRED);
BlacksmithPlugin.getStringFormatter().displayErrorMessage(sender,
BlacksmithTranslatableMessage.INPUT_POSITIVE_DOUBLE_REQUIRED);
}
return false;
}
@ -116,10 +121,11 @@ public final class TypeValidationHelper {
*/
private static boolean isPositiveInteger(Object value, CommandSender sender) {
try {
return ConfigHelper.asInt(value) > 0;
return ConfigHelper.asInt(value) >= 0;
} catch (NumberFormatException | NullPointerException exception) {
if (sender != null) {
displayErrorMessage(sender, TranslatableMessage.INPUT_POSITIVE_INTEGER_REQUIRED);
BlacksmithPlugin.getStringFormatter().displayErrorMessage(sender,
BlacksmithTranslatableMessage.INPUT_POSITIVE_INTEGER_REQUIRED);
}
return false;
}

View File

@ -28,6 +28,12 @@ global:
# reforging delay messages, instead of just vaguely hinting at the remaining time.
showExactTime: false
# The cost of fully repairing a chipped anvil
chippedAnvilReforgingCost: 10.0
# The cost of fully repairing a damaged anvil
damagedAnvilReforgingCost: 20.0
# The settings which are set to any new NPC. To change any of these settings for an existing NPC, you must change the
# Citizens NPC file, or use the /blacksmith command
defaults:
@ -39,6 +45,9 @@ defaults:
# 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%
@ -48,6 +57,9 @@ defaults:
# 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

View File

@ -1,7 +1,7 @@
name: Blacksmith
author: EpicKnarvik97, aPunch, jrbudda, HurricanKai
authors: [ EpicKnarvik97, aPunch, jrbudda, HurricanKai ]
version: 1.0.1
version: '${project.version}'
main: net.knarcraft.blacksmith.BlacksmithPlugin
depend: [ Citizens, Vault ]

View File

@ -7,7 +7,6 @@ en:
ITEM_TYPE_MATERIAL: "material"
RAW_VALUE: "Raw value: {rawValue}"
NO_NPC_SELECTED: "You must select an NPC before running this command"
DEFAULT_REFORGE_ABLE_ITEMS_UNCHANGEABLE: "Changing reforge-able items globally will make every new blacksmith unable to reforge anything not in the list, unless it's changed for the individual NPC. If you really want to change this, change it manually."
INPUT_STRING_LIST_REQUIRED: "A string list is required!"
INPUT_PERCENTAGE_REQUIRED: "You specified a value which isn't between 0 and 100!"
INPUT_STRING_REQUIRED: "A non-empty string is required!"
@ -24,6 +23,18 @@ en:
UNIT_SECONDS: "seconds"
UNIT_MINUTE: "minute"
UNIT_MINUTES: "minutes"
UNIT_HOUR: "hour"
UNIT_HOURS: "hours"
UNIT_DAY: "day"
UNIT_DAYS: "days"
UNIT_WEEK: "week"
UNIT_WEEKS: "weeks"
UNIT_MONTH: "month"
UNIT_MONTHS: "months"
UNIT_YEAR: "year"
UNIT_YEARS: "years"
UNIT_DECADE: "decade"
UNIT_DECADES: "decades"
INTERVAL_LESS_THAN_10_SECONDS: "in just a moment"
INTERVAL_LESS_THAN_30_SECONDS: "in a little while"
INTERVAL_LESS_THAN_1_MINUTE: "in a while"