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 }