    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}