package gregtech.api.util; import static gregtech.api.recipe.RecipeMaps.scannerFakeRecipes; import static gregtech.api.util.GTRecipeMapUtil.convertCellToFluid; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Optional; import net.minecraft.init.Items; import net.minecraft.item.ItemStack; import net.minecraftforge.fluids.FluidStack; import cpw.mods.fml.common.registry.GameRegistry; import gregtech.api.enums.GTValues; import gregtech.api.enums.Materials; import gregtech.api.enums.OrePrefixes; import gregtech.api.enums.TierEU; import gregtech.api.interfaces.IRecipeMap; import gregtech.api.objects.ItemData; import gregtech.api.recipe.RecipeCategories; import gregtech.api.recipe.RecipeMaps; import gregtech.api.recipe.RecipeMetadataKey; import gregtech.api.recipe.metadata.SimpleRecipeMetadataKey; import gregtech.common.items.IDMetaItem03; import gregtech.common.items.MetaGeneratedItem03; import gtnhlanth.common.item.ItemPhotolithographicMask; import gtnhlanth.common.item.MaskList; import gtnhlanth.common.register.LanthItemList; // this class is intended to be import-static-ed on every recipe script // so take care to not put unrelated stuff here! public class GTRecipeConstants { /** * Set to true to signal the recipe require low gravity. do nothing if recipe set specialValue explicitly. Can * coexist with CLEANROOM just fine */ public static final RecipeMetadataKey LOW_GRAVITY = SimpleRecipeMetadataKey .create(Boolean.class, "low_gravity"); /** * Set to true to signal the recipe require cleanroom. do nothing if recipe set specialValue explicitly. Can coexist * with LOW_GRAVITY just fine */ public static final RecipeMetadataKey CLEANROOM = SimpleRecipeMetadataKey .create(Boolean.class, "cleanroom"); /** * Common additive to use in recipe, e.g. for PBF, this is coal amount. */ public static final RecipeMetadataKey ADDITIVE_AMOUNT = SimpleRecipeMetadataKey .create(Integer.class, "additives"); /** * Used for fusion reactor. Denotes ignition threshold. */ public static final RecipeMetadataKey FUSION_THRESHOLD = SimpleRecipeMetadataKey .create(Integer.class, "fusion_threshold"); /** * Research time in a scanner used in ticks. */ public static final RecipeMetadataKey RESEARCH_TIME = SimpleRecipeMetadataKey .create(Integer.class, "research_time"); /** * Fuel type. TODO should we use enum directly? */ public static final RecipeMetadataKey FUEL_TYPE = SimpleRecipeMetadataKey .create(Integer.class, "fuel_type"); /** * Fuel value. */ public static final RecipeMetadataKey FUEL_VALUE = SimpleRecipeMetadataKey .create(Integer.class, "fuel_value"); /** * Required heat for heating coil (Kelvin). */ public static final RecipeMetadataKey COIL_HEAT = SimpleRecipeMetadataKey .create(Integer.class, "coil_heat"); /** * Research item used by assline recipes. */ public static final RecipeMetadataKey RESEARCH_ITEM = SimpleRecipeMetadataKey .create(ItemStack.class, "research_item"); /** * For assembler. It accepts a single item as oredict. It looks like no one uses this anyway... */ public static final RecipeMetadataKey OREDICT_INPUT = SimpleRecipeMetadataKey .create(Object.class, "oredict_input"); /** * Replicator output material. */ public static final RecipeMetadataKey MATERIAL = SimpleRecipeMetadataKey .create(Materials.class, "material"); /** * Marker for {@link #UniversalArcFurnace} to tell that the recipe belongs to recycling category. */ public static final RecipeMetadataKey RECYCLE = SimpleRecipeMetadataKey.create(Boolean.class, "recycle"); /** * For Microwave. */ public static final RecipeMetadataKey EXPLODE = SimpleRecipeMetadataKey.create(Boolean.class, "explode"); /** * For Microwave. */ public static final RecipeMetadataKey ON_FIRE = SimpleRecipeMetadataKey.create(Boolean.class, "on_fire"); /** * Nano Forge Tier. */ public static final RecipeMetadataKey NANO_FORGE_TIER = SimpleRecipeMetadataKey .create(Integer.class, "nano_forge_tier"); /** * FOG Exotic recipe tier. */ public static final RecipeMetadataKey FOG_EXOTIC_TIER = SimpleRecipeMetadataKey .create(Integer.class, "fog_exotic_tier"); /** * FOG Plasma recipe tier. */ public static final RecipeMetadataKey FOG_PLASMA_TIER = SimpleRecipeMetadataKey .create(Integer.class, "fog_plasma_tier"); /** * FOG Plasma multistep requirement. */ public static final RecipeMetadataKey FOG_PLASMA_MULTISTEP = SimpleRecipeMetadataKey .create(Boolean.class, "fog_plasma_multistep"); /** * DEFC Casing tier. */ public static final RecipeMetadataKey DEFC_CASING_TIER = SimpleRecipeMetadataKey .create(Integer.class, "defc_casing_tier"); /** * Chemplant Casing tier. Beware, codewise index starts at 0, but it is tier 1. */ public static final RecipeMetadataKey CHEMPLANT_CASING_TIER = SimpleRecipeMetadataKey .create(Integer.class, "chemplant_casing_tier"); /** * QFT Focus tier. */ public static final RecipeMetadataKey QFT_FOCUS_TIER = SimpleRecipeMetadataKey .create(Integer.class, "qft_focus_tier"); /** * Tier of advanced compression (HIP/black hole) */ public static final RecipeMetadataKey COMPRESSION_TIER = SimpleRecipeMetadataKey .create(Integer.class, "compression"); /** * Dissolution Tank Ratio. */ public static final RecipeMetadataKey DISSOLUTION_TANK_RATIO = SimpleRecipeMetadataKey .create(Integer.class, "dissolution_tank_ratio"); /** * Duration in days for the RTG. */ public static final RecipeMetadataKey RTG_DURATION_IN_DAYS = SimpleRecipeMetadataKey .create(Integer.class, "rtg_duration_in_days"); /** * Basic output for the Large Naquadah Generator. */ public static final RecipeMetadataKey LNG_BASIC_OUTPUT = SimpleRecipeMetadataKey .create(Integer.class, "lng_basic_output"); /** * Coil tier for the Naquadah Fuel Refinery. */ public static final RecipeMetadataKey NFR_COIL_TIER = SimpleRecipeMetadataKey .create(Integer.class, "nfr_coil_tier"); /** * NKE range for the neutron activator. */ public static final RecipeMetadataKey NKE_RANGE = SimpleRecipeMetadataKey .create(Integer.class, "nke_range"); /** * Precise Assembler casing tier. */ public static final RecipeMetadataKey PRECISE_ASSEMBLER_CASING_TIER = SimpleRecipeMetadataKey .create(Integer.class, "precise_assembler_casing_tier"); /** * CoAL casing tier. */ public static final RecipeMetadataKey COAL_CASING_TIER = SimpleRecipeMetadataKey .create(Integer.class, "coal_casing_tier"); /** * LFTR output power. */ public static final RecipeMetadataKey LFTR_OUTPUT_POWER = SimpleRecipeMetadataKey .create(Integer.class, "lftr_output_power"); /** * Research Station data. */ public static final RecipeMetadataKey RESEARCH_STATION_DATA = SimpleRecipeMetadataKey .create(Integer.class, "research_station_data"); /** * glass tier required for the biovat recipes. */ public static final RecipeMetadataKey SIEVERTS = SimpleRecipeMetadataKey.create(Integer.class, "sieverts"); public static final RecipeMetadataKey DECAY_TICKS = SimpleRecipeMetadataKey .create(Integer.class, "decay_ticks"); public static final RecipeMetadataKey NOBLE_GASES = SimpleRecipeMetadataKey .create(Boolean.class, "noble_gases"); public static final RecipeMetadataKey ANAEROBE_GASES = SimpleRecipeMetadataKey .create(Boolean.class, "anaerobe_gases"); public static final RecipeMetadataKey NO_GAS = SimpleRecipeMetadataKey.create(Boolean.class, "no_gas"); public static final RecipeMetadataKey EU_MULTIPLIER = SimpleRecipeMetadataKey .create(Integer.class, "eu_multiplier"); /** * Add a arc furnace recipe. Adds to both normal arc furnace and plasma arc furnace. * Will override the fluid input with oxygen/plasma for the respective recipe maps, so there is no point setting it. */ public static final IRecipeMap UniversalArcFurnace = IRecipeMap.newRecipeMap(builder -> { if (!GTUtility.isArrayOfLength(builder.getItemInputsBasic(), 1) || GTUtility.isArrayEmptyOrNull(builder.getItemOutputs())) return Collections.emptyList(); int aDuration = builder.getDuration(); if (aDuration <= 0) { return Collections.emptyList(); } builder.duration(aDuration); boolean recycle = builder.getMetadataOrDefault(RECYCLE, false); Collection ret = new ArrayList<>(); for (Materials mat : new Materials[] { Materials.Argon, Materials.Nitrogen }) { int tPlasmaAmount = (int) Math.max(1L, aDuration / (mat.getMass() * 16L)); GTRecipeBuilder plasmaBuilder = builder.copy() .fluidInputs(mat.getPlasma(tPlasmaAmount)) .fluidOutputs(mat.getGas(tPlasmaAmount)); if (recycle) { plasmaBuilder.recipeCategory(RecipeCategories.plasmaArcFurnaceRecycling); } ret.addAll(RecipeMaps.plasmaArcFurnaceRecipes.doAdd(plasmaBuilder)); } GTRecipeBuilder arcBuilder = builder.copy() .fluidInputs(Materials.Oxygen.getGas(aDuration)); if (recycle) { arcBuilder.recipeCategory(RecipeCategories.arcFurnaceRecycling); } ret.addAll(RecipeMaps.arcFurnaceRecipes.doAdd(arcBuilder)); return ret; }); /** * Add a chemical reactor recipe to both LCR and singleblocks. */ public static final IRecipeMap UniversalChemical = IRecipeMap.newRecipeMap(builder -> { for (ItemStack input : builder.getItemInputsBasic()) { // config >= 10 -> this is a special chemical recipe that output fluid/canned fluid variant. // it doesn't belong to multiblocks if (GTUtility.isAnyIntegratedCircuit(input) && input.getItemDamage() >= 10) { return builder.addTo(RecipeMaps.chemicalReactorRecipes); } } return GTUtility.concat( builder.copy() .addTo(RecipeMaps.chemicalReactorRecipes), convertCellToFluid(builder, false) // LCR does not need cleanroom. .metadata(CLEANROOM, false) .addTo(RecipeMaps.multiblockChemicalReactorRecipes)); }); /** * Adds an engraver recipe that might use purified water. Still added to the regular recipemap if it ends up not * needing it. */ public static final IRecipeMap WaferEngravingRecipes = IRecipeMap.newRecipeMap(builder -> { // spotless:off enum Wafer{ Naquadah, Europium, Americium, // Beamline masks MaskT1, MaskT2, MaskT3, } // spotless:on // Find the wafer used Wafer wafer = null; ItemPhotolithographicMask t1Item = (ItemPhotolithographicMask) LanthItemList.maskMap.get(MaskList.BLANK1); ItemPhotolithographicMask t2Item = (ItemPhotolithographicMask) LanthItemList.maskMap.get(MaskList.BLANK2); ItemPhotolithographicMask t3Item = (ItemPhotolithographicMask) LanthItemList.maskMap.get(MaskList.BLANK3); for (ItemStack input : builder.getItemInputsBasic()) { if (input.getItem() instanceof MetaGeneratedItem03) { int meta = input.getItemDamage() - 32000; // Check if this input item is indicating a wafer recipe we want to modify if (meta == IDMetaItem03.Circuit_Silicon_Wafer3.ID) wafer = Wafer.Naquadah; else if (meta == IDMetaItem03.Circuit_Silicon_Wafer4.ID) wafer = Wafer.Europium; else if (meta == IDMetaItem03.Circuit_Silicon_Wafer5.ID) wafer = Wafer.Americium; } // Now look for beamline masks if (input.getItem() instanceof ItemPhotolithographicMask mask) { String spectrum = mask.getDescSpectrum(); if (spectrum.equals(t1Item.getDescSpectrum())) wafer = Wafer.MaskT1; else if (spectrum.equals(t2Item.getDescSpectrum())) wafer = Wafer.MaskT2; else if (spectrum.equals(t3Item.getDescSpectrum())) wafer = Wafer.MaskT3; } // Found a wafer, stop checking inputs if (wafer != null) break; } int recipeTime = builder.duration; // Bonus for using purified water of a higher tier than necessary int halfBoostedRecipeTime = (int) (recipeTime * 0.75); int boostedRecipeTime = (int) (recipeTime * 0.5); // If this recipe does not use a wafer, exit without modifying it. if (wafer == null) return builder.addTo(RecipeMaps.laserEngraverRecipes); switch (wafer) { case Naquadah -> { ArrayList items = new ArrayList<>(Arrays.asList(builder.getItemInputsBasic())); ItemStack[] itemInputs = items.toArray(new ItemStack[] {}); // Naquadah wafers can use grade 1-2 purified water for a bonus, otherwise use distilled so we don't // have to // deal with circuits return GTUtility.concat( builder.copy() .itemInputs(itemInputs) .fluidInputs(GTModHandler.getDistilledWater(100L)) .addTo(RecipeMaps.laserEngraverRecipes), builder.copy() .itemInputs(itemInputs) .fluidInputs(Materials.Grade1PurifiedWater.getFluid(100L)) .duration(halfBoostedRecipeTime) .addTo(RecipeMaps.laserEngraverRecipes), builder.copy() .itemInputs(itemInputs) .fluidInputs(Materials.Grade2PurifiedWater.getFluid(100L)) .duration(boostedRecipeTime) .addTo(RecipeMaps.laserEngraverRecipes)); } case Europium -> { // Require purified water for europium wafers, at least grade 3 return GTUtility.concat( builder.copy() .fluidInputs(Materials.Grade3PurifiedWater.getFluid(100L)) .duration(recipeTime) .addTo(RecipeMaps.laserEngraverRecipes), builder.copy() .fluidInputs(Materials.Grade4PurifiedWater.getFluid(100L)) .duration(boostedRecipeTime) .addTo(RecipeMaps.laserEngraverRecipes)); } case Americium -> { // Require purified water for americium wafers, at least grade 5 return GTUtility.concat( builder.copy() .fluidInputs(Materials.Grade5PurifiedWater.getFluid(100L)) .duration(recipeTime) .addTo(RecipeMaps.laserEngraverRecipes), builder.copy() .fluidInputs(Materials.Grade6PurifiedWater.getFluid(100L)) .duration(boostedRecipeTime) .addTo(RecipeMaps.laserEngraverRecipes)); } // Masks require much more purified water because they can make many wafers at once case MaskT1 -> { // T1 masks require grade 1, 2 or 3 purified water return GTUtility.concat( builder.copy() .fluidInputs(Materials.Grade1PurifiedWater.getFluid(32000L)) .duration(recipeTime) .addTo(RecipeMaps.laserEngraverRecipes), builder.copy() .fluidInputs(Materials.Grade2PurifiedWater.getFluid(32000L)) .duration(halfBoostedRecipeTime) .addTo(RecipeMaps.laserEngraverRecipes), builder.copy() .fluidInputs(Materials.Grade3PurifiedWater.getFluid(32000L)) .duration(boostedRecipeTime) .addTo(RecipeMaps.laserEngraverRecipes)); } case MaskT2 -> { // T2 masks require grade 4 or 5 purified water return GTUtility.concat( builder.copy() .fluidInputs(Materials.Grade4PurifiedWater.getFluid(32000L)) .duration(recipeTime) .addTo(RecipeMaps.laserEngraverRecipes), builder.copy() .fluidInputs(Materials.Grade5PurifiedWater.getFluid(32000L)) .duration(boostedRecipeTime) .addTo(RecipeMaps.laserEngraverRecipes)); } case MaskT3 -> { // T3 masks require grade 6, 7 or 8 purified water return GTUtility.concat( builder.copy() .fluidInputs(Materials.Grade6PurifiedWater.getFluid(32000L)) .duration(recipeTime) .addTo(RecipeMaps.laserEngraverRecipes), builder.copy() .fluidInputs(Materials.Grade7PurifiedWater.getFluid(32000L)) .duration(halfBoostedRecipeTime) .addTo(RecipeMaps.laserEngraverRecipes), builder.copy() .fluidInputs(Materials.Grade8PurifiedWater.getFluid(32000L)) .duration(boostedRecipeTime) .addTo(RecipeMaps.laserEngraverRecipes)); } } throw new RuntimeException("Unreachable code reached in Laser Engraver Recipe Transformer"); }); /** * The one and only :tm: assline recipe adder. * Uses {@link #RESEARCH_ITEM} metadata as research item, and {@link #RESEARCH_TIME} metadata as research time, unit * in ticks. */ public static final IRecipeMap AssemblyLine = IRecipeMap.newRecipeMap(builder -> { Optional rr = builder.forceOreDictInput() .validateInputCount(4, 16) .validateOutputCount(1, 1) .validateOutputFluidCount(-1, 0) .validateInputFluidCount(1, 4) .buildWithAlt(); // noinspection SimplifyOptionalCallChains if (!rr.isPresent()) return Collections.emptyList(); GTRecipe.GTRecipe_WithAlt r = rr.get(); ItemStack[][] mOreDictAlt = r.mOreDictAlt; Object[] inputs = builder.getItemInputsOreDict(); ItemStack aResearchItem = builder.getMetadata(RESEARCH_ITEM); if (aResearchItem == null) { return Collections.emptyList(); } ItemStack aOutput = r.mOutputs[0]; int tPersistentHash = 1; for (int i = 0, mOreDictAltLength = mOreDictAlt.length; i < mOreDictAltLength; i++) { ItemStack[] alts = mOreDictAlt[i]; Object input = inputs[i]; if (input == null) { GTLog.err.println( "addAssemblingLineRecipe " + aResearchItem.getDisplayName() + " --> " + aOutput.getUnlocalizedName() + " there is some null item in that recipe"); } if (input instanceof ItemStack) { tPersistentHash = tPersistentHash * 31 + GTUtility.persistentHash((ItemStack) input, true, false); } else if (input instanceof ItemStack[]) { for (ItemStack alt : ((ItemStack[]) input)) { tPersistentHash = tPersistentHash * 31 + GTUtility.persistentHash(alt, true, false); if (alt == null) { GTLog.err.println( "addAssemblingLineRecipe " + aResearchItem.getDisplayName() + " --> " + aOutput.getUnlocalizedName() + " there is some null alt item in that recipe"); } } tPersistentHash *= 31; } else if (input instanceof Object[]objs) { Arrays.sort( alts, Comparator .comparing(s -> GameRegistry.findUniqueIdentifierFor(s.getItem()).modId) .thenComparing(s -> GameRegistry.findUniqueIdentifierFor(s.getItem()).name) .thenComparingInt(Items.feather::getDamage) .thenComparingInt(s -> s.stackSize)); tPersistentHash = tPersistentHash * 31 + (objs[0] == null ? "" : objs[0].toString()).hashCode(); tPersistentHash = tPersistentHash * 31 + ((Number) objs[1]).intValue(); } } tPersistentHash = tPersistentHash * 31 + GTUtility.persistentHash(aResearchItem, true, false); tPersistentHash = tPersistentHash * 31 + GTUtility.persistentHash(aOutput, true, false); for (FluidStack fluidInput : r.mFluidInputs) { if (fluidInput == null) continue; tPersistentHash = tPersistentHash * 31 + GTUtility.persistentHash(fluidInput, true, false); } int aResearchTime = builder.getMetadataOrDefault(RESEARCH_TIME, 0); tPersistentHash = tPersistentHash * 31 + aResearchTime; tPersistentHash = tPersistentHash * 31 + r.mDuration; tPersistentHash = tPersistentHash * 31 + r.mEUt; GTRecipe.RecipeAssemblyLine tRecipe = new GTRecipe.RecipeAssemblyLine( aResearchItem, aResearchTime, r.mInputs, r.mFluidInputs, aOutput, r.mDuration, r.mEUt, r.mOreDictAlt); tRecipe.setPersistentHash(tPersistentHash); GTRecipe.RecipeAssemblyLine.sAssemblylineRecipes.add(tRecipe); AssemblyLineUtils.addRecipeToCache(tRecipe); Collection ret = new ArrayList<>(3); ret.addAll( GTValues.RA.stdBuilder() .itemInputs(aResearchItem) .itemOutputs(aOutput) .special(tRecipe.newDataStickForNEI("Writes Research result")) .duration(aResearchTime) .eut(TierEU.RECIPE_LV) .specialValue(-201) // means it's scanned .noOptimize() .ignoreCollision() .fake() .addTo(scannerFakeRecipes)); ret.add( RecipeMaps.assemblylineVisualRecipes.addFakeRecipe( false, r.mInputs, new ItemStack[] { aOutput }, new ItemStack[] { tRecipe.newDataStickForNEI("Reads Research result") }, r.mFluidInputs, null, r.mDuration, r.mEUt, 0, r.mOreDictAlt, false)); return ret; }); /** * Adds an Electric Blast Furnace recipe that might use gas. */ public static final IRecipeMap BlastFurnaceWithGas = IRecipeMap.newRecipeMap(builder -> { Collection ret = new ArrayList<>(); int basicGasAmount = builder.getMetadataOrDefault(ADDITIVE_AMOUNT, 1000); double durationBase = builder.getDuration(); ArrayList items = new ArrayList<>(Arrays.asList(builder.getItemInputsBasic())); int circuitConfig = 1; if (items.size() == 1) {// Set circuit config if it is a dust -> ingot recipe. ItemData data = GTOreDictUnificator.getAssociation(items.get(0)); if (data != null) { OrePrefixes prefix = data.mPrefix; if (OrePrefixes.dust.equals(prefix)) { circuitConfig = 1; } else if (OrePrefixes.dustSmall.equals(prefix)) { circuitConfig = 4; } else if (OrePrefixes.dustTiny.equals(prefix)) { circuitConfig = 9; } } } else { // Set circuit config if there is an integrated circuit for (int i = 0; i < items.size(); i++) { if (GTUtility.isAnyIntegratedCircuit(items.get(i))) { circuitConfig = items.get(i) .getItemDamage(); items.remove(i--); } } } if (builder.getMetadataOrDefault(NO_GAS, false)) { items.add(GTUtility.getIntegratedCircuit(circuitConfig)); ret.addAll( builder.copy() .itemInputs(items.toArray(new ItemStack[0])) .fluidInputs() .duration((int) Math.max(durationBase * 1.1, 1)) .addTo(RecipeMaps.blastFurnaceRecipes)); items.remove(items.size() - 1); circuitConfig += 10; } items.add(GTUtility.getIntegratedCircuit(circuitConfig)); boolean nobleGases = builder.getMetadataOrDefault(NOBLE_GASES, false); boolean anaerobeGases = builder.getMetadataOrDefault(ANAEROBE_GASES, false); Collection gases = new ArrayList<>(); if (nobleGases && anaerobeGases) { gases = BlastFurnaceGasStat.getNobleAndAnaerobeGases(); } else if (nobleGases) { gases = BlastFurnaceGasStat.getNobleGases(); } else if (anaerobeGases) { gases = BlastFurnaceGasStat.getAnaerobeGases(); } for (BlastFurnaceGasStat gas : gases) { int gasAmount = (int) (gas.recipeConsumedAmountMultiplier * basicGasAmount); int duration = (int) Math.max(gas.recipeTimeMultiplier * durationBase, 1); ret.addAll( builder.copy() .itemInputs(items.toArray(new ItemStack[0])) .fluidInputs(GTUtility.copyAmount(gasAmount, gas.gas)) .duration(duration) .addTo(RecipeMaps.blastFurnaceRecipes)); } return ret; }); /** * Just like any normal assembler recipe, however it accepts one input item to be oredicted. Pass in the item to * oredict via {@link #OREDICT_INPUT}. It will be used along all other item inputs as input of this recipe. */ public static IRecipeMap AssemblerOD = IRecipeMap.newRecipeMap(builder -> { Collection ret = new ArrayList<>(); for (ItemStack input : GTOreDictUnificator.getOresImmutable(builder.getMetadata(OREDICT_INPUT))) { ret.addAll( builder.copy() .itemInputs(GTRecipeMapUtil.appendArray(builder.getItemInputsBasic(), input)) .addTo(RecipeMaps.assemblerRecipes)); } return ret; }); /** * A universal fuel adder. It's actually just a dispatcher towards all actual fuel recipe maps. * Dispatch based on {@link #FUEL_TYPE}. Uses {@link #FUEL_VALUE} as fuel value. * Can use {@link FuelType#ordinal()} as a human-readable form of what FUEL_TYPE should be. * You can bypass this and add to relevant fuel maps directly if you wish. */ public static IRecipeMap Fuel = IRecipeMap.newRecipeMap(builder -> { builder.validateInputCount(1, 1) .validateNoInputFluid() .validateOutputCount(-1, 1) .validateNoOutputFluid(); if (!builder.isValid()) return Collections.emptyList(); Integer fuelType = builder.getMetadata(FUEL_TYPE); if (fuelType == null) return Collections.emptyList(); builder.metadata(FUEL_VALUE, builder.getMetadataOrDefault(FUEL_VALUE, 0)); return FuelType.get(fuelType) .getTarget() .doAdd(builder); }); public enum FuelType { // ORDER MATTERS. DO NOT INSERT ELEMENT BETWEEN EXISTING ONES DieselFuel(RecipeMaps.dieselFuels), GasTurbine(RecipeMaps.gasTurbineFuels), // appears unused HotFuel(RecipeMaps.hotFuels), SemiFluid(RecipeMaps.denseLiquidFuels), PlasmaTurbine(RecipeMaps.plasmaFuels), Magic(RecipeMaps.magicFuels),; private static final FuelType[] VALUES = values(); private final IRecipeMap target; FuelType(IRecipeMap target) { this.target = target; } public static FuelType get(int fuelType) { if (fuelType < 0 || fuelType >= VALUES.length) return SemiFluid; return VALUES[fuelType]; } public IRecipeMap getTarget() { return target; } } static { GTRecipeMapUtil.SPECIAL_VALUE_ALIASES.add(COIL_HEAT); GTRecipeMapUtil.SPECIAL_VALUE_ALIASES.add(FUSION_THRESHOLD); GTRecipeMapUtil.SPECIAL_VALUE_ALIASES.add(FUEL_VALUE); GTRecipeMapUtil.SPECIAL_VALUE_ALIASES.add(NANO_FORGE_TIER); GTRecipeMapUtil.SPECIAL_VALUE_ALIASES.add(FOG_EXOTIC_TIER); GTRecipeMapUtil.SPECIAL_VALUE_ALIASES.add(FOG_PLASMA_TIER); GTRecipeMapUtil.SPECIAL_VALUE_ALIASES.add(DEFC_CASING_TIER); GTRecipeMapUtil.SPECIAL_VALUE_ALIASES.add(CHEMPLANT_CASING_TIER); GTRecipeMapUtil.SPECIAL_VALUE_ALIASES.add(QFT_FOCUS_TIER); GTRecipeMapUtil.SPECIAL_VALUE_ALIASES.add(DISSOLUTION_TANK_RATIO); GTRecipeMapUtil.SPECIAL_VALUE_ALIASES.add(RTG_DURATION_IN_DAYS); GTRecipeMapUtil.SPECIAL_VALUE_ALIASES.add(LNG_BASIC_OUTPUT); GTRecipeMapUtil.SPECIAL_VALUE_ALIASES.add(NFR_COIL_TIER); GTRecipeMapUtil.SPECIAL_VALUE_ALIASES.add(NKE_RANGE); GTRecipeMapUtil.SPECIAL_VALUE_ALIASES.add(PRECISE_ASSEMBLER_CASING_TIER); GTRecipeMapUtil.SPECIAL_VALUE_ALIASES.add(COAL_CASING_TIER); GTRecipeMapUtil.SPECIAL_VALUE_ALIASES.add(RESEARCH_STATION_DATA); GTRecipeMapUtil.SPECIAL_VALUE_ALIASES.add(SIEVERTS); GTRecipeMapUtil.SPECIAL_VALUE_ALIASES.add(DECAY_TICKS); } }