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         * Retrieves the ArrayList of items that are registered to this ore type.
174         * Creates the list as empty if it did not exist.
175         *  
176         * @param name The ore name, directly calls getOreID
177         * @return An arrayList containing ItemStacks registered for this ore
178         */
179        public static ArrayList<ItemStack> getOres(String name)
180        {
181            return getOres(getOreID(name));
182        }
183        
184        /**
185         * Retrieves a list of all unique ore names that are already registered.
186         * 
187         * @return All unique ore names that are currently registered.
188         */
189        public static String[] getOreNames()
190        {
191            return oreIDs.keySet().toArray(new String[0]);
192        }
193        
194        /**
195         * Retrieves the ArrayList of items that are registered to this ore type.
196         * Creates the list as empty if it did not exist.
197         *  
198         * @param id The ore ID, see getOreID
199         * @return An arrayList containing ItemStacks registered for this ore
200         */
201        public static ArrayList<ItemStack> getOres(Integer id)
202        {
203            ArrayList<ItemStack> val = oreStacks.get(id);
204            if (val == null)
205            {
206                val = new ArrayList<ItemStack>();
207                oreStacks.put(id, val);
208            }
209            return val;
210        }
211    
212        private static boolean containsMatch(boolean strict, ItemStack[] inputs, ItemStack... targets)
213        {
214            for (ItemStack input : inputs)
215            {
216                for (ItemStack target : targets)
217                {
218                    if (itemMatches(target, input, strict))
219                    {
220                        return true;
221                    }
222                }
223            }
224            return false;
225        }
226    
227        public static boolean itemMatches(ItemStack target, ItemStack input, boolean strict)
228        {
229            if (input == null && target != null || input != null && target == null)
230            {
231                return false;
232            }
233            return (target.itemID == input.itemID && ((target.getItemDamage() == -1 && !strict) || target.getItemDamage() == input.getItemDamage()));
234        }
235    
236        //Convenience functions that make for cleaner code mod side. They all drill down to registerOre(String, int, ItemStack)
237        public static void registerOre(String name, Item      ore){ registerOre(name, new ItemStack(ore));  }
238        public static void registerOre(String name, Block     ore){ registerOre(name, new ItemStack(ore));  }
239        public static void registerOre(String name, ItemStack ore){ registerOre(name, getOreID(name), ore); }
240        public static void registerOre(int    id,   Item      ore){ registerOre(id,   new ItemStack(ore));  }
241        public static void registerOre(int    id,   Block     ore){ registerOre(id,   new ItemStack(ore));  }
242        public static void registerOre(int    id,   ItemStack ore){ registerOre(getOreName(id), id, ore);   }
243        
244        /**
245         * Registers a ore item into the dictionary.
246         * Raises the registerOre function in all registered handlers.
247         * 
248         * @param name The name of the ore
249         * @param id The ID of the ore
250         * @param ore The ore's ItemStack
251         */
252        private static void registerOre(String name, int id, ItemStack ore)
253        {
254            ArrayList<ItemStack> ores = getOres(id);
255            ore = ore.copy();
256            ores.add(ore);
257            MinecraftForge.EVENT_BUS.post(new OreRegisterEvent(name, ore));
258        }
259        
260        public static class OreRegisterEvent extends Event
261        {
262            public final String Name;
263            public final ItemStack Ore;
264            
265            public OreRegisterEvent(String name, ItemStack ore)
266            {
267                this.Name = name;
268                this.Ore = ore;
269            }
270        }
271    }