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