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