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    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}