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