From 8e5d4c7a618aebbe0d96e28af3d69498741184c7 Mon Sep 17 00:00:00 2001 From: EpicKnarvik97 Date: Fri, 13 Jan 2023 20:34:34 +0100 Subject: [PATCH] Adds a helper class for calculating random item salvage #18 --- .../knarcraft/blacksmith/util/ItemHelper.java | 15 +- .../blacksmith/util/SalvageHelper.java | 167 ++++++++++++++++++ 2 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 src/main/java/net/knarcraft/blacksmith/util/SalvageHelper.java 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; + } + +}