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
}
}
}
}

189
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 - 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. makes the cost increase the more damaged the item is.
- EnchantmentTarget is used instead of a hard-coded list of repairable items - 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. 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 - This plugin is not directly compatible with the original. If you are using the old one, you will need to set it up
again! again!
@ -19,12 +19,16 @@ fee. Costs are highly customizable.
- Citizens2 - Citizens2
- Vault - Vault
- Any Vault-supported Economy plugin
## Basic usage ## Basic usage
To create a new blacksmith, simply add the blacksmith trait to an NPC. Right-clicking the NPC will tell you if the To create a new blacksmith, simply add the blacksmith trait to an NPC by selecting it with `/npc select`, and then using
currently held item is repairable by the blacksmith. If it is, the blacksmith should give a price quote. Right-clicking `/trait add Blacksmith` (See Citizens' documentation for more details).
again starts the repair. The item should be given back or dropped after a random delay according to the set limits.
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, 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, cool-down between each reforge, whether the item is dropped or given, the chance of failing or adding an enchantment,
@ -43,15 +47,16 @@ 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 - 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. 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 - 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 ## Commands
| Command | Arguments | Description | | Command | Arguments | Description |
| --- | --- | --- | |-------------------|-------------------------------|----------------------------------------------------------------------------------------------|
| /blacksmith | \<option> \[new-value] | Changes a configuration option for the selected blacksmith (use Citizens' /npc select first) | | /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 | | /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 | | /preset | \<preset>\[:filter] | Displays all materials included in the given preset, after applying the filter if set |
For /blacksmith and /blacksmithconfig, if a new value isn't specified, the current value is displayed instead. For /blacksmith and /blacksmithconfig, if a new value isn't specified, the current value is displayed instead.
@ -65,100 +70,126 @@ 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 material/enchantment>, using -1 or null as the value will clear the cost for the specified material/enchantment, using
the default one instead. 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: ### Presets and filters:
Note: All of these can be used when specifying reforge-able items, such as "preset:weapon-smith:bow,preset:armor_smith: **Presets are a nice way to make specialized blacksmiths by specifying categories of materials, instead of manually
gold,PRESET:TOOL_SMITH,shield" 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: All currently supported presets, and available filters for each preset:
- WEAPON_SMITH: - BLACKSMITH (WEAPON_SMITH + ARMOR_SMITH + TOOL_SMITH)
- BOW - WEAPON_SMITH: (RANGED + SWORD + SHIELD)
- SWORD - BOW (bows and crossbows)
- RANGED - SWORD (swords)
- ARMOR_SMITH: - RANGED (bows, crossbows and tridents)
- LEATHER - ARMOR_SMITH: (HELMET + BOOTS + LEGGINGS + CHESTPLATE + ELYTRA)
- IRON - LEATHER (all pieces of leather armor)
- CHAINMAIL - IRON (all pieces of iron armor)
- GOLD - CHAINMAIL (all pieces of chainmail armor)
- DIAMOND - GOLD (all pieces of gold armor)
- NETHERITE - DIAMOND (all pieces of diamond armor)
- HELMET - NETHERITE (all pieces of netherite armor)
- BOOTS - HELMET (all helmets)
- LEGGINGS - BOOTS (all boots)
- CHESTPLATE - LEGGINGS (all leggings)
- TOOL_SMITH - CHESTPLATE (all chest-plates)
- WOOD - TOOL_SMITH: (PICKAXE + AXE + HOE + SHOVEL + MISC)
- STONE - WOOD (all wood tools)
- IRON - STONE (all stone tools)
- GOLD - IRON (all iron tools)
- DIAMOND - GOLD (all gold tools)
- NETHERITE - DIAMOND (all diamond tools)
- PICKAXE - NETHERITE (all netherite tools)
- AXE - PICKAXE (all pickaxes)
- HOE - AXE (all axes)
- SHOVEL - HOE (all hoes)
- MISC - SHOVEL (all shovels)
- MISC (FISHING_ROD + SHEARS + FLINT_AND_STEEL)
## Permissions ## Permissions
| Permission node | Description | | Permission node | Description |
| --- | --- | |------------------|----------------------------------------------------------|
| blacksmith.admin | Allows overall blacksmith configuration | | blacksmith.admin | Allows overall blacksmith configuration |
| blacksmith.edit | Allows changing settings for the selected blacksmith NPC | | blacksmith.edit | Allows changing settings for the selected blacksmith NPC |
| blacksmith.use | Allows the player to repair items using blacksmiths | | blacksmith.use | Allows the player to repair items using blacksmiths |
## Configuration options ## Configuration options
### Plugin Options ### Plugin Options
| Key | Value type | Description | | Key | Value type | Description |
| --- | --- | --- | |----------|------------|----------------------------------------------------------------------------------------------|
| language | string | The language used for this plugin. Only "en" is supported, unless you add a custom language. | | language | string | The language used for this plugin. Only "en" is supported, unless you add a custom language. |
### Global-only options ### Global-only options
| Key | Value type | Description | | 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. | | 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. | | 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. | 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). | | 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 | | 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) ### Per-npc (with default values set in config.yml)
#### Configuration values #### Configuration values
| Key | Value type | Description | | 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). | | 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. | | 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. | | disableDelay | true/false | Whether to completely disable the delay required to reforge an item. |
| failReforgeChance | 0-100 | The chance of the blacksmith failing to repair an item. | | failReforgeChance | 0-100 | The chance of the blacksmith failing to repair an item. |
| extraEnchantmentChance | 0-100 | The chance of the blacksmith adding an enchantment to an item. | | extraEnchantmentChance | 0-100 | The chance of the blacksmith adding an enchantment to an item. |
| maxEnchantments | 0-10 | The maximum number of different enchantments a blacksmith can add. | | maxEnchantments | 0-10 | The maximum number of different enchantments a blacksmith can add. |
| maxReforgeDelay | 0-3600 | The maximum number of seconds a player needs to wait for an item to be repaired. | | 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. | | 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. | | 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 | | 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 #### Messages
| Message Key | Explanation | | Message Key | Explanation |
| --- | --- | |--------------------------|-----------------------------------------------------------------------------------------------------------------|
| busyPlayerMessage | The message displayed when the blacksmith is serving another player | | busyPlayerMessage | The message displayed when the blacksmith is serving another player |
| busyReforgeMessage | The message displayed when the blacksmith is busy reforging an item | | 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 | | coolDownUnexpiredMessage | The message displayed when the player has to wait for the cool-down to expire before using the blacksmith again |
| costMessage | The message displayed when telling a player about the cost of repairing an item | | costMessage | The message displayed when telling a player about the cost of repairing an item |
| failReforgeMessage | The message displayed when a blacksmith fails to reforge an item | | failReforgeMessage | The message displayed when a blacksmith fails to reforge an item |
| insufficientFundsMessage | The message displayed when a player is unable to pay for reforging an item | | insufficientFundsMessage | The message displayed when a player is unable to pay for reforging an item |
| invalidItemMessage | The message displayed when a blacksmith is presented an item which it cannot repair | | invalidItemMessage | The message displayed when a blacksmith is presented an item which it cannot repair |
| itemChangedMessage | The message displayed when a player changes their item after being shown the repair cost | | itemChangedMessage | The message displayed when a player changes their item after being shown the repair cost |
| startReforgeMessage | The message displayed when a blacksmith starts reforging an item | | startReforgeMessage | The message displayed when a blacksmith starts reforging an item |
| successMessage | The message displayed when a blacksmith successfully repairs an item | | successMessage | The message displayed when a blacksmith successfully repairs an item |
| notDamagedMessage | The message displayed if a player tries to reforge an item with full durability | | notDamagedMessage | The message displayed if a player tries to reforge an item with full durability |
## Language customization ## Language customization

