001 package org.bukkit.inventory;
002
003 import java.util.HashMap;
004 import java.util.Map;
005
006 import org.apache.commons.lang.Validate;
007
008 import org.bukkit.Material;
009 import org.bukkit.material.MaterialData;
010
011 /**
012 * Represents a shaped (ie normal) crafting recipe.
013 */
014 public class ShapedRecipe implements Recipe {
015 private ItemStack output;
016 private String[] rows;
017 private Map<Character, ItemStack> ingredients = new HashMap<Character, ItemStack>();
018
019 /**
020 * Create a shaped recipe to craft the specified ItemStack. The
021 * constructor merely determines the result and type; to set the actual
022 * recipe, you'll need to call the appropriate methods.
023 *
024 * @param result The item you want the recipe to create.
025 * @see ShapedRecipe#shape(String...)
026 * @see ShapedRecipe#setIngredient(char, Material)
027 * @see ShapedRecipe#setIngredient(char, Material, int)
028 * @see ShapedRecipe#setIngredient(char, MaterialData)
029 */
030 public ShapedRecipe(ItemStack result) {
031 this.output = new ItemStack(result);
032 }
033
034 /**
035 * Set the shape of this recipe to the specified rows. Each character
036 * represents a different ingredient; exactly what each character
037 * represents is set separately. The first row supplied corresponds with
038 * the upper most part of the recipe on the workbench e.g. if all three
039 * rows are supplies the first string represents the top row on the
040 * workbench.
041 *
042 * @param shape The rows of the recipe (up to 3 rows).
043 * @return The changed recipe, so you can chain calls.
044 */
045 public ShapedRecipe shape(final String... shape) {
046 Validate.notNull(shape, "Must provide a shape");
047 Validate.isTrue(shape.length > 0 && shape.length < 4, "Crafting recipes should be 1, 2, 3 rows, not ", shape.length);
048
049 for (String row : shape) {
050 Validate.notNull(row, "Shape cannot have null rows");
051 Validate.isTrue(row.length() > 0 && row.length() < 4, "Crafting rows should be 1, 2, or 3 characters, not ", row.length());
052 }
053 this.rows = new String[shape.length];
054 for (int i = 0; i < shape.length; i++) {
055 this.rows[i] = shape[i];
056 }
057
058 // Remove character mappings for characters that no longer exist in the shape
059 HashMap<Character, ItemStack> newIngredients = new HashMap<Character, ItemStack>();
060 for (String row : shape) {
061 for (Character c : row.toCharArray()) {
062 newIngredients.put(c, ingredients.get(c));
063 }
064 }
065 this.ingredients = newIngredients;
066
067 return this;
068 }
069
070 /**
071 * Sets the material that a character in the recipe shape refers to.
072 *
073 * @param key The character that represents the ingredient in the shape.
074 * @param ingredient The ingredient.
075 * @return The changed recipe, so you can chain calls.
076 */
077 public ShapedRecipe setIngredient(char key, MaterialData ingredient) {
078 return setIngredient(key, ingredient.getItemType(), ingredient.getData());
079 }
080
081 /**
082 * Sets the material that a character in the recipe shape refers to.
083 *
084 * @param key The character that represents the ingredient in the shape.
085 * @param ingredient The ingredient.
086 * @return The changed recipe, so you can chain calls.
087 */
088 public ShapedRecipe setIngredient(char key, Material ingredient) {
089 return setIngredient(key, ingredient, 0);
090 }
091
092 /**
093 * Sets the material that a character in the recipe shape refers to.
094 *
095 * @param key The character that represents the ingredient in the shape.
096 * @param ingredient The ingredient.
097 * @param raw The raw material data as an integer.
098 * @return The changed recipe, so you can chain calls.
099 * @deprecated Magic value
100 */
101 @Deprecated
102 public ShapedRecipe setIngredient(char key, Material ingredient, int raw) {
103 Validate.isTrue(ingredients.containsKey(key), "Symbol does not appear in the shape:", key);
104
105 // -1 is the old wildcard, map to Short.MAX_VALUE as the new one
106 if (raw == -1) {
107 raw = Short.MAX_VALUE;
108 }
109
110 ingredients.put(key, new ItemStack(ingredient, 1, (short) raw));
111 return this;
112 }
113
114 /**
115 * Get a copy of the ingredients map.
116 *
117 * @return The mapping of character to ingredients.
118 */
119 public Map<Character, ItemStack> getIngredientMap() {
120 HashMap<Character, ItemStack> result = new HashMap<Character, ItemStack>();
121 for (Map.Entry<Character, ItemStack> ingredient : ingredients.entrySet()) {
122 if (ingredient.getValue() == null) {
123 result.put(ingredient.getKey(), null);
124 } else {
125 result.put(ingredient.getKey(), ingredient.getValue().clone());
126 }
127 }
128 return result;
129 }
130
131 /**
132 * Get the shape.
133 *
134 * @return The recipe's shape.
135 */
136 public String[] getShape() {
137 return rows.clone();
138 }
139
140 /**
141 * Get the result.
142 *
143 * @return The result stack.
144 */
145 public ItemStack getResult() {
146 return output.clone();
147 }
148 }