    001package net.minecraftforge.oredict;
    002
    003import java.util.ArrayList;
    004import java.util.HashMap;
    005import java.util.Map;
    006import java.util.Map.Entry;
    007
    008import net.minecraft.block.Block;
    009import net.minecraft.item.crafting.IRecipe;
    010import net.minecraft.inventory.InventoryCrafting;
    011import net.minecraft.item.Item;
    012import net.minecraft.item.ItemStack;
    013import net.minecraft.item.crafting.ShapedRecipes;
    014import net.minecraft.world.World;
    015
    016public class ShapedOreRecipe implements IRecipe
    017{
    018    //Added in for future ease of change, but hard coded for now.
    019    private static final int MAX_CRAFT_GRID_WIDTH = 3;
    020    private static final int MAX_CRAFT_GRID_HEIGHT = 3;
    021
    022    private ItemStack output = null;
    023    private Object[] input = null;
    024    private int width = 0;
    025    private int height = 0;
    026    private boolean mirrored = true;
    027
    028    public ShapedOreRecipe(Block     result, Object... recipe){ this(new ItemStack(result), recipe); }
    029    public ShapedOreRecipe(Item      result, Object... recipe){ this(new ItemStack(result), recipe); }
    030    public ShapedOreRecipe(ItemStack result, Object... recipe)
    031    {
    032        output = result.copy();
    033
    034        String shape = "";
    035        int idx = 0;
    036
    037        if (recipe[idx] instanceof Boolean)
    038        {
    039            mirrored = (Boolean)recipe[idx];
    040            if (recipe[idx+1] instanceof Object[])
    041            {
    042                recipe = (Object[])recipe[idx+1];
    043            }
    044            else
    045            {
    046                idx = 1;
    047            }
    048        }
    049
    050        if (recipe[idx] instanceof String[])
    051        {
    052            String[] parts = ((String[])recipe[idx++]);
    053
    054            for (String s : parts)
    055            {
    056                width = s.length();
    057                shape += s;
    058            }
    059
    060            height = parts.length;
    061        }
    062        else
    063        {
    064            while (recipe[idx] instanceof String)
    065            {
    066                String s = (String)recipe[idx++];
    067                shape += s;
    068                width = s.length();
    069                height++;
    070            }
    071        }
    072
    073        if (width * height != shape.length())
    074        {
    075            String ret = "Invalid shaped ore recipe: ";
    076            for (Object tmp :  recipe)
    077            {
    078                ret += tmp + ", ";
    079            }
    080            ret += output;
    081            throw new RuntimeException(ret);
    082        }
    083
    084        HashMap<Character, Object> itemMap = new HashMap<Character, Object>();
    085
    086        for (; idx < recipe.length; idx += 2)
    087        {
    088            Character chr = (Character)recipe[idx];
    089            Object in = recipe[idx + 1];
    090
    091            if (in instanceof ItemStack)
    092            {
    093                itemMap.put(chr, ((ItemStack)in).copy());
    094            }
    095            else if (in instanceof Item)
    096            {
    097                itemMap.put(chr, new ItemStack((Item)in));
    098            }
    099            else if (in instanceof Block)
    100            {
    101                itemMap.put(chr, new ItemStack((Block)in, 1, OreDictionary.WILDCARD_VALUE));
    102            }
    103            else if (in instanceof String)
    104            {
    105                itemMap.put(chr, OreDictionary.getOres((String)in));
    106            }
    107            else
    108            {
    109                String ret = "Invalid shaped ore recipe: ";
    110                for (Object tmp :  recipe)
    111                {
    112                    ret += tmp + ", ";
    113                }
    114                ret += output;
    115                throw new RuntimeException(ret);
    116            }
    117        }
    118
    119        input = new Object[width * height];
    120        int x = 0;
    121        for (char chr : shape.toCharArray())
    122        {
    123            input[x++] = itemMap.get(chr);
    124        }
    125    }
    126
    127    ShapedOreRecipe(ShapedRecipes recipe, Map<ItemStack, String> replacements)
    128    {
    129        output = recipe.getRecipeOutput();
    130        width = recipe.recipeWidth;
    131        height = recipe.recipeHeight;
    132
    133        input = new Object[recipe.recipeItems.length];
    134
    135        for(int i = 0; i < input.length; i++)
    136        {
    137            ItemStack ingred = recipe.recipeItems[i];
    138
    139            if(ingred == null) continue;
    140
    141            input[i] = recipe.recipeItems[i];
    142
    143            for(Entry<ItemStack, String> replace : replacements.entrySet())
    144            {
    145                if(OreDictionary.itemMatches(replace.getKey(), ingred, true))
    146                {
    147                    input[i] = OreDictionary.getOres(replace.getValue());
    148                    break;
    149                }
    150            }
    151        }
    152    }
    153
    154    @Override
    155    public ItemStack getCraftingResult(InventoryCrafting var1){ return output.copy(); }
    156
    157    @Override
    158    public int getRecipeSize(){ return input.length; }
    159
    160    @Override
    161    public ItemStack getRecipeOutput(){ return output; }
    162
    163    @Override
    164    public boolean matches(InventoryCrafting inv, World world)
    165    {
    166        for (int x = 0; x <= MAX_CRAFT_GRID_WIDTH - width; x++)
    167        {
    168            for (int y = 0; y <= MAX_CRAFT_GRID_HEIGHT - height; ++y)
    169            {
    170                if (checkMatch(inv, x, y, false))
    171                {
    172                    return true;
    173                }
    174
    175                if (mirrored && checkMatch(inv, x, y, true))
    176                {
    177                    return true;
    178                }
    179            }
    180        }
    181
    182        return false;
    183    }
    184
    185    private boolean checkMatch(InventoryCrafting inv, int startX, int startY, boolean mirror)
    186    {
    187        for (int x = 0; x < MAX_CRAFT_GRID_WIDTH; x++)
    188        {
    189            for (int y = 0; y < MAX_CRAFT_GRID_HEIGHT; y++)
    190            {
    191                int subX = x - startX;
    192                int subY = y - startY;
    193                Object target = null;
    194
    195                if (subX >= 0 && subY >= 0 && subX < width && subY < height)
    196                {
    197                    if (mirror)
    198                    {
    199                        target = input[width - subX - 1 + subY * width];
    200                    }
    201                    else
    202                    {
    203                        target = input[subX + subY * width];
    204                    }
    205                }
    206
    207                ItemStack slot = inv.getStackInRowAndColumn(x, y);
    208
    209                if (target instanceof ItemStack)
    210                {
    211                    if (!checkItemEquals((ItemStack)target, slot))
    212                    {
    213                        return false;
    214                    }
    215                }
    216                else if (target instanceof ArrayList)
    217                {
    218                    boolean matched = false;
    219
    220                    for (ItemStack item : (ArrayList<ItemStack>)target)
    221                    {
    222                        matched = matched || checkItemEquals(item, slot);
    223                    }
    224
    225                    if (!matched)
    226                    {
    227                        return false;
    228                    }
    229                }
    230                else if (target == null && slot != null)
    231                {
    232                    return false;
    233                }
    234            }
    235        }
    236
    237        return true;
    238    }
    239
    240    private boolean checkItemEquals(ItemStack target, ItemStack input)
    241    {
    242        if (input == null && target != null || input != null && target == null)
    243        {
    244            return false;
    245        }
    246        return (target.itemID == input.itemID && (target.getItemDamage() == OreDictionary.WILDCARD_VALUE|| target.getItemDamage() == input.getItemDamage()));
    247    }
    248
    249    public ShapedOreRecipe setMirrored(boolean mirror)
    250    {
    251        mirrored = mirror;
    252        return this;
    253    }
    254
    255    /**
    256     * Returns the input for this recipe, any mod accessing this value should never
    257     * manipulate the values in this array as it will effect the recipe itself.
    258     * @return The recipes input vales.
    259     */
    260    public Object[] getInput()
    261    {
    262        return this.input;
    263    }
    264}