85
pom.xml
View File

@ -6,31 +6,46 @@
<groupId>net.knarcraft</groupId> <groupId>net.knarcraft</groupId>
<artifactId>blacksmith</artifactId> <artifactId>blacksmith</artifactId>
<version>1.0.1-SNAPSHOT</version> <version>1.0.4</version>
<name>Blacksmith</name> <name>Blacksmith</name>
<description>Blacksmith Character for the CitizensAPI</description> <description>Blacksmith NPC for the Citizens API</description>
<!-- Properties --> <!-- Properties -->
<properties> <properties>
<java.version>16</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<build.number>Unknown</build.number> <build.number>Unknown</build.number>
</properties> </properties>
<!-- Repositories --> <!-- Repositories -->
<repositories> <repositories>
<repository>
<id>knarcraft-repo</id>
<url>https://git.knarcraft.net/api/packages/EpicKnarvik97/maven</url>
</repository>
<repository> <repository>
<id>spigot-repo</id> <id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url> <url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository> </repository>
<repository> <repository>
<id>citizens-repo</id> <id>citizens-repo</id>
<url>http://repo.citizensnpcs.co/</url> <url>https://repo.citizensnpcs.co/</url>
</repository> </repository>
<repository> <repository>
<id>vault-repo</id> <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> </repository>
</repositories> </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 -->
<dependencies> <dependencies>
@ -50,7 +65,7 @@
<dependency> <dependency>
<groupId>org.spigotmc</groupId> <groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId> <artifactId>spigot-api</artifactId>
<version>1.19.2-R0.1-SNAPSHOT</version> <version>1.19.3-R0.1-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
@ -62,34 +77,66 @@
<dependency> <dependency>
<groupId>org.jetbrains</groupId> <groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId> <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> <scope>compile</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<!-- Build information --> <!-- Build information -->
<build> <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> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version> <version>3.8.1</version>
<configuration> <configuration>
<source>16</source> <source>${java.version}</source>
<target>16</target> <target>${java.version}</target>
</configuration> </configuration>
</plugin> </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> </plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build> </build>
</project> </project>

View File

