package org.bukkit.inventory; import com.google.common.base.Preconditions; import java.util.HashMap; import java.util.Map; import org.apache.commons.lang.Validate; import org.bukkit.Keyed; import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.material.MaterialData; /** * Represents a shaped (ie normal) crafting recipe. */ public class ShapedRecipe implements Recipe, Keyed { private final NamespacedKey key; private final ItemStack output; private String[] rows; private Map ingredients = new HashMap(); private String group = ""; @Deprecated public ShapedRecipe(ItemStack result) { this.key = NamespacedKey.randomKey(); this.output = new ItemStack(result); } /** * Create a shaped recipe to craft the specified ItemStack. The * constructor merely determines the result and type; to set the actual * recipe, you'll need to call the appropriate methods. * * @param key the unique recipe key * @param result The item you want the recipe to create. * @see ShapedRecipe#shape(String...) * @see ShapedRecipe#setIngredient(char, Material) * @see ShapedRecipe#setIngredient(char, Material, int) * @see ShapedRecipe#setIngredient(char, MaterialData) */ public ShapedRecipe(NamespacedKey key, ItemStack result) { Preconditions.checkArgument(key != null, "key"); this.key = key; this.output = new ItemStack(result); } /** * Set the shape of this recipe to the specified rows. Each character * represents a different ingredient; exactly what each character * represents is set separately. The first row supplied corresponds with * the upper most part of the recipe on the workbench e.g. if all three * rows are supplies the first string represents the top row on the * workbench. * * @param shape The rows of the recipe (up to 3 rows). * @return The changed recipe, so you can chain calls. */ public ShapedRecipe shape(final String... shape) { Validate.notNull(shape, "Must provide a shape"); Validate.isTrue(shape.length > 0 && shape.length < 4, "Crafting recipes should be 1, 2, 3 rows, not ", shape.length); int lastLen = -1; for (String row : shape) { Validate.notNull(row, "Shape cannot have null rows"); Validate.isTrue(row.length() > 0 && row.length() < 4, "Crafting rows should be 1, 2, or 3 characters, not ", row.length()); Validate.isTrue(lastLen == -1 || lastLen == row.length(), "Crafting recipes must be rectangular"); lastLen = row.length(); } this.rows = new String[shape.length]; for (int i = 0; i < shape.length; i++) { this.rows[i] = shape[i]; } // Remove character mappings for characters that no longer exist in the shape HashMap newIngredients = new HashMap(); for (String row : shape) { for (Character c : row.toCharArray()) { newIngredients.put(c, ingredients.get(c)); } } this.ingredients = newIngredients; return this; } /** * Sets the material that a character in the recipe shape refers to. * * @param key The character that represents the ingredient in the shape. * @param ingredient The ingredient. * @return The changed recipe, so you can chain calls. */ public ShapedRecipe setIngredient(char key, MaterialData ingredient) { return setIngredient(key, ingredient.getItemType(), ingredient.getData()); } /** * Sets the material that a character in the recipe shape refers to. * * @param key The character that represents the ingredient in the shape. * @param ingredient The ingredient. * @return The changed recipe, so you can chain calls. */ public ShapedRecipe setIngredient(char key, Material ingredient) { return setIngredient(key, ingredient, 0); } /** * Sets the material that a character in the recipe shape refers to. * * @param key The character that represents the ingredient in the shape. * @param ingredient The ingredient. * @param raw The raw material data as an integer. * @return The changed recipe, so you can chain calls. * @deprecated Magic value */ @Deprecated public ShapedRecipe setIngredient(char key, Material ingredient, int raw) { Validate.isTrue(ingredients.containsKey(key), "Symbol does not appear in the shape:", key); // -1 is the old wildcard, map to Short.MAX_VALUE as the new one if (raw == -1) { raw = Short.MAX_VALUE; } ingredients.put(key, new ItemStack(ingredient, 1, (short) raw)); return this; } /** * Get a copy of the ingredients map. * * @return The mapping of character to ingredients. */ public Map getIngredientMap() { HashMap result = new HashMap(); for (Map.Entry ingredient : ingredients.entrySet()) { if (ingredient.getValue() == null) { result.put(ingredient.getKey(), null); } else { result.put(ingredient.getKey(), ingredient.getValue().clone()); } } return result; } /** * Get the shape. * * @return The recipe's shape. */ public String[] getShape() { return rows.clone(); } /** * Get the result. * * @return The result stack. */ public ItemStack getResult() { return output.clone(); } @Override public NamespacedKey getKey() { return key; } /** * Get the group of this recipe. Recipes with the same group may be grouped * together when displayed in the client. * * @return recipe group. An empty string denotes no group. May not be null. */ public String getGroup() { return group; } /** * Set the group of this recipe. Recipes with the same group may be grouped * together when displayed in the client. * * @param group recipe group. An empty string denotes no group. May not be * null. */ public void setGroup(String group) { Preconditions.checkArgument(group != null, "group"); this.group = group; } }