From 4012e532da473bbe3c33eee23c91bde58c1471bb Mon Sep 17 00:00:00 2001
From: EpicKnarvik97
Date: Sat, 4 May 2024 01:01:56 +0200
Subject: [PATCH] Finishes the scrapper implementation
---
README.md | 94 +++++++++++++++----
pom.xml | 20 +++-
.../blacksmith/BlacksmithPlugin.java | 19 ++--
.../blacksmith/command/EditCommand.java | 2 +-
.../blacksmith/BlackSmithConfigCommand.java | 2 +-
.../knarcraft/blacksmith/config/Setting.java | 19 ++--
.../knarcraft/blacksmith/config/Settings.java | 7 +-
.../blacksmith/config/SmithPreset.java | 35 +++++--
.../blacksmith/BlacksmithNPCSettings.java | 38 ++++----
.../config/blacksmith/BlacksmithSetting.java | 2 +-
.../blacksmith/GlobalBlacksmithSettings.java | 31 +++---
.../scrapper/GlobalScrapperSettings.java | 25 +++--
.../config/scrapper/ScrapperNPCSettings.java | 54 ++++++++---
.../config/scrapper/ScrapperSetting.java | 20 ++++
.../blacksmith/listener/PlayerListener.java | 1 +
.../blacksmith/manager/EconomyManager.java | 45 ++++++++-
.../blacksmith/trait/BlacksmithTrait.java | 5 +-
.../blacksmith/trait/CustomTrait.java | 27 +++++-
.../blacksmith/trait/ReforgeSession.java | 2 +-
.../blacksmith/trait/SalvageSession.java | 38 +++-----
.../blacksmith/trait/ScrapperTrait.java | 67 +++++++++----
.../knarcraft/blacksmith/util/ItemHelper.java | 54 ++++++++---
.../blacksmith/util/SalvageHelper.java | 53 ++++++-----
src/main/resources/config-migrations.txt | 3 +-
src/main/resources/config.yml | 14 ++-
src/main/resources/plugin.yml | 4 +-
src/main/resources/strings.yml | 2 +-
.../blacksmith/CustomServerMock.java | 10 +-
.../blacksmith/util/ItemHelperTest.java | 2 +-
.../blacksmith/util/SalvageHelperTest.java | 22 ++---
30 files changed, 501 insertions(+), 216 deletions(-)
diff --git a/README.md b/README.md
index 4492ecc..74de978 100644
--- a/README.md
+++ b/README.md
@@ -26,41 +26,57 @@ fee. Costs are highly customizable.
To create a new blacksmith, simply add the blacksmith trait to an NPC by selecting it with `/npc select`, and then using
`/trait add Blacksmith` (See Citizens' documentation for more details).
-Right-clicking the NPC will tell you if the currently held item is repairable by the blacksmith. If it is, the
-blacksmith should give a price quote. Right-clicking again starts the repair. The item should be given back or dropped
-after a random delay according to the set limits.
+To create a new scrapper, simply add the scrapper trait to an NPC by selecting it with `/npc select`, and then using
+`/trait add Scrapper` (See Citizens' documentation for more details).
-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,
-the maximum number of added enchantments, max and min delays, the length of the cool-down and which items the blacksmith
-is able to reforge can be changed individually.
+Right-clicking the NPC will tell you if the currently held item is repairable by the blacksmith / salvageable by the
+scrapper. If it is, the blacksmith / scrapper should give a price quote. Right-clicking again starts the repair /
+salvage. 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 or scrapper can do the same job for cheaper), the blacksmith /
+scrapper's messages, cool-down between each reforge / salvage, whether the item is dropped or given, the chance of
+failing or adding an enchantment, the maximum number of added enchantments, max and min delays, the length of the
+cool-down and which items the blacksmith is able to reforge / salvage can be changed individually.
Also note: As a change from the original plugin, unless a value for an NPC has been explicitly set for that NPC, it will
mirror the values set in config.yml. This also means that configuration values aren't populated automatically in
citizen's NPC save file. While you can manually set the values using the same keys as in config.yml, you should use the
-/blacksmith command when possible.
+`/blacksmith` or `/scrapper` command when possible.
### Special behavior
-In addition to just being able to repair items, blacksmiths have some random features which can be cool or annoying:
+In addition to just being able to repair items, blacksmiths / scrappers have some random features which can be cool or
+annoying:
-- 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 / scrappers fail to repair an item, leaving it at about the same durability as
+ before (scrappers with `extendedSalvageEnabled` turned on might cause items in a stack to be lost). Use
failReforgeChance to control the chance. Set it to 0 to remove the feature.
- There is a chance a blacksmith may add an enchantment to a reforged item. You can control the probability using
- extraEnchantmentChance, and set the maximum number of enchantments using maxEnchantments. EnchantmentBlocklist can be
+ 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.
+### Scrapper basics
+
+A scrapper will produce salvage for a damage-able item by calculating the amount of items returned based on items in the
+recipe, and the percentage of durability left on the item. To avoid returning relatively worthless items instead of
+valuable items, `ignoredSalvage` can be configured. If the item is fully repaired, the worthless items will be returned
+as well, but otherwise, only the valuable items are considered as possible salvage. A scrapper will by default only
+salvage damage-able items (same as blacksmiths), but enabling `extendedSalvageEnabled` for a scrapper will allow it to
+salvage any crafting table recipe. Note that to salvage for example planks into wood, four wood will be taken.
+
+When an item is salvaged, EXP will be returned based on enchantments on the item.
+
## Commands
| Command | Arguments | Description |
|-------------------|-------------------------------|----------------------------------------------------------------------------------------------|
| /blacksmith | \
*
* @return
True if extended salvaging is enabled
*/
@@ -273,6 +285,7 @@ public class ScrapperNPCSettings implements TraitSettings {
*
* @return
The title of this scrapper NPC
*/
+ @NotNull
public String getScrapperTitle() {
return asString(ScrapperSetting.SCRAPPER_TITLE);
}
@@ -282,8 +295,19 @@ public class ScrapperNPCSettings implements TraitSettings {
*
* @return
The message to display
*/
+ @NotNull
public String getInvalidItemMessage() {
return asString(ScrapperSetting.INVALID_ITEM_MESSAGE);
}
+ /**
+ * Gets the message to display if the player is unable to afford an item salvage
+ *
+ * @return
The message to display
+ */
+ @NotNull
+ public String getInsufficientFundsMessage() {
+ return asString(ScrapperSetting.INSUFFICIENT_FUNDS_MESSAGE);
+ }
+
}
diff --git a/src/main/java/net/knarcraft/blacksmith/config/scrapper/ScrapperSetting.java b/src/main/java/net/knarcraft/blacksmith/config/scrapper/ScrapperSetting.java
index 075ffb0..8abbef6 100644
--- a/src/main/java/net/knarcraft/blacksmith/config/scrapper/ScrapperSetting.java
+++ b/src/main/java/net/knarcraft/blacksmith/config/scrapper/ScrapperSetting.java
@@ -140,10 +140,30 @@ public enum ScrapperSetting implements Setting {
START_SALVAGE_MESSAGE("startSalvageMessage", SettingValueType.STRING, "&eOk, let's see what I can do...",
"The message to display once the blacksmith starts re-forging", true, true),
+ /**
+ * The message displayed if a player is unable to pay the blacksmith
+ */
+ INSUFFICIENT_FUNDS_MESSAGE("insufficientFundsMessage", SettingValueType.STRING,
+ "&cYou don't have enough money to salvage an item!",
+ "The message to display when a player cannot pay for the salvaging", true, true),
+
+ /**
+ * The message displayed when displaying the cost of reforging the held item to the player
+ */
+ COST_MESSAGE("costMessage", SettingValueType.STRING,
+ "&eIt will cost &a{cost}&e to salvage that item! Click again to salvage!",
+ "The message to display when informing a player about the salvaging cost", true, true),
+
/*------------------
| Global settings |
------------------*/
+ /**
+ * The setting for the use cost of using the scrapper
+ */
+ USE_COST("basePrice", SettingValueType.POSITIVE_DOUBLE, 0, "The cost of using a scrapper",
+ false, false),
+
/**
* Whether to display exact time in minutes and seconds when displaying a remaining cool-down
*/
diff --git a/src/main/java/net/knarcraft/blacksmith/listener/PlayerListener.java b/src/main/java/net/knarcraft/blacksmith/listener/PlayerListener.java
index 5f89cd2..32affa9 100644
--- a/src/main/java/net/knarcraft/blacksmith/listener/PlayerListener.java
+++ b/src/main/java/net/knarcraft/blacksmith/listener/PlayerListener.java
@@ -65,6 +65,7 @@ public class PlayerListener implements Listener {
* @param
The type of entity the player is looking at
* @return
The entity the player is looking at, or null if no such entity exists
*/
+ @Nullable
private static T getTarget(final @Nullable Entity entity, final @NotNull Iterable entities) {
if (entity == null) {
return null;
diff --git a/src/main/java/net/knarcraft/blacksmith/manager/EconomyManager.java b/src/main/java/net/knarcraft/blacksmith/manager/EconomyManager.java
index e488074..8d374c4 100644
--- a/src/main/java/net/knarcraft/blacksmith/manager/EconomyManager.java
+++ b/src/main/java/net/knarcraft/blacksmith/manager/EconomyManager.java
@@ -48,7 +48,17 @@ public class EconomyManager {
* @return
Whether the player cannot pay for the reforge
*/
public static boolean cannotPayForHeldItemReforge(@NotNull Player player) {
- return !(economy.getBalance(player) - getHeldItemCost(player) >= 0);
+ return economy.getBalance(player) - getHeldItemCost(player) < 0;
+ }
+
+ /**
+ * Gets whether the given player cannot pay for salvaging an item
+ *
+ * @param player
The player holding an item
+ * @return
Whether the player cannot pay for the salvage
+ */
+ public static boolean cannotPayForSalvage(@NotNull Player player) {
+ return economy.getBalance(player) - BlacksmithPlugin.getInstance().getGlobalScrapperSettings().getCost() < 0;
}
/**
@@ -57,11 +67,23 @@ public class EconomyManager {
* @param player
The player holding an item
* @return
The formatted cost
*/
- public static String formatCost(@NotNull Player player) {
+ @NotNull
+ public static String formatBlacksmithCost(@NotNull Player player) {
double cost = getHeldItemCost(player);
return economy.format(cost);
}
+ /**
+ * Gets the human-readable cost of salvaging an item
+ *
+ * @return
The formatted cost
+ */
+ @NotNull
+ public static String formatScrapperCost() {
+ double cost = BlacksmithPlugin.getInstance().getGlobalScrapperSettings().getCost();
+ return economy.format(cost);
+ }
+
/**
* Withdraws the reforging cost from the given player
*
@@ -69,8 +91,23 @@ public class EconomyManager {
*
* @param player
The player to withdraw from
*/
- public static void withdraw(@NotNull Player player) {
- economy.withdrawPlayer(player, getHeldItemCost(player));
+ public static void withdrawBlacksmith(@NotNull Player player) {
+ double cost = getHeldItemCost(player);
+ if (cost > 0) {
+ economy.withdrawPlayer(player, cost);
+ }
+ }
+
+ /**
+ * Withdraws the salvaging cost from the given player
+ *
+ * @param player
*/
- public @NotNull BlacksmithNPCSettings getSettings() {
+ @NotNull
+ public BlacksmithNPCSettings getSettings() {
return config;
}
@@ -90,7 +91,7 @@ public class BlacksmithTrait extends CustomTrait {
session = new ReforgeSession(this, player, npc, config);
//Tell the player the cost of repairing the item
- String cost = EconomyManager.formatCost(player);
+ String cost = EconomyManager.formatBlacksmithCost(player);
String itemName = hand.getType().name().toLowerCase().replace('_', ' ');
sendNPCMessage(this.npc, player, config.getCostMessage().replace("{cost}", cost).replace("{item}",
itemName));
diff --git a/src/main/java/net/knarcraft/blacksmith/trait/CustomTrait.java b/src/main/java/net/knarcraft/blacksmith/trait/CustomTrait.java
index 5623978..ab20af5 100644
--- a/src/main/java/net/knarcraft/blacksmith/trait/CustomTrait.java
+++ b/src/main/java/net/knarcraft/blacksmith/trait/CustomTrait.java
@@ -7,10 +7,12 @@ import net.knarcraft.blacksmith.config.Settings;
import net.knarcraft.blacksmith.config.TraitSettings;
import net.knarcraft.blacksmith.formatting.TimeFormatter;
import net.knarcraft.blacksmith.manager.EconomyManager;
+import net.knarcraft.blacksmith.util.ItemHelper;
import net.knarcraft.knarlib.formatting.StringFormatter;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.PlayerInventory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -174,9 +176,17 @@ public abstract class CustomTrait extends Trait {
*/
private void startMainAction(@NotNull NPC npc, @NotNull Player player) {
sendNPCMessage(this.npc, player, config.getStartWorkingMessage());
- // TODO: Differentiate between blacksmiths and scrappers when withdrawing money
- EconomyManager.withdraw(player);
+
+ boolean isBlacksmith = this instanceof BlacksmithTrait;
+
+ if (isBlacksmith) {
+ EconomyManager.withdrawBlacksmith(player);
+ } else {
+ EconomyManager.withdrawScrapper(player);
+ }
+
session.scheduleAction();
+ PlayerInventory playerInventory = player.getInventory();
ItemStack heldItem = player.getInventory().getItemInMainHand();
//Display the item in the NPC's hand
@@ -186,9 +196,16 @@ public abstract class CustomTrait extends Trait {
Objects.requireNonNull(((LivingEntity) npc.getEntity()).getEquipment()).setItemInMainHand(heldItem);
}
//Remove the item from the player's inventory
- // TODO: For a scrapper with extended salvaging enabled, the item needs to be reduced with the amount specified
- // in the recipe, or removed as normal in the case where the player has exactly enough items to run the salvage.
- player.getInventory().setItemInMainHand(null);
+ if (!isBlacksmith) {
+ // For scrappers, just reduce the amounts of items, unless the remaining stack is salvaged
+ int amount = ItemHelper.getRequiredAmountForSalvage(player.getServer(), heldItem);
+ if (amount != heldItem.getAmount()) {
+ heldItem.setAmount(heldItem.getAmount() - amount);
+ playerInventory.setItemInMainHand(heldItem);
+ return;
+ }
+ }
+ playerInventory.setItemInMainHand(null);
}
}
diff --git a/src/main/java/net/knarcraft/blacksmith/trait/ReforgeSession.java b/src/main/java/net/knarcraft/blacksmith/trait/ReforgeSession.java
index dd1cab0..747bc69 100644
--- a/src/main/java/net/knarcraft/blacksmith/trait/ReforgeSession.java
+++ b/src/main/java/net/knarcraft/blacksmith/trait/ReforgeSession.java
@@ -178,7 +178,7 @@ public class ReforgeSession extends Session implements Runnable {
}
}
//Remove any enchantments in the block list
- usableEnchantments.removeAll(blacksmithTrait.getSettings().getEnchantmentBlocklist());
+ usableEnchantments.removeAll(blacksmithTrait.getSettings().getEnchantmentBlockList());
//In case all usable enchantments have been blocked, abort
if (usableEnchantments.isEmpty()) {
diff --git a/src/main/java/net/knarcraft/blacksmith/trait/SalvageSession.java b/src/main/java/net/knarcraft/blacksmith/trait/SalvageSession.java
index 3a5d47b..210c70e 100644
--- a/src/main/java/net/knarcraft/blacksmith/trait/SalvageSession.java
+++ b/src/main/java/net/knarcraft/blacksmith/trait/SalvageSession.java
@@ -1,11 +1,10 @@
package net.knarcraft.blacksmith.trait;
import net.citizensnpcs.api.npc.NPC;
-import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.config.scrapper.ScrapperNPCSettings;
+import net.knarcraft.blacksmith.manager.EconomyManager;
import net.knarcraft.blacksmith.util.ItemHelper;
import net.knarcraft.blacksmith.util.SalvageHelper;
-import org.bukkit.Material;
import org.bukkit.entity.Damageable;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
@@ -13,17 +12,15 @@ import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.Calendar;
-import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Random;
-import java.util.Set;
import static net.knarcraft.blacksmith.formatting.BlacksmithStringFormatter.sendNPCMessage;
import static net.knarcraft.blacksmith.util.ItemHelper.updateDamage;
/**
- * A representation of the session between a player and a blacksmith
+ * A representation of the session between a player and a scrapper
*/
public class SalvageSession extends Session implements Runnable {
@@ -38,28 +35,19 @@ public class SalvageSession extends Session implements Runnable {
/**
* Instantiates a new session
*
- * @param scrapperTrait
A reference to the blacksmith trait
+ * @param scrapperTrait
A reference to the scrapper trait
* @param player
The player initiating the session
- * @param npc
The Blacksmith NPC involved in the session
+ * @param npc
The scrapper NPC involved in the session
* @param config
The config to use for the session
*/
SalvageSession(@NotNull ScrapperTrait scrapperTrait, @NotNull Player player, @NotNull NPC npc,
- @NotNull ScrapperNPCSettings config) {
+ @NotNull ScrapperNPCSettings config, @NotNull List salvage) {
super(player);
this.scrapperTrait = scrapperTrait;
this.npc = npc;
this.itemToSalvage = player.getInventory().getItemInMainHand();
this.config = config;
-
- // Get the salvage, for the item, but ignore some materials if set, and the item isn't at full durability
- Set ignoredSalvage = BlacksmithPlugin.getInstance().getGlobalScrapperSettings().getIgnoredSalvage(
- this.itemToSalvage.getType());
- if (ignoredSalvage == null || ItemHelper.getDamage(this.itemToSalvage) == 0) {
- ignoredSalvage = new HashSet<>();
- }
- this.salvage = SalvageHelper.getSalvage(BlacksmithPlugin.getInstance().getServer(), this.itemToSalvage,
- ignoredSalvage);
-
+ this.salvage = salvage;
this.enchantmentLevels = SalvageHelper.getTotalEnchantmentLevels(this.itemToSalvage);
}
@@ -71,7 +59,11 @@ public class SalvageSession extends Session implements Runnable {
sendNPCMessage(this.npc, player, config.getItemChangedMessage());
return true;
}
- // TODO: Add economy check
+
+ if (EconomyManager.cannotPayForSalvage(player)) {
+ sendNPCMessage(this.npc, player, config.getInsufficientFundsMessage());
+ return true;
+ }
return false;
}
@@ -93,7 +85,7 @@ public class SalvageSession extends Session implements Runnable {
public void run() {
sendNPCMessage(this.npc, player, salvageItem() ? config.getSuccessMessage() : config.getFailMessage());
- //Stop the reforged item from displaying in the blacksmith's hand
+ //Stop the reforged item from displaying in the scrapper's hand
if (npc.getEntity() instanceof Player) {
((Player) npc.getEntity()).getInventory().setItemInMainHand(null);
} else {
@@ -126,10 +118,10 @@ public class SalvageSession extends Session implements Runnable {
}
/**
- * The method to run when a blacksmith fails re-forging an item
+ * The method to run when a crapper fails salvaging an item
*/
private void failSalvage() {
- if (itemToSalvage instanceof Damageable) {
+ if (itemToSalvage.getItemMeta() instanceof Damageable) {
//Damage the item
short currentItemDurability = ItemHelper.getDurability(itemToSalvage);
short newDurability = (short) (currentItemDurability + (currentItemDurability * random.nextInt(8)));
@@ -150,7 +142,7 @@ public class SalvageSession extends Session implements Runnable {
*/
private void giveSalvage() {
// TODO: Find a better calculation than 1 enchantment level = 1 exp level
- // Gives the player back some of the XP used on an item
+ // Gives the player back some of the EXP used on an item
player.giveExpLevels(enchantmentLevels);
if (config.getDropItem() || !player.isOnline() || player.getInventory().firstEmpty() == -1) {
// If the player isn't online, or the player cannot fit the item, drop the item to prevent it from
diff --git a/src/main/java/net/knarcraft/blacksmith/trait/ScrapperTrait.java b/src/main/java/net/knarcraft/blacksmith/trait/ScrapperTrait.java
index 9e06130..4eb7742 100644
--- a/src/main/java/net/knarcraft/blacksmith/trait/ScrapperTrait.java
+++ b/src/main/java/net/knarcraft/blacksmith/trait/ScrapperTrait.java
@@ -4,16 +4,21 @@ import net.citizensnpcs.api.util.DataKey;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.config.scrapper.ScrapperNPCSettings;
import net.knarcraft.blacksmith.config.scrapper.ScrapperSetting;
+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 org.apache.commons.lang.NotImplementedException;
import org.bukkit.Bukkit;
import org.bukkit.Material;
+import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import static net.knarcraft.blacksmith.formatting.BlacksmithStringFormatter.sendNPCMessage;
@@ -22,8 +27,6 @@ import static net.knarcraft.blacksmith.formatting.BlacksmithStringFormatter.send
*/
public class ScrapperTrait extends CustomTrait {
- //TODO: A scrapper will take items and turn them into the base ingredients
- //TODO: If an item is enchanted, give an appropriate amount of exp
private final ScrapperNPCSettings config;
/**
@@ -37,15 +40,6 @@ public class ScrapperTrait extends CustomTrait {
super.setTraitSettings(this.config);
}
- /**
- * Gets the current settings for this NPC
- *
- * @return
The current settings for this NPC
- */
- public ScrapperNPCSettings getSettings() {
- return config;
- }
-
/**
* Loads all config values stored in citizens' config file for this NPC
*
@@ -75,25 +69,58 @@ public class ScrapperTrait extends CustomTrait {
ItemStack itemInHand = player.getInventory().getItemInMainHand();
List salvageAbleItems = this.config.getSalvageAbleItems();
boolean extended = this.config.extendedSalvageEnabled();
+
+ List salvage = getSalvage(player.getServer(), itemInHand, salvageAbleItems, extended);
+
// If extended mode is disabled, only allow repairable items to be salvaged
- boolean notHoldingSalvageable = !ItemHelper.isSalvageable(player.getServer(), itemInHand) ||
- (!salvageAbleItems.isEmpty() && !salvageAbleItems.contains(itemInHand.getType())) ||
- (!extended && !ItemHelper.isRepairable(itemInHand));
+ boolean notHoldingSalvageable = salvage == null || salvage.isEmpty();
if (notHoldingSalvageable) {
String invalidMessage = StringFormatter.replacePlaceholder(config.getInvalidItemMessage(),
"{title}", config.getScrapperTitle());
sendNPCMessage(this.npc, player, invalidMessage);
return;
}
- // TODO: Mark the session start
- // TODO: Initialize a new session
- // TODO: Tell player about the required cost
- throw new NotImplementedException();
+
+ //Start a new scrapper session for the player
+ currentSessionStartTime = System.currentTimeMillis();
+ session = new SalvageSession(this, player, npc, config, salvage);
+ //Tell the player the cost of repairing the item
+ String cost = EconomyManager.formatScrapperCost();
+ sendNPCMessage(this.npc, player, config.getCostMessage().replace("{cost}", cost));
}
@Override
protected boolean showExactTime() {
- return BlacksmithPlugin.getInstance().getGlobalScrapperSettings().getShowExactTime();
+ return BlacksmithPlugin.getInstance().getGlobalScrapperSettings().showExactTime();
+ }
+
+ /**
+ * Gets salvage for an item, if it's salvage-able
+ *
+ * @param server
The server to get recipes from
+ * @param item
The item to calculate salvage for
+ * @param salvageAbleItems
The items this scrapper can salvage
+ * @param extended
Whether extended salvage is enabled
+ * @return
The possible salvage, or null if not salvage-able
+ */
+ @Nullable
+ private List getSalvage(@NotNull Server server, @NotNull ItemStack item,
+ @NotNull List salvageAbleItems, boolean extended) {
+ boolean isSalvageable = (extended || ItemHelper.isRepairable(item)) && ItemHelper.isSalvageable(server, item) &&
+ (salvageAbleItems.isEmpty() || salvageAbleItems.contains(item.getType()));
+ if (!isSalvageable) {
+ return null;
+ }
+
+ // Get the salvage, for the item, but ignore some materials if set, and the item isn't at full durability
+ Set ignoredSalvage = BlacksmithPlugin.getInstance().getGlobalScrapperSettings().getIgnoredSalvage(
+ item.getType());
+ // Don't ignore salvage for fully repaired items
+ if (ignoredSalvage == null || ItemHelper.getDamage(item) == 0) {
+ ignoredSalvage = new HashSet<>();
+ }
+
+ return SalvageHelper.getSalvage(BlacksmithPlugin.getInstance().getServer(), item, ignoredSalvage, extended);
}
}
diff --git a/src/main/java/net/knarcraft/blacksmith/util/ItemHelper.java b/src/main/java/net/knarcraft/blacksmith/util/ItemHelper.java
index 93a0768..232bd39 100644
--- a/src/main/java/net/knarcraft/blacksmith/util/ItemHelper.java
+++ b/src/main/java/net/knarcraft/blacksmith/util/ItemHelper.java
@@ -8,7 +8,6 @@ import org.bukkit.inventory.CraftingRecipe;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.Recipe;
-import org.bukkit.inventory.SmithingTransformRecipe;
import org.bukkit.inventory.meta.Damageable;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
@@ -46,7 +45,11 @@ public final class ItemHelper {
* @return
The max durability of the item
*/
public static short getMaxDurability(@NotNull ItemStack itemStack) {
- return itemStack.getType().getMaxDurability();
+ if (itemStack.getItemMeta() instanceof Damageable) {
+ return itemStack.getType().getMaxDurability();
+ } else {
+ return 0;
+ }
}
/**
@@ -56,12 +59,11 @@ public final class ItemHelper {
* @return
The durability of the item
*/
public static short getDurability(@NotNull ItemStack itemStack) {
- Damageable damageable = (Damageable) itemStack.getItemMeta();
- int maxDurability = getMaxDurability(itemStack);
- if (damageable != null) {
+ if (itemStack.getItemMeta() instanceof Damageable damageable) {
+ int maxDurability = getMaxDurability(itemStack);
return (short) (maxDurability - damageable.getDamage());
} else {
- return (short) maxDurability;
+ return 0;
}
}
@@ -72,8 +74,7 @@ public final class ItemHelper {
* @return
The damage done to the item
*/
public static short getDamage(@NotNull ItemStack itemStack) {
- Damageable damageable = (Damageable) itemStack.getItemMeta();
- if (damageable != null) {
+ if (itemStack.getItemMeta() instanceof Damageable damageable) {
return (short) damageable.getDamage();
} else {
return 0;
@@ -173,26 +174,49 @@ public final class ItemHelper {
* @return
True if the item can be salvaged
*/
public static boolean isSalvageable(@NotNull Server server, @NotNull ItemStack item) {
- for (Recipe recipe : server.getRecipesFor(item)) {
- // Only crafting recipes, and smithing transform recipes (diamond -> netherite) are allowed.
- if (recipe instanceof CraftingRecipe || recipe instanceof SmithingTransformRecipe &&
- item.getAmount() >= recipe.getResult().getAmount()) {
+ 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
The server to get recipes from
+ * @param item
The item to check
+ * @return
The number of items required for salvage, or -1 if the recipe was not found
+ */
+ 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
*
* @param materialName
The material name or material wildcard to match
+ * @param extended
Whether to use an extended match, allowing any material
* @return
The matched material(s)
*/
- public static @NotNull List getWildcardMatch(@NotNull String materialName) {
+ public static @NotNull List getWildcardMatch(@NotNull String materialName, boolean extended) {
String search = InputParsingHelper.regExIfy(materialName);
List materials = new ArrayList<>();
- for (Material material : ItemHelper.getAllReforgeAbleMaterials()) {
+ List all;
+ if (extended) {
+ all = List.of(Material.values());
+ } else {
+ all = ItemHelper.getAllReforgeAbleMaterials();
+ }
+ for (Material material : all) {
if (material.name().matches(search)) {
materials.add(material);
}
@@ -249,7 +273,7 @@ public final class ItemHelper {
}
/**
- * Replaces smoth presets in the given list of strings
+ * Replaces smith presets in the given list of strings
*
* @param stringList
The value specified by a user
* @return
The value with smith presets replaced
diff --git a/src/main/java/net/knarcraft/blacksmith/util/SalvageHelper.java b/src/main/java/net/knarcraft/blacksmith/util/SalvageHelper.java
index e25e411..a60ef18 100644
--- a/src/main/java/net/knarcraft/blacksmith/util/SalvageHelper.java
+++ b/src/main/java/net/knarcraft/blacksmith/util/SalvageHelper.java
@@ -22,6 +22,8 @@ import java.util.Random;
*/
public final class SalvageHelper {
+ private static final Random random = new Random();
+
/**
* Gets the sum of all enchantment levels for the given item
*
@@ -44,19 +46,22 @@ public final class SalvageHelper {
* @param server
The server to get recipes from
* @param salvagedItem
The item stack to salvage
* @param ignoredSalvage
Any material which should not be returned as part of the salvage.
+ * @param extended
Whether to enable extended salvage, ignoring the repairable restriction
* @return
The items to return to the user, or null if not salvageable
*/
public static @Nullable List getSalvage(@NotNull Server server, @Nullable ItemStack salvagedItem,
- @NotNull Collection ignoredSalvage) {
+ @NotNull Collection ignoredSalvage, boolean extended) {
if (salvagedItem == null || salvagedItem.getAmount() < 1 ||
- !ItemHelper.isRepairable(salvagedItem)) {
+ (!extended && !ItemHelper.isRepairable(salvagedItem))) {
return null;
}
- for (Recipe recipe : server.getRecipesFor(salvagedItem)) {
- List salvage = getRecipeSalvage(recipe, salvagedItem, ignoredSalvage);
- if (salvage != null) {
- return salvage;
+ for (Recipe recipe : server.getRecipesFor(new ItemStack(salvagedItem.getType(), salvagedItem.getAmount()))) {
+ if (recipe instanceof ShapedRecipe || recipe instanceof ShapelessRecipe) {
+ List salvage = getRecipeSalvage(recipe, salvagedItem, ignoredSalvage);
+ if (salvage != null && !salvage.isEmpty()) {
+ return salvage;
+ }
}
}
@@ -88,15 +93,7 @@ public final class SalvageHelper {
//Purge any ignored salvage to only calculate salvage using the remaining items
ingredients.removeIf((item) -> ignoredSalvage.contains(item.getType()));
- Random random = new Random();
-
- //Make sure to give salvage for all items if a stack > 1 is provided
- List allSalvage = new ArrayList<>();
- for (int i = 0; i < salvagedItem.getAmount(); i++) {
- allSalvage.addAll(getSalvage(copyItems(ingredients), salvagedItem, random));
- }
-
- return combineStacks(allSalvage);
+ return combineStacks(getSalvage(copyItems(ingredients), salvagedItem));
}
/**
@@ -120,21 +117,30 @@ public final class SalvageHelper {
*
* @param recipeItems
All items required for crafting the item to salvage
* @param salvagedItem
The item to be salvaged
- * @param random
The randomness generator to use
* @return
The items to be returned to the user as salvage
*/
- private static @NotNull List getSalvage(@NotNull List recipeItems,
- @NotNull ItemStack salvagedItem, @NotNull Random random) {
- double percentageRemaining = (ItemHelper.getDurability(salvagedItem) /
- (double) ItemHelper.getMaxDurability(salvagedItem));
+ @NotNull
+ private static List getSalvage(@NotNull List recipeItems,
+ @NotNull ItemStack salvagedItem) {
+ int durability = ItemHelper.getDurability(salvagedItem);
+ int maxDurability = ItemHelper.getMaxDurability(salvagedItem);
+
+ // Prevent divide by zero for items that don't have a set max durability
+ if (maxDurability == 0) {
+ maxDurability = 1;
+ durability = 1;
+ }
+
+ double percentageRemaining = (double) durability / maxDurability;
+
int totalItems = totalItemAmount(recipeItems);
//Get the amount of recipe items to be returned
int itemsToReturn = (int) Math.floor(percentageRemaining * totalItems);
int bound = recipeItems.size();
- List salvage = new ArrayList<>();
+ List salvage = new ArrayList<>();
for (int i = 0; i < itemsToReturn; i++) {
- int itemIndex = random.nextInt(bound);
+ int itemIndex = SalvageHelper.random.nextInt(bound);
ItemStack itemStack = recipeItems.get(itemIndex);
//Make sure to never give more of one item than the amount which exists in the recipe
@@ -192,7 +198,8 @@ public final class SalvageHelper {
* @param shapedRecipe
The shaped recipe to get ingredients for
* @return
The items contained in the recipe
*/
- private static @NotNull List getIngredients(@NotNull ShapedRecipe shapedRecipe) {
+ @NotNull
+ private static List getIngredients(@NotNull ShapedRecipe shapedRecipe) {
List ingredients = new ArrayList<>();
Map ingredientMap = shapedRecipe.getIngredientMap();
//The shape is a list of the three rows' strings. Each string contains 3 characters.
diff --git a/src/main/resources/config-migrations.txt b/src/main/resources/config-migrations.txt
index 8b0b84c..8b9f634 100644
--- a/src/main/resources/config-migrations.txt
+++ b/src/main/resources/config-migrations.txt
@@ -5,4 +5,5 @@ scrapper.defaults.delaysInSeconds.maximum=scrapper.defaults.maxSalvageWaitTimeSe
scrapper.defaults.delaysInSeconds.salvageCoolDown=scrapper.defaults.salvageCoolDownSeconds
blacksmith.defaults.delaysInSeconds.minimum=blacksmith.defaults.minReforgeWaitTimeSeconds
blacksmith.defaults.delaysInSeconds.maximum=blacksmith.defaults.maxReforgeWaitTimeSeconds
-blacksmith.defaults.delaysInSeconds.reforgeCoolDown=blacksmith.defaults.reforgeCoolDownSeconds
\ No newline at end of file
+blacksmith.defaults.delaysInSeconds.reforgeCoolDown=blacksmith.defaults.reforgeCoolDownSeconds
+blacksmith.defaults.enchantmentBlocklist=blacksmith.defaults.enchantmentBlockList
\ No newline at end of file
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index 1d1c7f2..744a129 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -48,7 +48,7 @@ blacksmith:
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" ]
+ enchantmentBlockList: [ "binding_curse", "mending", "vanishing_curse" ]
# The chance to fail reforging an item, which only repairs the item a tiny bit or not at all (0-100)
failReforgeChance: 10 # Default = 10%
@@ -114,7 +114,7 @@ blacksmith:
# Settings for the scrapper trait
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:
# Exact time displays the exact number of seconds and minutes remaining as part of the scrapping cool-down and
# scrapping delay messages, instead of just vaguely hinting at the remaining time.
@@ -123,12 +123,16 @@ scrapper:
# Whether enchanted salvaged items should return some amount of exp upon salvage
giveExperience: true
- # Items ignored during salvage calculation. This follows the format: "MATERIAL[,MATERIAL2][,MATERIAL3]:IGNORED", so
- # the material or materials listed will ignore the material specified after the ":" when calculating salvage
+ # Items ignored during salvage calculation. This follows the format:
+ # "MATERIAL[;MATERIAL2][;MATERIAL3]:IGNORED_MATERIAL[;IGNORED_MATERIAL2]",
+ # so the material or materials listed will ignore the material specified after the ":" when calculating salvage
# (* matches any character). This causes the player to lose some items during salvaging, but can prevent cases
# where a diamond pickaxe is salvaged and only sticks are returned.
ignoredSalvage:
- - "*_SHOVEL,*_PICKAXE,*_AXE,*_HOE,*_SWORD:STICK"
+ - "*_SHOVEL;*_PICKAXE;*_AXE;*_HOE;*_SWORD:STICK"
+
+ # The cost of using the scrapper
+ basePrice: 0
# 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
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index 049ed54..14c8892 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -13,7 +13,7 @@ commands:
permission: blacksmith.edit
usage: /
[new value]
description: Used for configuring the selected blacksmith NPC
- blacksmithconfig:
+ blacksmithConfig:
permission: blacksmith.admin
usage: /
[new value]
description: Used for configuring default blacksmith settings, or global blacksmith settings
@@ -21,7 +21,7 @@ commands:
permission: blacksmith.edit
usage: /
[new value]
description: Used for configuring the selected scrapper NPC
- scrapperconfig:
+ scrapperConfig:
permission: blacksmith.admin
usage: /
[new value]
description: Used for configuring default scrapper settings, or global scrapper settings
diff --git a/src/main/resources/strings.yml b/src/main/resources/strings.yml
index d699f70..7f021e7 100644
--- a/src/main/resources/strings.yml
+++ b/src/main/resources/strings.yml
@@ -1,5 +1,5 @@
# The english translation of internal strings. To add your own language, copy everything below, and change "en" to your
-# own language's code, and change each string. Afterwards, copy this file to Blacksmith's plugin folder, and change the
+# own language's code, and change each string. Afterward, copy this file to Blacksmith's plugin folder, and change the
# language in config.yml to your language's language code.
en:
# The format used to display when a setting's value has been changed
diff --git a/src/test/java/net/knarcraft/blacksmith/CustomServerMock.java b/src/test/java/net/knarcraft/blacksmith/CustomServerMock.java
index 32b32fc..7cee7cb 100644
--- a/src/test/java/net/knarcraft/blacksmith/CustomServerMock.java
+++ b/src/test/java/net/knarcraft/blacksmith/CustomServerMock.java
@@ -16,7 +16,9 @@ import java.util.List;
*/
public class CustomServerMock extends ServerMock {
- public @NotNull List getRecipesFor(@NotNull ItemStack itemStack) {
+ @Override
+ @NotNull
+ public List getRecipesFor(@NotNull ItemStack itemStack) {
List validRecipes = new ArrayList<>();
if (itemStack.getType() == Material.DIAMOND_PICKAXE) {
ShapedRecipe recipe = new ShapedRecipe(NamespacedKey.minecraft("diamond_pickaxe"), itemStack);
@@ -24,6 +26,12 @@ public class CustomServerMock extends ServerMock {
recipe.setIngredient('d', Material.DIAMOND);
recipe.setIngredient('s', Material.STICK);
validRecipes.add(recipe);
+ } else if (itemStack.getType() == Material.TNT) {
+ ShapedRecipe recipe = new ShapedRecipe(NamespacedKey.minecraft("tnt"), itemStack);
+ recipe.shape("gsg", "sgs", "gsg");
+ recipe.setIngredient('g', Material.GUNPOWDER);
+ recipe.setIngredient('s', Material.SAND);
+ validRecipes.add(recipe);
}
return validRecipes;
}
diff --git a/src/test/java/net/knarcraft/blacksmith/util/ItemHelperTest.java b/src/test/java/net/knarcraft/blacksmith/util/ItemHelperTest.java
index 7ba01ad..5515598 100644
--- a/src/test/java/net/knarcraft/blacksmith/util/ItemHelperTest.java
+++ b/src/test/java/net/knarcraft/blacksmith/util/ItemHelperTest.java
@@ -86,7 +86,7 @@ public class ItemHelperTest {
@Test
public void getAllReforgeAbleMaterialsTest() {
List materials = ItemHelper.getAllReforgeAbleMaterials();
- assertTrue(materials.size() > 0);
+ assertFalse(materials.isEmpty());
for (Material material : materials) {
assertTrue(ItemHelper.getMaxDurability(new ItemStack(material, 1)) > 0);
diff --git a/src/test/java/net/knarcraft/blacksmith/util/SalvageHelperTest.java b/src/test/java/net/knarcraft/blacksmith/util/SalvageHelperTest.java
index e39cf73..35ebc5a 100644
--- a/src/test/java/net/knarcraft/blacksmith/util/SalvageHelperTest.java
+++ b/src/test/java/net/knarcraft/blacksmith/util/SalvageHelperTest.java
@@ -41,14 +41,14 @@ public class SalvageHelperTest {
@Test
public void getNullForInvalidItemTest() {
//Assert that a non-reforge-able item will return null
- assertNull(SalvageHelper.getSalvage(server, new ItemStack(Material.POTATO, 1), new ArrayList<>()));
+ assertNull(SalvageHelper.getSalvage(server, new ItemStack(Material.POTATO, 1), new ArrayList<>(), false));
}
@Test
public void getNullForLessThanOneItemTest() {
//Assert that 0 or 1 items will return null
- assertNull(SalvageHelper.getSalvage(server, new ItemStack(Material.IRON_AXE, 0), new ArrayList<>()));
- assertNull(SalvageHelper.getSalvage(server, new ItemStack(Material.IRON_SWORD, -1), new ArrayList<>()));
+ assertNull(SalvageHelper.getSalvage(server, new ItemStack(Material.IRON_AXE, 0), new ArrayList<>(), false));
+ assertNull(SalvageHelper.getSalvage(server, new ItemStack(Material.IRON_SWORD, -1), new ArrayList<>(), false));
}
@Test
@@ -58,7 +58,7 @@ public class SalvageHelperTest {
expectedSalvage.add(new ItemStack(Material.STICK, 2));
ItemStack itemToSalvage = new ItemStack(Material.DIAMOND_PICKAXE, 1);
//Note: Conversion to sets makes sure the order doesn't matter
- List salvage = SalvageHelper.getSalvage(server, itemToSalvage, new ArrayList<>());
+ List salvage = SalvageHelper.getSalvage(server, itemToSalvage, new ArrayList<>(), false);
if (salvage == null) {
fail();
} else {
@@ -67,13 +67,13 @@ public class SalvageHelperTest {
}
@Test
- public void getSeveralFullSalvageTest() {
+ public void getExtendedSalvageTest() {
Set expectedSalvage = new HashSet<>();
- expectedSalvage.add(new ItemStack(Material.DIAMOND, 21));
- expectedSalvage.add(new ItemStack(Material.STICK, 14));
- ItemStack itemToSalvage = new ItemStack(Material.DIAMOND_PICKAXE, 7);
+ expectedSalvage.add(new ItemStack(Material.GUNPOWDER, 5));
+ expectedSalvage.add(new ItemStack(Material.SAND, 4));
+ ItemStack itemToSalvage = new ItemStack(Material.TNT, 1);
//Note: Conversion to sets makes sure the order doesn't matter
- List salvage = SalvageHelper.getSalvage(server, itemToSalvage, new ArrayList<>());
+ List salvage = SalvageHelper.getSalvage(server, itemToSalvage, new ArrayList<>(), true);
if (salvage == null) {
fail();
} else {
@@ -93,7 +93,7 @@ public class SalvageHelperTest {
damageable.setDamage(100);
}
itemToSalvage.setItemMeta(meta);
- List salvage = SalvageHelper.getSalvage(server, itemToSalvage, new ArrayList<>());
+ List salvage = SalvageHelper.getSalvage(server, itemToSalvage, new ArrayList<>(), false);
//Assert that some items are given
assertNotEquals(salvage, new ArrayList<>());
//Assert that a damaged item won't give full salvage
@@ -113,7 +113,7 @@ public class SalvageHelperTest {
damageable.setDamage(100);
}
itemToSalvage.setItemMeta(meta);
- List salvage = SalvageHelper.getSalvage(server, itemToSalvage, ignoredSalvage);
+ List salvage = SalvageHelper.getSalvage(server, itemToSalvage, ignoredSalvage, false);
//Assert that some items are given
assertNotEquals(salvage, new ArrayList<>());
//Assert that a damaged diamond pickaxe with sticks ignored returns 2 diamonds a salvage