diff --git a/src/main/java/net/knarcraft/blacksmith/util/ItemHelper.java b/src/main/java/net/knarcraft/blacksmith/util/ItemHelper.java
index 67915b5..4010b30 100644
--- a/src/main/java/net/knarcraft/blacksmith/util/ItemHelper.java
+++ b/src/main/java/net/knarcraft/blacksmith/util/ItemHelper.java
@@ -8,12 +8,25 @@ import org.bukkit.inventory.meta.Damageable;
import java.util.ArrayList;
import java.util.List;
+/**
+ * A helper class for getting information about items
+ */
public final class ItemHelper {
private ItemHelper() {
}
+ /**
+ * Gets the max durability of an item
+ *
+ * @param itemStack
The item to get the durability of
+ * @return The max durability of the item
+ */
+ public static short getMaxDurability(ItemStack itemStack) {
+ return itemStack.getType().getMaxDurability();
+ }
+
/**
* Gets the current durability of the given item
*
@@ -22,7 +35,7 @@ public final class ItemHelper {
*/
public static short getDurability(ItemStack itemStack) {
Damageable damageable = (Damageable) itemStack.getItemMeta();
- int maxDurability = itemStack.getType().getMaxDurability();
+ int maxDurability = getMaxDurability(itemStack);
if (damageable != null) {
return (short) (maxDurability - damageable.getDamage());
} else {
diff --git a/src/main/java/net/knarcraft/blacksmith/util/SalvageHelper.java b/src/main/java/net/knarcraft/blacksmith/util/SalvageHelper.java
new file mode 100644
index 0000000..a3ac408
--- /dev/null
+++ b/src/main/java/net/knarcraft/blacksmith/util/SalvageHelper.java
@@ -0,0 +1,167 @@
+package net.knarcraft.blacksmith.util;
+
+import net.knarcraft.blacksmith.trait.BlacksmithTrait;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.Recipe;
+import org.bukkit.inventory.ShapedRecipe;
+import org.bukkit.inventory.ShapelessRecipe;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+/**
+ * A helper class for deciding the salvage returned if salvaging an item
+ */
+public final class SalvageHelper {
+
+ /**
+ * Gets the items to return if salvaging the given item stack
+ *
+ * Note: Only items craft-able in a crafting table are salvageable. Netherite gear is therefore not salvageable.
+ *
+ * @param salvagedItem The item stack to salvage
+ * @return The items to return to the user, or null if not salvageable
+ */
+ public static List getSalvage(ItemStack salvagedItem) {
+ if (salvagedItem == null || salvagedItem.getAmount() < 1 ||
+ !BlacksmithTrait.isRepairable(salvagedItem)) {
+ return null;
+ }
+
+ for (Recipe recipe : Bukkit.getServer().getRecipesFor(salvagedItem)) {
+ List salvage = getRecipeSalvage(recipe, salvagedItem);
+ if (salvage != null) {
+ return salvage;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Gets the salvage resulting from the given recipe and the given item
+ *
+ * @param recipe The recipe to get salvage for
+ * @param salvagedItem The item to be salvaged
+ * @return A list of items, or null if not a valid type of recipe
+ */
+ private static List getRecipeSalvage(Recipe recipe, ItemStack salvagedItem) {
+ List ingredients;
+ if (recipe instanceof ShapedRecipe shapedRecipe) {
+ ingredients = getIngredients(shapedRecipe);
+ } else if (recipe instanceof ShapelessRecipe shapelessRecipe) {
+ ingredients = shapelessRecipe.getIngredientList();
+ } else {
+ //Recipes other than crafting shouldn't be considered for salvaging
+ return null;
+ }
+ //Make things easier by eliminating identical stacks
+ ingredients = combineStacks(ingredients);
+
+ //Make sure to give salvage for all items if a stack > 1 is provided
+ List salvage = new ArrayList<>();
+ for (int i = 0; i < salvagedItem.getAmount(); i++) {
+ salvage.addAll(getSalvage(ingredients, salvagedItem));
+ }
+
+ return combineStacks(salvage);
+ }
+
+ /**
+ * Gets the salvage resulting from the given item, and the given recipe items
+ *
+ * @param recipeItems All items required for crafting the item to salvage
+ * @param salvagedItem The item to be salvaged
+ * @return The items to be returned to the user as salvage
+ */
+ private static List getSalvage(List recipeItems, ItemStack salvagedItem) {
+ double percentageRemaining = (ItemHelper.getDurability(salvagedItem) /
+ (double) ItemHelper.getMaxDurability(salvagedItem));
+ int totalItems = totalItems(recipeItems);
+ //Get the amount of recipe items to be returned
+ int itemsToReturn = (int) Math.floor(percentageRemaining * totalItems);
+ Random random = new Random();
+ int bound = recipeItems.size();
+ List salvage = new ArrayList<>();
+
+ for (int i = 0; i < itemsToReturn; i++) {
+ int itemIndex = 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
+ if (itemStack.getAmount() <= 0) {
+ i--;
+ continue;
+ }
+ itemStack.setAmount(itemStack.getAmount() - 1);
+
+ salvage.add(new ItemStack(itemStack.getType(), 1));
+ }
+ return salvage;
+ }
+
+ /**
+ * Gets the total sum of items in the given list of items
+ *
+ * @param items The items to get the sum of
+ * @return The total number of items
+ */
+ private static int totalItems(List items) {
+ int sum = 0;
+ for (ItemStack itemStack : items) {
+ sum += itemStack.getAmount();
+ }
+ return sum;
+ }
+
+ /**
+ * Combines all items of the same type in the given list
+ *
+ * Basically, if the input is two item stacks containing one diamond each, the output will be an item stack with
+ * two diamonds instead.
+ *
+ * @param items The items to combine
+ * @return The given items, but combined
+ */
+ private static List combineStacks(List items) {
+ Map itemAmounts = new HashMap<>();
+ for (ItemStack item : items) {
+ Material itemType = item.getType();
+ itemAmounts.put(itemType, itemAmounts.getOrDefault(itemType, 0) + 1);
+ }
+
+ List combined = new ArrayList<>();
+ for (Material material : itemAmounts.keySet()) {
+ combined.add(new ItemStack(material, itemAmounts.get(material)));
+ }
+ return combined;
+ }
+
+ /**
+ * Gets all ingredients contained in the given shaped recipe
+ *
+ * @param shapedRecipe The shaped recipe to get ingredients for
+ * @return The items contained in the recipe
+ */
+ private static List getIngredients(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.
+ String[] shape = shapedRecipe.getShape();
+ for (String row : shape) {
+ for (int column = 0; column < row.length(); column++) {
+ ItemStack item = ingredientMap.get(row.charAt(column));
+ if (item != null && item.getType() != Material.AIR && item.getAmount() > 0) {
+ ingredients.add(item);
+ }
+ }
+ }
+ return ingredients;
+ }
+
+}