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