aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/gregtech/api/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/gregtech/api/util')
-rw-r--r--src/main/java/gregtech/api/util/GT_OverclockCalculator.java25
-rw-r--r--src/main/java/gregtech/api/util/GT_ParallelHelper.java77
-rw-r--r--src/main/java/gregtech/api/util/GT_Recipe.java77
-rw-r--r--src/main/java/gregtech/api/util/GT_StructureUtilityMuTE.java271
-rw-r--r--src/main/java/gregtech/api/util/VoidProtectionHelper.java124
-rw-r--r--src/main/java/gregtech/api/util/item/ItemHolder.java79
-rw-r--r--src/main/java/gregtech/api/util/recipe/RecipeInputRequirements.java77
7 files changed, 723 insertions, 7 deletions
diff --git a/src/main/java/gregtech/api/util/GT_OverclockCalculator.java b/src/main/java/gregtech/api/util/GT_OverclockCalculator.java
index 8e896fd8de..1b8748237d 100644
--- a/src/main/java/gregtech/api/util/GT_OverclockCalculator.java
+++ b/src/main/java/gregtech/api/util/GT_OverclockCalculator.java
@@ -152,6 +152,7 @@ public class GT_OverclockCalculator {
/**
* @param recipeEUt Sets the Recipe's starting voltage
*/
+ @Nonnull
public GT_OverclockCalculator setRecipeEUt(long recipeEUt) {
this.recipeVoltage = recipeEUt;
return this;
@@ -160,6 +161,7 @@ public class GT_OverclockCalculator {
/**
* @param machineVoltage Sets the EUt that the machine can use. This is the voltage of the machine
*/
+ @Nonnull
public GT_OverclockCalculator setEUt(long machineVoltage) {
this.machineVoltage = machineVoltage;
return this;
@@ -168,6 +170,7 @@ public class GT_OverclockCalculator {
/**
* @param duration Sets the duration of the recipe
*/
+ @Nonnull
public GT_OverclockCalculator setDuration(int duration) {
this.duration = duration;
return this;
@@ -176,6 +179,7 @@ public class GT_OverclockCalculator {
/**
* @param machineAmperage Sets the Amperage that the machine can support
*/
+ @Nonnull
public GT_OverclockCalculator setAmperage(long machineAmperage) {
this.machineAmperage = machineAmperage;
return this;
@@ -184,6 +188,7 @@ public class GT_OverclockCalculator {
/**
* @param recipeAmperage Sets the Amperage of the recipe
*/
+ @Nonnull
public GT_OverclockCalculator setRecipeAmperage(long recipeAmperage) {
this.recipeAmperage = recipeAmperage;
return this;
@@ -192,6 +197,7 @@ public class GT_OverclockCalculator {
/**
* Enables Perfect OC in calculation
*/
+ @Nonnull
public GT_OverclockCalculator enablePerfectOC() {
this.durationDecreasePerOC = 2;
return this;
@@ -200,6 +206,7 @@ public class GT_OverclockCalculator {
/**
* Set if we should be calculating overclocking using EBF's perfectOC
*/
+ @Nonnull
public GT_OverclockCalculator setHeatOC(boolean heatOC) {
this.heatOC = heatOC;
return this;
@@ -208,6 +215,7 @@ public class GT_OverclockCalculator {
/**
* Sets if we should add a heat discount at the end of calculating an overclock, just like the EBF
*/
+ @Nonnull
public GT_OverclockCalculator setHeatDiscount(boolean heatDiscount) {
this.heatDiscount = heatDiscount;
return this;
@@ -216,6 +224,7 @@ public class GT_OverclockCalculator {
/**
* Sets the starting heat of the recipe
*/
+ @Nonnull
public GT_OverclockCalculator setRecipeHeat(int recipeHeat) {
this.recipeHeat = recipeHeat;
return this;
@@ -224,6 +233,7 @@ public class GT_OverclockCalculator {
/**
* Sets the heat of the coils on the machine
*/
+ @Nonnull
public GT_OverclockCalculator setMachineHeat(int machineHeat) {
this.machineHeat = machineHeat;
return this;
@@ -232,6 +242,7 @@ public class GT_OverclockCalculator {
/**
* Sets an EUtDiscount. 0.9 is 10% less energy. 1.1 is 10% more energy
*/
+ @Nonnull
public GT_OverclockCalculator setEUtDiscount(float aEUtDiscount) {
this.eutDiscount = aEUtDiscount;
return this;
@@ -240,6 +251,7 @@ public class GT_OverclockCalculator {
/**
* Sets a Speed Boost for the multiblock. 0.9 is 10% faster. 1.1 is 10% slower
*/
+ @Nonnull
public GT_OverclockCalculator setSpeedBoost(float aSpeedBoost) {
this.speedBoost = aSpeedBoost;
return this;
@@ -248,6 +260,7 @@ public class GT_OverclockCalculator {
/**
* Sets the parallel that the multiblock uses
*/
+ @Nonnull
public GT_OverclockCalculator setParallel(int aParallel) {
this.parallel = aParallel;
return this;
@@ -257,6 +270,7 @@ public class GT_OverclockCalculator {
* Sets the heat discount during OC calculation if HeatOC is used. Default: 0.95 = 5% discount Used like a EU/t
* Discount
*/
+ @Nonnull
public GT_OverclockCalculator setHeatDiscountMultiplier(float heatDiscountExponent) {
this.heatDiscountExponent = heatDiscountExponent;
return this;
@@ -266,6 +280,7 @@ public class GT_OverclockCalculator {
* Sets the Overclock that should be calculated when one. This uses BitShifting! Default is 2, which is a 4x
* decrease
*/
+ @Nonnull
public GT_OverclockCalculator setHeatPerfectOC(int heatPerfectOC) {
this.durationDecreasePerHeatOC = heatPerfectOC;
return this;
@@ -274,6 +289,7 @@ public class GT_OverclockCalculator {
/**
* Sets the amount that the EUt increases per overclock. This uses BitShifting! Default is 2, which is a 4x increase
*/
+ @Nonnull
public GT_OverclockCalculator setEUtIncreasePerOC(int aEUtIncreasePerOC) {
this.eutIncreasePerOC = aEUtIncreasePerOC;
return this;
@@ -283,6 +299,7 @@ public class GT_OverclockCalculator {
* Sets the amount that the duration decreases per overclock. This uses BitShifting! Default is 1, which halves the
* duration
*/
+ @Nonnull
public GT_OverclockCalculator setDurationDecreasePerOC(int durationDecreasePerOC) {
this.durationDecreasePerOC = durationDecreasePerOC;
return this;
@@ -292,6 +309,7 @@ public class GT_OverclockCalculator {
* Set One Tick Discount on EUt based on Duration Decrease Per Overclock. This functions the same as single
* blocks.
*/
+ @Nonnull
public GT_OverclockCalculator setOneTickDiscount(boolean oneTickDiscount) {
this.oneTickDiscount = oneTickDiscount;
return this;
@@ -301,22 +319,26 @@ public class GT_OverclockCalculator {
* Limit the amount of overclocks that can be performed, regardless of how much power is available. Mainly used for
* fusion reactors.
*/
+ @Nonnull
public GT_OverclockCalculator limitOverclockCount(int maxOverclocks) {
this.limitOverclocks = true;
this.maxOverclocks = maxOverclocks;
return this;
}
+ @Nonnull
public GT_OverclockCalculator setLaserOC(boolean laserOC) {
this.laserOC = laserOC;
return this;
}
+ @Nonnull
public GT_OverclockCalculator setAmperageOC(boolean amperageOC) {
this.amperageOC = amperageOC;
return this;
}
+ @Nonnull
public GT_OverclockCalculator setLaserOCPenalty(double laserOCPenalty) {
this.laserOCPenalty = laserOCPenalty;
return this;
@@ -325,6 +347,7 @@ public class GT_OverclockCalculator {
/**
* Set a supplier for calculating custom duration for when its needed under one tick
*/
+ @Nonnull
public GT_OverclockCalculator setDurationUnderOneTickSupplier(Supplier<Double> supplier) {
this.durationUnderOneTickSupplier = supplier;
return this;
@@ -333,6 +356,7 @@ public class GT_OverclockCalculator {
/**
* Sets if we should do overclocking or not
*/
+ @Nonnull
public GT_OverclockCalculator setNoOverclock(boolean noOverclock) {
this.noOverclock = noOverclock;
return this;
@@ -341,6 +365,7 @@ public class GT_OverclockCalculator {
/**
* Call this when all values have been put it.
*/
+ @Nonnull
public GT_OverclockCalculator calculate() {
if (calculated) {
throw new IllegalStateException("Tried to calculate overclocks twice");
diff --git a/src/main/java/gregtech/api/util/GT_ParallelHelper.java b/src/main/java/gregtech/api/util/GT_ParallelHelper.java
index 6ec736b15f..ff739664f4 100644
--- a/src/main/java/gregtech/api/util/GT_ParallelHelper.java
+++ b/src/main/java/gregtech/api/util/GT_ParallelHelper.java
@@ -13,6 +13,8 @@ import net.minecraftforge.fluids.FluidStack;
import gregtech.api.interfaces.tileentity.IRecipeLockable;
import gregtech.api.interfaces.tileentity.IVoidable;
+import gregtech.api.logic.FluidInventoryLogic;
+import gregtech.api.logic.ItemInventoryLogic;
import gregtech.api.objects.XSTR;
import gregtech.api.recipe.RecipeMap;
import gregtech.api.recipe.check.CheckRecipeResult;
@@ -61,6 +63,14 @@ public class GT_ParallelHelper {
*/
private ItemStack[] itemInputs;
/**
+ * The inputs of the machine for current recipe check
+ */
+ private ItemInventoryLogic itemInputInventory;
+ /**
+ * The output item inventory of the machine
+ */
+ private ItemInventoryLogic itemOutputInventory;
+ /**
* The outputs of the recipe with the applied parallel
*/
private ItemStack[] itemOutputs;
@@ -69,6 +79,14 @@ public class GT_ParallelHelper {
*/
private FluidStack[] fluidInputs;
/**
+ * The inputs of the machine for the current recipe check
+ */
+ private FluidInventoryLogic fluidInputInventory;
+ /**
+ * The output fluid inventory of the machine;
+ */
+ private FluidInventoryLogic fluidOutputInventory;
+ /**
* The outputs of the recipe with the applied parallel
*/
private FluidStack[] fluidOutputs;
@@ -117,18 +135,25 @@ public class GT_ParallelHelper {
* Calculator to use for overclocking
*/
private GT_OverclockCalculator calculator;
-
+ @Nonnull
private CheckRecipeResult result = CheckRecipeResultRegistry.NONE;
private Function<Integer, ItemStack[]> customItemOutputCalculation;
private Function<Integer, FluidStack[]> customFluidOutputCalculation;
+ /**
+ * MuTE Mode this is a mode for changing how the GT_ParallelHelper works as Mutes don't use ItemStack and FluidStack
+ * arrays for inputs
+ */
+ private boolean muteMode = false;
+
public GT_ParallelHelper() {}
/**
* Sets machine, with current configuration for void protection mode.
*/
+ @Nonnull
public GT_ParallelHelper setMachine(IVoidable machine) {
return setMachine(machine, machine.protectsExcessItem(), machine.protectsExcessFluid());
}
@@ -136,6 +161,7 @@ public class GT_ParallelHelper {
/**
* Sets machine, with void protection mode forcibly.
*/
+ @Nonnull
public GT_ParallelHelper setMachine(IVoidable machine, boolean protectExcessItem, boolean protectExcessFluid) {
this.protectExcessItem = protectExcessItem;
this.protectExcessFluid = protectExcessFluid;
@@ -146,11 +172,13 @@ public class GT_ParallelHelper {
/**
* Sets the recipe, which will be used for the parallel calculation
*/
+ @Nonnull
public GT_ParallelHelper setRecipe(@Nonnull GT_Recipe aRecipe) {
recipe = Objects.requireNonNull(aRecipe);
return this;
}
+ @Nonnull
public GT_ParallelHelper setRecipeLocked(IRecipeLockable singleRecipeMachine, boolean isRecipeLocked) {
this.singleRecipeMachine = singleRecipeMachine;
this.isRecipeLocked = isRecipeLocked;
@@ -160,6 +188,7 @@ public class GT_ParallelHelper {
/**
* Sets the items available for the recipe check
*/
+ @Nonnull
public GT_ParallelHelper setItemInputs(ItemStack... aItemInputs) {
this.itemInputs = aItemInputs;
return this;
@@ -168,6 +197,7 @@ public class GT_ParallelHelper {
/**
* Sets the fluid inputs available for the recipe check
*/
+ @Nonnull
public GT_ParallelHelper setFluidInputs(FluidStack... aFluidInputs) {
this.fluidInputs = aFluidInputs;
return this;
@@ -176,6 +206,7 @@ public class GT_ParallelHelper {
/**
* Sets the available eut when trying for more parallels
*/
+ @Nonnull
public GT_ParallelHelper setAvailableEUt(long aAvailableEUt) {
this.availableEUt = aAvailableEUt;
return this;
@@ -184,11 +215,13 @@ public class GT_ParallelHelper {
/**
* Sets the modifier for recipe eut. 1 does nothing 0.9 is 10% less. 1.1 is 10% more
*/
+ @Nonnull
public GT_ParallelHelper setEUtModifier(float aEUtModifier) {
this.eutModifier = aEUtModifier;
return this;
}
+ @Nonnull
public GT_ParallelHelper setCalculator(GT_OverclockCalculator calculator) {
this.calculator = calculator;
return this;
@@ -199,6 +232,7 @@ public class GT_ParallelHelper {
*
* @param consume Should we consume inputs
*/
+ @Nonnull
public GT_ParallelHelper setConsumption(boolean consume) {
this.consume = consume;
return this;
@@ -207,6 +241,7 @@ public class GT_ParallelHelper {
/**
* Sets the MaxParallel a multi can handle
*/
+ @Nonnull
public GT_ParallelHelper setMaxParallel(int maxParallel) {
this.maxParallel = maxParallel;
return this;
@@ -216,6 +251,7 @@ public class GT_ParallelHelper {
* Enables Batch mode. Can do up to an additional processed recipes of mCurrentParallel * mBatchModifier A batch
* modifier of 1 does nothing
*/
+ @Nonnull
public GT_ParallelHelper enableBatchMode(int batchModifier) {
this.batchMode = batchModifier > 1;
this.batchModifier = batchModifier;
@@ -227,6 +263,7 @@ public class GT_ParallelHelper {
*
* @param calculateOutputs Should we calculate outputs with the helper or not
*/
+ @Nonnull
public GT_ParallelHelper setOutputCalculation(boolean calculateOutputs) {
this.calculateOutputs = calculateOutputs;
return this;
@@ -236,6 +273,7 @@ public class GT_ParallelHelper {
* Set a custom way to calculate item outputs. You are given the amount of parallels and must return an ItemStack
* array
*/
+ @Nonnull
public GT_ParallelHelper setCustomItemOutputCalculation(Function<Integer, ItemStack[]> custom) {
customItemOutputCalculation = custom;
return this;
@@ -245,11 +283,30 @@ public class GT_ParallelHelper {
* Set a custom way to calculate item outputs. You are given the amount of parallels and must return a FluidStack
* array
*/
+ @Nonnull
public GT_ParallelHelper setCustomFluidOutputCalculation(Function<Integer, FluidStack[]> custom) {
customFluidOutputCalculation = custom;
return this;
}
+ @Nonnull
+ public GT_ParallelHelper setMuTEMode(boolean muteMode) {
+ this.muteMode = muteMode;
+ return this;
+ }
+
+ @Nonnull
+ public GT_ParallelHelper setItemInputInventory(ItemInventoryLogic itemInputInventory) {
+ this.itemInputInventory = itemInputInventory;
+ return this;
+ }
+
+ @Nonnull
+ public GT_ParallelHelper setFluidInputInventory(FluidInventoryLogic fluidInputInventory) {
+ this.fluidInputInventory = fluidInputInventory;
+ return this;
+ }
+
/**
* Sets method for calculating max parallel from given inputs.
*/
@@ -266,9 +323,22 @@ public class GT_ParallelHelper {
return this;
}
+ @Nonnull
+ public GT_ParallelHelper setItemOutputInventory(ItemInventoryLogic itemOutputInventory) {
+ this.itemOutputInventory = itemOutputInventory;
+ return this;
+ }
+
+ @Nonnull
+ public GT_ParallelHelper setFluidOutputInventory(FluidInventoryLogic fluidOutputInventory) {
+ this.fluidOutputInventory = fluidOutputInventory;
+ return this;
+ }
+
/**
* Finishes the GT_ParallelHelper. Anything changed after this will not effect anything
*/
+ @Nonnull
public GT_ParallelHelper build() {
if (built) {
throw new IllegalStateException("Tried to build twice");
@@ -407,7 +477,7 @@ public class GT_ParallelHelper {
// Let's look at how many parallels we can get with void protection
if (protectExcessItem || protectExcessFluid) {
- if (machine == null) {
+ if (machine == null && !muteMode) {
throw new IllegalStateException("Tried to calculate void protection, but machine is not set");
}
VoidProtectionHelper voidProtectionHelper = new VoidProtectionHelper();
@@ -415,6 +485,9 @@ public class GT_ParallelHelper {
.setItemOutputs(truncatedItemOutputs)
.setFluidOutputs(truncatedFluidOutputs)
.setMaxParallel(maxParallel)
+ .setItemOutputInventory(itemOutputInventory)
+ .setFluidOutputInventory(fluidOutputInventory)
+ .setMuTEMode(muteMode)
.build();
maxParallel = Math.min(voidProtectionHelper.getMaxParallel(), maxParallel);
if (voidProtectionHelper.isItemFull()) {
diff --git a/src/main/java/gregtech/api/util/GT_Recipe.java b/src/main/java/gregtech/api/util/GT_Recipe.java
index 159ac1bbd1..0e4ef2162d 100644
--- a/src/main/java/gregtech/api/util/GT_Recipe.java
+++ b/src/main/java/gregtech/api/util/GT_Recipe.java
@@ -7,6 +7,7 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -23,6 +24,8 @@ import gregtech.GT_Mod;
import gregtech.api.GregTech_API;
import gregtech.api.enums.ItemList;
import gregtech.api.enums.Materials;
+import gregtech.api.logic.FluidInventoryLogic;
+import gregtech.api.logic.ItemInventoryLogic;
import gregtech.api.objects.GT_ItemStack;
import gregtech.api.recipe.RecipeCategory;
import gregtech.api.recipe.RecipeMap;
@@ -31,6 +34,7 @@ import gregtech.api.recipe.RecipeMetadataKey;
import gregtech.api.recipe.metadata.EmptyRecipeMetadataStorage;
import gregtech.api.recipe.metadata.IRecipeMetadataStorage;
import gregtech.api.util.extensions.ArrayExt;
+import gregtech.api.util.item.ItemHolder;
import ic2.core.Ic2Items;
public class GT_Recipe implements Comparable<GT_Recipe> {
@@ -548,6 +552,60 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
return currentParallel;
}
+ public boolean isRecipePossible(@Nullable ItemInventoryLogic itemInput, @Nullable FluidInventoryLogic fluidInput) {
+ return getAmountOfRecipesDone(itemInput, fluidInput, 1, true) > 0;
+ }
+
+ public long getAmountOfRecipesDone(@Nullable ItemInventoryLogic itemInput, @Nullable FluidInventoryLogic fluidInput,
+ long maxParallel, boolean simulate) {
+ if (itemInput == null) {
+ itemInput = new ItemInventoryLogic(0);
+ }
+
+ if (fluidInput == null) {
+ fluidInput = new FluidInventoryLogic(0, 0);
+ }
+
+ itemInput.startRecipeCheck();
+ Map<ItemHolder, Long> recipeItems = getItemInputsAsItemMap();
+ for (Entry<ItemHolder, Long> entry : recipeItems.entrySet()) {
+ maxParallel = Math
+ .min(maxParallel, itemInput.calculateAmountOfTimesItemCanBeTaken(entry.getKey(), entry.getValue()));
+ }
+
+ for (FluidStack fluid : mFluidInputs) {
+ if (fluid == null) continue;
+ maxParallel = Math
+ .min(maxParallel, fluidInput.calculateAmountOfTimesFluidCanBeTaken(fluid.getFluid(), fluid.amount));
+ }
+
+ if (simulate) {
+ itemInput.stopRecipeCheck();
+ return maxParallel;
+ }
+
+ for (Entry<ItemHolder, Long> entry : recipeItems.entrySet()) {
+ itemInput.subtractItemAmount(entry.getKey(), entry.getValue() * maxParallel, false);
+ }
+
+ for (FluidStack fluid : mFluidInputs) {
+ if (fluid == null) continue;
+ fluidInput.drain(fluid.getFluid(), fluid.amount * maxParallel, false);
+ }
+ itemInput.stopRecipeCheck();
+ return maxParallel;
+ }
+
+ private Map<ItemHolder, Long> getItemInputsAsItemMap() {
+ Map<ItemHolder, Long> items = new HashMap<>();
+ for (ItemStack item : mInputs) {
+ if (item == null) continue;
+ ItemHolder itemHolder = new ItemHolder(item);
+ items.put(itemHolder, items.getOrDefault(itemHolder, 0L) + item.stackSize);
+ }
+ return items;
+ }
+
@Override
public int compareTo(GT_Recipe recipe) {
// first lowest tier recipes
@@ -720,6 +778,25 @@ public class GT_Recipe implements Comparable<GT_Recipe> {
return this;
}
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) return true;
+ if (!(other instanceof GT_Recipe recipe)) return false;
+ for (int i = 0; i < Math.min(mInputs.length, recipe.mInputs.length); i++) {
+ if (mInputs[i] == null && recipe.mInputs[i] == null) continue;
+ if (mInputs[i] == null || recipe.mInputs[i] == null) return false;
+ ItemHolder currentIH = new ItemHolder(mInputs[i]);
+ ItemHolder otherIH = new ItemHolder(recipe.mInputs[i]);
+ if (!currentIH.equals(otherIH)) return false;
+ }
+ for (int i = 0; i < Math.min(mFluidInputs.length, recipe.mFluidInputs.length); i++) {
+ if (mFluidInputs[i] == null && recipe.mFluidInputs[i] == null) continue;
+ if (mFluidInputs[i] == null || recipe.mFluidInputs[i] == null) return false;
+ if (!FluidStack.areFluidStackTagsEqual(mFluidInputs[i], recipe.mFluidInputs[i])) return false;
+ }
+ return mDuration == recipe.mDuration && mEUt == recipe.mEUt && mSpecialValue == recipe.mSpecialValue;
+ }
+
public static class GT_Recipe_AssemblyLine {
public static final ArrayList<GT_Recipe_AssemblyLine> sAssemblylineRecipes = new ArrayList<>();
diff --git a/src/main/java/gregtech/api/util/GT_StructureUtilityMuTE.java b/src/main/java/gregtech/api/util/GT_StructureUtilityMuTE.java
new file mode 100644
index 0000000000..8e8d027463
--- /dev/null
+++ b/src/main/java/gregtech/api/util/GT_StructureUtilityMuTE.java
@@ -0,0 +1,271 @@
+package gregtech.api.util;
+
+import static gregtech.GT_Mod.GT_FML_LOGGER;
+import static gregtech.api.multitileentity.enums.GT_MultiTileComponentCasing.*;
+import static gregtech.api.multitileentity.enums.GT_MultiTileUpgradeCasing.*;
+import static gregtech.loaders.preload.GT_Loader_MultiTileEntities.*;
+
+import java.util.Arrays;
+
+import net.minecraft.block.Block;
+import net.minecraft.item.ItemStack;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.IIcon;
+import net.minecraft.world.World;
+
+import com.gtnewhorizon.structurelib.StructureLibAPI;
+import com.gtnewhorizon.structurelib.structure.IStructureElement;
+
+import gregtech.api.enums.GT_Values;
+import gregtech.api.enums.OrePrefixes;
+import gregtech.api.enums.TextureSet;
+import gregtech.api.multitileentity.MultiTileEntityContainer;
+import gregtech.api.multitileentity.MultiTileEntityRegistry;
+import gregtech.api.multitileentity.enums.GT_MultiTileUpgradeCasing;
+import gregtech.api.multitileentity.interfaces.IMultiBlockController;
+import gregtech.api.multitileentity.interfaces.IMultiTileEntity;
+import gregtech.api.multitileentity.multiblock.base.Controller;
+import gregtech.api.multitileentity.multiblock.base.MultiBlockPart;
+
+public class GT_StructureUtilityMuTE {
+
+ public static final MuTEStructureCasing MOTOR_CASINGS = FunctionalCasings.Motor.getCasing();
+ public static final MuTEStructureCasing PUMP_CASINGS = FunctionalCasings.Pump.getCasing();
+ public static final MuTEStructureCasing CONVEYOR_CASINGS = FunctionalCasings.Conveyor.getCasing();
+ public static final MuTEStructureCasing PISTON_CASINGS = FunctionalCasings.Piston.getCasing();
+ public static final MuTEStructureCasing ROBOT_ARM_CASINGS = FunctionalCasings.RobotArm.getCasing();
+ public static final MuTEStructureCasing EMITTER_CASINGS = FunctionalCasings.Emitter.getCasing();
+ public static final MuTEStructureCasing SENSOR_CASINGS = FunctionalCasings.Sensor.getCasing();
+ public static final MuTEStructureCasing FIELD_GENERATOR_CASINGS = FunctionalCasings.FieldGenerator.getCasing();
+ public static final MuTEStructureCasing INVENTORY_CASINGS = UpgradeCasings.Inventory.getCasing();
+ public static final MuTEStructureCasing TANK_CASINGS = UpgradeCasings.Tank.getCasing();
+ public static final MuTEStructureCasing AMPERAGE_CASINGS = UpgradeCasings.Amperage.getCasing();
+ public static final MuTEStructureCasing LASER_CASINGS = UpgradeCasings.Laser.getCasing();
+ public static final MuTEStructureCasing WIRELESS_CASINGS = UpgradeCasings.Wireless.getCasing();
+ public static final MuTEStructureCasing CLEANROOM_CASINGS = UpgradeCasings.Cleanroom.getCasing();
+ public static final MuTEStructureCasing HEATER_CASINGS = UpgradeCasings.Heater.getCasing();
+ public static final MuTEStructureCasing INSULATOR_CASINGS = UpgradeCasings.Insulator.getCasing();
+
+ public enum FunctionalCasings {
+
+ Motor(COMPONENT_CASING_REGISTRY_NAME, LV_Motor.getId(), MV_Motor.getId(), HV_Motor.getId(), EV_Motor.getId(),
+ IV_Motor.getId(), LuV_Motor.getId(), ZPM_Motor.getId(), UV_Motor.getId(), UHV_Motor.getId(),
+ UEV_Motor.getId(), UIV_Motor.getId(), UMV_Motor.getId(), UXV_Motor.getId(), MAX_Motor.getId()),
+
+ Pump(COMPONENT_CASING_REGISTRY_NAME, LV_Pump.getId(), MV_Pump.getId(), HV_Pump.getId(), EV_Pump.getId(),
+ IV_Pump.getId(), LuV_Pump.getId(), ZPM_Pump.getId(), UV_Pump.getId(), UHV_Pump.getId(), UEV_Pump.getId(),
+ UIV_Pump.getId(), UMV_Pump.getId(), UXV_Pump.getId(), MAX_Pump.getId()),
+
+ Conveyor(COMPONENT_CASING_REGISTRY_NAME, LV_Conveyor.getId(), MV_Conveyor.getId(), HV_Conveyor.getId(),
+ EV_Conveyor.getId(), IV_Conveyor.getId(), LuV_Conveyor.getId(), ZPM_Conveyor.getId(), UV_Conveyor.getId(),
+ UHV_Conveyor.getId(), UEV_Conveyor.getId(), UIV_Conveyor.getId(), UMV_Conveyor.getId(),
+ UXV_Conveyor.getId(), MAX_Conveyor.getId()),
+
+ Piston(COMPONENT_CASING_REGISTRY_NAME, LV_Piston.getId(), MV_Piston.getId(), HV_Piston.getId(),
+ EV_Piston.getId(), IV_Piston.getId(), LuV_Piston.getId(), ZPM_Piston.getId(), UV_Piston.getId(),
+ UHV_Piston.getId(), UEV_Piston.getId(), UIV_Piston.getId(), UMV_Piston.getId(), UXV_Piston.getId(),
+ MAX_Piston.getId()),
+
+ RobotArm(COMPONENT_CASING_REGISTRY_NAME, LV_RobotArm.getId(), MV_RobotArm.getId(), HV_RobotArm.getId(),
+ EV_RobotArm.getId(), IV_RobotArm.getId(), LuV_RobotArm.getId(), ZPM_RobotArm.getId(), UV_RobotArm.getId(),
+ UHV_RobotArm.getId(), UEV_RobotArm.getId(), UIV_RobotArm.getId(), UMV_RobotArm.getId(),
+ UXV_RobotArm.getId(), MAX_RobotArm.getId()),
+
+ Emitter(COMPONENT_CASING_REGISTRY_NAME, LV_Emitter.getId(), MV_Emitter.getId(), HV_Emitter.getId(),
+ EV_Emitter.getId(), IV_Emitter.getId(), LuV_Emitter.getId(), ZPM_Emitter.getId(), UV_Emitter.getId(),
+ UHV_Emitter.getId(), UEV_Emitter.getId(), UIV_Emitter.getId(), UMV_Emitter.getId(), UXV_Emitter.getId(),
+ MAX_Emitter.getId()),
+
+ Sensor(COMPONENT_CASING_REGISTRY_NAME, LV_Sensor.getId(), MV_Sensor.getId(), HV_Sensor.getId(),
+ EV_Sensor.getId(), IV_Sensor.getId(), LuV_Sensor.getId(), ZPM_Sensor.getId(), UV_Sensor.getId(),
+ UHV_Sensor.getId(), UEV_Sensor.getId(), UIV_Sensor.getId(), UMV_Sensor.getId(), UXV_Sensor.getId(),
+ MAX_Sensor.getId()),
+
+ FieldGenerator(COMPONENT_CASING_REGISTRY_NAME, LV_FieldGenerator.getId(), MV_FieldGenerator.getId(),
+ HV_FieldGenerator.getId(), EV_FieldGenerator.getId(), IV_FieldGenerator.getId(), LuV_FieldGenerator.getId(),
+ ZPM_FieldGenerator.getId(), UV_FieldGenerator.getId(), UHV_FieldGenerator.getId(),
+ UEV_FieldGenerator.getId(), UIV_FieldGenerator.getId(), UMV_FieldGenerator.getId(),
+ UXV_FieldGenerator.getId(), MAX_FieldGenerator.getId());
+
+ private final MuTEStructureCasing casing;
+
+ FunctionalCasings(String registryName, Integer... validIds) {
+ casing = createMuTEStructureCasing(registryName, validIds);
+ }
+
+ public MuTEStructureCasing getCasing() {
+ return casing;
+ }
+ }
+
+ public enum UpgradeCasings {
+
+ Inventory(UPGRADE_CASING_REGISTRY_NAME, ULV_Inventory.getId(), LV_Inventory.getId(), MV_Inventory.getId(),
+ HV_Inventory.getId(), EV_Inventory.getId(), IV_Inventory.getId(), LuV_Inventory.getId(),
+ ZPM_Inventory.getId(), UV_Inventory.getId(), UHV_Inventory.getId(), UEV_Inventory.getId(),
+ UIV_Inventory.getId(), UMV_Inventory.getId(), UXV_Inventory.getId(), MAX_Inventory.getId()),
+
+ Tank(UPGRADE_CASING_REGISTRY_NAME, ULV_Tank.getId(), LV_Tank.getId(), MV_Tank.getId(), HV_Tank.getId(),
+ EV_Tank.getId(), IV_Tank.getId(), LuV_Tank.getId(), ZPM_Tank.getId(), UV_Tank.getId(), UHV_Tank.getId(),
+ UEV_Tank.getId(), UIV_Tank.getId(), UMV_Tank.getId(), UXV_Tank.getId(), MAX_Tank.getId()),
+
+ Amperage(UPGRADE_CASING_REGISTRY_NAME, Amp_4.getId(), Amp_16.getId(), Amp_64.getId(), Amp_256.getId(),
+ Amp_1_024.getId(), Amp_4_096.getId(), Amp_16_384.getId(), Amp_65_536.getId(), Amp_262_144.getId(),
+ Amp_1_048_576.getId()),
+
+ Laser(UPGRADE_CASING_REGISTRY_NAME, GT_MultiTileUpgradeCasing.Laser.getId()),
+
+ Wireless(UPGRADE_CASING_REGISTRY_NAME, GT_MultiTileUpgradeCasing.Wireless.getId()),
+
+ Cleanroom(UPGRADE_CASING_REGISTRY_NAME, GT_MultiTileUpgradeCasing.Cleanroom.getId()),
+
+ Heater(UPGRADE_CASING_REGISTRY_NAME, Heater_Prototype.getId(), Heater_IndustrialGrade.getId(),
+ Heater_NextGen.getId(), Heater_Omnipotent.getId(), Heater_OmegaType.getId()),
+
+ Insulator(UPGRADE_CASING_REGISTRY_NAME, Insulator_Prototype.getId(), Insulator_IndustrialGrade.getId(),
+ Insulator_NextGen.getId(), Insulator_Omnipotent.getId(), Insulator_OmegaType.getId());
+
+ private final MuTEStructureCasing casing;
+
+ UpgradeCasings(String registryName, Integer... validIds) {
+ casing = createMuTEStructureCasing(registryName, validIds);
+ }
+
+ public MuTEStructureCasing getCasing() {
+ return casing;
+ }
+ }
+
+ /**
+ * Specify all casing sets that are valid for a multiblock structure position. The first casing will be used as
+ * default when doing auto place
+ *
+ * @param modes Allowed modes on the casings
+ * @param validCasings Allowed casing sets
+ * @return Structure Element
+ * @param <T> Multiblock class
+ */
+ public static <T> IStructureElement<T> ofMuTECasings(int modes, MuTEStructureCasing... validCasings) {
+ if (validCasings == null || validCasings.length == 0) {
+ throw new IllegalArgumentException();
+ }
+ return new IStructureElement<>() {
+
+ final MuTEStructureCasing[] allowedCasings = validCasings;
+ private final static short[] DEFAULT = new short[] { 255, 255, 255, 0 };
+ private static IIcon[] mIcons = null;
+
+ @Override
+ public boolean check(T t, World world, int x, int y, int z) {
+ final TileEntity tileEntity = world.getTileEntity(x, y, z);
+ if (!(tileEntity instanceof MultiBlockPart part)) return false;
+
+ for (MuTEStructureCasing casing : allowedCasings) {
+ if (casing.isCasingValid(part.getMultiTileEntityRegistryID(), part.getMultiTileEntityID())) {
+ final IMultiBlockController tTarget = part.getTarget(false);
+ if (tTarget != null && tTarget != t) return false;
+
+ part.setTarget((IMultiBlockController) t, modes);
+
+ ((Controller<?, ?>) t).registerSpecialCasings(part);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
+ // Moved here from Controller. TODO: Proper implementation
+ if (mIcons == null) {
+ mIcons = new IIcon[6];
+ Arrays.fill(mIcons, TextureSet.SET_NONE.mTextures[OrePrefixes.block.mTextureIndex].getIcon());
+ }
+ final short[] RGBA = DEFAULT;
+ StructureLibAPI.hintParticleTinted(world, x, y, z, mIcons, RGBA);
+ return true;
+ }
+
+ @Override
+ public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
+ final MultiTileEntityRegistry tRegistry = MultiTileEntityRegistry
+ .getRegistry(validCasings[0].getRegistryId());
+ if (tRegistry == null) {
+ GT_FML_LOGGER.error("NULL REGISTRY");
+ return false;
+ }
+ final MultiTileEntityContainer tContainer = tRegistry
+ .getNewTileEntityContainer(world, x, y, z, validCasings[0].defaultMeta, null);
+ if (tContainer == null) {
+ GT_FML_LOGGER.error("NULL CONTAINER");
+ return false;
+ }
+ final IMultiTileEntity te = ((IMultiTileEntity) tContainer.mTileEntity);
+ if (!(te instanceof MultiBlockPart)) {
+ GT_FML_LOGGER.error("Not a multiblock part");
+ return false;
+ }
+ if (world.setBlock(x, y, z, tContainer.mBlock, 15 - tContainer.mBlockMetaData, 2)) {
+ tContainer.setMultiTile(world, x, y, z);
+ ((MultiBlockPart) te).setTarget((IMultiBlockController) t, modes);
+
+ ((Controller<?, ?>) t).registerSpecialCasings((MultiBlockPart) te);
+ }
+
+ return false;
+ }
+ };
+ }
+
+ public static MuTEStructureCasing createMuTEStructureCasing(String registryName, Integer... validIds) {
+ return new MuTEStructureCasing(registryName, validIds);
+ }
+
+ /**
+ * Object used to store a set of casings (e.g. all motor casings)
+ */
+ public static class MuTEStructureCasing {
+
+ private String registryName;
+ private int registryId = GT_Values.W;
+ private final int defaultMeta;
+ private final Integer[] validIds;
+
+ public MuTEStructureCasing(String registryName, Integer... validIds) {
+ MultiTileEntityRegistry registry = MultiTileEntityRegistry.getRegistry(registryName);
+ if (validIds == null || validIds.length == 0 || registry == null) {
+ throw new IllegalArgumentException();
+ }
+ this.registryName = registryName;
+ this.validIds = validIds;
+ this.defaultMeta = validIds[0];
+ }
+
+ public boolean isCasingValid(int registryId, int id) {
+ if (getRegistryId() != registryId) {
+ return false;
+ }
+ for (Integer validId : validIds) {
+ if (validId == id) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public int getDefaultMeta() {
+ return defaultMeta;
+ }
+
+ public int getRegistryId() {
+ // TODO: MuTE registry seems to somehow shift, probably due to NBT shenanigans. Lazy init circumvents this
+ // but it should be properly fixed in the future
+ if (registryId == GT_Values.W) {
+ MultiTileEntityRegistry registry = MultiTileEntityRegistry.getRegistry(registryName);
+ registryId = Block.getIdFromBlock(registry.mBlock);
+ }
+ return registryId;
+ }
+ }
+}
diff --git a/src/main/java/gregtech/api/util/VoidProtectionHelper.java b/src/main/java/gregtech/api/util/VoidProtectionHelper.java
index 3a41bba934..245fc24bda 100644
--- a/src/main/java/gregtech/api/util/VoidProtectionHelper.java
+++ b/src/main/java/gregtech/api/util/VoidProtectionHelper.java
@@ -10,9 +10,12 @@ import net.minecraft.item.ItemStack;
import net.minecraftforge.fluids.FluidStack;
import com.gtnewhorizon.gtnhlib.util.map.ItemStackMap;
+import com.gtnewhorizons.modularui.api.fluids.IFluidTankLong;
import gregtech.api.interfaces.fluid.IFluidStore;
import gregtech.api.interfaces.tileentity.IVoidable;
+import gregtech.api.logic.FluidInventoryLogic;
+import gregtech.api.logic.ItemInventoryLogic;
/**
* Helper class to calculate how many parallels of items / fluids can fit in the output buses / hatches.
@@ -52,9 +55,21 @@ public class VoidProtectionHelper {
*/
private FluidStack[] fluidOutputs;
/**
+ * The item output inventory
+ */
+ private ItemInventoryLogic itemOutputInventory;
+ /**
+ * The fluid output inventory
+ */
+ private FluidInventoryLogic fluidOutputInventory;
+ /**
* Has this helper been built?
*/
private boolean built;
+ /**
+ * Is this helper working for a MuTE?
+ */
+ private boolean muteMode;
public VoidProtectionHelper() {}
@@ -93,6 +108,21 @@ public class VoidProtectionHelper {
return this;
}
+ public VoidProtectionHelper setItemOutputInventory(ItemInventoryLogic itemOutputInventory) {
+ this.itemOutputInventory = itemOutputInventory;
+ return this;
+ }
+
+ public VoidProtectionHelper setFluidOutputInventory(FluidInventoryLogic fluidOutputInventory) {
+ this.fluidOutputInventory = fluidOutputInventory;
+ return this;
+ }
+
+ public VoidProtectionHelper setMuTEMode(boolean muteMode) {
+ this.muteMode = muteMode;
+ return this;
+ }
+
/**
* Finishes the VoidProtectionHelper. Anything changed after this will not affect anything
*/
@@ -241,11 +271,95 @@ public class VoidProtectionHelper {
return aParallelQueue.element().batch;
}
+ private int calculateMaxFluidParallelsMuTE() {
+ if (fluidOutputs.length > fluidOutputInventory.getInventory()
+ .getTanks()) {
+ return 0;
+ }
+
+ // A map to hold the items we will be 'inputting' into the output hatches. These fluidstacks are actually
+ // the recipe outputs.
+ Map<FluidStack, Integer> tFluidOutputMap = new HashMap<>();
+
+ // Map that keeps track of the number of parallel crafts we can accommodate for each fluid output.
+ // In the pair, we keep track of number of full crafts plus mb of fluid in a partial craft, to avoid
+ // issues with floating point math not being completely accurate when summing.
+ Map<FluidStack, ParallelData> tParallels = new HashMap<>();
+
+ // Iterate over the outputs, calculating require stack spacing they will require.
+ for (FluidStack aY : fluidOutputs) {
+ if (aY == null || aY.amount <= 0) {
+ continue;
+ }
+ tFluidOutputMap.merge(aY, aY.amount, Integer::sum);
+ tParallels.put(aY, new ParallelData(0, 0));
+ }
+
+ if (tFluidOutputMap.isEmpty()) {
+ // nothing to output, bail early
+ return maxParallel;
+ }
+
+ for (int i = 0; i < fluidOutputInventory.getInventory()
+ .getTanks(); i++) {
+ IFluidTankLong tank = fluidOutputInventory.getInventory()
+ .getFluidTank(i);
+ long tSpaceLeft = tank.getCapacityLong() - tank.getFluidAmountLong();
+ // check if hatch filled
+ if (tSpaceLeft <= 0) continue;
+ // check if hatch is empty and unrestricted
+ if (tank.getStoredFluid() == null) continue;
+
+ for (Map.Entry<FluidStack, ParallelData> entry : tParallels.entrySet()) {
+ FluidStack tFluidOutput = entry.getKey();
+ if (tank.fill(tFluidOutput.getFluid(), tFluidOutput.amount, false) == tFluidOutput.amount) continue;
+ // this fluid is not prevented by restrictions on output hatch
+ ParallelData tParallel = entry.getValue();
+ Integer tCraftSize = tFluidOutputMap.get(tFluidOutput);
+ tParallel.batch += (tParallel.partial + tSpaceLeft) / tCraftSize;
+ tParallel.partial = (tParallel.partial + tSpaceLeft) % tCraftSize;
+ }
+ }
+ // now that all partial/restricted hatches have been counted, create a priority queue for our outputs
+ // the lowest priority fluid is the number of complete parallel crafts we can support
+ PriorityQueue<ParallelStackInfo<FluidStack>> aParallelQueue = new PriorityQueue<>(
+ Comparator.comparing(i -> i.batch));
+ for (Map.Entry<FluidStack, ParallelData> entry : tParallels.entrySet()) {
+ aParallelQueue
+ .add(new ParallelStackInfo<>(entry.getValue().batch, entry.getValue().partial, entry.getKey()));
+ }
+ // add extra parallels for open slots as well
+ for (int i = 0; i < fluidOutputInventory.getInventory()
+ .getTanks(); i++) {
+ IFluidTankLong tank = fluidOutputInventory.getInventory()
+ .getFluidTank(i);
+ // partially filled or restricted hatch. done in the last pass
+ if (tank.getStoredFluid() != null) continue;
+
+ ParallelStackInfo<FluidStack> tParallel = aParallelQueue.poll();
+ assert tParallel != null; // will always be true, specifying assert here to avoid IDE/compiler warnings
+ Integer tCraftSize = tFluidOutputMap.get(tParallel.stack);
+ long tSpaceLeft = tank.getCapacityLong();
+ tParallel.batch += (tParallel.partial + tSpaceLeft) / tCraftSize;
+ tParallel.partial = (tParallel.partial + tSpaceLeft) % tCraftSize;
+ aParallelQueue.add(tParallel);
+ }
+
+ return aParallelQueue.element().batch;
+ }
+
/**
* Calculates the max parallels one can do with items if void protection is on
*/
private int calculateMaxItemParallels() {
- List<ItemStack> busStacks = machine.getItemOutputSlots(itemOutputs);
+ List<ItemStack> busStacks;
+
+ if (muteMode) {
+ busStacks = itemOutputInventory.getInventory()
+ .getStacks();
+ } else {
+ busStacks = machine.getItemOutputSlots(itemOutputs);
+ }
// A map to hold the items we will be 'inputting' into the output buses. These itemstacks are actually the
// recipe outputs.
Map<ItemStack, Integer> tItemOutputMap = new ItemStackMap<>();
@@ -320,9 +434,9 @@ public class VoidProtectionHelper {
private static class ParallelData {
private int batch;
- private int partial;
+ private long partial;
- private ParallelData(int batch, int partial) {
+ private ParallelData(int batch, long partial) {
this.batch = batch;
this.partial = partial;
}
@@ -331,10 +445,10 @@ public class VoidProtectionHelper {
private static class ParallelStackInfo<T> {
private int batch;
- private int partial;
+ private long partial;
private final T stack;
- private ParallelStackInfo(int batch, int partial, T stack) {
+ private ParallelStackInfo(int batch, long partial, T stack) {
this.batch = batch;
this.partial = partial;
this.stack = stack;
diff --git a/src/main/java/gregtech/api/util/item/ItemHolder.java b/src/main/java/gregtech/api/util/item/ItemHolder.java
new file mode 100644
index 0000000000..4675d0ba0e
--- /dev/null
+++ b/src/main/java/gregtech/api/util/item/ItemHolder.java
@@ -0,0 +1,79 @@
+package gregtech.api.util.item;
+
+import static net.minecraftforge.oredict.OreDictionary.getOreIDs;
+
+import java.util.Arrays;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.init.Items;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+
+import gregtech.api.util.GT_Utility;
+
+public class ItemHolder {
+
+ private final Item item;
+ private final int meta;
+ private final NBTTagCompound tag;
+ private final int[] oreIDs;
+
+ public ItemHolder(@Nonnull ItemStack item) {
+ this.item = item.getItem();
+ this.meta = Items.feather.getDamage(item);
+ this.tag = item.getTagCompound();
+ this.oreIDs = getOreIDs(item);
+ }
+
+ public Item getItem() {
+ return item;
+ }
+
+ public int getMeta() {
+ return meta;
+ }
+
+ public NBTTagCompound getNBT() {
+ return tag;
+ }
+
+ public int[] getOreDictTagIDs() {
+ return oreIDs;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) return true;
+ if (!(other instanceof ItemHolder otherIH)) return false;
+ if (Arrays.stream(oreIDs)
+ .anyMatch(id -> {
+ for (int i = 0; i < otherIH.getOreDictTagIDs().length; i++) {
+ if (id == otherIH.getOreDictTagIDs()[i]) return true;
+ }
+ return false;
+ })) {
+ return true;
+ }
+
+ if (item != otherIH.getItem() || meta != otherIH.getMeta()) {
+ return false;
+ }
+ if (this.tag == null && otherIH.getNBT() == null) return true;
+ if (this.tag == null || otherIH.getNBT() == null) return false;
+ return this.tag.equals(otherIH);
+ }
+
+ @Override
+ public int hashCode() {
+ return GT_Utility.stackToInt(toStack());
+ }
+
+ @Nonnull
+ private ItemStack toStack() {
+ ItemStack item = new ItemStack(this.item, 1, meta);
+ item.stackTagCompound = tag;
+ return item;
+ }
+}
diff --git a/src/main/java/gregtech/api/util/recipe/RecipeInputRequirements.java b/src/main/java/gregtech/api/util/recipe/RecipeInputRequirements.java
new file mode 100644
index 0000000000..590c104101
--- /dev/null
+++ b/src/main/java/gregtech/api/util/recipe/RecipeInputRequirements.java
@@ -0,0 +1,77 @@
+package gregtech.api.util.recipe;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.fluids.Fluid;
+import net.minecraftforge.fluids.FluidStack;
+
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.item.ItemHolder;
+
+public class RecipeInputRequirements {
+
+ protected Map<ItemHolder, Long> itemInputs = new HashMap<>();
+ protected Set<ItemHolder> itemInputsMet = new HashSet<>();
+ protected boolean metAllItem = false;
+ protected Map<Fluid, Long> fluidInputs = new HashMap<>();
+ protected Set<Fluid> fluidInputsMet = new HashSet<>();
+ protected boolean metAllFluid = false;
+
+ public RecipeInputRequirements(@Nonnull GT_Recipe recipe) {
+ this(recipe.mInputs, recipe.mFluidInputs);
+ }
+
+ public RecipeInputRequirements(@Nonnull ItemStack[] itemInputs, @Nonnull FluidStack[] fluidInputs) {
+ for (ItemStack item : itemInputs) {
+ if (item == null) continue;
+ ItemHolder itemIH = new ItemHolder(item);
+ this.itemInputs.put(itemIH, this.itemInputs.getOrDefault(itemIH, 0L) + item.stackSize);
+ }
+
+ for (FluidStack fluid : fluidInputs) {
+ if (fluid == null) continue;
+ this.fluidInputs.put(fluid.getFluid(), this.fluidInputs.getOrDefault(fluid.getFluid(), 0L) + fluid.amount);
+ }
+ }
+
+ /**
+ *
+ * @param itemInputs we have and want to fill this request
+ * @return {@code true} when all item inputs are met
+ */
+ public boolean tryToFillItemRequirements(Map<ItemHolder, Long> itemInputs) {
+ if (metAllItem) return metAllItem;
+ for (Entry<ItemHolder, Long> entry : itemInputs.entrySet()) {
+ if (itemInputsMet.contains(entry.getKey())) continue;
+ if (!this.itemInputs.containsKey(entry.getKey())) continue;
+ if (this.itemInputs.get(entry.getKey()) > entry.getValue()) continue;
+ itemInputsMet.add(entry.getKey());
+ }
+ metAllItem = itemInputsMet.containsAll(this.itemInputs.keySet());
+ return metAllItem;
+ }
+
+ /**
+ *
+ * @param fluidInputs we have and want to fill this request
+ * @return {@code true} when all fluid inputs are met
+ */
+ public boolean tryToFillFluidRequirements(Map<Fluid, Long> fluidInputs) {
+ if (metAllFluid) return metAllFluid;
+ for (Entry<Fluid, Long> entry : fluidInputs.entrySet()) {
+ if (fluidInputsMet.contains(entry.getKey())) continue;
+ if (!this.fluidInputs.containsKey(entry.getKey())) continue;
+ if (this.fluidInputs.get(entry.getKey()) > entry.getValue()) continue;
+ fluidInputsMet.add(entry.getKey());
+ }
+ metAllFluid = fluidInputsMet.containsAll(this.fluidInputs.keySet());
+ return metAllFluid;
+ }
+}