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