package WayofTime.bloodmagic.compress; import WayofTime.bloodmagic.BloodMagic; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.Container; import net.minecraft.inventory.InventoryCrafting; import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.CraftingManager; import net.minecraft.item.crafting.IRecipe; import net.minecraft.item.crafting.ShapedRecipes; import net.minecraft.item.crafting.ShapelessRecipes; import net.minecraft.world.World; import net.minecraftforge.oredict.OreDictionary; import net.minecraftforge.oredict.ShapedOreRecipe; import net.minecraftforge.oredict.ShapelessOreRecipe; import java.util.*; public class StorageBlockCraftingRecipeAssimilator { public List getPackingRecipes() { // grab all recipes potentially suitable for packing or unpacking List packingRecipes = new LinkedList(); List unpackingRecipes = new ArrayList(); for (IRecipe recipe : getCraftingRecipes()) { ItemStack output = recipe.getRecipeOutput(); if (output == null || output.getItem() == null) continue; if (output.stackSize == 1) { PackingRecipe packingRecipe = getPackingRecipe(recipe); if (packingRecipe != null) { packingRecipes.add(packingRecipe); } } else if ((output.stackSize == 4 || output.stackSize == 9) && recipe.getRecipeSize() == 1) { unpackingRecipes.add(recipe); } } // grab all packing recipes which accept the output of any of the // unpacking recipes Container container = makeDummyContainer(); InventoryCrafting inventoryUnpack = new InventoryCrafting(container, 2, 2); InventoryCrafting inventory2x2 = new InventoryCrafting(container, 2, 2); InventoryCrafting inventory3x3 = new InventoryCrafting(container, 3, 3); World world = null; // TODO: use a proper dummy world? List ret = new ArrayList(); for (IRecipe recipeUnpack : unpackingRecipes) { ItemStack unpacked = recipeUnpack.getRecipeOutput(); InventoryCrafting inventory = null; for (Iterator it = packingRecipes.iterator(); it.hasNext();) { PackingRecipe recipePack = it.next(); // check if the packing recipe accepts the unpacking recipe's output boolean matched = false; if (recipePack.possibleInputs != null) { // the recipe could be parsed, use its inputs directly since that's faster verify recipe size if (recipePack.inputCount != unpacked.stackSize) continue; // check if any of the input options matches the unpacked // item stack for (ItemStack stack : recipePack.possibleInputs) { if (areInputsIdentical(unpacked, stack)) { matched = true; break; } } } else { // unknown IRecipe, check through the recipe conventionally verify recipe size for 3x3 to skip anything smaller quickly if (unpacked.stackSize == 9 && recipePack.recipe.getRecipeSize() < 9) continue; // initialize inventory late, but only once per unpack recipe if (inventory == null) { if (unpacked.stackSize == 4) { inventory = inventory2x2; } else { inventory = inventory3x3; } for (int i = 0; i < unpacked.stackSize; i++) { inventory.setInventorySlotContents(i, unpacked.copy()); } } // check if the packing recipe accepts the unpacked item // stack matched = recipePack.recipe.matches(inventory, world); } if (matched) { // check if the unpacking recipe accepts the packing // recipe's output ItemStack packOutput = recipePack.recipe.getRecipeOutput(); inventoryUnpack.setInventorySlotContents(0, packOutput.copy()); if (recipeUnpack.matches(inventoryUnpack, world)) { ret.add(recipePack.recipe); it.remove(); } } } } return ret; } @SuppressWarnings("unchecked") private List getCraftingRecipes() { return CraftingManager.getInstance().getRecipeList(); } private Container makeDummyContainer() { return new Container() { @Override public boolean canInteractWith(EntityPlayer player) { return true; } }; } private PackingRecipe getPackingRecipe(IRecipe recipe) { if (recipe.getRecipeSize() < 4) return null; List inputs; if (recipe instanceof ShapedRecipes) { inputs = Arrays.asList(((ShapedRecipes) recipe).recipeItems); } else if (recipe instanceof ShapelessRecipes) { inputs = ((ShapelessRecipes) recipe).recipeItems; } else if (recipe instanceof ShapedOreRecipe) { inputs = Arrays.asList(((ShapedOreRecipe) recipe).getInput()); } else if (recipe instanceof ShapelessOreRecipe) { inputs = ((ShapelessOreRecipe) recipe).getInput(); } else { return new PackingRecipe(recipe, null, -1); } // check if the recipe inputs are size 4 or 9 int count = 0; for (Object o : inputs) { if (o != null) count++; } if (count != 4 && count != 9) return null; // grab identical inputs List identicalInputs = getIdenticalInputs(inputs); if (identicalInputs == null) return null; return new PackingRecipe(recipe, identicalInputs, count); } /** * Determine the item stacks from the provided inputs which are suitable for * every input element. * * @param inputs * List of all inputs, null elements are being ignored. * @return List List of all options. */ @SuppressWarnings("unchecked") private List getIdenticalInputs(List inputs) { List options = null; for (Object input : inputs) { if (input == null) continue; List offers; if (input instanceof ItemStack) { offers = Collections.singletonList((ItemStack) input); } else if (input instanceof List) { offers = (List) input; if (offers.isEmpty()) return null; } else { throw new RuntimeException("invalid input: " + input.getClass()); } if (options == null) { options = new ArrayList(offers); continue; } for (Iterator it = options.iterator(); it.hasNext();) { ItemStack stackReq = it.next(); boolean found = false; for (ItemStack stackCmp : offers) { if (areInputsIdentical(stackReq, stackCmp)) { found = true; break; } } if (!found) { it.remove(); if (options.isEmpty()) return null; } } } return options; } private boolean areInputsIdentical(ItemStack a, ItemStack b) { try { if (a.getItem() != b.getItem()) return false; int dmgA = a.getItemDamage(); int dmgB = b.getItemDamage(); return dmgA == dmgB || dmgA == OreDictionary.WILDCARD_VALUE || dmgB == OreDictionary.WILDCARD_VALUE; } catch (NullPointerException e) { BloodMagic.instance.getLogger().error("A mod in this instance has registered an item with a null input. Known problem mods are:"); // String err = ""; // for (String problem : problemMods) // err += (err.length() > 0 ? ", " : "") + problem; // BloodMagic.instance.getLogger().error(err); return false; } } private static class PackingRecipe { PackingRecipe(IRecipe recipe, List possibleInputs, int inputCount) { this.recipe = recipe; this.possibleInputs = possibleInputs; this.inputCount = inputCount; } final IRecipe recipe; final List possibleInputs; final int inputCount; } }