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        {
189            return -1;
190        }
191
192        for(Entry<Integer, ArrayList<ItemStack>> ore : oreStacks.entrySet())
193        {
194            for(ItemStack target : ore.getValue())
195            {
196                if(itemStack.itemID == target.itemID && (target.getItemDamage() == -1 || itemStack.getItemDamage() == target.getItemDamage()))
197                {
198                    return ore.getKey();
199                }
200            }
201        }
202        return -1; // didn't find it.
203    }
204    
205    /**
206     * Retrieves the ArrayList of items that are registered to this ore type.
207     * Creates the list as empty if it did not exist.
208     *  
209     * @param name The ore name, directly calls getOreID
210     * @return An arrayList containing ItemStacks registered for this ore
211     */
212    public static ArrayList<ItemStack> getOres(String name)
213    {
214        return getOres(getOreID(name));
215    }
216    
217    /**
218     * Retrieves a list of all unique ore names that are already registered.
219     * 
220     * @return All unique ore names that are currently registered.
221     */
222    public static String[] getOreNames()
223    {
224        return oreIDs.keySet().toArray(new String[oreIDs.keySet().size()]);
225    }
226    
227    /**
228     * Retrieves the ArrayList of items that are registered to this ore type.
229     * Creates the list as empty if it did not exist.
230     *  
231     * @param id The ore ID, see getOreID
232     * @return An arrayList containing ItemStacks registered for this ore
233     */
234    public static ArrayList<ItemStack> getOres(Integer id)
235    {
236        ArrayList<ItemStack> val = oreStacks.get(id);
237        if (val == null)
238        {
239            val = new ArrayList<ItemStack>();
240            oreStacks.put(id, val);
241        }
242        return val;
243    }
244
245    private static boolean containsMatch(boolean strict, ItemStack[] inputs, ItemStack... targets)
246    {
247        for (ItemStack input : inputs)
248        {
249            for (ItemStack target : targets)
250            {
251                if (itemMatches(target, input, strict))
252                {
253                    return true;
254                }
255            }
256        }
257        return false;
258    }
259
260    public static boolean itemMatches(ItemStack target, ItemStack input, boolean strict)
261    {
262        if (input == null && target != null || input != null && target == null)
263        {
264            return false;
265        }
266        return (target.itemID == input.itemID && ((target.getItemDamage() == -1 && !strict) || target.getItemDamage() == input.getItemDamage()));
267    }
268
269    //Convenience functions that make for cleaner code mod side. They all drill down to registerOre(String, int, ItemStack)
270    public static void registerOre(String name, Item      ore){ registerOre(name, new ItemStack(ore));  }
271    public static void registerOre(String name, Block     ore){ registerOre(name, new ItemStack(ore));  }
272    public static void registerOre(String name, ItemStack ore){ registerOre(name, getOreID(name), ore); }
273    public static void registerOre(int    id,   Item      ore){ registerOre(id,   new ItemStack(ore));  }
274    public static void registerOre(int    id,   Block     ore){ registerOre(id,   new ItemStack(ore));  }
275    public static void registerOre(int    id,   ItemStack ore){ registerOre(getOreName(id), id, ore);   }
276    
277    /**
278     * Registers a ore item into the dictionary.
279     * Raises the registerOre function in all registered handlers.
280     * 
281     * @param name The name of the ore
282     * @param id The ID of the ore
283     * @param ore The ore's ItemStack
284     */
285    private static void registerOre(String name, int id, ItemStack ore)
286    {
287        ArrayList<ItemStack> ores = getOres(id);
288        ore = ore.copy();
289        ores.add(ore);
290        MinecraftForge.EVENT_BUS.post(new OreRegisterEvent(name, ore));
291    }
292    
293    public static class OreRegisterEvent extends Event
294    {
295        public final String Name;
296        public final ItemStack Ore;
297        
298        public OreRegisterEvent(String name, ItemStack ore)
299        {
300            this.Name = name;
301            this.Ore = ore;
302        }
303    }
304}