aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/gregtech/api
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/gregtech/api')
-rw-r--r--src/main/java/gregtech/api/GregTech_API.java4
-rw-r--r--src/main/java/gregtech/api/enums/GT_Values.java4
-rw-r--r--src/main/java/gregtech/api/enums/Materials.java8
-rw-r--r--src/main/java/gregtech/api/enums/ModIDs.java84
-rw-r--r--src/main/java/gregtech/api/enums/SoundResource.java48
-rw-r--r--src/main/java/gregtech/api/interfaces/IGT_RecipeMap.java64
-rw-r--r--src/main/java/gregtech/api/interfaces/internal/IGT_RecipeAdder.java3
-rw-r--r--src/main/java/gregtech/api/items/GT_MetaGenerated_Item.java4
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Cable.java9
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine_GT_Recipe.java6
-rw-r--r--src/main/java/gregtech/api/util/GT_BaseCrop.java4
-rw-r--r--src/main/java/gregtech/api/util/GT_ModReference.java29
-rw-r--r--src/main/java/gregtech/api/util/GT_Recipe.java601
-rw-r--r--src/main/java/gregtech/api/util/GT_RecipeBuilder.java713
-rw-r--r--src/main/java/gregtech/api/util/GT_RecipeConstants.java258
-rw-r--r--src/main/java/gregtech/api/util/GT_RecipeMapUtil.java196
-rw-r--r--src/main/java/gregtech/api/util/GT_Utility.java83
17 files changed, 2035 insertions, 83 deletions
diff --git a/src/main/java/gregtech/api/GregTech_API.java b/src/main/java/gregtech/api/GregTech_API.java
index 68d1afdebd..d8ad1ca07b 100644
--- a/src/main/java/gregtech/api/GregTech_API.java
+++ b/src/main/java/gregtech/api/GregTech_API.java
@@ -3,7 +3,7 @@ package gregtech.api;
import static gregtech.api.enums.GT_Values.B;
import static gregtech.api.enums.GT_Values.L;
import static gregtech.api.enums.GT_Values.M;
-import static gregtech.api.enums.GT_Values.MOD_ID_IC2;
+import static gregtech.api.enums.ModIDs.IndustrialCraft2;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@@ -284,7 +284,7 @@ public class GregTech_API {
public static boolean mUseOnlyGoodSolderingMaterials = false;
- private static final String aTextIC2Lower = MOD_ID_IC2.toLowerCase(Locale.ENGLISH);
+ private static final String aTextIC2Lower = IndustrialCraft2.modID.toLowerCase(Locale.ENGLISH);
/**
* Getting assigned by the Mod loading
*/
diff --git a/src/main/java/gregtech/api/enums/GT_Values.java b/src/main/java/gregtech/api/enums/GT_Values.java
index d06e70b0b5..6ccf092461 100644
--- a/src/main/java/gregtech/api/enums/GT_Values.java
+++ b/src/main/java/gregtech/api/enums/GT_Values.java
@@ -1,5 +1,7 @@
package gregtech.api.enums;
+import static gregtech.api.enums.ModIDs.IndustrialCraft2;
+
import java.math.BigInteger;
import java.util.*;
@@ -206,7 +208,7 @@ public class GT_Values {
TEX_DIR_ASPECTS = TEX_DIR + "aspects/", RES_PATH = MOD_ID + ":" + TEX_DIR,
RES_PATH_GUI = MOD_ID + ":" + TEX_DIR_GUI, RES_PATH_ITEM = MOD_ID + ":", RES_PATH_BLOCK = MOD_ID + ":",
RES_PATH_ENTITY = MOD_ID + ":" + TEX_DIR_ENTITY, RES_PATH_ASPECTS = MOD_ID + ":" + TEX_DIR_ASPECTS,
- RES_PATH_IC2 = MOD_ID_IC2.toLowerCase(Locale.ENGLISH) + ":",
+ RES_PATH_IC2 = IndustrialCraft2.modID.toLowerCase(Locale.ENGLISH) + ":",
RES_PATH_MODEL = MOD_ID + ":" + TEX_DIR + "models/";
/**
diff --git a/src/main/java/gregtech/api/enums/Materials.java b/src/main/java/gregtech/api/enums/Materials.java
index 9932cf3b09..3ff4d70920 100644
--- a/src/main/java/gregtech/api/enums/Materials.java
+++ b/src/main/java/gregtech/api/enums/Materials.java
@@ -2,7 +2,8 @@ package gregtech.api.enums;
import static gregtech.api.enums.FluidState.GAS;
import static gregtech.api.enums.GT_Values.M;
-import static gregtech.api.enums.GT_Values.MOD_ID_DC;
+import static gregtech.api.enums.ModIDs.NewHorizonsCoreMod;
+import static gregtech.api.enums.ModIDs.Thaumcraft;
import java.util.*;
import java.util.stream.Collectors;
@@ -13,7 +14,6 @@ import net.minecraft.item.ItemStack;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidStack;
-import cpw.mods.fml.common.Loader;
import gregtech.GT_Mod;
import gregtech.api.GregTech_API;
import gregtech.api.enums.TC_Aspects.TC_AspectStack;
@@ -2316,7 +2316,7 @@ public class Materials implements IColorModulationContainer, ISubTagContainer {
MATERIALS_ARRAY = MATERIALS_MAP.values().toArray(new Materials[0]); // Generate standard object array. This is a
// lot faster to loop over.
VALUES = Arrays.asList(MATERIALS_ARRAY);
- if (!Loader.isModLoaded(MOD_ID_DC) && !GT_Mod.gregtechproxy.mEnableAllComponents)
+ if (!NewHorizonsCoreMod.isModLoaded() && !GT_Mod.gregtechproxy.mEnableAllComponents)
OrePrefixes.initMaterialComponents();
else {
OrePrefixes.ingotHot.mDisabledItems.addAll(
@@ -2442,7 +2442,7 @@ public class Materials implements IColorModulationContainer, ISubTagContainer {
: aMaterial == Diamond || aMaterial == Thaumium ? Wood
: aMaterial.contains(SubTag.BURNING) ? Blaze
: aMaterial.contains(SubTag.MAGICAL) && aMaterial.contains(SubTag.CRYSTAL)
- && Loader.isModLoaded(GT_Values.MOD_ID_TC) ? Thaumium
+ && Thaumcraft.isModLoaded() ? Thaumium
: aMaterial.getMass() > Element.Tc.getMass() * 2 ? TungstenSteel
: aMaterial.getMass() > Element.Tc.getMass() ? Steel : Wood);
diff --git a/src/main/java/gregtech/api/enums/ModIDs.java b/src/main/java/gregtech/api/enums/ModIDs.java
new file mode 100644
index 0000000000..16c03e3ae5
--- /dev/null
+++ b/src/main/java/gregtech/api/enums/ModIDs.java
@@ -0,0 +1,84 @@
+package gregtech.api.enums;
+
+import cpw.mods.fml.common.Loader;
+
+public enum ModIDs {
+
+ AdvancedSolarPanel("AdvancedSolarPanel"),
+ AE2FluidCraft("ae2fc"),
+ AppleCore("AppleCore"),
+ AppliedEnergistics2("appliedenergistics2"),
+ Avaritia("Avaritia"),
+ AvaritiaAddons("avaritiaddons"),
+ BartWorks("bartworks"),
+ BetterLoadingScreen("betterloadingscreen"),
+ BiomesOPlanty("BiomesOPlenty"),
+ BuildCraftFactory("BuildCraft|Factory"),
+ BuildCraftSilicon("BuildCraft|Silicon"),
+ BuildCraftTransport("BuildCraft|Transport"),
+ Computronics("computronics"),
+ CraftTweaker("MineTweaker3"),
+ EnderIO("EnderIO"),
+ EnderStorage("EnderStorage"),
+ EternalSingularity("eternalsingularity"),
+ ExtraCells2("extracells"),
+ ExtraUtilities("ExtraUtilities"),
+ ForbiddenMagic("ForbiddenMagic"),
+ Forestry("Forestry"),
+ GalacticraftCore("GalacticraftCore"),
+ GalacticraftMars("GalacticraftMars"),
+ GalaxySpace("GalaxySpace"),
+ Gendustry("gendustry"),
+ GoodGenerator("GoodGenerator"),
+ GregTech("gregtech"),
+ GraviSuite("GraviSuite"),
+ GTNHLanthanides("gtnhlanth"),
+ GTPlusPlus("miscutils"),
+ PamsHarvestCraft("harvestcraft"),
+ HardcoreEnderExpansion("HardcoreEnderExpansion"),
+ HodgePodge("hodgepodge"),
+ IC2CropPlugin("Ic2Nei"),
+ IC2NuclearControl("IC2NuclearControl"),
+ IguanaTweaksTinkerConstruct("IguanaTweaksTConstruct"),
+ IndustrialCraft2("IC2"),
+ IronChests("IronChest"),
+ IronTanks("irontank"),
+ Minecraft("minecraft"),
+ Natura("Natura"),
+ NEICustomDiagrams("neicustomdiagram"),
+ NewHorizonsCoreMod("dreamcraft"),
+ NotEnoughItems("NotEnoughItems"),
+ OpenComputers("OpenComputers"),
+ ProjectRedCore("ProjRed|Core"),
+ Railcraft("Railcraft"),
+ TaintedMagic("TaintedMagic"),
+ Thaumcraft("Thaumcraft"),
+ ThaumicBases("thaumicbases"),
+ ThaumicTinkerer("ThaumicTinkerer"),
+ TinkerConstruct("TConstruct"),
+ TinkersGregworks("TGregworks"),
+ Translocator("Translocator"),
+ TwilightForest("TwilightForest"),
+ Waila("Waila"),
+ // Do we keep compat of those?
+ IndustrialCraft2Classic("IC2-Classic-Spmod"),
+ Metallurgy("Metallurgy"),
+ RotaryCraft("RotaryCraft"),
+ ThermalExpansion("ThermalExpansion"),
+ ThermalFondation("ThermalFoundation"),
+ UndergroundBiomes("UndergroundBiomes");
+
+ public final String modID;
+ private Boolean modLoaded;
+
+ ModIDs(String modID) {
+ this.modID = modID;
+ }
+
+ public boolean isModLoaded() {
+ if (this.modLoaded == null) {
+ this.modLoaded = Loader.isModLoaded(modID);
+ }
+ return this.modLoaded;
+ }
+}
diff --git a/src/main/java/gregtech/api/enums/SoundResource.java b/src/main/java/gregtech/api/enums/SoundResource.java
index f1dffb3884..63a8331496 100644
--- a/src/main/java/gregtech/api/enums/SoundResource.java
+++ b/src/main/java/gregtech/api/enums/SoundResource.java
@@ -1,7 +1,7 @@
package gregtech.api.enums;
import static gregtech.api.enums.GT_Values.MOD_ID;
-import static gregtech.api.enums.GT_Values.MOD_ID_IC2;
+import static gregtech.api.enums.ModIDs.IndustrialCraft2;
import java.util.EnumSet;
import java.util.Locale;
@@ -30,31 +30,31 @@ public enum SoundResource {
RANDOM_EXPLODE(5, "random.explode"),
FIRE_IGNITE(6, "fire.ignite"),
- IC2_TOOLS_WRENCH(100, MOD_ID_IC2, "tools.Wrench"),
- IC2_TOOLS_RUBBER_TRAMPOLINE(101, MOD_ID_IC2, "tools.RubberTrampoline"),
- IC2_TOOLS_PAINTER(102, MOD_ID_IC2, "tools.Painter"),
- IC2_TOOLS_BATTERY_USE(103, MOD_ID_IC2, "tools.BatteryUse"),
- IC2_TOOLS_CHAINSAW_CHAINSAW_USE_ONE(104, MOD_ID_IC2, "tools.chainsaw.ChainsawUseOne"),
- IC2_TOOLS_CHAINSAW_CHAINSAW_USE_TWO(105, MOD_ID_IC2, "tools.chainsaw.ChainsawUseTwo"),
- IC2_TOOLS_DRILL_DRILL_SOFT(106, MOD_ID_IC2, "tools.drill.DrillSoft"),
- IC2_TOOLS_DRILL_DRILL_HARD(107, MOD_ID_IC2, "tools.drill.DrillHard"),
- IC2_TOOLS_OD_SCANNER(108, MOD_ID_IC2, "tools.ODScanner"),
- IC2_TOOLS_INSULATION_CUTTERS(109, MOD_ID_IC2, "tools.InsulationCutters"),
+ IC2_TOOLS_WRENCH(100, IndustrialCraft2.modID, "tools.Wrench"),
+ IC2_TOOLS_RUBBER_TRAMPOLINE(101, IndustrialCraft2.modID, "tools.RubberTrampoline"),
+ IC2_TOOLS_PAINTER(102, IndustrialCraft2.modID, "tools.Painter"),
+ IC2_TOOLS_BATTERY_USE(103, IndustrialCraft2.modID, "tools.BatteryUse"),
+ IC2_TOOLS_CHAINSAW_CHAINSAW_USE_ONE(104, IndustrialCraft2.modID, "tools.chainsaw.ChainsawUseOne"),
+ IC2_TOOLS_CHAINSAW_CHAINSAW_USE_TWO(105, IndustrialCraft2.modID, "tools.chainsaw.ChainsawUseTwo"),
+ IC2_TOOLS_DRILL_DRILL_SOFT(106, IndustrialCraft2.modID, "tools.drill.DrillSoft"),
+ IC2_TOOLS_DRILL_DRILL_HARD(107, IndustrialCraft2.modID, "tools.drill.DrillHard"),
+ IC2_TOOLS_OD_SCANNER(108, IndustrialCraft2.modID, "tools.ODScanner"),
+ IC2_TOOLS_INSULATION_CUTTERS(109, IndustrialCraft2.modID, "tools.InsulationCutters"),
- IC2_MACHINES_EXTRACTOR_OP(200, MOD_ID_IC2, "machines.ExtractorOp"),
- IC2_MACHINES_MACERATOR_OP(201, MOD_ID_IC2, "machines.MaceratorOp"),
- IC2_MACHINES_INDUCTION_LOOP(202, MOD_ID_IC2, "machines.InductionLoop"),
- IC2_MACHINES_COMPRESSOR_OP(203, MOD_ID_IC2, "machines.CompressorOp"),
- IC2_MACHINES_RECYCLER_OP(204, MOD_ID_IC2, "machines.RecyclerOp"),
- IC2_MACHINES_MINER_OP(205, MOD_ID_IC2, "machines.MinerOp"),
- IC2_MACHINES_PUMP_OP(206, MOD_ID_IC2, "machines.PumpOp"),
- IC2_MACHINES_ELECTROFURNACE_LOOP(207, MOD_ID_IC2, "machines.ElectroFurnaceLoop"),
+ IC2_MACHINES_EXTRACTOR_OP(200, IndustrialCraft2.modID, "machines.ExtractorOp"),
+ IC2_MACHINES_MACERATOR_OP(201, IndustrialCraft2.modID, "machines.MaceratorOp"),
+ IC2_MACHINES_INDUCTION_LOOP(202, IndustrialCraft2.modID, "machines.InductionLoop"),
+ IC2_MACHINES_COMPRESSOR_OP(203, IndustrialCraft2.modID, "machines.CompressorOp"),
+ IC2_MACHINES_RECYCLER_OP(204, IndustrialCraft2.modID, "machines.RecyclerOp"),
+ IC2_MACHINES_MINER_OP(205, IndustrialCraft2.modID, "machines.MinerOp"),
+ IC2_MACHINES_PUMP_OP(206, IndustrialCraft2.modID, "machines.PumpOp"),
+ IC2_MACHINES_ELECTROFURNACE_LOOP(207, IndustrialCraft2.modID, "machines.ElectroFurnaceLoop"),
@Deprecated
- DEPRECATED_DUPE_OF_IC2_MACHINES_INDUCTION_LOOP(208, MOD_ID_IC2, "machines.InductionLoop"),
- IC2_MACHINES_MACHINE_OVERLOAD(209, MOD_ID_IC2, "machines.MachineOverload"),
- IC2_MACHINES_INTERRUPT_ONE(210, MOD_ID_IC2, "machines.InterruptOne"),
- IC2_MACHINES_KA_CHING(211, MOD_ID_IC2, "machines.KaChing"),
- IC2_MACHINES_MAGNETIZER_LOOP(212, MOD_ID_IC2, "machines.MagnetizerLoop"),
+ DEPRECATED_DUPE_OF_IC2_MACHINES_INDUCTION_LOOP(208, IndustrialCraft2.modID, "machines.InductionLoop"),
+ IC2_MACHINES_MACHINE_OVERLOAD(209, IndustrialCraft2.modID, "machines.MachineOverload"),
+ IC2_MACHINES_INTERRUPT_ONE(210, IndustrialCraft2.modID, "machines.InterruptOne"),
+ IC2_MACHINES_KA_CHING(211, IndustrialCraft2.modID, "machines.KaChing"),
+ IC2_MACHINES_MAGNETIZER_LOOP(212, IndustrialCraft2.modID, "machines.MagnetizerLoop"),
GT_MACHINES_FUSION_LOOP(230, MOD_ID, "machines.FusionLoop"),
GT_MACHINES_DISTILLERY_LOOP(231, MOD_ID, "machines.DistilleryLoop"),
diff --git a/src/main/java/gregtech/api/interfaces/IGT_RecipeMap.java b/src/main/java/gregtech/api/interfaces/IGT_RecipeMap.java
new file mode 100644
index 0000000000..69f87161d1
--- /dev/null
+++ b/src/main/java/gregtech/api/interfaces/IGT_RecipeMap.java
@@ -0,0 +1,64 @@
+package gregtech.api.interfaces;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_RecipeBuilder;
+import gregtech.api.util.GT_Utility;
+
+/**
+ * Represents the target of a recipe adding action, usually, but not necessarily, is a recipe map itself.
+ */
+public interface IGT_RecipeMap {
+
+ /**
+ * Add a downstream recipe map that will get to handle the original builder.
+ *
+ * @param downstream
+ */
+ void addDownstream(IGT_RecipeMap downstream);
+
+ /**
+ * Actually add the recipe represented by the builder. CAN modify the builder's internal states!!!
+ */
+ @Nonnull
+ Collection<GT_Recipe> doAdd(GT_RecipeBuilder builder);
+
+ /**
+ * Return a variant of this recipe map that will perform a deep copy on input recipe builder before doing anything
+ * to it.
+ *
+ * The returned recipe map will not have any downstreams, but can accept new downstreams.
+ */
+ default IGT_RecipeMap deepCopyInput() {
+ return newRecipeMap(b -> doAdd(b.copy()));
+ }
+
+ static IGT_RecipeMap newRecipeMap(Function<? super GT_RecipeBuilder, Collection<GT_Recipe>> func) {
+ return new IGT_RecipeMap() {
+
+ private final Collection<IGT_RecipeMap> downstreams = new ArrayList<>();
+
+ @Override
+ public void addDownstream(IGT_RecipeMap downstream) {
+ downstreams.add(downstream);
+ }
+
+ @Nonnull
+ @Override
+ public Collection<GT_Recipe> doAdd(GT_RecipeBuilder builder) {
+ List<Collection<GT_Recipe>> ret = new ArrayList<>();
+ ret.add(func.apply(builder));
+ for (IGT_RecipeMap downstream : downstreams) {
+ ret.add(downstream.doAdd(builder));
+ }
+ return GT_Utility.concat(ret);
+ }
+ };
+ }
+}
diff --git a/src/main/java/gregtech/api/interfaces/internal/IGT_RecipeAdder.java b/src/main/java/gregtech/api/interfaces/internal/IGT_RecipeAdder.java
index bda2523811..149600b426 100644
--- a/src/main/java/gregtech/api/interfaces/internal/IGT_RecipeAdder.java
+++ b/src/main/java/gregtech/api/interfaces/internal/IGT_RecipeAdder.java
@@ -5,6 +5,7 @@ import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidStack;
import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_RecipeBuilder;
public interface IGT_RecipeAdder {
@@ -960,4 +961,6 @@ public interface IGT_RecipeAdder {
*/
GT_Recipe addIC2ReactorFuelCell(ItemStack input, ItemStack output, boolean aMox, float aHeat, float aEnergy,
int aCells);
+
+ GT_RecipeBuilder stdBuilder();
}
diff --git a/src/main/java/gregtech/api/items/GT_MetaGenerated_Item.java b/src/main/java/gregtech/api/items/GT_MetaGenerated_Item.java
index 32a46c8641..b6ea08213b 100644
--- a/src/main/java/gregtech/api/items/GT_MetaGenerated_Item.java
+++ b/src/main/java/gregtech/api/items/GT_MetaGenerated_Item.java
@@ -1,6 +1,7 @@
package gregtech.api.items;
import static gregtech.api.enums.GT_Values.*;
+import static gregtech.api.enums.ModIDs.AppleCore;
import java.util.ArrayList;
import java.util.Arrays;
@@ -20,7 +21,6 @@ import net.minecraft.world.World;
import squeek.applecore.api.food.FoodValues;
import squeek.applecore.api.food.IEdible;
-import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.Optional;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@@ -321,7 +321,7 @@ public abstract class GT_MetaGenerated_Item extends GT_MetaBase_Item implements
public final ItemStack onEaten(ItemStack aStack, World aWorld, EntityPlayer aPlayer) {
IFoodStat tStat = mFoodStats.get((short) getDamage(aStack));
if (tStat != null) {
- if (Loader.isModLoaded(MOD_ID_APC)) {
+ if (AppleCore.isModLoaded()) {
aPlayer.getFoodStats().func_151686_a(
(ItemFood) GT_Utility.callConstructor(
"squeek.applecore.api.food.ItemFoodProxy.ItemFoodProxy",
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Cable.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Cable.java
index 1ea2e3e756..e2e0b13f12 100644
--- a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Cable.java
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Cable.java
@@ -1,6 +1,7 @@
package gregtech.api.metatileentity.implementations;
import static gregtech.api.enums.GT_Values.ALL_VALID_SIDES;
+import static gregtech.api.enums.ModIDs.GalacticraftCore;
import java.util.ArrayList;
import java.util.HashSet;
@@ -19,13 +20,9 @@ import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
import cofh.api.energy.IEnergyReceiver;
-import cpw.mods.fml.common.Loader;
import gregtech.GT_Mod;
import gregtech.api.GregTech_API;
-import gregtech.api.enums.Dyes;
-import gregtech.api.enums.Materials;
-import gregtech.api.enums.TextureSet;
-import gregtech.api.enums.Textures;
+import gregtech.api.enums.*;
import gregtech.api.graphs.Node;
import gregtech.api.graphs.NodeList;
import gregtech.api.graphs.PowerNode;
@@ -348,7 +345,7 @@ public class GT_MetaPipeEntity_Cable extends MetaPipeEntity implements IMetaTile
// ((tIsGregTechTileEntity && tIsTileEntityCable) && (tAlwaysLookConnected || tLetEnergyIn || tLetEnergyOut) )
// --> Not needed
- if (Loader.isModLoaded("GalacticraftCore") && GT_GC_Compat.canConnect(tTileEntity, tDir)) return true;
+ if (GalacticraftCore.isModLoaded() && GT_GC_Compat.canConnect(tTileEntity, tDir)) return true;
// AE2-p2p Compat
if (GT_Mod.gregtechproxy.mAE2Integration) {
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine_GT_Recipe.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine_GT_Recipe.java
index 7976ea94c6..e51da3bbdc 100644
--- a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine_GT_Recipe.java
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine_GT_Recipe.java
@@ -4,6 +4,7 @@ import static gregtech.api.enums.GT_Values.V;
import static gregtech.api.enums.GT_Values.VN;
import static gregtech.api.enums.GT_Values.W;
import static gregtech.api.enums.GT_Values.ticksBetweenSounds;
+import static gregtech.api.enums.ModIDs.BartWorks;
import static gregtech.api.objects.XSTR.XSTR_INSTANCE;
import java.util.Locale;
@@ -23,7 +24,6 @@ import com.gtnewhorizons.modularui.api.math.Pos2d;
import com.gtnewhorizons.modularui.api.math.Size;
import com.gtnewhorizons.modularui.common.widget.ProgressBar;
-import cpw.mods.fml.common.Loader;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import gregtech.api.enums.*;
@@ -206,12 +206,12 @@ public class GT_MetaTileEntity_BasicMachine_GT_Recipe extends GT_MetaTileEntity_
case 6:
case 7:
case 8:
- if (Loader.isModLoaded("bartworks")) { // todo remove via provider pattern on all enums?
+ if (BartWorks.isModLoaded()) { // todo remove via provider pattern on all enums?
aRecipe[i] = "blockGlass" + VN[aTier];
break;
}
default:
- if (Loader.isModLoaded("bartworks")) { // todo remove via provider pattern on all enums?
+ if (BartWorks.isModLoaded()) { // todo remove via provider pattern on all enums?
aRecipe[i] = "blockGlass" + VN[8];
} else {
aRecipe[i] = Ic2Items.reinforcedGlass;
diff --git a/src/main/java/gregtech/api/util/GT_BaseCrop.java b/src/main/java/gregtech/api/util/GT_BaseCrop.java
index 65d2900176..bfffbe8572 100644
--- a/src/main/java/gregtech/api/util/GT_BaseCrop.java
+++ b/src/main/java/gregtech/api/util/GT_BaseCrop.java
@@ -1,6 +1,7 @@
package gregtech.api.util;
import static gregtech.api.enums.GT_Values.E;
+import static gregtech.api.enums.ModIDs.IC2CropPlugin;
import java.util.ArrayList;
import java.util.List;
@@ -12,7 +13,6 @@ import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import speiger.src.crops.api.ICropCardInfo;
-import cpw.mods.fml.common.Loader;
import gregtech.GT_Mod;
import gregtech.api.GregTech_API;
import gregtech.api.enums.ConfigCategories;
@@ -33,7 +33,7 @@ public class GT_BaseCrop extends CropCard implements ICropCardInfo {
mGrowthSpeed = 0;
private ItemStack mDrop = null, mSpecialDrops[] = null;
private Materials mBlock = null;
- private static boolean bIc2NeiLoaded = Loader.isModLoaded("Ic2Nei");
+ private static boolean bIc2NeiLoaded = IC2CropPlugin.isModLoaded();
/**
* To create new Crops
diff --git a/src/main/java/gregtech/api/util/GT_ModReference.java b/src/main/java/gregtech/api/util/GT_ModReference.java
new file mode 100644
index 0000000000..02a1116f68
--- /dev/null
+++ b/src/main/java/gregtech/api/util/GT_ModReference.java
@@ -0,0 +1,29 @@
+package gregtech.api.util;
+
+import static gregtech.api.enums.ModIDs.GTPlusPlus;
+import static gregtech.api.enums.ModIDs.Railcraft;
+
+import cpw.mods.fml.common.Loader;
+
+@Deprecated // use gregtech.api.enums.ModIDs instead
+public enum GT_ModReference {
+
+ RAILCRAFT(Railcraft.modID),
+ GTPP(GTPlusPlus.modID),;
+
+ private final boolean loaded;
+ private final String modID;
+
+ GT_ModReference(String modID) {
+ loaded = Loader.isModLoaded(modID);
+ this.modID = modID;
+ }
+
+ public boolean isLoaded() {
+ return loaded;
+ }
+
+ public String getModID() {
+ return modID;
+ }
+}
diff --git a/src/main/java/gregtech/api/util/GT_Recipe.java b/src/main/java/gregtech/api/util/GT_Recipe.java
index 3b7f64aae8..e7443a63b5 100644
--- a/src/main/java/gregtech/api/util/GT_Recipe.java
+++ b/src/main/java/gregtech/api/util/GT_Recipe.java
@@ -1,7 +1,14 @@
package gregtech.api.util;
import static gregtech.api.enums.GT_Values.*;
+import static gregtech.api.enums.ModIDs.GTPlusPlus;
+import static gregtech.api.enums.ModIDs.NEICustomDiagrams;
+import static gregtech.api.enums.ModIDs.Railcraft;
+import static gregtech.api.util.GT_RecipeConstants.ADDITIVE_AMOUNT;
+import static gregtech.api.util.GT_RecipeMapUtil.*;
import static gregtech.api.util.GT_Utility.formatNumbers;
+import static gregtech.api.util.GT_Utility.isArrayEmptyOrNull;
+import static gregtech.api.util.GT_Utility.isArrayOfLength;
import static net.minecraft.util.EnumChatFormatting.GRAY;
import static net.minecraft.util.StatCollector.translateToLocal;
@@ -9,10 +16,15 @@ import java.awt.*;
import java.util.*;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+import java.util.function.Function;
import java.util.function.Supplier;
import javax.annotation.Nullable;
+import mods.railcraft.common.blocks.aesthetics.cube.EnumCube;
+import mods.railcraft.common.items.RailcraftToolItems;
+
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.init.Blocks;
@@ -27,6 +39,8 @@ import net.minecraftforge.fluids.FluidContainerRegistry;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.IFluidContainerItem;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
@@ -34,6 +48,7 @@ import appeng.util.ReadableNumberConverter;
import codechicken.lib.gui.GuiDraw;
import codechicken.nei.PositionedStack;
+import com.google.common.collect.Iterables;
import com.gtnewhorizons.modularui.api.GlStateManager;
import com.gtnewhorizons.modularui.api.ModularUITextures;
import com.gtnewhorizons.modularui.api.drawable.FallbackableUITexture;
@@ -60,6 +75,7 @@ import gregtech.api.gui.GT_GUIColorOverride;
import gregtech.api.gui.modularui.FallbackableSteamTexture;
import gregtech.api.gui.modularui.GT_UITextures;
import gregtech.api.gui.modularui.SteamTexture;
+import gregtech.api.interfaces.IGT_RecipeMap;
import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
import gregtech.api.interfaces.tileentity.IHasWorldObjectAndCoords;
import gregtech.api.objects.GT_FluidStack;
@@ -155,13 +171,13 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
*/
public List<List<StackTraceElement>> stackTraces = new ArrayList<>();
- private GT_Recipe(GT_Recipe aRecipe) {
- mInputs = GT_Utility.copyStackArray((Object[]) aRecipe.mInputs);
- mOutputs = GT_Utility.copyStackArray((Object[]) aRecipe.mOutputs);
+ private GT_Recipe(GT_Recipe aRecipe, boolean shallow) {
+ mInputs = shallow ? aRecipe.mInputs : GT_Utility.copyStackArray((Object[]) aRecipe.mInputs);
+ mOutputs = shallow ? aRecipe.mOutputs : GT_Utility.copyStackArray((Object[]) aRecipe.mOutputs);
mSpecialItems = aRecipe.mSpecialItems;
mChances = aRecipe.mChances;
- mFluidInputs = GT_Utility.copyFluidArray(aRecipe.mFluidInputs);
- mFluidOutputs = GT_Utility.copyFluidArray(aRecipe.mFluidOutputs);
+ mFluidInputs = shallow ? aRecipe.mFluidInputs : GT_Utility.copyFluidArray(aRecipe.mFluidInputs);
+ mFluidOutputs = shallow ? aRecipe.mFluidOutputs : GT_Utility.copyFluidArray(aRecipe.mFluidOutputs);
mDuration = aRecipe.mDuration;
mSpecialValue = aRecipe.mSpecialValue;
mEUt = aRecipe.mEUt;
@@ -174,6 +190,29 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
reloadOwner();
}
+ // only used for GT_RecipeBuilder. Should not be called otherwise
+ GT_Recipe(ItemStack[] mInputs, ItemStack[] mOutputs, FluidStack[] mFluidInputs, FluidStack[] mFluidOutputs,
+ int[] mChances, Object mSpecialItems, int mDuration, int mEUt, int mSpecialValue, boolean mEnabled,
+ boolean mHidden, boolean mFakeRecipe, boolean mCanBeBuffered, boolean mNeedsEmptyOutput, String[] neiDesc) {
+ this.mInputs = mInputs;
+ this.mOutputs = mOutputs;
+ this.mFluidInputs = mFluidInputs;
+ this.mFluidOutputs = mFluidOutputs;
+ this.mChances = mChances;
+ this.mSpecialItems = mSpecialItems;
+ this.mDuration = mDuration;
+ this.mEUt = mEUt;
+ this.mSpecialValue = mSpecialValue;
+ this.mEnabled = mEnabled;
+ this.mHidden = mHidden;
+ this.mFakeRecipe = mFakeRecipe;
+ this.mCanBeBuffered = mCanBeBuffered;
+ this.mNeedsEmptyOutput = mNeedsEmptyOutput;
+ this.neiDesc = neiDesc;
+
+ reloadOwner();
+ }
+
public GT_Recipe(boolean aOptimize, ItemStack[] aInputs, ItemStack[] aOutputs, Object aSpecialItems, int[] aChances,
FluidStack[] aFluidInputs, FluidStack[] aFluidOutputs, int aDuration, int aEUt, int aSpecialValue) {
if (aInputs == null) aInputs = new ItemStack[0];
@@ -556,7 +595,11 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
}
public GT_Recipe copy() {
- return new GT_Recipe(this);
+ return new GT_Recipe(this, false);
+ }
+
+ public GT_Recipe copyShallow() {
+ return new GT_Recipe(this, true);
}
public boolean isRecipeInputEqual(boolean aDecreaseStacksizeBySuccess, FluidStack[] aFluidInputs,
@@ -768,6 +811,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
final List<String> excludedClasses = Arrays.asList(
"java.lang.Thread",
"gregtech.api.util.GT_Recipe",
+ "gregtech.api.util.GT_RecipeBuilder",
"gregtech.api.util.GT_Recipe$GT_Recipe_Map",
"gregtech.common.GT_RecipeAdder");
if (GT_Mod.gregtechproxy.mNEIRecipeOwnerStackTrace) {
@@ -800,6 +844,37 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
}
}
+ public GT_Recipe setInputs(ItemStack... aInputs) {
+ // TODO determine if we need this without trailing nulls call
+ this.mInputs = ArrayExt.withoutTrailingNulls(aInputs, ItemStack[]::new);
+ return this;
+ }
+
+ public GT_Recipe setOutputs(ItemStack... aOutputs) {
+ this.mOutputs = ArrayExt.withoutTrailingNulls(aOutputs, ItemStack[]::new);
+ return this;
+ }
+
+ public GT_Recipe setFluidInputs(FluidStack... aInputs) {
+ this.mFluidInputs = ArrayExt.withoutTrailingNulls(aInputs, FluidStack[]::new);
+ return this;
+ }
+
+ public GT_Recipe setFluidOutputs(FluidStack... aOutputs) {
+ this.mFluidOutputs = ArrayExt.withoutTrailingNulls(aOutputs, FluidStack[]::new);
+ return this;
+ }
+
+ public GT_Recipe setDuration(int aDuration) {
+ this.mDuration = aDuration;
+ return this;
+ }
+
+ public GT_Recipe setEUt(int aEUt) {
+ this.mEUt = aEUt;
+ return this;
+ }
+
public static class GT_Recipe_AssemblyLine {
public static final ArrayList<GT_Recipe_AssemblyLine> sAssemblylineRecipes = new ArrayList<GT_Recipe_AssemblyLine>();
@@ -992,7 +1067,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
}
@SuppressWarnings("StaticInitializerReferencesSubClass")
- public static class GT_Recipe_Map {
+ public static class GT_Recipe_Map implements IGT_RecipeMap {
/**
* Contains all Recipe Maps
@@ -1020,6 +1095,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
true,
true).setSlotOverlay(false, false, GT_UITextures.OVERLAY_SLOT_CRUSHED_ORE)
.setSlotOverlay(false, true, GT_UITextures.OVERLAY_SLOT_DUST)
+ .setRecipeConfigFile("orewasher", FIRST_ITEM_INPUT)
.setProgressBar(GT_UITextures.PROGRESSBAR_BATH, ProgressBar.Direction.CIRCULAR_CW);
public static final GT_Recipe_Map sThermalCentrifugeRecipes = new GT_Recipe_Map(
new HashSet<>(1000),
@@ -1038,6 +1114,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
true,
true).setSlotOverlay(false, false, GT_UITextures.OVERLAY_SLOT_CRUSHED_ORE)
.setSlotOverlay(false, true, GT_UITextures.OVERLAY_SLOT_DUST)
+ .setRecipeConfigFile("thermalcentrifuge", FIRST_ITEM_INPUT)
.setProgressBar(GT_UITextures.PROGRESSBAR_ARROW, ProgressBar.Direction.RIGHT);
public static final GT_Recipe_Map sCompressorRecipes = new GT_Recipe_Map(
new HashSet<>(750),
@@ -1055,6 +1132,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
E,
true,
true).setSlotOverlay(false, false, GT_UITextures.OVERLAY_SLOT_COMPRESSOR)
+ .setRecipeConfigFile("compressor", FIRST_ITEM_INPUT)
.setProgressBar(GT_UITextures.PROGRESSBAR_COMPRESS, ProgressBar.Direction.RIGHT)
.setSlotOverlaySteam(false, GT_UITextures.OVERLAY_SLOT_COMPRESSOR_STEAM)
.setProgressBarSteam(GT_UITextures.PROGRESSBAR_COMPRESS_STEAM);
@@ -1074,6 +1152,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
E,
true,
true).setSlotOverlay(false, false, GT_UITextures.OVERLAY_SLOT_CENTRIFUGE)
+ .setRecipeConfigFile("extractor", FIRST_ITEM_INPUT)
.setProgressBar(GT_UITextures.PROGRESSBAR_EXTRACT, ProgressBar.Direction.RIGHT)
.setSlotOverlaySteam(false, GT_UITextures.OVERLAY_SLOT_CENTRIFUGE_STEAM)
.setProgressBarSteam(GT_UITextures.PROGRESSBAR_EXTRACT_STEAM);
@@ -1257,7 +1336,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
E,
true,
true).setSlotOverlay(false, false, true, true, GT_UITextures.OVERLAY_SLOT_DATA_ORB)
- .setUsualFluidInputCount(4);
+ .setUsualFluidInputCount(4).setDisableOptimize(true);
public static final GT_Recipe_Map sPlasmaArcFurnaceRecipes = new GT_Recipe_Map(
new HashSet<>(20000),
"gt.recipe.plasmaarcfurnace",
@@ -1273,7 +1352,8 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
1,
E,
true,
- true).setProgressBar(GT_UITextures.PROGRESSBAR_ARROW, ProgressBar.Direction.RIGHT);
+ true).setProgressBar(GT_UITextures.PROGRESSBAR_ARROW, ProgressBar.Direction.RIGHT)
+ .setRecipeConfigFile("arcfurnace", FIRST_ITEM_INPUT);
public static final GT_Recipe_Map sArcFurnaceRecipes = new GT_Recipe_Map(
new HashSet<>(20000),
"gt.recipe.arcfurnace",
@@ -1289,7 +1369,8 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
1,
E,
true,
- true).setProgressBar(GT_UITextures.PROGRESSBAR_ARROW, ProgressBar.Direction.RIGHT);
+ true).setProgressBar(GT_UITextures.PROGRESSBAR_ARROW, ProgressBar.Direction.RIGHT)
+ .setRecipeConfigFile("arcfurnace", FIRST_ITEM_INPUT);
public static final GT_Recipe_Map sPrinterRecipes = new GT_Recipe_Map_Printer(
new HashSet<>(5),
"gt.recipe.printer",
@@ -1308,6 +1389,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
true).setSlotOverlay(false, false, GT_UITextures.OVERLAY_SLOT_PAGE_BLANK)
.setSlotOverlay(false, true, GT_UITextures.OVERLAY_SLOT_PAGE_PRINTED)
.setSlotOverlay(false, false, true, true, GT_UITextures.OVERLAY_SLOT_DATA_STICK)
+ .setRecipeConfigFile("printer", FIRST_ITEM_INPUT)
.setProgressBar(GT_UITextures.PROGRESSBAR_ARROW, ProgressBar.Direction.RIGHT);
public static final GT_Recipe_Map sSifterRecipes = new GT_Recipe_Map(
new HashSet<>(105),
@@ -1324,7 +1406,8 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
1,
E,
true,
- true).setProgressBar(GT_UITextures.PROGRESSBAR_SIFT, ProgressBar.Direction.DOWN);
+ true).setProgressBar(GT_UITextures.PROGRESSBAR_SIFT, ProgressBar.Direction.DOWN)
+ .setRecipeConfigFile("sifter", FIRST_ITEM_INPUT);
public static final GT_Recipe_Map sPressRecipes = new GT_Recipe_Map_FormingPress(
new HashSet<>(300),
"gt.recipe.press",
@@ -1343,6 +1426,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
true).setSlotOverlay(false, false, true, GT_UITextures.OVERLAY_SLOT_PRESS_1)
.setSlotOverlay(false, false, false, GT_UITextures.OVERLAY_SLOT_PRESS_2)
.setSlotOverlay(false, true, GT_UITextures.OVERLAY_SLOT_PRESS_3)
+ .setRecipeConfigFile("press", FIRST_ITEM_OUTPUT)
.setProgressBar(GT_UITextures.PROGRESSBAR_COMPRESS, ProgressBar.Direction.RIGHT);
public static final GT_Recipe_Map sLaserEngraverRecipes = new GT_Recipe_Map(
new HashSet<>(810),
@@ -1361,7 +1445,8 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
true,
true).setSlotOverlay(false, false, false, GT_UITextures.OVERLAY_SLOT_LENS)
.setProgressBar(GT_UITextures.PROGRESSBAR_ARROW, ProgressBar.Direction.RIGHT)
- .setUsualFluidInputCount(2).setUsualFluidOutputCount(2);
+ .setRecipeConfigFile("laserengraving", FIRST_ITEM_OUTPUT).setUsualFluidInputCount(2)
+ .setUsualFluidOutputCount(2);
public static final GT_Recipe_Map sMixerRecipes = new GT_Recipe_Map(
new HashSet<>(900),
"gt.recipe.mixer",
@@ -1379,6 +1464,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
true,
true).setSlotOverlay(false, false, GT_UITextures.OVERLAY_SLOT_DUST)
.setSlotOverlay(false, true, GT_UITextures.OVERLAY_SLOT_DUST)
+ .setRecipeConfigFile("mixer", FIRST_ITEM_OR_FLUID_OUTPUT)
.setProgressBar(GT_UITextures.PROGRESSBAR_MIXER, ProgressBar.Direction.CIRCULAR_CW);
public static final GT_Recipe_Map sAutoclaveRecipes = new GT_Recipe_Map(
new HashSet<>(300),
@@ -1398,6 +1484,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
true).setSlotOverlay(false, false, GT_UITextures.OVERLAY_SLOT_DUST)
.setSlotOverlay(false, true, true, GT_UITextures.OVERLAY_SLOT_GEM)
.setSlotOverlay(false, true, false, GT_UITextures.OVERLAY_SLOT_DUST)
+ .setRecipeConfigFile("autoclave", FIRST_ITEM_INPUT)
.setProgressBar(GT_UITextures.PROGRESSBAR_ARROW, ProgressBar.Direction.RIGHT);
public static final GT_Recipe_Map sElectroMagneticSeparatorRecipes = new GT_Recipe_Map(
new HashSet<>(50),
@@ -1416,6 +1503,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
true,
true).setSlotOverlay(false, false, GT_UITextures.OVERLAY_SLOT_CRUSHED_ORE)
.setSlotOverlay(false, true, GT_UITextures.OVERLAY_SLOT_DUST)
+ .setRecipeConfigFile("electromagneticseparator", FIRST_ITEM_INPUT)
.setProgressBar(GT_UITextures.PROGRESSBAR_MAGNET, ProgressBar.Direction.RIGHT);
public static final GT_Recipe_Map sPolarizerRecipes = new GT_Recipe_Map(
new HashSet<>(300),
@@ -1432,7 +1520,8 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
1,
E,
true,
- true).setProgressBar(GT_UITextures.PROGRESSBAR_MAGNET, ProgressBar.Direction.RIGHT);
+ true).setProgressBar(GT_UITextures.PROGRESSBAR_MAGNET, ProgressBar.Direction.RIGHT)
+ .setRecipeConfigFile("polarizer", FIRST_ITEM_INPUT);
public static final GT_Recipe_Map sMaceratorRecipes = new GT_Recipe_Map_Macerator(
new HashSet<>(16600),
"gt.recipe.macerator",
@@ -1451,6 +1540,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
true).setSlotOverlay(false, false, GT_UITextures.OVERLAY_SLOT_CRUSHED_ORE)
.setSlotOverlay(false, true, GT_UITextures.OVERLAY_SLOT_DUST)
.setProgressBar(GT_UITextures.PROGRESSBAR_MACERATE, ProgressBar.Direction.RIGHT)
+ .setRecipeConfigFile("pulveriser", FIRST_ITEM_INPUT)
.setSlotOverlaySteam(false, GT_UITextures.OVERLAY_SLOT_CRUSHED_ORE_STEAM)
.setSlotOverlaySteam(true, GT_UITextures.OVERLAY_SLOT_DUST_STEAM)
.setProgressBarSteam(GT_UITextures.PROGRESSBAR_MACERATE_STEAM);
@@ -1469,7 +1559,8 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
1,
E,
true,
- true).setProgressBar(GT_UITextures.PROGRESSBAR_BATH, ProgressBar.Direction.CIRCULAR_CW);
+ true).setProgressBar(GT_UITextures.PROGRESSBAR_BATH, ProgressBar.Direction.CIRCULAR_CW)
+ .setRecipeConfigFile("chemicalbath", FIRST_ITEM_INPUT);
public static final GT_Recipe_Map sFluidCannerRecipes = new GT_Recipe_Map_FluidCanner(
new HashSet<>(2100),
"gt.recipe.fluidcanner",
@@ -1487,6 +1578,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
true,
true).setSlotOverlay(false, false, GT_UITextures.OVERLAY_SLOT_CANISTER)
.setSlotOverlay(false, true, GT_UITextures.OVERLAY_SLOT_CANISTER)
+ .setRecipeConfigFile("canning", FIRST_ITEM_INPUT)
.setProgressBar(GT_UITextures.PROGRESSBAR_CANNER, ProgressBar.Direction.RIGHT);
public static final GT_Recipe_Map sBrewingRecipes = new GT_Recipe_Map(
new HashSet<>(450),
@@ -1504,6 +1596,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
E,
true,
true).setSlotOverlay(false, false, GT_UITextures.OVERLAY_SLOT_CAULDRON)
+ .setRecipeConfigFile("brewing", FIRST_FLUIDSTACK_OUTPUT)
.setProgressBar(GT_UITextures.PROGRESSBAR_ARROW_MULTIPLE, ProgressBar.Direction.RIGHT);
public static final GT_Recipe_Map sFluidHeaterRecipes = new GT_Recipe_Map(
new HashSet<>(10),
@@ -1522,6 +1615,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
true,
true).setSlotOverlay(true, false, GT_UITextures.OVERLAY_SLOT_HEATER_1)
.setSlotOverlay(true, true, GT_UITextures.OVERLAY_SLOT_HEATER_2)
+ .setRecipeConfigFile("fluidheater", FIRST_FLUIDSTACK_OUTPUT)
.setProgressBar(GT_UITextures.PROGRESSBAR_ARROW_MULTIPLE, ProgressBar.Direction.RIGHT);
public static final GT_Recipe_Map sDistilleryRecipes = new GT_Recipe_Map(
new HashSet<>(400),
@@ -1540,7 +1634,48 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
true,
true).setSlotOverlay(true, false, GT_UITextures.OVERLAY_SLOT_BEAKER_1)
.setSlotOverlay(true, true, GT_UITextures.OVERLAY_SLOT_BEAKER_2)
- .setProgressBar(GT_UITextures.PROGRESSBAR_ARROW_MULTIPLE, ProgressBar.Direction.RIGHT);
+ .setRecipeConfigFile("distillery", FIRST_FLUIDSTACK_OUTPUT).setRecipeSpecialHandler(r -> {
+ int aInput = r.mFluidInputs[0].amount, aOutput = r.mFluidOutputs[0].amount,
+ aDuration = r.mDuration;
+
+ // reduce the batch size if fluid amount is exceeding
+ int tScale = (Math.max(aInput, aOutput) + 999) / 1000;
+ if (tScale <= 0) tScale = 1;
+ if (tScale > 1) {
+ // trying to find whether there is a better factor
+ for (int i = tScale; i <= 5; i++) {
+ if (aInput % i == 0 && aDuration % i == 0) {
+ tScale = i;
+ break;
+ }
+ }
+ for (int i = tScale; i <= 5; i++) {
+ if (aInput % i == 0 && aDuration % i == 0 && aOutput % i == 0) {
+ tScale = i;
+ break;
+ }
+ }
+ aInput = (aInput + tScale - 1) / tScale;
+ aOutput = aOutput / tScale;
+ if (!isArrayEmptyOrNull(r.mOutputs)) {
+ ItemData tData = GT_OreDictUnificator.getItemData(r.mOutputs[0]);
+ if (tData != null && (tData.mPrefix == OrePrefixes.dust
+ || OrePrefixes.dust.mFamiliarPrefixes.contains(tData.mPrefix))) {
+ r.mOutputs[0] = GT_OreDictUnificator.getDust(
+ tData.mMaterial.mMaterial,
+ tData.mMaterial.mAmount * r.mOutputs[0].stackSize / tScale);
+ } else {
+ if (r.mOutputs[0].stackSize / tScale == 0) r.mOutputs[0] = GT_Values.NI;
+ else r.mOutputs[0] = GT_Utility
+ .copyAmount(r.mOutputs[0].stackSize / tScale, r.mOutputs[0]);
+ }
+ }
+ aDuration = (aDuration + tScale - 1) / tScale;
+ r.mFluidInputs[0] = GT_Utility.copyAmount(aInput, r.mFluidInputs[0]);
+ r.mFluidOutputs[0] = GT_Utility.copyAmount(aOutput, r.mFluidOutputs[0]);
+ r.mDuration = aDuration;
+ }
+ }).setProgressBar(GT_UITextures.PROGRESSBAR_ARROW_MULTIPLE, ProgressBar.Direction.RIGHT);
public static final GT_Recipe_Map sFermentingRecipes = new GT_Recipe_Map(
new HashSet<>(50),
"gt.recipe.fermenter",
@@ -1556,7 +1691,8 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
1,
E,
true,
- true).setProgressBar(GT_UITextures.PROGRESSBAR_ARROW_MULTIPLE, ProgressBar.Direction.RIGHT);
+ true).setProgressBar(GT_UITextures.PROGRESSBAR_ARROW_MULTIPLE, ProgressBar.Direction.RIGHT)
+ .setRecipeConfigFile("fermenting", FIRST_FLUIDSTACK_OUTPUT);
public static final GT_Recipe_Map sFluidSolidficationRecipes = new GT_Recipe_Map(
new HashSet<>(35000),
"gt.recipe.fluidsolidifier",
@@ -1573,7 +1709,16 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
E,
true,
true).setSlotOverlay(false, false, GT_UITextures.OVERLAY_SLOT_MOLD)
- .setProgressBar(GT_UITextures.PROGRESSBAR_ARROW, ProgressBar.Direction.RIGHT);
+ .setRecipeConfigFile("fluidsolidifier", FIRST_ITEM_OUTPUT).setRecipeSpecialHandler(r -> {
+ if (ArrayUtils.isNotEmpty(r.mFluidInputs)) {
+ if (Materials.PhasedGold.getMolten(1).isFluidEqual(r.mFluidInputs[0]))
+ r.mFluidInputs = new FluidStack[] {
+ Materials.VibrantAlloy.getMolten(r.mFluidInputs[0].amount) };
+ else if (Materials.PhasedIron.getMolten(1).isFluidEqual(r.mFluidInputs[0]))
+ r.mFluidInputs = new FluidStack[] {
+ Materials.PulsatingIron.getMolten(r.mFluidInputs[0].amount) };
+ }
+ }).setProgressBar(GT_UITextures.PROGRESSBAR_ARROW, ProgressBar.Direction.RIGHT);
public static final GT_Recipe_Map sFluidExtractionRecipes = new GT_Recipe_Map(
new HashSet<>(15000),
"gt.recipe.fluidextractor",
@@ -1590,7 +1735,16 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
E,
true,
true).setSlotOverlay(false, false, GT_UITextures.OVERLAY_SLOT_CENTRIFUGE)
- .setProgressBar(GT_UITextures.PROGRESSBAR_EXTRACT, ProgressBar.Direction.RIGHT);
+ .setRecipeConfigFile("fluidextractor", FIRST_ITEM_INPUT).setRecipeSpecialHandler(r -> {
+ if (ArrayUtils.isNotEmpty(r.mFluidInputs)) {
+ if (Materials.PhasedGold.getMolten(1).isFluidEqual(r.mFluidInputs[0]))
+ r.mFluidInputs = new FluidStack[] {
+ Materials.VibrantAlloy.getMolten(r.mFluidInputs[0].amount) };
+ else if (Materials.PhasedIron.getMolten(1).isFluidEqual(r.mFluidInputs[0]))
+ r.mFluidInputs = new FluidStack[] {
+ Materials.PulsatingIron.getMolten(r.mFluidInputs[0].amount) };
+ }
+ }).setProgressBar(GT_UITextures.PROGRESSBAR_EXTRACT, ProgressBar.Direction.RIGHT);
public static final GT_Recipe_Map sBoxinatorRecipes = new GT_Recipe_Map(
new HashSet<>(2500),
"gt.recipe.packager",
@@ -1607,6 +1761,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
E,
true,
true).setSlotOverlay(false, false, false, GT_UITextures.OVERLAY_SLOT_BOX)
+ .setRecipeConfigFile("boxing", FIRST_ITEM_OUTPUT)
.setSlotOverlay(false, true, GT_UITextures.OVERLAY_SLOT_BOXED)
.setProgressBar(GT_UITextures.PROGRESSBAR_ARROW, ProgressBar.Direction.RIGHT);
public static final GT_Recipe_Map sUnboxinatorRecipes = new GT_Recipe_Map_Unboxinator(
@@ -1625,6 +1780,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
E,
true,
true).setSlotOverlay(false, false, GT_UITextures.OVERLAY_SLOT_BOXED)
+ .setRecipeConfigFile("unboxing", FIRST_ITEM_OUTPUT)
.setProgressBar(GT_UITextures.PROGRESSBAR_ARROW, ProgressBar.Direction.RIGHT);
public static final GT_Recipe_Map sFusionRecipes = new GT_Recipe_Map_FluidOnly(
new HashSet<>(50),
@@ -1643,6 +1799,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
true,
true).setProgressBar(GT_UITextures.PROGRESSBAR_ARROW, ProgressBar.Direction.RIGHT)
.useComparatorForNEI(true).setUsualFluidInputCount(2)
+ .setRecipeConfigFile("fusion", FIRST_FLUID_OUTPUT).setDisableOptimize(true)
.setNEISpecialInfoFormatter(FusionSpecialValueFormatter.INSTANCE);
public static final GT_Recipe_Map sComplexFusionRecipes = new GT_Recipe_Map_ComplexFusion(
new HashSet<>(50),
@@ -1662,7 +1819,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
true).setProgressBar(GT_UITextures.PROGRESSBAR_ARROW, ProgressBar.Direction.RIGHT)
.setUsualFluidInputCount(16).setUsualFluidOutputCount(16)
.setNEITransferRect(new Rectangle(79, 34, 18, 18)).setLogoPos(80, 61)
- .setNEISpecialInfoFormatter(FusionSpecialValueFormatter.INSTANCE);
+ .setNEISpecialInfoFormatter(FusionSpecialValueFormatter.INSTANCE).setDisableOptimize(true);
public static final GT_Recipe_Map sCentrifugeRecipes = new GT_Recipe_Map(
new HashSet<>(1200),
"gt.recipe.centrifuge",
@@ -1679,6 +1836,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
E,
true,
true).setSlotOverlay(false, false, true, GT_UITextures.OVERLAY_SLOT_CENTRIFUGE)
+ .setRecipeConfigFile("centrifuge", FIRST_ITEM_OR_FLUID_INPUT)
.setSlotOverlay(false, false, false, GT_UITextures.OVERLAY_SLOT_CANISTER)
.setSlotOverlay(true, false, GT_UITextures.OVERLAY_SLOT_CENTRIFUGE_FLUID)
.setProgressBar(GT_UITextures.PROGRESSBAR_EXTRACT, ProgressBar.Direction.RIGHT);
@@ -1698,9 +1856,13 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
E,
true,
true).setSlotOverlay(false, false, true, GT_UITextures.OVERLAY_SLOT_CHARGER)
+ .setRecipeConfigFile("electrolyzer", FIRST_ITEM_OR_FLUID_INPUT)
.setSlotOverlay(false, false, false, GT_UITextures.OVERLAY_SLOT_CANISTER)
.setSlotOverlay(true, false, GT_UITextures.OVERLAY_SLOT_CHARGER_FLUID)
.setProgressBar(GT_UITextures.PROGRESSBAR_EXTRACT, ProgressBar.Direction.RIGHT);
+ /**
+ * Use special value as coil heat level.
+ */
public static final GT_Recipe_Map sBlastRecipes = new GT_Recipe_Map(
new HashSet<>(800),
"gt.recipe.blastfurnace",
@@ -1717,7 +1879,11 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
" K",
false,
true).setProgressBar(GT_UITextures.PROGRESSBAR_ARROW, ProgressBar.Direction.RIGHT)
+ .setRecipeConfigFile("blastfurnace", FIRST_ITEM_INPUT)
.setNEISpecialInfoFormatter(HeatingCoilSpecialValueFormatter.INSTANCE);
+ /**
+ * Use special value as coil heat level.
+ */
public static final GT_Recipe_Map sPlasmaForgeRecipes = new GT_Recipe_Map_LargeNEI(
new HashSet<>(20),
"gt.recipe.plasmaforge",
@@ -1734,7 +1900,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
" K",
false,
true).setProgressBar(GT_UITextures.PROGRESSBAR_ARROW, ProgressBar.Direction.RIGHT)
- .setUsualFluidInputCount(9).setUsualFluidOutputCount(9)
+ .setUsualFluidInputCount(9).setUsualFluidOutputCount(9).setDisableOptimize(true)
.setNEISpecialInfoFormatter(HeatingCoilSpecialValueFormatter.INSTANCE);
public static final GT_Recipe_Map sTranscendentPlasmaMixerRecipes = new TranscendentPlasmaMixerRecipeMap(
@@ -1752,7 +1918,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
0,
"",
false,
- true);
+ true).setDisableOptimize(true);
public static class GT_FakeSpaceProjectRecipe extends GT_Recipe {
@@ -1877,7 +2043,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
.setSize(bar2Width, 72));
}
}.useModularUI(true).setRenderRealStackSizes(false).setUsualFluidInputCount(4).setNEIBackgroundOffset(2, 23)
- .setLogoPos(152, 83);
+ .setLogoPos(152, 83).setDisableOptimize(true);
public static class TranscendentPlasmaMixerRecipeMap extends GT_Recipe_Map {
@@ -1952,7 +2118,71 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
1,
E,
false,
- true).setProgressBar(GT_UITextures.PROGRESSBAR_ARROW, ProgressBar.Direction.RIGHT);
+ true).setProgressBar(GT_UITextures.PROGRESSBAR_ARROW, ProgressBar.Direction.RIGHT)
+ .setRecipeEmitter(builder -> {
+ Optional<GT_Recipe> rr = builder.validateInputCount(1, 2).validateOutputCount(1, 2)
+ .validateNoInputFluid().validateNoOutputFluid().noOptimize().build();
+ if (!rr.isPresent()) return Collections.emptyList();
+ ItemStack aInput1 = builder.getItemInputBasic(0);
+ ItemStack aInput2 = builder.getItemInputBasic(1);
+ ItemStack aOutput1 = builder.getItemOutput(0);
+ ItemStack aOutput2 = builder.getItemOutput(1);
+ if ((aInput1 == null && aInput2 == null) || (aOutput1 == null && aOutput2 == null))
+ return Collections.emptyList();
+ int aCoalAmount = builder.getMetadata(ADDITIVE_AMOUNT);
+ if (aCoalAmount <= 0) return Collections.emptyList();
+ GT_RecipeTemplate coll = asTemplate(rr.get());
+ for (Materials coal : new Materials[] { Materials.Coal, Materials.Charcoal }) {
+ coll.derive().setInputs(aInput1, aInput2, coal.getGems(aCoalAmount))
+ .setOutputs(aOutput1, aOutput2, Materials.DarkAsh.getDustTiny(aCoalAmount));
+ coll.derive().setInputs(aInput1, aInput2, coal.getDust(aCoalAmount))
+ .setOutputs(aOutput1, aOutput2, Materials.DarkAsh.getDustTiny(aCoalAmount));
+ }
+ int aDuration = builder.duration;
+ if (Railcraft.isModLoaded()) {
+ coll.derive()
+ .setInputs(aInput1, aInput2, RailcraftToolItems.getCoalCoke(aCoalAmount / 2))
+ .setOutputs(aOutput1, aOutput2, Materials.Ash.getDustTiny(aCoalAmount / 2))
+ .setDuration(aDuration * 2 / 3);
+ }
+ if (GTPlusPlus.isModLoaded()) {
+ ItemStack cactusCoke = GT_ModHandler
+ .getModItem(GTPlusPlus.modID, "itemCactusCoke", aCoalAmount * 2L);
+ ItemStack sugarCoke = GT_ModHandler
+ .getModItem(GTPlusPlus.modID, "itemSugarCoke", aCoalAmount * 2L);
+ coll.derive().setInputs(aInput1, aInput2, cactusCoke)
+ .setOutputs(aOutput1, aOutput2, Materials.Ash.getDustTiny(aCoalAmount * 2))
+ .setDuration(aDuration * 2 / 3);
+ coll.derive().setInputs(aInput1, aInput2, sugarCoke)
+ .setOutputs(aOutput1, aOutput2, Materials.Ash.getDustTiny(aCoalAmount * 2))
+ .setDuration(aDuration * 2 / 3);
+ }
+ if ((aInput1 == null || aInput1.stackSize <= 6)
+ && (aInput2 == null || aInput2.stackSize <= 6)
+ && (aOutput1 == null || aOutput1.stackSize <= 6)
+ && (aOutput2 == null || aOutput2.stackSize <= 6)) {
+ // we don't use GT_Utility.mul() here. It does not have the truncating we need here.
+ aInput1 = GT_Utility.multiplyStack(10L, aInput1);
+ aInput2 = GT_Utility.multiplyStack(10L, aInput2);
+ aOutput1 = GT_Utility.multiplyStack(10L, aOutput1);
+ aOutput2 = GT_Utility.multiplyStack(10L, aOutput2);
+ for (Materials coal : new Materials[] { Materials.Coal, Materials.Charcoal }) {
+ coll.derive().setInputs(aInput1, aInput2, coal.getBlocks(aCoalAmount))
+ .setOutputs(aOutput1, aOutput2, Materials.DarkAsh.getDust(aCoalAmount))
+ .setDuration(aDuration * 10);
+ coll.derive().setInputs(aInput1, aInput2, coal.getBlocks(aCoalAmount))
+ .setOutputs(aOutput1, aOutput2, Materials.DarkAsh.getDust(aCoalAmount))
+ .setDuration(aDuration * 10);
+ }
+ if (Railcraft.isModLoaded()) {
+ coll.derive()
+ .setInputs(aInput1, aInput2, EnumCube.COKE_BLOCK.getItem(aCoalAmount / 2))
+ .setOutputs(aOutput1, aOutput2, Materials.Ash.getDust(aCoalAmount / 2))
+ .setDuration(aDuration * 20 / 3);
+ }
+ }
+ return coll.getAll();
+ }).setRecipeConfigFile("primitiveblastfurnace", FIRST_ITEM_INPUT);
public static final GT_Recipe_Map sImplosionRecipes = new GT_Recipe_Map(
new HashSet<>(900),
"gt.recipe.implosioncompressor",
@@ -1970,6 +2200,33 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
true,
true).setSlotOverlay(false, false, true, GT_UITextures.OVERLAY_SLOT_IMPLOSION)
.setSlotOverlay(false, false, false, GT_UITextures.OVERLAY_SLOT_EXPLOSIVE)
+ .setRecipeConfigFile("implosion", FIRST_ITEM_INPUT).setRecipeEmitter(b -> {
+ switch (b.getItemInputsBasic().length) {
+ case 0:
+ return Collections.emptyList();
+ case 1:
+ break;
+ default:
+ return b.build().map(Collections::singletonList).orElse(Collections.emptyList());
+ }
+ Optional<GT_Recipe> t = b.noOptimize().duration(20).eut(30).validateInputCount(1, 1)
+ .validateOutputCount(1, 1).build();
+ if (!t.isPresent()) return Collections.emptyList();
+ ItemStack input = b.getItemInputBasic(0);
+ GT_RecipeTemplate coll = asTemplate(t.get());
+ int tExplosives = Math.min(b.getMetadata(ADDITIVE_AMOUNT), 64);
+ int tGunpowder = tExplosives << 1; // Worst
+ int tDynamite = Math.max(1, tExplosives >> 1); // good
+ int tTNT = tExplosives; // Slightly better
+ int tITNT = Math.max(1, tExplosives >> 2); // the best
+ if (tGunpowder < 65)
+ coll.derive().setInputs(input, ItemList.Block_Powderbarrel.get(tGunpowder));
+ if (tDynamite < 17)
+ coll.derive().setInputs(input, GT_ModHandler.getIC2Item("dynamite", tDynamite, null));
+ coll.derive().setInputs(input, new ItemStack(Blocks.tnt, tTNT));
+ coll.derive().setInputs(input, GT_ModHandler.getIC2Item("industrialTnt", tITNT, null));
+ return coll.getAll();
+ }).setDisableOptimize(true)
.setProgressBar(GT_UITextures.PROGRESSBAR_COMPRESS, ProgressBar.Direction.RIGHT);
public static final GT_Recipe_Map sVacuumRecipes = new GT_Recipe_Map(
new HashSet<>(305),
@@ -1987,7 +2244,21 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
E,
false,
true).setProgressBar(GT_UITextures.PROGRESSBAR_ARROW, ProgressBar.Direction.RIGHT)
- .setUsualFluidInputCount(2);
+ .setRecipeConfigFile("vacuumfreezer", FIRST_ITEM_INPUT).setRecipeEmitter(b -> {
+ b.noOptimize();
+ FluidStack in, out;
+ if (isArrayOfLength(b.getItemInputsBasic(), 1) && isArrayOfLength(b.getItemOutputs(), 1)
+ && isArrayEmptyOrNull(b.getFluidInputs())
+ && isArrayEmptyOrNull(b.getFluidOutputs())
+ && (in = GT_Utility.getFluidForFilledItem(b.getItemInputBasic(0), true)) != null
+ && (out = GT_Utility.getFluidForFilledItem(b.getItemOutput(0), true)) != null) {
+ return Arrays.asList(
+ b.build().get(),
+ b.fluidInputs(in).fluidOutputs(out).noItemInputs().noItemOutputs().build()
+ .get());
+ }
+ return buildOrEmpty(b);
+ }).setUsualFluidInputCount(2);
public static final GT_Recipe_Map sChemicalRecipes = new GT_Recipe_Map(
new HashSet<>(1170),
"gt.recipe.chemicalreactor",
@@ -2008,18 +2279,21 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
.setSlotOverlay(true, false, GT_UITextures.OVERLAY_SLOT_MOLECULAR_3)
.setSlotOverlay(false, true, GT_UITextures.OVERLAY_SLOT_VIAL_1)
.setSlotOverlay(true, true, GT_UITextures.OVERLAY_SLOT_VIAL_2)
+ .setRecipeConfigFile("chemicalreactor", FIRST_ITEM_OR_FLUID_OUTPUT)
.setProgressBar(GT_UITextures.PROGRESSBAR_ARROW_MULTIPLE, ProgressBar.Direction.RIGHT);
public static final GT_Recipe_Map sMultiblockChemicalRecipes = new GT_Recipe_Map_LargeChemicalReactor()
.setProgressBar(GT_UITextures.PROGRESSBAR_ARROW_MULTIPLE, ProgressBar.Direction.RIGHT)
.setUsualFluidInputCount(6).setUsualFluidOutputCount(6);
public static final GT_Recipe_Map sDistillationRecipes = new GT_Recipe_Map_DistillationTower()
+ .setRecipeConfigFile("distillation", FIRST_FLUIDSTACK_INPUT)
.setProgressBar(GT_UITextures.PROGRESSBAR_ARROW_MULTIPLE, ProgressBar.Direction.RIGHT)
- .setUsualFluidOutputCount(11);
+ .setUsualFluidOutputCount(11).setDisableOptimize(true);
public static final GT_Recipe_Map_OilCracker sCrackingRecipes = (GT_Recipe_Map_OilCracker) new GT_Recipe_Map_OilCracker()
+ .setRecipeConfigFile("cracking", FIRST_FLUIDSTACK_INPUT)
.setProgressBar(GT_UITextures.PROGRESSBAR_ARROW_MULTIPLE, ProgressBar.Direction.RIGHT)
.setUsualFluidInputCount(2);
/**
- * Use sCrackingRecipes instead
+ * @deprecated Use sCrackingRecipes instead
*/
@Deprecated
public static final GT_Recipe_Map sCrakingRecipes = sCrackingRecipes;
@@ -2039,7 +2313,8 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
1,
E,
true,
- true).setProgressBar(GT_UITextures.PROGRESSBAR_ARROW, ProgressBar.Direction.RIGHT);
+ true).setProgressBar(GT_UITextures.PROGRESSBAR_ARROW, ProgressBar.Direction.RIGHT)
+ .setDisableOptimize(true).setRecipeConfigFile("pyrolyse", FIRST_ITEM_INPUT);
public static final GT_Recipe_Map sWiremillRecipes = new GT_Recipe_Map(
new HashSet<>(450),
"gt.recipe.wiremill",
@@ -2056,6 +2331,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
E,
true,
true).setSlotOverlay(false, false, GT_UITextures.OVERLAY_SLOT_WIREMILL)
+ .setRecipeConfigFile("wiremill", FIRST_ITEM_INPUT)
.setProgressBar(GT_UITextures.PROGRESSBAR_WIREMILL, ProgressBar.Direction.RIGHT);
public static final GT_Recipe_Map sBenderRecipes = new GT_Recipe_Map(
new HashSet<>(5000),
@@ -2073,6 +2349,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
E,
true,
true).setSlotOverlay(false, false, GT_UITextures.OVERLAY_SLOT_BENDER)
+ .setRecipeConfigFile("bender", FIRST_ITEM_INPUT)
.setProgressBar(GT_UITextures.PROGRESSBAR_BENDING, ProgressBar.Direction.RIGHT);
public static final GT_Recipe_Map sAlloySmelterRecipes = new GT_Recipe_Map(
new HashSet<>(12000),
@@ -2089,7 +2366,21 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
1,
E,
true,
- true).setSlotOverlay(false, false, GT_UITextures.OVERLAY_SLOT_FURNACE)
+ true).setSlotOverlay(false, false, GT_UITextures.OVERLAY_SLOT_FURNACE).setRecipeEmitter(b -> {
+ if (Materials.Graphite.contains(b.getItemInputBasic(0))) return Collections.emptyList();
+ if (GT_Utility.isArrayOfLength(b.getItemInputsBasic(), 1)) {
+ ItemStack aInput1 = b.getItemInputBasic(0);
+ if (((OrePrefixes.ingot.contains(aInput1)) || (OrePrefixes.dust.contains(aInput1))
+ || (OrePrefixes.gem.contains(aInput1))))
+ return Collections.emptyList();
+ }
+ return buildOrEmpty(
+ b.validateNoInputFluid().validateNoOutputFluid().validateInputCount(1, 2)
+ .validateOutputCount(1, 1));
+ }).setRecipeConfigFile(
+ "alloysmelting",
+ r -> GT_Config.getStackConfigName(
+ GT_Utility.isArrayOfLength(r.mInputs, 1) ? r.mInputs[0] : r.mOutputs[0]))
.setProgressBar(GT_UITextures.PROGRESSBAR_ARROW, ProgressBar.Direction.RIGHT)
.setSlotOverlaySteam(false, GT_UITextures.OVERLAY_SLOT_FURNACE_STEAM)
.setProgressBarSteam(GT_UITextures.PROGRESSBAR_ARROW_STEAM);
@@ -2109,6 +2400,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
E,
true,
true).setSlotOverlay(false, false, GT_UITextures.OVERLAY_SLOT_CIRCUIT)
+ .setRecipeConfigFile("assembling", FIRST_ITEM_OUTPUT)
.setProgressBar(GT_UITextures.PROGRESSBAR_ASSEMBLE, ProgressBar.Direction.RIGHT);
public static final GT_Recipe_Map sCircuitAssemblerRecipes = new GT_Recipe_Map_Assembler(
new HashSet<>(605),
@@ -2125,7 +2417,8 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
1,
E,
true,
- true).setNEIUnificateOutput(!Loader.isModLoaded("neicustomdiagram"))
+ true).setNEIUnificateOutput(!NEICustomDiagrams.isModLoaded())
+ .setRecipeConfigFile("circuitassembler", FIRST_ITEM_OUTPUT)
.setSlotOverlay(false, false, GT_UITextures.OVERLAY_SLOT_CIRCUIT)
.setProgressBar(GT_UITextures.PROGRESSBAR_CIRCUIT_ASSEMBLER, ProgressBar.Direction.RIGHT);
public static final GT_Recipe_Map sCannerRecipes = new GT_Recipe_Map(
@@ -2144,8 +2437,10 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
E,
true,
true).setSlotOverlay(false, false, true, GT_UITextures.OVERLAY_SLOT_CANNER)
+ .setRecipeConfigFile("canning", FIRST_ITEM_INPUT)
.setSlotOverlay(false, false, false, GT_UITextures.OVERLAY_SLOT_CANISTER)
.setProgressBar(GT_UITextures.PROGRESSBAR_CANNER, ProgressBar.Direction.RIGHT);
+ @Deprecated
public static final GT_Recipe_Map sCNCRecipes = new GT_Recipe_Map(
new HashSet<>(100),
"gt.recipe.cncmachine",
@@ -2180,6 +2475,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
true).setSlotOverlay(false, false, GT_UITextures.OVERLAY_SLOT_ROD_1)
.setSlotOverlay(false, true, true, GT_UITextures.OVERLAY_SLOT_ROD_2)
.setSlotOverlay(false, true, false, GT_UITextures.OVERLAY_SLOT_DUST)
+ .setRecipeConfigFile("lathe", FIRST_ITEM_INPUT)
.setProgressBar(GT_UITextures.PROGRESSBAR_LATHE, ProgressBar.Direction.RIGHT)
.addSpecialTexture(5, 18, 98, 24, GT_UITextures.PROGRESSBAR_LATHE_BASE);
public static final GT_Recipe_Map sCutterRecipes = new GT_Recipe_Map(
@@ -2199,7 +2495,23 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
true,
true).setSlotOverlay(false, false, GT_UITextures.OVERLAY_SLOT_BOX)
.setSlotOverlay(false, true, true, GT_UITextures.OVERLAY_SLOT_CUTTER_SLICED)
- .setSlotOverlay(false, true, false, GT_UITextures.OVERLAY_SLOT_DUST)
+ .setSlotOverlay(false, true, false, GT_UITextures.OVERLAY_SLOT_DUST).setRecipeEmitter(b -> {
+ b.validateInputCount(1, 2).validateOutputCount(1, 4).validateNoOutputFluid();
+ if (b.getFluidInputs() != null || !b.isValid())
+ return buildOrEmpty(b.validateInputFluidCount(1, 1));
+ int aDuration = b.getDuration(), aEUt = b.getEUt();
+ Collection<GT_Recipe> ret = new ArrayList<>();
+ b.copy().fluidInputs(
+ Materials.Water.getFluid(GT_Utility.clamp(aDuration * aEUt / 320, 4, 1000)))
+ .duration(aDuration * 2).build().ifPresent(ret::add);
+ b.copy().fluidInputs(
+ GT_ModHandler.getDistilledWater(GT_Utility.clamp(aDuration * aEUt / 426, 3, 750)))
+ .duration(aDuration * 2).build().ifPresent(ret::add);
+ b.fluidInputs(
+ Materials.Lubricant.getFluid(GT_Utility.clamp(aDuration * aEUt / 1280, 1, 250)))
+ .duration(aDuration).build().ifPresent(ret::add);
+ return ret;
+ }).setRecipeConfigFile("cutting", FIRST_ITEM_INPUT)
.setProgressBar(GT_UITextures.PROGRESSBAR_CUT, ProgressBar.Direction.RIGHT);
public static final GT_Recipe_Map sSlicerRecipes = new GT_Recipe_Map(
new HashSet<>(20),
@@ -2219,6 +2531,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
true).setSlotOverlay(false, false, true, GT_UITextures.OVERLAY_SLOT_SQUARE)
.setSlotOverlay(false, false, false, GT_UITextures.OVERLAY_SLOT_SLICE_SHAPE)
.setSlotOverlay(false, true, GT_UITextures.OVERLAY_SLOT_SLICER_SLICED)
+ .setRecipeConfigFile("slicer", FIRST_ITEM_OUTPUT)
.setProgressBar(GT_UITextures.PROGRESSBAR_SLICE, ProgressBar.Direction.RIGHT);
public static final GT_Recipe_Map sExtruderRecipes = new GT_Recipe_Map(
new HashSet<>(13000),
@@ -2236,6 +2549,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
E,
true,
true).setSlotOverlay(false, false, false, GT_UITextures.OVERLAY_SLOT_EXTRUDER_SHAPE)
+ .setRecipeConfigFile("extruder", FIRST_ITEM_OUTPUT)
.setProgressBar(GT_UITextures.PROGRESSBAR_EXTRUDE, ProgressBar.Direction.RIGHT);
public static final GT_Recipe_Map sHammerRecipes = new GT_Recipe_Map(
@@ -2255,6 +2569,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
true,
true).setUsualFluidInputCount(2).setUsualFluidOutputCount(2)
.setSlotOverlay(false, false, GT_UITextures.OVERLAY_SLOT_HAMMER)
+ .setRecipeConfigFile("forgehammer", FIRST_ITEM_OUTPUT)
.setProgressBar(GT_UITextures.PROGRESSBAR_HAMMER, ProgressBar.Direction.DOWN)
.addSpecialTexture(20, 6, 78, 42, GT_UITextures.PROGRESSBAR_HAMMER_BASE)
.setSlotOverlaySteam(false, GT_UITextures.OVERLAY_SLOT_HAMMER_STEAM)
@@ -2277,6 +2592,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
true,
true).setSlotOverlay(false, false, GT_UITextures.OVERLAY_SLOT_CENTRIFUGE)
.setSlotOverlay(true, true, GT_UITextures.OVERLAY_SLOT_UUA)
+ .setRecipeConfigFile("amplifier", FIRST_ITEM_INPUT)
.setProgressBar(GT_UITextures.PROGRESSBAR_EXTRACT, ProgressBar.Direction.RIGHT);
public static final GT_Recipe_Map sMassFabFakeRecipes = new GT_Recipe_Map(
new HashSet<>(2),
@@ -2519,7 +2835,8 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
0,
"",
true,
- false);
+ false).setRecipeEmitter(GT_RecipeMapUtil::buildRecipeForMultiblockNoCircuit);
+
public static final GT_Recipe_Map sMultiblockCentrifugeRecipes = new GT_Recipe_Map(
new HashSet<>(1200),
"gt.recipe.largecentrifuge",
@@ -2535,7 +2852,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
0,
"",
true,
- false);
+ false).setRecipeEmitter(GT_RecipeMapUtil::buildRecipeForMultiblock).setDisableOptimize(true);
public static final GT_Recipe_Map sMultiblockMixerRecipes = new GT_Recipe_Map(
new HashSet<>(900),
"gt.recipe.largemixer",
@@ -2551,9 +2868,9 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
0,
"",
true,
- false);
+ false).setRecipeEmitter(GT_RecipeMapUtil::buildRecipeForMultiblock).setDisableOptimize(true);
public static final GT_Recipe_Map_LargeBoilerFakeFuels sLargeBoilerFakeFuels = (GT_Recipe_Map_LargeBoilerFakeFuels) new GT_Recipe_Map_LargeBoilerFakeFuels()
- .setProgressBar(GT_UITextures.PROGRESSBAR_ARROW, ProgressBar.Direction.RIGHT);
+ .setProgressBar(GT_UITextures.PROGRESSBAR_ARROW, ProgressBar.Direction.RIGHT).setDisableOptimize(true);
public static final GT_Recipe_Map sNanoForge = new GT_Recipe_Map(
new HashSet<>(10),
@@ -2570,7 +2887,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
1,
"",
false,
- true).useModularUI(true).setUsualFluidInputCount(3)
+ true).useModularUI(true).setUsualFluidInputCount(3).setDisableOptimize(true)
.setSlotOverlay(false, false, true, GT_UITextures.OVERLAY_SLOT_LENS)
.setProgressBar(GT_UITextures.PROGRESSBAR_ASSEMBLE, ProgressBar.Direction.RIGHT);
@@ -2589,7 +2906,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
0,
E,
true,
- true).useModularUI(true).setUsualFluidInputCount(3).setUsualFluidOutputCount(0)
+ true).useModularUI(true).setUsualFluidInputCount(3).setUsualFluidOutputCount(0).setDisableOptimize(true)
.setProgressBar(GT_UITextures.PROGRESSBAR_ASSEMBLE, ProgressBar.Direction.RIGHT)
.setNEISpecialInfoFormatter((recipeInfo, applyPrefixAndSuffix) -> {
List<String> result = new ArrayList<>();
@@ -2608,7 +2925,22 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
return result;
});
- public static final GT_Recipe_Map_IC2NuclearFake sIC2NuclearFakeRecipe = new GT_Recipe_Map_IC2NuclearFake();
+ public static final GT_Recipe_Map_IC2NuclearFake sIC2NuclearFakeRecipe = (GT_Recipe_Map_IC2NuclearFake) new GT_Recipe_Map_IC2NuclearFake()
+ .setDisableOptimize(true);
+
+ static {
+ sCentrifugeRecipes.addDownstream(sMultiblockCentrifugeRecipes.deepCopyInput());
+ sMixerRecipes.addDownstream(sMultiblockMixerRecipes.deepCopyInput());
+ sElectrolyzerRecipes.addDownstream(sMultiblockElectrolyzerRecipes.deepCopyInput());
+ sDieselFuels.addDownstream(
+ IGT_RecipeMap.newRecipeMap(
+ b -> b.build().map(sLargeBoilerFakeFuels::addDieselRecipe).map(Collections::singletonList)
+ .orElse(Collections.emptyList())));
+ sDenseLiquidFuels.addDownstream(
+ IGT_RecipeMap.newRecipeMap(
+ b -> b.build().map(sLargeBoilerFakeFuels::addDenseLiquidRecipe)
+ .map(Collections::singletonList).orElse(Collections.emptyList())));
+ }
/**
* HashMap of Recipes based on their Items
@@ -2747,6 +3079,13 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
private int neiTextColorOverride = -1;
private INEISpecialInfoFormatter neiSpecialInfoFormatter;
+ private boolean checkForCollision = true, allowNoInput, allowNoInputFluid, allowNoOutput, allowNoOutputFluid;
+ private boolean disableOptimize = false;
+ private Function<? super GT_RecipeBuilder, ? extends Iterable<? extends GT_Recipe>> recipeEmitter = this::defaultBuildRecipe;
+ private Function<? super GT_Recipe, ? extends GT_Recipe> specialHandler;
+ private String recipeConfigCategory;
+ private Function<? super GT_Recipe, String> recipeConfigKeyConvertor;
+ private final List<IGT_RecipeMap> downstreams = new ArrayList<>(0);
/**
* Flag if a comparator should be used to search the recipe in NEI (which is defined in {@link Power}). Else
@@ -2838,6 +3177,11 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
setNEIUnificateOutput(aNEIUnificateOutput);
}
+ public GT_Recipe_Map setDisableOptimize(boolean disableOptimize) {
+ this.disableOptimize = disableOptimize;
+ return this;
+ }
+
public GT_Recipe_Map setNEIUnificateOutput(boolean mNEIUnificateOutput) {
this.mNEIUnificateOutput = mNEIUnificateOutput;
return this;
@@ -3015,6 +3359,112 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
return this;
}
+ /**
+ * Change how recipes are emitted by a particular recipe builder. Can emit multiple recipe per builder.
+ */
+ public GT_Recipe_Map setRecipeEmitter(
+ Function<? super GT_RecipeBuilder, ? extends Iterable<? extends GT_Recipe>> func) {
+ this.recipeEmitter = func;
+ return this;
+ }
+
+ /**
+ * Change how recipes are emitted by a particular recipe builder. Can emit multiple recipe per builder.
+ *
+ * Unlike {@link #setRecipeEmitter(Function)}, this one does not clear the existing recipe being emitted, if any
+ */
+ public GT_Recipe_Map combineRecipeEmitter(
+ Function<? super GT_RecipeBuilder, ? extends Iterable<? extends GT_Recipe>> func) {
+ // move recipeEmitter to local variable, so lambda capture the function itself instead of this
+ Function<? super GT_RecipeBuilder, ? extends Iterable<? extends GT_Recipe>> cur = recipeEmitter;
+ this.recipeEmitter = b -> Iterables.concat(cur.apply(b), func.apply(b));
+ return this;
+ }
+
+ /**
+ * Change how recipes are emitted by a particular recipe builder. Should not return null.
+ */
+ public GT_Recipe_Map setRecipeEmitterSingle(Function<? super GT_RecipeBuilder, ? extends GT_Recipe> func) {
+ return setRecipeEmitter(func.andThen(Collections::singletonList));
+ }
+
+ /**
+ * Change how recipes are emitted by a particular recipe builder. Effectively add a new recipe per recipe added.
+ * func must not return null.
+ *
+ * Unlike {@link #setRecipeEmitter(Function)}, this one does not clear the existing recipe being emitted, if any
+ */
+ public GT_Recipe_Map combineRecipeEmitterSingle(Function<? super GT_RecipeBuilder, ? extends GT_Recipe> func) {
+ return combineRecipeEmitter(func.andThen(Collections::singletonList));
+ }
+
+ private static <T> Function<? super T, ? extends T> withIdentityReturn(Consumer<T> func) {
+ return r -> {
+ func.accept(r);
+ return r;
+ };
+ }
+
+ /**
+ * Run a custom hook on all recipes added <b>via builder</b>. For more complicated behavior subclass this, then
+ * override {@link #doAdd(GT_RecipeBuilder)}
+ *
+ * Recipes added via one of the overloads of addRecipe will NOT be affected by this function.
+ */
+ public GT_Recipe_Map setRecipeSpecialHandler(Function<? super GT_Recipe, ? extends GT_Recipe> func) {
+ this.specialHandler = func;
+ return this;
+ }
+
+ /**
+ * Run a custom hook on all recipes added <b>via builder</b>. For more complicated behavior, create a subclass
+ * and override {@link #doAdd(GT_RecipeBuilder)}
+ *
+ * Recipes added via one of the overloads of addRecipe will NOT be affected by this function.
+ */
+ public GT_Recipe_Map setRecipeSpecialHandler(Consumer<GT_Recipe> func) {
+ return setRecipeSpecialHandler(withIdentityReturn(func));
+ }
+
+ /**
+ * Run a custom hook on all recipes added <b>via builder</b>. For more complicated behavior subclass this, then
+ * override {@link #doAdd(GT_RecipeBuilder)}
+ *
+ * Recipes added via one of the overloads of addRecipe will NOT be affected by this function.
+ *
+ * Unlike {@link #setRecipeSpecialHandler(Function)}, this one will not replace the existing special handler.
+ * The supplied function will be given the output of existing handler when a recipe is added.
+ */
+ public GT_Recipe_Map chainRecipeSpecialHandler(Function<? super GT_Recipe, ? extends GT_Recipe> func) {
+ this.specialHandler = specialHandler == null ? func : specialHandler.andThen(func);
+ return this;
+ }
+
+ /**
+ * Run a custom hook on all recipes added <b>via builder</b>. For more complicated behavior subclass this, then
+ * override {@link #doAdd(GT_RecipeBuilder)}
+ *
+ * Recipes added via one of the overloads of addRecipe will NOT be affected by this function.
+ *
+ * Unlike {@link #setRecipeSpecialHandler(Function)}, this one will not replace the existing special handler.
+ * The supplied function will be given the output of existing handler when a recipe is added.
+ */
+ public GT_Recipe_Map chainRecipeSpecialHandler(Consumer<GT_Recipe> func) {
+ return chainRecipeSpecialHandler(withIdentityReturn(func));
+ }
+
+ public GT_Recipe_Map setRecipeConfigFile(String category, Function<? super GT_Recipe, String> keyConvertor) {
+ if (StringUtils.isBlank(category) || keyConvertor == null) throw new IllegalArgumentException();
+ this.recipeConfigCategory = category;
+ this.recipeConfigKeyConvertor = keyConvertor;
+ return this;
+ }
+
+ @Override
+ public void addDownstream(IGT_RecipeMap downstream) {
+ this.downstreams.add(downstream);
+ }
+
public GT_Recipe addRecipe(boolean aOptimize, ItemStack[] aInputs, ItemStack[] aOutputs, Object aSpecial,
int[] aOutputChances, FluidStack[] aFluidInputs, FluidStack[] aFluidOutputs, int aDuration, int aEUt,
int aSpecialValue) {
@@ -3178,6 +3628,55 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
return addRecipe(aRecipe, aCheckForCollisions, true, hidden);
}
+ @Override
+ public Collection<GT_Recipe> doAdd(GT_RecipeBuilder builder) {
+ Iterable<? extends GT_Recipe> recipes = recipeEmitter.apply(builder);
+ Collection<GT_Recipe> ret = new ArrayList<>();
+ for (GT_Recipe r : recipes) {
+ if (recipeConfigCategory != null) {
+ String configKey = recipeConfigKeyConvertor.apply(r);
+ if (configKey != null && (r.mDuration = GregTech_API.sRecipeFile
+ .get(recipeConfigCategory, configKey, r.mDuration)) <= 0) {
+ continue;
+ }
+ }
+ if (r.mFluidInputs.length < mMinimalInputFluids && r.mInputs.length < mMinimalInputItems) return null;
+ if (r.mSpecialValue == 0) {
+ // new style cleanroom/lowgrav handling
+ int specialValue = 0;
+ if (builder.getMetadata(GT_RecipeConstants.LOW_GRAVITY, false)) specialValue -= 100;
+ if (builder.getMetadata(GT_RecipeConstants.CLEANROOM, false)) specialValue -= 200;
+ for (GT_RecipeBuilder.MetadataIdentifier<Integer> ident : SPECIAL_VALUE_ALIASES) {
+ Integer metadata = builder.getMetadata(ident, null);
+ if (metadata != null) {
+ specialValue = metadata;
+ break;
+ }
+ }
+ r.mSpecialValue = specialValue;
+ }
+ if (specialHandler != null) r = specialHandler.apply(r);
+ if (r == null) continue;
+ if (checkForCollision
+ && findRecipe(null, false, true, Long.MAX_VALUE, r.mFluidInputs, r.mInputs) != null)
+ continue;
+ ret.add(add(r));
+ }
+ if (!ret.isEmpty()) {
+ for (IGT_RecipeMap downstream : downstreams) {
+ downstream.doAdd(builder);
+ }
+ }
+ return ret;
+ }
+
+ public final Iterable<? extends GT_Recipe> defaultBuildRecipe(GT_RecipeBuilder builder) {
+ // TODO sensible validation
+ if (!downstreams.isEmpty() && disableOptimize && !builder.optimize)
+ return buildOrEmpty(builder.copy().noOptimize());
+ return buildOrEmpty(builder);
+ }
+
public GT_Recipe add(GT_Recipe aRecipe) {
mRecipeList.add(aRecipe);
for (FluidStack aFluid : aRecipe.mFluidInputs) {
@@ -4095,6 +4594,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
aNEISpecialValuePost,
aShowVoltageAmperageInNEI,
aNEIAllowed);
+ setDisableOptimize(true);
}
public GT_Recipe addFuel(ItemStack aInput, ItemStack aOutput, int aFuelValueInEU) {
@@ -5371,7 +5871,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
}
private GT_Recipe addRecipe(GT_Recipe recipe, double baseBurnTime, boolean isAllowedFuel) {
- recipe = new GT_Recipe(recipe);
+ recipe = new GT_Recipe(recipe, true);
// Some recipes will have a burn time like 15.9999999 and % always rounds down
double floatErrorCorrection = 0.0001;
@@ -5404,7 +5904,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
}
private GT_Recipe addRecipe(GT_Recipe recipe, double baseBurnTime) {
- recipe = new GT_Recipe(recipe);
+ recipe = new GT_Recipe(recipe, true);
// Some recipes will have a burn time like 15.9999999 and % always rounds down
double floatErrorCorrection = 0.0001;
@@ -5696,6 +6196,29 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
ItemStack[][] mOreDictAlt;
+ GT_Recipe_WithAlt(ItemStack[] mInputs, ItemStack[] mOutputs, FluidStack[] mFluidInputs,
+ FluidStack[] mFluidOutputs, int[] mChances, Object mSpecialItems, int mDuration, int mEUt,
+ int mSpecialValue, boolean mEnabled, boolean mHidden, boolean mFakeRecipe, boolean mCanBeBuffered,
+ boolean mNeedsEmptyOutput, String[] neiDesc, ItemStack[][] mOreDictAlt) {
+ super(
+ mInputs,
+ mOutputs,
+ mFluidInputs,
+ mFluidOutputs,
+ mChances,
+ mSpecialItems,
+ mDuration,
+ mEUt,
+ mSpecialValue,
+ mEnabled,
+ mHidden,
+ mFakeRecipe,
+ mCanBeBuffered,
+ mNeedsEmptyOutput,
+ neiDesc);
+ this.mOreDictAlt = mOreDictAlt;
+ }
+
public GT_Recipe_WithAlt(boolean aOptimize, ItemStack[] aInputs, ItemStack[] aOutputs, Object aSpecialItems,
int[] aChances, FluidStack[] aFluidInputs, FluidStack[] aFluidOutputs, int aDuration, int aEUt,
int aSpecialValue, ItemStack[][] aAlt) {
diff --git a/src/main/java/gregtech/api/util/GT_RecipeBuilder.java b/src/main/java/gregtech/api/util/GT_RecipeBuilder.java
new file mode 100644
index 0000000000..199d79af84
--- /dev/null
+++ b/src/main/java/gregtech/api/util/GT_RecipeBuilder.java
@@ -0,0 +1,713 @@
+package gregtech.api.util;
+
+import java.util.*;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.launchwrapper.Launch;
+import net.minecraftforge.fluids.FluidStack;
+
+import gregtech.api.interfaces.IGT_RecipeMap;
+import gregtech.api.objects.GT_FluidStack;
+import gregtech.api.util.extensions.ArrayExt;
+
+public class GT_RecipeBuilder {
+
+ private static final boolean DEBUG_MODE;
+
+ public static final int WILDCARD = 32767;
+
+ // time units
+ public static final int HOURS = 20 * 60 * 60;
+ public static final int MINUTES = 20 * 60;
+ public static final int SECONDS = 20;
+ public static final int TICKS = 1;
+
+ // fluid units
+ public static final int INGOTS = 144;
+ public static final int HALF_INGOT = 72;
+ public static final int QUARTER_INGOT = 36;
+ public static final int EIGHTH_INGOT = 18;
+ public static final int NUGGETS = 16;
+ public static final int BUCKETS = 1000;
+
+ static {
+ boolean tmp;
+ try {
+ tmp = Boolean.parseBoolean(System.getProperty("gt.recipebuilder.debug"));
+ } catch (IllegalArgumentException | NullPointerException e) {
+ // turn on debug by default in dev mode
+ // this will be overridden if above property is present and set to false
+ tmp = (Boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment");
+ }
+ DEBUG_MODE = tmp;
+ }
+
+ protected ItemStack[] inputsBasic;
+ protected Object[] inputsOreDict;
+ protected ItemStack[] outputs;
+ protected ItemStack[][] alts;
+ protected FluidStack[] fluidInputs;
+ protected FluidStack[] fluidOutputs;
+ protected int[] chances;
+ protected Object special;
+ protected int duration = -1;
+ protected int eut = -1;
+ protected int specialValue;
+ protected boolean enabled = true;
+ protected boolean hidden = false;
+ protected boolean fakeRecipe = false;
+ protected boolean mCanBeBuffered = true;
+ protected boolean mNeedsEmptyOutput = false;
+ protected String[] neiDesc;
+ protected boolean optimize = true;
+ protected Map<MetadataIdentifier<?>, Object> additionalData = new HashMap<>();
+ protected boolean valid = true;
+
+ GT_RecipeBuilder() {}
+
+ private GT_RecipeBuilder(ItemStack[] inputsBasic, Object[] inputsOreDict, ItemStack[] outputs, ItemStack[][] alts,
+ FluidStack[] fluidInputs, FluidStack[] fluidOutputs, int[] chances, Object special, int duration, int eut,
+ int specialValue, boolean enabled, boolean hidden, boolean fakeRecipe, boolean mCanBeBuffered,
+ boolean mNeedsEmptyOutput, String[] neiDesc, boolean optimize,
+ Map<MetadataIdentifier<?>, Object> additionalData) {
+ this.inputsBasic = inputsBasic;
+ this.inputsOreDict = inputsOreDict;
+ this.outputs = outputs;
+ this.alts = alts;
+ this.fluidInputs = fluidInputs;
+ this.fluidOutputs = fluidOutputs;
+ this.chances = chances;
+ this.special = special;
+ this.duration = duration;
+ this.eut = eut;
+ this.specialValue = specialValue;
+ this.enabled = enabled;
+ this.hidden = hidden;
+ this.fakeRecipe = fakeRecipe;
+ this.mCanBeBuffered = mCanBeBuffered;
+ this.mNeedsEmptyOutput = mNeedsEmptyOutput;
+ this.neiDesc = neiDesc;
+ this.optimize = optimize;
+ this.additionalData.putAll(additionalData);
+ }
+
+ private static FluidStack[] fix(FluidStack[] fluidInputs) {
+ return Arrays.stream(fluidInputs).filter(Objects::nonNull).map(GT_FluidStack::new).toArray(FluidStack[]::new);
+ }
+
+ private static ItemStack[] fix(ItemStack[] inputs) {
+ return GT_OreDictUnificator.setStackArray(true, ArrayExt.withoutTrailingNulls(inputs, ItemStack[]::new));
+ }
+
+ public static GT_RecipeBuilder builder() {
+ return new GT_RecipeBuilder();
+ }
+
+ /**
+ * Non-OreDicted item inputs. Assumes input is unified.
+ */
+ public GT_RecipeBuilder itemInputsUnified(ItemStack... inputs) {
+ inputsBasic = ArrayExt.withoutTrailingNulls(inputs, ItemStack[]::new);
+ inputsOreDict = null;
+ alts = null;
+ return this;
+ }
+
+ /**
+ * Non-OreDicted item inputs. Assumes input is not unified.
+ */
+ public GT_RecipeBuilder itemInputs(ItemStack... inputs) {
+ inputsBasic = fix(inputs);
+ inputsOreDict = null;
+ alts = null;
+ return this;
+ }
+
+ /**
+ * OreDicted item inputs. Currently only used for assline recipes adder.
+ */
+ public GT_RecipeBuilder itemInputs(Object... inputs) {
+ inputsOreDict = inputs;
+ alts = new ItemStack[inputs.length][];
+ for (int i = 0, inputsLength = inputs.length; i < inputsLength; i++) {
+ Object input = inputs[i];
+ if (input instanceof ItemStack) {
+ alts[i] = new ItemStack[] { (ItemStack) input };
+ } else if (input instanceof ItemStack[]) {
+ alts[i] = ((ItemStack[]) input).clone();
+ } else if (input instanceof Object[]) {
+ Object[] arr = (Object[]) input;
+ if (arr.length != 2) continue;
+ List<ItemStack> ores = GT_OreDictUnificator.getOres(arr[0]);
+ if (ores.isEmpty()) continue;
+ int size = ((Number) arr[1]).intValue();
+ alts[i] = ores.stream().map(s -> GT_Utility.copyAmount(size, s)).filter(GT_Utility::isStackValid)
+ .toArray(ItemStack[]::new);
+ } else if (input == null) {
+ if (DEBUG_MODE) {
+ throw new NullPointerException();
+ } else {
+ GT_Log.err.printf(
+ "null detected in recipe oredict input. ignoring. %s%n",
+ new NullPointerException());
+ alts[i] = new ItemStack[0];
+ }
+ } else {
+ throw new IllegalArgumentException("index " + i + ", unexpected type: " + input.getClass());
+ }
+ }
+ inputsBasic = Arrays.stream(alts).map(ss -> ss.length > 0 ? ss[0] : null).toArray(ItemStack[]::new);
+ // optimize cannot handle recipes with alts
+ return noOptimize();
+ }
+
+ /**
+ * Same as itemInputs(), but make it clear that no item inputs is intended, instead of a mistake.
+ */
+ public GT_RecipeBuilder noItemInputs() {
+ // this does not call into one of the itemInputs, to make it clear what is the expected behavior here.
+ inputsBasic = new ItemStack[0];
+ inputsOreDict = null;
+ alts = null;
+ return this;
+ }
+
+ public GT_RecipeBuilder itemOutputs(ItemStack... outputs) {
+ this.outputs = outputs;
+ if (chances != null && chances.length != outputs.length) {
+ throw new IllegalArgumentException("Output chances array and items array length differs");
+ }
+ return this;
+ }
+
+ public GT_RecipeBuilder noItemOutputs() {
+ return itemOutputs();
+ }
+
+ public GT_RecipeBuilder fluidInputs(FluidStack... fluidInputs) {
+ this.fluidInputs = fix(fluidInputs);
+ return this;
+ }
+
+ public GT_RecipeBuilder noFluidInputs() {
+ return fluidInputs == null ? fluidInputs() : this;
+ }
+
+ public GT_RecipeBuilder fluidOutputs(FluidStack... fluidOutputs) {
+ this.fluidOutputs = fix(fluidOutputs);
+ return this;
+ }
+
+ public GT_RecipeBuilder noFluidOutputs() {
+ return fluidOutputs();
+ }
+
+ public GT_RecipeBuilder noOutputs() {
+ return noFluidOutputs().noItemOutputs();
+ }
+
+ public GT_RecipeBuilder outputChances(int... chances) {
+ if (outputs != null && chances.length != outputs.length) {
+ throw new IllegalArgumentException("Output chances array and items array length differs");
+ }
+ this.chances = chances;
+ return this;
+ }
+
+ public GT_RecipeBuilder special(Object special) {
+ this.special = special;
+ return this;
+ }
+
+ /**
+ * Really just {@link #special(Object)}, but with a different signature to make it less confusing. WARNING: only for
+ * legacy recipe map. do not abuse.
+ */
+ public GT_RecipeBuilder specialItem(ItemStack specialItem) {
+ return special(specialItem);
+ }
+
+ public GT_RecipeBuilder duration(int duration) {
+ this.duration = duration;
+ return this;
+ }
+
+ public GT_RecipeBuilder eut(int eut) {
+ this.eut = eut;
+ return this;
+ }
+
+ public GT_RecipeBuilder eut(long eut) {
+ this.eut = (int) eut;
+ return this;
+ }
+
+ /**
+ * prefer to use metadata over this. should only use when the target recipe map does not yet support metadata
+ * system, or it's to bridge legacy code and modern code.
+ */
+ public GT_RecipeBuilder specialValue(int specialValue) {
+ this.specialValue = specialValue;
+ return this;
+ }
+
+ // I don't expect anyone to actually call this...
+ public GT_RecipeBuilder disabled() {
+ this.enabled = false;
+ return this;
+ }
+
+ public GT_RecipeBuilder hidden() {
+ this.hidden = true;
+ return this;
+ }
+
+ public GT_RecipeBuilder fake() {
+ this.fakeRecipe = true;
+ return this;
+ }
+
+ public GT_RecipeBuilder noBuffer() {
+ this.mCanBeBuffered = false;
+ return this;
+ }
+
+ public GT_RecipeBuilder needsEmptyOutput() {
+ this.mNeedsEmptyOutput = true;
+ return this;
+ }
+
+ public GT_RecipeBuilder setNEIDesc(String... neiDesc) {
+ this.neiDesc = neiDesc;
+ return this;
+ }
+
+ /**
+ * Prevent the resulting recipe from optimizing recipe, which is a process that reduce recipe batch size.
+ */
+ public GT_RecipeBuilder noOptimize() {
+ this.optimize = false;
+ return this;
+ }
+
+ public <T> GT_RecipeBuilder metadata(MetadataIdentifier<T> key, T value) {
+ additionalData.put(key, value);
+ return this;
+ }
+
+ public <T> T getMetadata(MetadataIdentifier<T> key) {
+ return key.cast(additionalData.get(key));
+ }
+
+ public <T> T getMetadata(MetadataIdentifier<T> key, T defaultValue) {
+ return key.cast(additionalData.getOrDefault(key, defaultValue));
+ }
+
+ public GT_RecipeBuilder requiresCleanRoom() {
+ return metadata(GT_RecipeConstants.CLEANROOM, true);
+ }
+
+ public GT_RecipeBuilder requiresLowGravity() {
+ return metadata(GT_RecipeConstants.LOW_GRAVITY, true);
+ }
+
+ private static <T> T[] copy(T[] arr) {
+ return arr == null ? null : arr.clone();
+ }
+
+ private static int[] copy(int[] arr) {
+ return arr == null ? null : arr.clone();
+ }
+
+ /**
+ * produce a deep copy of current values. anything unset will remain unset. IMPORTANT: If metadata contains mutable
+ * value, they will not be cloned!
+ *
+ * checkout docs/RecipeBuilder.md for more info on whether to copy or not.
+ */
+ public GT_RecipeBuilder copy() {
+ return new GT_RecipeBuilder(
+ copy(inputsBasic),
+ copy(inputsOreDict),
+ copy(outputs),
+ copy(alts),
+ copy(fluidInputs),
+ copy(fluidOutputs),
+ copy(chances),
+ special,
+ duration,
+ eut,
+ specialValue,
+ enabled,
+ hidden,
+ fakeRecipe,
+ mCanBeBuffered,
+ mNeedsEmptyOutput,
+ neiDesc,
+ optimize,
+ additionalData);
+ }
+
+ /**
+ * produce a deep copy of current values. anything unset will remain unset. discard all existing metadata
+ */
+ public GT_RecipeBuilder copyNoMetadata() {
+ return new GT_RecipeBuilder(
+ copy(inputsBasic),
+ copy(inputsOreDict),
+ copy(outputs),
+ copy(alts),
+ copy(fluidInputs),
+ copy(fluidOutputs),
+ copy(chances),
+ special,
+ duration,
+ eut,
+ specialValue,
+ enabled,
+ hidden,
+ fakeRecipe,
+ mCanBeBuffered,
+ mNeedsEmptyOutput,
+ neiDesc,
+ optimize,
+ Collections.emptyMap());
+ }
+
+ public ItemStack getItemInputBasic(int index) {
+ return index < inputsBasic.length ? inputsBasic[index] : null;
+ }
+
+ public Object getItemInputOreDict(int index) {
+ return index < inputsOreDict.length ? inputsOreDict[index] : null;
+ }
+
+ public ItemStack getItemOutput(int index) {
+ return index < outputs.length ? outputs[index] : null;
+ }
+
+ public FluidStack getFluidInput(int index) {
+ return index < fluidInputs.length ? fluidInputs[index] : null;
+ }
+
+ public FluidStack getFluidOutput(int index) {
+ return index < fluidOutputs.length ? fluidOutputs[index] : null;
+ }
+
+ public ItemStack[] getItemInputsBasic() {
+ return inputsBasic;
+ }
+
+ public Object[] getItemInputsOreDict() {
+ return inputsOreDict;
+ }
+
+ public ItemStack[] getItemOutputs() {
+ return outputs;
+ }
+
+ public FluidStack[] getFluidInputs() {
+ return fluidInputs;
+ }
+
+ public FluidStack[] getFluidOutputs() {
+ return fluidOutputs;
+ }
+
+ public int getDuration() {
+ return duration;
+ }
+
+ public int getEUt() {
+ return eut;
+ }
+
+ public GT_RecipeBuilder clearInvalid() {
+ valid = true;
+ return this;
+ }
+
+ public GT_RecipeBuilder invalidate() {
+ valid = false;
+ return this;
+ }
+
+ public boolean isValid() {
+ return valid;
+ }
+
+ private static boolean isArrayValid(@Nonnull Object[] arr, int min, int max) {
+ int count = 0;
+ for (Object o : arr) {
+ if (o != null) count += 1;
+ }
+ return min <= count && max >= count;
+ }
+
+ /**
+ * Validate if input item match requirement. Return as invalidated if fails prereq. Specify -1 as min to allow
+ * unset. Both bound inclusive. Only supposed to be called by IGT_RecipeMap and not client code.
+ */
+ public GT_RecipeBuilder validateNoInput() {
+ return GT_Utility.isArrayEmptyOrNull(inputsBasic) ? noItemInputs() : invalidate();
+ }
+
+ /**
+ * Validate if input fluid match requirement. Return as invalidated if fails prereq. Specify -1 as min to allow
+ * unset. Both bound inclusive. Only supposed to be called by IGT_RecipeMap and not client code.
+ */
+ public GT_RecipeBuilder validateNoInputFluid() {
+ return GT_Utility.isArrayEmptyOrNull(fluidInputs) ? noItemInputs() : invalidate();
+ }
+
+ /**
+ * Validate if output item match requirement. Return as invalidated if fails prereq. Specify -1 as min to allow
+ * unset. Both bound inclusive. Only supposed to be called by IGT_RecipeMap and not client code.
+ */
+ public GT_RecipeBuilder validateNoOutput() {
+ return GT_Utility.isArrayEmptyOrNull(outputs) ? noItemInputs() : invalidate();
+ }
+
+ /**
+ * Validate if output fluid match requirement. Return as invalidated if fails prereq. Specify -1 as min to allow
+ * unset. Both bound inclusive. Only supposed to be called by IGT_RecipeMap and not client code.
+ */
+ public GT_RecipeBuilder validateNoOutputFluid() {
+ return GT_Utility.isArrayEmptyOrNull(fluidOutputs) ? noItemInputs() : invalidate();
+ }
+
+ /**
+ * Validate if input item match requirement. Return as invalidated if fails prereq. Specify -1 as min to allow
+ * unset. Both bound inclusive. Only supposed to be called by IGT_RecipeMap and not client code.
+ */
+ public GT_RecipeBuilder validateInputCount(int min, int max) {
+ if (inputsBasic == null) return min < 0 ? noItemInputs() : invalidate();
+ return isArrayValid(inputsBasic, min, max) ? this : invalidate();
+ }
+
+ /**
+ * Validate if input fluid match requirement. Return as invalidated if fails prereq. Specify -1 as min to allow
+ * unset. Both bound inclusive. Only supposed to be called by IGT_RecipeMap and not client code.
+ */
+ public GT_RecipeBuilder validateInputFluidCount(int min, int max) {
+ if (fluidInputs == null) return min < 0 ? noItemInputs() : invalidate();
+ return isArrayValid(fluidInputs, min, max) ? this : invalidate();
+ }
+
+ /**
+ * Validate if output item match requirement. Return as invalidated if fails prereq. Specify -1 as min to allow
+ * unset. Both bound inclusive. Only supposed to be called by IGT_RecipeMap and not client code.
+ */
+ public GT_RecipeBuilder validateOutputCount(int min, int max) {
+ if (outputs == null) return min < 0 ? noItemInputs() : invalidate();
+ return isArrayValid(outputs, min, max) ? this : invalidate();
+ }
+
+ /**
+ * Validate if output fluid match requirement. Return as invalidated if fails prereq. Specify -1 as min to allow
+ * unset. Both bound inclusive. Only supposed to be called by IGT_RecipeMap and not client code.
+ */
+ public GT_RecipeBuilder validateOutputFluidCount(int min, int max) {
+ if (fluidOutputs == null) return min < 0 ? noItemInputs() : invalidate();
+ return isArrayValid(fluidOutputs, min, max) ? this : invalidate();
+ }
+
+ public GT_RecipeBuilder validateAnyInput() {
+ if (fluidInputs != null && isArrayValid(fluidInputs, 1, Integer.MAX_VALUE)) {
+ return inputsBasic == null ? noItemInputs() : this;
+ }
+ if (inputsBasic != null && isArrayValid(inputsBasic, 1, Integer.MAX_VALUE)) {
+ return fluidInputs == null ? noFluidInputs() : this;
+ }
+ return invalidate();
+ }
+
+ public GT_RecipeBuilder validateAnyOutput() {
+ if (fluidOutputs != null && isArrayValid(fluidOutputs, 1, Integer.MAX_VALUE)) {
+ return outputs == null ? noItemOutputs() : this;
+ }
+ if (outputs != null && isArrayValid(outputs, 1, Integer.MAX_VALUE)) {
+ return fluidOutputs == null ? noFluidOutputs() : this;
+ }
+ return invalidate();
+ }
+
+ public Optional<GT_Recipe> build() {
+ if (!valid) return Optional.empty();
+ preBuildChecks();
+ optimize();
+ return Optional.of(
+ decorate(
+ new GT_Recipe(
+ inputsBasic,
+ outputs,
+ fluidInputs,
+ fluidOutputs,
+ chances,
+ special,
+ duration,
+ eut,
+ specialValue,
+ enabled,
+ hidden,
+ fakeRecipe,
+ mCanBeBuffered,
+ mNeedsEmptyOutput,
+ neiDesc)));
+ }
+
+ public GT_RecipeBuilder forceOreDictInput() {
+ if (inputsOreDict != null || inputsBasic == null) return this;
+ return itemInputs((Object[]) inputsBasic);
+ }
+
+ public Optional<GT_Recipe.GT_Recipe_WithAlt> buildWithAlt() {
+ if (inputsOreDict == null) {
+ throw new UnsupportedOperationException();
+ }
+ if (!valid) return Optional.empty();
+ preBuildChecks();
+ // no optimize.
+ return Optional.of(
+ decorate(
+ new GT_Recipe.GT_Recipe_WithAlt(
+ inputsBasic,
+ outputs,
+ fluidInputs,
+ fluidOutputs,
+ chances,
+ special,
+ duration,
+ eut,
+ specialValue,
+ enabled,
+ hidden,
+ fakeRecipe,
+ mCanBeBuffered,
+ mNeedsEmptyOutput,
+ neiDesc,
+ alts)));
+ }
+
+ private void preBuildChecks() {
+ if (inputsBasic == null) throw new IllegalStateException("no itemInputs");
+ if (outputs == null) throw new IllegalStateException("no itemOutputs");
+ if (fluidInputs == null) throw new IllegalStateException("no fluidInputs");
+ if (fluidOutputs == null) throw new IllegalStateException("no fluidOutputs");
+ if (duration == -1) throw new IllegalStateException("no duration");
+ if (eut == -1) throw new IllegalStateException("no eut");
+ }
+
+ private void optimize() {
+ if (optimize) {
+ ArrayList<ItemStack> l = new ArrayList<>();
+ l.addAll(Arrays.asList(inputsBasic));
+ l.addAll(Arrays.asList(outputs));
+ for (int i = 0; i < l.size(); i++) if (l.get(i) == null) l.remove(i--);
+
+ for (byte i = (byte) Math.min(64, duration / 16); i > 1; i--) if (duration / i >= 16) {
+ boolean temp = true;
+ for (ItemStack stack : l) if (stack.stackSize % i != 0) {
+ temp = false;
+ break;
+ }
+ if (temp) for (FluidStack fluidInput : fluidInputs) if (fluidInput.amount % i != 0) {
+ temp = false;
+ break;
+ }
+ if (temp) for (FluidStack fluidOutput : fluidOutputs) if (fluidOutput.amount % i != 0) {
+ temp = false;
+ break;
+ }
+ if (temp) {
+ for (ItemStack itemStack : l) itemStack.stackSize /= i;
+ for (FluidStack fluidInput : fluidInputs) fluidInput.amount /= i;
+ for (FluidStack fluidOutput : fluidOutputs) fluidOutput.amount /= i;
+ duration /= i;
+ }
+ }
+ optimize = false;
+ }
+ }
+
+ private <T extends GT_Recipe> T decorate(T r) {
+ r.mHidden = hidden;
+ r.mCanBeBuffered = mCanBeBuffered;
+ r.mNeedsEmptyOutput = mNeedsEmptyOutput;
+ r.mFakeRecipe = fakeRecipe;
+ r.mEnabled = enabled;
+ if (neiDesc != null) r.setNeiDesc(neiDesc);
+ return r;
+ }
+
+ public Collection<GT_Recipe> addTo(IGT_RecipeMap recipeMap) {
+ return recipeMap.doAdd(this);
+ }
+
+ public GT_RecipeBuilder reset() {
+ additionalData.clear();
+ alts = null;
+ chances = null;
+ duration = -1;
+ enabled = true;
+ eut = -1;
+ fakeRecipe = false;
+ fluidInputs = null;
+ fluidOutputs = null;
+ hidden = false;
+ inputsBasic = null;
+ inputsOreDict = null;
+ mCanBeBuffered = true;
+ mNeedsEmptyOutput = false;
+ neiDesc = null;
+ optimize = true;
+ outputs = null;
+ special = null;
+ specialValue = 0;
+ valid = true;
+ return this;
+ }
+
+ public final static class MetadataIdentifier<T> {
+
+ private static final Map<MetadataIdentifier<?>, MetadataIdentifier<?>> allIdentifiers = Collections
+ .synchronizedMap(new HashMap<>());
+ private final Class<T> clazz;
+ private final String identifier;
+
+ private MetadataIdentifier(Class<T> clazz, String identifier) {
+ this.clazz = clazz;
+ this.identifier = identifier;
+ }
+
+ public static <T> MetadataIdentifier<T> create(Class<T> clazz, String identifier) {
+ MetadataIdentifier<T> key = new MetadataIdentifier<>(clazz, identifier);
+ return (MetadataIdentifier<T>) allIdentifiers.computeIfAbsent(key, Function.identity());
+ }
+
+ public T cast(Object o) {
+ return clazz.cast(o);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ MetadataIdentifier<?> that = (MetadataIdentifier<?>) o;
+
+ if (!clazz.equals(that.clazz)) return false;
+ return identifier.equals(that.identifier);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = clazz.hashCode();
+ result = 31 * result + identifier.hashCode();
+ return result;
+ }
+ }
+}
diff --git a/src/main/java/gregtech/api/util/GT_RecipeConstants.java b/src/main/java/gregtech/api/util/GT_RecipeConstants.java
new file mode 100644
index 0000000000..5ccb84da9a
--- /dev/null
+++ b/src/main/java/gregtech/api/util/GT_RecipeConstants.java
@@ -0,0 +1,258 @@
+package gregtech.api.util;
+
+import java.util.*;
+
+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.GregTech_API;
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.Materials;
+import gregtech.api.interfaces.IGT_RecipeMap;
+import gregtech.api.util.GT_Recipe.GT_Recipe_Map;
+
+// this class is intended to be import-static-ed on every recipe script
+// so take care to not put unrelated stuff here!
+public class GT_RecipeConstants {
+
+ /**
+ * 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 GT_RecipeBuilder.MetadataIdentifier<Boolean> LOW_GRAVITY = GT_RecipeBuilder.MetadataIdentifier
+ .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 GT_RecipeBuilder.MetadataIdentifier<Boolean> CLEANROOM = GT_RecipeBuilder.MetadataIdentifier
+ .create(Boolean.class, "cleanroom");
+ /**
+ * Common additive to use in recipe, e.g. for PBF, this is coal amount.
+ */
+ public static final GT_RecipeBuilder.MetadataIdentifier<Integer> ADDITIVE_AMOUNT = GT_RecipeBuilder.MetadataIdentifier
+ .create(Integer.class, "additives");
+ /**
+ * Used for fusion reactor. Denotes ignition threshold.
+ */
+ public static final GT_RecipeBuilder.MetadataIdentifier<Integer> FUSION_THRESHOLD = GT_RecipeBuilder.MetadataIdentifier
+ .create(Integer.class, "fusion_threshold");
+ /**
+ * Research time in a scanner used in ticks.
+ */
+ public static final GT_RecipeBuilder.MetadataIdentifier<Integer> RESEARCH_TIME = GT_RecipeBuilder.MetadataIdentifier
+ .create(Integer.class, "research_time");
+ /**
+ * Fuel type. TODO should we use enum directly?
+ */
+ public static final GT_RecipeBuilder.MetadataIdentifier<Integer> FUEL_TYPE = GT_RecipeBuilder.MetadataIdentifier
+ .create(Integer.class, "fuel_type");
+ /**
+ * Fuel value.
+ */
+ public static final GT_RecipeBuilder.MetadataIdentifier<Integer> FUEL_VALUE = GT_RecipeBuilder.MetadataIdentifier
+ .create(Integer.class, "fuel_value");
+ /**
+ * Fuel value.
+ */
+ public static final GT_RecipeBuilder.MetadataIdentifier<Integer> COIL_HEAT = GT_RecipeBuilder.MetadataIdentifier
+ .create(Integer.class, "coil_heat");
+ /**
+ * Research item used by assline recipes.
+ */
+ public static final GT_RecipeBuilder.MetadataIdentifier<ItemStack> RESEARCH_ITEM = GT_RecipeBuilder.MetadataIdentifier
+ .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 GT_RecipeBuilder.MetadataIdentifier<Object> OREDICT_INPUT = GT_RecipeBuilder.MetadataIdentifier
+ .create(Object.class, "oredict_input");
+
+ public static final IGT_RecipeMap Fusion = IGT_RecipeMap.newRecipeMap(builder -> {
+ if (GT_Utility.isArrayEmptyOrNull(builder.getFluidInputs())
+ || GT_Utility.isArrayEmptyOrNull(builder.getFluidOutputs()))
+ return Collections.emptyList();
+ if (builder.getFluidInputs().length > 2 || builder.getFluidOutputs().length > 2)
+ return GT_Recipe_Map.sComplexFusionRecipes.doAdd(builder);
+ return GT_Recipe_Map.sFusionRecipes.doAdd(builder);
+ });
+
+ public static final IGT_RecipeMap UniversalArcFurnace = IGT_RecipeMap.newRecipeMap(builder -> {
+ if (!GT_Utility.isArrayOfLength(builder.getItemInputsBasic(), 1)
+ || GT_Utility.isArrayEmptyOrNull(builder.getItemOutputs()))
+ return Collections.emptyList();
+ int aDuration = builder.getDuration();
+ if ((aDuration = GregTech_API.sRecipeFile.get("arcfurnace", builder.getItemInputBasic(0), aDuration)) <= 0) {
+ return Collections.emptyList();
+ }
+ builder.duration(aDuration);
+ Collection<GT_Recipe> ret = new ArrayList<>();
+ for (Materials mat : new Materials[] { Materials.Argon, Materials.Nitrogen }) {
+ int tPlasmaAmount = (int) Math.max(1L, aDuration / (mat.getMass() * 16L));
+ GT_RecipeBuilder b2 = builder.copy();
+ b2.fluidInputs(mat.getPlasma(tPlasmaAmount)).fluidOutputs(mat.getGas(tPlasmaAmount));
+ ret.addAll(GT_Recipe_Map.sPlasmaArcFurnaceRecipes.doAdd(b2));
+ }
+ ret.addAll(
+ GT_Recipe_Map.sArcFurnaceRecipes.doAdd(builder.copy().fluidInputs(Materials.Oxygen.getGas(aDuration))));
+ return ret;
+ });
+
+ public static final IGT_RecipeMap UniversalChemical = IGT_RecipeMap.newRecipeMap(builder -> {
+ for (ItemStack input : builder.getItemInputsBasic()) {
+ if (GT_Utility.isAnyIntegratedCircuit(input) && input.getItemDamage() >= 10) return GT_Utility.concat(
+ builder.copy().addTo(GT_Recipe_Map.sChemicalRecipes),
+ // LCR does not need cleanroom, for now.
+ builder.metadata(CLEANROOM, false).addTo(GT_Recipe_Map.sMultiblockChemicalRecipes));
+ }
+ return builder.addTo(GT_Recipe_Map.sChemicalRecipes);
+ });
+
+ public static final IGT_RecipeMap AssemblyLine = IGT_RecipeMap.newRecipeMap(builder -> {
+ Optional<GT_Recipe.GT_Recipe_WithAlt> rr = builder.forceOreDictInput().validateInputCount(4, 16)
+ .validateOutputCount(1, 1).validateOutputFluidCount(-1, 0).validateInputFluidCount(0, 4).buildWithAlt();
+ if (!rr.isPresent()) return Collections.emptyList();
+ GT_Recipe.GT_Recipe_WithAlt r = rr.get();
+ ItemStack[][] mOreDictAlt = r.mOreDictAlt;
+ Object[] inputs = builder.getItemInputsOreDict();
+ ItemStack aResearchItem = builder.getMetadata(RESEARCH_ITEM);
+ 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 instanceof ItemStack)
+ tPersistentHash = tPersistentHash * 31 + GT_Utility.persistentHash((ItemStack) input, true, false);
+ else if (input instanceof ItemStack[]) {
+ for (ItemStack alt : ((ItemStack[]) input)) {
+ tPersistentHash = tPersistentHash * 31 + GT_Utility.persistentHash(alt, true, false);
+ }
+ tPersistentHash *= 31;
+ } else if (input instanceof Object[]) {
+ Object[] objs = (Object[]) input;
+ Arrays.sort(
+ alts,
+ Comparator
+ .<ItemStack, String>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();
+ }
+ GT_Log.err.println(
+ "addAssemblingLineRecipe " + aResearchItem.getDisplayName()
+ + " --> "
+ + aOutput.getUnlocalizedName()
+ + " there is some null item in that recipe");
+ }
+ tPersistentHash = tPersistentHash * 31 + GT_Utility.persistentHash(aResearchItem, true, false);
+ tPersistentHash = tPersistentHash * 31 + GT_Utility.persistentHash(aOutput, true, false);
+ for (FluidStack fluidInput : r.mFluidInputs) {
+ if (fluidInput == null) continue;
+ tPersistentHash = tPersistentHash * 31 + GT_Utility.persistentHash(fluidInput, true, false);
+ }
+ int aResearchTime = builder.getMetadata(RESEARCH_TIME);
+ tPersistentHash = tPersistentHash * 31 + aResearchTime;
+ tPersistentHash = tPersistentHash * 31 + r.mDuration;
+ tPersistentHash = tPersistentHash * 31 + r.mEUt;
+ Collection<GT_Recipe> ret = new ArrayList<>(3);
+ ret.add(
+ GT_Recipe.GT_Recipe_Map.sScannerFakeRecipes.addFakeRecipe(
+ false,
+ new ItemStack[] { aResearchItem },
+ new ItemStack[] { aOutput },
+ new ItemStack[] { ItemList.Tool_DataStick.getWithName(1L, "Writes Research result") },
+ null,
+ null,
+ aResearchTime,
+ 30,
+ -201)); // means it's scanned
+
+ ret.add(
+ GT_Recipe.GT_Recipe_Map.sAssemblylineVisualRecipes.addFakeRecipe(
+ false,
+ r.mInputs,
+ new ItemStack[] { aOutput },
+ new ItemStack[] { ItemList.Tool_DataStick.getWithName(1L, "Reads Research result") },
+ r.mFluidInputs,
+ null,
+ r.mDuration,
+ r.mEUt,
+ 0,
+ r.mOreDictAlt,
+ false));
+
+ GT_Recipe.GT_Recipe_AssemblyLine tRecipe = new GT_Recipe.GT_Recipe_AssemblyLine(
+ aResearchItem,
+ aResearchTime,
+ r.mInputs,
+ r.mFluidInputs,
+ aOutput,
+ r.mDuration,
+ r.mEUt,
+ r.mOreDictAlt);
+ tRecipe.setPersistentHash(tPersistentHash);
+ GT_Recipe.GT_Recipe_AssemblyLine.sAssemblylineRecipes.add(tRecipe);
+ GT_AssemblyLineUtils.addRecipeToCache(tRecipe);
+ return ret;
+ });
+
+ public static IGT_RecipeMap AssemblerOD = IGT_RecipeMap.newRecipeMap(builder -> {
+ Collection<GT_Recipe> ret = new ArrayList<>();
+ for (ItemStack input : GT_OreDictUnificator.getOresImmutable(builder.getMetadata(OREDICT_INPUT))) {
+ ret.addAll(
+ builder.copy().itemInputs(GT_RecipeMapUtil.appendArray(builder.getItemInputsBasic(), input))
+ .addTo(GT_Recipe_Map.sAssemblerRecipes));
+ }
+ return ret;
+ });
+
+ public static IGT_RecipeMap Fuel = IGT_RecipeMap.newRecipeMap(builder -> {
+ builder.validateInputCount(1, 1).validateNoInputFluid().validateOutputCount(-1, 0).validateNoOutputFluid();
+ if (!builder.isValid()) return Collections.emptyList();
+ int fuelType = builder.getMetadata(FUEL_TYPE);
+ builder.metadata(
+ FUEL_VALUE,
+ GregTech_API.sRecipeFile
+ .get("fuel_" + fuelType, builder.getItemInputBasic(0), builder.getMetadata(FUEL_VALUE)));
+ return FuelType.get(fuelType).getTarget().doAdd(builder);
+ });
+
+ public enum FuelType {
+
+ // ORDER MATTERS. DO NOT INSERT ELEMENT BETWEEN EXISTING ONES
+ DieselFuel(GT_Recipe_Map.sDieselFuels),
+ GasTurbine(GT_Recipe_Map.sTurbineFuels),
+ // appears unused
+ HotFuel(GT_Recipe_Map.sHotFuels),
+ SemiFluid(GT_Recipe_Map.sDenseLiquidFuels),
+ PlasmaTurbine(GT_Recipe_Map.sPlasmaFuels),
+ Magic(GT_Recipe_Map.sMagicFuels),;
+
+ private static final FuelType[] VALUES = values();
+ private final IGT_RecipeMap target;
+
+ FuelType(IGT_RecipeMap target) {
+ this.target = target;
+ }
+
+ public static FuelType get(int fuelType) {
+ if (fuelType < 0 || fuelType >= VALUES.length) return SemiFluid;
+ return VALUES[fuelType];
+ }
+
+ public IGT_RecipeMap getTarget() {
+ return target;
+ }
+ }
+
+ static {
+ GT_RecipeMapUtil.SPECIAL_VALUE_ALIASES.add(COIL_HEAT);
+ GT_RecipeMapUtil.SPECIAL_VALUE_ALIASES.add(FUSION_THRESHOLD);
+ GT_RecipeMapUtil.SPECIAL_VALUE_ALIASES.add(FUEL_VALUE);
+ }
+}
diff --git a/src/main/java/gregtech/api/util/GT_RecipeMapUtil.java b/src/main/java/gregtech/api/util/GT_RecipeMapUtil.java
new file mode 100644
index 0000000000..7bb02a5681
--- /dev/null
+++ b/src/main/java/gregtech/api/util/GT_RecipeMapUtil.java
@@ -0,0 +1,196 @@
+package gregtech.api.util;
+
+import static gregtech.api.util.GT_Config.getStackConfigName;
+import static gregtech.api.util.GT_Utility.isArrayEmptyOrNull;
+
+import java.util.*;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.fluids.FluidStack;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+
+import cpw.mods.fml.common.Loader;
+import gregtech.api.interfaces.IGT_RecipeMap;
+
+/**
+ * Define helpers useful in the creation of recipe maps.
+ */
+// Do not place arbitrary stuff here! These are all statically imported in GT_Recipe.java file.
+public class GT_RecipeMapUtil {
+
+ public static final Function<GT_Recipe, GT_Recipe> ALL_FAKE_RECIPE = r -> {
+ r.mFakeRecipe = true;
+ return r;
+ };
+
+ public static final Function<GT_Recipe, String> FIRST_FLUID_INPUT = r -> isArrayEmptyOrNull(r.mFluidInputs) ? null
+ : r.mFluidInputs[0].getFluid().getName();
+ public static final Function<GT_Recipe, String> FIRST_FLUID_OUTPUT = r -> isArrayEmptyOrNull(r.mFluidInputs) ? null
+ : r.mFluidOutputs[0].getFluid().getName();
+ public static final Function<GT_Recipe, String> FIRST_FLUIDSTACK_INPUT = r -> isArrayEmptyOrNull(r.mFluidInputs)
+ ? null
+ : r.mFluidInputs[0].getUnlocalizedName();
+ public static final Function<GT_Recipe, String> FIRST_FLUIDSTACK_OUTPUT = r -> isArrayEmptyOrNull(r.mFluidOutputs)
+ ? null
+ : r.mFluidOutputs[0].getUnlocalizedName();
+ public static final Function<GT_Recipe, String> FIRST_ITEM_INPUT = r -> isArrayEmptyOrNull(r.mInputs) ? null
+ : getStackConfigName(r.mInputs[0]);
+ public static final Function<GT_Recipe, String> FIRST_ITEM_OUTPUT = r -> isArrayEmptyOrNull(r.mOutputs) ? null
+ : getStackConfigName(r.mOutputs[0]);
+ public static final Function<GT_Recipe, String> FIRST_ITEM_OR_FLUID_INPUT = r -> isArrayEmptyOrNull(r.mInputs)
+ ? isArrayEmptyOrNull(r.mFluidInputs) ? null : r.mFluidInputs[0].getFluid().getName()
+ : getStackConfigName(r.mInputs[0]);
+ public static final Function<GT_Recipe, String> FIRST_ITEM_OR_FLUID_OUTPUT = r -> isArrayEmptyOrNull(r.mOutputs)
+ ? isArrayEmptyOrNull(r.mFluidOutputs) ? null : r.mFluidOutputs[0].getFluid().getName()
+ : getStackConfigName(r.mOutputs[0]);
+ private static final Map<String, IGT_RecipeMap> addonRecipeMaps = new HashMap<>();
+ private static final Multimap<String, Consumer<IGT_RecipeMap>> delayedActions = ArrayListMultimap.create();
+
+ public static final Set<GT_RecipeBuilder.MetadataIdentifier<Integer>> SPECIAL_VALUE_ALIASES = new HashSet<>();
+
+ public static <T> T[] appendArray(T[] arr, T val) {
+ T[] newArr = Arrays.copyOf(arr, arr.length + 1);
+ newArr[arr.length] = val;
+ return newArr;
+ }
+
+ public static GT_RecipeTemplate asTemplate(GT_Recipe r) {
+ return asTemplate(r, false);
+ }
+
+ public static GT_RecipeTemplate asTemplate(GT_Recipe r, boolean includeTemplate) {
+ return new GT_RecipeTemplate(r, includeTemplate);
+ }
+
+ public static List<GT_Recipe> buildRecipeForMultiblock(GT_RecipeBuilder b) {
+ List<ItemStack> itemInputs = new ArrayList<>(Arrays.asList(b.getItemInputsBasic()));
+ List<ItemStack> itemOutputs = new ArrayList<>(Arrays.asList(b.getItemOutputs()));
+ List<FluidStack> fluidInputs = new ArrayList<>(Arrays.asList(b.getFluidInputs()));
+ List<FluidStack> fluidOutputs = new ArrayList<>(Arrays.asList(b.getFluidOutputs()));
+ cellToFluid(itemInputs, fluidInputs, true);
+ cellToFluid(itemInputs, fluidInputs, false);
+ return buildOrEmpty(
+ b.itemInputs(itemInputs.toArray(new ItemStack[0])).itemOutputs(itemOutputs.toArray(new ItemStack[0]))
+ .fluidInputs(fluidInputs.toArray(new FluidStack[0]))
+ .fluidOutputs(fluidOutputs.toArray(new FluidStack[0])));
+ }
+
+ public static List<GT_Recipe> buildRecipeForMultiblockNoCircuit(GT_RecipeBuilder b) {
+ List<ItemStack> itemInputs = new ArrayList<>(Arrays.asList(b.getItemInputsBasic()));
+ List<ItemStack> itemOutputs = new ArrayList<>(Arrays.asList(b.getItemOutputs()));
+ List<FluidStack> fluidInputs = new ArrayList<>(Arrays.asList(b.getFluidInputs()));
+ List<FluidStack> fluidOutputs = new ArrayList<>(Arrays.asList(b.getFluidOutputs()));
+ cellToFluid(itemInputs, fluidInputs, false);
+ cellToFluid(itemInputs, fluidInputs, false);
+ return buildOrEmpty(
+ b.itemInputs(itemInputs.toArray(new ItemStack[0])).itemOutputs(itemOutputs.toArray(new ItemStack[0]))
+ .fluidInputs(fluidInputs.toArray(new FluidStack[0]))
+ .fluidOutputs(fluidOutputs.toArray(new FluidStack[0])));
+ }
+
+ private static void cellToFluid(List<ItemStack> items, List<FluidStack> fluids, boolean removeIntegratedCircuit) {
+ for (ListIterator<ItemStack> iterator = items.listIterator(items.size()); iterator.hasPrevious();) {
+ ItemStack item = iterator.previous();
+ if (GT_Utility.getFluidForFilledItem(item, true) != null || GT_Utility.isCellEmpty(item)
+ || (removeIntegratedCircuit && GT_Utility.isAnyIntegratedCircuit(item))) {
+ fluids.add(GT_Utility.convertCellToFluid(item));
+ iterator.remove();
+ }
+ }
+ }
+
+ public static List<GT_Recipe> buildOrEmpty(GT_RecipeBuilder builder) {
+ return builder.build().map(Collections::singletonList).orElse(Collections.emptyList());
+ }
+
+ /**
+ * Register a recipe map as part of your mod's public API under your modid and your given identifier.
+ *
+ * @param identifier
+ * @param recipeMap
+ * @param dependencies fully qualified identifier of dependent recipe maps. scheduler will only add recipes to one
+ * of the dependent recipe maps and this recipe map concurrently, guaranteeing thread safety.
+ * Currently unused, but you are advised to fill them, so that when The Day (tm) comes we don't
+ * end up with a bunch of weird concurrency bugs.
+ */
+ public static void registerRecipeMap(String identifier, IGT_RecipeMap recipeMap,
+ RecipeMapDependency... dependencies) {
+ String modId = Loader.instance().activeModContainer().getModId();
+ if ("gregtech".equals(modId)) throw new IllegalStateException(
+ "do not register recipe map under the name of gregtech! do it in your own preinit!");
+ String id = modId + "@" + identifier;
+ addonRecipeMaps.put(id, recipeMap);
+ for (Consumer<IGT_RecipeMap> action : delayedActions.get(id)) {
+ action.accept(recipeMap);
+ }
+ }
+
+ /**
+ * Use this to register recipes for a recipe map in addon not present at compile time.
+ * <p>
+ * Do not use this for recipes maps already in {@link GT_RecipeConstants}. None of them will be available via this
+ * interface!
+ *
+ * @param identifier recipe map id
+ * @param registerAction DO NOT ADD RECIPES TO MAPS OTHER THAN THE ONE PASSED TO YOU. DO NOT DO ANYTHING OTHER THAN
+ * ADDING RECIPES TO THIS R
+ */
+ public static void registerRecipesFor(String modid, String identifier, Consumer<IGT_RecipeMap> registerAction) {
+ String id = modid + "@" + identifier;
+ IGT_RecipeMap map = addonRecipeMaps.get(id);
+ if (map == null) delayedActions.put(id, registerAction);
+ else registerAction.accept(map);
+ }
+
+ public static final class GT_RecipeTemplate {
+
+ private final GT_Recipe template;
+ private final List<GT_Recipe> derivatives = new ArrayList<>();
+
+ private GT_RecipeTemplate(GT_Recipe template, boolean includeTemplate) {
+ this.template = template;
+ if (includeTemplate) derivatives.add(template);
+ }
+
+ public GT_Recipe derive() {
+ GT_Recipe derived = template.copyShallow();
+ derivatives.add(derived);
+ return derived;
+ }
+
+ public List<GT_Recipe> getAll() {
+ // fix shallow references
+ Set<Object> references = Collections.newSetFromMap(new IdentityHashMap<>());
+ for (GT_Recipe r : derivatives) {
+ if (!references.add(r.mInputs)) r.mInputs = r.mInputs.clone();
+ if (!references.add(r.mOutputs)) r.mOutputs = r.mOutputs.clone();
+ if (!references.add(r.mFluidInputs)) r.mFluidInputs = r.mFluidInputs.clone();
+ if (!references.add(r.mFluidOutputs)) r.mFluidOutputs = r.mFluidOutputs.clone();
+ }
+ return derivatives;
+ }
+ }
+
+ public static final class RecipeMapDependency {
+
+ private final IGT_RecipeMap obj;
+ private final String id;
+
+ public RecipeMapDependency(IGT_RecipeMap obj, String id) {
+ this.obj = obj;
+ this.id = id;
+ }
+
+ public static RecipeMapDependency create(String id) {
+ return new RecipeMapDependency(null, id);
+ }
+
+ public static RecipeMapDependency create(IGT_RecipeMap obj) {
+ return new RecipeMapDependency(obj, null);
+ }
+ }
+}
diff --git a/src/main/java/gregtech/api/util/GT_Utility.java b/src/main/java/gregtech/api/util/GT_Utility.java
index 4dd99e3ff7..32ccca715a 100644
--- a/src/main/java/gregtech/api/util/GT_Utility.java
+++ b/src/main/java/gregtech/api/util/GT_Utility.java
@@ -25,6 +25,7 @@ import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Supplier;
import java.util.stream.Collector;
+import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
@@ -2795,6 +2796,13 @@ public class GT_Utility {
return null;
}
+ public static FluidStack copyAmount(int aAmount, FluidStack aStack) {
+ if (aStack == null) return null;
+ FluidStack rStack = aStack.copy();
+ rStack.amount = aAmount;
+ return rStack;
+ }
+
public static ItemStack copyAmount(long aAmount, Object... aStacks) {
ItemStack rStack = copy(aStacks);
if (isStackInvalid(rStack)) return null;
@@ -2805,6 +2813,17 @@ public class GT_Utility {
return rStack;
}
+ public static ItemStack multiplyStack(long aMultiplier, Object... aStacks) {
+ ItemStack rStack = copy(aStacks);
+ if (isStackInvalid(rStack)) return null;
+ long tAmount = rStack.stackSize * aMultiplier;
+ if (tAmount > 64) tAmount = 64;
+ else if (tAmount == -1) tAmount = 111;
+ else if (tAmount < 0) tAmount = 0;
+ rStack.stackSize = (byte) tAmount;
+ return rStack;
+ }
+
public static ItemStack copyAmountUnsafe(long aAmount, Object... aStacks) {
ItemStack rStack = copy(aStacks);
if (isStackInvalid(rStack)) return null;
@@ -4337,6 +4356,10 @@ public class GT_Utility {
return GT_Utility.areStacksEqual(itemStack, tStack);
}
+ /**
+ * Convert a cell to fluid. If given itemstack does not contain any fluid, return null. Will correctly multiple
+ * output fluid amount if input stack size is greater than 1.
+ */
public static FluidStack convertCellToFluid(ItemStack itemStack) {
if (itemStack == null) return null;
if (getFluidForFilledItem(itemStack, true) != null) {
@@ -4347,12 +4370,22 @@ public class GT_Utility {
return null;
}
+ /**
+ * @deprecated typo in method name. use {@link #isAnyIntegratedCircuit(ItemStack)} instead.
+ */
+ @Deprecated
public static boolean checkIfSameIntegratedCircuit(ItemStack itemStack) {
if (itemStack == null) return false;
for (int i = 0; i < 25; i++) if (itemStack.isItemEqual(GT_Utility.getIntegratedCircuit(i))) return true;
return false;
}
+ public static boolean isAnyIntegratedCircuit(ItemStack itemStack) {
+ if (itemStack == null) return false;
+ return itemStack.getItem() == ItemList.Circuit_Integrated.getItem() && 0 <= itemStack.getItemDamage()
+ && itemStack.getItemDamage() < 25;
+ }
+
public static byte convertRatioToRedstone(long used, long max, int threshold, boolean inverted) {
byte signal;
if (used <= 0) { // Empty
@@ -4412,6 +4445,56 @@ public class GT_Utility {
ImmutableMap.Builder::build);
}
+ public static boolean isArrayEmptyOrNull(Object[] arr) {
+ return arr == null || arr.length == 0;
+ }
+
+ public static boolean isArrayOfLength(Object[] arr, int expectedLength) {
+ return arr != null && arr.length == expectedLength;
+ }
+
+ @SafeVarargs
+ public static <E> Collection<E> concat(Collection<E>... colls) {
+ return concat(Arrays.asList(colls));
+ }
+
+ public static <E> Collection<E> concat(Collection<Collection<E>> colls) {
+ return new ConcatCollection<>(colls);
+ }
+
+ private static class ConcatCollection<E> extends AbstractCollection<E> {
+
+ private final Collection<Collection<E>> colls;
+ private final int size;
+
+ public ConcatCollection(Collection<Collection<E>> lists) {
+ Collection<Collection<E>> colls1 = null;
+ for (Collection<E> list : lists) {
+ if (list == null || list.isEmpty()) {
+ colls1 = lists.stream().filter(c -> c != null && !c.isEmpty()).collect(Collectors.toList());
+ break;
+ }
+ }
+ if (colls1 == null) colls1 = lists;
+ colls = colls1;
+ int sum = 0;
+ for (Collection<E> list : colls) {
+ sum += list.size();
+ }
+ size = sum;
+ }
+
+ @Override
+ public Iterator<E> iterator() {
+ return colls.stream().flatMap(Collection::stream).iterator();
+ }
+
+ @Override
+ public int size() {
+ return size;
+ }
+ }
+
@AutoValue
public abstract static class ItemId {