/*
* Copyright or © or Copr. Amaury Carrade (2014 - 2016)
*
* http://amaury.carrade.eu
*
* This software is governed by the CeCILL-B license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL-B
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL-B license and that you accept its terms.
*/
package eu.carrade.amaury.UHCReloaded.recipes;
import org.bukkit.inventory.FurnaceRecipe;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.Recipe;
import org.bukkit.inventory.ShapedRecipe;
import org.bukkit.inventory.ShapelessRecipe;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* Utility class to compare Bukkit recipes.<br>
* Useful for identifying your recipes in events, where recipes are re-generated in a different manner.
*
* @version R1.3
* @author Digi
*/
public class RecipeUtil
{
/**
* The wildcard data value for ingredients.<br>
* If this is used as data value on an ingredient it will accept any data value.
*/
public static final short DATA_WILDCARD = Short.MAX_VALUE;
/**
* Checks if both recipes are equal.<br>
* Compares both ingredients and results.<br>
* <br>
* NOTE: If both arguments are null it returns true.
*
* @param recipe1 the first recipe
* @param recipe2 the second recipe
* @return true if ingredients and results match, false otherwise.
* @throws IllegalArgumentException
* if recipe is other than ShapedRecipe, ShapelessRecipe or FurnaceRecipe.
*/
public static boolean areEqual(Recipe recipe1, Recipe recipe2)
{
return recipe1 == recipe2 || !(recipe1 == null || recipe2 == null) && recipe1.getResult().equals(recipe2.getResult()) && match(recipe1, recipe2);
}
/**
* Checks if recipes are similar.<br>
* Only checks ingredients, not results.<br>
* <br>
* NOTE: If both arguments are null it returns true. <br>
*
* @param recipe1 the first recipe
* @param recipe2 the second recipe
* @return true if ingredients match, false otherwise.
* @throws IllegalArgumentException
* if recipe is other than ShapedRecipe, ShapelessRecipe or FurnaceRecipe.
*/
public static boolean areSimilar(Recipe recipe1, Recipe recipe2)
{
return recipe1 == recipe2 || !(recipe1 == null || recipe2 == null) && match(recipe1, recipe2);
}
private static boolean match(Recipe recipe1, Recipe recipe2)
{
if (recipe1 instanceof ShapedRecipe)
{
if (!(recipe2 instanceof ShapedRecipe))
{
return false; // if other recipe is not the same type then they're not equal.
}
ShapedRecipe r1 = (ShapedRecipe) recipe1;
ShapedRecipe r2 = (ShapedRecipe) recipe2;
// convert both shapes and ingredient maps to common ItemStack array.
ItemStack[] matrix1 = shapeToMatrix(r1.getShape(), r1.getIngredientMap());
ItemStack[] matrix2 = shapeToMatrix(r2.getShape(), r2.getIngredientMap());
if (!Arrays.equals(matrix1, matrix2)) // compare arrays and if they don't match run another check with one shape mirrored.
{
mirrorMatrix(matrix1);
return Arrays.equals(matrix1, matrix2);
}
return true; // ingredients match.
}
else if (recipe1 instanceof ShapelessRecipe)
{
if (!(recipe2 instanceof ShapelessRecipe))
{
return false; // if other recipe is not the same type then they're not equal.
}
ShapelessRecipe r1 = (ShapelessRecipe) recipe1;
ShapelessRecipe r2 = (ShapelessRecipe) recipe2;
// get copies of the ingredient lists
List<ItemStack> find = r1.getIngredientList();
List<ItemStack> compare = r2.getIngredientList();
if (find.size() != compare.size())
{
return false; // if they don't have the same amount of ingredients they're not equal.
}
for (ItemStack item : compare)
{
if (!find.remove(item))
{
return false; // if ingredient wasn't removed (not found) then they're not equal.
}
}
return find.isEmpty(); // if there are any ingredients not removed then they're not equal.
}
else if (recipe1 instanceof FurnaceRecipe)
{
if (!(recipe2 instanceof FurnaceRecipe))
{
return false; // if other recipe is not the same type then they're not equal.
}
FurnaceRecipe r1 = (FurnaceRecipe) recipe1;
FurnaceRecipe r2 = (FurnaceRecipe) recipe2;
//return (r1.getInput().equals(r2.getInput()));
return r1.getInput().getType() == r2.getInput().getType();
}
else
{
throw new IllegalArgumentException("Unsupported recipe type: '" + recipe1 + "', update this class!");
}
}
private static ItemStack[] shapeToMatrix(String[] shape, Map<Character, ItemStack> map)
{
ItemStack[] matrix = new ItemStack[9];
int slot = 0;
for (int r = 0; r < shape.length; r++)
{
for (char col : shape[r].toCharArray())
{
matrix[slot] = map.get(col);
slot++;
}
slot = ((r + 1) * 3);
}
return matrix;
}
private static void mirrorMatrix(ItemStack[] matrix)
{
ItemStack tmp;
for (int r = 0; r < 3; r++)
{
tmp = matrix[(r * 3)];
matrix[(r * 3)] = matrix[(r * 3) + 2];
matrix[(r * 3) + 2] = tmp;
}
}
/**
* Returns the list of the ingredients of the given recipe.
*
* @author Amaury Carrade
*
* @param recipe The recipe to analyze.
* @return A list of the ingredients.
*/
public static List<ItemStack> getListOfIngredients(Recipe recipe)
{
List<ItemStack> listOfItems;
if (recipe instanceof ShapelessRecipe)
{
listOfItems = ((ShapelessRecipe) recipe).getIngredientList();
}
else
{
try
{
listOfItems = new LinkedList<>(((ShapedRecipe) recipe).getIngredientMap().values());
}
catch (NullPointerException e) // If the list of items is null
{
listOfItems = new LinkedList<>(); // empty list
}
}
return listOfItems;
}
}