Implements netherite salvaging #24
All checks were successful
EpicKnarvik97/Blacksmith/pipeline/head This commit looks good

This commit is contained in:
Kristian Knarvik 2024-05-06 21:26:21 +02:00
parent e6047f3866
commit 1d7e8a0732
15 changed files with 430 additions and 209 deletions

View File

@ -119,6 +119,10 @@ 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:
- BLACKSMITH (WEAPON_SMITH + ARMOR_SMITH + TOOL_SMITH) - BLACKSMITH (WEAPON_SMITH + ARMOR_SMITH + TOOL_SMITH)
- GOLD
- IRON
- DIAMOND
- NETHERITE
- WEAPON_SMITH: (RANGED + SWORD + SHIELD) - WEAPON_SMITH: (RANGED + SWORD + SHIELD)
- BOW (bows and crossbows) - BOW (bows and crossbows)
- SWORD (swords) - SWORD (swords)
@ -212,12 +216,14 @@ All currently supported presets, and available filters for each preset:
### Scrapper global-only options ### Scrapper global-only options
| Key | Value type | Default | Description | | Key | Value type | Default | Description |
|----------------|------------------------------------------------------------|---------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |-----------------------|------------------------------------------------------------|---------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| basePrice | positive decimal number | 0 | The cost of using a scrapper. | | salvagePrice | positive decimal number | 0 | The cost of using a scrapper to salvage an item. |
| showExactTime | true/false | false | If true, scrappers will display exact time remaining in minutes and seconds, instead of vague expressions. | | armorTrimSalvagePrice | positive decimal number | 5 | The cost of using a scrapper to remove armor trim from an item. |
| giveExperience | true/false | true | If true, each enchantment level on the salvaged item will give one EXP level as salvage. | | netheriteSalvagePrice | positive decimal number | 15 | The cost of using a scrapper to remove netherite from an item. |
| trashSalvage | TARGET_MATERIAL:IGNORED_MATERIAL\[,\*_TARGET2:\*_IGNORED2] | `"*_SHOVEL;*_PICKAXE;*_AXE;*_HOE;*_SWORD;SHIELD;*_BOW:STICK"` | The items that should be deferred when calculating partial salvage. Because receiving just the sticks when salvaging a diamond pickaxe is kind of sad, this allows specifying for example: `*_SHOVEL;*_PICKAXE;*_AXE;*_HOE;*_SWORD;SHIELD;*_BOW:STICK` (the default) for deferring sticks in salvage for shovels, pickaxes, axes, hoes and swords. A `:` character splits selected items and the trash salvage. Different item specifications are split by a `;` character. Use `,` to split separate trash salvages when using commands, like: `SHIELD:STICK,BOW_STRING`. | | showExactTime | true/false | false | If true, scrappers will display exact time remaining in minutes and seconds, instead of vague expressions. |
| giveExperience | true/false | true | If true, each enchantment level on the salvaged item will give one EXP level as salvage. |
| trashSalvage | TARGET_MATERIAL:IGNORED_MATERIAL\[,\*_TARGET2:\*_IGNORED2] | `"*_SHOVEL;*_PICKAXE;*_AXE;*_HOE;*_SWORD;SHIELD;*_BOW:STICK"` | The items that should be deferred when calculating partial salvage. Because receiving just the sticks when salvaging a diamond pickaxe is kind of sad, this allows specifying for example: `*_SHOVEL;*_PICKAXE;*_AXE;*_HOE;*_SWORD;SHIELD;*_BOW:STICK` (the default) for deferring sticks in salvage for shovels, pickaxes, axes, hoes and swords. A `:` character splits selected items and the trash salvage. Different item specifications are split by a `;` character. Use `,` to split separate trash salvages when using commands, like: `SHIELD:STICK,BOW_STRING`. |
### Scrapper per-npc (with default values set in config.yml) ### Scrapper per-npc (with default values set in config.yml)
@ -234,28 +240,32 @@ All currently supported presets, and available filters for each preset:
| scrapperTitle | text string | scrapper | The title displayed as part of the message explaining that a scrapper doesn't recognize a player's held item. | | scrapperTitle | text string | scrapper | The title displayed as part of the message explaining that a scrapper doesn't recognize a player's held item. |
| extendedSalvageEnabled | true/false | false | Whether to enable the extended salvage behavior for this scrapper. As long as it is allowed by salvageAbleItems and it can be crafted in a crafting table, it can be salvaged. This includes things like four planks salvaged into wood. | | extendedSalvageEnabled | true/false | false | Whether to enable the extended salvage behavior for this scrapper. As long as it is allowed by salvageAbleItems and it can be crafted in a crafting table, it can be salvaged. This includes things like four planks salvaged into wood. |
| salvageEnchanted | true/false | false | Whether the scrapper is able to salvage enchanted items. This is disabled by default as it's very easy to accidentally salvage heavily enchanted items if one is not careful. | | salvageEnchanted | true/false | false | Whether the scrapper is able to salvage enchanted items. This is disabled by default as it's very easy to accidentally salvage heavily enchanted items if one is not careful. |
| salvageArmorTrims | true/false | true | Whether the scrapper is able to salvage armor trims. | | salvageArmorTrims | true/false | true | Whether the scrapper is able to salvage armor trims from items. |
| salvageNetherite | true/false | true | Whether the scrapper is able to salvage netherite items into diamond items. |
#### Messages #### Messages
| Message Key | Default | Explanation | | Message Key | Default | Explanation |
|---------------------------------|-----------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------| |---------------------------------|-----------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------|
| busyPlayerMessage | &cI'm busy at the moment. Come back later! | The message displayed when the scrapper is serving another player. | | busyPlayerMessage | &cI'm busy at the moment. Come back later! | The message displayed when the scrapper is serving another player. |
| busySalvageMessage | &cI'm working on it. Be patient! I'll finish {time}! | The message displayed when the scrapper is busy salvaging an item. | | busySalvageMessage | &cI'm working on it. Be patient! I'll finish {time}! | The message displayed when the scrapper is busy salvaging an item. |
| coolDownUnexpiredMessage | &cYou've already had your chance! Give me a break! I'll be ready {time}! | The message displayed when the player has to wait for the cool-down to expire before using the scrapper again. | | coolDownUnexpiredMessage | &cYou've already had your chance! Give me a break! I'll be ready {time}! | The message displayed when the player has to wait for the cool-down to expire before using the scrapper again. |
| invalidItemMessage | &cI'm sorry, but I'm a/an {title}, I don't know how to salvage that! | The message displayed when a scrapper is presented an item which it cannot salvage. | | invalidItemMessage | &cI'm sorry, but I'm a/an {title}, I don't know how to salvage that! | The message displayed when a scrapper is presented an item which it cannot salvage. |
| tooDamagedForSalvageMessage | &cThat item is too damaged to be salvaged into anything useful | The message displayed when a scrapper is presented with an item too damaged to produce salvage. | | tooDamagedForSalvageMessage | &cThat item is too damaged to be salvaged into anything useful | The message displayed when a scrapper is presented with an item too damaged to produce salvage. |
| successSalvagedMessage | There you go! | The message displayed when a scrapper successfully repairs an item. | | successSalvagedMessage | There you go! | The message displayed when a scrapper successfully repairs an item. |
| failSalvageMessage | &cWhoops! The item broke! Maybe next time? | The message displayed when a scrapper fails to salvage an item. | | failSalvageMessage | &cWhoops! The item broke! Maybe next time? | The message displayed when a scrapper fails to salvage an item. |
| itemChangedMessage | &cThat's not the item you wanted to salvage before! | The message displayed when a player changes their item after being shown the salvage cost. | | itemChangedMessage | &cThat's not the item you wanted to salvage before! | The message displayed when a player changes their item after being shown the salvage cost. |
| startSalvageMessage | &eOk, let's see what I can do... | The message displayed when a scrapper starts salvaging an item. | | startSalvageMessage | &eOk, let's see what I can do... | The message displayed when a scrapper starts salvaging an item. |
| insufficientFundsMessage | &cYou don't have enough money to salvage an item! | The message displayed when a player is unable to pay for scrapping an item. | | insufficientFundsMessage | &cYou don't have enough money to salvage an item! | The message displayed when a player is unable to pay for salvaging an item. |
| costMessage | &eIt will cost &a{cost}&e to salvage that item! {yield} &eClick again to salvage! | The message displayed when telling a player about the cost of scrapping an item. | | costMessage | &eIt will cost &a{cost}&e to salvage that &a{item}&e! {yield} &eClick again to salvage! | The message displayed when telling a player about the cost of salvaging an item. |
| fullSalvageMessage | &aI should be able to extract all components from that pristine item.&r | The message displayed as part of costMessage's yield placeholder if all components will be returned. | | costMessageArmorTrim | &eIt will cost &a{cost}&e to salvage that &a{item}&e's armor trim! | The message displayed when telling a player about the cost of salvaging an item's armor trim. |
| partialSalvageMessage | &cI cannot extract all components from that damaged item.&r | The message displayed as part of costMessage's yield placeholder if only some components will be returned. | | costMessageNetherite | &eIt will cost &a{cost}&e to salvage that &a{item}&e into diamond! | The message displayed when telling a player about the cost of salvaging a netherite item into a diamond item. |
| cannotSalvageEnchantedMessage | &cI'm sorry, but I'm unable to salvage enchanted items! | The message displayed when telling a player that the scrapper cannot salvage enchanted items. | | fullSalvageMessage | &aI should be able to extract all components from that pristine item.&r | The message displayed as part of costMessage's yield placeholder if all components will be returned. |
| cannotSalvageArmorTrimMessage | &cI'm sorry, but I'm unable to salvage armor trims! | The message displayed when telling a player that the scrapper cannot salvage items with armor trim. | | partialSalvageMessage | &cI cannot extract all components from that damaged item.&r | The message displayed as part of costMessage's yield placeholder if only some components will be returned. |
| armorTrimSalvageNotFoundMessage | "&cI'm sorry, but I don't know how to salvage that armor trim!" | The message displayed when telling a player that the scrapper cannot find the correct items to return as armor trim salvage. | | cannotSalvageEnchantedMessage | &cI'm sorry, but I'm unable to salvage enchanted items! | The message displayed when telling a player that the scrapper cannot salvage enchanted items. |
| cannotSalvageArmorTrimMessage | &cI'm sorry, but I'm unable to salvage armor trims! | The message displayed when telling a player that the scrapper cannot salvage items with armor trim. |
| armorTrimSalvageNotFoundMessage | &cI'm sorry, but I don't know how to salvage that armor trim! | The message displayed when telling a player that the scrapper cannot find the correct items to return as armor trim salvage. |
| cannotSalvageNetheriteMessage | &cI'm sorry, but I'm unable to salvage netherite items! | The message displayed when telling a player that the scrapper cannot salvage netherite items. |
## Language customization ## Language customization

