package mekanism.common.recipe; import java.lang.reflect.Constructor; import java.util.HashMap; import java.util.Map; import java.util.Set; import mekanism.api.gas.GasStack; import mekanism.api.infuse.InfuseType; import mekanism.api.util.StackUtils; import mekanism.common.block.BlockMachine.MachineType; import mekanism.common.recipe.inputs.AdvancedMachineInput; import mekanism.common.recipe.inputs.ChemicalPairInput; import mekanism.common.recipe.inputs.FluidInput; import mekanism.common.recipe.inputs.GasInput; import mekanism.common.recipe.inputs.InfusionInput; import mekanism.common.recipe.inputs.IntegerInput; import mekanism.common.recipe.inputs.ItemStackInput; import mekanism.common.recipe.inputs.MachineInput; import mekanism.common.recipe.inputs.PressurizedInput; import mekanism.common.recipe.machines.AdvancedMachineRecipe; import mekanism.common.recipe.machines.AmbientGasRecipe; import mekanism.common.recipe.machines.BasicMachineRecipe; import mekanism.common.recipe.machines.ChanceMachineRecipe; import mekanism.common.recipe.machines.ChemicalInfuserRecipe; import mekanism.common.recipe.machines.CombinerRecipe; import mekanism.common.recipe.machines.CrusherRecipe; import mekanism.common.recipe.machines.CrystallizerRecipe; import mekanism.common.recipe.machines.DissolutionRecipe; import mekanism.common.recipe.machines.EnrichmentRecipe; import mekanism.common.recipe.machines.InjectionRecipe; import mekanism.common.recipe.machines.MachineRecipe; import mekanism.common.recipe.machines.MetallurgicInfuserRecipe; import mekanism.common.recipe.machines.OsmiumCompressorRecipe; import mekanism.common.recipe.machines.OxidationRecipe; import mekanism.common.recipe.machines.PressurizedRecipe; import mekanism.common.recipe.machines.PurificationRecipe; import mekanism.common.recipe.machines.SawmillRecipe; import mekanism.common.recipe.machines.SeparatorRecipe; import mekanism.common.recipe.machines.SmeltingRecipe; import mekanism.common.recipe.machines.SolarEvaporationRecipe; import mekanism.common.recipe.machines.SolarNeutronRecipe; import mekanism.common.recipe.machines.WasherRecipe; import mekanism.common.recipe.outputs.ChanceOutput; import mekanism.common.recipe.outputs.ChemicalPairOutput; import mekanism.common.recipe.outputs.FluidOutput; import mekanism.common.recipe.outputs.GasOutput; import mekanism.common.recipe.outputs.ItemStackOutput; import mekanism.common.recipe.outputs.MachineOutput; import mekanism.common.recipe.outputs.PressurizedOutput; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fluids.FluidStack; /** * Class used to handle machine recipes. This is used for both adding and fetching recipes. * @author AidanBrady, unpairedbracket * */ public final class RecipeHandler { public static void addRecipe(Recipe recipeMap, MachineRecipe recipe) { recipeMap.put(recipe); } /** * Add an Enrichment Chamber recipe. * @param input - input ItemStack * @param output - output ItemStack */ public static void addEnrichmentChamberRecipe(ItemStack input, ItemStack output) { addRecipe(Recipe.ENRICHMENT_CHAMBER, new EnrichmentRecipe(input, output)); } /** * Add an Osmium Compressor recipe. * @param input - input ItemStack * @param output - output ItemStack */ public static void addOsmiumCompressorRecipe(ItemStack input, ItemStack output) { addRecipe(Recipe.OSMIUM_COMPRESSOR, new OsmiumCompressorRecipe(input, output)); } /** * Add a Combiner recipe. * @param input - input ItemStack * @param output - output ItemStack */ public static void addCombinerRecipe(ItemStack input, ItemStack output) { addRecipe(Recipe.COMBINER, new CombinerRecipe(input, output)); } /** * Add a Crusher recipe. * @param input - input ItemStack * @param output - output ItemStack */ public static void addCrusherRecipe(ItemStack input, ItemStack output) { addRecipe(Recipe.CRUSHER, new CrusherRecipe(input, output)); } /** * Add a Purification Chamber recipe. * @param input - input ItemStack * @param output - output ItemStack */ public static void addPurificationChamberRecipe(ItemStack input, ItemStack output) { addRecipe(Recipe.PURIFICATION_CHAMBER, new PurificationRecipe(input, output)); } /** * Add a Metallurgic Infuser recipe. * @param infuse - which Infuse to use * @param amount - how much of the Infuse to use * @param input - input ItemStack * @param output - output ItemStack */ public static void addMetallurgicInfuserRecipe(InfuseType infuse, int amount, ItemStack input, ItemStack output) { addRecipe(Recipe.METALLURGIC_INFUSER, new MetallurgicInfuserRecipe(new InfusionInput(infuse, amount, input), output)); } /** * Add a Chemical Infuser recipe. * @param leftInput - left GasStack to input * @param rightInput - right GasStack to input * @param output - output GasStack */ public static void addChemicalInfuserRecipe(GasStack leftInput, GasStack rightInput, GasStack output) { addRecipe(Recipe.CHEMICAL_INFUSER, new ChemicalInfuserRecipe(leftInput, rightInput, output)); } /** * Add a Chemical Oxidizer recipe. * @param input - input ItemStack * @param output - output GasStack */ public static void addChemicalOxidizerRecipe(ItemStack input, GasStack output) { addRecipe(Recipe.CHEMICAL_OXIDIZER, new OxidationRecipe(input, output)); } /** * Add a Chemical Injection Chamber recipe. * @param input - input ItemStack * @param output - output ItemStack */ public static void addChemicalInjectionChamberRecipe(ItemStack input, String gasName, ItemStack output) { addRecipe(Recipe.CHEMICAL_INJECTION_CHAMBER, new InjectionRecipe(input, gasName, output)); } /** * Add an Electrolytic Separator recipe. * @param fluid - FluidStack to electrolyze * @param leftOutput - left gas to produce when the fluid is electrolyzed * @param rightOutput - right gas to produce when the fluid is electrolyzed */ public static void addElectrolyticSeparatorRecipe(FluidStack fluid, double energy, GasStack leftOutput, GasStack rightOutput) { addRecipe(Recipe.ELECTROLYTIC_SEPARATOR, new SeparatorRecipe(fluid, energy, leftOutput, rightOutput)); } /** * Add a Precision Sawmill recipe. * @param input - input ItemStack * @param primaryOutput - guaranteed output * @param secondaryOutput - possible extra output * @param chance - probability of obtaining extra output */ public static void addPrecisionSawmillRecipe(ItemStack input, ItemStack primaryOutput, ItemStack secondaryOutput, double chance) { addRecipe(Recipe.PRECISION_SAWMILL, new SawmillRecipe(input, primaryOutput, secondaryOutput, chance)); } /** * Add a Precision Sawmill recipe with no chance output * @param input - input ItemStack * @param primaryOutput - guaranteed output */ public static void addPrecisionSawmillRecipe(ItemStack input, ItemStack primaryOutput) { addRecipe(Recipe.PRECISION_SAWMILL, new SawmillRecipe(input, primaryOutput)); } /** * Add a Chemical Dissolution Chamber recipe. * @param input - input ItemStack * @param output - output GasStack */ public static void addChemicalDissolutionChamberRecipe(ItemStack input, GasStack output) { addRecipe(Recipe.CHEMICAL_DISSOLUTION_CHAMBER, new DissolutionRecipe(input, output)); } /** * Add a Chemical Washer recipe. * @param input - input GasStack * @param output - output GasStack */ public static void addChemicalWasherRecipe(GasStack input, GasStack output) { addRecipe(Recipe.CHEMICAL_WASHER, new WasherRecipe(input, output)); } /** * Add a Chemical Crystallizer recipe. * @param input - input GasStack * @param output - output ItemStack */ public static void addChemicalCrystallizerRecipe(GasStack input, ItemStack output) { addRecipe(Recipe.CHEMICAL_CRYSTALLIZER, new CrystallizerRecipe(input, output)); } /** * Add a Pressurized Reaction Chamber recipe. * @param inputSolid - input ItemStack * @param inputFluid - input FluidStack * @param inputGas - input GasStack * @param outputSolid - output ItemStack * @param outputGas - output GasStack * @param extraEnergy - extra energy needed by the recipe * @param ticks - amount of ticks it takes for this recipe to complete */ public static void addPRCRecipe(ItemStack inputSolid, FluidStack inputFluid, GasStack inputGas, ItemStack outputSolid, GasStack outputGas, double extraEnergy, int ticks) { addRecipe(Recipe.PRESSURIZED_REACTION_CHAMBER, new PressurizedRecipe(inputSolid, inputFluid, inputGas, outputSolid, outputGas, extraEnergy, ticks)); } public static void addSolarEvaporationRecipe(FluidStack inputFluid, FluidStack outputFluid) { addRecipe(Recipe.SOLAR_EVAPORATION_PLANT, new SolarEvaporationRecipe(inputFluid, outputFluid)); } public static void addSolarNeutronRecipe(GasStack inputGas, GasStack outputGas) { addRecipe(Recipe.SOLAR_NEUTRON_ACTIVATOR, new SolarNeutronRecipe(inputGas, outputGas)); } public static void addAmbientGas(int dimensionID, String ambientGasName) { addRecipe(Recipe.AMBIENT_ACCUMULATOR, new AmbientGasRecipe(dimensionID, ambientGasName)); } /** * Gets the Metallurgic Infuser Recipe for the InfusionInput in the parameters. * @param input - input Infusion * @return MetallurgicInfuserRecipe */ public static MetallurgicInfuserRecipe getMetallurgicInfuserRecipe(InfusionInput input) { if(input.isValid()) { HashMap<InfusionInput, MetallurgicInfuserRecipe> recipes = Recipe.METALLURGIC_INFUSER.get(); MetallurgicInfuserRecipe recipe = recipes.get(input); return recipe == null ? null : recipe.copy(); } return null; } /** * Gets the Chemical Infuser Recipe of the ChemicalPairInput in the parameters. * @param input - the pair of gases to infuse * @return ChemicalInfuserRecipe */ public static ChemicalInfuserRecipe getChemicalInfuserRecipe(ChemicalPairInput input) { if(input.isValid()) { HashMap<ChemicalPairInput, ChemicalInfuserRecipe> recipes = Recipe.CHEMICAL_INFUSER.get(); ChemicalInfuserRecipe recipe = recipes.get(input); return recipe == null ? null : recipe.copy(); } return null; } /** * Gets the Chemical Crystallizer Recipe for the defined Gas input. * @param input - GasInput * @return CrystallizerRecipe */ public static CrystallizerRecipe getChemicalCrystallizerRecipe(GasInput input) { if(input.isValid()) { HashMap<GasInput, CrystallizerRecipe> recipes = Recipe.CHEMICAL_CRYSTALLIZER.get(); CrystallizerRecipe recipe = recipes.get(input); return recipe == null ? null : recipe.copy(); } return null; } /** * Gets the Chemical Washer Recipe for the defined Gas input. * @param input - GasInput * @return WasherRecipe */ public static WasherRecipe getChemicalWasherRecipe(GasInput input) { if(input.isValid()) { HashMap<GasInput, WasherRecipe> recipes = Recipe.CHEMICAL_WASHER.get(); WasherRecipe recipe = recipes.get(input); return recipe == null ? null : recipe.copy(); } return null; } /** * Gets the Chemical Dissolution Chamber of the ItemStackInput in the parameters * @param input - ItemStackInput * @return DissolutionRecipe */ public static DissolutionRecipe getDissolutionRecipe(ItemStackInput input) { if(input.isValid()) { HashMap<ItemStackInput, DissolutionRecipe> recipes = Recipe.CHEMICAL_DISSOLUTION_CHAMBER.get(); DissolutionRecipe recipe = getRecipeTryWildcard(input, recipes); return recipe == null ? null : recipe.copy(); } return null; } /** * Gets the Chemical Oxidizer Recipe for the ItemStackInput in the parameters. * @param input - ItemStackInput * @return OxidationRecipe */ public static OxidationRecipe getOxidizerRecipe(ItemStackInput input) { if(input.isValid()) { HashMap<ItemStackInput, OxidationRecipe> recipes = Recipe.CHEMICAL_OXIDIZER.get(); OxidationRecipe recipe = getRecipeTryWildcard(input, recipes); return recipe == null ? null : recipe.copy(); } return null; } /** * Gets the ChanceMachineRecipe of the ItemStackInput in the parameters, using the map in the parameters. * @param input - ItemStackInput * @param recipes - Map of recipes * @return ChanceRecipe */ public static <RECIPE extends ChanceMachineRecipe<RECIPE>> RECIPE getChanceRecipe(ItemStackInput input, Map<ItemStackInput, RECIPE> recipes) { if(input.isValid()) { RECIPE recipe = getRecipeTryWildcard(input, recipes); return recipe == null ? null : recipe.copy(); } return null; } /** * Gets the BasicMachineRecipe of the ItemStackInput in the parameters, using the map in the parameters. * @param input - ItemStackInput * @param recipes - Map of recipes * @return BasicMachineRecipe */ public static <RECIPE extends BasicMachineRecipe<RECIPE>> RECIPE getRecipe(ItemStackInput input, Map<ItemStackInput, RECIPE> recipes) { if(input.isValid()) { RECIPE recipe = getRecipeTryWildcard(input, recipes); return recipe == null ? null : recipe.copy(); } return null; } /** * Gets the AdvancedMachineRecipe of the AdvancedInput in the parameters, using the map in the paramaters. * @param input - AdvancedInput * @param recipes - Map of recipes * @return AdvancedMachineRecipe */ public static <RECIPE extends AdvancedMachineRecipe<RECIPE>> RECIPE getRecipe(AdvancedMachineInput input, Map<AdvancedMachineInput, RECIPE> recipes) { if(input.isValid()) { RECIPE recipe = recipes.get(input); return recipe == null ? null : recipe.copy(); } return null; } /** * Get the Electrolytic Separator Recipe corresponding to electrolysing a given fluid. * @param input - the FluidInput to electrolyse fluid from * @return SeparatorRecipe */ public static SeparatorRecipe getElectrolyticSeparatorRecipe(FluidInput input) { if(input.isValid()) { HashMap<FluidInput, SeparatorRecipe> recipes = Recipe.ELECTROLYTIC_SEPARATOR.get(); SeparatorRecipe recipe = recipes.get(input); return recipe == null ? null : recipe.copy(); } return null; } public static SolarEvaporationRecipe getSolarEvaporationRecipe(FluidInput input) { if(input.isValid()) { HashMap<FluidInput, SolarEvaporationRecipe> recipes = Recipe.SOLAR_EVAPORATION_PLANT.get(); SolarEvaporationRecipe recipe = recipes.get(input); return recipe == null ? null : recipe.copy(); } return null; } public static SolarNeutronRecipe getSolarNeutronRecipe(GasInput input) { if(input.isValid()) { HashMap<GasInput, SolarNeutronRecipe> recipes = Recipe.SOLAR_NEUTRON_ACTIVATOR.get(); SolarNeutronRecipe recipe = recipes.get(input); return recipe == null ? null : recipe.copy(); } return null; } public static PressurizedRecipe getPRCRecipe(PressurizedInput input) { if(input.isValid()) { HashMap<PressurizedInput, PressurizedRecipe> recipes = Recipe.PRESSURIZED_REACTION_CHAMBER.get(); PressurizedRecipe recipe = recipes.get(input); return recipe == null ? null : recipe.copy(); } return null; } public static AmbientGasRecipe getDimensionGas(IntegerInput input) { HashMap<IntegerInput, AmbientGasRecipe> recipes = Recipe.AMBIENT_ACCUMULATOR.get(); AmbientGasRecipe recipe = recipes.get(input); return recipe == null ? null : recipe.copy(); } /** * Gets the whether the input ItemStack is in a recipe * @param itemstack - input ItemStack * @param recipes - Map of recipes * @return whether the item can be used in a recipe */ public static <RECIPE extends MachineRecipe<ItemStackInput, ?, RECIPE>> boolean isInRecipe(ItemStack itemstack, Map<ItemStackInput, RECIPE> recipes) { if(itemstack != null) { for(RECIPE recipe : recipes.values()) { ItemStackInput required = recipe.getInput(); if(required.useItemStackFromInventory(new ItemStack[]{itemstack}, 0, false)) { return true; } } } return false; } public static boolean isInPressurizedRecipe(ItemStack stack) { if(stack != null) { for(PressurizedInput key : (Set<PressurizedInput>)Recipe.PRESSURIZED_REACTION_CHAMBER.get().keySet()) { if(key.containsType(stack)) { return true; } } } return false; } public static <RECIPE extends MachineRecipe<ItemStackInput, ?, RECIPE>> RECIPE getRecipeTryWildcard(ItemStack stack, Map<ItemStackInput, RECIPE> recipes) { return getRecipeTryWildcard(new ItemStackInput(stack), recipes); } public static <RECIPE extends MachineRecipe<ItemStackInput, ?, RECIPE>> RECIPE getRecipeTryWildcard(ItemStackInput input, Map<ItemStackInput, RECIPE> recipes) { RECIPE recipe = recipes.get(input); if(recipe == null) { recipe = recipes.get(input.wildCopy()); } return recipe; } public static enum Recipe { ENERGIZED_SMELTER(MachineType.ENERGIZED_SMELTER.name, ItemStackInput.class, ItemStackOutput.class, SmeltingRecipe.class), ENRICHMENT_CHAMBER(MachineType.ENRICHMENT_CHAMBER.name, ItemStackInput.class, ItemStackOutput.class, EnrichmentRecipe.class), OSMIUM_COMPRESSOR(MachineType.OSMIUM_COMPRESSOR.name, AdvancedMachineInput.class, ItemStackOutput.class, OsmiumCompressorRecipe.class), COMBINER(MachineType.COMBINER.name, AdvancedMachineInput.class, ItemStackOutput.class, CombinerRecipe.class), CRUSHER(MachineType.CRUSHER.name, ItemStackInput.class, ItemStackOutput.class, CrusherRecipe.class), PURIFICATION_CHAMBER(MachineType.PURIFICATION_CHAMBER.name, AdvancedMachineInput.class, ItemStackOutput.class, PurificationRecipe.class), METALLURGIC_INFUSER(MachineType.METALLURGIC_INFUSER.name, InfusionInput.class, ItemStackOutput.class, MetallurgicInfuserRecipe.class), CHEMICAL_INFUSER(MachineType.CHEMICAL_INFUSER.name, ChemicalPairInput.class, GasOutput.class, ChemicalInfuserRecipe.class), CHEMICAL_OXIDIZER(MachineType.CHEMICAL_OXIDIZER.name, ItemStackInput.class, GasOutput.class, OxidationRecipe.class), CHEMICAL_INJECTION_CHAMBER(MachineType.CHEMICAL_INJECTION_CHAMBER.name, AdvancedMachineInput.class, ItemStackOutput.class, InjectionRecipe.class), ELECTROLYTIC_SEPARATOR(MachineType.ELECTROLYTIC_SEPARATOR.name, FluidInput.class, ChemicalPairOutput.class, SeparatorRecipe.class), PRECISION_SAWMILL(MachineType.PRECISION_SAWMILL.name, ItemStackInput.class, ChanceOutput.class, SawmillRecipe.class), CHEMICAL_DISSOLUTION_CHAMBER(MachineType.CHEMICAL_DISSOLUTION_CHAMBER.name, ItemStackInput.class, GasOutput.class, DissolutionRecipe.class), CHEMICAL_WASHER(MachineType.CHEMICAL_WASHER.name, GasInput.class, GasOutput.class, WasherRecipe.class), CHEMICAL_CRYSTALLIZER(MachineType.CHEMICAL_CRYSTALLIZER.name, GasInput.class, ItemStackOutput.class, CrystallizerRecipe.class), PRESSURIZED_REACTION_CHAMBER(MachineType.PRESSURIZED_REACTION_CHAMBER.name, PressurizedInput.class, PressurizedOutput.class, PressurizedRecipe.class), AMBIENT_ACCUMULATOR(MachineType.AMBIENT_ACCUMULATOR.name, IntegerInput.class, GasOutput.class, AmbientGasRecipe.class), SOLAR_EVAPORATION_PLANT("SolarEvaporationPlant", FluidInput.class, FluidOutput.class, SolarEvaporationRecipe.class), SOLAR_NEUTRON_ACTIVATOR(MachineType.SOLAR_NEUTRON_ACTIVATOR.name, GasInput.class, GasOutput.class, SolarNeutronRecipe.class); private HashMap recipes; private String recipeName; private Class<? extends MachineInput> inputClass; private Class<? extends MachineOutput> outputClass; private Class<? extends MachineRecipe> recipeClass; private <INPUT extends MachineInput<INPUT>, OUTPUT extends MachineOutput<OUTPUT>, RECIPE extends MachineRecipe<INPUT, ?, RECIPE>> Recipe(String name, Class<INPUT> input, Class<OUTPUT> output, Class<RECIPE> recipe) { recipeName = name; inputClass = input; outputClass = output; recipeClass = recipe; recipes = new HashMap<INPUT, RECIPE>(); } public <RECIPE extends MachineRecipe<?, ?, RECIPE>> void put(RECIPE recipe) { recipes.put(recipe.getInput(), recipe); } public String getRecipeName() { return recipeName; } public <INPUT> INPUT createInput(NBTTagCompound nbtTags) { try { MachineInput input = inputClass.newInstance(); input.load(nbtTags); return (INPUT)input; } catch(Exception e) { return null; } } public <RECIPE, INPUT> RECIPE createRecipe(INPUT input, NBTTagCompound nbtTags) { try { MachineOutput output = outputClass.newInstance(); output.load(nbtTags); Constructor<? extends MachineRecipe> construct = recipeClass.getDeclaredConstructor(inputClass, outputClass); return (RECIPE)construct.newInstance(input, output); } catch(Exception e) { return null; } } public boolean containsRecipe(ItemStack input) { for(Object obj : get().entrySet()) { if(obj instanceof Map.Entry) { Map.Entry entry = (Map.Entry)obj; if(entry.getKey() instanceof ItemStackInput) { ItemStack stack = ((ItemStackInput)entry.getKey()).ingredient; if(StackUtils.equalsWildcard(stack, input)) { return true; } } else if(entry.getKey() instanceof FluidInput) { if(((FluidInput)entry.getKey()).ingredient.isFluidEqual(input)) { return true; } } else if(entry.getKey() instanceof AdvancedMachineInput) { ItemStack stack = ((AdvancedMachineInput)entry.getKey()).itemStack; if(StackUtils.equalsWildcard(stack, input)) { return true; } } } } return false; } public boolean containsRecipe(Fluid input) { for(Object obj : get().entrySet()) { if(obj instanceof Map.Entry) { Map.Entry entry = (Map.Entry)obj; if(entry.getKey() instanceof FluidInput) { if(((FluidInput)entry.getKey()).ingredient.getFluid() == input) { return true; } } } } return false; } public HashMap get() { return recipes; } } }