@ -8,11 +8,15 @@ import net.knarcraft.blacksmith.command.BlackSmithEditTabCompleter;
import net.knarcraft.blacksmith.command.PresetCommand; import net.knarcraft.blacksmith.command.PresetCommand;
import net.knarcraft.blacksmith.command.PresetTabCompleter; import net.knarcraft.blacksmith.command.PresetTabCompleter;
import net.knarcraft.blacksmith.config.GlobalSettings; 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.NPCClickListener;
import net.knarcraft.blacksmith.listener.PlayerListener; import net.knarcraft.blacksmith.listener.PlayerListener;
import net.knarcraft.blacksmith.manager.EconomyManager; import net.knarcraft.blacksmith.manager.EconomyManager;
import net.knarcraft.blacksmith.trait.BlacksmithTrait; 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.command.PluginCommand;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.PluginManager;
@ -27,6 +31,8 @@ public class BlacksmithPlugin extends JavaPlugin {
private static BlacksmithPlugin instance; private static BlacksmithPlugin instance;
private GlobalSettings config; private GlobalSettings config;
private static Translator translator;
private static StringFormatter stringFormatter;
/** /**
* Gets an instance of the Blacksmith plugin * Gets an instance of the Blacksmith plugin
@ -50,9 +56,27 @@ public class BlacksmithPlugin extends JavaPlugin {
* Reloads the configuration file from disk * Reloads the configuration file from disk
*/ */
public void reload() { public void reload() {
config.load();
this.reloadConfig(); 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 @Override
@ -75,7 +99,12 @@ public class BlacksmithPlugin extends JavaPlugin {
config = new GlobalSettings(this); config = new GlobalSettings(this);
config.load(); 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 //Set up Vault integration
if (!setUpVault()) { if (!setUpVault()) {
@ -92,6 +121,10 @@ public class BlacksmithPlugin extends JavaPlugin {
registerListeners(); registerListeners();
getLogger().log(Level.INFO, " v" + getDescription().getVersion() + " enabled."); 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.GlobalSettings;
import net.knarcraft.blacksmith.config.NPCSetting; import net.knarcraft.blacksmith.config.NPCSetting;
import net.knarcraft.blacksmith.config.SettingValueType; import net.knarcraft.blacksmith.config.SettingValueType;
import net.knarcraft.blacksmith.formatting.BlacksmithTranslatableMessage;
import net.knarcraft.blacksmith.formatting.ItemType; import net.knarcraft.blacksmith.formatting.ItemType;
import net.knarcraft.blacksmith.formatting.TranslatableMessage;
import net.knarcraft.blacksmith.util.InputParsingHelper; import net.knarcraft.blacksmith.util.InputParsingHelper;
import net.knarcraft.blacksmith.util.ItemHelper;
import net.knarcraft.blacksmith.util.TypeValidationHelper; import net.knarcraft.blacksmith.util.TypeValidationHelper;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import org.bukkit.Material; import org.bukkit.Material;
@ -19,9 +20,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.Arrays; import java.util.Arrays;
import static net.knarcraft.blacksmith.formatting.StringFormatter.displayErrorMessage; import static net.knarcraft.blacksmith.formatting.BlacksmithTranslatableMessage.getValueChangedMessage;
import static net.knarcraft.blacksmith.formatting.StringFormatter.displaySuccessMessage;
import static net.knarcraft.blacksmith.formatting.TranslatableMessage.getValueChangedMessage;
/** /**
* The command used for changing global configuration options * The command used for changing global configuration options
@ -41,12 +40,6 @@ public class BlackSmithConfigCommand implements CommandExecutor {
} }
GlobalSettings settings = BlacksmithPlugin.getInstance().getSettings(); 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 //Find which global setting the user has specified, if any
GlobalSetting detectedGlobalSetting = null; GlobalSetting detectedGlobalSetting = null;
for (GlobalSetting globalSetting : GlobalSetting.values()) { for (GlobalSetting globalSetting : GlobalSetting.values()) {
@ -105,7 +98,8 @@ public class BlackSmithConfigCommand implements CommandExecutor {
String newValue = args[1]; String newValue = args[1];
if (detectedGlobalSetting != null) { if (detectedGlobalSetting != null) {
settings.changeValue(detectedGlobalSetting, newValue); settings.changeValue(detectedGlobalSetting, newValue);
displaySuccessMessage(sender, getValueChangedMessage(detectedGlobalSetting.getCommandName(), newValue)); BlacksmithPlugin.getStringFormatter().displaySuccessMessage(sender,
getValueChangedMessage(detectedGlobalSetting.getCommandName(), newValue));
return true; return true;
} else if (detectedNPCSetting != null) { } else if (detectedNPCSetting != null) {
//This makes sure all arguments are treated as a sentence //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)); newValue = String.join(" ", Arrays.asList(args).subList(1, args.length));
} }
settings.changeValue(detectedNPCSetting, newValue); settings.changeValue(detectedNPCSetting, newValue);
displaySuccessMessage(sender, getValueChangedMessage(detectedNPCSetting.getCommandName(), newValue)); BlacksmithPlugin.getStringFormatter().displaySuccessMessage(sender,
getValueChangedMessage(detectedNPCSetting.getCommandName(), newValue));
return true; return true;
} else { } else {
return false; return false;
@ -151,10 +146,11 @@ public class BlackSmithConfigCommand implements CommandExecutor {
return false; return false;
} }
//Display the current value of the setting //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 //Print the value with any colors displayed as &a-f0-9
if (printRawValue) { if (printRawValue) {
sender.sendMessage(TranslatableMessage.getRawValueMessage( sender.sendMessage(BlacksmithTranslatableMessage.getRawValueMessage(
settingValue.replace(ChatColor.COLOR_CHAR, '&'))); settingValue.replace(ChatColor.COLOR_CHAR, '&')));
} }
return true; return true;
@ -182,17 +178,19 @@ public class BlackSmithConfigCommand implements CommandExecutor {
} else { } else {
currentValue = String.valueOf(settings.getPricePerDurabilityPoint(material)); currentValue = String.valueOf(settings.getPricePerDurabilityPoint(material));
} }
displaySuccessMessage(sender, TranslatableMessage.getItemCurrentValueMessage(setting.getCommandName(), BlacksmithPlugin.getStringFormatter().displaySuccessMessage(sender,
ItemType.MATERIAL, material.name(), currentValue)); BlacksmithTranslatableMessage.getItemCurrentValueMessage(setting.getCommandName(),
ItemType.MATERIAL, material.name(), currentValue));
return true; return true;
} else if (setting == GlobalSetting.ENCHANTMENT_COST) { } else if (setting == GlobalSetting.ENCHANTMENT_COST) {
Enchantment enchantment = InputParsingHelper.matchEnchantment(selector); Enchantment enchantment = InputParsingHelper.matchEnchantment(selector);
if (enchantment == null) { if (enchantment == null) {
return false; return false;
} }
displaySuccessMessage(sender, TranslatableMessage.getItemCurrentValueMessage(setting.getCommandName(), BlacksmithPlugin.getStringFormatter().displaySuccessMessage(sender,
ItemType.ENCHANTMENT, enchantment.getKey().getKey(), BlacksmithTranslatableMessage.getItemCurrentValueMessage(setting.getCommandName(),
String.valueOf(settings.getEnchantmentCost(enchantment)))); ItemType.ENCHANTMENT, enchantment.getKey().getKey(),
String.valueOf(settings.getEnchantmentCost(enchantment))));
return true; return true;
} else { } else {
return false; return false;
@ -228,38 +226,88 @@ public class BlackSmithConfigCommand implements CommandExecutor {
return true; return true;
} }
double newPrice = Double.parseDouble(args[2]); double newPrice = Double.parseDouble(args[2]);
String itemChanged;
ItemType itemType;
String newValue = String.valueOf(newPrice); String newValue = String.valueOf(newPrice);
if (detectedGlobalSetting == GlobalSetting.BASE_PRICE || if (detectedGlobalSetting == GlobalSetting.BASE_PRICE ||
detectedGlobalSetting == GlobalSetting.PRICE_PER_DURABILITY_POINT) { 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) { if (material == null) {
return false; return false;
} }
itemType = ItemType.MATERIAL;
itemChanged = material.name(); itemChanged = material.name();
if (detectedGlobalSetting == GlobalSetting.BASE_PRICE) { if (detectedGlobalSetting == GlobalSetting.BASE_PRICE) {
settings.setBasePrice(material, newPrice); settings.setBasePrice(material, newPrice);
} else { } else {
settings.setPricePerDurabilityPoint(material, newPrice); 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(), BlacksmithPlugin.getStringFormatter().displaySuccessMessage(sender,
itemType, itemChanged, newValue)); BlacksmithTranslatableMessage.getItemValueChangedMessage(detectedGlobalSetting.getCommandName(),
itemType, itemChanged, String.valueOf(newPrice)));
return true; 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 java.util.List;
import static net.knarcraft.blacksmith.util.TabCompleteValuesHelper.getTabCompletions; 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 * 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.BlacksmithPlugin;
import net.knarcraft.blacksmith.config.NPCSetting; import net.knarcraft.blacksmith.config.NPCSetting;
import net.knarcraft.blacksmith.config.SettingValueType; import net.knarcraft.blacksmith.config.SettingValueType;
import net.knarcraft.blacksmith.formatting.StringFormatter; import net.knarcraft.blacksmith.formatting.BlacksmithTranslatableMessage;
import net.knarcraft.blacksmith.formatting.TranslatableMessage;
import net.knarcraft.blacksmith.formatting.Translator;
import net.knarcraft.blacksmith.trait.BlacksmithTrait; import net.knarcraft.blacksmith.trait.BlacksmithTrait;
import net.knarcraft.blacksmith.util.InputParsingHelper; import net.knarcraft.blacksmith.util.InputParsingHelper;
import net.knarcraft.blacksmith.util.TypeValidationHelper; import net.knarcraft.blacksmith.util.TypeValidationHelper;
@ -19,9 +17,8 @@ import org.jetbrains.annotations.NotNull;
import java.util.Arrays; import java.util.Arrays;
import static net.knarcraft.blacksmith.formatting.StringFormatter.displaySuccessMessage; import static net.knarcraft.blacksmith.formatting.BlacksmithTranslatableMessage.getCurrentValueMessage;
import static net.knarcraft.blacksmith.formatting.TranslatableMessage.getCurrentValueMessage; import static net.knarcraft.blacksmith.formatting.BlacksmithTranslatableMessage.getValueChangedMessage;
import static net.knarcraft.blacksmith.formatting.TranslatableMessage.getValueChangedMessage;
/** /**
* The main command used for blacksmith editing * The main command used for blacksmith editing
@ -33,7 +30,8 @@ public class BlackSmithEditCommand implements CommandExecutor {
@NotNull String[] args) { @NotNull String[] args) {
NPC npc = CitizensAPI.getDefaultNPCSelector().getSelected(sender); NPC npc = CitizensAPI.getDefaultNPCSelector().getSelected(sender);
if (npc == null || !npc.hasTrait(BlacksmithTrait.class)) { if (npc == null || !npc.hasTrait(BlacksmithTrait.class)) {
StringFormatter.displayErrorMessage(sender, TranslatableMessage.NO_NPC_SELECTED); BlacksmithPlugin.getStringFormatter().displayErrorMessage(sender,
BlacksmithTranslatableMessage.NO_NPC_SELECTED);
return true; return true;
} }
@ -86,7 +84,8 @@ public class BlackSmithEditCommand implements CommandExecutor {
//Change the setting //Change the setting
blacksmithTrait.getSettings().changeSetting(npcSetting, newValue); 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 //Save the changes immediately to prevent data loss on server crash
CitizensAPI.getNPCRegistry().saveToStore(); CitizensAPI.getNPCRegistry().saveToStore();
} }
@ -105,14 +104,18 @@ public class BlackSmithEditCommand implements CommandExecutor {
if (InputParsingHelper.isEmpty(rawValue)) { if (InputParsingHelper.isEmpty(rawValue)) {
//Display the default value, if no custom value has been specified //Display the default value, if no custom value has been specified
rawValue = String.valueOf(BlacksmithPlugin.getInstance().getSettings().getRawValue(npcSetting)); rawValue = String.valueOf(BlacksmithPlugin.getInstance().getSettings().getRawValue(npcSetting));
displaySuccessMessage(sender, getCurrentValueMessage(npcSetting.getCommandName(), rawValue)); BlacksmithPlugin.getStringFormatter().displaySuccessMessage(sender,
getCurrentValueMessage(npcSetting.getCommandName(), rawValue));
} else { } else {
//Add a marker if the value has been customized //Add a marker if the value has been customized
String marker = Translator.getTranslatedMessage(TranslatableMessage.SETTING_OVERRIDDEN_MARKER); String marker = BlacksmithPlugin.getTranslator().getTranslatedMessage(
displaySuccessMessage(sender, getCurrentValueMessage(npcSetting.getCommandName(), rawValue) + marker); BlacksmithTranslatableMessage.SETTING_OVERRIDDEN_MARKER);
BlacksmithPlugin.getStringFormatter().displaySuccessMessage(sender,
getCurrentValueMessage(npcSetting.getCommandName(), rawValue) + marker);
} }
if (npcSetting.getPath().startsWith("defaults.messages")) { 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.config.NPCSetting;
import net.knarcraft.blacksmith.util.TabCompleteValuesHelper; 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.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter; import org.bukkit.command.TabCompleter;
@ -33,7 +33,7 @@ public class BlackSmithEditTabCompleter implements TabCompleter {
if (args.length == 1) { if (args.length == 1) {
return TabCompletionHelper.filterMatchingContains(npcSettings, args[0]); return TabCompletionHelper.filterMatchingContains(npcSettings, args[0]);
} else { } else {
if (npcSettings.contains(args[0]) && args.length <= 2) { if (npcSettings.contains(args[0]) && args.length == 2) {
return tabCompleteCommandValues(args[0], args[1]); return tabCompleteCommandValues(args[0], args[1]);
} else { } else {
return new ArrayList<>(); return new ArrayList<>();

View File

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

View File

@ -2,7 +2,7 @@ package net.knarcraft.blacksmith.command;
import net.knarcraft.blacksmith.config.SmithPreset; import net.knarcraft.blacksmith.config.SmithPreset;
import net.knarcraft.blacksmith.config.SmithPresetFilter; 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.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter; import org.bukkit.command.TabCompleter;

View File

@ -1,7 +1,7 @@
package net.knarcraft.blacksmith.command; package net.knarcraft.blacksmith.command;
import net.knarcraft.blacksmith.BlacksmithPlugin; 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.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor; import org.bukkit.command.TabExecutor;
@ -11,8 +11,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static net.knarcraft.blacksmith.formatting.StringFormatter.displaySuccessMessage;
/** /**
* The command for re-loading the plugin * 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, public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] args) { @NotNull String[] args) {
BlacksmithPlugin.getInstance().reload(); BlacksmithPlugin.getInstance().reload();
displaySuccessMessage(sender, TranslatableMessage.PLUGIN_RELOADED); BlacksmithPlugin.getStringFormatter().displaySuccessMessage(sender, BlacksmithTranslatableMessage.PLUGIN_RELOADED);
return true; 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> * <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 * 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 * 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 path;
private final String parent; 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.BlacksmithPlugin;
import net.knarcraft.blacksmith.util.ConfigHelper; import net.knarcraft.blacksmith.util.ConfigHelper;
import net.knarcraft.blacksmith.util.InputParsingHelper; import net.knarcraft.blacksmith.util.InputParsingHelper;
import net.knarcraft.blacksmith.util.ItemHelper;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.Enchantment;
import java.io.File; import java.io.File;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.logging.Level; 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> materialBasePrices = new HashMap<>();
private final Map<Material, Double> materialPricePerDurabilityPoints = new HashMap<>(); private final Map<Material, Double> materialPricePerDurabilityPoints = new HashMap<>();
private final Map<Enchantment, Double> enchantmentCosts = new HashMap<>(); private final Map<Enchantment, Double> enchantmentCosts = new HashMap<>();
private final Map<NPCSetting, Object> defaultNPCSettings = 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 Map<GlobalSetting, Object> globalSettings = new HashMap<>();
private final YamlStorage defaultConfig; private final YamlStorage defaultConfig;
@ -81,8 +85,19 @@ public class GlobalSettings {
* @param newValue <p>The new value for the setting</p> * @param newValue <p>The new value for the setting</p>
*/ */
public void changeValue(NPCSetting npcSetting, Object newValue) { public void changeValue(NPCSetting npcSetting, Object newValue) {
defaultNPCSettings.put(npcSetting, 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(); 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 * Gets the given value as a boolean
* *
@ -302,48 +351,98 @@ public class GlobalSettings {
} }
//Load all base prices //Load all base prices
DataKey basePriceNode = root.getRelative(GlobalSetting.BASE_PRICE.getParent()); loadBasePrices(root);
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);
}
}
//Load all per-durability-point prices //Load all per-durability-point prices
DataKey basePerDurabilityPriceNode = root.getRelative(GlobalSetting.PRICE_PER_DURABILITY_POINT.getParent()); loadPricesPerDurabilityPoint(root);
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);
}
}
//Load all enchantment prices //Load all enchantment prices
DataKey enchantmentCostNode = root.getRelative(GlobalSetting.ENCHANTMENT_COST.getParent()); DataKey enchantmentCostNode = root.getRelative(GlobalSetting.ENCHANTMENT_COST.getParent());
relevantKeys = getRelevantKeys(basePerDurabilityPriceNode); Map<String, String> relevantKeys = getRelevantKeys(enchantmentCostNode);
for (String key : relevantKeys.keySet()) { for (String key : relevantKeys.keySet()) {
String enchantmentName = relevantKeys.get(key); String enchantmentName = relevantKeys.get(key);
Enchantment enchantment = InputParsingHelper.matchEnchantment(enchantmentName); Enchantment enchantment = InputParsingHelper.matchEnchantment(enchantmentName);
if (enchantment != null) { setItemPrice(enchantmentCosts, enchantmentName, enchantment, enchantmentCostNode.getDouble(key));
enchantmentCosts.put(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 { } else {
BlacksmithPlugin.getInstance().getLogger().log(Level.WARNING, Material material = InputParsingHelper.matchMaterial(materialName);
"Unable to find an enchantment matching " + enchantmentName); 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 a material/enchantment matching " + itemName);
}
}
/** /**
* Gets a map between relevant keys and their normalized name * Gets a map between relevant keys and their normalized name
* *
@ -389,6 +488,26 @@ public class GlobalSettings {
defaultNPCSettings.put(setting, root.getRaw(setting.getPath())); 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 * 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 * 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"), 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 | | Messages |
-----------*/ -----------*/

View File

@ -6,12 +6,15 @@ import net.knarcraft.blacksmith.trait.BlacksmithTrait;
import net.knarcraft.blacksmith.util.ConfigHelper; import net.knarcraft.blacksmith.util.ConfigHelper;
import net.knarcraft.blacksmith.util.InputParsingHelper; import net.knarcraft.blacksmith.util.InputParsingHelper;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
/** /**
@ -20,6 +23,7 @@ import java.util.logging.Level;
public class NPCSettings { public class NPCSettings {
private final List<Material> reforgeAbleItems = new ArrayList<>(); private final List<Material> reforgeAbleItems = new ArrayList<>();
private final List<Enchantment> enchantmentBlocklist = new ArrayList<>();
private final Map<NPCSetting, Object> currentValues = new HashMap<>(); private final Map<NPCSetting, Object> currentValues = new HashMap<>();
private final GlobalSettings globalSettings; private final GlobalSettings globalSettings;
@ -43,6 +47,7 @@ public class NPCSettings {
} }
//Updates the list of reforge-able items/materials //Updates the list of reforge-able items/materials
updateReforgeAbleItems(); updateReforgeAbleItems();
updateEnchantmentBlocklist();
} }
/** /**
@ -63,10 +68,19 @@ public class NPCSettings {
* @param newValue <p>The new value of the setting</p> * @param newValue <p>The new value of the setting</p>
*/ */
public void changeSetting(NPCSetting setting, Object newValue) { public void changeSetting(NPCSetting setting, Object newValue) {
currentValues.put(setting, 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) { if (setting == NPCSetting.REFORGE_ABLE_ITEMS) {
updateReforgeAbleItems(); updateReforgeAbleItems();
} }
if (setting == NPCSetting.ENCHANTMENT_BLOCKLIST) {
updateEnchantmentBlocklist();
}
} }
/** /**
@ -186,7 +200,26 @@ public class NPCSettings {
* @return <p>All items reforge-able by this NPC</p> * @return <p>All items reforge-able by this NPC</p>
*/ */
public List<Material> getReforgeAbleItems() { 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; 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 * Gets the given value as an integer
* *
@ -326,26 +368,57 @@ public class NPCSettings {
/** /**
* Replaces placeholders in the given reforge-able value * 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> * @return <p>The value with placeholders replaced</p>
*/ */
private Object replaceReforgeAblePresets(Object value) { private static List<String> replaceReforgeAblePresets(List<String> stringList) {
if (value instanceof String string) { List<String> newStrings = new ArrayList<>();
String[] list = string.split(","); for (String item : stringList) {
List<String> replaced = new ArrayList<>(list.length); if (item == null) {
for (String item : list) { continue;
replaced.add(SmithPreset.replacePreset(item));
} }
return String.join(",", replaced); String replaced = SmithPreset.replacePreset(item);
} else if (value instanceof String[] stringList) { if (!replaced.equals(item)) {
List<String> replaced = new ArrayList<>(stringList.length); newStrings.addAll(List.of(replaced.split(",")));
for (String item : stringList) { } else {
replaced.add(SmithPreset.replacePreset(item)); newStrings.add(item);
} }
return replaced.toArray();
} else {
throw new IllegalArgumentException("Unexpected object type encountered!");
} }
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;
} }
/** /**
@ -353,26 +426,55 @@ public class NPCSettings {
*/ */
private void updateReforgeAbleItems() { private void updateReforgeAbleItems() {
this.reforgeAbleItems.clear(); this.reforgeAbleItems.clear();
String newReforgeAbleItems = (String) currentValues.get(NPCSetting.REFORGE_ABLE_ITEMS); this.reforgeAbleItems.addAll(getReforgeAbleItems(ConfigHelper.asStringList(getValue(
if (newReforgeAbleItems == null) { NPCSetting.REFORGE_ABLE_ITEMS))));
return; }
/**
* 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 //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)) { if (InputParsingHelper.isEmpty(item)) {
continue; continue;
} }
boolean blacklist = false;
if (item.startsWith("-")) {
blacklist = true;
item = item.substring(1);
}
Material material = InputParsingHelper.matchMaterial(item); Material material = InputParsingHelper.matchMaterial(item);
if (material != null && BlacksmithTrait.isRepairable(new ItemStack(material, 1))) { if (material != null && BlacksmithTrait.isRepairable(new ItemStack(material, 1))) {
this.reforgeAbleItems.add(material); if (!blacklist) {
reforgeAbleItems.add(material);
} else {
blacklisted.add(material);
}
} else { } else {
BlacksmithPlugin.getInstance().getLogger().log(Level.WARNING, "Unable to verify " + item + BlacksmithPlugin.getInstance().getLogger().log(Level.WARNING, "Unable to verify " + item +
" as a valid reforge-able 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 * 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; package net.knarcraft.blacksmith.config;
import net.knarcraft.blacksmith.BlacksmithPlugin; import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.util.ItemHelper;
import org.bukkit.Material; import org.bukkit.Material;
import java.util.ArrayList; import java.util.ArrayList;
@ -12,6 +13,11 @@ import java.util.logging.Level;
*/ */
public enum SmithPreset { public enum SmithPreset {
/**
* A blacksmith capable of re-forging everything
*/
BLACKSMITH(new SmithPresetFilter[]{}),
/** /**
* A blacksmith capable of re-forging all weapons (including shields) * 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> * @return <p>The string, possibly with the preset replaced</p>
*/ */
public static String replacePreset(String possiblePreset) { public static String replacePreset(String possiblePreset) {
boolean negated = false;
String upperCasedPreset = possiblePreset.replace('-', '_').toUpperCase(); 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; return possiblePreset;
} }
//Strip the "-" here to prevent stripping for material names
if (negated) {
upperCasedPreset = upperCasedPreset.substring(1);
}
//Parse the input //Parse the input
SmithPresetFilter filter = null; SmithPresetFilter filter = null;
SmithPreset preset; SmithPreset preset;
@ -105,11 +123,16 @@ public enum SmithPreset {
} }
//Return the list of materials included in the preset //Return the list of materials included in the preset
List<String> materialNames;
if (filter != null) { if (filter != null) {
return String.join(",", preset.getMaterialNames(filter)); materialNames = preset.getMaterialNames(filter);
} else { } 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() { public List<Material> getMaterials() {
return switch (this) { return switch (this) {
case BLACKSMITH -> ItemHelper.getAllReforgeAbleMaterials();
case WEAPON_SMITH -> getWeapons(); case WEAPON_SMITH -> getWeapons();
case ARMOR_SMITH -> getArmor(); case ARMOR_SMITH -> getArmor();
case TOOL_SMITH -> getTools(); case TOOL_SMITH -> getTools();
@ -205,13 +229,13 @@ public enum SmithPreset {
* @return <p>The resulting materials</p> * @return <p>The resulting materials</p>
*/ */
private List<Material> getMaterialsEndingWith(String end) { private List<Material> getMaterialsEndingWith(String end) {
List<Material> swords = new ArrayList<>(); List<Material> matchedMaterials = new ArrayList<>();
for (Material material : Material.values()) { for (Material material : ItemHelper.getAllReforgeAbleMaterials()) {
if (!material.name().startsWith("LEGACY") && material.name().endsWith(end)) { 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; 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; 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 * 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 * <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> * 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 * The message displayed when a configuration value has been successfully changed
@ -50,11 +52,6 @@ public enum TranslatableMessage {
*/ */
NO_NPC_SELECTED, 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 * The message displayed if a string list is required, but something else is given
*/ */
@ -105,36 +102,6 @@ public enum TranslatableMessage {
*/ */
PRESET_MATERIALS, 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 * 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> * @return <p>The message to display</p>
*/ */
public static String getRawValueMessage(String rawValue) { public static String getRawValueMessage(String rawValue) {
return StringFormatter.replacePlaceholder(Translator.getTranslatedMessage(TranslatableMessage.RAW_VALUE), return StringFormatter.replacePlaceholder(BlacksmithPlugin.getTranslator().getTranslatedMessage(
"{rawValue}", rawValue); BlacksmithTranslatableMessage.RAW_VALUE), "{rawValue}", rawValue);
} }
/** /**
@ -184,8 +151,9 @@ public enum TranslatableMessage {
* @return <p>The string to display to a user</p> * @return <p>The string to display to a user</p>
*/ */
public static String getValueChangedMessage(String setting, String newValue) { public static String getValueChangedMessage(String setting, String newValue) {
return replacePlaceholders(Translator.getTranslatedMessage(TranslatableMessage.VALUE_CHANGED), return StringFormatter.replacePlaceholders(BlacksmithPlugin.getTranslator().getTranslatedMessage(
new String[]{"{setting}", "{newValue}"}, new String[]{setting, newValue}); 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> * @return <p>The string to display to a user</p>
*/ */
public static String getItemValueChangedMessage(String setting, ItemType itemType, String item, String newValue) { 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}", "{item}", "{newValue}"},
new String[]{setting, itemType.getItemTypeName(), 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> * @return <p>The string to display to a user</p>
*/ */
public static String getCurrentValueMessage(String setting, String currentValue) { public static String getCurrentValueMessage(String setting, String currentValue) {
return replacePlaceholders(Translator.getTranslatedMessage(TranslatableMessage.CURRENT_VALUE), return StringFormatter.replacePlaceholders(BlacksmithPlugin.getTranslator().getTranslatedMessage(
new String[]{"{setting}", "{currentValue}"}, new String[]{setting, currentValue}); 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> * @return <p>The string to display to a user</p>
*/ */
public static String getItemCurrentValueMessage(String setting, ItemType itemType, String item, String currentValue) { 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}", "{item}", "{currentValue}"},
new String[]{setting, itemType.getItemTypeName(), 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; package net.knarcraft.blacksmith.formatting;
import net.knarcraft.blacksmith.BlacksmithPlugin;
/** /**
* An enum representing all item types used in messages * An enum representing all item types used in messages
*/ */
@ -15,8 +17,10 @@ public enum ItemType {
*/ */
public String getItemTypeName() { public String getItemTypeName() {
return switch (this) { return switch (this) {
case MATERIAL -> Translator.getTranslatedMessage(TranslatableMessage.ITEM_TYPE_MATERIAL); case MATERIAL -> BlacksmithPlugin.getTranslator().getTranslatedMessage(
case ENCHANTMENT -> Translator.getTranslatedMessage(TranslatableMessage.ITEM_TYPE_ENCHANTMENT); 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; package net.knarcraft.blacksmith.formatting;
import java.util.ArrayList; import net.knarcraft.blacksmith.BlacksmithPlugin;
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 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 * A utility for formatting a string specifying an amount of time
*/ */
public final class TimeFormatter { public final class TimeFormatter {
private static Map<Double, TranslatableMessage[]> timeUnits;
private static List<Double> sortedUnits;
private TimeFormatter() { private TimeFormatter() {
} }
@ -31,7 +24,7 @@ public final class TimeFormatter {
*/ */
public static String formatTime(boolean exact, int seconds) { public static String formatTime(boolean exact, int seconds) {
if (exact) { if (exact) {
return getDurationString(seconds); return net.knarcraft.knarlib.formatting.TimeFormatter.getDurationString(BlacksmithPlugin.getTranslator(), seconds);
} else { } else {
return formatUnclearTime(seconds); return formatUnclearTime(seconds);
} }
@ -60,7 +53,7 @@ public final class TimeFormatter {
* @return <p>Text describing the time interval</p> * @return <p>Text describing the time interval</p>
*/ */
private static String getMessageFromInterval(TimeInterval interval) { 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 //Choose a random entry if a comma-separated list is provided
if (text != null && text.contains(",")) { 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; package net.knarcraft.blacksmith.listener;
import net.knarcraft.blacksmith.formatting.StringFormatter; import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.formatting.TranslatableMessage; import net.knarcraft.blacksmith.formatting.BlacksmithTranslatableMessage;
import net.knarcraft.blacksmith.trait.BlacksmithTrait; import net.knarcraft.blacksmith.trait.BlacksmithTrait;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
@ -25,7 +25,8 @@ public class NPCClickListener implements Listener {
//Permission check //Permission check
if (!player.hasPermission("blacksmith.use")) { if (!player.hasPermission("blacksmith.use")) {
StringFormatter.displayErrorMessage(player, TranslatableMessage.PERMISSION_DENIED); BlacksmithPlugin.getStringFormatter().displayErrorMessage(player,
BlacksmithTranslatableMessage.PERMISSION_DENIED);
return; return;
} }

View File

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

View File

@ -8,6 +8,7 @@ import net.knarcraft.blacksmith.config.NPCSettings;
import net.knarcraft.blacksmith.formatting.TimeFormatter; import net.knarcraft.blacksmith.formatting.TimeFormatter;
import net.knarcraft.blacksmith.manager.EconomyManager; import net.knarcraft.blacksmith.manager.EconomyManager;
import net.knarcraft.blacksmith.util.ItemHelper; import net.knarcraft.blacksmith.util.ItemHelper;
import net.knarcraft.knarlib.formatting.StringFormatter;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.enchantments.EnchantmentTarget; import org.bukkit.enchantments.EnchantmentTarget;
@ -23,8 +24,7 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.UUID; import java.util.UUID;
import static net.knarcraft.blacksmith.formatting.StringFormatter.replacePlaceholder; import static net.knarcraft.blacksmith.formatting.BlacksmithStringFormatter.sendNPCMessage;
import static net.knarcraft.blacksmith.formatting.StringFormatter.sendNPCMessage;
/** /**
* The class representing the Blacksmith NPC trait * The class representing the Blacksmith NPC trait
@ -114,7 +114,7 @@ public class BlacksmithTrait extends Trait {
coolDowns.remove(playerId); coolDowns.remove(playerId);
} }
//Deny if permission is missing //Deny if permission is missing
if (!player.hasPermission("blacksmith.reforge")) { if (!player.hasPermission("blacksmith.use")) {
return false; return false;
} }
@ -124,7 +124,7 @@ public class BlacksmithTrait extends Trait {
if (!calendar.after(coolDowns.get(playerId))) { if (!calendar.after(coolDowns.get(playerId))) {
int secondDifference = (int) (coolDowns.get(playerId).getTimeInMillis() - calendar.getTimeInMillis()) / 1000; int secondDifference = (int) (coolDowns.get(playerId).getTimeInMillis() - calendar.getTimeInMillis()) / 1000;
boolean exactTime = BlacksmithPlugin.getInstance().getSettings().getShowExactTime(); 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))); "{time}", TimeFormatter.formatTime(exactTime, secondDifference)));
return false; return false;
} }
@ -157,8 +157,8 @@ public class BlacksmithTrait extends Trait {
if (session.isRunning()) { if (session.isRunning()) {
int timeRemaining = (int) ((session.getFinishTime() - System.currentTimeMillis()) / 1000); int timeRemaining = (int) ((session.getFinishTime() - System.currentTimeMillis()) / 1000);
boolean showExactTime = BlacksmithPlugin.getInstance().getSettings().getShowExactTime(); boolean showExactTime = BlacksmithPlugin.getInstance().getSettings().getShowExactTime();
sendNPCMessage(this.npc, player, replacePlaceholder(config.getBusyReforgingMessage(), "{time}", sendNPCMessage(this.npc, player, StringFormatter.replacePlaceholder(config.getBusyReforgingMessage(),
TimeFormatter.formatTime(showExactTime, timeRemaining))); "{time}", TimeFormatter.formatTime(showExactTime, timeRemaining)));
return; return;
} }
if (session.endSession()) { if (session.endSession()) {
@ -179,14 +179,15 @@ public class BlacksmithTrait extends Trait {
ItemStack hand = player.getInventory().getItemInMainHand(); ItemStack hand = player.getInventory().getItemInMainHand();
//Refuse if not repairable, or if reforge-able items is set, but doesn't include the held item //Refuse if not repairable, or if reforge-able items is set, but doesn't include the held item
List<Material> reforgeAbleItems = config.getReforgeAbleItems(); List<Material> reforgeAbleItems = config.getReforgeAbleItems();
if (!isRepairable(hand) || (!reforgeAbleItems.isEmpty() && !reforgeAbleItems.contains(hand.getType()))) { if ((!this.config.getRepairAnvils() || !ItemHelper.isAnvil(hand.getType(), false)) &&
String invalidMessage = replacePlaceholder(config.getInvalidItemMessage(), (!isRepairable(hand) || (!reforgeAbleItems.isEmpty() && !reforgeAbleItems.contains(hand.getType())))) {
String invalidMessage = StringFormatter.replacePlaceholder(config.getInvalidItemMessage(),
"{title}", config.getBlacksmithTitle()); "{title}", config.getBlacksmithTitle());
sendNPCMessage(this.npc, player, invalidMessage); sendNPCMessage(this.npc, player, invalidMessage);
return; return;
} }
if (ItemHelper.getDamage(hand) == 0) { if (ItemHelper.getDamage(hand) == 0 && !ItemHelper.isAnvil(hand.getType(), true)) {
sendNPCMessage(this.npc, player, config.getNotDamagedMessage()); sendNPCMessage(this.npc, player, config.getNotDamagedMessage());
return; return;
} }

View File

@ -6,6 +6,7 @@ import net.knarcraft.blacksmith.config.NPCSettings;
import net.knarcraft.blacksmith.manager.EconomyManager; import net.knarcraft.blacksmith.manager.EconomyManager;
import net.knarcraft.blacksmith.util.InputParsingHelper; import net.knarcraft.blacksmith.util.InputParsingHelper;
import net.knarcraft.blacksmith.util.ItemHelper; import net.knarcraft.blacksmith.util.ItemHelper;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -21,7 +22,7 @@ import java.util.Objects;
import java.util.Random; import java.util.Random;
import java.util.logging.Level; 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 * 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 // Remove any damage done to the item
updateDamage(itemToReforge, 0); updateDamage(itemToReforge, 0);
//Replace damaged anvils with a normal anvil
if (ItemHelper.isAnvil(itemToReforge.getType(), true)) {
itemToReforge.setType(Material.ANVIL);
}
// Add random enchantments // Add random enchantments
int roll = random.nextInt(100); int roll = random.nextInt(100);
if (!(roll < config.getExtraEnchantmentChance() && if (!(roll < config.getExtraEnchantmentChance() &&
@ -145,6 +151,13 @@ public class ReforgeSession implements Runnable {
usableEnchantments.add(enchantment); 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 //Choose a random enchantment
Enchantment randomEnchantment = usableEnchantments.get(random.nextInt(usableEnchantments.size())); Enchantment randomEnchantment = usableEnchantments.get(random.nextInt(usableEnchantments.size()));

View File

@ -1,5 +1,8 @@
package net.knarcraft.blacksmith.util; 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 * 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 * 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("-", "_"))); 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; package net.knarcraft.blacksmith.util;
import org.bukkit.Material;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.Damageable; import org.bukkit.inventory.meta.Damageable;
import java.util.ArrayList;
import java.util.List;
public final class ItemHelper { public final class ItemHelper {
private 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; package net.knarcraft.blacksmith.util;
import net.knarcraft.blacksmith.config.SettingValueType; 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.Material;
import org.bukkit.enchantments.Enchantment; 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.ArrayList;
import java.util.List; import java.util.List;
@ -32,25 +31,47 @@ public final class TabCompleteValuesHelper {
case POSITIVE_DOUBLE -> getPositiveDoubles(); case POSITIVE_DOUBLE -> getPositiveDoubles();
case STRING -> getStrings(); case STRING -> getStrings();
case PERCENTAGE -> getPercentages(); case PERCENTAGE -> getPercentages();
case STRING_LIST -> getReforgeAbleMaterials(); case REFORGE_ABLE_ITEMS -> getReforgeAbleMaterials();
case MATERIAL -> getAllReforgeAbleMaterials(); case MATERIAL -> getAllReforgeAbleMaterialNames();
case ENCHANTMENT -> getAllEnchantments(); 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<>(); List<String> reforgeAbleMaterials = new ArrayList<>();
for (Material material : Material.values()) { for (Material material : ItemHelper.getAllReforgeAbleMaterials()) {
ItemStack item = new ItemStack(material); reforgeAbleMaterials.add(material.name());
if (item.getItemMeta() instanceof Damageable && EnchantmentTarget.BREAKABLE.includes(item)) {
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; return reforgeAbleMaterials;
} }
@ -74,12 +95,17 @@ public final class TabCompleteValuesHelper {
*/ */
private static List<String> getReforgeAbleMaterials() { private static List<String> getReforgeAbleMaterials() {
List<String> stringLists = new ArrayList<>(); List<String> stringLists = new ArrayList<>();
stringLists.add("preset:sword-smith"); for (SmithPreset preset : SmithPreset.values()) {
stringLists.add("preset:weapon-smith"); stringLists.add("preset:" + preset.name());
stringLists.add("preset:armor-smith"); for (SmithPresetFilter filter : preset.getSupportedFilters()) {
stringLists.add("preset:tool-smith"); stringLists.add("preset:" + preset.name() + ":" + filter.name());
stringLists.add("preset:ranged-smith"); }
stringLists.add("bow,crossbow,elytra"); }
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; 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; package net.knarcraft.blacksmith.util;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.config.SettingValueType; import net.knarcraft.blacksmith.config.SettingValueType;
import net.knarcraft.blacksmith.formatting.TranslatableMessage; import net.knarcraft.blacksmith.formatting.BlacksmithTranslatableMessage;
import org.bukkit.command.CommandSender; 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 * A helper class for validating a value's type
@ -31,7 +32,7 @@ public final class TypeValidationHelper {
case POSITIVE_INTEGER -> isPositiveInteger(value, sender); case POSITIVE_INTEGER -> isPositiveInteger(value, sender);
case PERCENTAGE -> isPercentage(value, sender); case PERCENTAGE -> isPercentage(value, sender);
case BOOLEAN -> true; case BOOLEAN -> true;
case STRING_LIST -> isStringList(value, sender); case STRING_LIST, REFORGE_ABLE_ITEMS -> isStringList(value, sender);
case MATERIAL, ENCHANTMENT -> false; case MATERIAL, ENCHANTMENT -> false;
}; };
} catch (ClassCastException exception) { } catch (ClassCastException exception) {
@ -48,9 +49,10 @@ public final class TypeValidationHelper {
* @return <p>True if the value is a string list</p> * @return <p>True if the value is a string list</p>
*/ */
private static boolean isStringList(Object value, CommandSender sender) { 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) { if (!isStringList && sender != null) {
displayErrorMessage(sender, TranslatableMessage.INPUT_STRING_LIST_REQUIRED); BlacksmithPlugin.getStringFormatter().displayErrorMessage(sender,
BlacksmithTranslatableMessage.INPUT_STRING_LIST_REQUIRED);
} }
return isStringList; return isStringList;
} }
@ -65,10 +67,11 @@ public final class TypeValidationHelper {
private static boolean isPercentage(Object value, CommandSender sender) { private static boolean isPercentage(Object value, CommandSender sender) {
try { try {
int intValue = ConfigHelper.asInt(value); int intValue = ConfigHelper.asInt(value);
return intValue > 0 && intValue <= 100; return intValue >= 0 && intValue <= 100;
} catch (NumberFormatException | NullPointerException exception) { } catch (NumberFormatException | NullPointerException exception) {
if (sender != null) { if (sender != null) {
displayErrorMessage(sender, TranslatableMessage.INPUT_PERCENTAGE_REQUIRED); BlacksmithPlugin.getStringFormatter().displayErrorMessage(sender,
BlacksmithTranslatableMessage.INPUT_PERCENTAGE_REQUIRED);
} }
return false; return false;
} }
@ -82,9 +85,10 @@ public final class TypeValidationHelper {
* @return <p>True if the value is a non-empty string</p> * @return <p>True if the value is a non-empty string</p>
*/ */
private static boolean isNonEmptyString(Object value, CommandSender sender) { private static boolean isNonEmptyString(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) { if (!isString && sender != null) {
displayErrorMessage(sender, TranslatableMessage.INPUT_STRING_REQUIRED); BlacksmithPlugin.getStringFormatter().displayErrorMessage(sender,
BlacksmithTranslatableMessage.INPUT_STRING_REQUIRED);
} }
return isString; return isString;
} }
@ -98,10 +102,11 @@ public final class TypeValidationHelper {
*/ */
private static boolean isPositiveDouble(Object value, CommandSender sender) { private static boolean isPositiveDouble(Object value, CommandSender sender) {
try { try {
return ConfigHelper.asDouble(value) > 0.0; return ConfigHelper.asDouble(value) >= 0.0;
} catch (NumberFormatException | NullPointerException exception) { } catch (NumberFormatException | NullPointerException exception) {
if (sender != null) { if (sender != null) {
displayErrorMessage(sender, TranslatableMessage.INPUT_POSITIVE_DOUBLE_REQUIRED); BlacksmithPlugin.getStringFormatter().displayErrorMessage(sender,
BlacksmithTranslatableMessage.INPUT_POSITIVE_DOUBLE_REQUIRED);
} }
return false; return false;
} }
@ -116,10 +121,11 @@ public final class TypeValidationHelper {
*/ */
private static boolean isPositiveInteger(Object value, CommandSender sender) { private static boolean isPositiveInteger(Object value, CommandSender sender) {
try { try {
return ConfigHelper.asInt(value) > 0; return ConfigHelper.asInt(value) >= 0;
} catch (NumberFormatException | NullPointerException exception) { } catch (NumberFormatException | NullPointerException exception) {
if (sender != null) { if (sender != null) {
displayErrorMessage(sender, TranslatableMessage.INPUT_POSITIVE_INTEGER_REQUIRED); BlacksmithPlugin.getStringFormatter().displayErrorMessage(sender,
BlacksmithTranslatableMessage.INPUT_POSITIVE_INTEGER_REQUIRED);
} }
return false; return false;
} }

View File

@ -27,6 +27,12 @@ global:
# Exact time displays the exact number of seconds and minutes remaining as part of the reforging cool-down and # Exact time displays the exact number of seconds and minutes remaining as part of the reforging cool-down and
# reforging delay messages, instead of just vaguely hinting at the remaining time. # reforging delay messages, instead of just vaguely hinting at the remaining time.
showExactTime: false 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 # 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 # Citizens NPC file, or use the /blacksmith command
@ -39,6 +45,9 @@ defaults:
# up yet. # up yet.
reforgeAbleItems: [ ] 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) # 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% failReforgeChance: 10 # Default = 10%
@ -47,6 +56,9 @@ defaults:
# The maximum number of enchantments the blacksmith will try to add # The maximum number of enchantments the blacksmith will try to add
maxEnchantments: 3 maxEnchantments: 3
# Whether the blacksmith will reforge anvils as a special case
reforgeAnvils: false
# All settable delays # All settable delays
delaysInSeconds: delaysInSeconds:

View File

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

View File

@ -7,7 +7,6 @@ en:
ITEM_TYPE_MATERIAL: "material" ITEM_TYPE_MATERIAL: "material"
RAW_VALUE: "Raw value: {rawValue}" RAW_VALUE: "Raw value: {rawValue}"
NO_NPC_SELECTED: "You must select an NPC before running this command" 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_STRING_LIST_REQUIRED: "A string list is required!"
INPUT_PERCENTAGE_REQUIRED: "You specified a value which isn't between 0 and 100!" INPUT_PERCENTAGE_REQUIRED: "You specified a value which isn't between 0 and 100!"
INPUT_STRING_REQUIRED: "A non-empty string is required!" INPUT_STRING_REQUIRED: "A non-empty string is required!"
@ -24,6 +23,18 @@ en:
UNIT_SECONDS: "seconds" UNIT_SECONDS: "seconds"
UNIT_MINUTE: "minute" UNIT_MINUTE: "minute"
UNIT_MINUTES: "minutes" 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_10_SECONDS: "in just a moment"
INTERVAL_LESS_THAN_30_SECONDS: "in a little while" INTERVAL_LESS_THAN_30_SECONDS: "in a little while"
INTERVAL_LESS_THAN_1_MINUTE: "in a while" INTERVAL_LESS_THAN_1_MINUTE: "in a while"