compat for (hopefully) robust external recipe registering

This commit is contained in:
Boblet 2025-03-06 14:25:30 +01:00
parent a78dae4a8e
commit 5d24e76601
24 changed files with 343 additions and 21 deletions

View File

@ -19,6 +19,8 @@
* Similar to soil sand, entities will sink in taint and get slowed down
* The sludge consumeth
* `enableGuns` config option now applies to SEDNA system guns, simply canceling all gun-related keybinds
* Cinnabar dust, if registered by another mod, can now be acidized into cinnabar using hydrogen peroxide
* Copper wires, like AA and gold, can now be welded into dense wires
## Fixed
* Fixed animation error on the MAS-36

View File

@ -12,6 +12,7 @@ credits=HbMinecraft,\
\ MellowArpeggiation (new animation system, turbine sounds, sound fixes, industrial lights, better particle diodes),\
\ Pheo (textures, various machines, models, weapons),\
\ Vær (gas centrifuges, better worldgen, ZIRNOX, CP-1 parts, starter guide, new cyclotron, weapon animations),\
\ UFFR (RTG pellets, guns, casings, euphemium capacitor, nucleartech.wiki),\
\ LePeep (coilgun model, BDCL QC),\
\ Adam29 (liquid petroleum, ethanol, electric furnace),\
\ Pvndols (thorium fuel recipe, gas turbine),\
@ -33,7 +34,6 @@ credits=HbMinecraft,\
\ Maksymisio (polish localization)\
\ el3ctro4ndre (italian localization),\
\ Pu-238 (Tom impact effects),\
\ UFFR (RTG pellets, guns, casings, euphemium capacitor),\
\ Frooz (gun models),\
\ VT-6/24 (models, textures),\
\ Nos (models),\

View File

@ -0,0 +1,13 @@
package api.hbm.recipe;
public interface IRecipeRegisterListener {
/**
* Called during SerializableRecipe.initialize(), after the defaults are loaded but before the template is written.
* Due to the way the recipes are handled, calling it once is not enough, it has to be called once for every SerializableRecipe
* instance handled, therefore the load operation passes the type name of the recipe, so that the listeners know what type of recipe
* to register at that point. Note that the actual SerializableRecipe instance is irrelevant, since recipes are static anyway,
* and direct tampering with SerializableRecipes is not recommended.
*/
public void onRecipeLoad(String recipeClassName);
}

View File

@ -0,0 +1,17 @@
package api.hbm.recipe;
/*
Quick guide on how to make robust and safe recipe integration:
* Implement IRecipeRegisterListener, the resulting class will handle all recipes, and the onRecipeLoad method is called every time the SerializableRecipe system updates
* Register your IRecipeRegisterListener using CompatExternal.registerRecipeRegisterListener, this has to happen before the SerializableRecipe initializes, doing this during PreInit should be safe
* In your IRecipeRegisterListener, check the supplied recipe type string (which will be the class name of the SerializableRecipe currently being registered) and register your custom recipes accordingly using CompatRecipeRegistry
Explanation:
* Order of operations is important for the recipes to work, if recipes are loaded outside the scope of SerializableRecipe.initialize, they will not work correctly
* If recipes are registered before the init, they are deleted, if they are registered after the init, they will not be part of the config template file, and get deleted when running /ntmreload
* Machines change all the time, so the recipe classes should not be considered API, since the compat would break immediately if a machine is changed or removed
* CompatRecipeRegistry promises to never change its method signatures, and have solid sanity checking when recipes are registered, allowing it to make the bst of whatever data is thrown at it
* Using this dedicated registry class means that even if a machine is changed or removed, the recipes will continue to work to the best of its abilities
*/

View File

