diff options
Diffstat (limited to 'src/main/java/gregtech/api')
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 { |