001package net.minecraftforge.oredict; 002 003import java.util.ArrayList; 004import java.util.HashMap; 005import java.util.Iterator; 006import java.util.List; 007import java.util.Map; 008import java.util.Map.Entry; 009 010import net.minecraft.block.Block; 011import net.minecraft.item.Item; 012import net.minecraft.item.ItemStack; 013import net.minecraft.item.crafting.CraftingManager; 014import net.minecraft.item.crafting.IRecipe; 015import net.minecraft.item.crafting.ShapedRecipes; 016import net.minecraft.item.crafting.ShapelessRecipes; 017import net.minecraftforge.common.MinecraftForge; 018import net.minecraftforge.event.Event; 019 020public class OreDictionary 021{ 022 private static boolean hasInit = false; 023 private static int maxID = 0; 024 private static HashMap<String, Integer> oreIDs = new HashMap<String, Integer>(); 025 private static HashMap<Integer, ArrayList<ItemStack>> oreStacks = new HashMap<Integer, ArrayList<ItemStack>>(); 026 027 028 /** 029 * Minecraft changed from -1 to Short.MAX_VALUE in 1.5 release for the "block wildcard". Use this in case it 030 * changes again. 031 */ 032 public static final int WILDCARD_VALUE = Short.MAX_VALUE; 033 034 static { 035 initVanillaEntries(); 036 } 037 038 public static void initVanillaEntries() 039 { 040 if (!hasInit) 041 { 042 registerOre("logWood", new ItemStack(Block.wood, 1, WILDCARD_VALUE)); 043 registerOre("plankWood", new ItemStack(Block.planks, 1, WILDCARD_VALUE)); 044 registerOre("slabWood", new ItemStack(Block.woodSingleSlab, 1, WILDCARD_VALUE)); 045 registerOre("stairWood", Block.stairCompactPlanks); 046 registerOre("stairWood", Block.stairsWoodBirch); 047 registerOre("stairWood", Block.stairsWoodJungle); 048 registerOre("stairWood", Block.stairsWoodSpruce); 049 registerOre("stickWood", Item.stick); 050 registerOre("treeSapling", new ItemStack(Block.sapling, 1, WILDCARD_VALUE)); 051 registerOre("treeLeaves", new ItemStack(Block.leaves, 1, WILDCARD_VALUE)); 052 } 053 054 // Build our list of items to replace with ore tags 055 Map<ItemStack, String> replacements = new HashMap<ItemStack, String>(); 056 replacements.put(new ItemStack(Block.planks, 1, WILDCARD_VALUE), "plankWood"); 057 replacements.put(new ItemStack(Item.stick), "stickWood"); 058 059 // Register dyes 060 String[] dyes = 061 { 062 "dyeBlack", 063 "dyeRed", 064 "dyeGreen", 065 "dyeBrown", 066 "dyeBlue", 067 "dyePurple", 068 "dyeCyan", 069 "dyeLightGray", 070 "dyeGray", 071 "dyePink", 072 "dyeLime", 073 "dyeYellow", 074 "dyeLightBlue", 075 "dyeMagenta", 076 "dyeOrange", 077 "dyeWhite" 078 }; 079 080 for(int i = 0; i < 16; i++) 081 { 082 ItemStack dye = new ItemStack(Item.dyePowder, 1, i); 083 if (!hasInit) 084 { 085 registerOre(dyes[i], dye); 086 } 087 replacements.put(dye, dyes[i]); 088 } 089 hasInit = true; 090 091 ItemStack[] replaceStacks = replacements.keySet().toArray(new ItemStack[replacements.keySet().size()]); 092 093 // Ignore recipes for the following items 094 ItemStack[] exclusions = new ItemStack[] 095 { 096 new ItemStack(Block.blockLapis), 097 new ItemStack(Item.cookie), 098 }; 099 100 List recipes = CraftingManager.getInstance().getRecipeList(); 101 List<IRecipe> recipesToRemove = new ArrayList<IRecipe>(); 102 List<IRecipe> recipesToAdd = new ArrayList<IRecipe>(); 103 104 // Search vanilla recipes for recipes to replace 105 for(Object obj : recipes) 106 { 107 if(obj instanceof ShapedRecipes) 108 { 109 ShapedRecipes recipe = (ShapedRecipes)obj; 110 ItemStack output = recipe.getRecipeOutput(); 111 if (output != null && containsMatch(false, exclusions, output)) 112 { 113 continue; 114 } 115 116 if(containsMatch(true, recipe.recipeItems, replaceStacks)) 117 { 118 recipesToRemove.add(recipe); 119 recipesToAdd.add(new ShapedOreRecipe(recipe, replacements)); 120 } 121 } 122 else if(obj instanceof ShapelessRecipes) 123 { 124 ShapelessRecipes recipe = (ShapelessRecipes)obj; 125 ItemStack output = recipe.getRecipeOutput(); 126 if (output != null && containsMatch(false, exclusions, output)) 127 { 128 continue; 129 } 130 131 if(containsMatch(true, (ItemStack[])recipe.recipeItems.toArray(new ItemStack[recipe.recipeItems.size()]), replaceStacks)) 132 { 133 recipesToRemove.add((IRecipe)obj); 134 IRecipe newRecipe = new ShapelessOreRecipe(recipe, replacements); 135 recipesToAdd.add(newRecipe); 136 } 137 } 138 } 139 140 recipes.removeAll(recipesToRemove); 141 recipes.addAll(recipesToAdd); 142 if (recipesToRemove.size() > 0) 143 { 144 System.out.println("Replaced " + recipesToRemove.size() + " ore recipies"); 145 } 146 } 147 148 /** 149 * Gets the integer ID for the specified ore name. 150 * If the name does not have a ID it assigns it a new one. 151 * 152 * @param name The unique name for this ore 'oreIron', 'ingotIron', etc.. 153 * @return A number representing the ID for this ore type 154 */ 155 public static int getOreID(String name) 156 { 157 Integer val = oreIDs.get(name); 158 if (val == null) 159 { 160 val = maxID++; 161 oreIDs.put(name, val); 162 oreStacks.put(val, new ArrayList<ItemStack>()); 163 } 164 return val; 165 } 166 167 /** 168 * Reverse of getOreID, will not create new entries. 169 * 170 * @param id The ID to translate to a string 171 * @return The String name, or "Unknown" if not found. 172 */ 173 public static String getOreName(int id) 174 { 175 for (Map.Entry<String, Integer> entry : oreIDs.entrySet()) 176 { 177 if (id == entry.getValue()) 178 { 179 return entry.getKey(); 180 } 181 } 182 return "Unknown"; 183 } 184 185 /** 186 * Gets the integer ID for the specified item stack. 187 * If the item stack is not linked to any ore, this will return -1 and no new entry will be created. 188 * 189 * @param itemStack The item stack of the ore. 190 * @return A number representing the ID for this ore type, or -1 if couldn't find it. 191 */ 192 public static int getOreID(ItemStack itemStack) 193 { 194 if (itemStack == null) 195 { 196 return -1; 197 } 198 199 for(Entry<Integer, ArrayList<ItemStack>> ore : oreStacks.entrySet()) 200 { 201 for(ItemStack target : ore.getValue()) 202 { 203 if(itemStack.itemID == target.itemID && (target.getItemDamage() == WILDCARD_VALUE || itemStack.getItemDamage() == target.getItemDamage())) 204 { 205 return ore.getKey(); 206 } 207 } 208 } 209 return -1; // didn't find it. 210 } 211 212 /** 213 * Retrieves the ArrayList of items that are registered to this ore type. 214 * Creates the list as empty if it did not exist. 215 * 216 * @param name The ore name, directly calls getOreID 217 * @return An arrayList containing ItemStacks registered for this ore 218 */ 219 public static ArrayList<ItemStack> getOres(String name) 220 { 221 return getOres(getOreID(name)); 222 } 223 224 /** 225 * Retrieves a list of all unique ore names that are already registered. 226 * 227 * @return All unique ore names that are currently registered. 228 */ 229 public static String[] getOreNames() 230 { 231 return oreIDs.keySet().toArray(new String[oreIDs.keySet().size()]); 232 } 233 234 /** 235 * Retrieves the ArrayList of items that are registered to this ore type. 236 * Creates the list as empty if it did not exist. 237 * 238 * @param id The ore ID, see getOreID 239 * @return An arrayList containing ItemStacks registered for this ore 240 */ 241 public static ArrayList<ItemStack> getOres(Integer id) 242 { 243 ArrayList<ItemStack> val = oreStacks.get(id); 244 if (val == null) 245 { 246 val = new ArrayList<ItemStack>(); 247 oreStacks.put(id, val); 248 } 249 return val; 250 } 251 252 private static boolean containsMatch(boolean strict, ItemStack[] inputs, ItemStack... targets) 253 { 254 for (ItemStack input : inputs) 255 { 256 for (ItemStack target : targets) 257 { 258 if (itemMatches(target, input, strict)) 259 { 260 return true; 261 } 262 } 263 } 264 return false; 265 } 266 267 public static boolean itemMatches(ItemStack target, ItemStack input, boolean strict) 268 { 269 if (input == null && target != null || input != null && target == null) 270 { 271 return false; 272 } 273 return (target.itemID == input.itemID && ((target.getItemDamage() == WILDCARD_VALUE && !strict) || target.getItemDamage() == input.getItemDamage())); 274 } 275 276 //Convenience functions that make for cleaner code mod side. They all drill down to registerOre(String, int, ItemStack) 277 public static void registerOre(String name, Item ore){ registerOre(name, new ItemStack(ore)); } 278 public static void registerOre(String name, Block ore){ registerOre(name, new ItemStack(ore)); } 279 public static void registerOre(String name, ItemStack ore){ registerOre(name, getOreID(name), ore); } 280 public static void registerOre(int id, Item ore){ registerOre(id, new ItemStack(ore)); } 281 public static void registerOre(int id, Block ore){ registerOre(id, new ItemStack(ore)); } 282 public static void registerOre(int id, ItemStack ore){ registerOre(getOreName(id), id, ore); } 283 284 /** 285 * Registers a ore item into the dictionary. 286 * Raises the registerOre function in all registered handlers. 287 * 288 * @param name The name of the ore 289 * @param id The ID of the ore 290 * @param ore The ore's ItemStack 291 */ 292 private static void registerOre(String name, int id, ItemStack ore) 293 { 294 ArrayList<ItemStack> ores = getOres(id); 295 ore = ore.copy(); 296 ores.add(ore); 297 MinecraftForge.EVENT_BUS.post(new OreRegisterEvent(name, ore)); 298 } 299 300 public static class OreRegisterEvent extends Event 301 { 302 public final String Name; 303 public final ItemStack Ore; 304 305 public OreRegisterEvent(String name, ItemStack ore) 306 { 307 this.Name = name; 308 this.Ore = ore; 309 } 310 } 311}