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