View File

@ -0,0 +1,23 @@
package net.knarcraft.blacksmith.config;
/**
* A representation of the different ways an item can be salvaged
*/
public enum SalvageMethod {
/**
* Salvaging the item normally by returning the item's crafting recipe
*/
SALVAGE,
/**
* Removing the armor trim from an item
*/
ARMOR_TRIM,
/**
* Un-doing netherite application for an item
*/
NETHERITE,
}

View File

@ -22,7 +22,8 @@ public enum SmithPreset {
/** /**
* A blacksmith capable of re-forging everything * A blacksmith capable of re-forging everything
*/ */
BLACKSMITH(new SmithPresetFilter[]{}), BLACKSMITH(new SmithPresetFilter[]{SmithPresetFilter.GOLD, SmithPresetFilter.IRON, SmithPresetFilter.DIAMOND,
SmithPresetFilter.NETHERITE}),
/** /**
* A blacksmith capable of re-forging all weapons (including shields) * A blacksmith capable of re-forging all weapons (including shields)

View File

@ -34,7 +34,7 @@ public class GlobalScrapperSettings implements Settings<ScrapperSetting> {
* *
* @param instance <p>A reference to the blacksmith plugin</p> * @param instance <p>A reference to the blacksmith plugin</p>
*/ */
public GlobalScrapperSettings(BlacksmithPlugin instance) { public GlobalScrapperSettings(@NotNull BlacksmithPlugin instance) {
this.instance = instance; this.instance = instance;
} }
@ -78,7 +78,8 @@ public class GlobalScrapperSettings implements Settings<ScrapperSetting> {
* @param scrapperSetting <p>The setting to get</p> * @param scrapperSetting <p>The setting to get</p>
* @return <p>The current raw setting value</p> * @return <p>The current raw setting value</p>
*/ */
public @NotNull Object getRawValue(@NotNull ScrapperSetting scrapperSetting) { @NotNull
public Object getRawValue(@NotNull ScrapperSetting scrapperSetting) {
return this.settings.get(scrapperSetting); return this.settings.get(scrapperSetting);
} }
@ -87,6 +88,7 @@ public class GlobalScrapperSettings implements Settings<ScrapperSetting> {
* *
* @return <p>The current value of the default NPC settings</p> * @return <p>The current value of the default NPC settings</p>
*/ */
@NotNull
public Map<ScrapperSetting, Object> getDefaultNPCSettings() { public Map<ScrapperSetting, Object> getDefaultNPCSettings() {
return new HashMap<>(this.settings); return new HashMap<>(this.settings);
} }
@ -108,10 +110,22 @@ public class GlobalScrapperSettings implements Settings<ScrapperSetting> {
* @param setting <p>The setting to get the value of</p> * @param setting <p>The setting to get the value of</p>
* @return <p>The value of the given setting as a boolean</p> * @return <p>The value of the given setting as a boolean</p>
*/ */
public boolean asBoolean(ScrapperSetting setting) { public boolean asBoolean(@NotNull ScrapperSetting setting) {
return ConfigHelper.asBoolean(getValue(setting)); return ConfigHelper.asBoolean(getValue(setting));
} }
/**
* Gets the given value as a double
*
* <p>This will throw an exception if used for a non-double setting</p>
*
* @param setting <p>The setting to get the value of</p>
* @return <p>The value of the given setting as a double</p>
*/
public double asDouble(@NotNull ScrapperSetting setting) {
return ConfigHelper.asDouble(getValue(setting));
}
/** /**
* Gets the value of a setting, using the default if not set * Gets the value of a setting, using the default if not set
* *
@ -173,12 +187,30 @@ public class GlobalScrapperSettings implements Settings<ScrapperSetting> {
} }
/** /**
* Gets the cost of using a scrapper * Gets the cost of using a scrapper to salvage an item
* *
* @return <p>The cost of using a scrapper</p> * @return <p>The cost of using a scrapper to salvage an item</p>
*/ */
public double getCost() { public double getSalvageCost() {
return ConfigHelper.asDouble(getValue(ScrapperSetting.USE_COST)); return asDouble(ScrapperSetting.SALVAGE_COST);
}
/**
* Gets the cost of using a scrapper to remove armor trim
*
* @return <p>The cost of using a scrapper to remove armor trim</p>
*/
public double getArmorTrimSalvageCost() {
return asDouble(ScrapperSetting.ARMOR_TRIM_SALVAGE_COST);
}
/**
* Gets the cost of using a scrapper to remove netherite from an item
*
* @return <p>The cost of using a scrapper to remove netherite from an item</p>
*/
public double getNetheriteSalvageCost() {
return asDouble(ScrapperSetting.NETHERITE_SALVAGE_COST);
} }
/** /**

View File

@ -151,6 +151,16 @@ public class ScrapperNPCSettings implements TraitSettings<ScrapperSetting> {
return asString(ScrapperSetting.COST_MESSAGE_ARMOR_TRIM); return asString(ScrapperSetting.COST_MESSAGE_ARMOR_TRIM);
} }
/**
* Gets the message to use for displaying netherite salvage cost
*
* @return <p>The message to use for displaying netherite salvage cost</p>
*/
@NotNull
public String getNetheriteCostMessage() {
return asString(ScrapperSetting.COST_MESSAGE_NETHERITE);
}
@Override @Override
@NotNull @NotNull
public String getCoolDownUnexpiredMessage() { public String getCoolDownUnexpiredMessage() {
@ -316,6 +326,15 @@ public class ScrapperNPCSettings implements TraitSettings<ScrapperSetting> {
return asBoolean(ScrapperSetting.SALVAGE_ARMOR_TRIMS); return asBoolean(ScrapperSetting.SALVAGE_ARMOR_TRIMS);
} }
/**
* Whether salvage of netherite items is enabled
*
* @return <p>True if this scrapper can salvage netherite items</p>
*/
public boolean salvageNetherite() {
return asBoolean(ScrapperSetting.SALVAGE_NETHERITE);
}
/** /**
* Gets the title of this scrapper NPC * Gets the title of this scrapper NPC
* *
@ -415,4 +434,14 @@ public class ScrapperNPCSettings implements TraitSettings<ScrapperSetting> {
return asString(ScrapperSetting.ARMOR_TRIM_SALVAGE_NOT_FOUND_MESSAGE); return asString(ScrapperSetting.ARMOR_TRIM_SALVAGE_NOT_FOUND_MESSAGE);
} }
/**
* Gets the message to display when explaining that this scrapper is unable to salvage netherite items
*
* @return <p>The message to display when explaining that this scrapper is unable to salvage netherite items</p>
*/
@NotNull
public String getCannotSalvageNetheriteMessage() {
return asString(ScrapperSetting.CANNOT_SALVAGE_NETHERITE_MESSAGE);
}
} }

View File

@ -19,7 +19,7 @@ public enum ScrapperSetting implements Setting {
* <p>If set to false, the item will be directly put in the player's inventory instead</p> * <p>If set to false, the item will be directly put in the player's inventory instead</p>
*/ */
DROP_ITEM("dropItem", SettingValueType.BOOLEAN, true, "Whether the " + DROP_ITEM("dropItem", SettingValueType.BOOLEAN, true, "Whether the " +
"item will drop materials resulting from scrapping on the ground, instead of putting them into the user's" + "item will drop materials resulting from salvaging on the ground, instead of putting them into the user's" +
" inventory", true, false), " inventory", true, false),
/** /**
@ -85,6 +85,12 @@ public enum ScrapperSetting implements Setting {
*/ */
SALVAGE_ARMOR_TRIMS("salvageArmorTrims", SettingValueType.BOOLEAN, true, SALVAGE_ARMOR_TRIMS("salvageArmorTrims", SettingValueType.BOOLEAN, true,
"Whether to enable salvaging of armor trims.", true, false), "Whether to enable salvaging of armor trims.", true, false),
/**
* The setting for whether the NPC should allow salvaging netherite items to diamond items
*/
SALVAGE_NETHERITE("salvageNetherite", SettingValueType.BOOLEAN, true,
"Whether to enable salvaging of netherite items", true, false),
/*----------- /*-----------
| Messages | | Messages |
@ -164,16 +170,23 @@ public enum ScrapperSetting implements Setting {
* The message displayed when displaying the cost of salvaging the held item to the player * The message displayed when displaying the cost of salvaging the held item to the player
*/ */
COST_MESSAGE("costMessage", SettingValueType.STRING, COST_MESSAGE("costMessage", SettingValueType.STRING,
"&eIt will cost &a{cost}&e to salvage that item! {yield} Click again to salvage!", "&eIt will cost &a{cost}&e to salvage that &a{item}&e! {yield} &eClick again to salvage!",
"The message to display when informing a player about the salvaging cost", true, true), "The message to display when informing a player about the salvaging cost", true, true),
/** /**
* The message displayed when displaying the cost of salvaging the armor trim of the held item to the player * The message displayed when displaying the cost of salvaging the armor trim of the held item to the player
*/ */
COST_MESSAGE_ARMOR_TRIM("costMessageArmorTrim", SettingValueType.STRING, COST_MESSAGE_ARMOR_TRIM("costMessageArmorTrim", SettingValueType.STRING,
"&eIt will cost &a{cost}&e to salvage that armor trim!", "&eIt will cost &a{cost}&e to salvage that &a{item}&e's armor trim!",
"The message to display when explaining the shown item's armor trim's salvage cost", true, true), "The message to display when explaining the shown item's armor trim's salvage cost", true, true),
/**
* The message displayed when displaying the cost of salvaging the netherite of the held item to the player
*/
COST_MESSAGE_NETHERITE("costMessageNetherite", SettingValueType.STRING,
"&eIt will cost &a{cost}&e to salvage that &a{item}&e into diamond!",
"The message to display when explaining the shown item's netherite salvage cost", true, true),
/** /**
* The message displayed when explaining that all items will be returned as salvage * The message displayed when explaining that all items will be returned as salvage
*/ */
@ -209,21 +222,40 @@ public enum ScrapperSetting implements Setting {
"&cI'm sorry, but I don't know how to salvage that armor trim!", "&cI'm sorry, but I don't know how to salvage that armor trim!",
"The message to display if the correct materials to return for the armor trim are unknown", true, true), "The message to display if the correct materials to return for the armor trim are unknown", true, true),
/**
* The message displayed when explaining that netherite salvage is disabled
*/
CANNOT_SALVAGE_NETHERITE_MESSAGE("cannotSalvageNetheriteMessage", SettingValueType.STRING,
"&cI'm sorry, but I'm unable to salvage netherite items!",
"The message to display when asked to salvage netherite items, and that option is disabled", true, true),
/*------------------ /*------------------
| Global settings | | Global settings |
------------------*/ ------------------*/
/** /**
* The setting for the use cost of using the scrapper * The setting for the salvage cost
*/ */
USE_COST("basePrice", SettingValueType.POSITIVE_DOUBLE, 0, "The cost of using a scrapper", SALVAGE_COST("salvagePrice", SettingValueType.POSITIVE_DOUBLE, 0,
false, false), "The cost of using a scrapper to salvage an item", false, false),
/**
* The setting for the armor trim salvage cost
*/
ARMOR_TRIM_SALVAGE_COST("armorTrimSalvagePrice", SettingValueType.POSITIVE_DOUBLE, 5,
"The cost of using the scrapper to remove armor trim", false, false),
/**
* The setting for the netherite salvage cost
*/
NETHERITE_SALVAGE_COST("netheriteSalvagePrice", SettingValueType.POSITIVE_DOUBLE, 15,
"The cost of using the scrapper to remove netherite from an item", false, false),
/** /**
* Whether to display exact time in minutes and seconds when displaying a remaining cool-down * Whether to display exact time in minutes and seconds when displaying a remaining cool-down
*/ */
SHOW_EXACT_TIME("showExactTime", SettingValueType.BOOLEAN, "false", "Exact time displays the " + SHOW_EXACT_TIME("showExactTime", SettingValueType.BOOLEAN, "false", "Exact time displays the " +
"exact number of seconds and minutes remaining as part of the scrapping cool-down and scrapping delay " + "exact number of seconds and minutes remaining as part of the salvaging cool-down and salvaging delay " +
"messages, instead of just vaguely hinting at the remaining time.", false, false), "messages, instead of just vaguely hinting at the remaining time.", false, false),
/** /**
@ -263,7 +295,7 @@ public enum ScrapperSetting implements Setting {
* @param isPerNPC <p>Whether this setting is per-NPC or global</p> * @param isPerNPC <p>Whether this setting is per-NPC or global</p>
* @param isMessage <p>Whether this option is for an NPC message</p> * @param isMessage <p>Whether this option is for an NPC message</p>
*/ */
ScrapperSetting(String key, SettingValueType valueType, Object value, ScrapperSetting(@NotNull String key, @NotNull SettingValueType valueType, @NotNull Object value,
@NotNull String description, boolean isPerNPC, boolean isMessage) { @NotNull String description, boolean isPerNPC, boolean isMessage) {
if (isPerNPC) { if (isPerNPC) {
if (isMessage) { if (isMessage) {
@ -289,32 +321,38 @@ public enum ScrapperSetting implements Setting {
} }
@Override @Override
public @NotNull String getPath() { @NotNull
public String getPath() {
return this.path; return this.path;
} }
@Override @Override
public @NotNull String getChildPath() { @NotNull
public String getChildPath() {
return this.childPath; return this.childPath;
} }
@Override @Override
public @NotNull Object getDefaultValue() { @NotNull
public Object getDefaultValue() {
return this.value; return this.value;
} }
@Override @Override
public @NotNull String getCommandName() { @NotNull
public String getCommandName() {
return this.commandName; return this.commandName;
} }
@Override @Override
public @NotNull SettingValueType getValueType() { @NotNull
public SettingValueType getValueType() {
return this.valueType; return this.valueType;
} }
@Override @Override
public @NotNull String getDescription() { @NotNull
public String getDescription() {
return this.description; return this.description;
} }

View File

@ -1,7 +1,9 @@
package net.knarcraft.blacksmith.manager; package net.knarcraft.blacksmith.manager;
import net.knarcraft.blacksmith.BlacksmithPlugin; import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.config.SalvageMethod;
import net.knarcraft.blacksmith.config.blacksmith.GlobalBlacksmithSettings; import net.knarcraft.blacksmith.config.blacksmith.GlobalBlacksmithSettings;
import net.knarcraft.blacksmith.config.scrapper.GlobalScrapperSettings;
import net.knarcraft.blacksmith.util.ItemHelper; import net.knarcraft.blacksmith.util.ItemHelper;
import net.milkbowl.vault.economy.Economy; import net.milkbowl.vault.economy.Economy;
import org.bukkit.Material; import org.bukkit.Material;
@ -54,11 +56,12 @@ public class EconomyManager {
/** /**
* Gets whether the given player cannot pay for salvaging an item * Gets whether the given player cannot pay for salvaging an item
* *
* @param player <p>The player holding an item</p> * @param player <p>The player holding an item</p>
* @param salvageMethod <p>The salvage method to check</p>
* @return <p>Whether the player cannot pay for the salvage</p> * @return <p>Whether the player cannot pay for the salvage</p>
*/ */
public static boolean cannotPayForSalvage(@NotNull Player player) { public static boolean cannotPayForSalvage(@NotNull Player player, @NotNull SalvageMethod salvageMethod) {
return economy.getBalance(player) - BlacksmithPlugin.getInstance().getGlobalScrapperSettings().getCost() < 0; return economy.getBalance(player) - getSalvageCost(salvageMethod) < 0;
} }
/** /**
@ -76,12 +79,27 @@ public class EconomyManager {
/** /**
* Gets the human-readable cost of salvaging an item * Gets the human-readable cost of salvaging an item
* *
* @param salvageMethod <p>The salvage method to get the cost for</p>
* @return <p>The formatted cost</p> * @return <p>The formatted cost</p>
*/ */
@NotNull @NotNull
public static String formatScrapperCost() { public static String formatSalvageCost(@NotNull SalvageMethod salvageMethod) {
double cost = BlacksmithPlugin.getInstance().getGlobalScrapperSettings().getCost(); return economy.format(getSalvageCost(salvageMethod));
return economy.format(cost); }
/**
* Gets the cost of salvaging using the specified method
*
* @param salvageMethod <p>The salvage method to get cost for</p>
* @return <p>The salvage cost</p>
*/
private static double getSalvageCost(@NotNull SalvageMethod salvageMethod) {
GlobalScrapperSettings settings = BlacksmithPlugin.getInstance().getGlobalScrapperSettings();
return switch (salvageMethod) {
case SALVAGE -> settings.getSalvageCost();
case NETHERITE -> settings.getNetheriteSalvageCost();
case ARMOR_TRIM -> settings.getArmorTrimSalvageCost();
};
} }
/** /**
@ -101,10 +119,11 @@ public class EconomyManager {
/** /**
* Withdraws the salvaging cost from the given player * Withdraws the salvaging cost from the given player
* *
* @param player <p>The player to withdraw from</p> * @param player <p>The player to withdraw from</p>
* @param salvageMethod <p>The salvage method to withdraw for</p>
*/ */
public static void withdrawScrapper(Player player) { public static void withdrawScrapper(@NotNull Player player, @NotNull SalvageMethod salvageMethod) {
double cost = BlacksmithPlugin.getInstance().getGlobalScrapperSettings().getCost(); double cost = getSalvageCost(salvageMethod);
if (cost > 0) { if (cost > 0) {
economy.withdrawPlayer(player, cost); economy.withdrawPlayer(player, cost);
} }

View File

@ -7,7 +7,7 @@ import net.knarcraft.blacksmith.config.Settings;
import net.knarcraft.blacksmith.config.TraitSettings; import net.knarcraft.blacksmith.config.TraitSettings;
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.SalvageHelper;
import net.knarcraft.knarlib.formatting.StringFormatter; import net.knarcraft.knarlib.formatting.StringFormatter;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -181,8 +181,8 @@ public abstract class CustomTrait<K extends Setting> extends Trait {
if (isBlacksmith) { if (isBlacksmith) {
EconomyManager.withdrawBlacksmith(player); EconomyManager.withdrawBlacksmith(player);
} else { } else if (this.session instanceof SalvageSession salvageSession) {
EconomyManager.withdrawScrapper(player); EconomyManager.withdrawScrapper(player, salvageSession.salvageMethod);
} }
session.scheduleAction(); session.scheduleAction();
@ -198,7 +198,7 @@ public abstract class CustomTrait<K extends Setting> extends Trait {
//Remove the item from the player's inventory //Remove the item from the player's inventory
if (!isBlacksmith) { if (!isBlacksmith) {
// For scrappers, just reduce the amounts of items, unless the remaining stack is salvaged // For scrappers, just reduce the amounts of items, unless the remaining stack is salvaged
int amount = ItemHelper.getRequiredAmountForSalvage(player.getServer(), heldItem); int amount = SalvageHelper.getRequiredAmountForSalvage(player.getServer(), heldItem);
if (amount != heldItem.getAmount()) { if (amount != heldItem.getAmount()) {
heldItem.setAmount(heldItem.getAmount() - amount); heldItem.setAmount(heldItem.getAmount() - amount);
playerInventory.setItemInMainHand(heldItem); playerInventory.setItemInMainHand(heldItem);

View File

@ -69,14 +69,14 @@ public class ReforgeSession extends Session implements Runnable {
@Override @Override
public boolean isSessionInvalid() { public boolean isSessionInvalid() {
// Prevent player from switching items during session // Prevent player from switching items during session
ItemStack itemInHand = player.getInventory().getItemInMainHand(); ItemStack itemInHand = this.player.getInventory().getItemInMainHand();
if (!itemToReforge.equals(itemInHand)) { if (!itemToReforge.equals(itemInHand)) {
sendNPCMessage(this.npc, player, config.getItemChangedMessage()); sendNPCMessage(this.npc, this.player, this.config.getItemChangedMessage());
return true; return true;
} }
// The player is unable to pay // The player is unable to pay
if (EconomyManager.cannotPayForHeldItemReforge(player)) { if (EconomyManager.cannotPayForHeldItemReforge(this.player)) {
sendNPCMessage(this.npc, player, config.getInsufficientFundsMessage()); sendNPCMessage(this.npc, this.player, this.config.getInsufficientFundsMessage());
return true; return true;
} }
return false; return false;
@ -84,9 +84,9 @@ public class ReforgeSession extends Session implements Runnable {
@Override @Override
protected int getActionDelay() { protected int getActionDelay() {
if (config.getMaxReforgeDelay() > 0) { if (this.config.getMaxReforgeDelay() > 0) {
//Finish the reforging after a random delay between the max and min //Finish the reforging after a random delay between the max and min
return new Random().nextInt(config.getMaxReforgeDelay()) + config.getMinReforgeDelay(); return new Random().nextInt(this.config.getMaxReforgeDelay()) + this.config.getMinReforgeDelay();
} else { } else {
//Finish the salvaging as soon as possible //Finish the salvaging as soon as possible
return 0; return 0;
@ -98,41 +98,41 @@ public class ReforgeSession extends Session implements Runnable {
*/ */
@Override @Override
public void run() { public void run() {
sendNPCMessage(this.npc, player, reforgeItem() ? config.getSuccessMessage() : config.getFailMessage()); sendNPCMessage(this.npc, this.player, reforgeItem() ? this.config.getSuccessMessage() : this.config.getFailMessage());
//Stop the reforged item from displaying in the blacksmith's hand //Stop the reforged item from displaying in the blacksmith's hand
if (npc.getEntity() instanceof Player) { if (this.npc.getEntity() instanceof Player) {
((Player) npc.getEntity()).getInventory().setItemInMainHand(null); ((Player) this.npc.getEntity()).getInventory().setItemInMainHand(null);
} else { } else {
Objects.requireNonNull(((LivingEntity) npc.getEntity()).getEquipment()).setItemInMainHand(null); Objects.requireNonNull(((LivingEntity) this.npc.getEntity()).getEquipment()).setItemInMainHand(null);
} }
//Give the item back to the player //Give the item back to the player
giveReforgedItem(); giveReforgedItem();
//Mark this blacksmith as available //Mark this blacksmith as available
blacksmithTrait.unsetSession(); this.blacksmithTrait.unsetSession();
// Start cool-down // Start cool-down
Calendar wait = Calendar.getInstance(); Calendar wait = Calendar.getInstance();
wait.add(Calendar.SECOND, config.getReforgeCoolDown()); wait.add(Calendar.SECOND, this.config.getReforgeCoolDown());
blacksmithTrait.addCoolDown(player.getUniqueId(), wait); this.blacksmithTrait.addCoolDown(this.player.getUniqueId(), wait);
} }
/** /**
* Gives the reforged item back to the player * Gives the reforged item back to the player
*/ */
private void giveReforgedItem() { private void giveReforgedItem() {
if (config.getMaxReforgeDelay() > 0) { if (this.config.getMaxReforgeDelay() > 0) {
//If the player isn't online, or the player cannot fit the item, drop the item to prevent it from disappearing //If the player isn't online, or the player cannot fit the item, drop the item to prevent it from disappearing
if (config.getDropItem() || !player.isOnline() || player.getInventory().firstEmpty() == -1) { if (this.config.getDropItem() || !this.player.isOnline() || this.player.getInventory().firstEmpty() == -1) {
player.getWorld().dropItemNaturally(npc.getEntity().getLocation(), itemToReforge); this.player.getWorld().dropItemNaturally(this.npc.getEntity().getLocation(), this.itemToReforge);
} else { } else {
player.getInventory().addItem(itemToReforge); this.player.getInventory().addItem(this.itemToReforge);
} }
} else { } else {
//It can be assumed as this happens instantly, that the player still has the item's previous slot selected //It can be assumed as this happens instantly, that the player still has the item's previous slot selected
player.getInventory().setItemInMainHand(itemToReforge); this.player.getInventory().setItemInMainHand(this.itemToReforge);
} }
} }
@ -142,7 +142,7 @@ public class ReforgeSession extends Session implements Runnable {
* @return <p>Whether the reforge was successful. False if the blacksmith failed to fully repair the item.</p> * @return <p>Whether the reforge was successful. False if the blacksmith failed to fully repair the item.</p>
*/ */
private boolean reforgeItem() { private boolean reforgeItem() {
if (random.nextInt(100) < config.getFailChance()) { if (random.nextInt(100) < this.config.getFailChance()) {
failReforge(); failReforge();
return false; return false;
} else { } else {
@ -156,18 +156,18 @@ public class ReforgeSession extends Session implements Runnable {
*/ */
private void succeedReforge() { private void succeedReforge() {
// Remove any damage done to the item // Remove any damage done to the item
updateDamage(itemToReforge, 0); updateDamage(this.itemToReforge, 0);
//Replace damaged anvils with a normal anvil //Replace damaged anvils with a normal anvil
if (ItemHelper.isAnvil(itemToReforge.getType(), true)) { if (ItemHelper.isAnvil(this.itemToReforge.getType(), true)) {
itemToReforge.setType(Material.ANVIL); this.itemToReforge.setType(Material.ANVIL);
} }
//See if a random roll (0-99) is less than extraEnchantmentChance, and add a random enchantment //See if a random roll (0-99) is less than extraEnchantmentChance, and add a random enchantment
int roll = random.nextInt(100); int roll = random.nextInt(100);
if (roll < config.getExtraEnchantmentChance() && if (roll < this.config.getExtraEnchantmentChance() &&
itemToReforge.getEnchantments().keySet().size() < config.getMaxEnchantments() && this.itemToReforge.getEnchantments().keySet().size() < this.config.getMaxEnchantments() &&
!ItemHelper.isAnvil(itemToReforge.getType(), false)) { !ItemHelper.isAnvil(this.itemToReforge.getType(), false)) {
addRandomEnchantment(); addRandomEnchantment();
} }
} }
@ -180,12 +180,12 @@ public class ReforgeSession extends Session implements Runnable {
List<Enchantment> usableEnchantments = new ArrayList<>(); List<Enchantment> usableEnchantments = new ArrayList<>();
for (String enchantmentName : enchantments) { for (String enchantmentName : enchantments) {
Enchantment enchantment = InputParsingHelper.matchEnchantment(enchantmentName); Enchantment enchantment = InputParsingHelper.matchEnchantment(enchantmentName);
if (enchantment != null && enchantment.canEnchantItem(itemToReforge)) { if (enchantment != null && enchantment.canEnchantItem(this.itemToReforge)) {
usableEnchantments.add(enchantment); usableEnchantments.add(enchantment);
} }
} }
//Remove any enchantments in the block list //Remove any enchantments in the block list
usableEnchantments.removeAll(blacksmithTrait.getSettings().getEnchantmentBlockList()); usableEnchantments.removeAll(this.blacksmithTrait.getSettings().getEnchantmentBlockList());
//In case all usable enchantments have been blocked, abort //In case all usable enchantments have been blocked, abort
if (usableEnchantments.isEmpty()) { if (usableEnchantments.isEmpty()) {
@ -198,10 +198,10 @@ public class ReforgeSession extends Session implements Runnable {
int randomBound = randomEnchantment.getMaxLevel() + 1; int randomBound = randomEnchantment.getMaxLevel() + 1;
//A workaround for the random method's bound sometimes being negative //A workaround for the random method's bound sometimes being negative
if (randomBound >= 0) { if (randomBound >= 0) {
int existingLevel = itemToReforge.getEnchantmentLevel(randomEnchantment); int existingLevel = this.itemToReforge.getEnchantmentLevel(randomEnchantment);
/* Add a random enchantment whose level is no lower than the start level, and no lower than the /* Add a random enchantment whose level is no lower than the start level, and no lower than the
existing level (to prevent making the item worse) */ existing level (to prevent making the item worse) */
itemToReforge.addEnchantment(randomEnchantment, Math.max(Math.max(random.nextInt(randomBound), this.itemToReforge.addEnchantment(randomEnchantment, Math.max(Math.max(random.nextInt(randomBound),
randomEnchantment.getStartLevel()), existingLevel)); randomEnchantment.getStartLevel()), existingLevel));
} }
} }
@ -211,20 +211,20 @@ public class ReforgeSession extends Session implements Runnable {
* The method to run when a blacksmith fails re-forging an item * The method to run when a blacksmith fails re-forging an item
*/ */
private void failReforge() { private void failReforge() {
if (config.getFailRemovesEnchantments()) { if (this.config.getFailRemovesEnchantments()) {
removeOrDowngradeEnchantments(); removeOrDowngradeEnchantments();
} }
//Damage the item //Damage the item
short currentItemDurability = ItemHelper.getDurability(itemToReforge); short currentItemDurability = ItemHelper.getDurability(this.itemToReforge);
short newDurability = (short) (currentItemDurability + (currentItemDurability * random.nextInt(8))); short newDurability = (short) (currentItemDurability + (currentItemDurability * random.nextInt(8)));
short maxDurability = itemToReforge.getType().getMaxDurability(); short maxDurability = this.itemToReforge.getType().getMaxDurability();
if (newDurability <= 0) { if (newDurability <= 0) {
newDurability = (short) (maxDurability / 3); newDurability = (short) (maxDurability / 3);
} else if (currentItemDurability + newDurability > maxDurability) { } else if (currentItemDurability + newDurability > maxDurability) {
newDurability = (short) (maxDurability - random.nextInt(maxDurability - 25)); newDurability = (short) (maxDurability - random.nextInt(maxDurability - 25));
} }
updateDamage(itemToReforge, maxDurability - newDurability); updateDamage(this.itemToReforge, maxDurability - newDurability);
} }
/** /**
@ -232,13 +232,13 @@ public class ReforgeSession extends Session implements Runnable {
*/ */
private void removeOrDowngradeEnchantments() { private void removeOrDowngradeEnchantments() {
//Remove or downgrade existing enchantments //Remove or downgrade existing enchantments
for (Enchantment enchantment : itemToReforge.getEnchantments().keySet()) { for (Enchantment enchantment : this.itemToReforge.getEnchantments().keySet()) {
//Completely remove the enchantment, downgrade it, or keep it if lucky and already level 1 //Completely remove the enchantment, downgrade it, or keep it if lucky and already level 1
if (random.nextBoolean()) { if (random.nextBoolean()) {
itemToReforge.removeEnchantment(enchantment); this.itemToReforge.removeEnchantment(enchantment);
} else if (itemToReforge.getEnchantmentLevel(enchantment) > 1) { } else if (this.itemToReforge.getEnchantmentLevel(enchantment) > 1) {
itemToReforge.removeEnchantment(enchantment); this.itemToReforge.removeEnchantment(enchantment);
itemToReforge.addEnchantment(enchantment, 1); this.itemToReforge.addEnchantment(enchantment, 1);
} }
} }
} }

View File

@ -1,6 +1,7 @@
package net.knarcraft.blacksmith.trait; package net.knarcraft.blacksmith.trait;
import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.npc.NPC;
import net.knarcraft.blacksmith.config.SalvageMethod;
import net.knarcraft.blacksmith.config.scrapper.ScrapperNPCSettings; import net.knarcraft.blacksmith.config.scrapper.ScrapperNPCSettings;
import net.knarcraft.blacksmith.manager.EconomyManager; import net.knarcraft.blacksmith.manager.EconomyManager;
import net.knarcraft.blacksmith.util.ItemHelper; import net.knarcraft.blacksmith.util.ItemHelper;
@ -30,6 +31,7 @@ public class SalvageSession extends Session implements Runnable {
private final ScrapperNPCSettings config; private final ScrapperNPCSettings config;
private final List<ItemStack> salvage; private final List<ItemStack> salvage;
private final int enchantmentLevels; private final int enchantmentLevels;
protected final SalvageMethod salvageMethod;
private static final Random random = new Random(); private static final Random random = new Random();
/** /**
@ -39,9 +41,11 @@ public class SalvageSession extends Session implements Runnable {
* @param player <p>The player initiating the session</p> * @param player <p>The player initiating the session</p>
* @param npc <p>The scrapper NPC involved in the session</p> * @param npc <p>The scrapper NPC involved in the session</p>
* @param config <p>The config to use for the session</p> * @param config <p>The config to use for the session</p>
* @param salvageMethod <p>The salvage method performed in this session</p>
*/ */
SalvageSession(@NotNull ScrapperTrait scrapperTrait, @NotNull Player player, @NotNull NPC npc, SalvageSession(@NotNull ScrapperTrait scrapperTrait, @NotNull Player player, @NotNull NPC npc,
@NotNull ScrapperNPCSettings config, @NotNull List<ItemStack> salvage) { @NotNull ScrapperNPCSettings config, @NotNull List<ItemStack> salvage,
@NotNull SalvageMethod salvageMethod) {
super(player); super(player);
this.scrapperTrait = scrapperTrait; this.scrapperTrait = scrapperTrait;
this.npc = npc; this.npc = npc;
@ -49,19 +53,20 @@ public class SalvageSession extends Session implements Runnable {
this.config = config; this.config = config;
this.salvage = salvage; this.salvage = salvage;
this.enchantmentLevels = SalvageHelper.getTotalEnchantmentLevels(this.itemToSalvage); this.enchantmentLevels = SalvageHelper.getTotalEnchantmentLevels(this.itemToSalvage);
this.salvageMethod = salvageMethod;
} }
@Override @Override
public boolean isSessionInvalid() { public boolean isSessionInvalid() {
// Prevent player from switching items during session // Prevent player from switching items during session
ItemStack itemInHand = player.getInventory().getItemInMainHand(); ItemStack itemInHand = this.player.getInventory().getItemInMainHand();
if (!itemToSalvage.equals(itemInHand)) { if (!itemToSalvage.equals(itemInHand)) {
sendNPCMessage(this.npc, player, config.getItemChangedMessage()); sendNPCMessage(this.npc, this.player, this.config.getItemChangedMessage());
return true; return true;
} }
if (EconomyManager.cannotPayForSalvage(player)) { if (EconomyManager.cannotPayForSalvage(this.player, this.salvageMethod)) {
sendNPCMessage(this.npc, player, config.getInsufficientFundsMessage()); sendNPCMessage(this.npc, this.player, this.config.getInsufficientFundsMessage());
return true; return true;
} }
return false; return false;
@ -69,9 +74,9 @@ public class SalvageSession extends Session implements Runnable {
@Override @Override
protected int getActionDelay() { protected int getActionDelay() {
if (config.getMaxSalvageDelay() > 0) { if (this.config.getMaxSalvageDelay() > 0) {
//Finish the salvaging after a random delay between the max and min //Finish the salvaging after a random delay between the max and min
return new Random().nextInt(config.getMaxSalvageDelay()) + config.getMinSalvageDelay(); return new Random().nextInt(this.config.getMaxSalvageDelay()) + this.config.getMinSalvageDelay();
} else { } else {
//Finish the salvaging as soon as possible //Finish the salvaging as soon as possible
return 0; return 0;
@ -83,22 +88,22 @@ public class SalvageSession extends Session implements Runnable {
*/ */
@Override @Override
public void run() { public void run() {
sendNPCMessage(this.npc, player, salvageItem() ? config.getSuccessMessage() : config.getFailMessage()); sendNPCMessage(this.npc, this.player, salvageItem() ? this.config.getSuccessMessage() : this.config.getFailMessage());
//Stop the reforged item from displaying in the scrapper's hand //Stop the reforged item from displaying in the scrapper's hand
if (npc.getEntity() instanceof Player) { if (this.npc.getEntity() instanceof Player) {
((Player) npc.getEntity()).getInventory().setItemInMainHand(null); ((Player) this.npc.getEntity()).getInventory().setItemInMainHand(null);
} else { } else {
Objects.requireNonNull(((LivingEntity) npc.getEntity()).getEquipment()).setItemInMainHand(null); Objects.requireNonNull(((LivingEntity) this.npc.getEntity()).getEquipment()).setItemInMainHand(null);
} }
//Mark this scrapper as available //Mark this scrapper as available
scrapperTrait.unsetSession(); this.scrapperTrait.unsetSession();
// Start cool-down // Start cool-down
Calendar wait = Calendar.getInstance(); Calendar wait = Calendar.getInstance();
wait.add(Calendar.SECOND, config.getSalvageCoolDown()); wait.add(Calendar.SECOND, this.config.getSalvageCoolDown());
scrapperTrait.addCoolDown(player.getUniqueId(), wait); this.scrapperTrait.addCoolDown(this.player.getUniqueId(), wait);
} }
/** /**
@ -107,7 +112,7 @@ public class SalvageSession extends Session implements Runnable {
* @return <p>True if the salvage was successful. False otherwise.</p> * @return <p>True if the salvage was successful. False otherwise.</p>
*/ */
private boolean salvageItem() { private boolean salvageItem() {
if (random.nextInt(100) < config.getFailChance()) { if (random.nextInt(100) < this.config.getFailChance()) {
failSalvage(); failSalvage();
return false; return false;
} else { } else {
@ -121,19 +126,19 @@ public class SalvageSession extends Session implements Runnable {
* The method to run when a crapper fails salvaging an item * The method to run when a crapper fails salvaging an item
*/ */
private void failSalvage() { private void failSalvage() {
if (itemToSalvage.getItemMeta() instanceof Damageable) { if (this.itemToSalvage.getItemMeta() instanceof Damageable) {
//Damage the item //Damage the item
short currentItemDurability = ItemHelper.getDurability(itemToSalvage); short currentItemDurability = ItemHelper.getDurability(this.itemToSalvage);
short newDurability = (short) (currentItemDurability + (currentItemDurability * random.nextInt(8))); short newDurability = (short) (currentItemDurability + (currentItemDurability * random.nextInt(8)));
short maxDurability = itemToSalvage.getType().getMaxDurability(); short maxDurability = this.itemToSalvage.getType().getMaxDurability();
if (newDurability <= 0) { if (newDurability <= 0) {
newDurability = (short) (maxDurability / 3); newDurability = (short) (maxDurability / 3);
} else if (currentItemDurability + newDurability > maxDurability) { } else if (currentItemDurability + newDurability > maxDurability) {
newDurability = (short) (maxDurability - random.nextInt(maxDurability - 25)); newDurability = (short) (maxDurability - random.nextInt(maxDurability - 25));
} }
updateDamage(itemToSalvage, maxDurability - newDurability); updateDamage(this.itemToSalvage, maxDurability - newDurability);
} else { } else {
itemToSalvage.setAmount(Math.max(itemToSalvage.getAmount() - 1, 1)); this.itemToSalvage.setAmount(Math.max(this.itemToSalvage.getAmount() - 1, 1));
} }
} }
@ -143,20 +148,20 @@ public class SalvageSession extends Session implements Runnable {
private void giveSalvage() { private void giveSalvage() {
// TODO: Find a better calculation than 1 enchantment level = 1 exp level // TODO: Find a better calculation than 1 enchantment level = 1 exp level
// Gives the player back some of the EXP used on an item // Gives the player back some of the EXP used on an item
player.giveExpLevels(enchantmentLevels); this.player.giveExpLevels(this.enchantmentLevels);
if (config.getDropItem() || !player.isOnline() || player.getInventory().firstEmpty() == -1) { if (this.config.getDropItem() || !this.player.isOnline() || this.player.getInventory().firstEmpty() == -1) {
// If the player isn't online, or the player cannot fit the item, drop the item to prevent it from // If the player isn't online, or the player cannot fit the item, drop the item to prevent it from
// disappearing, even if drop item is disabled. // disappearing, even if drop item is disabled.
for (ItemStack item : salvage) { for (ItemStack item : this.salvage) {
player.getWorld().dropItemNaturally(npc.getEntity().getLocation(), item); this.player.getWorld().dropItemNaturally(this.npc.getEntity().getLocation(), item);
} }
} else { } else {
for (ItemStack item : salvage) { for (ItemStack item : this.salvage) {
if (ItemHelper.canFitItem(player.getInventory(), item)) { if (ItemHelper.canFitItem(this.player.getInventory(), item)) {
player.getInventory().addItem(item); this.player.getInventory().addItem(item);
} else { } else {
// If the player cannot fit all the salvage, drop it on the ground // If the player cannot fit all the salvage, drop it on the ground
player.getWorld().dropItemNaturally(player.getLocation(), item); this.player.getWorld().dropItemNaturally(this.player.getLocation(), item);
} }
} }
} }

View File

@ -2,15 +2,18 @@ package net.knarcraft.blacksmith.trait;
import net.citizensnpcs.api.util.DataKey; import net.citizensnpcs.api.util.DataKey;
import net.knarcraft.blacksmith.BlacksmithPlugin; import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.config.SalvageMethod;
import net.knarcraft.blacksmith.config.SmithPreset;
import net.knarcraft.blacksmith.config.SmithPresetFilter;
import net.knarcraft.blacksmith.config.scrapper.ScrapperNPCSettings; import net.knarcraft.blacksmith.config.scrapper.ScrapperNPCSettings;
import net.knarcraft.blacksmith.config.scrapper.ScrapperSetting; import net.knarcraft.blacksmith.config.scrapper.ScrapperSetting;
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.blacksmith.util.SalvageHelper; import net.knarcraft.blacksmith.util.SalvageHelper;
import net.knarcraft.knarlib.formatting.StringFormatter; import net.knarcraft.knarlib.formatting.StringFormatter;
import net.knarcraft.knarlib.formatting.StringReplacer;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ArmorMeta; import org.bukkit.inventory.meta.ArmorMeta;
@ -82,7 +85,7 @@ public class ScrapperTrait extends CustomTrait<ScrapperSetting> {
boolean extended = getSettings().extendedSalvageEnabled(); boolean extended = getSettings().extendedSalvageEnabled();
// Check if the item can be salvaged // Check if the item can be salvaged
if (!canBeSalvaged(player.getServer(), itemInHand, salvageAbleItems, extended)) { if (!canBeSalvaged(itemInHand, salvageAbleItems, extended)) {
sendNPCMessage(this.npc, player, StringFormatter.replacePlaceholder(getSettings().getInvalidItemMessage(), sendNPCMessage(this.npc, player, StringFormatter.replacePlaceholder(getSettings().getInvalidItemMessage(),
"{title}", getSettings().getScrapperTitle())); "{title}", getSettings().getScrapperTitle()));
return; return;
@ -91,18 +94,35 @@ public class ScrapperTrait extends CustomTrait<ScrapperSetting> {
List<ItemStack> salvage = null; List<ItemStack> salvage = null;
// Deal with armor trim salvage // Deal with armor trim salvage
boolean salvagingArmorTrim = false; SalvageMethod salvageMethod = SalvageMethod.SALVAGE;
if (itemInHand.getItemMeta() instanceof ArmorMeta armorMeta && armorMeta.hasTrim()) { if (itemInHand.getItemMeta() instanceof ArmorMeta armorMeta && armorMeta.hasTrim()) {
if (!getSettings().salvageArmorTrims()) { if (!getSettings().salvageArmorTrims()) {
sendNPCMessage(this.npc, player, getSettings().getCannotSalvageArmorTrimMessage()); sendNPCMessage(this.npc, player, getSettings().getCannotSalvageArmorTrimMessage());
return; return;
} }
salvage = SalvageHelper.getTrimSalvage(itemInHand, armorMeta); salvage = SalvageHelper.getArmorTrimSalvage(itemInHand, armorMeta);
if (salvage == null) { if (salvage == null) {
sendNPCMessage(this.npc, player, getSettings().getArmorTrimSalvageNotFoundMessage()); sendNPCMessage(this.npc, player, getSettings().getArmorTrimSalvageNotFoundMessage());
return; return;
} }
salvagingArmorTrim = true; salvageMethod = SalvageMethod.ARMOR_TRIM;
}
// Remove the netherite ingot from the item
if (salvage == null && SmithPreset.BLACKSMITH.getFilteredMaterials(SmithPresetFilter.NETHERITE).contains(itemInHand.getType())) {
if (!getSettings().salvageNetherite()) {
sendNPCMessage(this.npc, player, getSettings().getCannotSalvageNetheriteMessage());
return;
}
salvage = SalvageHelper.getNetheriteSalvage(itemInHand);
salvageMethod = SalvageMethod.NETHERITE;
}
// As there is no recipe for netherite items, the check needs to be after the netherite salvage check
if (salvageMethod == SalvageMethod.SALVAGE && !SalvageHelper.isSalvageable(player.getServer(), itemInHand)) {
sendNPCMessage(this.npc, player, StringFormatter.replacePlaceholder(getSettings().getInvalidItemMessage(),
"{title}", getSettings().getScrapperTitle()));
} }
// Check if the item is enchanted, and whether this blacksmith can salvage it // Check if the item is enchanted, and whether this blacksmith can salvage it
@ -121,26 +141,53 @@ public class ScrapperTrait extends CustomTrait<ScrapperSetting> {
return; return;
} }
//Start a new scrapper session for the player
startSession(player, salvage, salvageMethod);
// Print the cost to the player
printCostMessage(player, itemInHand, EconomyManager.formatSalvageCost(salvageMethod), salvageMethod);
}
/**
* Prints a message to the given player, explaining the cost of salvaging the held item
*
* @param player <p>The player that interacted with the scrapper</p>
* @param itemInHand <p>The item the player wants to salvage</p>
* @param cost <p>The cost of salvaging the item</p>
* @param salvageMethod <p>The type of salvage performed</p>
*/
private void printCostMessage(@NotNull Player player, @NotNull ItemStack itemInHand, @NotNull String cost,
@NotNull SalvageMethod salvageMethod) {
StringReplacer replacer = new StringReplacer();
replacer.add("{cost}", cost);
replacer.add("{item}", itemInHand.getType().name().toLowerCase().replace('_', ' '));
if (salvageMethod == SalvageMethod.ARMOR_TRIM) {
sendNPCMessage(this.npc, player, replacer.replace(getSettings().getArmorTrimCostMessage()));
} else if (salvageMethod == SalvageMethod.SALVAGE) {
String expectedYield;
if (ItemHelper.getDamage(itemInHand) <= 0) {
expectedYield = getSettings().getFullSalvageMessage();
} else {
expectedYield = getSettings().getPartialSalvageMessage();
}
replacer.add("{yield}", expectedYield);
sendNPCMessage(this.npc, player, replacer.replace(getSettings().getCostMessage()));
} else if (salvageMethod == SalvageMethod.NETHERITE) {
sendNPCMessage(this.npc, player, replacer.replace(getSettings().getNetheriteCostMessage()));
}
}
/**
* Starts a new scrapper session
*
* @param player <p>The player to start the session for</p>
* @param salvage <p>The salvage to return to the player</p>
* @param salvageMethod <p>The type of salvage performed</p>
*/
private void startSession(@NotNull Player player, @NotNull List<ItemStack> salvage,
@NotNull SalvageMethod salvageMethod) {
//Start a new scrapper session for the player //Start a new scrapper session for the player
currentSessionStartTime = System.currentTimeMillis(); currentSessionStartTime = System.currentTimeMillis();
session = new SalvageSession(this, player, npc, getSettings(), salvage); session = new SalvageSession(this, player, npc, getSettings(), salvage, salvageMethod);
//Tell the player the cost of repairing the item
String cost = EconomyManager.formatScrapperCost();
String expectedYield;
if (ItemHelper.getDamage(itemInHand) <= 0) {
expectedYield = getSettings().getFullSalvageMessage();
} else {
expectedYield = getSettings().getPartialSalvageMessage();
}
if (salvagingArmorTrim) {
sendNPCMessage(this.npc, player, StringFormatter.replacePlaceholder(getSettings().getArmorTrimCostMessage(),
"{cost}", cost));
} else {
sendNPCMessage(this.npc, player, StringFormatter.replacePlaceholders(getSettings().getCostMessage(),
List.of("{cost}", "{yield}"), List.of(cost, expectedYield)));
}
} }
@Override @Override
@ -151,17 +198,14 @@ public class ScrapperTrait extends CustomTrait<ScrapperSetting> {
/** /**
* Gets whether this scrapper can salvage the given item * Gets whether this scrapper can salvage the given item
* *
* @param server <p>The server to get recipes from</p>
* @param item <p>The item to check</p> * @param item <p>The item to check</p>
* @param salvageAbleItems <p>The items this scrapper can salvage</p> * @param salvageAbleItems <p>The items this scrapper can salvage</p>
* @param extended <p>Whether extended salvage is enabled</p> * @param extended <p>Whether extended salvage is enabled</p>
* @return <p>True if the item can be theoretically salvaged</p> * @return <p>True if the item can be theoretically salvaged</p>
*/ */
private boolean canBeSalvaged(@NotNull Server server, @NotNull ItemStack item, private boolean canBeSalvaged(@NotNull ItemStack item, @NotNull List<Material> salvageAbleItems, boolean extended) {
@NotNull List<Material> salvageAbleItems, boolean extended) { return (extended || ItemHelper.isRepairable(item)) &&
return (extended || ItemHelper.isRepairable(item)) && ItemHelper.isSalvageable(server, item) && (salvageAbleItems.isEmpty() || salvageAbleItems.contains(item.getType()));
(salvageAbleItems.isEmpty() || salvageAbleItems.contains(item.getType())) &&
SalvageHelper.hasRecipe(item);
} }
/** /**

View File

@ -110,6 +110,7 @@ public final class ConfigHelper {
/** /**
* Changes all configuration values from the old name to the new name * Changes all configuration values from the old name to the new name
* *
* @param dataFolderPath <p>The path to this plugin's data</p>
* @param currentConfiguration <p>The current config to back up</p> * @param currentConfiguration <p>The current config to back up</p>
*/ */
public static void migrateConfig(@NotNull String dataFolderPath, @NotNull FileConfiguration currentConfiguration) { public static void migrateConfig(@NotNull String dataFolderPath, @NotNull FileConfiguration currentConfiguration) {

View File

@ -3,11 +3,8 @@ package net.knarcraft.blacksmith.util;
import net.knarcraft.blacksmith.BlacksmithPlugin; import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.config.SmithPreset; import net.knarcraft.blacksmith.config.SmithPreset;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.inventory.CraftingRecipe;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.Recipe;
import org.bukkit.inventory.meta.Damageable; import org.bukkit.inventory.meta.Damageable;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -168,40 +165,6 @@ public final class ItemHelper {
return false; return false;
} }
/**
* Checks whether the given item can be salvaged, assuming no restrictions apply
*
* @param server <p>The server to get recipes from</p>
* @param item <p>The item to check</p>
* @return <p>True if the item can be salvaged</p>
*/
public static boolean isSalvageable(@NotNull Server server, @NotNull ItemStack item) {
for (Recipe recipe : server.getRecipesFor(new ItemStack(item.getType(), item.getAmount()))) {
// Only crafting recipes are allowed.
if ((recipe instanceof CraftingRecipe) && item.getAmount() >= recipe.getResult().getAmount()) {
return true;
}
}
return false;
}
/**
* Gets the amount of an item that's required to salvage that item
*
* @param server <p>The server to get recipes from</p>
* @param item <p>The item to check</p>
* @return <p>The number of items required for salvage, or -1 if the recipe was not found</p>
*/
public static int getRequiredAmountForSalvage(@NotNull Server server, @NotNull ItemStack item) {
for (Recipe recipe : server.getRecipesFor(new ItemStack(item.getType(), item.getAmount()))) {
// Only crafting recipes are allowed.
if (recipe instanceof CraftingRecipe) {
return recipe.getResult().getAmount();
}
}
return -1;
}
/** /**
* Gets all materials matching the given material wildcard * Gets all materials matching the given material wildcard
* *

View File

@ -1,6 +1,5 @@
package net.knarcraft.blacksmith.util; package net.knarcraft.blacksmith.util;
import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Server; import org.bukkit.Server;
import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.Enchantment;
@ -43,6 +42,29 @@ public final class SalvageHelper {
trimMaterialToMaterial.put(TrimMaterial.REDSTONE, Material.REDSTONE); trimMaterialToMaterial.put(TrimMaterial.REDSTONE, Material.REDSTONE);
} }
/**
* Gets salvage for a given netherite item
*
* @param item <p>The netherite armor/tool to salvage</p>
* @return <p></p>
*/
@Nullable
public static List<ItemStack> getNetheriteSalvage(@NotNull ItemStack item) {
Material newMaterial = Material.matchMaterial(item.getType().name().replace("NETHERITE", "DIAMOND"));
if (newMaterial == null) {
return null;
}
ItemStack clone = item.clone();
clone.setType(newMaterial);
List<ItemStack> salvage = new ArrayList<>();
salvage.add(clone);
salvage.add(new ItemStack(Material.NETHERITE_INGOT, 1));
salvage.add(new ItemStack(Material.NETHERITE_UPGRADE_SMITHING_TEMPLATE, 1));
return salvage;
}
/** /**
* Gets salvage for the given armor trim * Gets salvage for the given armor trim
* *
@ -51,7 +73,7 @@ public final class SalvageHelper {
* @return <p>The salvage, or null if salvage could not be calculated</p> * @return <p>The salvage, or null if salvage could not be calculated</p>
*/ */
@Nullable @Nullable
public static List<ItemStack> getTrimSalvage(@NotNull ItemStack item, @NotNull ArmorMeta armorMeta) { public static List<ItemStack> getArmorTrimSalvage(@NotNull ItemStack item, @NotNull ArmorMeta armorMeta) {
ArmorTrim armorTrim = armorMeta.getTrim(); ArmorTrim armorTrim = armorMeta.getTrim();
if (armorTrim == null) { if (armorTrim == null) {
return null; return null;
@ -82,22 +104,41 @@ public final class SalvageHelper {
} }
/** /**
* Gets whether the given item has a valid crafting recipe * Checks whether the given item can be salvaged, assuming no restrictions apply
* *
* @param item <p>The item to check</p> * @param server <p>The server to get recipes from</p>
* @return <p>True if the item has a valid crafting recipe</p> * @param item <p>The item to check</p>
* @return <p>True if the item can be salvaged</p>
*/ */
public static boolean hasRecipe(@NotNull ItemStack item) { public static boolean isSalvageable(@NotNull Server server, @NotNull ItemStack item) {
List<Recipe> recipes = Bukkit.getRecipesFor(new ItemStack(item.getType(), item.getAmount())); List<Recipe> recipes = server.getRecipesFor(new ItemStack(item.getType(), item.getAmount()));
for (Recipe recipe : recipes) { for (Recipe recipe : recipes) {
if (recipe instanceof ShapedRecipe || recipe instanceof ShapelessRecipe) { // Only crafting recipes are allowed.
if ((recipe instanceof ShapedRecipe || recipe instanceof ShapelessRecipe) &&
item.getAmount() >= recipe.getResult().getAmount()) {
return true; return true;
} }
} }
return false; return false;
} }
/**
* Gets the amount of an item that's required to salvage that item
*
* @param server <p>The server to get recipes from</p>
* @param item <p>The item to check</p>
* @return <p>The number of items required for salvage, or -1 if the recipe was not found</p>
*/
public static int getRequiredAmountForSalvage(@NotNull Server server, @NotNull ItemStack item) {
for (Recipe recipe : server.getRecipesFor(new ItemStack(item.getType(), item.getAmount()))) {
// Only crafting recipes are allowed.
if (recipe instanceof ShapedRecipe || recipe instanceof ShapelessRecipe) {
return recipe.getResult().getAmount();
}
}
return 1;
}
/** /**
* Gets the sum of all enchantment levels for the given item * Gets the sum of all enchantment levels for the given item
* *

View File

@ -116,8 +116,8 @@ blacksmith:
scrapper: scrapper:
# The settings which apply to all Scrapper NPCs. These can also be changed using the /scrapperConfig command # The settings which apply to all Scrapper NPCs. These can also be changed using the /scrapperConfig command
global: global:
# Exact time displays the exact number of seconds and minutes remaining as part of the scrapping cool-down and # Exact time displays the exact number of seconds and minutes remaining as part of the salvaging cool-down and
# scrapping delay messages, instead of just vaguely hinting at the remaining time. # salvaging delay messages, instead of just vaguely hinting at the remaining time.
showExactTime: false showExactTime: false
# Whether enchanted salvaged items should return some amount of exp upon salvage # Whether enchanted salvaged items should return some amount of exp upon salvage
@ -130,14 +130,20 @@ scrapper:
trashSalvage: trashSalvage:
- "*_SHOVEL;*_PICKAXE;*_AXE;*_HOE;*_SWORD;SHIELD;*_BOW:STICK" - "*_SHOVEL;*_PICKAXE;*_AXE;*_HOE;*_SWORD;SHIELD;*_BOW:STICK"
# The cost of using the scrapper # The cost of using a scrapper to salvage an item
basePrice: 0 salvagePrice: 0
# The cost of using the scrapper to remove armor trim
armorTrimSalvagePrice: 5
# The cost of using the scrapper to remove netherite from an item
netheriteSalvagePrice: 15
# The settings which are set to any new scrapper NPC. To change any of these settings for an existing NPC, you must # The settings which are set to any new scrapper NPC. To change any of these settings for an existing NPC, you must
# change the Citizens NPC file, or use the /scrapper command # change the Citizens NPC file, or use the /scrapper command
defaults: defaults:
# Whether the item will drop materials resulting from scrapping on the ground, instead of putting them into the user's inventory # Whether the item will drop materials resulting from salvaging on the ground, instead of putting them into the user's inventory
dropItem: true dropItem: true
# The chance to fail a salvage, thus destroying the item (0-100) # The chance to fail a salvage, thus destroying the item (0-100)
@ -170,6 +176,9 @@ scrapper:
# Whether to enable salvaging of armor trims # Whether to enable salvaging of armor trims
salvageArmorTrims: true salvageArmorTrims: true
# Whether to enable salvaging of netherite items
salvageNetherite: true
# Default values for messages used by NPCs # Default values for messages used by NPCs
messages: messages:
# The message to display when another player is using the scrapper # The message to display when another player is using the scrapper
@ -203,10 +212,13 @@ scrapper:
insufficientFundsMessage: "&cYou don't have enough money to salvage an item!" insufficientFundsMessage: "&cYou don't have enough money to salvage an item!"
# The message to display when explaining the shown item's salvage cost # The message to display when explaining the shown item's salvage cost
costMessage: "&eIt will cost &a{cost}&e to salvage that item! {yield} &eClick again to salvage!" costMessage: "&eIt will cost &a{cost}&e to salvage that &a{item}&e! {yield} &eClick again to salvage!"
# The message to display when explaining the shown item's armor trim's salvage cost # The message to display when explaining the shown item's armor trim's salvage cost
costMessageArmorTrim: "&eIt will cost &a{cost}&e to salvage that armor trim!" costMessageArmorTrim: "&eIt will cost &a{cost}&e to salvage that &a{item}&e's armor trim!"
# The message to display when explaining the shown item's netherite salvage cost
costMessageNetherite: "&eIt will cost &a{cost}&e to salvage that &a{item}&e into diamond!"
# The yield message to display if trying to salvage a non-damaged item # The yield message to display if trying to salvage a non-damaged item
fullSalvageMessage: "&aI should be able to extract all components from that pristine item.&r" fullSalvageMessage: "&aI should be able to extract all components from that pristine item.&r"
@ -221,4 +233,7 @@ scrapper:
cannotSalvageArmorTrimMessage: "&cI'm sorry, but I'm unable to salvage armor trims!" cannotSalvageArmorTrimMessage: "&cI'm sorry, but I'm unable to salvage armor trims!"
# The message to display if the correct materials to return for the armor trim are unknown # The message to display if the correct materials to return for the armor trim are unknown
armorTrimSalvageNotFoundMessage: "&cI'm sorry, but I don't know how to salvage that armor trim!" armorTrimSalvageNotFoundMessage: "&cI'm sorry, but I don't know how to salvage that armor trim!"
# The message to display when asked to salvage netherite items, and that option is disabled
cannotSalvageNetheriteMessage: "&cI'm sorry, but I'm unable to salvage netherite items!"