@ -46,6 +46,8 @@ public class ArcWelderRecipes extends SerializableRecipe {
new OreDictStack(ANY_BISMOIDBRONZE.plateCast(), 2), new OreDictStack(CMB.plateWelded(), 1), new ComparableStack(ModItems.ingot_cft)));
//Dense Wires
recipes.add(new ArcWelderRecipe(new ItemStack(ModItems.wire_dense, 1, Mats.MAT_COPPER.id), 100, 10_000L,
new OreDictStack(CU.wireFine(), 8)));
recipes.add(new ArcWelderRecipe(new ItemStack(ModItems.wire_dense, 1, Mats.MAT_ALLOY.id), 100, 10_000L,
new OreDictStack(ALLOY.wireFine(), 8)));
recipes.add(new ArcWelderRecipe(new ItemStack(ModItems.wire_dense, 1, Mats.MAT_GOLD.id), 100, 10_000L,

View File

@ -77,7 +77,7 @@ public class BlastFurnaceRecipes extends SerializableRecipe {
hiddenRecipes.add(new ComparableStack(ModItems.meteorite_sword_alloyed));
}
private static void addRecipe(Object in1, Object in2, ItemStack out) {
public static void addRecipe(Object in1, Object in2, ItemStack out) {
if(in1 instanceof Item) in1 = new ComparableStack((Item) in1);
if(in1 instanceof Block) in1 = new ComparableStack((Block) in1);

View File

@ -19,7 +19,7 @@ import net.minecraft.item.ItemStack;
public class BreederRecipes extends SerializableRecipe {
private static HashMap<ComparableStack, BreederRecipe> recipes = new HashMap();
public static HashMap<ComparableStack, BreederRecipe> recipes = new HashMap();
@Override
public void registerDefaults() {

View File

@ -43,7 +43,7 @@ import net.minecraftforge.oredict.OreDictionary;
public class CentrifugeRecipes extends SerializableRecipe {
private static HashMap<AStack, ItemStack[]> recipes = new HashMap();
public static HashMap<AStack, ItemStack[]> recipes = new HashMap();
@Override
public void registerDefaults() {

View File

@ -38,7 +38,7 @@ public class ChemplantRecipes extends SerializableRecipe {
@Override
public void registerDefaults() {
//6-30, formerly oil cracking, coal liquefaction and solidifciation
//6-30, formerly oil cracking, coal liquefaction and solidification
registerOtherOil();
recipes.add(new ChemRecipe(36, "COOLANT", 50)

View File

@ -25,7 +25,7 @@ import net.minecraft.item.ItemStack;
public class CokerRecipes extends SerializableRecipe {
private static HashMap<FluidType, Triplet<Integer, ItemStack, FluidStack>> recipes = new HashMap();
public static HashMap<FluidType, Triplet<Integer, ItemStack, FluidStack>> recipes = new HashMap();
@Override
public void registerDefaults() {
@ -68,7 +68,7 @@ public class CokerRecipes extends SerializableRecipe {
registerRecipe(VITRIOL, 4000, new ItemStack(ModItems.powder_iron), new FluidStack(SULFURIC_ACID, 500));
}
private static void registerAuto(FluidType fluid, FluidType type) {
public static void registerAuto(FluidType fluid, FluidType type) {
registerSFAuto(fluid, 820_000L, DictFrame.fromOne(ModItems.coke, EnumCokeType.PETROLEUM), type); //3200 burntime * 1.25 burntime bonus * 200 TU/t + 20000TU per operation
}
private static void registerSFAuto(FluidType fluid, long tuPerSF, ItemStack fuel, FluidType type) {

View File

@ -34,7 +34,7 @@ import net.minecraft.item.ItemStack;
public class CombinationRecipes extends SerializableRecipe {
private static HashMap<Object, Pair<ItemStack, FluidStack>> recipes = new HashMap();
public static HashMap<Object, Pair<ItemStack, FluidStack>> recipes = new HashMap();
@Override
public void registerDefaults() {

View File

@ -36,7 +36,7 @@ public class CrackingRecipes extends SerializableRecipe {
public static final int xyl_crack_aroma = 80;
public static final int xyl_crack_petro = 20;
private static Map<FluidType, Pair<FluidStack, FluidStack>> cracking = new HashMap();
public static Map<FluidType, Pair<FluidStack, FluidStack>> cracking = new HashMap();
@Override
public void registerDefaults() {

View File

@ -235,6 +235,12 @@ public class CrystallizerRecipes extends SerializableRecipe {
registerRecipe(P_WHITE.dust(), new CrystallizerRecipe(new ItemStack(ModItems.ingot_phosphorus), utilityTime), new FluidStack(Fluids.AROMATICS, 50));
}
/// COMPAT CINNABAR DUST ///
List<ItemStack> dustCinnabar = OreDictionary.getOres(CINNABAR.dust());
if(dustCinnabar != null && !dustCinnabar.isEmpty()) {
registerRecipe(CINNABAR.dust(), new CrystallizerRecipe(new ItemStack(ModItems.cinnebar), utilityTime), new FluidStack(Fluids.PEROXIDE, 50));
}
if(!IMCCrystallizer.buffer.isEmpty()) {
recipes.putAll(IMCCrystallizer.buffer);
MainRegistry.logger.info("Fetched " + IMCCrystallizer.buffer.size() + " IMC crystallizer recipes!");

View File

@ -19,7 +19,7 @@ import net.minecraft.item.ItemStack;
public class FractionRecipes extends SerializableRecipe {
private static Map<FluidType, Pair<FluidStack, FluidStack>> fractions = new HashMap();
public static Map<FluidType, Pair<FluidStack, FluidStack>> fractions = new HashMap();
@Override
public void registerDefaults() {
@ -40,8 +40,8 @@ public class FractionRecipes extends SerializableRecipe {
fractions.put(Fluids.OIL_COKER, new Pair(new FluidStack(Fluids.CRACKOIL, 30), new FluidStack(Fluids.HEATINGOIL, 70)));
fractions.put(Fluids.NAPHTHA_COKER, new Pair(new FluidStack(Fluids.NAPHTHA_CRACK, 75), new FluidStack(Fluids.LIGHTOIL_CRACK, 25)));
fractions.put(Fluids.GAS_COKER, new Pair(new FluidStack(Fluids.AROMATICS, 25), new FluidStack(Fluids.CARBONDIOXIDE, 75)));
fractions.put(Fluids.CHLOROCALCITE_MIX, new Pair(new FluidStack(Fluids.CHLOROCALCITE_CLEANED, 50), new FluidStack(Fluids.COLLOID, 50)));
fractions.put(Fluids.BAUXITE_SOLUTION, new Pair(new FluidStack(Fluids.REDMUD, 50), new FluidStack(Fluids.SODIUM_ALUMINATE, 50)));
fractions.put(Fluids.CHLOROCALCITE_MIX, new Pair(new FluidStack(Fluids.CHLOROCALCITE_CLEANED, 50), new FluidStack(Fluids.COLLOID, 50)));
fractions.put(Fluids.BAUXITE_SOLUTION, new Pair(new FluidStack(Fluids.REDMUD, 50), new FluidStack(Fluids.SODIUM_ALUMINATE, 50)));
}
public static Pair<FluidStack, FluidStack> getFractions(FluidType oil) {

View File

@ -14,7 +14,7 @@ import com.hbm.tileentity.machine.TileEntityHadron.EnumHadronState;
import net.minecraft.init.Items;
import net.minecraft.item.ItemStack;
public class HadronRecipes extends SerializableRecipe {
@Deprecated public class HadronRecipes extends SerializableRecipe {
/*
* Since we're dealing with like 10 or so recipes, using a HashMap (or to combine two keys, a HashMap *in* a HashMap)

View File

@ -18,7 +18,7 @@ import net.minecraft.item.ItemStack;
public class HydrotreatingRecipes extends SerializableRecipe {
private static HashMap<FluidType, Triplet<FluidStack, FluidStack, FluidStack>> recipes = new HashMap();
public static HashMap<FluidType, Triplet<FluidStack, FluidStack, FluidStack>> recipes = new HashMap();
@Override
public void registerDefaults() {

View File

@ -27,7 +27,7 @@ import net.minecraftforge.oredict.OreDictionary;
public class LiquefactionRecipes extends SerializableRecipe {
private static HashMap<Object, FluidStack> recipes = new HashMap();
public static HashMap<Object, FluidStack> recipes = new HashMap();
@Override
public void registerDefaults() {

View File

@ -124,7 +124,7 @@ public class PyroOvenRecipes extends SerializableRecipe {
.out(new FluidStack(Fluids.HYDROGEN, 8_000)).out(new ItemStack(ModItems.ingot_graphite, 1)));
}
private static void registerSFAuto(FluidType fluid) {
public static void registerSFAuto(FluidType fluid) {
registerSFAuto(fluid, 1_440_000L, ModItems.solid_fuel); //3200 burntime * 1.5 burntime bonus * 300 TU/t
}
private static void registerSFAuto(FluidType fluid, long tuPerSF, Item fuel) {

View File

@ -18,7 +18,7 @@ import net.minecraft.item.ItemStack;
public class ReformingRecipes extends SerializableRecipe {
private static HashMap<FluidType, Triplet<FluidStack, FluidStack, FluidStack>> recipes = new HashMap();
public static HashMap<FluidType, Triplet<FluidStack, FluidStack, FluidStack>> recipes = new HashMap();
@Override
public void registerDefaults() {

View File

@ -56,7 +56,7 @@ public class SolidificationRecipes extends SerializableRecipe {
//aromatics can be idfk wax or soap or sth, perhaps artificial lubricant?
//on that note, add more leaded variants
private static HashMap<FluidType, Pair<Integer, ItemStack>> recipes = new HashMap();
public static HashMap<FluidType, Pair<Integer, ItemStack>> recipes = new HashMap();
@Override
public void registerDefaults() {

View File

@ -38,8 +38,8 @@ import net.minecraftforge.oredict.OreDictionary;
public class AnvilRecipes extends SerializableRecipe {
private static List<AnvilSmithingRecipe> smithingRecipes = new ArrayList();
private static List<AnvilConstructionRecipe> constructionRecipes = new ArrayList();
public static List<AnvilSmithingRecipe> smithingRecipes = new ArrayList();
public static List<AnvilConstructionRecipe> constructionRecipes = new ArrayList();
public static void register() {
registerSmithing();

View File

@ -29,14 +29,16 @@ import com.hbm.items.ModItems;
import com.hbm.main.MainRegistry;
import com.hbm.util.Tuple.Pair;
import api.hbm.recipe.IRecipeRegisterListener;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
//the anti-spaghetti. this class provides so much functionality and saves so much time, i just love you, SerializableRecipe <3
public abstract class SerializableRecipe { //TODO: #1969
public abstract class SerializableRecipe {
public static final Gson gson = new Gson();
public static List<SerializableRecipe> recipeHandlers = new ArrayList();
public static List<IRecipeRegisterListener> additionalListeners = new ArrayList();
public boolean modified = false;
@ -114,6 +116,10 @@ public abstract class SerializableRecipe { //TODO: #1969
MainRegistry.logger.info("No recipe file found, registering defaults for " + recipe.getFileName());
recipe.registerDefaults();
for(IRecipeRegisterListener listener : additionalListeners) {
listener.onRecipeLoad(recipe.getClass().getSimpleName());
}
File recTemplate = new File(recDir.getAbsolutePath() + File.separatorChar + "_" + recipe.getFileName());
MainRegistry.logger.info("Writing template file " + recTemplate.getName());
recipe.writeTemplateFile(recTemplate);

View File

@ -10,11 +10,14 @@ import java.util.function.Consumer;
import api.hbm.energymk2.IEnergyHandlerMK2;
import api.hbm.energymk2.IEnergyReceiverMK2;
import api.hbm.fluid.IFluidUser;
import api.hbm.recipe.IRecipeRegisterListener;
import com.hbm.blocks.BlockDummyable;
import com.hbm.entity.missile.EntityMissileCustom;
import com.hbm.explosion.ExplosionNukeSmall;
import com.hbm.inventory.fluid.FluidType;
import com.hbm.inventory.fluid.tank.FluidTank;
import com.hbm.inventory.recipes.loader.SerializableRecipe;
import com.hbm.items.weapon.ItemCustomMissilePart.WarheadType;
import com.hbm.tileentity.machine.TileEntityDummy;
import com.hbm.tileentity.turret.TileEntityTurretSentry;
@ -186,6 +189,15 @@ public class CompatExternal {
public static void setWarheadLabel(WarheadType type, String label) { type.labelCustom = label; }
public static void setWarheadImpact(WarheadType type, Consumer<EntityMissileCustom> impact) { type.impactCustom = impact; }
public static void setWarheadUpdate(WarheadType type, Consumer<EntityMissileCustom> update) { type.updateCustom = update; }
/**
* Registers an IRecipeRegisterListener to the recipe system. The listener is called every time a SerializableRecipe instance has its recipes loaded, before the
* config files are written, but after the defaults are initialized.
* @param listener
*/
public static void registerRecipeRegisterListener(IRecipeRegisterListener listener) {
SerializableRecipe.additionalListeners.add(listener);
}
public static void compatExamples() {
// Makes all cows be targeted by turrets if player mode is active in addition to the existing rules. Applies to all entities that inherit EntityCow.

View File

@ -0,0 +1,264 @@
package com.hbm.util;
import java.util.Arrays;
import com.hbm.interfaces.Untested;
import com.hbm.inventory.FluidStack;
import com.hbm.inventory.RecipesCommon.*;
import com.hbm.inventory.fluid.FluidType;
import com.hbm.inventory.material.Mats.MaterialStack;
import com.hbm.inventory.recipes.*;
import com.hbm.inventory.recipes.AmmoPressRecipes.AmmoPressRecipe;
import com.hbm.inventory.recipes.ArcFurnaceRecipes.ArcFurnaceRecipe;
import com.hbm.inventory.recipes.ArcWelderRecipes.ArcWelderRecipe;
import com.hbm.inventory.recipes.BreederRecipes.BreederRecipe;
import com.hbm.inventory.recipes.CrucibleRecipes.CrucibleRecipe;
import com.hbm.inventory.recipes.CrystallizerRecipes.CrystallizerRecipe;
import com.hbm.inventory.recipes.ElectrolyserFluidRecipes.ElectrolysisRecipe;
import com.hbm.inventory.recipes.ElectrolyserMetalRecipes.ElectrolysisMetalRecipe;
import com.hbm.inventory.recipes.ExposureChamberRecipes.ExposureChamberRecipe;
import com.hbm.inventory.recipes.ParticleAcceleratorRecipes.ParticleAcceleratorRecipe;
import com.hbm.inventory.recipes.PedestalRecipes.PedestalExtraCondition;
import com.hbm.inventory.recipes.PedestalRecipes.PedestalRecipe;
import com.hbm.inventory.recipes.PyroOvenRecipes.PyroOvenRecipe;
import com.hbm.inventory.recipes.RotaryFurnaceRecipes.RotaryFurnaceRecipe;
import com.hbm.inventory.recipes.ChemplantRecipes.ChemRecipe;
import com.hbm.inventory.recipes.CompressorRecipes.CompressorRecipe;
import com.hbm.inventory.recipes.SolderingRecipes.SolderingRecipe;
import com.hbm.inventory.recipes.anvil.AnvilRecipes;
import com.hbm.inventory.recipes.anvil.AnvilRecipes.AnvilConstructionRecipe;
import com.hbm.inventory.recipes.anvil.AnvilRecipes.AnvilOutput;
import com.hbm.inventory.recipes.anvil.AnvilRecipes.OverlayType;
import com.hbm.items.machine.ItemStamp.StampType;
import com.hbm.util.Tuple.Pair;
import com.hbm.util.Tuple.Triplet;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
/**
* Public methods for registering recipes. Method signature should never change, only the body to reflect any changes to recipes/machines themselves.
* Recipe creation is either 1:1 with the original SerializableRecipe or even simpler (in the case of the compressor, two FluidStacks instead of a ton of loose numbers).
* Call these with a registered IRecipeRegisterListener, otherwise the recipe loading/reloading will break the custom recipes.
* @author hbm
*/
@Untested
public class CompatRecipeRegistry {
public static void registerPress(StampType stamp, AStack input, ItemStack output) {
PressRecipes.recipes.put(new Pair(input, stamp), output);
}
/** Same loose rules as BlastFurnaceRecipes, valid inputs are Items, Blocks, ItemStacks, ComparableStacks, Strings (for oredict) and DictFrames */
public static void registerBlastFurnace(Object[] inputs, ItemStack output) {
if(inputs.length != 2) return;
BlastFurnaceRecipes.addRecipe(inputs[0], inputs[1], output);
}
public static void registerShredder(AStack input, ItemStack output) {
for(ItemStack allItems : input.extractForNEI()) {
ComparableStack comp = new ComparableStack(allItems);
ShredderRecipes.shredderRecipes.put(comp, output);
}
}
/** Items should strictly be categorized as pcb, topping or solder. An item that is used as a topping in one recipe should not be a pcb in another.
* This is because the soldering station's item IO will automatically place items based on this category, and having items in more than one category would break it. */
public static void registerSoldering(ItemStack output, int time, long power, FluidStack fluid, AStack[] toppings, AStack[] pcb, AStack[] solder) {
SolderingRecipes.recipes.add(new SolderingRecipe(output, time, power, fluid, copyFirst(toppings, 3), copyFirst(pcb, 2), copyFirst(solder, 1)));
}
/** Chemplant recipes need unique IDs, game will crash when an ID collision is detected! */
public static void registerChemplant(int id, String name, int duration, AStack[] inputItems, FluidStack[] inputFluids, ItemStack[] outputItems, FluidStack[] outputFluids) {
ChemRecipe recipe = new ChemRecipe(id, name, duration);
if(inputItems != null) recipe.inputItems(copyFirst(inputItems, 4));
if(inputFluids != null) recipe.inputFluids(copyFirst(inputFluids, 2));
if(outputItems != null) recipe.outputItems(copyFirst(outputItems, 4));
if(outputFluids != null) recipe.outputFluids(copyFirst(outputFluids, 2));
ChemplantRecipes.recipes.add(recipe);
}
/** Either solid or liquid output can be null */
public static void registerCombination(AStack input, ItemStack output, FluidStack fluid) {
if(output == null && fluid == null) return;
Object o = input instanceof OreDictStack ? ((OreDictStack) input).name : input;
CombinationRecipes.recipes.put(o, new Pair(output, fluid));
}
/** Crucible recipes need unique IDs, game will crash when an ID collision is detected! */
public static void registerCrucible(int index, String name, int frequency, ItemStack icon, MaterialStack[] input, MaterialStack[] output) {
CrucibleRecipe recipe = new CrucibleRecipe(index, name, frequency, icon).inputs(input).outputs(output);
CrucibleRecipes.recipes.add(recipe);
}
public static void registerCentrifuge(AStack input, ItemStack[] outputs) {
CentrifugeRecipes.recipes.put(input, copyFirst(outputs, 4));
}
public static void registerCrystallizer(AStack input, ItemStack output, int time, float productivity, FluidStack fluid) {
CrystallizerRecipe recipe = new CrystallizerRecipe(output, time).prod(productivity);
CrystallizerRecipes.registerRecipe(input instanceof OreDictStack ? ((OreDictStack) input).name : input, recipe, fluid);
}
/** Fractions always use 100mB of input fluid per operation. None of the outputs can be null. */
public static void registerFraction(FluidType input, FluidStack[] output) {
if(output.length != 2) return;
FractionRecipes.fractions.put(input, new Pair(output[0], output[1]));
}
/** Cracking always uses 100mB of input fluid and 200mB of steam per operation. None of the outputs can be null. */
public static void registerCracking(FluidType input, FluidStack[] output) {
if(output.length != 2) return;
CrackingRecipes.cracking.put(input, new Pair(output[0], output[1]));
}
/** Reforming always uses 100mB of input fluid per operation. None of the outputs can be null. */
public static void registerReforming(FluidType input, FluidStack[] output) {
output = copyFirst(output, 3);
if(output.length < 3) return;
ReformingRecipes.recipes.put(input, new Triplet(output[0], output[1], output[2]));
}
/** Hydrotreating always uses 100mB of input fluid per operation. None of the outputs can be null. */
public static void registerHydrotreating(FluidType input, FluidStack hydrogen, FluidStack[] output) {
output = copyFirst(output, 2);
if(output.length < 2) return;
HydrotreatingRecipes.recipes.put(input, new Triplet(hydrogen, output[0], output[1]));
}
public static void registerLiquefaction(AStack input, FluidStack output) {
LiquefactionRecipes.recipes.put(input instanceof OreDictStack ? ((OreDictStack) input).name : input, output);
}
public static void registerSolidifying(FluidStack input, ItemStack output) {
SolidificationRecipes.recipes.put(input.type, new Pair(input.fill, output));
}
public static void registerCoker(FluidStack input, ItemStack output, FluidStack fluid) {
CokerRecipes.recipes.put(input.type, new Triplet(input.fill, output, fluid));
}
/** Registers a coker recipe based on the standardized fluid to coke values */
public static void registerCokerAuto(FluidType input, FluidType output) {
CokerRecipes.registerAuto(input, output);
}
public static void registerPyro(FluidStack inputFluid, AStack inputItem, FluidStack outputFluid, ItemStack outputItem, int duration) {
PyroOvenRecipes.recipes.add(new PyroOvenRecipe(duration).in(inputFluid).in(inputItem).out(outputFluid).out(outputItem));
}
/** Registers a pyro oven recipe based on the standardized fluid to solid fuel values */
public static void registerPyroAuto(FluidType input) {
PyroOvenRecipes.registerSFAuto(input);
}
/** Breeding reactor does not handle OreDictStacks */
public static void registerBreeder(ComparableStack input, ItemStack output, int flux) {
BreederRecipes.recipes.put(input, new BreederRecipe(output, flux));
}
public static void registerCyclotron(ComparableStack box, AStack target, ItemStack output, int antimatter) {
CyclotronRecipes.recipes.put(new Pair(box, target), new Pair(output, antimatter));
}
/** Fuel pools do not handle OreDictStacks */
public static void registerFuelPool(ComparableStack input, ItemStack output) {
FuelPoolRecipes.recipes.put(input, output);
}
//TBI mixer
public static void registerOutgasser(AStack input, ItemStack output, FluidStack fluid) {
OutgasserRecipes.recipes.put(input, new Pair(output, fluid));
}
public static void registerCompressor(FluidStack input, FluidStack output, int time) {
CompressorRecipes.recipes.put(new Pair(input.type, input.pressure), new CompressorRecipe(input.fill, output, time));
}
/** Byproduct array can be null, fluid output length must be 2 */
public static void registerElectrolyzerFluid(FluidStack input, FluidStack[] output, ItemStack[] byproduct, int time) {
output = copyFirst(output, 2);
if(output.length < 2) return;
if(byproduct != null) byproduct = copyFirst(byproduct, 3);
ElectrolyserFluidRecipes.recipes.put(input.type, new ElectrolysisRecipe(input.fill, output[0], output[1], time, byproduct));
}
/** Output array length must be 2, outputs can be null. Byproduct array can be null. */
public static void registerElectrolyzerMetal(AStack input, MaterialStack[] output, ItemStack[] byproduct, int time) {
output = copyFirst(output, 2);
if(byproduct != null) byproduct = copyFirst(byproduct, 6);
ElectrolyserMetalRecipes.recipes.put(input, new ElectrolysisMetalRecipe(output[0], output[1], time, byproduct));
}
public static void registerArcWelder(ItemStack output, int time, long power, FluidStack fluid, AStack[] inputs) {
ArcWelderRecipes.recipes.add(new ArcWelderRecipe(output, time, power, fluid, copyFirst(inputs, 3)));
}
public static void registerRotaryFurnace(MaterialStack output, int time, int steam, FluidStack fluid, AStack[] inputs) {
RotaryFurnaceRecipes.recipes.add(new RotaryFurnaceRecipe(output, time, steam, fluid, copyFirst(inputs, 3)));
}
/** Particles will always perform 8 recipes */
public static void registerExposureChamber(AStack particle, AStack input, ItemStack output) {
ExposureChamberRecipes.recipes.add(new ExposureChamberRecipe(particle, input, output));
}
/** Input needs two AStacks, output can take 1-2 ItemStacks. If the same recipe with different
* momentum should yield different results, register the lower momentum recipes first. */
public static void registerParticleAccelerator(AStack[] input, int momentum, ItemStack[] output) {
input = copyFirst(input, 2);
if(input.length < 2) return;
output = copyFirst(output, 2);
if(output.length < 1) return;
ParticleAcceleratorRecipes.recipes.add(new ParticleAcceleratorRecipe(input[0], input[1], momentum, output[0], output.length > 1 ? output[1] : null));
}
public static void registerAmmoPress(ItemStack output, AStack[] input) {
if(input.length != 9) return;
AmmoPressRecipes.recipes.add(new AmmoPressRecipe(output, input));
}
public static void registerAssembler(ItemStack output, AStack[] input, int time) {
AssemblerRecipes.makeRecipe(new ComparableStack(output), copyFirst(input, 12), time);
}
/** Registers an assembler recipe but with the template only being obtainable via the specified folders */
public static void registerAssembler(ItemStack output, AStack[] input, int time, Item... folder) {
AssemblerRecipes.makeRecipe(new ComparableStack(output), copyFirst(input, 12), time, folder);
}
public static void registerAnvilConstruction(AStack[] input, AnvilOutput[] output, int tier, int overlayIndex) {
AnvilRecipes.constructionRecipes.add(new AnvilConstructionRecipe(input, output).setTier(tier).setOverlay(EnumUtil.grabEnumSafely(OverlayType.class, overlayIndex)));
}
public static void registerAnvilConstruction(AStack[] input, AnvilOutput[] output, int tierLower, int tierUpper, int overlayIndex) {
AnvilRecipes.constructionRecipes.add(new AnvilConstructionRecipe(input, output).setTierRange(tierLower, tierUpper).setOverlay(EnumUtil.grabEnumSafely(OverlayType.class, overlayIndex)));
}
public static void registerPedestal(ItemStack output, AStack[] input) {
registerPedestal(output, input, 0);
}
public static void registerPedestal(ItemStack output, AStack[] input, int condition) {
input = copyFirst(input, 9);
if(input.length < 9) return;
PedestalRecipes.recipes.add(new PedestalRecipe(output, input).extra(EnumUtil.grabEnumSafely(PedestalExtraCondition.class, condition)));
}
/** Either output or fluid can be null */
public static void registerArcFurnace(AStack input, ItemStack output, MaterialStack fluid) {
if(output == null && fluid == null) return;
ArcFurnaceRecipes.recipeList.add(new Pair(input, new ArcFurnaceRecipe().solid(output).fluid(fluid)));
}
/////////////////////////////////////////////////////////////////////////////////////////
/** If the supplied array exceeds the specified length, creates a copy and trunkates the array. Otherwise, returns the original array */
private static <T> T[] copyFirst(T[] array, int amount) {
if(array.length <= amount) return array;
return Arrays.copyOf(array, amount);
}
}