001    package net.minecraftforge.oredict;
002    
003    import java.util.ArrayList;
004    import java.util.HashMap;
005    import java.util.Map;
006    import java.util.Map.Entry;
007    
008    import net.minecraft.block.Block;
009    import net.minecraft.item.crafting.IRecipe;
010    import net.minecraft.inventory.InventoryCrafting;
011    import net.minecraft.item.Item;
012    import net.minecraft.item.ItemStack;
013    import net.minecraft.item.crafting.ShapedRecipes;
014    import net.minecraft.world.World;
015    
016    public 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                Object val = null;
091    
092                if (in instanceof ItemStack)
093                {
094                    itemMap.put(chr, ((ItemStack)in).copy());
095                }
096                else if (in instanceof Item)
097                {
098                    itemMap.put(chr, new ItemStack((Item)in));
099                }
100                else if (in instanceof Block)
101                {
102                    itemMap.put(chr, new ItemStack((Block)in, 1, -1));
103                }
104                else if (in instanceof String)
105                {
106                    itemMap.put(chr, OreDictionary.getOres((String)in));
107                }
108                else
109                {
110                    String ret = "Invalid shaped ore recipe: ";
111                    for (Object tmp :  recipe)
112                    {
113                        ret += tmp + ", ";
114                    }
115                    ret += output;
116                    throw new RuntimeException(ret);
117                }
118            }
119    
120            input = new Object[width * height];
121            int x = 0;
122            for (char chr : shape.toCharArray())
123            {
124                input[x++] = itemMap.get(chr);   
125            }
126        }
127    
128        ShapedOreRecipe(ShapedRecipes recipe, Map<ItemStack, String> replacements)
129        {
130            output = recipe.getRecipeOutput();
131            width = recipe.recipeWidth;
132            height = recipe.recipeHeight;
133    
134            input = new Object[recipe.recipeItems.length];
135    
136            for(int i = 0; i < input.length; i++)
137            {
138                ItemStack ingred = recipe.recipeItems[i];
139    
140                if(ingred == null) continue;
141    
142                input[i] = recipe.recipeItems[i];
143    
144                for(Entry<ItemStack, String> replace : replacements.entrySet())
145                {
146                    if(OreDictionary.itemMatches(replace.getKey(), ingred, true))
147                    {
148                        input[i] = OreDictionary.getOres(replace.getValue());
149                        break;
150                    }
151                }
152            }
153        }
154    
155        @Override
156        public ItemStack getCraftingResult(InventoryCrafting var1){ return output.copy(); }
157    
158        @Override
159        public int getRecipeSize(){ return input.length; }
160    
161        @Override
162        public ItemStack getRecipeOutput(){ return output; }
163    
164        @Override
165        public boolean matches(InventoryCrafting inv, World world)
166        {        
167            for (int x = 0; x <= MAX_CRAFT_GRID_WIDTH - width; x++)
168            {
169                for (int y = 0; y <= MAX_CRAFT_GRID_HEIGHT - height; ++y)
170                {
171                    if (checkMatch(inv, x, y, true))
172                    {
173                        return true;
174                    }
175        
176                    if (mirrored && checkMatch(inv, x, y, false))
177                    {
178                        return true;
179                    }
180                }
181            }
182        
183            return false;
184        }
185        
186        private boolean checkMatch(InventoryCrafting inv, int startX, int startY, boolean mirror)
187        {
188            for (int x = 0; x < MAX_CRAFT_GRID_WIDTH; x++)
189            {
190                for (int y = 0; y < MAX_CRAFT_GRID_HEIGHT; y++)
191                {
192                    int subX = x - startX;
193                    int subY = y - startY;
194                    Object target = null;
195    
196                    if (subX >= 0 && subY >= 0 && subX < width && subY < height)
197                    {
198                        if (mirror)
199                        {
200                            target = input[width - subX - 1 + subY * width];
201                        }
202                        else
203                        {
204                            target = input[subX + subY * width];
205                        }
206                    }
207    
208                    ItemStack slot = inv.getStackInRowAndColumn(x, y);
209                    
210                    if (target instanceof ItemStack)
211                    {
212                        if (!checkItemEquals((ItemStack)target, slot))
213                        {
214                            return false;
215                        }
216                    }
217                    else if (target instanceof ArrayList)
218                    {
219                        boolean matched = false;
220                        
221                        for (ItemStack item : (ArrayList<ItemStack>)target)
222                        {
223                            matched = matched || checkItemEquals(item, slot);
224                        }
225                        
226                        if (!matched)
227                        {
228                            return false;
229                        }
230                    }
231                    else if (target == null && slot != null)
232                    {
233                        return false;
234                    }
235                }
236            }
237    
238            return true;
239        }
240    
241        private boolean checkItemEquals(ItemStack target, ItemStack input)
242        {
243            if (input == null && target != null || input != null && target == null)
244            {
245                return false;
246            }
247            return (target.itemID == input.itemID && (target.getItemDamage() == -1 || target.getItemDamage() == input.getItemDamage()));
248        }
249    
250        public ShapedOreRecipe setMirrored(boolean mirror)
251        {
252            mirrored = mirror;
253            return this;
254        }
255    }