Adds a helper class for calculating random item salvage #18
All checks were successful
EpicKnarvik97/Blacksmith/pipeline/head This commit looks good

This commit is contained in:
Kristian Knarvik 2023-01-13 20:34:34 +01:00
parent 89cebb85c3
commit 8e5d4c7a61
2 changed files with 181 additions and 1 deletions

View File

@ -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 <p>The item to get the durability of</p>
* @return <p>The max durability of the item</p>
*/
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 {

View File

@ -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
*
* <p>Note: Only items craft-able in a crafting table are salvageable. Netherite gear is therefore not salvageable.</p>
*
* @param salvagedItem <p>The item stack to salvage</p>
* @return <p>The items to return to the user, or null if not salvageable</p>
*/
public static List<ItemStack> getSalvage(ItemStack salvagedItem) {
if (salvagedItem == null || salvagedItem.getAmount() < 1 ||
!BlacksmithTrait.isRepairable(salvagedItem)) {
return null;
}
for (Recipe recipe : Bukkit.getServer().getRecipesFor(salvagedItem)) {
List<ItemStack> 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 <p>The recipe to get salvage for</p>
* @param salvagedItem <p>The item to be salvaged</p>
* @return <p>A list of items, or null if not a valid type of recipe</p>
*/
private static List<ItemStack> getRecipeSalvage(Recipe recipe, ItemStack salvagedItem) {
List<ItemStack> 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<ItemStack> 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 <p>All items required for crafting the item to salvage</p>
* @param salvagedItem <p>The item to be salvaged</p>
* @return <p>The items to be returned to the user as salvage</p>
*/
private static List<ItemStack> getSalvage(List<ItemStack> 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<ItemStack> 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 <p>The items to get the sum of</p>
* @return <p>The total number of items</p>
*/
private static int totalItems(List<ItemStack> items) {
int sum = 0;
for (ItemStack itemStack : items) {
sum += itemStack.getAmount();
}
return sum;
}
/**
* Combines all items of the same type in the given list
*
* <p>Basically, if the input is two item stacks containing one diamond each, the output will be an item stack with
* two diamonds instead.</p>
*
* @param items <p>The items to combine</p>
* @return <p>The given items, but combined</p>
*/
private static List<ItemStack> combineStacks(List<ItemStack> items) {
Map<Material, Integer> itemAmounts = new HashMap<>();
for (ItemStack item : items) {
Material itemType = item.getType();
itemAmounts.put(itemType, itemAmounts.getOrDefault(itemType, 0) + 1);
}
List<ItemStack> 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 <p>The shaped recipe to get ingredients for</p>
* @return <p>The items contained in the recipe</p>
*/
private static List<ItemStack> getIngredients(ShapedRecipe shapedRecipe) {
List<ItemStack> ingredients = new ArrayList<>();
Map<Character, ItemStack> 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;
}
}