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