aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/gregtech/api/metatileentity/implementations
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/gregtech/api/metatileentity/implementations')
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Cable.java659
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Fluid.java980
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Frame.java119
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Item.java530
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicBatteryBuffer.java436
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicGenerator.java339
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicHull.java175
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicHull_NonElectric.java78
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine.java1563
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine_Bronze.java387
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine_GT_Recipe.java818
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine_Steel.java150
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicTank.java331
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Buffer.java563
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_CubicMultiBlockBase.java141
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_EnhancedMultiBlockBase.java318
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_ExtendedPowerMultiBlockBase.java242
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_FilterBase.java106
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch.java252
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_DataAccess.java170
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Dynamo.java110
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Energy.java125
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Input.java196
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_InputBus.java318
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Maintenance.java459
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Muffler.java208
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_MultiInput.java300
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Output.java497
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_OutputBus.java307
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_QuadrupleHumongous.java27
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_MagHatch.java103
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_MultiBlockBase.java2696
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_SpecialFilter.java134
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_TieredMachineBlock.java119
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_TooltipMultiBlockBase.java57
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Transformer.java325
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_WetTransformer.java93
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Wireless_Dynamo.java146
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Wireless_Hatch.java168
39 files changed, 14745 insertions, 0 deletions
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
new file mode 100644
index 0000000000..f5ab88fb45
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Cable.java
@@ -0,0 +1,659 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.Mods.GalacticraftCore;
+import static net.minecraftforge.common.util.ForgeDirection.DOWN;
+import static net.minecraftforge.common.util.ForgeDirection.EAST;
+import static net.minecraftforge.common.util.ForgeDirection.NORTH;
+import static net.minecraftforge.common.util.ForgeDirection.SOUTH;
+import static net.minecraftforge.common.util.ForgeDirection.UP;
+import static net.minecraftforge.common.util.ForgeDirection.WEST;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.AxisAlignedBB;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import cofh.api.energy.IEnergyReceiver;
+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.graphs.Node;
+import gregtech.api.graphs.NodeList;
+import gregtech.api.graphs.PowerNode;
+import gregtech.api.graphs.PowerNodes;
+import gregtech.api.graphs.consumers.ConsumerNode;
+import gregtech.api.graphs.paths.PowerNodePath;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IConnectable;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntityCable;
+import gregtech.api.interfaces.tileentity.ICoverable;
+import gregtech.api.interfaces.tileentity.IEnergyConnected;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.logic.interfaces.PowerLogicHost;
+import gregtech.api.metatileentity.BaseMetaPipeEntity;
+import gregtech.api.metatileentity.MetaPipeEntity;
+import gregtech.api.objects.GT_Cover_None;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_CoverBehavior;
+import gregtech.api.util.GT_CoverBehaviorBase;
+import gregtech.api.util.GT_GC_Compat;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.ISerializableObject;
+import gregtech.common.GT_Client;
+import gregtech.common.covers.CoverInfo;
+import gregtech.common.covers.GT_Cover_SolarPanel;
+import ic2.api.energy.EnergyNet;
+import ic2.api.energy.tile.IEnergyEmitter;
+import ic2.api.energy.tile.IEnergySink;
+import ic2.api.energy.tile.IEnergySource;
+import ic2.api.energy.tile.IEnergyTile;
+import ic2.api.reactor.IReactorChamber;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+public class GT_MetaPipeEntity_Cable extends MetaPipeEntity implements IMetaTileEntityCable {
+
+ public final float mThickNess;
+ public final Materials mMaterial;
+ public final long mCableLossPerMeter, mAmperage, mVoltage;
+ public final boolean mInsulated, mCanShock;
+
+ public int mTransferredAmperage = 0;
+
+ public int mOverheat;
+ public static short mMaxOverheat = (short) (GT_Mod.gregtechproxy.mWireHeatingTicks * 100);
+
+ public GT_MetaPipeEntity_Cable(int aID, String aName, String aNameRegional, float aThickNess, Materials aMaterial,
+ long aCableLossPerMeter, long aAmperage, long aVoltage, boolean aInsulated, boolean aCanShock) {
+ super(aID, aName, aNameRegional, 0);
+ mThickNess = aThickNess;
+ mMaterial = aMaterial;
+ mAmperage = aAmperage;
+ mVoltage = aVoltage;
+ mInsulated = aInsulated;
+ mCanShock = aCanShock;
+ mCableLossPerMeter = aCableLossPerMeter;
+ }
+
+ public GT_MetaPipeEntity_Cable(String aName, float aThickNess, Materials aMaterial, long aCableLossPerMeter,
+ long aAmperage, long aVoltage, boolean aInsulated, boolean aCanShock) {
+ super(aName, 0);
+ mThickNess = aThickNess;
+ mMaterial = aMaterial;
+ mAmperage = aAmperage;
+ mVoltage = aVoltage;
+ mInsulated = aInsulated;
+ mCanShock = aCanShock;
+ mCableLossPerMeter = aCableLossPerMeter;
+ }
+
+ @Override
+ public byte getTileEntityBaseType() {
+ return (byte) (mInsulated ? 9 : 8);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaPipeEntity_Cable(
+ mName,
+ mThickNess,
+ mMaterial,
+ mCableLossPerMeter,
+ mAmperage,
+ mVoltage,
+ mInsulated,
+ mCanShock);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection,
+ int facingDirection, int colorIndex, boolean active, boolean redstoneLevel) {
+ if (!mInsulated) return new ITexture[] { TextureFactory
+ .of(mMaterial.mIconSet.mTextures[TextureSet.INDEX_wire], Dyes.getModulation(colorIndex, mMaterial.mRGBa)) };
+ if (active) {
+ float tThickNess = getThickNess();
+ if (tThickNess < 0.124F) return new ITexture[] { TextureFactory
+ .of(Textures.BlockIcons.INSULATION_FULL, Dyes.getModulation(colorIndex, Dyes.CABLE_INSULATION.mRGBa)) };
+ if (tThickNess < 0.374F) // 0.375 x1
+ return new ITexture[] {
+ TextureFactory.of(mMaterial.mIconSet.mTextures[TextureSet.INDEX_wire], mMaterial.mRGBa),
+ TextureFactory.of(
+ Textures.BlockIcons.INSULATION_TINY,
+ Dyes.getModulation(colorIndex, Dyes.CABLE_INSULATION.mRGBa)) };
+ if (tThickNess < 0.499F) // 0.500 x2
+ return new ITexture[] {
+ TextureFactory.of(mMaterial.mIconSet.mTextures[TextureSet.INDEX_wire], mMaterial.mRGBa),
+ TextureFactory.of(
+ Textures.BlockIcons.INSULATION_SMALL,
+ Dyes.getModulation(colorIndex, Dyes.CABLE_INSULATION.mRGBa)) };
+ if (tThickNess < 0.624F) // 0.625 x4
+ return new ITexture[] {
+ TextureFactory.of(mMaterial.mIconSet.mTextures[TextureSet.INDEX_wire], mMaterial.mRGBa),
+ TextureFactory.of(
+ Textures.BlockIcons.INSULATION_MEDIUM,
+ Dyes.getModulation(colorIndex, Dyes.CABLE_INSULATION.mRGBa)) };
+ if (tThickNess < 0.749F) // 0.750 x8
+ return new ITexture[] {
+ TextureFactory.of(mMaterial.mIconSet.mTextures[TextureSet.INDEX_wire], mMaterial.mRGBa),
+ TextureFactory.of(
+ Textures.BlockIcons.INSULATION_MEDIUM_PLUS,
+ Dyes.getModulation(colorIndex, Dyes.CABLE_INSULATION.mRGBa)) };
+ if (tThickNess < 0.874F) // 0.825 x12
+ return new ITexture[] {
+ TextureFactory.of(mMaterial.mIconSet.mTextures[TextureSet.INDEX_wire], mMaterial.mRGBa),
+ TextureFactory.of(
+ Textures.BlockIcons.INSULATION_LARGE,
+ Dyes.getModulation(colorIndex, Dyes.CABLE_INSULATION.mRGBa)) };
+ return new ITexture[] {
+ TextureFactory.of(mMaterial.mIconSet.mTextures[TextureSet.INDEX_wire], mMaterial.mRGBa),
+ TextureFactory.of(
+ Textures.BlockIcons.INSULATION_HUGE,
+ Dyes.getModulation(colorIndex, Dyes.CABLE_INSULATION.mRGBa)) };
+ }
+ return new ITexture[] { TextureFactory
+ .of(Textures.BlockIcons.INSULATION_FULL, Dyes.getModulation(colorIndex, Dyes.CABLE_INSULATION.mRGBa)) };
+ }
+
+ @Override
+ public void onEntityCollidedWithBlock(World aWorld, int aX, int aY, int aZ, Entity aEntity) {
+
+ if (!mCanShock) return;
+
+ final BaseMetaPipeEntity baseEntity = (BaseMetaPipeEntity) getBaseMetaTileEntity();
+
+ if (!(aEntity instanceof EntityLivingBase livingEntity)) return;
+ if (!(baseEntity.getNodePath() instanceof PowerNodePath powerPath)) return;
+
+ if (isCoverOnSide(baseEntity, livingEntity)) return;
+ if ((baseEntity.mConnections & IConnectable.HAS_HARDENEDFOAM) == 1) return;
+
+ final long amperage = powerPath.getAmperage();
+ final long voltage = powerPath.getVoltage();
+
+ if (amperage == 0L) return;
+
+ GT_Utility.applyElectricityDamage(livingEntity, voltage, amperage);
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return true;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return false;
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return true;
+ }
+
+ @Override
+ public final boolean renderInside(ForgeDirection side) {
+ return false;
+ }
+
+ @Override
+ public int getProgresstime() {
+ return (int) mTransferredAmperage * 64;
+ }
+
+ @Override
+ public int maxProgresstime() {
+ return (int) mAmperage * 64;
+ }
+
+ @Override
+ public long injectEnergyUnits(ForgeDirection side, long voltage, long amperage) {
+ if (!isConnectedAtSide(side) && side != ForgeDirection.UNKNOWN) return 0;
+ if (!getBaseMetaTileEntity().getCoverInfoAtSide(side)
+ .letsEnergyIn()) return 0;
+ return transferElectricity(side, voltage, amperage, (HashSet<TileEntity>) null);
+ }
+
+ @Override
+ @Deprecated
+ public long transferElectricity(ForgeDirection side, long aVoltage, long aAmperage,
+ ArrayList<TileEntity> aAlreadyPassedTileEntityList) {
+ return transferElectricity(side, aVoltage, aAmperage, new HashSet<>(aAlreadyPassedTileEntityList));
+ }
+
+ @Override
+ public long transferElectricity(ForgeDirection side, long voltage, long amperage,
+ HashSet<TileEntity> alreadyPassedSet) {
+ if (!getBaseMetaTileEntity().isServerSide() || !isConnectedAtSide(side) && side != ForgeDirection.UNKNOWN)
+ return 0;
+ final BaseMetaPipeEntity tBase = (BaseMetaPipeEntity) getBaseMetaTileEntity();
+ if (!(tBase.getNode() instanceof PowerNode tNode)) return 0;
+ int tPlace = 0;
+ final Node[] tToPower = new Node[tNode.mConsumers.size()];
+ if (tNode.mHadVoltage) {
+ for (ConsumerNode consumer : tNode.mConsumers) {
+ if (consumer.needsEnergy()) tToPower[tPlace++] = consumer;
+ }
+ } else {
+ tNode.mHadVoltage = true;
+ for (ConsumerNode consumer : tNode.mConsumers) {
+ tToPower[tPlace++] = consumer;
+ }
+ }
+ return PowerNodes.powerNode(tNode, null, new NodeList(tToPower), (int) voltage, (int) amperage);
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ if (aTick % 20 == 0 && aBaseMetaTileEntity.isServerSide()
+ && (!GT_Mod.gregtechproxy.gt6Cable || mCheckConnections)) {
+ checkConnections();
+ }
+ }
+
+ @Override
+ public boolean onWireCutterRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer,
+ float aX, float aY, float aZ) {
+ if (GT_Mod.gregtechproxy.gt6Cable
+ && GT_ModHandler.damageOrDechargeItem(aPlayer.inventory.getCurrentItem(), 1, 500, aPlayer)) {
+ if (isConnectedAtSide(wrenchingSide)) {
+ disconnect(wrenchingSide);
+ GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("215", "Disconnected"));
+ } else if (!GT_Mod.gregtechproxy.costlyCableConnection) {
+ if (connect(wrenchingSide) > 0)
+ GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("214", "Connected"));
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onSolderingToolRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer,
+ float aX, float aY, float aZ) {
+ if (GT_Mod.gregtechproxy.gt6Cable
+ && GT_ModHandler.damageOrDechargeItem(aPlayer.inventory.getCurrentItem(), 1, 500, aPlayer)) {
+ if (isConnectedAtSide(wrenchingSide)) {
+ disconnect(wrenchingSide);
+ GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("215", "Disconnected"));
+ } else if (!GT_Mod.gregtechproxy.costlyCableConnection || GT_ModHandler.consumeSolderingMaterial(aPlayer)) {
+ if (connect(wrenchingSide) > 0)
+ GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("214", "Connected"));
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean letsIn(GT_CoverBehavior coverBehavior, ForgeDirection side, int aCoverID, int aCoverVariable,
+ ICoverable aTileEntity) {
+ return coverBehavior.letsEnergyIn(side, aCoverID, aCoverVariable, aTileEntity);
+ }
+
+ @Override
+ public boolean letsOut(GT_CoverBehavior coverBehavior, ForgeDirection side, int aCoverID, int aCoverVariable,
+ ICoverable aTileEntity) {
+ return coverBehavior.letsEnergyOut(side, aCoverID, aCoverVariable, aTileEntity);
+ }
+
+ @Override
+ public boolean letsIn(GT_CoverBehaviorBase<?> coverBehavior, ForgeDirection side, int aCoverID,
+ ISerializableObject aCoverVariable, ICoverable aTileEntity) {
+ return coverBehavior.letsEnergyIn(side, aCoverID, aCoverVariable, aTileEntity);
+ }
+
+ @Override
+ public boolean letsOut(GT_CoverBehaviorBase<?> coverBehavior, ForgeDirection side, int aCoverID,
+ ISerializableObject aCoverVariable, ICoverable aTileEntity) {
+ return coverBehavior.letsEnergyOut(side, aCoverID, aCoverVariable, aTileEntity);
+ }
+
+ @Override
+ public boolean letsIn(CoverInfo coverInfo) {
+ return coverInfo.letsEnergyIn();
+ }
+
+ @Override
+ public boolean letsOut(CoverInfo coverInfo) {
+ return coverInfo.letsEnergyOut();
+ }
+
+ @Override
+ public boolean canConnect(ForgeDirection side, TileEntity tileEntity) {
+ final IGregTechTileEntity baseMetaTile = getBaseMetaTileEntity();
+ final GT_CoverBehaviorBase<?> coverBehavior = baseMetaTile.getCoverBehaviorAtSideNew(side);
+ final ForgeDirection oppositeSide = side.getOpposite();
+
+ // GT Machine handling
+ if ((tileEntity instanceof PowerLogicHost powerLogic && powerLogic.getPowerLogic(oppositeSide) != null)
+ || ((tileEntity instanceof IEnergyConnected energyConnected)
+ && (energyConnected.inputEnergyFrom(oppositeSide, false)
+ || energyConnected.outputsEnergyTo(oppositeSide, false))))
+ return true;
+
+ // Solar Panel Compat
+ if (coverBehavior instanceof GT_Cover_SolarPanel) return true;
+
+ // ((tIsGregTechTileEntity && tIsTileEntityCable) && (tAlwaysLookConnected || tLetEnergyIn || tLetEnergyOut) )
+ // --> Not needed
+ if (GalacticraftCore.isModLoaded() && GT_GC_Compat.canConnect(tileEntity, oppositeSide)) return true;
+
+ // AE2-p2p Compat
+ if (tileEntity instanceof appeng.tile.powersink.IC2 ic2sink
+ && ic2sink.acceptsEnergyFrom((TileEntity) baseMetaTile, oppositeSide)) return true;
+
+ // IC2 Compat
+ {
+ final TileEntity ic2Energy;
+
+ if (tileEntity instanceof IReactorChamber)
+ ic2Energy = (TileEntity) ((IReactorChamber) tileEntity).getReactor();
+ else ic2Energy = (tileEntity == null || tileEntity instanceof IEnergyTile || EnergyNet.instance == null)
+ ? tileEntity
+ : EnergyNet.instance
+ .getTileEntity(tileEntity.getWorldObj(), tileEntity.xCoord, tileEntity.yCoord, tileEntity.zCoord);
+
+ // IC2 Sink Compat
+ if ((ic2Energy instanceof IEnergySink)
+ && ((IEnergySink) ic2Energy).acceptsEnergyFrom((TileEntity) baseMetaTile, oppositeSide)) return true;
+
+ // IC2 Source Compat
+ if (GT_Mod.gregtechproxy.ic2EnergySourceCompat && (ic2Energy instanceof IEnergySource)) {
+ if (((IEnergySource) ic2Energy).emitsEnergyTo((TileEntity) baseMetaTile, oppositeSide)) {
+ return true;
+ }
+ }
+ }
+ // RF Output Compat
+ if (GregTech_API.mOutputRF && tileEntity instanceof IEnergyReceiver
+ && ((IEnergyReceiver) tileEntity).canConnectEnergy(oppositeSide)) return true;
+
+ // RF Input Compat
+ return GregTech_API.mInputRF && (tileEntity instanceof IEnergyEmitter
+ && ((IEnergyEmitter) tileEntity).emitsEnergyTo((TileEntity) baseMetaTile, oppositeSide));
+ }
+
+ @Override
+ public boolean getGT6StyleConnection() {
+ // Yes if GT6 Cables are enabled
+ return GT_Mod.gregtechproxy.gt6Cable;
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public String[] getDescription() {
+ return new String[] {
+ StatCollector.translateToLocal("GT5U.item.cable.max_voltage") + ": %%%"
+ + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(mVoltage)
+ + " ("
+ + GT_Utility.getColoredTierNameFromVoltage(mVoltage)
+ + EnumChatFormatting.GREEN
+ + ")"
+ + EnumChatFormatting.GRAY,
+ StatCollector.translateToLocal("GT5U.item.cable.max_amperage") + ": %%%"
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(mAmperage)
+ + EnumChatFormatting.GRAY,
+ StatCollector.translateToLocal("GT5U.item.cable.loss") + ": %%%"
+ + EnumChatFormatting.RED
+ + GT_Utility.formatNumbers(mCableLossPerMeter)
+ + EnumChatFormatting.GRAY
+ + "%%% "
+ + StatCollector.translateToLocal("GT5U.item.cable.eu_volt") };
+ }
+
+ @Override
+ public float getThickNess() {
+ if (GT_Mod.instance.isClientSide() && (GT_Client.hideValue & 0x1) != 0) return 0.0625F;
+ return mThickNess;
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ if (GT_Mod.gregtechproxy.gt6Cable) aNBT.setByte("mConnections", mConnections);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ if (GT_Mod.gregtechproxy.gt6Cable) {
+ mConnections = aNBT.getByte("mConnections");
+ }
+ }
+
+ @Override
+ public boolean isGivingInformation() {
+ return true;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ final BaseMetaPipeEntity base = (BaseMetaPipeEntity) getBaseMetaTileEntity();
+ final PowerNodePath path = (PowerNodePath) base.getNodePath();
+
+ if (path == null)
+ return new String[] { EnumChatFormatting.RED + "Failed to get Power Node info" + EnumChatFormatting.RESET };
+
+ final long currAmp = path.getAmperage();
+ final long currVoltage = path.getVoltage();
+
+ final double avgAmp = path.getAvgAmperage();
+ final double avgVoltage = path.getAvgVoltage();
+
+ final long maxVoltageOut = (mVoltage - mCableLossPerMeter) * mAmperage;
+
+ return new String[] {
+ "Heat: " + EnumChatFormatting.RED
+ + GT_Utility.formatNumbers(mOverheat)
+ + EnumChatFormatting.RESET
+ + " / "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(mMaxOverheat)
+ + EnumChatFormatting.RESET,
+ "Amperage: " + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(currAmp)
+ + EnumChatFormatting.RESET
+ + " / "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(mAmperage)
+ + EnumChatFormatting.RESET
+ + " A",
+ "Voltage Out: " + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(currVoltage)
+ + EnumChatFormatting.RESET
+ + " / "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(maxVoltageOut)
+ + EnumChatFormatting.RESET
+ + " EU/t",
+ "Avg Amperage (20t): " + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(avgAmp)
+ + EnumChatFormatting.RESET
+ + " A",
+ "Avg Output (20t): " + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(avgVoltage)
+ + EnumChatFormatting.RESET
+ + " EU/t" };
+ }
+
+ @Override
+ public AxisAlignedBB getCollisionBoundingBoxFromPool(World aWorld, int aX, int aY, int aZ) {
+ if (GT_Mod.instance.isClientSide() && (GT_Client.hideValue & 0x2) != 0)
+ return AxisAlignedBB.getBoundingBox(aX, aY, aZ, aX + 1, aY + 1, aZ + 1);
+ else return getActualCollisionBoundingBoxFromPool(aWorld, aX, aY, aZ);
+ }
+
+ private AxisAlignedBB getActualCollisionBoundingBoxFromPool(World aWorld, int aX, int aY, int aZ) {
+ float tSpace = (1f - mThickNess) / 2;
+ float spaceDown = tSpace;
+ float spaceUp = 1f - tSpace;
+ float spaceNorth = tSpace;
+ float spaceSouth = 1f - tSpace;
+ float spaceWest = tSpace;
+ float spaceEast = 1f - tSpace;
+
+ if (getBaseMetaTileEntity().getCoverIDAtSide(DOWN) != 0) {
+ spaceDown = spaceNorth = spaceWest = 0;
+ spaceSouth = spaceEast = 1;
+ }
+ if (getBaseMetaTileEntity().getCoverIDAtSide(UP) != 0) {
+ spaceNorth = spaceWest = 0;
+ spaceUp = spaceSouth = spaceEast = 1;
+ }
+ if (getBaseMetaTileEntity().getCoverIDAtSide(NORTH) != 0) {
+ spaceDown = spaceNorth = spaceWest = 0;
+ spaceUp = spaceEast = 1;
+ }
+ if (getBaseMetaTileEntity().getCoverIDAtSide(SOUTH) != 0) {
+ spaceDown = spaceWest = 0;
+ spaceUp = spaceSouth = spaceEast = 1;
+ }
+ if (getBaseMetaTileEntity().getCoverIDAtSide(WEST) != 0) {
+ spaceDown = spaceNorth = spaceWest = 0;
+ spaceUp = spaceSouth = 1;
+ }
+ if (getBaseMetaTileEntity().getCoverIDAtSide(EAST) != 0) {
+ spaceDown = spaceNorth = 0;
+ spaceUp = spaceSouth = spaceEast = 1;
+ }
+
+ byte tConn = ((BaseMetaPipeEntity) getBaseMetaTileEntity()).mConnections;
+ if ((tConn & DOWN.flag) != 0) spaceDown = 0f;
+ if ((tConn & UP.flag) != 0) spaceUp = 1f;
+ if ((tConn & NORTH.flag) != 0) spaceNorth = 0f;
+ if ((tConn & SOUTH.flag) != 0) spaceSouth = 1f;
+ if ((tConn & WEST.flag) != 0) spaceWest = 0f;
+ if ((tConn & EAST.flag) != 0) spaceEast = 1f;
+
+ return AxisAlignedBB.getBoundingBox(
+ aX + spaceWest,
+ aY + spaceDown,
+ aZ + spaceNorth,
+ aX + spaceEast,
+ aY + spaceUp,
+ aZ + spaceSouth);
+ }
+
+ @Override
+ public void addCollisionBoxesToList(World aWorld, int aX, int aY, int aZ, AxisAlignedBB inputAABB,
+ List<AxisAlignedBB> outputAABB, Entity collider) {
+ super.addCollisionBoxesToList(aWorld, aX, aY, aZ, inputAABB, outputAABB, collider);
+ if (GT_Mod.instance.isClientSide() && (GT_Client.hideValue & 0x2) != 0) {
+ final AxisAlignedBB aabb = getActualCollisionBoundingBoxFromPool(aWorld, aX, aY, aZ);
+ if (inputAABB.intersectsWith(aabb)) outputAABB.add(aabb);
+ }
+ }
+
+ @Override
+ public boolean shouldJoinIc2Enet() {
+ if (!GT_Mod.gregtechproxy.ic2EnergySourceCompat) return false;
+
+ if (mConnections != 0) {
+ final IGregTechTileEntity baseMeta = getBaseMetaTileEntity();
+ for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
+ if (isConnectedAtSide(side)) {
+ final TileEntity tTileEntity = baseMeta.getTileEntityAtSide(side);
+ final TileEntity tEmitter = (tTileEntity == null || tTileEntity instanceof IEnergyTile
+ || EnergyNet.instance == null)
+ ? tTileEntity
+ : EnergyNet.instance.getTileEntity(
+ tTileEntity.getWorldObj(),
+ tTileEntity.xCoord,
+ tTileEntity.yCoord,
+ tTileEntity.zCoord);
+
+ if (tEmitter instanceof IEnergyEmitter) return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void reloadLocks() {
+ final BaseMetaPipeEntity pipe = (BaseMetaPipeEntity) getBaseMetaTileEntity();
+ if (pipe.getNode() != null) {
+ for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
+ if (isConnectedAtSide(side)) {
+ final CoverInfo coverInfo = pipe.getCoverInfoAtSide(side);
+ if (coverInfo.getCoverBehavior() instanceof GT_Cover_None) continue;
+ if (!letsIn(coverInfo) || !letsOut(coverInfo)) {
+ pipe.addToLock(pipe, side);
+ } else {
+ pipe.removeFromLock(pipe, side);
+ }
+ }
+ }
+ } else {
+ boolean dontAllow = false;
+ for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
+ if (isConnectedAtSide(side)) {
+ final CoverInfo coverInfo = pipe.getCoverInfoAtSide(side);
+ if (coverInfo.getCoverBehavior() instanceof GT_Cover_None) continue;
+
+ if (!letsIn(coverInfo) || !letsOut(coverInfo)) {
+ dontAllow = true;
+ }
+ }
+ }
+ if (dontAllow) {
+ pipe.addToLock(pipe, DOWN);
+ } else {
+ pipe.removeFromLock(pipe, DOWN);
+ }
+ }
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+
+ currenttip.add(
+ StatCollector.translateToLocal("GT5U.item.cable.max_voltage") + ": "
+ + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(mVoltage)
+ + " ("
+ + GT_Utility.getColoredTierNameFromVoltage(mVoltage)
+ + EnumChatFormatting.GREEN
+ + ")");
+ currenttip.add(
+ StatCollector.translateToLocal("GT5U.item.cable.max_amperage") + ": "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(mAmperage));
+ currenttip.add(
+ StatCollector.translateToLocal("GT5U.item.cable.loss") + ": "
+ + EnumChatFormatting.RED
+ + GT_Utility.formatNumbers(mCableLossPerMeter)
+ + EnumChatFormatting.RESET
+ + " "
+ + StatCollector.translateToLocal("GT5U.item.cable.eu_volt"));
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Fluid.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Fluid.java
new file mode 100644
index 0000000000..d94a7c0ac2
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Fluid.java
@@ -0,0 +1,980 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.GT_Values.ALL_VALID_SIDES;
+import static gregtech.api.enums.GT_Values.D1;
+import static gregtech.api.enums.Mods.TinkerConstruct;
+import static gregtech.api.enums.Mods.Translocator;
+import static gregtech.api.metatileentity.implementations.GT_MetaPipeEntity_Fluid.Border.BOTTOM;
+import static gregtech.api.metatileentity.implementations.GT_MetaPipeEntity_Fluid.Border.LEFT;
+import static gregtech.api.metatileentity.implementations.GT_MetaPipeEntity_Fluid.Border.RIGHT;
+import static gregtech.api.metatileentity.implementations.GT_MetaPipeEntity_Fluid.Border.TOP;
+import static gregtech.api.objects.XSTR.XSTR_INSTANCE;
+import static net.minecraftforge.common.util.ForgeDirection.DOWN;
+import static net.minecraftforge.common.util.ForgeDirection.EAST;
+import static net.minecraftforge.common.util.ForgeDirection.NORTH;
+import static net.minecraftforge.common.util.ForgeDirection.SOUTH;
+import static net.minecraftforge.common.util.ForgeDirection.UP;
+import static net.minecraftforge.common.util.ForgeDirection.WEST;
+
+import java.util.ArrayList;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.AxisAlignedBB;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.fluids.FluidTankInfo;
+import net.minecraftforge.fluids.IFluidHandler;
+
+import org.apache.commons.lang3.tuple.MutableTriple;
+
+import cpw.mods.fml.common.Optional;
+import gregtech.GT_Mod;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.ConfigCategories;
+import gregtech.api.enums.Dyes;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.Mods;
+import gregtech.api.enums.OrePrefixes;
+import gregtech.api.enums.ParticleFX;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.enums.Textures;
+import gregtech.api.enums.ToolModes;
+import gregtech.api.interfaces.IIconContainer;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.ICoverable;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.items.GT_MetaGenerated_Tool;
+import gregtech.api.metatileentity.BaseMetaPipeEntity;
+import gregtech.api.metatileentity.MetaPipeEntity;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_CoverBehavior;
+import gregtech.api.util.GT_CoverBehaviorBase;
+import gregtech.api.util.GT_Log;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.ISerializableObject;
+import gregtech.api.util.WorldSpawnedEventBuilder.ParticleEventBuilder;
+import gregtech.common.GT_Client;
+import gregtech.common.covers.CoverInfo;
+import gregtech.common.covers.GT_Cover_Drain;
+import gregtech.common.covers.GT_Cover_FluidRegulator;
+
+public class GT_MetaPipeEntity_Fluid extends MetaPipeEntity {
+
+ protected static final EnumMap<ForgeDirection, EnumMap<Border, ForgeDirection>> FACE_BORDER_MAP = new EnumMap<>(
+ ForgeDirection.class);
+
+ static {
+ FACE_BORDER_MAP.put(DOWN, borderMap(NORTH, SOUTH, EAST, WEST));
+ FACE_BORDER_MAP.put(UP, borderMap(NORTH, SOUTH, WEST, EAST));
+ FACE_BORDER_MAP.put(NORTH, borderMap(UP, DOWN, EAST, WEST));
+ FACE_BORDER_MAP.put(SOUTH, borderMap(UP, DOWN, WEST, EAST));
+ FACE_BORDER_MAP.put(WEST, borderMap(UP, DOWN, NORTH, SOUTH));
+ FACE_BORDER_MAP.put(EAST, borderMap(UP, DOWN, SOUTH, NORTH));
+ }
+
+ protected static final Map<Integer, IIconContainer> RESTR_TEXTURE_MAP = new HashMap<>();
+
+ static {
+ RESTR_TEXTURE_MAP.put(TOP.mask, Textures.BlockIcons.PIPE_RESTRICTOR_UP);
+ RESTR_TEXTURE_MAP.put(BOTTOM.mask, Textures.BlockIcons.PIPE_RESTRICTOR_DOWN);
+ RESTR_TEXTURE_MAP.put(TOP.mask | BOTTOM.mask, Textures.BlockIcons.PIPE_RESTRICTOR_UD);
+ RESTR_TEXTURE_MAP.put(LEFT.mask, Textures.BlockIcons.PIPE_RESTRICTOR_LEFT);
+ RESTR_TEXTURE_MAP.put(TOP.mask | LEFT.mask, Textures.BlockIcons.PIPE_RESTRICTOR_UL);
+ RESTR_TEXTURE_MAP.put(BOTTOM.mask | LEFT.mask, Textures.BlockIcons.PIPE_RESTRICTOR_DL);
+ RESTR_TEXTURE_MAP.put(TOP.mask | BOTTOM.mask | LEFT.mask, Textures.BlockIcons.PIPE_RESTRICTOR_NR);
+ RESTR_TEXTURE_MAP.put(RIGHT.mask, Textures.BlockIcons.PIPE_RESTRICTOR_RIGHT);
+ RESTR_TEXTURE_MAP.put(TOP.mask | RIGHT.mask, Textures.BlockIcons.PIPE_RESTRICTOR_UR);
+ RESTR_TEXTURE_MAP.put(BOTTOM.mask | RIGHT.mask, Textures.BlockIcons.PIPE_RESTRICTOR_DR);
+ RESTR_TEXTURE_MAP.put(TOP.mask | BOTTOM.mask | RIGHT.mask, Textures.BlockIcons.PIPE_RESTRICTOR_NL);
+ RESTR_TEXTURE_MAP.put(LEFT.mask | RIGHT.mask, Textures.BlockIcons.PIPE_RESTRICTOR_LR);
+ RESTR_TEXTURE_MAP.put(TOP.mask | LEFT.mask | RIGHT.mask, Textures.BlockIcons.PIPE_RESTRICTOR_ND);
+ RESTR_TEXTURE_MAP.put(BOTTOM.mask | LEFT.mask | RIGHT.mask, Textures.BlockIcons.PIPE_RESTRICTOR_NU);
+ RESTR_TEXTURE_MAP.put(TOP.mask | BOTTOM.mask | LEFT.mask | RIGHT.mask, Textures.BlockIcons.PIPE_RESTRICTOR);
+ }
+
+ public final float mThickNess;
+ public final Materials mMaterial;
+ public final int mCapacity, mHeatResistance, mPipeAmount;
+ public final boolean mGasProof;
+ public final FluidStack[] mFluids;
+ public byte mLastReceivedFrom = 0, oLastReceivedFrom = 0;
+ /**
+ * Bitmask for whether disable fluid input form each side.
+ */
+ public byte mDisableInput = 0;
+
+ public GT_MetaPipeEntity_Fluid(int aID, String aName, String aNameRegional, float aThickNess, Materials aMaterial,
+ int aCapacity, int aHeatResistance, boolean aGasProof) {
+ this(aID, aName, aNameRegional, aThickNess, aMaterial, aCapacity, aHeatResistance, aGasProof, 1);
+ }
+
+ public GT_MetaPipeEntity_Fluid(int aID, String aName, String aNameRegional, float aThickNess, Materials aMaterial,
+ int aCapacity, int aHeatResistance, boolean aGasProof, int aFluidTypes) {
+ super(aID, aName, aNameRegional, 0, false);
+ mThickNess = aThickNess;
+ mMaterial = aMaterial;
+ mCapacity = aCapacity;
+ mGasProof = aGasProof;
+ mHeatResistance = aHeatResistance;
+ mPipeAmount = aFluidTypes;
+ mFluids = new FluidStack[mPipeAmount];
+ addInfo(aID);
+ }
+
+ public GT_MetaPipeEntity_Fluid(String aName, float aThickNess, Materials aMaterial, int aCapacity,
+ int aHeatResistance, boolean aGasProof, int aFluidTypes) {
+ super(aName, 0);
+ mThickNess = aThickNess;
+ mMaterial = aMaterial;
+ mCapacity = aCapacity;
+ mGasProof = aGasProof;
+ mHeatResistance = aHeatResistance;
+ mPipeAmount = aFluidTypes;
+ mFluids = new FluidStack[mPipeAmount];
+ }
+
+ @Override
+ public byte getTileEntityBaseType() {
+ return (byte) (mMaterial == null ? 4 : (byte) (4) + Math.max(0, Math.min(3, mMaterial.mToolQuality)));
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaPipeEntity_Fluid(
+ mName,
+ mThickNess,
+ mMaterial,
+ mCapacity,
+ mHeatResistance,
+ mGasProof,
+ mPipeAmount);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, int aConnections,
+ int colorIndex, boolean aConnected, boolean redstoneLevel) {
+ if (side == ForgeDirection.UNKNOWN) return Textures.BlockIcons.ERROR_RENDERING;
+ final float tThickNess = getThickNess();
+ if (mDisableInput == 0)
+ return new ITexture[] { aConnected ? getBaseTexture(tThickNess, mPipeAmount, mMaterial, colorIndex)
+ : TextureFactory.of(
+ mMaterial.mIconSet.mTextures[OrePrefixes.pipe.mTextureIndex],
+ Dyes.getModulation(colorIndex, mMaterial.mRGBa)) };
+ int borderMask = 0;
+ for (Border border : Border.values()) {
+ if (isInputDisabledAtSide(getSideAtBorder(side, border))) borderMask |= border.mask;
+ }
+
+ return new ITexture[] { aConnected ? getBaseTexture(tThickNess, mPipeAmount, mMaterial, colorIndex)
+ : TextureFactory.of(
+ mMaterial.mIconSet.mTextures[OrePrefixes.pipe.mTextureIndex],
+ Dyes.getModulation(colorIndex, mMaterial.mRGBa)),
+ getRestrictorTexture(borderMask) };
+ }
+
+ protected static ITexture getBaseTexture(float aThickNess, int aPipeAmount, Materials aMaterial, int colorIndex) {
+ if (aPipeAmount >= 9) return TextureFactory.of(
+ aMaterial.mIconSet.mTextures[OrePrefixes.pipeNonuple.mTextureIndex],
+ Dyes.getModulation(colorIndex, aMaterial.mRGBa));
+ if (aPipeAmount >= 4) return TextureFactory.of(
+ aMaterial.mIconSet.mTextures[OrePrefixes.pipeQuadruple.mTextureIndex],
+ Dyes.getModulation(colorIndex, aMaterial.mRGBa));
+ if (aThickNess < 0.124F) return TextureFactory.of(
+ aMaterial.mIconSet.mTextures[OrePrefixes.pipe.mTextureIndex],
+ Dyes.getModulation(colorIndex, aMaterial.mRGBa));
+ if (aThickNess < 0.374F) return TextureFactory.of(
+ aMaterial.mIconSet.mTextures[OrePrefixes.pipeTiny.mTextureIndex],
+ Dyes.getModulation(colorIndex, aMaterial.mRGBa));
+ if (aThickNess < 0.499F) return TextureFactory.of(
+ aMaterial.mIconSet.mTextures[OrePrefixes.pipeSmall.mTextureIndex],
+ Dyes.getModulation(colorIndex, aMaterial.mRGBa));
+ if (aThickNess < 0.749F) return TextureFactory.of(
+ aMaterial.mIconSet.mTextures[OrePrefixes.pipeMedium.mTextureIndex],
+ Dyes.getModulation(colorIndex, aMaterial.mRGBa));
+ if (aThickNess < 0.874F) return TextureFactory.of(
+ aMaterial.mIconSet.mTextures[OrePrefixes.pipeLarge.mTextureIndex],
+ Dyes.getModulation(colorIndex, aMaterial.mRGBa));
+ return TextureFactory.of(
+ aMaterial.mIconSet.mTextures[OrePrefixes.pipeHuge.mTextureIndex],
+ Dyes.getModulation(colorIndex, aMaterial.mRGBa));
+ }
+
+ protected static ITexture getRestrictorTexture(int borderMask) {
+ final IIconContainer restrictorIcon = RESTR_TEXTURE_MAP.get(borderMask);
+ return restrictorIcon != null ? TextureFactory.of(restrictorIcon) : null;
+ }
+
+ @Override
+ public void onValueUpdate(byte aValue) {
+ mDisableInput = aValue;
+ }
+
+ @Override
+ public byte getUpdateData() {
+ return mDisableInput;
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return true;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return false;
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return false;
+ }
+
+ @Override
+ public final boolean renderInside(ForgeDirection side) {
+ return false;
+ }
+
+ @Override
+ public int getProgresstime() {
+ return getFluidAmount();
+ }
+
+ @Override
+ public int maxProgresstime() {
+ return getCapacity();
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ for (int i = 0; i < mPipeAmount; i++) if (mFluids[i] != null)
+ aNBT.setTag("mFluid" + (i == 0 ? "" : i), mFluids[i].writeToNBT(new NBTTagCompound()));
+ aNBT.setByte("mLastReceivedFrom", mLastReceivedFrom);
+ if (GT_Mod.gregtechproxy.gt6Pipe) {
+ aNBT.setByte("mConnections", mConnections);
+ aNBT.setByte("mDisableInput", mDisableInput);
+ }
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ for (int i = 0; i < mPipeAmount; i++)
+ mFluids[i] = FluidStack.loadFluidStackFromNBT(aNBT.getCompoundTag("mFluid" + (i == 0 ? "" : i)));
+ mLastReceivedFrom = aNBT.getByte("mLastReceivedFrom");
+ if (GT_Mod.gregtechproxy.gt6Pipe) {
+ mConnections = aNBT.getByte("mConnections");
+ mDisableInput = aNBT.getByte("mDisableInput");
+ }
+ }
+
+ @Override
+ public void onEntityCollidedWithBlock(World aWorld, int aX, int aY, int aZ, Entity aEntity) {
+ if ((((BaseMetaPipeEntity) getBaseMetaTileEntity()).mConnections & -128) == 0
+ && aEntity instanceof EntityLivingBase) {
+ for (FluidStack tFluid : mFluids) {
+ if (tFluid != null) {
+ final int tTemperature = tFluid.getFluid()
+ .getTemperature(tFluid);
+ if (tTemperature > 320
+ && !isCoverOnSide((BaseMetaPipeEntity) getBaseMetaTileEntity(), (EntityLivingBase) aEntity)) {
+ GT_Utility.applyHeatDamage((EntityLivingBase) aEntity, (tTemperature - 300) / 50.0F);
+ break;
+ } else if (tTemperature < 260
+ && !isCoverOnSide((BaseMetaPipeEntity) getBaseMetaTileEntity(), (EntityLivingBase) aEntity)) {
+ GT_Utility.applyFrostDamage((EntityLivingBase) aEntity, (270 - tTemperature) / 25.0F);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ if (aBaseMetaTileEntity.isServerSide() && aTick % 5 == 0) {
+ mLastReceivedFrom &= 63;
+ if (mLastReceivedFrom == 63) {
+ mLastReceivedFrom = 0;
+ }
+
+ if (!GT_Mod.gregtechproxy.gt6Pipe || mCheckConnections) checkConnections();
+
+ final boolean shouldDistribute = (oLastReceivedFrom == mLastReceivedFrom);
+ for (int i = 0, j = aBaseMetaTileEntity.getRandomNumber(mPipeAmount); i < mPipeAmount; i++) {
+ final int index = (i + j) % mPipeAmount;
+ if (mFluids[index] != null && mFluids[index].amount <= 0) mFluids[index] = null;
+ if (mFluids[index] == null) continue;
+
+ if (checkEnvironment(index, aBaseMetaTileEntity)) return;
+
+ if (shouldDistribute) {
+ distributeFluid(index, aBaseMetaTileEntity);
+ mLastReceivedFrom = 0;
+ }
+ }
+
+ oLastReceivedFrom = mLastReceivedFrom;
+ }
+ }
+
+ private boolean checkEnvironment(int index, IGregTechTileEntity aBaseMetaTileEntity) {
+ // Check for hot liquids that melt the pipe or gasses that escape and burn/freeze people
+ final FluidStack tFluid = mFluids[index];
+
+ if (tFluid != null && tFluid.amount > 0) {
+ final int tTemperature = tFluid.getFluid()
+ .getTemperature(tFluid);
+ if (tTemperature > mHeatResistance) {
+ if (aBaseMetaTileEntity.getRandomNumber(100) == 0) {
+ // Poof
+ GT_Log.exp.println(
+ "Set Pipe to Fire due to to low heat resistance at " + aBaseMetaTileEntity.getXCoord()
+ + " | "
+ + aBaseMetaTileEntity.getYCoord()
+ + " | "
+ + aBaseMetaTileEntity.getZCoord()
+ + " DIMID: "
+ + aBaseMetaTileEntity.getWorld().provider.dimensionId);
+ aBaseMetaTileEntity.setToFire();
+ return true;
+ }
+ // Mmhmm, Fire
+ aBaseMetaTileEntity.setOnFire();
+ GT_Log.exp.println(
+ "Set Blocks around Pipe to Fire due to to low heat resistance at " + aBaseMetaTileEntity.getXCoord()
+ + " | "
+ + aBaseMetaTileEntity.getYCoord()
+ + " | "
+ + aBaseMetaTileEntity.getZCoord()
+ + " DIMID: "
+ + aBaseMetaTileEntity.getWorld().provider.dimensionId);
+ }
+ if (!mGasProof && tFluid.getFluid()
+ .isGaseous(tFluid)) {
+ tFluid.amount -= 5;
+ sendSound((byte) 9);
+ if (tTemperature > 320) {
+ try {
+ for (EntityLivingBase tLiving : getBaseMetaTileEntity().getWorld()
+ .getEntitiesWithinAABB(
+ EntityLivingBase.class,
+ AxisAlignedBB.getBoundingBox(
+ getBaseMetaTileEntity().getXCoord() - 2,
+ getBaseMetaTileEntity().getYCoord() - 2,
+ getBaseMetaTileEntity().getZCoord() - 2,
+ getBaseMetaTileEntity().getXCoord() + 3,
+ getBaseMetaTileEntity().getYCoord() + 3,
+ getBaseMetaTileEntity().getZCoord() + 3))) {
+ GT_Utility.applyHeatDamage(tLiving, (tTemperature - 300) / 25.0F);
+ }
+ } catch (Throwable e) {
+ if (D1) e.printStackTrace(GT_Log.err);
+ }
+ } else if (tTemperature < 260) {
+ try {
+ for (EntityLivingBase tLiving : getBaseMetaTileEntity().getWorld()
+ .getEntitiesWithinAABB(
+ EntityLivingBase.class,
+ AxisAlignedBB.getBoundingBox(
+ getBaseMetaTileEntity().getXCoord() - 2,
+ getBaseMetaTileEntity().getYCoord() - 2,
+ getBaseMetaTileEntity().getZCoord() - 2,
+ getBaseMetaTileEntity().getXCoord() + 3,
+ getBaseMetaTileEntity().getYCoord() + 3,
+ getBaseMetaTileEntity().getZCoord() + 3))) {
+ GT_Utility.applyFrostDamage(tLiving, (270 - tTemperature) / 12.5F);
+ }
+ } catch (Throwable e) {
+ if (D1) e.printStackTrace(GT_Log.err);
+ }
+ }
+ }
+ if (tFluid.amount <= 0) mFluids[index] = null;
+ }
+ return false;
+ }
+
+ private void distributeFluid(int index, IGregTechTileEntity aBaseMetaTileEntity) {
+ final FluidStack tFluid = mFluids[index];
+ if (tFluid == null) return;
+
+ // Tank, From, Amount to receive
+ final List<MutableTriple<IFluidHandler, ForgeDirection, Integer>> tTanks = new ArrayList<>();
+ final int amount = tFluid.amount;
+ final byte tOffset = (byte) getBaseMetaTileEntity().getRandomNumber(6);
+ for (final byte i : ALL_VALID_SIDES) {
+ // Get a list of tanks accepting fluids, and what side they're on
+ final ForgeDirection side = ForgeDirection.getOrientation((i + tOffset) % 6);
+ final ForgeDirection oppositeSide = side.getOpposite();
+ final IFluidHandler tTank = aBaseMetaTileEntity.getITankContainerAtSide(side);
+ final IGregTechTileEntity gTank = tTank instanceof IGregTechTileEntity ? (IGregTechTileEntity) tTank : null;
+
+ if (isConnectedAtSide(side) && tTank != null
+ && (mLastReceivedFrom & side.flag) == 0
+ && getBaseMetaTileEntity().getCoverInfoAtSide(side)
+ .letsFluidOut(tFluid.getFluid())
+ && (gTank == null || gTank.getCoverInfoAtSide(oppositeSide)
+ .letsFluidIn(tFluid.getFluid()))) {
+ if (tTank.fill(oppositeSide, tFluid, false) > 0) {
+ tTanks.add(new MutableTriple<>(tTank, oppositeSide, 0));
+ }
+ tFluid.amount = amount; // Because some mods do actually modify input fluid stack
+ }
+ }
+
+ // How much of this fluid is available for distribution?
+ final double tAmount = Math.max(1, Math.min(mCapacity * 10, tFluid.amount));
+
+ final FluidStack maxFluid = tFluid.copy();
+ maxFluid.amount = Integer.MAX_VALUE;
+
+ double availableCapacity = 0;
+ // Calculate available capacity for distribution from all tanks
+ for (final MutableTriple<IFluidHandler, ForgeDirection, Integer> tEntry : tTanks) {
+ tEntry.right = tEntry.left.fill(tEntry.middle, maxFluid, false);
+ availableCapacity += tEntry.right;
+ }
+
+ // Now distribute
+ for (final MutableTriple<IFluidHandler, ForgeDirection, Integer> tEntry : tTanks) {
+ // Distribue fluids based on percentage available space at destination
+ if (availableCapacity > tAmount)
+ tEntry.right = (int) Math.floor(tEntry.right * tAmount / availableCapacity);
+
+ // If the percent is not enough to give at least 1L, try to give 1L
+ if (tEntry.right == 0) tEntry.right = (int) Math.min(1, tAmount);
+
+ if (tEntry.right <= 0) continue;
+
+ final int tFilledAmount = tEntry.left
+ .fill(tEntry.middle, drainFromIndex(tEntry.right, false, index), false);
+
+ if (tFilledAmount > 0) tEntry.left.fill(tEntry.middle, drainFromIndex(tFilledAmount, true, index), true);
+
+ if (mFluids[index] == null || mFluids[index].amount <= 0) return;
+ }
+ }
+
+ public void connectPipeOnSide(ForgeDirection side, EntityPlayer entityPlayer) {
+ if (!isConnectedAtSide(side)) {
+ if (connect(side) > 0) GT_Utility.sendChatToPlayer(entityPlayer, GT_Utility.trans("214", "Connected"));
+ } else {
+ disconnect(side);
+ GT_Utility.sendChatToPlayer(entityPlayer, GT_Utility.trans("215", "Disconnected"));
+ }
+ }
+
+ public void blockPipeOnSide(ForgeDirection side, EntityPlayer entityPlayer, byte mask) {
+ if (isInputDisabledAtSide(side)) {
+ mDisableInput &= ~mask;
+ GT_Utility.sendChatToPlayer(entityPlayer, GT_Utility.trans("212", "Input enabled"));
+ if (!isConnectedAtSide(side)) connect(side);
+ } else {
+ mDisableInput |= mask;
+ GT_Utility.sendChatToPlayer(entityPlayer, GT_Utility.trans("213", "Input disabled"));
+ }
+ }
+
+ @Override
+ public boolean onWrenchRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer entityPlayer,
+ float aX, float aY, float aZ, ItemStack aTool) {
+
+ if (GT_Mod.gregtechproxy.gt6Pipe) {
+ final int mode = GT_MetaGenerated_Tool.getToolMode(aTool);
+ IGregTechTileEntity currentPipeBase = getBaseMetaTileEntity();
+ GT_MetaPipeEntity_Fluid currentPipe = (GT_MetaPipeEntity_Fluid) currentPipeBase.getMetaTileEntity();
+ final ForgeDirection tSide = GT_Utility.determineWrenchingSide(side, aX, aY, aZ);
+ final byte tMask = (byte) (tSide.flag);
+
+ if (mode == ToolModes.REGULAR.get()) {
+ if (entityPlayer.isSneaking()) {
+ currentPipe.blockPipeOnSide(tSide, entityPlayer, tMask);
+ } else currentPipe.connectPipeOnSide(tSide, entityPlayer);
+ return true;
+ }
+
+ if (mode == ToolModes.WRENCH_LINE.get()) {
+
+ boolean initialState = entityPlayer.isSneaking() ? currentPipe.isInputDisabledAtSide(tSide)
+ : currentPipe.isConnectedAtSide(tSide);
+
+ boolean wasActionPerformed = false;
+
+ int limit = GregTech_API.sSpecialFile.get(ConfigCategories.general, "PipeWrenchingChainRange", 64);
+ for (int connected = 0; connected < limit; connected++) {
+
+ TileEntity nextPipeBaseTile = currentPipeBase.getTileEntityAtSide(tSide);
+
+ // if next tile doesn't exist or if next tile is not GT tile
+ if (!(nextPipeBaseTile instanceof IGregTechTileEntity nextPipeBase)) {
+ return wasActionPerformed;
+ }
+
+ // if next tile is wrong color
+ if (!currentPipe.connectableColor(nextPipeBaseTile)) {
+ return wasActionPerformed;
+ }
+
+ GT_MetaPipeEntity_Fluid nextPipe = nextPipeBase
+ .getMetaTileEntity() instanceof GT_MetaPipeEntity_Fluid
+ ? (GT_MetaPipeEntity_Fluid) nextPipeBase.getMetaTileEntity()
+ : null;
+
+ // if next tile entity is not a pipe
+ if (nextPipe == null) {
+ return wasActionPerformed;
+ }
+
+ // if pipes are same size
+ if (mPipeAmount != nextPipe.mPipeAmount) {
+ return wasActionPerformed;
+ }
+
+ // making sure next pipe has same fluid
+ for (int i = 0; i < mPipeAmount; i++) {
+ if (mFluids[i] != null && nextPipe.mFluids[i] != null) {
+ if (!mFluids[i].isFluidEqual(nextPipe.mFluids[i])) {
+ return wasActionPerformed;
+ }
+ } else if (mFluids[i] != nextPipe.mFluids[i]) {
+ return wasActionPerformed;
+ }
+ }
+
+ boolean currentState = entityPlayer.isSneaking() ? currentPipe.isInputDisabledAtSide(tSide)
+ : currentPipe.isConnectedAtSide(tSide);
+
+ /*
+ * Making sure next pipe will have same action applied to it
+ * e.g. Connecting pipe won`t trigger disconnect if next pipe is already connected
+ */
+ if (currentState != initialState) {
+ return wasActionPerformed;
+ }
+
+ if (entityPlayer.isSneaking()) {
+ currentPipe.blockPipeOnSide(tSide, entityPlayer, tMask);
+ } else currentPipe.connectPipeOnSide(tSide, entityPlayer);
+
+ wasActionPerformed = true;
+
+ currentPipeBase = (IGregTechTileEntity) nextPipeBase;
+ currentPipe = nextPipe;
+
+ }
+ return wasActionPerformed;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean letsIn(GT_CoverBehavior coverBehavior, ForgeDirection side, int aCoverID, int aCoverVariable,
+ ICoverable aTileEntity) {
+ return coverBehavior.letsFluidIn(side, aCoverID, aCoverVariable, null, aTileEntity);
+ }
+
+ @Override
+ public boolean letsOut(GT_CoverBehavior coverBehavior, ForgeDirection side, int aCoverID, int aCoverVariable,
+ ICoverable aTileEntity) {
+ return coverBehavior.letsFluidOut(side, aCoverID, aCoverVariable, null, aTileEntity);
+ }
+
+ @Override
+ public boolean letsIn(GT_CoverBehaviorBase<?> coverBehavior, ForgeDirection side, int aCoverID,
+ ISerializableObject aCoverVariable, ICoverable aTileEntity) {
+ return coverBehavior.letsFluidIn(side, aCoverID, aCoverVariable, null, aTileEntity);
+ }
+
+ @Override
+ public boolean letsOut(GT_CoverBehaviorBase<?> coverBehavior, ForgeDirection side, int aCoverID,
+ ISerializableObject aCoverVariable, ICoverable aTileEntity) {
+ return coverBehavior.letsFluidOut(side, aCoverID, aCoverVariable, null, aTileEntity);
+ }
+
+ @Override
+ public boolean letsIn(CoverInfo coverInfo) {
+ return coverInfo.letsFluidIn(null);
+ }
+
+ @Override
+ public boolean letsOut(CoverInfo coverInfo) {
+ return coverInfo.letsFluidOut(null);
+ }
+
+ @Override
+ public boolean canConnect(ForgeDirection side, TileEntity tileEntity) {
+ if (tileEntity == null) return false;
+
+ final ForgeDirection tSide = side.getOpposite();
+ final IGregTechTileEntity baseMetaTile = getBaseMetaTileEntity();
+ if (baseMetaTile == null) return false;
+
+ final GT_CoverBehaviorBase<?> coverBehavior = baseMetaTile.getCoverBehaviorAtSideNew(side);
+ final IGregTechTileEntity gTileEntity = (tileEntity instanceof IGregTechTileEntity)
+ ? (IGregTechTileEntity) tileEntity
+ : null;
+
+ if (coverBehavior instanceof GT_Cover_Drain
+ || (TinkerConstruct.isModLoaded() && isTConstructFaucet(tileEntity))) return true;
+
+ final IFluidHandler fTileEntity = (tileEntity instanceof IFluidHandler) ? (IFluidHandler) tileEntity : null;
+
+ if (fTileEntity != null) {
+ final FluidTankInfo[] tInfo = fTileEntity.getTankInfo(tSide);
+ if (tInfo != null) {
+ return tInfo.length > 0 || (Translocator.isModLoaded() && isTranslocator(tileEntity))
+ || gTileEntity != null
+ && gTileEntity.getCoverBehaviorAtSideNew(tSide) instanceof GT_Cover_FluidRegulator;
+ }
+ }
+ return false;
+ }
+
+ @Optional.Method(modid = Mods.Names.TINKER_CONSTRUCT)
+ private boolean isTConstructFaucet(TileEntity tTileEntity) {
+ // Tinker Construct Faucets return a null tank info, so check the class
+ return tTileEntity instanceof tconstruct.smeltery.logic.FaucetLogic;
+ }
+
+ @Optional.Method(modid = Mods.Names.TRANSLOCATOR)
+ private boolean isTranslocator(TileEntity tTileEntity) {
+ // Translocators return a TankInfo, but it's of 0 length - so check the class if we see this pattern
+ return tTileEntity instanceof codechicken.translocator.TileLiquidTranslocator;
+ }
+
+ @Override
+ public boolean getGT6StyleConnection() {
+ // Yes if GT6 pipes are enabled
+ return GT_Mod.gregtechproxy.gt6Pipe;
+ }
+
+ @Override
+ public void doSound(byte aIndex, double aX, double aY, double aZ) {
+ super.doSound(aIndex, aX, aY, aZ);
+ if (aIndex == 9) {
+ GT_Utility.doSoundAtClient(SoundResource.RANDOM_FIZZ, 5, 1.0F, aX, aY, aZ);
+
+ new ParticleEventBuilder().setIdentifier(ParticleFX.CLOUD)
+ .setWorld(getBaseMetaTileEntity().getWorld())
+ .<ParticleEventBuilder>times(
+ 6,
+ (x, i) -> x
+ .setMotion(
+ ForgeDirection.getOrientation(i).offsetX / 5.0,
+ ForgeDirection.getOrientation(i).offsetY / 5.0,
+ ForgeDirection.getOrientation(i).offsetZ / 5.0)
+ .setPosition(
+ aX - 0.5 + XSTR_INSTANCE.nextFloat(),
+ aY - 0.5 + XSTR_INSTANCE.nextFloat(),
+ aZ - 0.5 + XSTR_INSTANCE.nextFloat())
+ .run());
+ }
+ }
+
+ @Override
+ public final int getCapacity() {
+ return mCapacity * 20 * mPipeAmount;
+ }
+
+ @Override
+ public FluidTankInfo getInfo() {
+ for (FluidStack tFluid : mFluids) {
+ if (tFluid != null) return new FluidTankInfo(tFluid, mCapacity * 20);
+ }
+ return new FluidTankInfo(null, mCapacity * 20);
+ }
+
+ @Override
+ public FluidTankInfo[] getTankInfo(ForgeDirection side) {
+ if (getCapacity() <= 0 && !getBaseMetaTileEntity().hasSteamEngineUpgrade()) return new FluidTankInfo[] {};
+ ArrayList<FluidTankInfo> tList = new ArrayList<>();
+ for (FluidStack tFluid : mFluids) tList.add(new FluidTankInfo(tFluid, mCapacity * 20));
+ return tList.toArray(new FluidTankInfo[mPipeAmount]);
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public final FluidStack getFluid() {
+ for (FluidStack tFluid : mFluids) {
+ if (tFluid != null) return tFluid;
+ }
+ return null;
+ }
+
+ @Override
+ public final int getFluidAmount() {
+ int rAmount = 0;
+ for (FluidStack tFluid : mFluids) {
+ if (tFluid != null) rAmount += tFluid.amount;
+ }
+ return rAmount;
+ }
+
+ @Override
+ public final int fill_default(ForgeDirection side, FluidStack aFluid, boolean doFill) {
+ if (aFluid == null || aFluid.getFluid()
+ .getID() <= 0) return 0;
+
+ int index = -1;
+ for (int i = 0; i < mPipeAmount; i++) {
+ if (mFluids[i] != null && mFluids[i].isFluidEqual(aFluid)) {
+ index = i;
+ break;
+ } else if ((mFluids[i] == null || mFluids[i].getFluid()
+ .getID() <= 0) && index < 0) {
+ index = i;
+ }
+ }
+
+ return fill_default_intoIndex(side, aFluid, doFill, index);
+ }
+
+ private int fill_default_intoIndex(ForgeDirection side, FluidStack aFluid, boolean doFill, int index) {
+ if (index < 0 || index >= mPipeAmount) return 0;
+ if (aFluid == null || aFluid.getFluid()
+ .getID() <= 0) return 0;
+
+ final int ordinalSide = side.ordinal();
+
+ if (mFluids[index] == null || mFluids[index].getFluid()
+ .getID() <= 0) {
+ if (aFluid.amount * mPipeAmount <= getCapacity()) {
+ if (doFill) {
+ mFluids[index] = aFluid.copy();
+ mLastReceivedFrom |= (1 << ordinalSide);
+ }
+ return aFluid.amount;
+ }
+ if (doFill) {
+ mFluids[index] = aFluid.copy();
+ mLastReceivedFrom |= (1 << ordinalSide);
+ mFluids[index].amount = getCapacity() / mPipeAmount;
+ }
+ return getCapacity() / mPipeAmount;
+ }
+
+ if (!mFluids[index].isFluidEqual(aFluid)) return 0;
+
+ final int space = getCapacity() / mPipeAmount - mFluids[index].amount;
+ if (aFluid.amount <= space) {
+ if (doFill) {
+ mFluids[index].amount += aFluid.amount;
+ mLastReceivedFrom |= (1 << ordinalSide);
+ }
+ return aFluid.amount;
+ }
+ if (doFill) {
+ mFluids[index].amount = getCapacity() / mPipeAmount;
+ mLastReceivedFrom |= (1 << ordinalSide);
+ }
+ return space;
+ }
+
+ @Override
+ public final FluidStack drain(int maxDrain, boolean doDrain) {
+ FluidStack drained;
+ for (int i = 0; i < mPipeAmount; i++) {
+ if ((drained = drainFromIndex(maxDrain, doDrain, i)) != null) return drained;
+ }
+ return null;
+ }
+
+ private FluidStack drainFromIndex(int maxDrain, boolean doDrain, int index) {
+ if (index < 0 || index >= mPipeAmount) return null;
+ if (mFluids[index] == null) return null;
+ if (mFluids[index].amount <= 0) {
+ mFluids[index] = null;
+ return null;
+ }
+
+ int used = maxDrain;
+ if (mFluids[index].amount < used) used = mFluids[index].amount;
+
+ if (doDrain) {
+ mFluids[index].amount -= used;
+ }
+
+ final FluidStack drained = mFluids[index].copy();
+ drained.amount = used;
+
+ if (mFluids[index].amount <= 0) {
+ mFluids[index] = null;
+ }
+
+ return drained;
+ }
+
+ @Override
+ public int getTankPressure() {
+ return getFluidAmount() - (getCapacity() / 2);
+ }
+
+ @Override
+ public String[] getDescription() {
+ List<String> descriptions = new ArrayList<>();
+ descriptions.add(
+ EnumChatFormatting.BLUE + "Fluid Capacity: %%%"
+ + GT_Utility.formatNumbers(mCapacity * 20L)
+ + "%%% L/sec"
+ + EnumChatFormatting.GRAY);
+ descriptions.add(
+ EnumChatFormatting.RED + "Heat Limit: %%%"
+ + GT_Utility.formatNumbers(mHeatResistance)
+ + "%%% K"
+ + EnumChatFormatting.GRAY);
+ if (!mGasProof) {
+ descriptions.add(EnumChatFormatting.DARK_GREEN + "Cannot handle gas" + EnumChatFormatting.GRAY);
+ }
+ if (mPipeAmount != 1) {
+ descriptions.add(EnumChatFormatting.AQUA + "Pipe Amount: %%%" + mPipeAmount + EnumChatFormatting.GRAY);
+ }
+ return descriptions.toArray(new String[0]);
+ }
+
+ @Override
+ public float getThickNess() {
+ if (GT_Mod.instance.isClientSide() && (GT_Client.hideValue & 0x1) != 0) return 0.0625F;
+ return mThickNess;
+ }
+
+ @Override
+ public boolean isLiquidInput(ForgeDirection side) {
+ return !isInputDisabledAtSide(side);
+ }
+
+ @Override
+ public boolean isLiquidOutput(ForgeDirection side) {
+ return true;
+ }
+
+ public boolean isInputDisabledAtSide(ForgeDirection side) {
+ return (mDisableInput & side.flag) != 0;
+ }
+
+ @Override
+ public AxisAlignedBB getCollisionBoundingBoxFromPool(World aWorld, int aX, int aY, int aZ) {
+ if (GT_Mod.instance.isClientSide() && (GT_Client.hideValue & 0x2) != 0)
+ return AxisAlignedBB.getBoundingBox(aX, aY, aZ, aX + 1, aY + 1, aZ + 1);
+ else return getActualCollisionBoundingBoxFromPool(aWorld, aX, aY, aZ);
+ }
+
+ private AxisAlignedBB getActualCollisionBoundingBoxFromPool(World aWorld, int aX, int aY, int aZ) {
+ final float tSpace = (1f - mThickNess) / 2;
+ float tSide0 = tSpace;
+ float tSide1 = 1f - tSpace;
+ float tSide2 = tSpace;
+ float tSide3 = 1f - tSpace;
+ float tSide4 = tSpace;
+ float tSide5 = 1f - tSpace;
+
+ if (getBaseMetaTileEntity().getCoverIDAtSide(ForgeDirection.DOWN) != 0) {
+ tSide0 = tSide2 = tSide4 = 0;
+ tSide3 = tSide5 = 1;
+ }
+ if (getBaseMetaTileEntity().getCoverIDAtSide(ForgeDirection.UP) != 0) {
+ tSide2 = tSide4 = 0;
+ tSide1 = tSide3 = tSide5 = 1;
+ }
+ if (getBaseMetaTileEntity().getCoverIDAtSide(ForgeDirection.NORTH) != 0) {
+ tSide0 = tSide2 = tSide4 = 0;
+ tSide1 = tSide5 = 1;
+ }
+ if (getBaseMetaTileEntity().getCoverIDAtSide(ForgeDirection.SOUTH) != 0) {
+ tSide0 = tSide4 = 0;
+ tSide1 = tSide3 = tSide5 = 1;
+ }
+ if (getBaseMetaTileEntity().getCoverIDAtSide(ForgeDirection.WEST) != 0) {
+ tSide0 = tSide2 = tSide4 = 0;
+ tSide1 = tSide3 = 1;
+ }
+ if (getBaseMetaTileEntity().getCoverIDAtSide(ForgeDirection.EAST) != 0) {
+ tSide0 = tSide2 = 0;
+ tSide1 = tSide3 = tSide5 = 1;
+ }
+
+ final byte tConn = ((BaseMetaPipeEntity) getBaseMetaTileEntity()).mConnections;
+ if ((tConn & ForgeDirection.DOWN.flag) != 0) tSide0 = 0f;
+ if ((tConn & ForgeDirection.UP.flag) != 0) tSide1 = 1f;
+ if ((tConn & ForgeDirection.NORTH.flag) != 0) tSide2 = 0f;
+ if ((tConn & ForgeDirection.SOUTH.flag) != 0) tSide3 = 1f;
+ if ((tConn & ForgeDirection.WEST.flag) != 0) tSide4 = 0f;
+ if ((tConn & ForgeDirection.EAST.flag) != 0) tSide5 = 1f;
+
+ return AxisAlignedBB
+ .getBoundingBox(aX + tSide4, aY + tSide0, aZ + tSide2, aX + tSide5, aY + tSide1, aZ + tSide3);
+ }
+
+ @Override
+ public void addCollisionBoxesToList(World aWorld, int aX, int aY, int aZ, AxisAlignedBB inputAABB,
+ List<AxisAlignedBB> outputAABB, Entity collider) {
+ super.addCollisionBoxesToList(aWorld, aX, aY, aZ, inputAABB, outputAABB, collider);
+ if (GT_Mod.instance.isClientSide() && (GT_Client.hideValue & 0x2) != 0) {
+ final AxisAlignedBB aabb = getActualCollisionBoundingBoxFromPool(aWorld, aX, aY, aZ);
+ if (inputAABB.intersectsWith(aabb)) outputAABB.add(aabb);
+ }
+ }
+
+ @Override
+ public FluidStack drain(ForgeDirection side, FluidStack aFluid, boolean doDrain) {
+ if (aFluid == null) return null;
+ for (int i = 0; i < mFluids.length; ++i) {
+ final FluidStack f = mFluids[i];
+ if (f == null || !f.isFluidEqual(aFluid)) continue;
+ return drainFromIndex(aFluid.amount, doDrain, i);
+ }
+ return null;
+ }
+
+ private static EnumMap<Border, ForgeDirection> borderMap(ForgeDirection topSide, ForgeDirection bottomSide,
+ ForgeDirection leftSide, ForgeDirection rightSide) {
+ final EnumMap<Border, ForgeDirection> sideMap = new EnumMap<>(Border.class);
+ sideMap.put(TOP, topSide);
+ sideMap.put(BOTTOM, bottomSide);
+ sideMap.put(LEFT, leftSide);
+ sideMap.put(RIGHT, rightSide);
+ return sideMap;
+ }
+
+ protected static ForgeDirection getSideAtBorder(ForgeDirection side, Border border) {
+ return FACE_BORDER_MAP.get(side)
+ .get(border);
+ }
+
+ protected enum Border {
+
+ TOP(),
+ BOTTOM(),
+ LEFT(),
+ RIGHT();
+
+ public final int mask;
+
+ Border() {
+ mask = 1 << this.ordinal();
+ }
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Frame.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Frame.java
new file mode 100644
index 0000000000..7cb2b3a9bd
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Frame.java
@@ -0,0 +1,119 @@
+package gregtech.api.metatileentity.implementations;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.api.enums.Dyes;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.OrePrefixes;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaPipeEntity;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_LanguageManager;
+
+public class GT_MetaPipeEntity_Frame extends MetaPipeEntity {
+
+ private static final String localizedDescFormat = GT_LanguageManager
+ .addStringLocalization("gt.blockmachines.gt_frame.desc.format", "Just something you can put covers on.");
+ public final Materials mMaterial;
+
+ public GT_MetaPipeEntity_Frame(int aID, String aName, String aNameRegional, Materials aMaterial) {
+ super(aID, aName, aNameRegional, 0);
+ mMaterial = aMaterial;
+ // Hide TileEntity frame in NEI, since we have the block version now that should always be used
+ codechicken.nei.api.API.hideItem(this.getStackForm(1));
+ }
+
+ public GT_MetaPipeEntity_Frame(String aName, Materials aMaterial) {
+ super(aName, 0);
+ mMaterial = aMaterial;
+ }
+
+ @Override
+ public byte getTileEntityBaseType() {
+ return (byte) (mMaterial == null ? 4 : (byte) (4) + Math.max(0, Math.min(3, mMaterial.mToolQuality)));
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaPipeEntity_Frame(mName, mMaterial);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection, int connections,
+ int colorIndex, boolean active, boolean redstoneLevel) {
+ return new ITexture[] { TextureFactory.of(
+ mMaterial.mIconSet.mTextures[OrePrefixes.frameGt.mTextureIndex],
+ Dyes.getModulation(colorIndex, mMaterial.mRGBa)) };
+ }
+
+ @Override
+ public String[] getDescription() {
+ return localizedDescFormat.split("\\R");
+ }
+
+ @Override
+ public final boolean isSimpleMachine() {
+ return true;
+ }
+
+ @Override
+ public final boolean isFacingValid(ForgeDirection facing) {
+ return false;
+ }
+
+ @Override
+ public final boolean isValidSlot(int aIndex) {
+ return false;
+ }
+
+ @Override
+ public final boolean renderInside(ForgeDirection side) {
+ return true;
+ }
+
+ @Override
+ public final float getThickNess() {
+ return 1.0F;
+ }
+
+ @Override
+ public final void saveNBTData(NBTTagCompound aNBT) {
+ /* Do nothing */
+ }
+
+ @Override
+ public final void loadNBTData(NBTTagCompound aNBT) {
+ /* Do nothing */
+ }
+
+ @Override
+ public final boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public final boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public int connect(ForgeDirection side) {
+ return 0;
+ }
+
+ @Override
+ public void disconnect(ForgeDirection side) {
+ /* Do nothing */
+ }
+
+ @Override
+ public boolean isMachineBlockUpdateRecursive() {
+ return true;
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Item.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Item.java
new file mode 100644
index 0000000000..660230660e
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Item.java
@@ -0,0 +1,530 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.GT_Values.ALL_VALID_SIDES;
+import static gregtech.api.enums.Textures.BlockIcons.PIPE_RESTRICTOR;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.ISidedInventory;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.tileentity.TileEntityDispenser;
+import net.minecraft.tileentity.TileEntityHopper;
+import net.minecraft.util.AxisAlignedBB;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.GT_Mod;
+import gregtech.api.enums.Dyes;
+import gregtech.api.enums.GT_Values;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.OrePrefixes;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntityItemPipe;
+import gregtech.api.interfaces.tileentity.ICoverable;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.BaseMetaPipeEntity;
+import gregtech.api.metatileentity.MetaPipeEntity;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_CoverBehavior;
+import gregtech.api.util.GT_CoverBehaviorBase;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.ISerializableObject;
+import gregtech.common.GT_Client;
+import gregtech.common.covers.CoverInfo;
+
+public class GT_MetaPipeEntity_Item extends MetaPipeEntity implements IMetaTileEntityItemPipe {
+
+ public final float mThickNess;
+ public final Materials mMaterial;
+ public final int mStepSize;
+ public final int mTickTime;
+ public int mTransferredItems = 0;
+ public long mCurrentTransferStartTick = 0;
+ public ForgeDirection mLastReceivedFrom = ForgeDirection.UNKNOWN, oLastReceivedFrom = ForgeDirection.UNKNOWN;
+ public boolean mIsRestrictive = false;
+ private int[] cacheSides;
+
+ public GT_MetaPipeEntity_Item(int aID, String aName, String aNameRegional, float aThickNess, Materials aMaterial,
+ int aInvSlotCount, int aStepSize, boolean aIsRestrictive, int aTickTime) {
+ super(aID, aName, aNameRegional, aInvSlotCount, false);
+ mIsRestrictive = aIsRestrictive;
+ mThickNess = aThickNess;
+ mMaterial = aMaterial;
+ mStepSize = aStepSize;
+ mTickTime = aTickTime;
+ addInfo(aID);
+ }
+
+ public GT_MetaPipeEntity_Item(int aID, String aName, String aNameRegional, float aThickNess, Materials aMaterial,
+ int aInvSlotCount, int aStepSize, boolean aIsRestrictive) {
+ this(aID, aName, aNameRegional, aThickNess, aMaterial, aInvSlotCount, aStepSize, aIsRestrictive, 20);
+ }
+
+ public GT_MetaPipeEntity_Item(String aName, float aThickNess, Materials aMaterial, int aInvSlotCount, int aStepSize,
+ boolean aIsRestrictive, int aTickTime) {
+ super(aName, aInvSlotCount);
+ mIsRestrictive = aIsRestrictive;
+ mThickNess = aThickNess;
+ mMaterial = aMaterial;
+ mStepSize = aStepSize;
+ mTickTime = aTickTime;
+ }
+
+ @Override
+ public byte getTileEntityBaseType() {
+ return (byte) (mMaterial == null ? 4 : (byte) (4) + Math.max(0, Math.min(3, mMaterial.mToolQuality)));
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaPipeEntity_Item(
+ mName,
+ mThickNess,
+ mMaterial,
+ mInventory.length,
+ mStepSize,
+ mIsRestrictive,
+ mTickTime);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, int aConnections,
+ int aColorIndex, boolean aConnected, boolean redstoneLevel) {
+ if (mIsRestrictive) {
+ if (aConnected) {
+ float tThickNess = getThickNess();
+ if (tThickNess < 0.124F) return new ITexture[] { TextureFactory.of(
+ mMaterial.mIconSet.mTextures[OrePrefixes.pipe.mTextureIndex],
+ Dyes.getModulation(aColorIndex, mMaterial.mRGBa)), TextureFactory.of(PIPE_RESTRICTOR) };
+ if (tThickNess < 0.374F) // 0.375
+ return new ITexture[] { TextureFactory.of(
+ mMaterial.mIconSet.mTextures[OrePrefixes.pipeTiny.mTextureIndex],
+ Dyes.getModulation(aColorIndex, mMaterial.mRGBa)), TextureFactory.of(PIPE_RESTRICTOR) };
+ if (tThickNess < 0.499F) // 0.500
+ return new ITexture[] { TextureFactory.of(
+ mMaterial.mIconSet.mTextures[OrePrefixes.pipeSmall.mTextureIndex],
+ Dyes.getModulation(aColorIndex, mMaterial.mRGBa)), TextureFactory.of(PIPE_RESTRICTOR) };
+ if (tThickNess < 0.749F) // 0.750
+ return new ITexture[] { TextureFactory.of(
+ mMaterial.mIconSet.mTextures[OrePrefixes.pipeMedium.mTextureIndex],
+ Dyes.getModulation(aColorIndex, mMaterial.mRGBa)), TextureFactory.of(PIPE_RESTRICTOR) };
+ if (tThickNess < 0.874F) // 0.825
+ return new ITexture[] { TextureFactory.of(
+ mMaterial.mIconSet.mTextures[OrePrefixes.pipeLarge.mTextureIndex],
+ Dyes.getModulation(aColorIndex, mMaterial.mRGBa)), TextureFactory.of(PIPE_RESTRICTOR) };
+ return new ITexture[] { TextureFactory.of(
+ mMaterial.mIconSet.mTextures[OrePrefixes.pipeHuge.mTextureIndex],
+ Dyes.getModulation(aColorIndex, mMaterial.mRGBa)), TextureFactory.of(PIPE_RESTRICTOR) };
+ }
+ return new ITexture[] { TextureFactory.of(
+ mMaterial.mIconSet.mTextures[OrePrefixes.pipe.mTextureIndex],
+ Dyes.getModulation(aColorIndex, mMaterial.mRGBa)), TextureFactory.of(PIPE_RESTRICTOR) };
+ }
+ if (aConnected) {
+ float tThickNess = getThickNess();
+ if (tThickNess < 0.124F) return new ITexture[] { TextureFactory.of(
+ mMaterial.mIconSet.mTextures[OrePrefixes.pipe.mTextureIndex],
+ Dyes.getModulation(aColorIndex, mMaterial.mRGBa)) };
+ if (tThickNess < 0.374F) // 0.375
+ return new ITexture[] { TextureFactory.of(
+ mMaterial.mIconSet.mTextures[OrePrefixes.pipeTiny.mTextureIndex],
+ Dyes.getModulation(aColorIndex, mMaterial.mRGBa)) };
+ if (tThickNess < 0.499F) // 0.500
+ return new ITexture[] { TextureFactory.of(
+ mMaterial.mIconSet.mTextures[OrePrefixes.pipeSmall.mTextureIndex],
+ Dyes.getModulation(aColorIndex, mMaterial.mRGBa)) };
+ if (tThickNess < 0.749F) // 0.750
+ return new ITexture[] { TextureFactory.of(
+ mMaterial.mIconSet.mTextures[OrePrefixes.pipeMedium.mTextureIndex],
+ Dyes.getModulation(aColorIndex, mMaterial.mRGBa)) };
+ if (tThickNess < 0.874F) // 0.825
+ return new ITexture[] { TextureFactory.of(
+ mMaterial.mIconSet.mTextures[OrePrefixes.pipeLarge.mTextureIndex],
+ Dyes.getModulation(aColorIndex, mMaterial.mRGBa)) };
+ return new ITexture[] { TextureFactory.of(
+ mMaterial.mIconSet.mTextures[OrePrefixes.pipeHuge.mTextureIndex],
+ Dyes.getModulation(aColorIndex, mMaterial.mRGBa)) };
+ }
+ return new ITexture[] { TextureFactory.of(
+ mMaterial.mIconSet.mTextures[OrePrefixes.pipe.mTextureIndex],
+ Dyes.getModulation(aColorIndex, mMaterial.mRGBa)) };
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return true;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return false;
+ }
+
+ @Override
+ public boolean isValidSlot(int ignoredSlotIndex) {
+ return true;
+ }
+
+ @Override
+ public final boolean renderInside(ForgeDirection side) {
+ return false;
+ }
+
+ @Override
+ public int getProgresstime() {
+ return getPipeContent() * 64;
+ }
+
+ @Override
+ public int maxProgresstime() {
+ return getMaxPipeCapacity() * 64;
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ aNBT.setByte("mLastReceivedFrom", (byte) mLastReceivedFrom.ordinal());
+ if (GT_Mod.gregtechproxy.gt6Pipe) aNBT.setByte("mConnections", mConnections);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ mLastReceivedFrom = ForgeDirection.getOrientation(aNBT.getByte("mLastReceivedFrom"));
+ if (GT_Mod.gregtechproxy.gt6Pipe) {
+ mConnections = aNBT.getByte("mConnections");
+ }
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ if (aBaseMetaTileEntity.isServerSide() && (aTick - mCurrentTransferStartTick) % 10 == 0) {
+ if ((aTick - mCurrentTransferStartTick) % mTickTime == 0) {
+ mTransferredItems = 0;
+ mCurrentTransferStartTick = 0;
+ }
+
+ if (!GT_Mod.gregtechproxy.gt6Pipe || mCheckConnections) checkConnections();
+
+ if (oLastReceivedFrom == mLastReceivedFrom) {
+ doTickProfilingInThisTick = false;
+
+ final ArrayList<IMetaTileEntityItemPipe> tPipeList = new ArrayList<>();
+
+ for (boolean temp = true; temp && !isInventoryEmpty() && pipeCapacityCheck();) {
+ temp = false;
+ tPipeList.clear();
+ for (IMetaTileEntityItemPipe tTileEntity : GT_Utility
+ .sortMapByValuesAcending(
+ IMetaTileEntityItemPipe.Util.scanPipes(this, new HashMap<>(), 0, false, false))
+ .keySet()) {
+ if (temp) break;
+ tPipeList.add(tTileEntity);
+ while (!temp && !isInventoryEmpty() && tTileEntity.sendItemStack(aBaseMetaTileEntity))
+ for (IMetaTileEntityItemPipe tPipe : tPipeList)
+ if (!tPipe.incrementTransferCounter(1)) temp = true;
+ }
+ }
+ }
+
+ if (isInventoryEmpty()) mLastReceivedFrom = ForgeDirection.UNKNOWN;
+ oLastReceivedFrom = mLastReceivedFrom;
+ }
+ }
+
+ @Override
+ public boolean onWrenchRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer entityPlayer,
+ float aX, float aY, float aZ) {
+ if (GT_Mod.gregtechproxy.gt6Pipe) {
+ final ForgeDirection tSide = GT_Utility.determineWrenchingSide(side, aX, aY, aZ);
+ if (isConnectedAtSide(tSide)) {
+ disconnect(tSide);
+ GT_Utility.sendChatToPlayer(entityPlayer, GT_Utility.trans("215", "Disconnected"));
+ } else {
+ if (connect(tSide) > 0) GT_Utility.sendChatToPlayer(entityPlayer, GT_Utility.trans("214", "Connected"));
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean letsIn(GT_CoverBehavior coverBehavior, ForgeDirection side, int aCoverID, int aCoverVariable,
+ ICoverable aTileEntity) {
+ return coverBehavior.letsItemsIn(side, aCoverID, aCoverVariable, -1, aTileEntity);
+ }
+
+ @Override
+ public boolean letsOut(GT_CoverBehavior coverBehavior, ForgeDirection side, int aCoverID, int aCoverVariable,
+ ICoverable aTileEntity) {
+ return coverBehavior.letsItemsOut(side, aCoverID, aCoverVariable, -1, aTileEntity);
+ }
+
+ @Override
+ public boolean letsIn(GT_CoverBehaviorBase<?> coverBehavior, ForgeDirection side, int aCoverID,
+ ISerializableObject aCoverVariable, ICoverable aTileEntity) {
+ return coverBehavior.letsItemsIn(side, aCoverID, aCoverVariable, -1, aTileEntity);
+ }
+
+ @Override
+ public boolean letsOut(GT_CoverBehaviorBase<?> coverBehavior, ForgeDirection side, int aCoverID,
+ ISerializableObject aCoverVariable, ICoverable aTileEntity) {
+ return coverBehavior.letsItemsOut(side, aCoverID, aCoverVariable, -1, aTileEntity);
+ }
+
+ @Override
+ public boolean letsIn(CoverInfo coverInfo) {
+ return coverInfo.letsItemsOut(-1);
+ }
+
+ @Override
+ public boolean letsOut(CoverInfo coverInfo) {
+ return coverInfo.letsItemsOut(-1);
+ }
+
+ @Override
+ public boolean canConnect(ForgeDirection side, TileEntity tileEntity) {
+ if (tileEntity == null) return false;
+
+ final ForgeDirection oppositeSide = side.getOpposite();
+ boolean connectable = GT_Utility.isConnectableNonInventoryPipe(tileEntity, oppositeSide);
+
+ final IGregTechTileEntity gTileEntity = (tileEntity instanceof IGregTechTileEntity)
+ ? (IGregTechTileEntity) tileEntity
+ : null;
+ if (gTileEntity != null) {
+ if (gTileEntity.getMetaTileEntity() == null) return false;
+ if (gTileEntity.getMetaTileEntity()
+ .connectsToItemPipe(oppositeSide)) return true;
+ connectable = true;
+ }
+
+ if (tileEntity instanceof IInventory) {
+ if (((IInventory) tileEntity).getSizeInventory() <= 0) return false;
+ connectable = true;
+ }
+ if (tileEntity instanceof ISidedInventory) {
+ final int[] tSlots = ((ISidedInventory) tileEntity).getAccessibleSlotsFromSide(oppositeSide.ordinal());
+ if (tSlots == null || tSlots.length == 0) return false;
+ connectable = true;
+ }
+
+ return connectable;
+ }
+
+ @Override
+ public boolean getGT6StyleConnection() {
+ // Yes if GT6 pipes are enabled
+ return GT_Mod.gregtechproxy.gt6Pipe;
+ }
+
+ @Override
+ public boolean incrementTransferCounter(int aIncrement) {
+ if (mTransferredItems == 0) mCurrentTransferStartTick = getBaseMetaTileEntity().getTimer();
+ mTransferredItems += aIncrement;
+ return pipeCapacityCheck();
+ }
+
+ @Override
+ public boolean sendItemStack(Object aSender) {
+ if (pipeCapacityCheck()) {
+ final byte tOffset = (byte) getBaseMetaTileEntity().getRandomNumber(6);
+ for (final byte i : ALL_VALID_SIDES) {
+ final ForgeDirection tSide = ForgeDirection.getOrientation((i + tOffset) % 6);
+ if (isConnectedAtSide(tSide)
+ && (isInventoryEmpty() || (tSide != mLastReceivedFrom || aSender != getBaseMetaTileEntity()))) {
+ if (insertItemStackIntoTileEntity(aSender, tSide)) return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean insertItemStackIntoTileEntity(Object aSender, ForgeDirection side) {
+ if (getBaseMetaTileEntity().getCoverInfoAtSide(side)
+ .letsItemsOut(-1)) {
+ final TileEntity tInventory = getBaseMetaTileEntity().getTileEntityAtSide(side);
+ if (tInventory != null && !(tInventory instanceof BaseMetaPipeEntity)) {
+ if ((!(tInventory instanceof TileEntityHopper) && !(tInventory instanceof TileEntityDispenser))
+ || getBaseMetaTileEntity().getMetaIDAtSide(side) != side.getOpposite()
+ .ordinal()) {
+ return GT_Utility.moveMultipleItemStacks(
+ aSender,
+ tInventory,
+ ForgeDirection.UNKNOWN,
+ side.getOpposite(),
+ null,
+ false,
+ (byte) 64,
+ (byte) 1,
+ (byte) 64,
+ (byte) 1,
+ 1) > 0;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean pipeCapacityCheck() {
+ return mTransferredItems <= 0 || getPipeContent() < getMaxPipeCapacity();
+ }
+
+ private int getPipeContent() {
+ return mTransferredItems;
+ }
+
+ private int getMaxPipeCapacity() {
+ return Math.max(1, getPipeCapacity());
+ }
+
+ /**
+ * Amount of ItemStacks this Pipe can conduct per Second.
+ */
+ public int getPipeCapacity() {
+ return mInventory.length;
+ }
+
+ @Override
+ public int getStepSize() {
+ return mStepSize;
+ }
+
+ @Override
+ public boolean canInsertItem(int aIndex, ItemStack aStack, int ordinalSide) {
+ return isConnectedAtSide(ForgeDirection.getOrientation(ordinalSide))
+ && super.canInsertItem(aIndex, aStack, ordinalSide);
+ }
+
+ @Override
+ public boolean canExtractItem(int aIndex, ItemStack aStack, int ordinalSide) {
+ return isConnectedAtSide(ForgeDirection.getOrientation(ordinalSide));
+ }
+
+ @Override
+ public int[] getAccessibleSlotsFromSide(int ordinalSide) {
+ final IGregTechTileEntity tTileEntity = getBaseMetaTileEntity();
+ final CoverInfo coverInfo = tTileEntity.getCoverInfoAtSide(ForgeDirection.getOrientation(ordinalSide));
+ final boolean tAllow = coverInfo.letsItemsIn(-2) || coverInfo.letsItemsOut(-2);
+ if (tAllow) {
+ if (cacheSides == null) cacheSides = super.getAccessibleSlotsFromSide(ordinalSide);
+ return cacheSides;
+ } else {
+ return GT_Values.emptyIntArray;
+ }
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return isConnectedAtSide(side);
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ if (!isConnectedAtSide(side)) return false;
+ if (isInventoryEmpty()) mLastReceivedFrom = side;
+ return mLastReceivedFrom == side && mInventory[aIndex] == null;
+ }
+
+ @Override
+ public String[] getDescription() {
+ if (mTickTime == 20) return new String[] { "Item Capacity: %%%" + getMaxPipeCapacity() + "%%% Stacks/sec",
+ "Routing Value: %%%" + GT_Utility.formatNumbers(mStepSize) };
+ else if (mTickTime % 20 == 0) return new String[] {
+ "Item Capacity: %%%" + getMaxPipeCapacity() + "%%% Stacks/%%%" + (mTickTime / 20) + "%%% sec",
+ "Routing Value: %%%" + GT_Utility.formatNumbers(mStepSize) };
+ else return new String[] {
+ "Item Capacity: %%%" + getMaxPipeCapacity() + "%%% Stacks/%%%" + mTickTime + "%%% ticks",
+ "Routing Value: %%%" + GT_Utility.formatNumbers(mStepSize) };
+ }
+
+ private boolean isInventoryEmpty() {
+ for (ItemStack tStack : mInventory) if (tStack != null) return false;
+ return true;
+ }
+
+ @Override
+ public float getThickNess() {
+ if (GT_Mod.instance.isClientSide() && (GT_Client.hideValue & 0x1) != 0) return 0.0625F;
+ return mThickNess;
+ }
+
+ @Override
+ public AxisAlignedBB getCollisionBoundingBoxFromPool(World aWorld, int aX, int aY, int aZ) {
+ if (GT_Mod.instance.isClientSide() && (GT_Client.hideValue & 0x2) != 0)
+ return AxisAlignedBB.getBoundingBox(aX, aY, aZ, aX + 1, aY + 1, aZ + 1);
+ else return getActualCollisionBoundingBoxFromPool(aWorld, aX, aY, aZ);
+ }
+
+ private AxisAlignedBB getActualCollisionBoundingBoxFromPool(World ignoredAWorld, int aX, int aY, int aZ) {
+ final float tSpace = (1f - mThickNess) / 2;
+ float spaceDown = tSpace;
+ float spaceUp = 1f - tSpace;
+ float spaceNorth = tSpace;
+ float spaceSouth = 1f - tSpace;
+ float spaceWest = tSpace;
+ float spaceEast = 1f - tSpace;
+
+ if (getBaseMetaTileEntity().getCoverIDAtSide(ForgeDirection.DOWN) != 0) {
+ spaceDown = spaceNorth = spaceWest = 0;
+ spaceSouth = spaceEast = 1;
+ }
+ if (getBaseMetaTileEntity().getCoverIDAtSide(ForgeDirection.UP) != 0) {
+ spaceNorth = spaceWest = 0;
+ spaceUp = spaceSouth = spaceEast = 1;
+ }
+ if (getBaseMetaTileEntity().getCoverIDAtSide(ForgeDirection.NORTH) != 0) {
+ spaceDown = spaceNorth = spaceWest = 0;
+ spaceUp = spaceEast = 1;
+ }
+ if (getBaseMetaTileEntity().getCoverIDAtSide(ForgeDirection.SOUTH) != 0) {
+ spaceDown = spaceWest = 0;
+ spaceUp = spaceSouth = spaceEast = 1;
+ }
+ if (getBaseMetaTileEntity().getCoverIDAtSide(ForgeDirection.WEST) != 0) {
+ spaceDown = spaceNorth = spaceWest = 0;
+ spaceUp = spaceSouth = 1;
+ }
+ if (getBaseMetaTileEntity().getCoverIDAtSide(ForgeDirection.EAST) != 0) {
+ spaceDown = spaceNorth = 0;
+ spaceUp = spaceSouth = spaceEast = 1;
+ }
+
+ final byte tConn = ((BaseMetaPipeEntity) getBaseMetaTileEntity()).mConnections;
+ if ((tConn & ForgeDirection.DOWN.flag) != 0) spaceDown = 0f;
+ if ((tConn & ForgeDirection.UP.flag) != 0) spaceUp = 1f;
+ if ((tConn & ForgeDirection.NORTH.flag) != 0) spaceNorth = 0f;
+ if ((tConn & ForgeDirection.SOUTH.flag) != 0) spaceSouth = 1f;
+ if ((tConn & ForgeDirection.WEST.flag) != 0) spaceWest = 0f;
+ if ((tConn & ForgeDirection.EAST.flag) != 0) spaceEast = 1f;
+
+ return AxisAlignedBB.getBoundingBox(
+ aX + spaceWest,
+ aY + spaceDown,
+ aZ + spaceNorth,
+ aX + spaceEast,
+ aY + spaceUp,
+ aZ + spaceSouth);
+ }
+
+ @Override
+ public void addCollisionBoxesToList(World aWorld, int aX, int aY, int aZ, AxisAlignedBB inputAABB,
+ List<AxisAlignedBB> outputAABB, Entity collider) {
+ super.addCollisionBoxesToList(aWorld, aX, aY, aZ, inputAABB, outputAABB, collider);
+ if (GT_Mod.instance.isClientSide() && (GT_Client.hideValue & 0x2) != 0) {
+ final AxisAlignedBB aabb = getActualCollisionBoundingBoxFromPool(aWorld, aX, aY, aZ);
+ if (inputAABB.intersectsWith(aabb)) outputAABB.add(aabb);
+ }
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicBatteryBuffer.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicBatteryBuffer.java
new file mode 100644
index 0000000000..cff63caeb5
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicBatteryBuffer.java
@@ -0,0 +1,436 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.GT_Values.V;
+
+import java.util.List;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.internal.wrapper.BaseSlot;
+import com.gtnewhorizons.modularui.common.widget.SlotGroup;
+
+import gregtech.api.enums.Textures;
+import gregtech.api.gui.modularui.GT_UIInfos;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.modularui.IAddUIWidgets;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.items.GT_MetaBase_Item;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_Utility;
+import ic2.api.item.IElectricItem;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+/**
+ * NEVER INCLUDE THIS FILE IN YOUR MOD!!!
+ * <p/>
+ * This is the main construct for my Basic Machines such as the Automatic Extractor Extend this class to make a simple
+ * Machine
+ */
+public class GT_MetaTileEntity_BasicBatteryBuffer extends GT_MetaTileEntity_TieredMachineBlock
+ implements IAddUIWidgets {
+
+ public boolean mCharge = false, mDecharge = false;
+ public int mBatteryCount = 0, mChargeableCount = 0;
+ private long count = 0;
+ private long mStored = 0;
+ private long mMax = 0;
+
+ public GT_MetaTileEntity_BasicBatteryBuffer(int aID, String aName, String aNameRegional, int aTier,
+ String aDescription, int aSlotCount) {
+ super(aID, aName, aNameRegional, aTier, aSlotCount, aDescription);
+ }
+
+ public GT_MetaTileEntity_BasicBatteryBuffer(String aName, int aTier, String aDescription, ITexture[][][] aTextures,
+ int aSlotCount) {
+ super(aName, aTier, aSlotCount, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_BasicBatteryBuffer(String aName, int aTier, String[] aDescription,
+ ITexture[][][] aTextures, int aSlotCount) {
+ super(aName, aTier, aSlotCount, aDescription, aTextures);
+ }
+
+ @Override
+ public String[] getDescription() {
+ String[] desc = new String[mDescriptionArray.length + 1];
+ System.arraycopy(mDescriptionArray, 0, desc, 0, mDescriptionArray.length);
+ desc[mDescriptionArray.length] = mInventory.length + " Slots";
+ return desc;
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+ ITexture[][][] rTextures = new ITexture[2][17][];
+ for (byte i = -1; i < 16; i++) {
+ rTextures[0][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1] };
+ rTextures[1][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1],
+ mInventory.length == 16 ? Textures.BlockIcons.OVERLAYS_ENERGY_OUT_POWER[mTier]
+ : mInventory.length > 4 ? Textures.BlockIcons.OVERLAYS_ENERGY_OUT_MULTI[mTier]
+ : Textures.BlockIcons.OVERLAYS_ENERGY_OUT[mTier] };
+ }
+ return rTextures;
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ return mTextures[side == aFacing ? 1 : 0][colorIndex + 1];
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_BasicBatteryBuffer(mName, mTier, mDescriptionArray, mTextures, mInventory.length);
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return false;
+ }
+
+ @Override
+ public boolean isElectric() {
+ return true;
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return true;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnetInput() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnetOutput() {
+ return true;
+ }
+
+ @Override
+ public boolean isInputFacing(ForgeDirection side) {
+ return side != getBaseMetaTileEntity().getFrontFacing();
+ }
+
+ @Override
+ public boolean isOutputFacing(ForgeDirection side) {
+ return side == getBaseMetaTileEntity().getFrontFacing();
+ }
+
+ @Override
+ public boolean isTeleporterCompatible() {
+ return false;
+ }
+
+ @Override
+ public long getMinimumStoredEU() {
+ return V[mTier] * 16L * mInventory.length;
+ }
+
+ @Override
+ public long maxEUStore() {
+ return V[mTier] * 64L * mInventory.length;
+ }
+
+ @Override
+ public long maxEUInput() {
+ return V[mTier];
+ }
+
+ @Override
+ public long maxEUOutput() {
+ return V[mTier];
+ }
+
+ @Override
+ public long maxAmperesIn() {
+ return mChargeableCount * 2L;
+ }
+
+ @Override
+ public long maxAmperesOut() {
+ return mBatteryCount;
+ }
+
+ @Override
+ public int rechargerSlotStartIndex() {
+ return 0;
+ }
+
+ @Override
+ public int dechargerSlotStartIndex() {
+ return 0;
+ }
+
+ @Override
+ public int rechargerSlotCount() {
+ return mCharge ? mInventory.length : 0;
+ }
+
+ @Override
+ public int dechargerSlotCount() {
+ return mDecharge ? mInventory.length : 0;
+ }
+
+ @Override
+ public int getProgresstime() {
+ return (int) getBaseMetaTileEntity().getUniversalEnergyStored();
+ }
+
+ @Override
+ public int maxProgresstime() {
+ return (int) getBaseMetaTileEntity().getUniversalEnergyCapacity();
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ //
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ //
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ return true;
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (aBaseMetaTileEntity.isServerSide()) {
+ mCharge = aBaseMetaTileEntity.getStoredEU() / 2 > aBaseMetaTileEntity.getEUCapacity() / 3;
+ mDecharge = aBaseMetaTileEntity.getStoredEU() < aBaseMetaTileEntity.getEUCapacity() / 3;
+ mBatteryCount = 0;
+ mChargeableCount = 0;
+ for (ItemStack tStack : mInventory) if (GT_ModHandler.isElectricItem(tStack, mTier)) {
+ if (GT_ModHandler.isChargerItem(tStack)) mBatteryCount++;
+ mChargeableCount++;
+ }
+ }
+ count++;
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ if (GT_ModHandler.isElectricItem(aStack) && aStack.getUnlocalizedName()
+ .startsWith("gt.metaitem.01.")) {
+ String name = aStack.getUnlocalizedName();
+ if (name.equals("gt.metaitem.01.32510") || name.equals("gt.metaitem.01.32511")
+ || name.equals("gt.metaitem.01.32520")
+ || name.equals("gt.metaitem.01.32521")
+ || name.equals("gt.metaitem.01.32530")
+ || name.equals("gt.metaitem.01.32531")) {
+ return ic2.api.item.ElectricItem.manager.getCharge(aStack) == 0;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ if (!GT_Utility.isStackValid(aStack)) {
+ return false;
+ }
+ return mInventory[aIndex] == null && GT_ModHandler.isElectricItem(aStack, this.mTier);
+ }
+
+ @Override
+ public int getInventoryStackLimit() {
+ return 1;
+ }
+
+ public long[] getStoredEnergy() {
+ boolean scaleOverflow = false;
+ boolean storedOverflow = false;
+ long tScale = getBaseMetaTileEntity().getEUCapacity();
+ long tStored = getBaseMetaTileEntity().getStoredEU();
+ long tStep = 0;
+ if (mInventory != null) {
+ for (ItemStack aStack : mInventory) {
+ if (GT_ModHandler.isElectricItem(aStack)) {
+
+ if (aStack.getItem() instanceof GT_MetaBase_Item) {
+ Long[] stats = ((GT_MetaBase_Item) aStack.getItem()).getElectricStats(aStack);
+ if (stats != null) {
+ if (stats[0] > Long.MAX_VALUE / 2) {
+ scaleOverflow = true;
+ }
+ tScale = tScale + stats[0];
+ tStep = ((GT_MetaBase_Item) aStack.getItem()).getRealCharge(aStack);
+ if (tStep > Long.MAX_VALUE / 2) {
+ storedOverflow = true;
+ }
+ tStored = tStored + tStep;
+ }
+ } else if (aStack.getItem() instanceof IElectricItem) {
+ tStored = tStored + (long) ic2.api.item.ElectricItem.manager.getCharge(aStack);
+ tScale = tScale + (long) ((IElectricItem) aStack.getItem()).getMaxCharge(aStack);
+ }
+ }
+ }
+ }
+ if (scaleOverflow) {
+ tScale = Long.MAX_VALUE;
+ }
+ if (storedOverflow) {
+ tStored = Long.MAX_VALUE;
+ }
+ return new long[] { tStored, tScale };
+ }
+
+ @Override
+ public String[] getInfoData() {
+ updateStorageInfo();
+
+ return new String[] { EnumChatFormatting.BLUE + getLocalName() + EnumChatFormatting.RESET, "Stored Items:",
+ EnumChatFormatting.GREEN + GT_Utility.formatNumbers(mStored)
+ + EnumChatFormatting.RESET
+ + " EU / "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(mMax)
+ + EnumChatFormatting.RESET
+ + " EU",
+ "Average input:", GT_Utility.formatNumbers(getBaseMetaTileEntity().getAverageElectricInput()) + " EU/t",
+ "Average output:", GT_Utility.formatNumbers(getBaseMetaTileEntity().getAverageElectricOutput()) + " EU/t" };
+ }
+
+ private void updateStorageInfo() {
+ if (mMax == 0 || (count > 20)) {
+ long[] tmp = getStoredEnergy();
+ mStored = tmp[0];
+ mMax = tmp[1];
+ count = 0;
+ }
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ NBTTagCompound tag = accessor.getNBTData();
+ currenttip.add(
+ StatCollector.translateToLocalFormatted(
+ "GT5U.waila.energy.stored",
+ GT_Utility.formatNumbers(tag.getLong("mStored")),
+ GT_Utility.formatNumbers(tag.getLong("mMax"))));
+ long avgIn = tag.getLong("AvgIn");
+ long avgOut = tag.getLong("AvgOut");
+ currenttip.add(
+ StatCollector.translateToLocalFormatted(
+ "GT5U.waila.energy.avg_in_with_amperage",
+ GT_Utility.formatNumbers(avgIn),
+ GT_Utility.getAmperageForTier(avgIn, (byte) getInputTier()),
+ GT_Utility.getColoredTierNameFromTier((byte) getInputTier())));
+ currenttip.add(
+ StatCollector.translateToLocalFormatted(
+ "GT5U.waila.energy.avg_out_with_amperage",
+ GT_Utility.formatNumbers(avgOut),
+ GT_Utility.getAmperageForTier(avgOut, (byte) getOutputTier()),
+ GT_Utility.getColoredTierNameFromTier((byte) getOutputTier())));
+ super.getWailaBody(itemStack, currenttip, accessor, config);
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ updateStorageInfo();
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ tag.setLong("mStored", mStored);
+ tag.setLong("mMax", mMax);
+ tag.setLong("AvgIn", getBaseMetaTileEntity().getAverageElectricInput());
+ tag.setLong("AvgOut", getBaseMetaTileEntity().getAverageElectricOutput());
+ }
+
+ @Override
+ public boolean isGivingInformation() {
+ return true;
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ switch (mInventory.length) {
+ case 4 -> builder.widget(
+ SlotGroup.ofItemHandler(inventoryHandler, 2)
+ .startFromSlot(0)
+ .endAtSlot(3)
+ .slotCreator(index -> new BaseSlot(inventoryHandler, index) {
+
+ @Override
+ public int getSlotStackLimit() {
+ return 1;
+ }
+ })
+ .background(getGUITextureSet().getItemSlot())
+ .build()
+ .setPos(70, 25));
+ case 9 -> builder.widget(
+ SlotGroup.ofItemHandler(inventoryHandler, 3)
+ .startFromSlot(0)
+ .endAtSlot(8)
+ .slotCreator(index -> new BaseSlot(inventoryHandler, index) {
+
+ @Override
+ public int getSlotStackLimit() {
+ return 1;
+ }
+ })
+ .background(getGUITextureSet().getItemSlot())
+ .build()
+ .setPos(61, 16));
+ case 16 -> builder.widget(
+ SlotGroup.ofItemHandler(inventoryHandler, 4)
+ .startFromSlot(0)
+ .endAtSlot(15)
+ .slotCreator(index -> new BaseSlot(inventoryHandler, index) {
+
+ @Override
+ public int getSlotStackLimit() {
+ return 1;
+ }
+ })
+ .background(getGUITextureSet().getItemSlot())
+ .build()
+ .setPos(52, 7));
+ default -> builder.widget(
+ SlotGroup.ofItemHandler(inventoryHandler, 1)
+ .startFromSlot(0)
+ .endAtSlot(0)
+ .slotCreator(index -> new BaseSlot(inventoryHandler, index) {
+
+ @Override
+ public int getSlotStackLimit() {
+ return 1;
+ }
+ })
+ .background(getGUITextureSet().getItemSlot())
+ .build()
+ .setPos(79, 34));
+ }
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicGenerator.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicGenerator.java
new file mode 100644
index 0000000000..7a968afc4b
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicGenerator.java
@@ -0,0 +1,339 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.GT_Values.V;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.fluids.IFluidContainerItem;
+import net.minecraftforge.fluids.IFluidHandler;
+
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.OrePrefixes;
+import gregtech.api.enums.Textures;
+import gregtech.api.gui.modularui.GT_UIInfos;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.interfaces.tileentity.RecipeMapWorkable;
+import gregtech.api.objects.ItemData;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.maps.FuelBackend;
+import gregtech.api.util.GT_OreDictUnificator;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.GT_Pollution;
+
+public abstract class GT_MetaTileEntity_BasicGenerator extends GT_MetaTileEntity_BasicTank
+ implements RecipeMapWorkable {
+
+ public GT_MetaTileEntity_BasicGenerator(int aID, String aName, String aNameRegional, int aTier, String aDescription,
+ ITexture... aTextures) {
+ super(aID, aName, aNameRegional, aTier, 3, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_BasicGenerator(int aID, String aName, String aNameRegional, int aTier,
+ String[] aDescription, ITexture... aTextures) {
+ super(aID, aName, aNameRegional, aTier, 3, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_BasicGenerator(String aName, int aTier, String aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 3, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_BasicGenerator(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 3, aDescription, aTextures);
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+ ITexture[][][] rTextures = new ITexture[10][17][];
+ for (byte i = -1; i < 16; i++) {
+ rTextures[0][i + 1] = getFront(i);
+ rTextures[1][i + 1] = getBack(i);
+ rTextures[2][i + 1] = getBottom(i);
+ rTextures[3][i + 1] = getTop(i);
+ rTextures[4][i + 1] = getSides(i);
+ rTextures[5][i + 1] = getFrontActive(i);
+ rTextures[6][i + 1] = getBackActive(i);
+ rTextures[7][i + 1] = getBottomActive(i);
+ rTextures[8][i + 1] = getTopActive(i);
+ rTextures[9][i + 1] = getSidesActive(i);
+ }
+ return rTextures;
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection side,
+ ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) {
+ return mTextures[(active ? 5 : 0) + (side == facingDirection ? 0
+ : side == facingDirection.getOpposite() ? 1
+ : side == ForgeDirection.DOWN ? 2 : side == ForgeDirection.UP ? 3 : 4)][colorIndex + 1];
+ }
+
+ @Override
+ public String[] getDescription() {
+ String[] desc = new String[mDescriptionArray.length + 1];
+ System.arraycopy(mDescriptionArray, 0, desc, 0, mDescriptionArray.length);
+ desc[mDescriptionArray.length] = "Fuel Efficiency: " + getEfficiency() + "%";
+ return desc;
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ return true;
+ }
+
+ public ITexture[] getFront(byte aColor) {
+ return new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][aColor + 1] };
+ }
+
+ public ITexture[] getBack(byte aColor) {
+ return new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][aColor + 1] };
+ }
+
+ public ITexture[] getBottom(byte aColor) {
+ return new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][aColor + 1] };
+ }
+
+ public ITexture[] getTop(byte aColor) {
+ return new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][aColor + 1] };
+ }
+
+ public ITexture[] getSides(byte aColor) {
+ return new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][aColor + 1] };
+ }
+
+ public ITexture[] getFrontActive(byte aColor) {
+ return getFront(aColor);
+ }
+
+ public ITexture[] getBackActive(byte aColor) {
+ return getBack(aColor);
+ }
+
+ public ITexture[] getBottomActive(byte aColor) {
+ return getBottom(aColor);
+ }
+
+ public ITexture[] getTopActive(byte aColor) {
+ return getTop(aColor);
+ }
+
+ public ITexture[] getSidesActive(byte aColor) {
+ return getSides(aColor);
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection side) {
+ return true;
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return false;
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return aIndex < 2;
+ }
+
+ @Override
+ public boolean isEnetOutput() {
+ return true;
+ }
+
+ @Override
+ public boolean isOutputFacing(ForgeDirection side) {
+ return true;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public long maxEUOutput() {
+ return getBaseMetaTileEntity().isAllowedToWork() ? V[mTier] : 0L;
+ }
+
+ @Override
+ public long maxEUStore() {
+ return Math.max(getEUVar(), V[mTier] * 80L + getMinimumStoredEU());
+ }
+
+ @Override
+ public boolean doesFillContainers() {
+ return getBaseMetaTileEntity().isAllowedToWork();
+ // return false;
+ }
+
+ @Override
+ public boolean doesEmptyContainers() {
+ return getBaseMetaTileEntity().isAllowedToWork();
+ }
+
+ @Override
+ public boolean canTankBeFilled() {
+ return getBaseMetaTileEntity().isAllowedToWork();
+ }
+
+ @Override
+ public boolean canTankBeEmptied() {
+ return getBaseMetaTileEntity().isAllowedToWork();
+ }
+
+ @Override
+ public boolean displaysItemStack() {
+ return true;
+ }
+
+ @Override
+ public boolean displaysStackSize() {
+ return false;
+ }
+
+ @Override
+ public boolean isFluidInputAllowed(FluidStack aFluid) {
+ return getFuelValue(aFluid) > 0;
+ }
+
+ @Override
+ public boolean isLiquidOutput(ForgeDirection side) {
+ // return super.isLiquidOutput(aSide);
+ return false;
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (aBaseMetaTileEntity.isServerSide() && aBaseMetaTileEntity.isAllowedToWork() && aTick % 10 == 0) {
+ if (mFluid != null) {
+ long tFuelValue = getFuelValue(mFluid), tConsumed = consumedFluidPerOperation(mFluid);
+ if (tFuelValue > 0 && tConsumed > 0 && mFluid.amount >= tConsumed) {
+ long tFluidAmountToUse = Math.min(
+ mFluid.amount / tConsumed,
+ (maxEUStore() - aBaseMetaTileEntity.getUniversalEnergyStored()) / tFuelValue);
+ if (tFluidAmountToUse > 0
+ && aBaseMetaTileEntity.increaseStoredEnergyUnits(tFluidAmountToUse * tFuelValue, true)) {
+ // divided by two because this is called every 10 ticks, not 20
+ GT_Pollution.addPollution(getBaseMetaTileEntity(), getPollution() / 2);
+ mFluid.amount -= tFluidAmountToUse * tConsumed;
+ }
+ }
+ }
+
+ if (mInventory[getInputSlot()] != null
+ && aBaseMetaTileEntity.getUniversalEnergyStored() < (maxEUOutput() * 20 + getMinimumStoredEU())
+ && ((GT_Utility.getFluidForFilledItem(mInventory[getInputSlot()], true) != null)
+ || solidFuelOverride(mInventory[getInputSlot()]))) {
+ long tFuelValue = getFuelValue(mInventory[getInputSlot()]);
+ if (tFuelValue <= 0) tFuelValue = getFuelValue(mInventory[getInputSlot()], true);
+ // System.out.println(" tFuelValue : " + tFuelValue );
+ if (tFuelValue > 0) {
+ ItemStack tEmptyContainer = getEmptyContainer(mInventory[getInputSlot()]);
+ if (aBaseMetaTileEntity.addStackToSlot(getOutputSlot(), tEmptyContainer)) {
+ aBaseMetaTileEntity.increaseStoredEnergyUnits(tFuelValue, true);
+ aBaseMetaTileEntity.decrStackSize(getInputSlot(), 1);
+ // divided by two because this is called every 10 ticks, not 20
+ GT_Pollution.addPollution(getBaseMetaTileEntity(), getPollution() / 2);
+ }
+ }
+ }
+ }
+
+ if (aBaseMetaTileEntity.isServerSide()) aBaseMetaTileEntity.setActive(
+ aBaseMetaTileEntity.isAllowedToWork()
+ && aBaseMetaTileEntity.getUniversalEnergyStored() >= maxEUOutput() + getMinimumStoredEU());
+ }
+
+ /**
+ * @param stack the fuel stack
+ * @return if the stack is a solid fuel
+ */
+ public boolean solidFuelOverride(ItemStack stack) {
+ // this could be used for a coal generator for example aswell...
+ ItemData association = GT_OreDictUnificator.getAssociation(stack);
+ // if it is a gregtech Item, make sure its not a VOLUMETRIC_FLASK or any type of cell, else do vanilla checks
+ if (association != null) {
+ return !OrePrefixes.CELL_TYPES.contains(association.mPrefix)
+ && !GT_Utility.areStacksEqual(ItemList.VOLUMETRIC_FLASK.get(1L), stack, true);
+ } else {
+ return stack != null && // when the stack is null its not a solid
+ stack.getItem() != null && // when the item in the stack is null its not a solid
+ !(stack.getItem() instanceof IFluidContainerItem) && // when the item is a fluid container its not a
+ // solid...
+ !(stack.getItem() instanceof IFluidHandler) && // when the item is a fluid handler its not a
+ // solid...
+ !stack.getItem()
+ .getUnlocalizedName()
+ .contains("bucket"); // since we cant really check for
+ // buckets...
+ }
+ }
+
+ public abstract int getPollution();
+
+ @Override
+ public abstract RecipeMap<?> getRecipeMap();
+
+ public abstract int getEfficiency();
+
+ public int consumedFluidPerOperation(FluidStack aLiquid) {
+ return 1;
+ }
+
+ public int getFuelValue(FluidStack aLiquid) {
+ long value = getFuelValue(aLiquid, true);
+ return (value > Integer.MAX_VALUE) ? 0 : (int) value;
+ }
+
+ public long getFuelValue(FluidStack aLiquid, boolean aLong) {
+ RecipeMap<?> tRecipes = getRecipeMap();
+ if (aLiquid == null || !(tRecipes.getBackend() instanceof FuelBackend tFuels)) return 0;
+ GT_Recipe tFuel = tFuels.findFuel(aLiquid);
+ if (tFuel == null) return 0;
+
+ return (long) tFuel.mSpecialValue * getEfficiency() * consumedFluidPerOperation(aLiquid) / 100;
+ }
+
+ public int getFuelValue(ItemStack aStack) {
+ long value = getFuelValue(aStack, true);
+ return (value > Integer.MAX_VALUE) ? 0 : (int) value;
+ }
+
+ public long getFuelValue(ItemStack aStack, boolean aLong) {
+ if (GT_Utility.isStackInvalid(aStack) || getRecipeMap() == null) return 0;
+ GT_Recipe tFuel = getRecipeMap().findRecipe(getBaseMetaTileEntity(), false, Long.MAX_VALUE, null, aStack);
+ if (tFuel == null) return 0;
+
+ long liters = 10L; // 1000mb/100
+ return (long) tFuel.mSpecialValue * liters * getEfficiency();
+ }
+
+ public ItemStack getEmptyContainer(ItemStack aStack) {
+ if (GT_Utility.isStackInvalid(aStack) || getRecipeMap() == null) return null;
+ GT_Recipe tFuel = getRecipeMap().findRecipe(getBaseMetaTileEntity(), false, Long.MAX_VALUE, null, aStack);
+ if (tFuel != null) return GT_Utility.copyOrNull(tFuel.getOutput(0));
+ return GT_Utility.getContainerItem(aStack, true);
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return super.allowPutStack(aBaseMetaTileEntity, aIndex, side, aStack) && (getFuelValue(aStack, true) > 0
+ || getFuelValue(GT_Utility.getFluidForFilledItem(aStack, true), true) > 0);
+ }
+
+ @Override
+ public int getCapacity() {
+ return 16000;
+ }
+
+ @Override
+ public int getTankPressure() {
+ return -100;
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicHull.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicHull.java
new file mode 100644
index 0000000000..e5766eee39
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicHull.java
@@ -0,0 +1,175 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.GT_Values.V;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.api.enums.Textures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+
+public class GT_MetaTileEntity_BasicHull extends GT_MetaTileEntity_BasicTank {
+
+ public GT_MetaTileEntity_BasicHull(int aID, String aName, String aNameRegional, int aTier, String aDescription,
+ ITexture... aTextures) {
+ super(aID, aName, aNameRegional, aTier, 1, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_BasicHull(int aID, String aName, String aNameRegional, int aTier, int aInvSlotCount,
+ String aDescription, ITexture... aTextures) {
+ super(aID, aName, aNameRegional, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_BasicHull(String aName, int aTier, int aInvSlotCount, String aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_BasicHull(String aName, int aTier, int aInvSlotCount, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_BasicHull(mName, mTier, mInventory.length, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public boolean isElectric() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnetInput() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnetOutput() {
+ return true;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public boolean isInputFacing(ForgeDirection side) {
+ return !isOutputFacing(side);
+ }
+
+ @Override
+ public boolean isOutputFacing(ForgeDirection side) {
+ return side == getBaseMetaTileEntity().getFrontFacing();
+ }
+
+ @Override
+ public long getMinimumStoredEU() {
+ return 512;
+ }
+
+ @Override
+ public long maxEUStore() {
+ return 512 + V[mTier] * 50;
+ }
+
+ @Override
+ public long maxEUInput() {
+ return V[mTier];
+ }
+
+ @Override
+ public long maxEUOutput() {
+ return V[mTier];
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return true;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return true;
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return true;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aConnected, boolean redstoneLevel) {
+ return mTextures[Math.min(2, side.ordinal()) + (side == aFacing ? 3 : 0)][colorIndex + 1];
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+ ITexture[][][] rTextures = new ITexture[6][17][];
+ for (byte i = -1; i < 16; i++) {
+ rTextures[0][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1] };
+ rTextures[1][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1] };
+ rTextures[2][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1] };
+ rTextures[3][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1],
+ Textures.BlockIcons.OVERLAYS_ENERGY_OUT[mTier] };
+ rTextures[4][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1],
+ Textures.BlockIcons.OVERLAYS_ENERGY_OUT[mTier] };
+ rTextures[5][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1],
+ Textures.BlockIcons.OVERLAYS_ENERGY_OUT[mTier] };
+ }
+ return rTextures;
+ }
+
+ @Override
+ public boolean doesFillContainers() {
+ return false;
+ }
+
+ @Override
+ public boolean doesEmptyContainers() {
+ return false;
+ }
+
+ @Override
+ public boolean canTankBeFilled() {
+ return true;
+ }
+
+ @Override
+ public boolean canTankBeEmptied() {
+ return true;
+ }
+
+ @Override
+ public boolean displaysItemStack() {
+ return false;
+ }
+
+ @Override
+ public boolean displaysStackSize() {
+ return false;
+ }
+
+ @Override
+ public int getCapacity() {
+ return (mTier + 1) * 1000;
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicHull_NonElectric.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicHull_NonElectric.java
new file mode 100644
index 0000000000..b6584b50ab
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicHull_NonElectric.java
@@ -0,0 +1,78 @@
+package gregtech.api.metatileentity.implementations;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+
+public abstract class GT_MetaTileEntity_BasicHull_NonElectric extends GT_MetaTileEntity_BasicHull {
+
+ public GT_MetaTileEntity_BasicHull_NonElectric(int aID, String aName, String aNameRegional, int aTier,
+ String aDescription) {
+ super(aID, aName, aNameRegional, aTier, aDescription);
+ }
+
+ public GT_MetaTileEntity_BasicHull_NonElectric(String aName, int aTier, String aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, 1, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_BasicHull_NonElectric(String aName, int aTier, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, 1, aDescription, aTextures);
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection,
+ ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) {
+ return mTextures[Math.min(2, sideDirection.ordinal())][colorIndex + 1];
+ }
+
+ @Override
+ public boolean isElectric() {
+ return false;
+ }
+
+ @Override
+ public boolean isEnetInput() {
+ return false;
+ }
+
+ @Override
+ public boolean isEnetOutput() {
+ return false;
+ }
+
+ @Override
+ public boolean isInputFacing(ForgeDirection side) {
+ return false;
+ }
+
+ @Override
+ public boolean isOutputFacing(ForgeDirection side) {
+ return false;
+ }
+
+ @Override
+ public long getMinimumStoredEU() {
+ return 0;
+ }
+
+ @Override
+ public long maxEUStore() {
+ return 0;
+ }
+
+ @Override
+ public long maxEUInput() {
+ return 0;
+ }
+
+ @Override
+ public long maxEUOutput() {
+ return 0;
+ }
+
+ @Override
+ public abstract ITexture[][][] getTextureSet(ITexture[] aTextures);
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine.java
new file mode 100644
index 0000000000..377646f85a
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine.java
@@ -0,0 +1,1563 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.GT_Values.V;
+import static gregtech.api.enums.GT_Values.debugCleanroom;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASINGS;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPE_OUT;
+import static gregtech.api.metatileentity.BaseTileEntity.FLUID_TRANSFER_TOOLTIP;
+import static gregtech.api.metatileentity.BaseTileEntity.ITEM_TRANSFER_TOOLTIP;
+import static gregtech.api.metatileentity.BaseTileEntity.NEI_TRANSFER_STEAM_TOOLTIP;
+import static gregtech.api.metatileentity.BaseTileEntity.NEI_TRANSFER_VOLTAGE_TOOLTIP;
+import static gregtech.api.metatileentity.BaseTileEntity.POWER_SOURCE_KEY;
+import static gregtech.api.metatileentity.BaseTileEntity.SPECIAL_SLOT_TOOLTIP;
+import static gregtech.api.metatileentity.BaseTileEntity.STALLED_STUTTERING_TOOLTIP;
+import static gregtech.api.metatileentity.BaseTileEntity.STALLED_VENT_TOOLTIP;
+import static gregtech.api.metatileentity.BaseTileEntity.TOOLTIP_DELAY;
+import static gregtech.api.metatileentity.BaseTileEntity.UNUSED_SLOT_TOOLTIP;
+import static gregtech.api.util.GT_RecipeConstants.EXPLODE;
+import static gregtech.api.util.GT_RecipeConstants.ON_FIRE;
+import static gregtech.api.util.GT_Utility.moveMultipleItemStacks;
+import static net.minecraftforge.common.util.ForgeDirection.DOWN;
+import static net.minecraftforge.common.util.ForgeDirection.UNKNOWN;
+import static net.minecraftforge.common.util.ForgeDirection.UP;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.World;
+import net.minecraftforge.common.DimensionManager;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.fluids.IFluidHandler;
+
+import org.apache.commons.lang3.tuple.Pair;
+
+import com.gtnewhorizons.modularui.api.drawable.IDrawable;
+import com.gtnewhorizons.modularui.api.math.Pos2d;
+import com.gtnewhorizons.modularui.api.math.Size;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.api.widget.Widget;
+import com.gtnewhorizons.modularui.common.fluid.FluidStackTank;
+import com.gtnewhorizons.modularui.common.widget.CycleButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+import com.gtnewhorizons.modularui.common.widget.FluidSlotWidget;
+import com.gtnewhorizons.modularui.common.widget.ProgressBar;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+
+import gregtech.GT_Mod;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.gui.modularui.GT_UIInfos;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.gui.modularui.SteamTexture;
+import gregtech.api.interfaces.ICleanroom;
+import gregtech.api.interfaces.IConfigurationCircuitSupport;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.modularui.IAddGregtechLogo;
+import gregtech.api.interfaces.modularui.IAddUIWidgets;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.interfaces.tileentity.IOverclockDescriptionProvider;
+import gregtech.api.interfaces.tileentity.RecipeMapWorkable;
+import gregtech.api.objects.GT_ItemStack;
+import gregtech.api.objects.overclockdescriber.EUOverclockDescriber;
+import gregtech.api.objects.overclockdescriber.OverclockDescriber;
+import gregtech.api.recipe.BasicUIProperties;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_ClientPreference;
+import gregtech.api.util.GT_CoverBehaviorBase;
+import gregtech.api.util.GT_Log;
+import gregtech.api.util.GT_OreDictUnificator;
+import gregtech.api.util.GT_OverclockCalculator;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_TooltipDataCache;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.GT_Waila;
+import gregtech.common.gui.modularui.UIHelper;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+/**
+ * NEVER INCLUDE THIS FILE IN YOUR MOD!!!
+ * <p/>
+ * This is the main construct for my Basic Machines such as the Automatic Extractor Extend this class to make a simple
+ * Machine
+ */
+public abstract class GT_MetaTileEntity_BasicMachine extends GT_MetaTileEntity_BasicTank implements RecipeMapWorkable,
+ IConfigurationCircuitSupport, IOverclockDescriptionProvider, IAddGregtechLogo, IAddUIWidgets {
+
+ /**
+ * return values for checkRecipe()
+ */
+ protected static final int DID_NOT_FIND_RECIPE = 0, FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS = 1,
+ FOUND_AND_SUCCESSFULLY_USED_RECIPE = 2;
+
+ public static final int OTHER_SLOT_COUNT = 5;
+ public final ItemStack[] mOutputItems;
+ public final int mInputSlotCount, mAmperage;
+ public boolean mAllowInputFromOutputSide = false, mFluidTransfer = false, mItemTransfer = false,
+ mHasBeenUpdated = false, mStuttering = false, mCharge = false, mDecharge = false;
+ public boolean mDisableFilter = true;
+ public boolean mDisableMultiStack = true;
+ public int mProgresstime = 0, mMaxProgresstime = 0, mEUt = 0, mOutputBlocked = 0;
+ public ForgeDirection mMainFacing = ForgeDirection.WEST;
+ public FluidStack mOutputFluid;
+ protected final OverclockDescriber overclockDescriber;
+
+ /**
+ * Contains the Recipe which has been previously used, or null if there was no previous Recipe, which could have
+ * been buffered
+ */
+ protected GT_Recipe mLastRecipe = null;
+
+ private FluidStack mFluidOut;
+ protected final FluidStackTank fluidOutputTank = new FluidStackTank(
+ () -> mFluidOut,
+ fluidStack -> mFluidOut = fluidStack,
+ this::getCapacity);
+
+ /**
+ * Registers machine with single-line description.
+ *
+ * @param aOverlays 0 = SideFacingActive 1 = SideFacingInactive 2 = FrontFacingActive 3 = FrontFacingInactive 4 =
+ * TopFacingActive 5 = TopFacingInactive 6 = BottomFacingActive 7 = BottomFacingInactive ----- Not
+ * all Array Elements have to be initialised, you can also just use 8 Parameters for the Default
+ * Pipe Texture Overlays ----- 8 = BottomFacingPipeActive 9 = BottomFacingPipeInactive 10 =
+ * TopFacingPipeActive 11 = TopFacingPipeInactive 12 = SideFacingPipeActive 13 =
+ * SideFacingPipeInactive
+ */
+ public GT_MetaTileEntity_BasicMachine(int aID, String aName, String aNameRegional, int aTier, int aAmperage,
+ String aDescription, int aInputSlotCount, int aOutputSlotCount, ITexture... aOverlays) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ OTHER_SLOT_COUNT + aInputSlotCount + aOutputSlotCount + 1,
+ aDescription,
+ aOverlays);
+ mInputSlotCount = Math.max(0, aInputSlotCount);
+ mOutputItems = new ItemStack[Math.max(0, aOutputSlotCount)];
+ mAmperage = aAmperage;
+ overclockDescriber = createOverclockDescriber();
+ }
+
+ /**
+ * Registers machine with multi-line descriptions.
+ */
+ public GT_MetaTileEntity_BasicMachine(int aID, String aName, String aNameRegional, int aTier, int aAmperage,
+ String[] aDescription, int aInputSlotCount, int aOutputSlotCount, ITexture... aOverlays) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ OTHER_SLOT_COUNT + aInputSlotCount + aOutputSlotCount + 1,
+ aDescription,
+ aOverlays);
+ mInputSlotCount = Math.max(0, aInputSlotCount);
+ mOutputItems = new ItemStack[Math.max(0, aOutputSlotCount)];
+ mAmperage = aAmperage;
+ overclockDescriber = createOverclockDescriber();
+ }
+
+ /**
+ * For {@link #newMetaEntity}.
+ */
+ public GT_MetaTileEntity_BasicMachine(String aName, int aTier, int aAmperage, String[] aDescription,
+ ITexture[][][] aTextures, int aInputSlotCount, int aOutputSlotCount) {
+ super(aName, aTier, OTHER_SLOT_COUNT + aInputSlotCount + aOutputSlotCount + 1, aDescription, aTextures);
+ mInputSlotCount = Math.max(0, aInputSlotCount);
+ mOutputItems = new ItemStack[Math.max(0, aOutputSlotCount)];
+ mAmperage = aAmperage;
+ overclockDescriber = createOverclockDescriber();
+ }
+
+ /**
+ * To be called by the constructor to initialize this instance's overclock behavior
+ */
+ protected OverclockDescriber createOverclockDescriber() {
+ return new EUOverclockDescriber(mTier, mAmperage);
+ }
+
+ protected boolean isValidMainFacing(ForgeDirection side) {
+ return (side.flag & (UP.flag | DOWN.flag | UNKNOWN.flag)) == 0; // Horizontal
+ }
+
+ public boolean setMainFacing(ForgeDirection side) {
+ if (!isValidMainFacing(side)) return false;
+ mMainFacing = side;
+ if (getBaseMetaTileEntity().getFrontFacing() == mMainFacing) {
+ getBaseMetaTileEntity().setFrontFacing(side.getOpposite());
+ }
+ onFacingChange();
+ onMachineBlockUpdate();
+ return true;
+ }
+
+ @Override
+ public void onFacingChange() {
+ super.onFacingChange();
+ // Set up the correct facing (front towards player, output opposite) client-side before the server packet
+ // arrives
+ if (mMainFacing == UNKNOWN) {
+ IGregTechTileEntity te = getBaseMetaTileEntity();
+ if (te != null && te.getWorld().isRemote) {
+ mMainFacing = te.getFrontFacing();
+ te.setFrontFacing(te.getBackFacing());
+ }
+ }
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+ ITexture[][][] rTextures = new ITexture[14][17][];
+ aTextures = Arrays.copyOf(aTextures, 14);
+
+ for (int i = 0; i < aTextures.length; i++) if (aTextures[i] != null) for (byte c = -1; c < 16; c++) {
+ if (rTextures[i][c + 1] == null)
+ rTextures[i][c + 1] = new ITexture[] { MACHINE_CASINGS[mTier][c + 1], aTextures[i] };
+ }
+
+ for (byte c = -1; c < 16; c++) {
+ if (rTextures[0][c + 1] == null) rTextures[0][c + 1] = getSideFacingActive(c);
+ if (rTextures[1][c + 1] == null) rTextures[1][c + 1] = getSideFacingInactive(c);
+ if (rTextures[2][c + 1] == null) rTextures[2][c + 1] = getFrontFacingActive(c);
+ if (rTextures[3][c + 1] == null) rTextures[3][c + 1] = getFrontFacingInactive(c);
+ if (rTextures[4][c + 1] == null) rTextures[4][c + 1] = getTopFacingActive(c);
+ if (rTextures[5][c + 1] == null) rTextures[5][c + 1] = getTopFacingInactive(c);
+ if (rTextures[6][c + 1] == null) rTextures[6][c + 1] = getBottomFacingActive(c);
+ if (rTextures[7][c + 1] == null) rTextures[7][c + 1] = getBottomFacingInactive(c);
+ if (rTextures[8][c + 1] == null) rTextures[8][c + 1] = getBottomFacingPipeActive(c);
+ if (rTextures[9][c + 1] == null) rTextures[9][c + 1] = getBottomFacingPipeInactive(c);
+ if (rTextures[10][c + 1] == null) rTextures[10][c + 1] = getTopFacingPipeActive(c);
+ if (rTextures[11][c + 1] == null) rTextures[11][c + 1] = getTopFacingPipeInactive(c);
+ if (rTextures[12][c + 1] == null) rTextures[12][c + 1] = getSideFacingPipeActive(c);
+ if (rTextures[13][c + 1] == null) rTextures[13][c + 1] = getSideFacingPipeInactive(c);
+ }
+ return rTextures;
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection,
+ ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) {
+ final int textureIndex;
+ if ((mMainFacing.flag & (UP.flag | DOWN.flag)) != 0) { // UP or DOWN
+ if (sideDirection == facingDirection) {
+ textureIndex = active ? 2 : 3;
+ } else {
+ textureIndex = switch (sideDirection) {
+ case DOWN -> active ? 6 : 7;
+ case UP -> active ? 4 : 5;
+ default -> active ? 0 : 1;
+ };
+ }
+ } else {
+ if (sideDirection == mMainFacing) {
+ textureIndex = active ? 2 : 3;
+ } else {
+ if (showPipeFacing() && sideDirection == facingDirection) {
+ textureIndex = switch (sideDirection) {
+ case DOWN -> active ? 8 : 9;
+ case UP -> active ? 10 : 11;
+ default -> active ? 12 : 13;
+ };
+ } else {
+ textureIndex = switch (sideDirection) {
+ case DOWN -> active ? 6 : 7;
+ case UP -> active ? 4 : 5;
+ default -> active ? 0 : 1;
+ };
+ }
+ }
+ }
+ return mTextures[textureIndex][colorIndex + 1];
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return false;
+ }
+
+ @Override
+ public boolean isOverclockerUpgradable() {
+ return false;
+ }
+
+ @Override
+ public boolean isTransformerUpgradable() {
+ return false;
+ }
+
+ @Override
+ public boolean isElectric() {
+ return true;
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return aIndex > 0 && super.isValidSlot(aIndex)
+ && aIndex != getCircuitSlot()
+ && aIndex != OTHER_SLOT_COUNT + mInputSlotCount + mOutputItems.length;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ // Either mMainFacing or mMainFacing is horizontal
+ return ((facing.flag | mMainFacing.flag) & ~(UP.flag | DOWN.flag | UNKNOWN.flag)) != 0;
+ }
+
+ @Override
+ public boolean isEnetInput() {
+ return true;
+ }
+
+ @Override
+ public boolean isInputFacing(ForgeDirection side) {
+ return side != mMainFacing;
+ }
+
+ @Override
+ public boolean isOutputFacing(ForgeDirection side) {
+ return false;
+ }
+
+ @Override
+ public boolean isTeleporterCompatible() {
+ return false;
+ }
+
+ @Override
+ public boolean isLiquidInput(ForgeDirection side) {
+ return side != mMainFacing && (mAllowInputFromOutputSide || side != getBaseMetaTileEntity().getFrontFacing());
+ }
+
+ @Override
+ public boolean isLiquidOutput(ForgeDirection side) {
+ return side != mMainFacing;
+ }
+
+ @Override
+ public long getMinimumStoredEU() {
+ return V[mTier] * 16L;
+ }
+
+ @Override
+ public long maxEUStore() {
+ return V[mTier] * 64L;
+ }
+
+ @Override
+ public long maxEUInput() {
+ return V[mTier];
+ }
+
+ @Override
+ public long maxSteamStore() {
+ return maxEUStore();
+ }
+
+ @Override
+ public long maxAmperesIn() {
+ return ((long) mEUt * 2L) / V[mTier] + 1L;
+ }
+
+ @Override
+ public int getInputSlot() {
+ return OTHER_SLOT_COUNT;
+ }
+
+ @Override
+ public int getOutputSlot() {
+ return OTHER_SLOT_COUNT + mInputSlotCount;
+ }
+
+ public int getSpecialSlotIndex() {
+ return 3;
+ }
+
+ @Override
+ public int getStackDisplaySlot() {
+ return 2;
+ }
+
+ @Override
+ public int rechargerSlotStartIndex() {
+ return 1;
+ }
+
+ @Override
+ public int dechargerSlotStartIndex() {
+ return 1;
+ }
+
+ @Override
+ public int rechargerSlotCount() {
+ return mCharge ? 1 : 0;
+ }
+
+ @Override
+ public int dechargerSlotCount() {
+ return mDecharge ? 1 : 0;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public int getProgresstime() {
+ return mProgresstime;
+ }
+
+ @Override
+ public int maxProgresstime() {
+ return mMaxProgresstime;
+ }
+
+ @Override
+ public int increaseProgress(int aProgress) {
+ mProgresstime += aProgress;
+ return mMaxProgresstime - mProgresstime;
+ }
+
+ @Override
+ public boolean isFluidInputAllowed(FluidStack aFluid) {
+ return getFillableStack() != null || (getRecipeMap() != null && getRecipeMap().containsInput(aFluid));
+ }
+
+ @Override
+ public boolean isFluidChangingAllowed() {
+ return true;
+ }
+
+ @Override
+ public boolean doesFillContainers() {
+ return false;
+ }
+
+ @Override
+ public boolean doesEmptyContainers() {
+ return false;
+ }
+
+ @Override
+ public boolean canTankBeFilled() {
+ return true;
+ }
+
+ @Override
+ public boolean canTankBeEmptied() {
+ return true;
+ }
+
+ @Override
+ public boolean displaysItemStack() {
+ return true;
+ }
+
+ @Override
+ public boolean displaysStackSize() {
+ return true;
+ }
+
+ @Override
+ public FluidStack getDrainableStack() {
+ return mFluidOut;
+ }
+
+ @Override
+ public FluidStack setDrainableStack(FluidStack aFluid) {
+ markDirty();
+ mFluidOut = aFluid;
+ return mFluidOut;
+ }
+
+ @Override
+ public boolean isDrainableStackSeparate() {
+ return true;
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ if (aBaseMetaTileEntity.isClientSide()) return true;
+ if (!GT_Mod.gregtechproxy.mForceFreeFace) {
+ GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ return true;
+ }
+ for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
+ if (aBaseMetaTileEntity.getAirAtSide(side)) {
+ GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ return true;
+ }
+ }
+ GT_Utility.sendChatToPlayer(aPlayer, "No free Side!");
+ return true;
+ }
+
+ @Override
+ public void initDefaultModes(NBTTagCompound aNBT) {
+ mMainFacing = ForgeDirection.UNKNOWN;
+ if (!getBaseMetaTileEntity().getWorld().isRemote) {
+ final GT_ClientPreference tPreference = GT_Mod.gregtechproxy
+ .getClientPreference(getBaseMetaTileEntity().getOwnerUuid());
+ if (tPreference != null) {
+ mDisableFilter = !tPreference.isSingleBlockInitialFilterEnabled();
+ mDisableMultiStack = !tPreference.isSingleBlockInitialMultiStackEnabled();
+ }
+ }
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setBoolean("mFluidTransfer", mFluidTransfer);
+ aNBT.setBoolean("mItemTransfer", mItemTransfer);
+ aNBT.setBoolean("mHasBeenUpdated", mHasBeenUpdated);
+ aNBT.setBoolean("mAllowInputFromOutputSide", mAllowInputFromOutputSide);
+ aNBT.setBoolean("mDisableFilter", mDisableFilter);
+ aNBT.setBoolean("mDisableMultiStack", mDisableMultiStack);
+ aNBT.setInteger("mEUt", mEUt);
+ aNBT.setInteger("mMainFacing", mMainFacing.ordinal());
+ aNBT.setInteger("mProgresstime", mProgresstime);
+ aNBT.setInteger("mMaxProgresstime", mMaxProgresstime);
+ if (mOutputFluid != null) aNBT.setTag("mOutputFluid", mOutputFluid.writeToNBT(new NBTTagCompound()));
+ if (mFluidOut != null) aNBT.setTag("mFluidOut", mFluidOut.writeToNBT(new NBTTagCompound()));
+
+ for (int i = 0; i < mOutputItems.length; i++)
+ if (mOutputItems[i] != null) GT_Utility.saveItem(aNBT, "mOutputItem" + i, mOutputItems[i]);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ mFluidTransfer = aNBT.getBoolean("mFluidTransfer");
+ mItemTransfer = aNBT.getBoolean("mItemTransfer");
+ mHasBeenUpdated = aNBT.getBoolean("mHasBeenUpdated");
+ mAllowInputFromOutputSide = aNBT.getBoolean("mAllowInputFromOutputSide");
+ mDisableFilter = aNBT.getBoolean("mDisableFilter");
+ mDisableMultiStack = aNBT.getBoolean("mDisableMultiStack");
+ mEUt = aNBT.getInteger("mEUt");
+ mMainFacing = ForgeDirection.getOrientation(aNBT.getInteger("mMainFacing"));
+ mProgresstime = aNBT.getInteger("mProgresstime");
+ mMaxProgresstime = aNBT.getInteger("mMaxProgresstime");
+ mOutputFluid = FluidStack.loadFluidStackFromNBT(aNBT.getCompoundTag("mOutputFluid"));
+ mFluidOut = FluidStack.loadFluidStackFromNBT(aNBT.getCompoundTag("mFluidOut"));
+
+ for (int i = 0; i < mOutputItems.length; i++) mOutputItems[i] = GT_Utility.loadItem(aNBT, "mOutputItem" + i);
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+
+ if (aBaseMetaTileEntity.isServerSide()) {
+ mCharge = aBaseMetaTileEntity.getStoredEU() / 2 > aBaseMetaTileEntity.getEUCapacity() / 3;
+ mDecharge = aBaseMetaTileEntity.getStoredEU() < aBaseMetaTileEntity.getEUCapacity() / 3;
+
+ doDisplayThings();
+
+ boolean tSucceeded = false;
+
+ if (mMaxProgresstime > 0 && (mProgresstime >= 0 || aBaseMetaTileEntity.isAllowedToWork())) {
+ markDirty();
+ aBaseMetaTileEntity.setActive(true);
+ if (mProgresstime < 0 || drainEnergyForProcess(mEUt)) {
+ if (++mProgresstime >= mMaxProgresstime) {
+ for (int i = 0; i < mOutputItems.length; i++)
+ for (int j = 0; j < mOutputItems.length; j++) if (aBaseMetaTileEntity
+ .addStackToSlot(getOutputSlot() + ((j + i) % mOutputItems.length), mOutputItems[i]))
+ break;
+ if (mOutputFluid != null)
+ if (getDrainableStack() == null) setDrainableStack(mOutputFluid.copy());
+ else if (mOutputFluid.isFluidEqual(getDrainableStack()))
+ getDrainableStack().amount += mOutputFluid.amount;
+ Arrays.fill(mOutputItems, null);
+ mOutputFluid = null;
+ mEUt = 0;
+ mProgresstime = 0;
+ mMaxProgresstime = 0;
+ mStuttering = false;
+ tSucceeded = true;
+ endProcess();
+ }
+ if (mProgresstime > 5) mStuttering = false;
+ } else {
+ if (!mStuttering) {
+ stutterProcess();
+ if (canHaveInsufficientEnergy()) mProgresstime = -100;
+ mStuttering = true;
+ }
+ }
+ } else {
+ aBaseMetaTileEntity.setActive(false);
+ }
+
+ boolean tRemovedOutputFluid = false;
+
+ if (doesAutoOutputFluids() && getDrainableStack() != null
+ && aBaseMetaTileEntity.getFrontFacing() != mMainFacing
+ && (tSucceeded || aTick % 20 == 0)) {
+ IFluidHandler tTank = aBaseMetaTileEntity.getITankContainerAtSide(aBaseMetaTileEntity.getFrontFacing());
+ if (tTank != null) {
+ FluidStack tDrained = drain(1000, false);
+ if (tDrained != null) {
+ final int tFilledAmount = tTank.fill(aBaseMetaTileEntity.getBackFacing(), tDrained, false);
+ if (tFilledAmount > 0)
+ tTank.fill(aBaseMetaTileEntity.getBackFacing(), drain(tFilledAmount, true), true);
+ }
+ }
+ if (getDrainableStack() == null) tRemovedOutputFluid = true;
+ }
+
+ if (doesAutoOutput() && !isOutputEmpty()
+ && aBaseMetaTileEntity.getFrontFacing() != mMainFacing
+ && (tSucceeded || mOutputBlocked % 300 == 1
+ || aBaseMetaTileEntity.hasInventoryBeenModified()
+ || aTick % 600 == 0)) {
+ TileEntity tTileEntity2 = aBaseMetaTileEntity.getTileEntityAtSide(aBaseMetaTileEntity.getFrontFacing());
+ long tStoredEnergy = aBaseMetaTileEntity.getUniversalEnergyStored();
+ int tMaxStacks = (int) (tStoredEnergy / 64L);
+ if (tMaxStacks > mOutputItems.length) tMaxStacks = mOutputItems.length;
+
+ moveMultipleItemStacks(
+ aBaseMetaTileEntity,
+ tTileEntity2,
+ aBaseMetaTileEntity.getFrontFacing(),
+ aBaseMetaTileEntity.getBackFacing(),
+ null,
+ false,
+ (byte) 64,
+ (byte) 1,
+ (byte) 64,
+ (byte) 1,
+ tMaxStacks);
+ }
+
+ if (mOutputBlocked != 0) if (isOutputEmpty()) mOutputBlocked = 0;
+ else mOutputBlocked++;
+
+ if (allowToCheckRecipe()) {
+ if (mMaxProgresstime <= 0 && aBaseMetaTileEntity.isAllowedToWork()
+ && (tRemovedOutputFluid || tSucceeded
+ || aBaseMetaTileEntity.hasInventoryBeenModified()
+ || aTick % 600 == 0
+ || aBaseMetaTileEntity.hasWorkJustBeenEnabled())
+ && hasEnoughEnergyToCheckRecipe()) {
+ if (checkRecipe() == FOUND_AND_SUCCESSFULLY_USED_RECIPE) {
+ if (getSpecialSlot() != null && getSpecialSlot().stackSize <= 0)
+ mInventory[getSpecialSlotIndex()] = null;
+ for (int i = getInputSlot(), j = i + mInputSlotCount; i < j; i++)
+ if (mInventory[i] != null && mInventory[i].stackSize <= 0) mInventory[i] = null;
+ for (int i = 0; i < mOutputItems.length; i++) {
+ mOutputItems[i] = GT_Utility.copyOrNull(mOutputItems[i]);
+ if (mOutputItems[i] != null && mOutputItems[i].stackSize > 64)
+ mOutputItems[i].stackSize = 64;
+ mOutputItems[i] = GT_OreDictUnificator.get(true, mOutputItems[i]);
+ }
+ if (mFluid != null && mFluid.amount <= 0) mFluid = null;
+ mMaxProgresstime = Math.max(1, mMaxProgresstime);
+ if (GT_Utility.isDebugItem(mInventory[dechargerSlotStartIndex()])) {
+ mEUt = mMaxProgresstime = 1;
+ }
+ startProcess();
+ } else {
+ mMaxProgresstime = 0;
+ Arrays.fill(mOutputItems, null);
+ mOutputFluid = null;
+ }
+ }
+ } else {
+ if (!mStuttering) {
+ stutterProcess();
+ mStuttering = true;
+ }
+ }
+ }
+ // Only using mNeedsSteamVenting right now and assigning it to 64 to space in the range for more single block
+ // machine problems.
+ // Value | Class | Field
+ // 1 | GT_MetaTileEntity_BasicMachine | mStuttering
+ // 64 | GT_MetaTileEntity_BasicMachine_Bronze | mNeedsSteamVenting
+ aBaseMetaTileEntity.setErrorDisplayID((aBaseMetaTileEntity.getErrorDisplayID() & ~127)); // | (mStuttering ? 1 :
+ // 0));
+ }
+
+ protected void doDisplayThings() {
+ if (!isValidMainFacing(mMainFacing) && isValidMainFacing(getBaseMetaTileEntity().getFrontFacing())) {
+ mMainFacing = getBaseMetaTileEntity().getFrontFacing();
+ }
+ if (isValidMainFacing(mMainFacing) && !mHasBeenUpdated) {
+ mHasBeenUpdated = true;
+ getBaseMetaTileEntity().setFrontFacing(getBaseMetaTileEntity().getBackFacing());
+ }
+ }
+
+ protected boolean hasEnoughEnergyToCheckRecipe() {
+ return getBaseMetaTileEntity().isUniversalEnergyStored(getMinimumStoredEU() / 2);
+ }
+
+ protected boolean drainEnergyForProcess(long aEUt) {
+ return getBaseMetaTileEntity().decreaseStoredEnergyUnits(aEUt, false);
+ }
+
+ /**
+ * Calculates overclock based on {@link #overclockDescriber}.
+ */
+ protected void calculateCustomOverclock(GT_Recipe recipe) {
+ GT_OverclockCalculator calculator = overclockDescriber.createCalculator(
+ new GT_OverclockCalculator().setRecipeEUt(recipe.mEUt)
+ .setDuration(recipe.mDuration)
+ .setOneTickDiscount(true),
+ recipe);
+ calculator.calculate();
+ mEUt = (int) calculator.getConsumption();
+ mMaxProgresstime = calculator.getDuration();
+ }
+
+ /**
+ * Helper method for calculating simple overclock.
+ */
+ protected void calculateOverclockedNess(int eut, int duration) {
+ GT_OverclockCalculator calculator = new GT_OverclockCalculator().setRecipeEUt(eut)
+ .setEUt(V[mTier] * mAmperage)
+ .setDuration(duration)
+ .setOneTickDiscount(true)
+ .calculate();
+ mEUt = (int) calculator.getConsumption();
+ mMaxProgresstime = calculator.getDuration();
+ }
+
+ protected ItemStack getSpecialSlot() {
+ return mInventory[getSpecialSlotIndex()];
+ }
+
+ protected ItemStack getOutputAt(int aIndex) {
+ return mInventory[getOutputSlot() + aIndex];
+ }
+
+ protected ItemStack[] getAllOutputs() {
+ ItemStack[] rOutputs = new ItemStack[mOutputItems.length];
+ for (int i = 0; i < mOutputItems.length; i++) rOutputs[i] = getOutputAt(i);
+ return rOutputs;
+ }
+
+ protected boolean canOutput(GT_Recipe aRecipe) {
+ return aRecipe != null && (aRecipe.mNeedsEmptyOutput ? isOutputEmpty() && getDrainableStack() == null
+ : canOutput(aRecipe.getFluidOutput(0)) && canOutput(aRecipe.mOutputs));
+ }
+
+ protected boolean canOutput(ItemStack... aOutputs) {
+ if (aOutputs == null) return true;
+ ItemStack[] tOutputSlots = getAllOutputs();
+ for (int i = 0; i < tOutputSlots.length && i < aOutputs.length; i++)
+ if (tOutputSlots[i] != null && aOutputs[i] != null
+ && (!GT_Utility.areStacksEqual(tOutputSlots[i], aOutputs[i], false)
+ || tOutputSlots[i].stackSize + aOutputs[i].stackSize > tOutputSlots[i].getMaxStackSize())) {
+ mOutputBlocked++;
+ return false;
+ }
+ return true;
+ }
+
+ protected boolean canOutput(FluidStack aOutput) {
+ if (aOutput == null) return true;
+ FluidStack drainableStack = getDrainableStack();
+ if (drainableStack != null && !drainableStack.isFluidEqual(aOutput)) return false;
+ return (drainableStack != null ? drainableStack.amount : 0) + aOutput.amount <= getCapacity();
+ }
+
+ protected ItemStack getInputAt(int aIndex) {
+ return mInventory[getInputSlot() + aIndex];
+ }
+
+ protected ItemStack[] getAllInputs() {
+ int tRealInputSlotCount = this.mInputSlotCount + (allowSelectCircuit() ? 1 : 0);
+ ItemStack[] rInputs = new ItemStack[tRealInputSlotCount];
+ for (int i = 0; i < mInputSlotCount; i++) rInputs[i] = getInputAt(i);
+ if (allowSelectCircuit()) rInputs[mInputSlotCount] = getStackInSlot(getCircuitSlot());
+ return rInputs;
+ }
+
+ protected boolean isOutputEmpty() {
+ boolean rIsEmpty = true;
+ for (ItemStack tOutputSlotContent : getAllOutputs()) if (tOutputSlotContent != null) {
+ rIsEmpty = false;
+ break;
+ }
+ return rIsEmpty;
+ }
+
+ @Override
+ public void onValueUpdate(byte aValue) {
+ mMainFacing = ForgeDirection.getOrientation(aValue);
+ }
+
+ @Override
+ public byte getUpdateData() {
+ return (byte) mMainFacing.ordinal();
+ }
+
+ @Override
+ public void doSound(byte aIndex, double aX, double aY, double aZ) {
+ super.doSound(aIndex, aX, aY, aZ);
+ if (aIndex == 8) GT_Utility.doSoundAtClient(SoundResource.IC2_MACHINES_INTERRUPT_ONE, 100, 1.0F, aX, aY, aZ);
+ }
+
+ public boolean doesAutoOutput() {
+ return mItemTransfer;
+ }
+
+ public boolean doesAutoOutputFluids() {
+ return mFluidTransfer;
+ }
+
+ public boolean allowToCheckRecipe() {
+ return true;
+ }
+
+ public boolean showPipeFacing() {
+ return true;
+ }
+
+ /**
+ * Called whenever the Machine successfully started a Process, useful for Sound Effects
+ */
+ public void startProcess() {
+ //
+ }
+
+ /**
+ * Called whenever the Machine successfully finished a Process, useful for Sound Effects
+ */
+ public void endProcess() {
+ //
+ }
+
+ /**
+ * Called whenever the Machine aborted a Process, useful for Sound Effects
+ */
+ public void abortProcess() {
+ //
+ }
+
+ /**
+ * Called whenever the Machine aborted a Process but still works on it, useful for Sound Effects
+ */
+ public void stutterProcess() {
+ if (useStandardStutterSound()) sendSound((byte) 8);
+ }
+
+ /**
+ * If this Machine can have the Insufficient Energy Line Problem
+ */
+ public boolean canHaveInsufficientEnergy() {
+ return true;
+ }
+
+ public boolean useStandardStutterSound() {
+ return true;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ return new String[] { "Progress:",
+ EnumChatFormatting.GREEN + GT_Utility.formatNumbers((mProgresstime / 20))
+ + EnumChatFormatting.RESET
+ + " s / "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(mMaxProgresstime / 20)
+ + EnumChatFormatting.RESET
+ + " s",
+ "Stored Energy:",
+ EnumChatFormatting.GREEN + GT_Utility.formatNumbers(getBaseMetaTileEntity().getStoredEU())
+ + EnumChatFormatting.RESET
+ + " EU / "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(getBaseMetaTileEntity().getEUCapacity())
+ + EnumChatFormatting.RESET
+ + " EU",
+ "Probably uses: " + EnumChatFormatting.RED
+ + GT_Utility.formatNumbers(mEUt)
+ + EnumChatFormatting.RESET
+ + " EU/t at "
+ + EnumChatFormatting.RED
+ + GT_Utility.formatNumbers(mEUt == 0 ? 0 : mAmperage)
+ + EnumChatFormatting.RESET
+ + " A" };
+ }
+
+ @Override
+ public boolean isGivingInformation() {
+ return true;
+ }
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ if (side == getBaseMetaTileEntity().getFrontFacing() || side == mMainFacing) {
+ if (aPlayer.isSneaking()) {
+ mDisableFilter = !mDisableFilter;
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ StatCollector.translateToLocal("GT5U.hatch.disableFilter." + mDisableFilter));
+ } else {
+ mAllowInputFromOutputSide = !mAllowInputFromOutputSide;
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ mAllowInputFromOutputSide ? GT_Utility.trans("095", "Input from Output Side allowed")
+ : GT_Utility.trans("096", "Input from Output Side forbidden"));
+ }
+ }
+ }
+
+ @Override
+ public boolean onSolderingToolRightClick(ForgeDirection side, ForgeDirection wrenchingSide,
+ EntityPlayer entityPlayer, float aX, float aY, float aZ) {
+ if (!entityPlayer.isSneaking()) return false;
+ final boolean click = super.onSolderingToolRightClick(side, wrenchingSide, entityPlayer, aX, aY, aZ);
+ if (click) return true;
+ if (wrenchingSide != mMainFacing) return false;
+ mDisableMultiStack = !mDisableMultiStack;
+ GT_Utility.sendChatToPlayer(
+ entityPlayer,
+ StatCollector.translateToLocal("GT5U.hatch.disableMultiStack." + mDisableMultiStack));
+ return true;
+ }
+
+ @Override
+ public boolean allowCoverOnSide(ForgeDirection side, GT_ItemStack aCoverID) {
+ if (side != mMainFacing) return true;
+ GT_CoverBehaviorBase<?> tBehavior = GregTech_API.getCoverBehaviorNew(aCoverID.toStack());
+ return tBehavior.isGUIClickable(
+ side,
+ GT_Utility.stackToInt(aCoverID.toStack()),
+ tBehavior.createDataObject(),
+ getBaseMetaTileEntity());
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return side != mMainFacing && aIndex >= getOutputSlot() && aIndex < getOutputSlot() + mOutputItems.length;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ if (side == mMainFacing || aIndex < getInputSlot()
+ || aIndex >= getInputSlot() + mInputSlotCount
+ || (!mAllowInputFromOutputSide && side == aBaseMetaTileEntity.getFrontFacing())) return false;
+ for (int i = getInputSlot(), j = i + mInputSlotCount; i < j; i++)
+ if (GT_Utility.areStacksEqual(GT_OreDictUnificator.get(aStack), mInventory[i]) && mDisableMultiStack)
+ return i == aIndex;
+ return mDisableFilter || allowPutStackValidated(aBaseMetaTileEntity, aIndex, side, aStack);
+ }
+
+ /**
+ * Test if given stack can be inserted into specified slot. If mDisableMultiStack is false, before execution of this
+ * method it is ensured there is no such kind of item inside any input slots already. Otherwise, you don't need to
+ * check for it anyway.
+ */
+ protected boolean allowPutStackValidated(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return !mDisableMultiStack || mInventory[aIndex] == null;
+ }
+
+ @Override
+ public boolean allowSelectCircuit() {
+ return false;
+ }
+
+ protected final ItemStack[] appendSelectedCircuit(ItemStack... inputs) {
+ if (allowSelectCircuit()) {
+ ItemStack circuit = getStackInSlot(getCircuitSlot());
+ if (circuit != null) {
+ ItemStack[] result = Arrays.copyOf(inputs, inputs.length + 1);
+ result[inputs.length] = circuit;
+ return result;
+ }
+ }
+ return inputs;
+ }
+
+ @Override
+ public int getCircuitSlot() {
+ return 4;
+ }
+
+ @Override
+ public int getCircuitGUISlot() {
+ return 3;
+ }
+
+ @Override
+ public List<ItemStack> getConfigurationCircuits() {
+ return GregTech_API.getConfigurationCircuitList(mTier);
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return null;
+ }
+
+ /**
+ * Override this to check the Recipes yourself, super calls to this could be useful if you just want to add a
+ * special case
+ * <p/>
+ * I thought about Enum too, but Enum doesn't add support for people adding other return Systems.
+ * <p/>
+ * Funny how Eclipse marks the word Enum as not correctly spelled.
+ *
+ * @return see constants above
+ */
+ public int checkRecipe() {
+ return checkRecipe(false);
+ }
+
+ public static boolean isValidForLowGravity(GT_Recipe tRecipe, int dimId) {
+ return // TODO check or get a better solution
+ DimensionManager.getProvider(dimId)
+ .getClass()
+ .getName()
+ .contains("Orbit")
+ || DimensionManager.getProvider(dimId)
+ .getClass()
+ .getName()
+ .endsWith("Space")
+ || DimensionManager.getProvider(dimId)
+ .getClass()
+ .getName()
+ .endsWith("Asteroids")
+ || DimensionManager.getProvider(dimId)
+ .getClass()
+ .getName()
+ .endsWith("SS")
+ || DimensionManager.getProvider(dimId)
+ .getClass()
+ .getName()
+ .contains("SpaceStation");
+ }
+
+ /**
+ *
+ * @param skipOC disables OverclockedNess calculation and check - if you do you must implement your own method...
+ * @return DID_NOT_FIND_RECIPE = 0, FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS = 1,
+ * FOUND_AND_SUCCESSFULLY_USED_RECIPE = 2;
+ */
+ public int checkRecipe(boolean skipOC) {
+ RecipeMap<?> tMap = getRecipeMap();
+ if (tMap == null) return DID_NOT_FIND_RECIPE;
+ GT_Recipe tRecipe = tMap.findRecipeQuery()
+ .items(getAllInputs())
+ .fluids(getFillableStack())
+ .specialSlot(getSpecialSlot())
+ .voltage(V[mTier])
+ .cachedRecipe(mLastRecipe)
+ .find();
+ if (tRecipe == null) {
+ return DID_NOT_FIND_RECIPE;
+ }
+ if (tRecipe.getMetadataOrDefault(EXPLODE, false) && getBaseMetaTileEntity() != null) {
+ getBaseMetaTileEntity().doExplosion(V[mTier] * 4);
+ return DID_NOT_FIND_RECIPE;
+ }
+ if (tRecipe.getMetadataOrDefault(ON_FIRE, false) && getBaseMetaTileEntity() != null) {
+ getBaseMetaTileEntity().setOnFire();
+ return DID_NOT_FIND_RECIPE;
+ }
+
+ if (GT_Mod.gregtechproxy.mLowGravProcessing && (tRecipe.mSpecialValue == -100 || tRecipe.mSpecialValue == -300)
+ && !isValidForLowGravity(tRecipe, getBaseMetaTileEntity().getWorld().provider.dimensionId))
+ return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS;
+ if (tRecipe.mCanBeBuffered) mLastRecipe = tRecipe;
+ if (!canOutput(tRecipe)) {
+ mOutputBlocked++;
+ return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS;
+ }
+ ICleanroom cleanroom = getCleanroom();
+ if (tRecipe.mSpecialValue == -200 || tRecipe.mSpecialValue == -300) {
+ if (cleanroom == null || !cleanroom.isValidCleanroom() || cleanroom.getCleanness() == 0) {
+ return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS;
+ }
+ }
+ if (!tRecipe.isRecipeInputEqual(true, new FluidStack[] { getFillableStack() }, getAllInputs()))
+ return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS;
+ for (int i = 0; i < mOutputItems.length; i++)
+ if (getBaseMetaTileEntity().getRandomNumber(10000) < tRecipe.getOutputChance(i))
+ mOutputItems[i] = tRecipe.getOutput(i);
+ if (tRecipe.mSpecialValue == -200 || tRecipe.mSpecialValue == -300) {
+ assert cleanroom != null;
+ for (int i = 0; i < mOutputItems.length; i++) if (mOutputItems[i] != null
+ && getBaseMetaTileEntity().getRandomNumber(10000) > cleanroom.getCleanness()) {
+ if (debugCleanroom) {
+ GT_Log.out.println(
+ "BasicMachine: Voiding output due to cleanness failure. Cleanness = "
+ + cleanroom.getCleanness());
+ }
+ mOutputItems[i] = null;
+ }
+ }
+ mOutputFluid = tRecipe.getFluidOutput(0);
+ if (!skipOC) {
+ calculateCustomOverclock(tRecipe);
+ // In case recipe is too OP for that machine
+ if (mMaxProgresstime == Integer.MAX_VALUE - 1 && mEUt == Integer.MAX_VALUE - 1)
+ return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS;
+ }
+ return FOUND_AND_SUCCESSFULLY_USED_RECIPE;
+ }
+
+ public ITexture[] getSideFacingActive(byte aColor) {
+ return new ITexture[] { MACHINE_CASINGS[mTier][aColor + 1] };
+ }
+
+ public ITexture[] getSideFacingInactive(byte aColor) {
+ return new ITexture[] { MACHINE_CASINGS[mTier][aColor + 1] };
+ }
+
+ public ITexture[] getFrontFacingActive(byte aColor) {
+ return new ITexture[] { MACHINE_CASINGS[mTier][aColor + 1] };
+ }
+
+ public ITexture[] getFrontFacingInactive(byte aColor) {
+ return new ITexture[] { MACHINE_CASINGS[mTier][aColor + 1] };
+ }
+
+ public ITexture[] getTopFacingActive(byte aColor) {
+ return new ITexture[] { MACHINE_CASINGS[mTier][aColor + 1] };
+ }
+
+ public ITexture[] getTopFacingInactive(byte aColor) {
+ return new ITexture[] { MACHINE_CASINGS[mTier][aColor + 1] };
+ }
+
+ public ITexture[] getBottomFacingActive(byte aColor) {
+ return new ITexture[] { MACHINE_CASINGS[mTier][aColor + 1] };
+ }
+
+ public ITexture[] getBottomFacingInactive(byte aColor) {
+ return new ITexture[] { MACHINE_CASINGS[mTier][aColor + 1] };
+ }
+
+ public ITexture[] getBottomFacingPipeActive(byte aColor) {
+ return new ITexture[] { MACHINE_CASINGS[mTier][aColor + 1], TextureFactory.of(OVERLAY_PIPE_OUT) };
+ }
+
+ public ITexture[] getBottomFacingPipeInactive(byte aColor) {
+ return new ITexture[] { MACHINE_CASINGS[mTier][aColor + 1], TextureFactory.of(OVERLAY_PIPE_OUT) };
+ }
+
+ public ITexture[] getTopFacingPipeActive(byte aColor) {
+ return new ITexture[] { MACHINE_CASINGS[mTier][aColor + 1], TextureFactory.of(OVERLAY_PIPE_OUT) };
+ }
+
+ public ITexture[] getTopFacingPipeInactive(byte aColor) {
+ return new ITexture[] { MACHINE_CASINGS[mTier][aColor + 1], TextureFactory.of(OVERLAY_PIPE_OUT) };
+ }
+
+ public ITexture[] getSideFacingPipeActive(byte aColor) {
+ return new ITexture[] { MACHINE_CASINGS[mTier][aColor + 1], TextureFactory.of(OVERLAY_PIPE_OUT) };
+ }
+
+ public ITexture[] getSideFacingPipeInactive(byte aColor) {
+ return new ITexture[] { MACHINE_CASINGS[mTier][aColor + 1], TextureFactory.of(OVERLAY_PIPE_OUT) };
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ final NBTTagCompound tag = accessor.getNBTData();
+
+ if (tag.getBoolean("stutteringSingleBlock")) {
+ currenttip.add("Status: insufficient energy");
+ } else {
+ boolean isActive = tag.getBoolean("isActiveSingleBlock");
+ if (isActive) {
+ int mEUt = tag.getInteger("eut");
+ if (!isSteampowered()) {
+ if (mEUt > 0) {
+ currenttip.add(
+ StatCollector.translateToLocalFormatted(
+ "GT5U.waila.energy.use_with_amperage",
+ GT_Utility.formatNumbers(mEUt),
+ GT_Utility.getAmperageForTier(mEUt, (byte) getInputTier()),
+ GT_Utility.getColoredTierNameFromTier((byte) getInputTier())));
+ } else if (mEUt < 0) {
+ currenttip.add(
+ StatCollector.translateToLocalFormatted(
+ "GT5U.waila.energy.produce_with_amperage",
+ GT_Utility.formatNumbers(-mEUt),
+ GT_Utility.getAmperageForTier(-mEUt, (byte) getOutputTier()),
+ GT_Utility.getColoredTierNameFromTier((byte) getOutputTier())));
+ }
+ } else {
+ if (mEUt > 0) {
+ currenttip.add(
+ StatCollector.translateToLocalFormatted(
+ "GT5U.waila.energy.use",
+ GT_Utility.formatNumbers(mEUt),
+ GT_Utility.getColoredTierNameFromVoltage(mEUt)));
+ } else if (mEUt < 0) {
+ currenttip.add(
+ StatCollector.translateToLocalFormatted(
+ "GT5U.waila.energy.produce",
+ GT_Utility.formatNumbers(-mEUt),
+ GT_Utility.getColoredTierNameFromVoltage(-mEUt)));
+ }
+ }
+ }
+ currenttip.add(
+ GT_Waila.getMachineProgressString(
+ isActive,
+ tag.getInteger("maxProgressSingleBlock"),
+ tag.getInteger("progressSingleBlock")));
+ }
+
+ currenttip.add(
+ String.format(
+ "Machine Facing: %s",
+ ForgeDirection.getOrientation(tag.getInteger("mainFacingSingleBlock"))
+ .name()));
+
+ currenttip.add(
+ String.format(
+ "Output Facing: %s",
+ ForgeDirection.getOrientation(tag.getInteger("outputFacingSingleBlock"))
+ .name()));
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+
+ tag.setInteger("progressSingleBlock", mProgresstime);
+ tag.setInteger("maxProgressSingleBlock", mMaxProgresstime);
+ tag.setInteger("mainFacingSingleBlock", mMainFacing.ordinal());
+ tag.setBoolean("stutteringSingleBlock", mStuttering);
+
+ final IGregTechTileEntity tileEntity = getBaseMetaTileEntity();
+ if (tileEntity != null) {
+ tag.setBoolean("isActiveSingleBlock", tileEntity.isActive());
+ tag.setInteger(
+ "outputFacingSingleBlock",
+ tileEntity.getFrontFacing()
+ .ordinal());
+ if (tileEntity.isActive()) tag.setInteger("eut", mEUt);
+ }
+ }
+
+ @Nonnull
+ @Override
+ public OverclockDescriber getOverclockDescriber() {
+ return overclockDescriber;
+ }
+
+ // GUI stuff
+
+ @Override
+ public int getCircuitSlotX() {
+ return 153;
+ }
+
+ @Override
+ public int getCircuitSlotY() {
+ return 63;
+ }
+
+ @Override
+ public void addGregTechLogo(ModularWindow.Builder builder) {
+ if (getRecipeMap() != null) {
+ getRecipeMap().getFrontend()
+ .addGregTechLogo(builder, new Pos2d(0, 0));
+ } else {
+ builder.widget(
+ new DrawableWidget().setDrawable(getGUITextureSet().getGregTechLogo())
+ .setSize(17, 17)
+ .setPos(152, 63));
+ }
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ if (!isSteampowered()) {
+ builder.widget(createFluidAutoOutputButton());
+ builder.widget(createItemAutoOutputButton());
+ }
+
+ BasicUIProperties uiProperties = getUIProperties();
+ addIOSlots(builder, uiProperties);
+
+ builder.widget(createChargerSlot(79, 62));
+
+ addProgressBar(builder, uiProperties);
+
+ builder.widget(
+ createErrorStatusArea(
+ builder,
+ isSteampowered() ? GT_UITextures.PICTURE_STALLED_STEAM : GT_UITextures.PICTURE_STALLED_ELECTRICITY));
+ }
+
+ /**
+ * Override to specify UI properties if this machine doesn't work with recipemap.
+ */
+ protected BasicUIProperties getUIProperties() {
+ if (getRecipeMap() != null) {
+ BasicUIProperties originalProperties = getRecipeMap().getFrontend()
+ .getUIProperties();
+ return originalProperties.toBuilder()
+ .maxItemInputs(mInputSlotCount)
+ .maxItemOutputs(mOutputItems.length)
+ .maxFluidInputs(Math.min(originalProperties.maxFluidInputs, 1))
+ .maxFluidOutputs(Math.min(originalProperties.maxFluidOutputs, 1))
+ .build();
+ }
+ return BasicUIProperties.builder()
+ .maxItemInputs(mInputSlotCount)
+ .maxItemOutputs(mOutputItems.length)
+ .maxFluidInputs(getCapacity() != 0 ? 1 : 0)
+ .maxFluidOutputs(0)
+ .build();
+ }
+
+ /**
+ * Adds item I/O, special item, and fluid I/O slots.
+ */
+ protected void addIOSlots(ModularWindow.Builder builder, BasicUIProperties uiProperties) {
+ UIHelper.forEachSlots(
+ (i, backgrounds, pos) -> builder.widget(createItemInputSlot(i, backgrounds, pos)),
+ (i, backgrounds, pos) -> builder.widget(createItemOutputSlot(i, backgrounds, pos)),
+ (i, backgrounds, pos) -> builder.widget(createSpecialSlot(backgrounds, pos, uiProperties)),
+ (i, backgrounds, pos) -> builder.widget(createFluidInputSlot(backgrounds, pos)),
+ (i, backgrounds, pos) -> builder.widget(createFluidOutputSlot(backgrounds, pos)),
+ getGUITextureSet().getItemSlot(),
+ getGUITextureSet().getFluidSlot(),
+ uiProperties,
+ uiProperties.maxItemInputs,
+ uiProperties.maxItemOutputs,
+ uiProperties.maxFluidInputs,
+ uiProperties.maxFluidOutputs,
+ getSteamVariant(),
+ Pos2d.ZERO);
+ }
+
+ protected void addProgressBar(ModularWindow.Builder builder, BasicUIProperties uiProperties) {
+ boolean isSteamPowered = isSteampowered();
+ RecipeMap<?> recipeMap = getRecipeMap();
+ if (!isSteamPowered && uiProperties.progressBarTexture == null) {
+ if (recipeMap != null) {
+ // Require progress bar texture for machines working with recipemap, otherwise permit
+ throw new RuntimeException("Missing progressbar texture for " + recipeMap.unlocalizedName);
+ } else {
+ return;
+ }
+ }
+ if (isSteamPowered && uiProperties.progressBarTextureSteam == null) {
+ if (recipeMap != null) {
+ throw new RuntimeException("Missing steam progressbar texture for " + recipeMap.unlocalizedName);
+ } else {
+ return;
+ }
+ }
+
+ builder.widget(
+ setNEITransferRect(
+ new ProgressBar()
+ .setProgress(() -> maxProgresstime() != 0 ? (float) getProgresstime() / maxProgresstime() : 0)
+ .setTexture(
+ isSteamPowered ? uiProperties.progressBarTextureSteam.get(getSteamVariant())
+ : uiProperties.progressBarTexture.get(),
+ uiProperties.progressBarImageSize)
+ .setDirection(uiProperties.progressBarDirection)
+ .setPos(uiProperties.progressBarPos)
+ .setSize(uiProperties.progressBarSize),
+ uiProperties.neiTransferRectId));
+ addProgressBarSpecialTextures(builder, uiProperties);
+ }
+
+ /**
+ * Override this as needed instead of calling.
+ */
+ protected SlotWidget createItemInputSlot(int index, IDrawable[] backgrounds, Pos2d pos) {
+ return (SlotWidget) new SlotWidget(inventoryHandler, getInputSlot() + index).setAccess(true, true)
+ .setBackground(backgrounds)
+ .setPos(pos);
+ }
+
+ /**
+ * Override this as needed instead of calling.
+ */
+ protected SlotWidget createItemOutputSlot(int index, IDrawable[] backgrounds, Pos2d pos) {
+ return (SlotWidget) new SlotWidget(inventoryHandler, getOutputSlot() + index).setAccess(true, false)
+ .setBackground(backgrounds)
+ .setPos(pos);
+ }
+
+ /**
+ * Override this as needed instead of calling.
+ */
+ protected SlotWidget createSpecialSlot(IDrawable[] backgrounds, Pos2d pos, BasicUIProperties uiProperties) {
+ return (SlotWidget) new SlotWidget(inventoryHandler, getSpecialSlotIndex()).setAccess(true, true)
+ .disableShiftInsert()
+ .setGTTooltip(
+ () -> mTooltipCache.getData(uiProperties.useSpecialSlot ? SPECIAL_SLOT_TOOLTIP : UNUSED_SLOT_TOOLTIP))
+ .setTooltipShowUpDelay(TOOLTIP_DELAY)
+ .setBackground(backgrounds)
+ .setPos(pos);
+ }
+
+ protected FluidSlotWidget createFluidInputSlot(IDrawable[] backgrounds, Pos2d pos) {
+ return (FluidSlotWidget) new FluidSlotWidget(fluidTank).setBackground(backgrounds)
+ .setPos(pos);
+ }
+
+ protected FluidSlotWidget createFluidOutputSlot(IDrawable[] backgrounds, Pos2d pos) {
+ return (FluidSlotWidget) new FluidSlotWidget(fluidOutputTank).setInteraction(true, false)
+ .setBackground(backgrounds)
+ .setPos(pos);
+ }
+
+ @Override
+ protected SlotWidget createChargerSlot(int x, int y) {
+ if (isSteampowered()) {
+ return (SlotWidget) createChargerSlot(x, y, UNUSED_SLOT_TOOLTIP, new String[0])
+ .setBackground(getGUITextureSet().getItemSlot());
+ } else {
+ return super.createChargerSlot(x, y);
+ }
+ }
+
+ protected CycleButtonWidget createItemAutoOutputButton() {
+ return (CycleButtonWidget) new CycleButtonWidget().setToggle(() -> mItemTransfer, val -> mItemTransfer = val)
+ .setStaticTexture(GT_UITextures.OVERLAY_BUTTON_AUTOOUTPUT_ITEM)
+ .setVariableBackground(GT_UITextures.BUTTON_STANDARD_TOGGLE)
+ .setGTTooltip(() -> mTooltipCache.getData(ITEM_TRANSFER_TOOLTIP))
+ .setTooltipShowUpDelay(TOOLTIP_DELAY)
+ .setPos(25, 62)
+ .setSize(18, 18);
+ }
+
+ protected CycleButtonWidget createFluidAutoOutputButton() {
+ return (CycleButtonWidget) new CycleButtonWidget().setToggle(() -> mFluidTransfer, val -> mFluidTransfer = val)
+ .setStaticTexture(GT_UITextures.OVERLAY_BUTTON_AUTOOUTPUT_FLUID)
+ .setVariableBackground(GT_UITextures.BUTTON_STANDARD_TOGGLE)
+ .setGTTooltip(() -> mTooltipCache.getData(FLUID_TRANSFER_TOOLTIP))
+ .setTooltipShowUpDelay(TOOLTIP_DELAY)
+ .setPos(7, 62)
+ .setSize(18, 18);
+ }
+
+ protected Widget setNEITransferRect(Widget widget, String transferRectID) {
+ if (GT_Utility.isStringInvalid(transferRectID)) {
+ return widget;
+ }
+ final String transferRectTooltip;
+ if (isSteampowered()) {
+ transferRectTooltip = StatCollector
+ .translateToLocalFormatted(NEI_TRANSFER_STEAM_TOOLTIP, overclockDescriber.getTierString());
+ } else {
+ transferRectTooltip = StatCollector
+ .translateToLocalFormatted(NEI_TRANSFER_VOLTAGE_TOOLTIP, overclockDescriber.getTierString());
+ }
+ widget.setNEITransferRect(transferRectID, new Object[] { overclockDescriber }, transferRectTooltip);
+ return widget;
+ }
+
+ protected void addProgressBarSpecialTextures(ModularWindow.Builder builder, BasicUIProperties uiProperties) {
+ if (isSteampowered()) {
+ for (Pair<SteamTexture, Pair<Size, Pos2d>> specialTexture : uiProperties.specialTexturesSteam) {
+ builder.widget(
+ new DrawableWidget().setDrawable(
+ specialTexture.getLeft()
+ .get(getSteamVariant()))
+ .setSize(
+ specialTexture.getRight()
+ .getLeft())
+ .setPos(
+ specialTexture.getRight()
+ .getRight()));
+ }
+ } else {
+ for (Pair<IDrawable, Pair<Size, Pos2d>> specialTexture : uiProperties.specialTextures) {
+ builder.widget(
+ new DrawableWidget().setDrawable(specialTexture.getLeft())
+ .setSize(
+ specialTexture.getRight()
+ .getLeft())
+ .setPos(
+ specialTexture.getRight()
+ .getRight()));
+ }
+ }
+ }
+
+ protected DrawableWidget createErrorStatusArea(ModularWindow.Builder builder, IDrawable picture) {
+ return (DrawableWidget) new DrawableWidget().setDrawable(picture)
+ .setTooltipShowUpDelay(TOOLTIP_DELAY)
+ .setEnabled(
+ widget -> !widget.getTooltip()
+ .isEmpty())
+ .dynamicTooltip(this::getErrorDescriptions)
+ .dynamicTooltipShift(this::getErrorDescriptionsShift)
+ .setPos(79, 44)
+ .setSize(18, 18)
+ .attachSyncer(
+ new FakeSyncWidget.BooleanSyncer(() -> mStuttering, val -> mStuttering = val),
+ builder,
+ (widget, val) -> widget.notifyTooltipChange())
+ .attachSyncer(
+ new FakeSyncWidget.IntegerSyncer(
+ () -> getBaseMetaTileEntity().getErrorDisplayID(),
+ val -> getBaseMetaTileEntity().setErrorDisplayID(val)),
+ builder,
+ (widget, val) -> widget.notifyTooltipChange());
+ }
+
+ protected List<String> getErrorDescriptions() {
+ final GT_TooltipDataCache.TooltipData tooltip = getErrorTooltip();
+ return tooltip != null ? tooltip.text : Collections.emptyList();
+ }
+
+ protected List<String> getErrorDescriptionsShift() {
+ final GT_TooltipDataCache.TooltipData tooltip = getErrorTooltip();
+ return tooltip != null ? tooltip.shiftText : Collections.emptyList();
+ }
+
+ protected GT_TooltipDataCache.TooltipData getErrorTooltip() {
+ if (isSteampowered()) {
+ if ((getBaseMetaTileEntity().getErrorDisplayID() & 64) != 0) {
+ return mTooltipCache.getData(STALLED_VENT_TOOLTIP);
+ }
+ }
+ if (mStuttering) {
+ return mTooltipCache.getData(
+ STALLED_STUTTERING_TOOLTIP,
+ StatCollector.translateToLocal(POWER_SOURCE_KEY + (isSteampowered() ? "steam" : "power")));
+ }
+ return null;
+ }
+
+ protected static int getCapacityForTier(int tier) {
+ return switch (tier) {
+ case 0 -> 8000;
+ case 1 -> 16000;
+ case 2 -> 32000;
+ case 3 -> 64000;
+ case 4 -> 128000;
+ default -> 256000;
+ };
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine_Bronze.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine_Bronze.java
new file mode 100644
index 0000000000..5eb648d560
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine_Bronze.java
@@ -0,0 +1,387 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.GT_Values.D1;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_BRONZEBRICKS_BOTTOM;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_BRONZEBRICKS_SIDE;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_BRONZEBRICKS_TOP;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_BRONZE_BOTTOM;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_BRONZE_SIDE;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_BRONZE_TOP;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPE_OUT;
+import static gregtech.api.objects.XSTR.XSTR_INSTANCE;
+
+import java.util.Arrays;
+
+import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.AxisAlignedBB;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizons.modularui.api.drawable.IDrawable;
+import com.gtnewhorizons.modularui.api.math.Pos2d;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+import com.gtnewhorizons.modularui.common.widget.FluidSlotWidget;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.Dyes;
+import gregtech.api.enums.ParticleFX;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.enums.SteamVariant;
+import gregtech.api.enums.TierEU;
+import gregtech.api.gui.modularui.GUITextureSet;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.objects.GT_ItemStack;
+import gregtech.api.objects.overclockdescriber.OverclockDescriber;
+import gregtech.api.objects.overclockdescriber.SteamOverclockDescriber;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Log;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.WorldSpawnedEventBuilder.ParticleEventBuilder;
+
+/**
+ * NEVER INCLUDE THIS FILE IN YOUR MOD!!!
+ * <p/>
+ * This is the main construct for my Basic Machines such as the Automatic Extractor Extend this class to make a simple
+ * Machine
+ */
+public abstract class GT_MetaTileEntity_BasicMachine_Bronze extends GT_MetaTileEntity_BasicMachine {
+
+ private static final String TT_machineType = "GT5U.MBTT.MachineType";
+ private static final int NEEDS_STEAM_VENTING = 64;
+ public boolean mNeedsSteamVenting = false;
+
+ public GT_MetaTileEntity_BasicMachine_Bronze(int aID, String aName, String aNameRegional, String aDescription,
+ int aInputSlotCount, int aOutputSlotCount, boolean aHighPressure) {
+ super(aID, aName, aNameRegional, aHighPressure ? 2 : 1, 0, aDescription, aInputSlotCount, aOutputSlotCount);
+ }
+
+ public GT_MetaTileEntity_BasicMachine_Bronze(String aName, String[] aDescription, ITexture[][][] aTextures,
+ int aInputSlotCount, int aOutputSlotCount, boolean aHighPressure) {
+ super(aName, aHighPressure ? 2 : 1, 0, aDescription, aTextures, aInputSlotCount, aOutputSlotCount);
+ }
+
+ protected boolean isBricked() {
+ return false;
+ }
+
+ @Override
+ public OverclockDescriber createOverclockDescriber() {
+ return new SteamOverclockDescriber(SteamVariant.BRONZE, 1, 2);
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setBoolean("mNeedsSteamVenting", mNeedsSteamVenting);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ mNeedsSteamVenting = aNBT.getBoolean("mNeedsSteamVenting");
+ }
+
+ @Override
+ public boolean isElectric() {
+ return false;
+ }
+
+ @Override
+ public boolean isEnetInput() {
+ return false;
+ }
+
+ @Override
+ public boolean isInputFacing(ForgeDirection side) {
+ return false;
+ }
+
+ @Override
+ public long maxEUStore() {
+ return 0;
+ }
+
+ @Override
+ public long maxEUInput() {
+ return 0;
+ }
+
+ @Override
+ public int rechargerSlotCount() {
+ return 0;
+ }
+
+ @Override
+ public int dechargerSlotCount() {
+ return 0;
+ }
+
+ @Override
+ public boolean isSteampowered() {
+ return true;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return super.isFacingValid(facing) && facing != mMainFacing;
+ }
+
+ @Override
+ public long getMinimumStoredEU() {
+ return 1000;
+ }
+
+ @Override
+ public long maxSteamStore() {
+ return 16000;
+ }
+
+ @Override
+ public boolean isLiquidInput(ForgeDirection side) {
+ return side != mMainFacing;
+ }
+
+ @Override
+ public boolean isLiquidOutput(ForgeDirection side) {
+ return side != mMainFacing;
+ }
+
+ @Override
+ public boolean doesAutoOutput() {
+ return false;
+ }
+
+ @Override
+ public boolean allowToCheckRecipe() {
+ if (mNeedsSteamVenting
+ && getBaseMetaTileEntity().getCoverIDAtSide(getBaseMetaTileEntity().getFrontFacing()) == 0
+ && !GT_Utility.hasBlockHitBox(
+ getBaseMetaTileEntity().getWorld(),
+ getBaseMetaTileEntity().getOffsetX(getBaseMetaTileEntity().getFrontFacing(), 1),
+ getBaseMetaTileEntity().getOffsetY(getBaseMetaTileEntity().getFrontFacing(), 1),
+ getBaseMetaTileEntity().getOffsetZ(getBaseMetaTileEntity().getFrontFacing(), 1))) {
+ sendSound((byte) 9);
+ mNeedsSteamVenting = false;
+ try {
+ for (EntityLivingBase tLiving : getBaseMetaTileEntity().getWorld()
+ .getEntitiesWithinAABB(
+ EntityLivingBase.class,
+ AxisAlignedBB.getBoundingBox(
+ getBaseMetaTileEntity().getOffsetX(getBaseMetaTileEntity().getFrontFacing(), 1),
+ getBaseMetaTileEntity().getOffsetY(getBaseMetaTileEntity().getFrontFacing(), 1),
+ getBaseMetaTileEntity().getOffsetZ(getBaseMetaTileEntity().getFrontFacing(), 1),
+ getBaseMetaTileEntity().getOffsetX(getBaseMetaTileEntity().getFrontFacing(), 1) + 1,
+ getBaseMetaTileEntity().getOffsetY(getBaseMetaTileEntity().getFrontFacing(), 1) + 1,
+ getBaseMetaTileEntity().getOffsetZ(getBaseMetaTileEntity().getFrontFacing(), 1) + 1))) {
+ GT_Utility.applyHeatDamage(tLiving, getSteamDamage());
+ }
+ } catch (Throwable e) {
+ if (D1) e.printStackTrace(GT_Log.err);
+ }
+ }
+ return !mNeedsSteamVenting;
+ }
+
+ @Override
+ public int checkRecipe() {
+ GT_Recipe tRecipe = getRecipeMap().findRecipe(getBaseMetaTileEntity(), false, TierEU.LV, null, getAllInputs());
+ if ((tRecipe != null) && (canOutput(tRecipe.mOutputs))
+ && (tRecipe.isRecipeInputEqual(true, null, getAllInputs()))) {
+ this.mOutputItems[0] = tRecipe.getOutput(0);
+ calculateCustomOverclock(tRecipe);
+ return FOUND_AND_SUCCESSFULLY_USED_RECIPE;
+ }
+ return DID_NOT_FIND_RECIPE;
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ // Super already zeroed out setErrorDisplayID, add additional error codes here.
+ aBaseMetaTileEntity.setErrorDisplayID(aBaseMetaTileEntity.getErrorDisplayID() | (mNeedsSteamVenting ? 64 : 0));
+ }
+
+ @Override
+ public void endProcess() {
+ if (isSteampowered()) mNeedsSteamVenting = true;
+ }
+
+ @Override
+ public void doSound(byte aIndex, double aX, double aY, double aZ) {
+ super.doSound(aIndex, aX, aY, aZ);
+ if (aIndex == 9) {
+ GT_Utility.doSoundAtClient(SoundResource.RANDOM_FIZZ, 5, 1.0F, aX, aY, aZ);
+
+ new ParticleEventBuilder().setIdentifier(ParticleFX.CLOUD)
+ .setWorld(getBaseMetaTileEntity().getWorld())
+ .setMotion(
+ getBaseMetaTileEntity().getFrontFacing().offsetX / 5.0,
+ getBaseMetaTileEntity().getFrontFacing().offsetY / 5.0,
+ getBaseMetaTileEntity().getFrontFacing().offsetZ / 5.0)
+ .<ParticleEventBuilder>times(
+ 8,
+ x -> x
+ .setPosition(
+ aX - 0.5 + XSTR_INSTANCE.nextFloat(),
+ aY - 0.5 + XSTR_INSTANCE.nextFloat(),
+ aZ - 0.5 + XSTR_INSTANCE.nextFloat())
+ .run());
+ }
+ }
+
+ @Override
+ public boolean isGivingInformation() {
+ return false;
+ }
+
+ @Override
+ public boolean allowCoverOnSide(ForgeDirection side, GT_ItemStack aCoverID) {
+ return GregTech_API.getCoverBehaviorNew(aCoverID.toStack())
+ .isSimpleCover() && super.allowCoverOnSide(side, aCoverID);
+ }
+
+ public float getSteamDamage() {
+ return 6.0F * mTier;
+ }
+
+ @Override
+ public ITexture[] getSideFacingActive(byte aColor) {
+ return new ITexture[] { TextureFactory.of(
+ isBricked() ? MACHINE_BRONZEBRICKS_SIDE : MACHINE_BRONZE_SIDE,
+ Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) };
+ }
+
+ @Override
+ public ITexture[] getSideFacingInactive(byte aColor) {
+ return new ITexture[] { TextureFactory.of(
+ isBricked() ? MACHINE_BRONZEBRICKS_SIDE : MACHINE_BRONZE_SIDE,
+ Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) };
+ }
+
+ @Override
+ public ITexture[] getFrontFacingActive(byte aColor) {
+ return new ITexture[] { TextureFactory.of(
+ isBricked() ? MACHINE_BRONZEBRICKS_SIDE : MACHINE_BRONZE_SIDE,
+ Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) };
+ }
+
+ @Override
+ public ITexture[] getFrontFacingInactive(byte aColor) {
+ return new ITexture[] { TextureFactory.of(
+ isBricked() ? MACHINE_BRONZEBRICKS_SIDE : MACHINE_BRONZE_SIDE,
+ Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) };
+ }
+
+ @Override
+ public ITexture[] getTopFacingActive(byte aColor) {
+ return new ITexture[] { TextureFactory.of(
+ isBricked() ? MACHINE_BRONZEBRICKS_TOP : MACHINE_BRONZE_TOP,
+ Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) };
+ }
+
+ @Override
+ public ITexture[] getTopFacingInactive(byte aColor) {
+ return new ITexture[] { TextureFactory.of(
+ isBricked() ? MACHINE_BRONZEBRICKS_TOP : MACHINE_BRONZE_TOP,
+ Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingActive(byte aColor) {
+ return new ITexture[] { TextureFactory.of(
+ isBricked() ? MACHINE_BRONZEBRICKS_BOTTOM : MACHINE_BRONZE_BOTTOM,
+ Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingInactive(byte aColor) {
+ return new ITexture[] { TextureFactory.of(
+ isBricked() ? MACHINE_BRONZEBRICKS_BOTTOM : MACHINE_BRONZE_BOTTOM,
+ Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingPipeActive(byte aColor) {
+ return new ITexture[] { TextureFactory.of(
+ isBricked() ? MACHINE_BRONZEBRICKS_BOTTOM : MACHINE_BRONZE_BOTTOM,
+ Dyes.getModulation(aColor, Dyes._NULL.mRGBa)), TextureFactory.of(OVERLAY_PIPE_OUT) };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingPipeInactive(byte aColor) {
+ return new ITexture[] { TextureFactory.of(
+ isBricked() ? MACHINE_BRONZEBRICKS_BOTTOM : MACHINE_BRONZE_BOTTOM,
+ Dyes.getModulation(aColor, Dyes._NULL.mRGBa)), TextureFactory.of(OVERLAY_PIPE_OUT) };
+ }
+
+ @Override
+ public ITexture[] getTopFacingPipeActive(byte aColor) {
+ return new ITexture[] { TextureFactory.of(
+ isBricked() ? MACHINE_BRONZEBRICKS_TOP : MACHINE_BRONZE_TOP,
+ Dyes.getModulation(aColor, Dyes._NULL.mRGBa)), TextureFactory.of(OVERLAY_PIPE_OUT) };
+ }
+
+ @Override
+ public ITexture[] getTopFacingPipeInactive(byte aColor) {
+ return new ITexture[] { TextureFactory.of(
+ isBricked() ? MACHINE_BRONZEBRICKS_TOP : MACHINE_BRONZE_TOP,
+ Dyes.getModulation(aColor, Dyes._NULL.mRGBa)), TextureFactory.of(OVERLAY_PIPE_OUT) };
+ }
+
+ @Override
+ public ITexture[] getSideFacingPipeActive(byte aColor) {
+ return new ITexture[] { TextureFactory.of(
+ isBricked() ? MACHINE_BRONZEBRICKS_SIDE : MACHINE_BRONZE_SIDE,
+ Dyes.getModulation(aColor, Dyes._NULL.mRGBa)), TextureFactory.of(OVERLAY_PIPE_OUT) };
+ }
+
+ @Override
+ public ITexture[] getSideFacingPipeInactive(byte aColor) {
+ return new ITexture[] { TextureFactory.of(
+ isBricked() ? MACHINE_BRONZEBRICKS_SIDE : MACHINE_BRONZE_SIDE,
+ Dyes.getModulation(aColor, Dyes._NULL.mRGBa)), TextureFactory.of(OVERLAY_PIPE_OUT) };
+ }
+
+ @Override
+ public SteamVariant getSteamVariant() {
+ return SteamVariant.BRONZE;
+ }
+
+ @Override
+ public GUITextureSet getGUITextureSet() {
+ return GUITextureSet.STEAM.apply(getSteamVariant());
+ }
+
+ @Override
+ public void addGregTechLogo(ModularWindow.Builder builder) {
+ builder.widget(
+ new DrawableWidget().setDrawable(getGUITextureSet().getGregTechLogo())
+ .setSize(17, 17)
+ .setPos(152, 63));
+ }
+
+ @Override
+ protected FluidSlotWidget createFluidInputSlot(IDrawable[] backgrounds, Pos2d pos) {
+ return null;
+ }
+
+ @Override
+ protected FluidSlotWidget createFluidOutputSlot(IDrawable[] backgrounds, Pos2d pos) {
+ return null;
+ }
+
+ @Override
+ public String[] getDescription() {
+ String[] description = Arrays.copyOf(mDescriptionArray, mDescriptionArray.length + 1);
+ description[mDescriptionArray.length] = StatCollector.translateToLocal(TT_machineType) + ": "
+ + EnumChatFormatting.YELLOW
+ + StatCollector.translateToLocal(this.getRecipeMap().unlocalizedName)
+ + EnumChatFormatting.RESET;
+ return description;
+ }
+}
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
new file mode 100644
index 0000000000..a10e735843
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine_GT_Recipe.java
@@ -0,0 +1,818 @@
+package gregtech.api.metatileentity.implementations;
+
+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.objects.XSTR.XSTR_INSTANCE;
+import static net.minecraftforge.common.util.ForgeDirection.UP;
+
+import java.util.Locale;
+
+import net.minecraft.block.Block;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.ResourceLocation;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.oredict.OreDictionary;
+
+import com.gtnewhorizons.modularui.api.drawable.FallbackableUITexture;
+import com.gtnewhorizons.modularui.api.drawable.UITexture;
+
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.OrePrefixes;
+import gregtech.api.enums.ParticleFX;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.enums.Textures.BlockIcons.CustomIcon;
+import gregtech.api.enums.Tier;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.BaseMetaTileEntity;
+import gregtech.api.recipe.BasicUIProperties;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.ExternalMaterials;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.WorldSpawnedEventBuilder.ParticleEventBuilder;
+
+/**
+ * NEVER INCLUDE THIS FILE IN YOUR MOD!!!
+ * <p/>
+ * This is the main construct for my Basic Machines such as the Automatic Extractor Extend this class to make a simple
+ * Machine
+ */
+public class GT_MetaTileEntity_BasicMachine_GT_Recipe extends GT_MetaTileEntity_BasicMachine {
+
+ private final RecipeMap<?> mRecipes;
+ private final int mTankCapacity;
+ private final SpecialEffects mSpecialEffect;
+ private final ResourceLocation mSoundResourceLocation;
+ private FallbackableUITexture progressBarTexture;
+ private int recipeCatalystPriority;
+
+ /**
+ * Registers machine with single-line description, specific tank capacity, and sound specified by ResourceLocation.
+ */
+ public GT_MetaTileEntity_BasicMachine_GT_Recipe(int aID, String aName, String aNameRegional, int aTier,
+ String aDescription, RecipeMap<?> aRecipes, int aInputSlots, int aOutputSlots, int aTankCapacity,
+ ResourceLocation aSound, SpecialEffects aSpecialEffect, String aOverlays, Object[] aRecipe) {
+ this(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ new String[] { aDescription },
+ aRecipes,
+ aInputSlots,
+ aOutputSlots,
+ aTankCapacity,
+ aSound,
+ aSpecialEffect,
+ aOverlays,
+ aRecipe);
+ }
+
+ /**
+ * Registers machine with multi-line descriptions, specific tank capacity, and sound specified by ResourceLocation.
+ */
+ public GT_MetaTileEntity_BasicMachine_GT_Recipe(int aID, String aName, String aNameRegional, int aTier,
+ String[] aDescription, RecipeMap<?> aRecipes, int aInputSlots, int aOutputSlots, int aTankCapacity,
+ ResourceLocation aSound, SpecialEffects aSpecialEffect, String aOverlays, Object[] aRecipe) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ aRecipes.getAmperage(),
+ aDescription,
+ aInputSlots,
+ aOutputSlots,
+ TextureFactory.of(
+ TextureFactory.of(
+ new CustomIcon("basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_SIDE_ACTIVE")),
+ TextureFactory.builder()
+ .addIcon(
+ (new CustomIcon(
+ "basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_SIDE_ACTIVE_GLOW")))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory
+ .of(new CustomIcon("basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_SIDE")),
+ TextureFactory.builder()
+ .addIcon(
+ (new CustomIcon(
+ "basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_SIDE_GLOW")))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(
+ new CustomIcon("basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_FRONT_ACTIVE")),
+ TextureFactory.builder()
+ .addIcon(
+ (new CustomIcon(
+ "basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_FRONT_ACTIVE_GLOW")))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory
+ .of(new CustomIcon("basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_FRONT")),
+ TextureFactory.builder()
+ .addIcon(
+ (new CustomIcon(
+ "basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_FRONT_GLOW")))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(
+ new CustomIcon("basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_TOP_ACTIVE")),
+ TextureFactory.builder()
+ .addIcon(
+ (new CustomIcon(
+ "basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_TOP_ACTIVE_GLOW")))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory
+ .of(new CustomIcon("basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_TOP")),
+ TextureFactory.builder()
+ .addIcon(
+ (new CustomIcon(
+ "basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_TOP_GLOW")))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory.of(
+ new CustomIcon(
+ "basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_BOTTOM_ACTIVE")),
+ TextureFactory.builder()
+ .addIcon(
+ (new CustomIcon(
+ "basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_BOTTOM_ACTIVE_GLOW")))
+ .glow()
+ .build()),
+ TextureFactory.of(
+ TextureFactory
+ .of(new CustomIcon("basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_BOTTOM")),
+ TextureFactory.builder()
+ .addIcon(
+ (new CustomIcon(
+ "basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_BOTTOM_GLOW")))
+ .glow()
+ .build()));
+ this.mTankCapacity = aTankCapacity;
+ this.mSpecialEffect = aSpecialEffect;
+ this.mRecipes = aRecipes;
+ this.mSoundResourceLocation = aSound;
+ this.progressBarTexture = mRecipes.getFrontend()
+ .getUIProperties().progressBarTexture;
+
+ // TODO: CHECK
+ if (aRecipe != null) {
+ for (int i = 3; i < aRecipe.length; i++) {
+ if (!(aRecipe[i] instanceof X)) continue;
+
+ // spotless:off
+ aRecipe[i] = switch ((X) aRecipe[i]) {
+ case CIRCUIT -> Tier.ELECTRIC[this.mTier].mManagingObject;
+ case BETTER_CIRCUIT -> Tier.ELECTRIC[this.mTier].mBetterManagingObject;
+ case HULL -> Tier.ELECTRIC[this.mTier].mHullObject;
+ case WIRE -> Tier.ELECTRIC[this.mTier].mConductingObject;
+ case WIRE4 -> Tier.ELECTRIC[this.mTier].mLargerConductingObject;
+ case STICK_DISTILLATION -> OrePrefixes.stick.get(Materials.Blaze);
+
+ case GLASS -> switch (this.mTier) {
+ case 0, 1, 2, 3 -> new ItemStack(Blocks.glass, 1, W);
+ case 4, 5, 6, 7, 8 -> "blockGlass" + VN[aTier];
+ default -> "blockGlass" + VN[8];
+ };
+
+ case PLATE -> switch (this.mTier) {
+ case 0, 1 -> OrePrefixes.plate.get(Materials.Steel);
+ case 2 -> OrePrefixes.plate.get(Materials.Aluminium);
+ case 3 -> OrePrefixes.plate.get(Materials.StainlessSteel);
+ case 4 -> OrePrefixes.plate.get(Materials.Titanium);
+ case 5 -> OrePrefixes.plate.get(Materials.TungstenSteel);
+ case 6 -> OrePrefixes.plate.get(Materials.HSSG);
+ case 7 -> OrePrefixes.plate.get(Materials.HSSE);
+ default -> OrePrefixes.plate.get(Materials.Neutronium);
+ };
+
+ case PIPE -> switch (this.mTier) {
+ case 0, 1 -> OrePrefixes.pipeMedium.get(Materials.Bronze);
+ case 2 -> OrePrefixes.pipeMedium.get(Materials.Steel);
+ case 3 -> OrePrefixes.pipeMedium.get(Materials.StainlessSteel);
+ case 4 -> OrePrefixes.pipeMedium.get(Materials.Titanium);
+ case 5 -> OrePrefixes.pipeMedium.get(Materials.TungstenSteel);
+ case 6 -> OrePrefixes.pipeSmall.get(Materials.Ultimate);
+ case 7 -> OrePrefixes.pipeMedium.get(Materials.Ultimate);
+ case 8 -> OrePrefixes.pipeLarge.get(Materials.Ultimate);
+ default -> OrePrefixes.pipeHuge.get(Materials.Ultimate);
+ };
+
+ case COIL_HEATING -> switch (this.mTier) {
+ case 0, 1 -> OrePrefixes.wireGt02.get(Materials.AnyCopper);
+ case 2 -> OrePrefixes.wireGt02.get(Materials.Cupronickel);
+ case 3 -> OrePrefixes.wireGt02.get(Materials.Kanthal);
+ case 4 -> OrePrefixes.wireGt02.get(Materials.Nichrome);
+ case 5 -> OrePrefixes.wireGt02.get(Materials.TPV);
+ case 6 -> OrePrefixes.wireGt02.get(Materials.HSSG);
+ case 7 -> OrePrefixes.wireGt02.get(Materials.Naquadah);
+ case 8 -> OrePrefixes.wireGt02.get(Materials.NaquadahAlloy);
+ case 9 -> OrePrefixes.wireGt04.get(Materials.NaquadahAlloy);
+ default -> OrePrefixes.wireGt08.get(Materials.NaquadahAlloy);
+ };
+
+ case COIL_HEATING_DOUBLE -> switch (this.mTier) {
+ case 0, 1 -> OrePrefixes.wireGt04.get(Materials.AnyCopper);
+ case 2 -> OrePrefixes.wireGt04.get(Materials.Cupronickel);
+ case 3 -> OrePrefixes.wireGt04.get(Materials.Kanthal);
+ case 4 -> OrePrefixes.wireGt04.get(Materials.Nichrome);
+ case 5 -> OrePrefixes.wireGt04.get(Materials.TPV);
+ case 6 -> OrePrefixes.wireGt04.get(Materials.HSSG);
+ case 7 -> OrePrefixes.wireGt04.get(Materials.Naquadah);
+ case 8 -> OrePrefixes.wireGt04.get(Materials.NaquadahAlloy);
+ case 9 -> OrePrefixes.wireGt08.get(Materials.NaquadahAlloy);
+ default -> OrePrefixes.wireGt16.get(Materials.NaquadahAlloy);
+ };
+
+ case STICK_MAGNETIC -> switch (this.mTier) {
+ case 0, 1 -> OrePrefixes.stick.get(Materials.IronMagnetic);
+ case 2, 3 -> OrePrefixes.stick.get(Materials.SteelMagnetic);
+ case 4, 5 -> OrePrefixes.stick.get(Materials.NeodymiumMagnetic);
+ case 6, 7, 8, 9 -> OrePrefixes.stick.get(Materials.SamariumMagnetic);
+ default -> OrePrefixes.stick.get(Materials.TengamAttuned);
+ };
+
+ case STICK_ELECTROMAGNETIC -> switch (this.mTier) {
+ case 0, 1 -> OrePrefixes.stick.get(Materials.AnyIron);
+ case 2, 3 -> OrePrefixes.stick.get(Materials.Steel);
+ case 4 -> OrePrefixes.stick.get(Materials.Neodymium);
+ default -> OrePrefixes.stick.get(Materials.VanadiumGallium);
+ };
+
+ case COIL_ELECTRIC -> switch (this.mTier) {
+ case 0 -> OrePrefixes.wireGt01.get(Materials.Lead);
+ case 1 -> OrePrefixes.wireGt02.get(Materials.Tin);
+ case 2 -> OrePrefixes.wireGt02.get(Materials.AnyCopper);
+ case 3 -> OrePrefixes.wireGt04.get(Materials.AnyCopper);
+ case 4 -> OrePrefixes.wireGt08.get(Materials.AnnealedCopper);
+ case 5 -> OrePrefixes.wireGt16.get(Materials.AnnealedCopper);
+ case 6 -> OrePrefixes.wireGt04.get(Materials.YttriumBariumCuprate);
+ case 7 -> OrePrefixes.wireGt08.get(Materials.Iridium);
+ default -> OrePrefixes.wireGt16.get(Materials.Osmium);
+ };
+
+ case ROBOT_ARM -> switch (this.mTier) {
+ case 0, 1 -> ItemList.Robot_Arm_LV;
+ case 2 -> ItemList.Robot_Arm_MV;
+ case 3 -> ItemList.Robot_Arm_HV;
+ case 4 -> ItemList.Robot_Arm_EV;
+ case 5 -> ItemList.Robot_Arm_IV;
+ case 6 -> ItemList.Robot_Arm_LuV;
+ case 7 -> ItemList.Robot_Arm_ZPM;
+ case 8 -> ItemList.Robot_Arm_UV;
+ case 9 -> ItemList.Robot_Arm_UHV;
+ case 10 -> ItemList.Robot_Arm_UEV;
+ case 11 -> ItemList.Robot_Arm_UIV;
+ case 12 -> ItemList.Robot_Arm_UMV;
+ case 13 -> ItemList.Robot_Arm_UXV;
+ default -> ItemList.Robot_Arm_MAX;
+ };
+
+ case PUMP -> switch (this.mTier) {
+ case 0, 1 -> ItemList.Electric_Pump_LV;
+ case 2 -> ItemList.Electric_Pump_MV;
+ case 3 -> ItemList.Electric_Pump_HV;
+ case 4 -> ItemList.Electric_Pump_EV;
+ case 5 -> ItemList.Electric_Pump_IV;
+ case 6 -> ItemList.Electric_Pump_LuV;
+ case 7 -> ItemList.Electric_Pump_ZPM;
+ case 8 -> ItemList.Electric_Pump_UV;
+ case 9 -> ItemList.Electric_Pump_UHV;
+ case 10 -> ItemList.Electric_Pump_UEV;
+ case 11 -> ItemList.Electric_Pump_UIV;
+ case 12 -> ItemList.Electric_Pump_UMV;
+ case 13 -> ItemList.Electric_Pump_UXV;
+ default -> ItemList.Electric_Pump_MAX;
+ };
+
+ case MOTOR -> switch (this.mTier) {
+ case 0, 1 -> ItemList.Electric_Motor_LV;
+ case 2 -> ItemList.Electric_Motor_MV;
+ case 3 -> ItemList.Electric_Motor_HV;
+ case 4 -> ItemList.Electric_Motor_EV;
+ case 5 -> ItemList.Electric_Motor_IV;
+ case 6 -> ItemList.Electric_Motor_LuV;
+ case 7 -> ItemList.Electric_Motor_ZPM;
+ case 8 -> ItemList.Electric_Motor_UV;
+ case 9 -> ItemList.Electric_Motor_UHV;
+ case 10 -> ItemList.Electric_Motor_UEV;
+ case 11 -> ItemList.Electric_Motor_UIV;
+ case 12 -> ItemList.Electric_Motor_UMV;
+ case 13 -> ItemList.Electric_Motor_UXV;
+ default -> ItemList.Electric_Motor_MAX;
+ };
+
+ case PISTON -> switch (this.mTier) {
+ case 0, 1 -> ItemList.Electric_Piston_LV;
+ case 2 -> ItemList.Electric_Piston_MV;
+ case 3 -> ItemList.Electric_Piston_HV;
+ case 4 -> ItemList.Electric_Piston_EV;
+ case 5 -> ItemList.Electric_Piston_IV;
+ case 6 -> ItemList.Electric_Piston_LuV;
+ case 7 -> ItemList.Electric_Piston_ZPM;
+ case 8 -> ItemList.Electric_Piston_UV;
+ case 9 -> ItemList.Electric_Piston_UHV;
+ case 10 -> ItemList.Electric_Piston_UEV;
+ case 11 -> ItemList.Electric_Piston_UIV;
+ case 12 -> ItemList.Electric_Piston_UMV;
+ case 13 -> ItemList.Electric_Piston_UXV;
+ default -> ItemList.Electric_Piston_MAX;
+ };
+
+ case CONVEYOR -> switch (this.mTier) {
+ case 0, 1 -> ItemList.Conveyor_Module_LV;
+ case 2 -> ItemList.Conveyor_Module_MV;
+ case 3 -> ItemList.Conveyor_Module_HV;
+ case 4 -> ItemList.Conveyor_Module_EV;
+ case 5 -> ItemList.Conveyor_Module_IV;
+ case 6 -> ItemList.Conveyor_Module_LuV;
+ case 7 -> ItemList.Conveyor_Module_ZPM;
+ case 8 -> ItemList.Conveyor_Module_UV;
+ case 9 -> ItemList.Conveyor_Module_UHV;
+ case 10 -> ItemList.Conveyor_Module_UEV;
+ case 11 -> ItemList.Conveyor_Module_UIV;
+ case 12 -> ItemList.Conveyor_Module_UMV;
+ case 13 -> ItemList.Conveyor_Module_UXV;
+ default -> ItemList.Conveyor_Module_MAX;
+ };
+
+ case EMITTER -> switch (this.mTier) {
+ case 0, 1 -> ItemList.Emitter_LV;
+ case 2 -> ItemList.Emitter_MV;
+ case 3 -> ItemList.Emitter_HV;
+ case 4 -> ItemList.Emitter_EV;
+ case 5 -> ItemList.Emitter_IV;
+ case 6 -> ItemList.Emitter_LuV;
+ case 7 -> ItemList.Emitter_ZPM;
+ case 8 -> ItemList.Emitter_UV;
+ case 9 -> ItemList.Emitter_UHV;
+ case 10 -> ItemList.Emitter_UEV;
+ case 11 -> ItemList.Emitter_UIV;
+ case 12 -> ItemList.Emitter_UMV;
+ case 13 -> ItemList.Emitter_UXV;
+ default -> ItemList.Emitter_MAX;
+ };
+
+ case SENSOR -> switch (this.mTier) {
+ case 0, 1 -> ItemList.Sensor_LV;
+ case 2 -> ItemList.Sensor_MV;
+ case 3 -> ItemList.Sensor_HV;
+ case 4 -> ItemList.Sensor_EV;
+ case 5 -> ItemList.Sensor_IV;
+ case 6 -> ItemList.Sensor_LuV;
+ case 7 -> ItemList.Sensor_ZPM;
+ case 8 -> ItemList.Sensor_UV;
+ case 9 -> ItemList.Sensor_UHV;
+ case 10 -> ItemList.Sensor_UEV;
+ case 11 -> ItemList.Sensor_UIV;
+ case 12 -> ItemList.Sensor_UMV;
+ case 13 -> ItemList.Sensor_UXV;
+ default -> ItemList.Sensor_MAX;
+ };
+
+ case FIELD_GENERATOR -> switch (this.mTier) {
+ case 0, 1 -> ItemList.Field_Generator_LV;
+ case 2 -> ItemList.Field_Generator_MV;
+ case 3 -> ItemList.Field_Generator_HV;
+ case 4 -> ItemList.Field_Generator_EV;
+ case 5 -> ItemList.Field_Generator_IV;
+ case 6 -> ItemList.Field_Generator_LuV;
+ case 7 -> ItemList.Field_Generator_ZPM;
+ case 8 -> ItemList.Field_Generator_UV;
+ case 9 -> ItemList.Field_Generator_UHV;
+ case 10 -> ItemList.Field_Generator_UEV;
+ case 11 -> ItemList.Field_Generator_UIV;
+ case 12 -> ItemList.Field_Generator_UMV;
+ case 13 -> ItemList.Field_Generator_UXV;
+ default -> ItemList.Field_Generator_MAX;
+ };
+
+ case ROTOR -> switch (this.mTier) {
+ case 0, 1 -> OrePrefixes.rotor.get(Materials.Tin);
+ case 2 -> OrePrefixes.rotor.get(Materials.Bronze);
+ case 3 -> OrePrefixes.rotor.get(Materials.Steel);
+ case 4 -> OrePrefixes.rotor.get(Materials.StainlessSteel);
+ case 5 -> OrePrefixes.rotor.get(Materials.TungstenSteel);
+ case 6 -> OrePrefixes.rotor.get(ExternalMaterials.getRhodiumPlatedPalladium());
+ case 7 -> OrePrefixes.rotor.get(Materials.Iridium);
+ default -> OrePrefixes.rotor.get(Materials.Osmium);
+ };
+
+ default -> throw new IllegalArgumentException("MISSING TIER MAPPING FOR: " + aRecipe[i] + " AT TIER " + this.mTier);
+ };
+ // spotless:on
+ }
+
+ if (!GT_ModHandler.addCraftingRecipe(
+ getStackForm(1),
+ GT_ModHandler.RecipeBits.DISMANTLEABLE | GT_ModHandler.RecipeBits.BUFFERED
+ | GT_ModHandler.RecipeBits.NOT_REMOVABLE
+ | GT_ModHandler.RecipeBits.REVERSIBLE,
+ aRecipe)) {
+ throw new IllegalArgumentException("INVALID CRAFTING RECIPE FOR: " + getStackForm(1).getDisplayName());
+ }
+ }
+ }
+
+ /**
+ * Registers machine with single-line description, auto-scaled fluid tank, and sound specified by SoundResource.
+ */
+ public GT_MetaTileEntity_BasicMachine_GT_Recipe(int aID, String aName, String aNameRegional, int aTier,
+ String aDescription, RecipeMap<?> aRecipes, int aInputSlots, int aOutputSlots, boolean usesFluids,
+ SoundResource aSound, SpecialEffects aSpecialEffect, String aOverlays, Object[] aRecipe) {
+ this(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ aDescription,
+ aRecipes,
+ aInputSlots,
+ aOutputSlots,
+ usesFluids ? getCapacityForTier(aTier) : 0,
+ aSound.resourceLocation,
+ aSpecialEffect,
+ aOverlays,
+ aRecipe);
+ }
+
+ /**
+ * Registers machine with multi-line descriptions, auto-scaled fluid tank, and sound specified by SoundResource.
+ */
+ public GT_MetaTileEntity_BasicMachine_GT_Recipe(int aID, String aName, String aNameRegional, int aTier,
+ String[] aDescription, RecipeMap<?> aRecipes, int aInputSlots, int aOutputSlots, boolean usesFluids,
+ SoundResource aSound, SpecialEffects aSpecialEffect, String aOverlays, Object[] aRecipe) {
+ this(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ aDescription,
+ aRecipes,
+ aInputSlots,
+ aOutputSlots,
+ usesFluids ? getCapacityForTier(aTier) : 0,
+ aSound.resourceLocation,
+ aSpecialEffect,
+ aOverlays,
+ aRecipe);
+ }
+
+ /**
+ * Registers machine with single-line description, specific tank capacity, and sound specified by SoundResource.
+ */
+ public GT_MetaTileEntity_BasicMachine_GT_Recipe(int aID, String aName, String aNameRegional, int aTier,
+ String aDescription, RecipeMap<?> aRecipes, int aInputSlots, int aOutputSlots, int aTankCapacity,
+ SoundResource aSound, SpecialEffects aSpecialEffect, String aOverlays, Object[] aRecipe) {
+ this(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ aDescription,
+ aRecipes,
+ aInputSlots,
+ aOutputSlots,
+ aTankCapacity,
+ aSound.resourceLocation,
+ aSpecialEffect,
+ aOverlays,
+ aRecipe);
+ }
+
+ /**
+ * Registers machine with multi-line descriptions, specific tank capacity, and sound specified by SoundResource.
+ */
+ public GT_MetaTileEntity_BasicMachine_GT_Recipe(int aID, String aName, String aNameRegional, int aTier,
+ String[] aDescription, RecipeMap<?> aRecipes, int aInputSlots, int aOutputSlots, int aTankCapacity,
+ SoundResource aSound, SpecialEffects aSpecialEffect, String aOverlays, Object[] aRecipe) {
+ this(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ aDescription,
+ aRecipes,
+ aInputSlots,
+ aOutputSlots,
+ aTankCapacity,
+ aSound.resourceLocation,
+ aSpecialEffect,
+ aOverlays,
+ aRecipe);
+ }
+
+ /**
+ * For {@link #newMetaEntity}.
+ */
+ public GT_MetaTileEntity_BasicMachine_GT_Recipe(String aName, int aTier, String[] aDescription,
+ RecipeMap<?> aRecipes, int aInputSlots, int aOutputSlots, int aTankCapacity, int aAmperage,
+ ITexture[][][] aTextures, ResourceLocation aSound, SpecialEffects aSpecialEffect) {
+ super(aName, aTier, aAmperage, aDescription, aTextures, aInputSlots, aOutputSlots);
+ this.mTankCapacity = aTankCapacity;
+ this.mSpecialEffect = aSpecialEffect;
+ this.mRecipes = aRecipes;
+ this.mSoundResourceLocation = aSound;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_BasicMachine_GT_Recipe(
+ this.mName,
+ this.mTier,
+ this.mDescriptionArray,
+ this.mRecipes,
+ this.mInputSlotCount,
+ this.mOutputItems == null ? 0 : this.mOutputItems.length,
+ this.mTankCapacity,
+ this.mAmperage,
+ this.mTextures,
+ this.mSoundResourceLocation,
+ this.mSpecialEffect).setProgressBarTexture(this.progressBarTexture)
+ .setRecipeCatalystPriority(this.recipeCatalystPriority);
+ }
+
+ public GT_MetaTileEntity_BasicMachine_GT_Recipe setProgressBarTexture(FallbackableUITexture progressBarTexture) {
+ this.progressBarTexture = progressBarTexture;
+ return this;
+ }
+
+ public GT_MetaTileEntity_BasicMachine_GT_Recipe setProgressBarTextureName(String name, UITexture fallback) {
+ return setProgressBarTexture(GT_UITextures.fallbackableProgressbar(name, fallback));
+ }
+
+ public GT_MetaTileEntity_BasicMachine_GT_Recipe setProgressBarTextureName(String name) {
+ return setProgressBarTextureName(name, GT_UITextures.PROGRESSBAR_ARROW);
+ }
+
+ @Override
+ protected boolean allowPutStackValidated(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ if (!super.allowPutStackValidated(aBaseMetaTileEntity, aIndex, side, aStack)) return false;
+ switch (this.mInputSlotCount) {
+ case 0 -> {
+ return false;
+ }
+ case 1 -> {
+ if (this.getFillableStack() == null) return this.getRecipeMap()
+ .containsInput(aStack);
+ else return this.getRecipeMap()
+ .findRecipe(
+ this.getBaseMetaTileEntity(),
+ this.mLastRecipe,
+ true,
+ true,
+ V[this.mTier],
+ new FluidStack[] { this.getFillableStack() },
+ this.getSpecialSlot(),
+ appendSelectedCircuit(aStack))
+ != null;
+ }
+ case 2 -> {
+ return ((this.getInputAt(0) != null && this.getInputAt(1) != null)
+ || (this.getInputAt(0) == null && this.getInputAt(1) == null ? this.getRecipeMap()
+ .containsInput(aStack)
+ : (this.getRecipeMap()
+ .containsInput(aStack)
+ && this.getRecipeMap()
+ .findRecipe(
+ this.getBaseMetaTileEntity(),
+ this.mLastRecipe,
+ true,
+ true,
+ V[this.mTier],
+ new FluidStack[] { this.getFillableStack() },
+ this.getSpecialSlot(),
+ aIndex == this.getInputSlot() ? appendSelectedCircuit(aStack, this.getInputAt(1))
+ : appendSelectedCircuit(this.getInputAt(0), aStack))
+ != null)));
+ }
+ default -> {
+ int tID = this.getBaseMetaTileEntity()
+ .getMetaTileID();
+ if (tID >= 211 && tID <= 218 || tID >= 1180 && tID <= 1187 || tID >= 10780 && tID <= 10786) { // assembler
+ // lv-iv;
+ // circuit
+ // asseblers
+ // lv -
+ // uv;
+ // assemblers
+ // luv-uev
+ if (GT_Utility.isStackValid(aStack)) for (int oreID : OreDictionary.getOreIDs(aStack)) {
+ if (OreDictionary.getOreName(oreID)
+ .startsWith("circuit")) return true;
+ }
+ }
+ return this.getRecipeMap()
+ .containsInput(aStack);
+ }
+ }
+ }
+
+ @Override
+ public boolean allowSelectCircuit() {
+ return true;
+ }
+
+ @Override
+ public void onPreTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onPreTick(aBaseMetaTileEntity, aTick);
+ if (aBaseMetaTileEntity.isClientSide() && aBaseMetaTileEntity.isActive()) {
+ // noinspection SwitchStatementWithTooFewBranches
+ switch (this.mSpecialEffect) {
+ case TOP_SMOKE -> {
+ if (aBaseMetaTileEntity.getFrontFacing() != UP && aBaseMetaTileEntity.getCoverIDAtSide(UP) == 0
+ && !aBaseMetaTileEntity.getOpacityAtSide(UP)) {
+
+ new ParticleEventBuilder().setMotion(0.0D, 0.0D, 0.0D)
+ .setIdentifier(ParticleFX.SMOKE)
+ .setPosition(
+ aBaseMetaTileEntity.getXCoord() + 0.8F - XSTR_INSTANCE.nextFloat() * 0.6F,
+ aBaseMetaTileEntity.getYCoord() + 0.9F + XSTR_INSTANCE.nextFloat() * 0.2F,
+ aBaseMetaTileEntity.getZCoord() + 0.8F - XSTR_INSTANCE.nextFloat() * 0.6F)
+ .setWorld(aBaseMetaTileEntity.getWorld())
+ .run();
+ }
+ }
+ default -> {}
+ }
+ }
+ }
+
+ /**
+ * Handles {@link Block#randomDisplayTick} driven Special Effects
+ *
+ * @param aBaseMetaTileEntity The entity that will handle the {@see Block#randomDisplayTick}
+ */
+ @SideOnly(Side.CLIENT)
+ @Override
+ public void onRandomDisplayTick(IGregTechTileEntity aBaseMetaTileEntity) {
+
+ // noinspection SwitchStatementWithTooFewBranches
+ switch (this.mSpecialEffect) {
+ case MAIN_RANDOM_SPARKS -> {
+ // Random Sparkles at main face
+ if (aBaseMetaTileEntity.isActive() && XSTR_INSTANCE.nextInt(3) == 0) {
+
+ final ForgeDirection mainFacing = this.mMainFacing;
+
+ if ((mainFacing.flag & (ForgeDirection.UP.flag | ForgeDirection.DOWN.flag)) == 0
+ && aBaseMetaTileEntity.getCoverIDAtSide(mainFacing) == 0
+ && !aBaseMetaTileEntity.getOpacityAtSide(mainFacing)) {
+
+ final double oX = aBaseMetaTileEntity.getXCoord();
+ final double oY = aBaseMetaTileEntity.getYCoord();
+ final double oZ = aBaseMetaTileEntity.getZCoord();
+ final double offset = 0.02D;
+ final double horizontal = 0.5D + XSTR_INSTANCE.nextFloat() * 8D / 16D - 4D / 16D;
+
+ final double x, y, z, mX, mZ;
+
+ y = oY + XSTR_INSTANCE.nextFloat() * 10D / 16D + 5D / 16D;
+
+ if (mainFacing == ForgeDirection.WEST) {
+ x = oX - offset;
+ mX = -.05D;
+ z = oZ + horizontal;
+ mZ = 0D;
+ } else if (mainFacing == ForgeDirection.EAST) {
+ x = oX + offset;
+ mX = .05D;
+ z = oZ + horizontal;
+ mZ = 0D;
+ } else if (mainFacing == ForgeDirection.NORTH) {
+ x = oX + horizontal;
+ mX = 0D;
+ z = oZ - offset;
+ mZ = -.05D;
+ } else // if (frontFacing == ForgeDirection.SOUTH.ordinal())
+ {
+ x = oX + horizontal;
+ mX = 0D;
+ z = oZ + offset;
+ mZ = .05D;
+ }
+
+ ParticleEventBuilder particleEventBuilder = (new ParticleEventBuilder()).setMotion(mX, 0, mZ)
+ .setPosition(x, y, z)
+ .setWorld(getBaseMetaTileEntity().getWorld());
+ particleEventBuilder.setIdentifier(ParticleFX.LAVA)
+ .run();
+ }
+ }
+ }
+ default -> {}
+ }
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return this.mRecipes;
+ }
+
+ @Override
+ public int getRecipeCatalystPriority() {
+ return recipeCatalystPriority;
+ }
+
+ public GT_MetaTileEntity_BasicMachine_GT_Recipe setRecipeCatalystPriority(int recipeCatalystPriority) {
+ this.recipeCatalystPriority = recipeCatalystPriority;
+ return this;
+ }
+
+ @Override
+ public int getCapacity() {
+ return this.mTankCapacity;
+ }
+
+ @Override
+ public void startSoundLoop(byte aIndex, double aX, double aY, double aZ) {
+ super.startSoundLoop(aIndex, aX, aY, aZ);
+ if (aIndex == 1 && this.mSoundResourceLocation != null
+ && GT_Utility.isStringValid(this.mSoundResourceLocation.getResourceDomain())
+ && GT_Utility.isStringValid(this.mSoundResourceLocation.getResourcePath()))
+ GT_Utility.doSoundAtClient(this.mSoundResourceLocation, 100, 1.0F, aX, aY, aZ);
+ }
+
+ @Override
+ public void startProcess() {
+ BaseMetaTileEntity myMetaTileEntity = ((BaseMetaTileEntity) this.getBaseMetaTileEntity());
+ // Added to throttle sounds. To reduce lag, this is on the server side so BlockUpdate packets aren't sent.
+ if (myMetaTileEntity.mTickTimer > (myMetaTileEntity.mLastSoundTick + ticksBetweenSounds)) {
+ if (this.mSoundResourceLocation != null
+ && GT_Utility.isStringValid(this.mSoundResourceLocation.getResourceDomain())
+ && GT_Utility.isStringValid(this.mSoundResourceLocation.getResourcePath()))
+ this.sendLoopStart((byte) 1);
+ // Does not have overflow protection, but they are longs.
+ myMetaTileEntity.mLastSoundTick = myMetaTileEntity.mTickTimer;
+ }
+ }
+
+ @Override
+ protected BasicUIProperties getUIProperties() {
+ return super.getUIProperties().toBuilder()
+ .progressBarTexture(progressBarTexture)
+ .build();
+ }
+
+ public enum X {
+ PUMP,
+ WIRE,
+ WIRE4,
+ HULL,
+ PIPE,
+ GLASS,
+ PLATE,
+ MOTOR,
+ ROTOR,
+ SENSOR,
+ PISTON,
+ CIRCUIT,
+ EMITTER,
+ CONVEYOR,
+ ROBOT_ARM,
+ COIL_HEATING,
+ COIL_ELECTRIC,
+ STICK_MAGNETIC,
+ STICK_DISTILLATION,
+ BETTER_CIRCUIT,
+ FIELD_GENERATOR,
+ COIL_HEATING_DOUBLE,
+ STICK_ELECTROMAGNETIC
+ }
+
+ /**
+ * Special Effects
+ */
+ public enum SpecialEffects {
+
+ NONE,
+ TOP_SMOKE,
+ MAIN_RANDOM_SPARKS;
+
+ static final SpecialEffects[] VALID_SPECIAL_EFFECTS = { NONE, TOP_SMOKE, MAIN_RANDOM_SPARKS };
+
+ static SpecialEffects fromId(int id) {
+ return id >= 0 && id < VALID_SPECIAL_EFFECTS.length ? VALID_SPECIAL_EFFECTS[id] : NONE;
+ }
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine_Steel.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine_Steel.java
new file mode 100644
index 0000000000..d6ae385430
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine_Steel.java
@@ -0,0 +1,150 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_STEELBRICKS_BOTTOM;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_STEELBRICKS_SIDE;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_STEELBRICKS_TOP;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_STEEL_BOTTOM;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_STEEL_SIDE;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_STEEL_TOP;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPE_OUT;
+
+import gregtech.api.enums.Dyes;
+import gregtech.api.enums.SteamVariant;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.modularui.IGetTitleColor;
+import gregtech.api.objects.overclockdescriber.OverclockDescriber;
+import gregtech.api.objects.overclockdescriber.SteamOverclockDescriber;
+import gregtech.api.render.TextureFactory;
+
+/**
+ * NEVER INCLUDE THIS FILE IN YOUR MOD!!!
+ * <p/>
+ * This is the main construct for my Basic Machines such as the Automatic Extractor Extend this class to make a simple
+ * Machine
+ */
+public abstract class GT_MetaTileEntity_BasicMachine_Steel extends GT_MetaTileEntity_BasicMachine_Bronze
+ implements IGetTitleColor {
+
+ public GT_MetaTileEntity_BasicMachine_Steel(int aID, String aName, String aNameRegional, String aDescription,
+ int aInputSlotCount, int aOutputSlotCount, boolean aHighPressure) {
+ super(aID, aName, aNameRegional, aDescription, aInputSlotCount, aOutputSlotCount, aHighPressure);
+ }
+
+ public GT_MetaTileEntity_BasicMachine_Steel(String aName, String[] aDescription, ITexture[][][] aTextures,
+ int aInputSlotCount, int aOutputSlotCount, boolean aHighPressure) {
+ super(aName, aDescription, aTextures, aInputSlotCount, aOutputSlotCount, aHighPressure);
+ }
+
+ @Override
+ public OverclockDescriber createOverclockDescriber() {
+ return new SteamOverclockDescriber(SteamVariant.STEEL, 2, 1);
+ }
+
+ @Override
+ public ITexture[] getSideFacingActive(byte aColor) {
+ return new ITexture[] { TextureFactory.of(
+ isBricked() ? MACHINE_STEELBRICKS_SIDE : MACHINE_STEEL_SIDE,
+ Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) };
+ }
+
+ @Override
+ public ITexture[] getSideFacingInactive(byte aColor) {
+ return new ITexture[] { TextureFactory.of(
+ isBricked() ? MACHINE_STEELBRICKS_SIDE : MACHINE_STEEL_SIDE,
+ Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) };
+ }
+
+ @Override
+ public ITexture[] getFrontFacingActive(byte aColor) {
+ return new ITexture[] { TextureFactory.of(
+ isBricked() ? MACHINE_STEELBRICKS_SIDE : MACHINE_STEEL_SIDE,
+ Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) };
+ }
+
+ @Override
+ public ITexture[] getFrontFacingInactive(byte aColor) {
+ return new ITexture[] { TextureFactory.of(
+ isBricked() ? MACHINE_STEELBRICKS_SIDE : MACHINE_STEEL_SIDE,
+ Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) };
+ }
+
+ @Override
+ public ITexture[] getTopFacingActive(byte aColor) {
+ return new ITexture[] { TextureFactory.of(
+ isBricked() ? MACHINE_STEELBRICKS_TOP : MACHINE_STEEL_TOP,
+ Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) };
+ }
+
+ @Override
+ public ITexture[] getTopFacingInactive(byte aColor) {
+ return new ITexture[] { TextureFactory.of(
+ isBricked() ? MACHINE_STEELBRICKS_TOP : MACHINE_STEEL_TOP,
+ Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingActive(byte aColor) {
+ return new ITexture[] { TextureFactory.of(
+ isBricked() ? MACHINE_STEELBRICKS_BOTTOM : MACHINE_STEEL_BOTTOM,
+ Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingInactive(byte aColor) {
+ return new ITexture[] { TextureFactory.of(
+ isBricked() ? MACHINE_STEELBRICKS_BOTTOM : MACHINE_STEEL_BOTTOM,
+ Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingPipeActive(byte aColor) {
+ return new ITexture[] { TextureFactory.of(
+ isBricked() ? MACHINE_STEELBRICKS_BOTTOM : MACHINE_STEEL_BOTTOM,
+ Dyes.getModulation(aColor, Dyes._NULL.mRGBa)), TextureFactory.of(OVERLAY_PIPE_OUT) };
+ }
+
+ @Override
+ public ITexture[] getBottomFacingPipeInactive(byte aColor) {
+ return new ITexture[] { TextureFactory.of(
+ isBricked() ? MACHINE_STEELBRICKS_BOTTOM : MACHINE_STEEL_BOTTOM,
+ Dyes.getModulation(aColor, Dyes._NULL.mRGBa)), TextureFactory.of(OVERLAY_PIPE_OUT) };
+ }
+
+ @Override
+ public ITexture[] getTopFacingPipeActive(byte aColor) {
+ return new ITexture[] { TextureFactory.of(
+ isBricked() ? MACHINE_STEELBRICKS_TOP : MACHINE_STEEL_TOP,
+ Dyes.getModulation(aColor, Dyes._NULL.mRGBa)), TextureFactory.of(OVERLAY_PIPE_OUT) };
+ }
+
+ @Override
+ public ITexture[] getTopFacingPipeInactive(byte aColor) {
+ return new ITexture[] { TextureFactory.of(
+ isBricked() ? MACHINE_STEELBRICKS_TOP : MACHINE_STEEL_TOP,
+ Dyes.getModulation(aColor, Dyes._NULL.mRGBa)), TextureFactory.of(OVERLAY_PIPE_OUT) };
+ }
+
+ @Override
+ public ITexture[] getSideFacingPipeActive(byte aColor) {
+ return new ITexture[] { TextureFactory.of(
+ isBricked() ? MACHINE_STEELBRICKS_SIDE : MACHINE_STEEL_SIDE,
+ Dyes.getModulation(aColor, Dyes._NULL.mRGBa)), TextureFactory.of(OVERLAY_PIPE_OUT) };
+ }
+
+ @Override
+ public ITexture[] getSideFacingPipeInactive(byte aColor) {
+ return new ITexture[] { TextureFactory.of(
+ isBricked() ? MACHINE_STEELBRICKS_SIDE : MACHINE_STEEL_SIDE,
+ Dyes.getModulation(aColor, Dyes._NULL.mRGBa)), TextureFactory.of(OVERLAY_PIPE_OUT) };
+ }
+
+ @Override
+ public SteamVariant getSteamVariant() {
+ return SteamVariant.STEEL;
+ }
+
+ @Override
+ public int getTitleColor() {
+ return COLOR_TITLE_WHITE.get();
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicTank.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicTank.java
new file mode 100644
index 0000000000..25fc7e7855
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicTank.java
@@ -0,0 +1,331 @@
+package gregtech.api.metatileentity.implementations;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.fluids.FluidTankInfo;
+
+import com.gtnewhorizons.modularui.api.NumberFormatMUI;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.fluid.FluidStackTank;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+import com.gtnewhorizons.modularui.common.widget.FluidSlotWidget;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.modularui.IAddUIWidgets;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.util.GT_Utility;
+
+/**
+ * NEVER INCLUDE THIS FILE IN YOUR MOD!!!
+ * <p>
+ * This is the main construct for my generic Tanks. Filling and emptying behavior have to be implemented manually
+ */
+public abstract class GT_MetaTileEntity_BasicTank extends GT_MetaTileEntity_TieredMachineBlock
+ implements IAddUIWidgets {
+
+ public FluidStack mFluid;
+ // Due to class initializing order, getCapacity might not work properly at this time.
+ // So we pass supplier instead of current value here.
+ protected final FluidStackTank fluidTank = new FluidStackTank(
+ () -> mFluid,
+ fluidStack -> mFluid = fluidStack,
+ this::getRealCapacity);
+
+ /**
+ * @param aInvSlotCount should be 3
+ */
+ public GT_MetaTileEntity_BasicTank(int aID, String aName, String aNameRegional, int aTier, int aInvSlotCount,
+ String aDescription, ITexture... aTextures) {
+ super(aID, aName, aNameRegional, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_BasicTank(int aID, String aName, String aNameRegional, int aTier, int aInvSlotCount,
+ String[] aDescription, ITexture... aTextures) {
+ super(aID, aName, aNameRegional, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_BasicTank(String aName, int aTier, int aInvSlotCount, String aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_BasicTank(String aName, int aTier, int aInvSlotCount, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return false;
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return aIndex != getStackDisplaySlot();
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ if (mFluid != null) aNBT.setTag("mFluid", mFluid.writeToNBT(new NBTTagCompound()));
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ mFluid = FluidStack.loadFluidStackFromNBT(aNBT.getCompoundTag("mFluid"));
+ }
+
+ public abstract boolean doesFillContainers();
+
+ public abstract boolean doesEmptyContainers();
+
+ public abstract boolean canTankBeFilled();
+
+ public abstract boolean canTankBeEmptied();
+
+ public abstract boolean displaysItemStack();
+
+ /**
+ * @return If fluid amount is shown on FluidDisplayItem
+ */
+ public abstract boolean displaysStackSize();
+
+ public int getInputSlot() {
+ return 0;
+ }
+
+ public int getOutputSlot() {
+ return 1;
+ }
+
+ public int getStackDisplaySlot() {
+ return 2;
+ }
+
+ public boolean isFluidInputAllowed(FluidStack aFluid) {
+ return true;
+ }
+
+ public boolean isFluidChangingAllowed() {
+ return true;
+ }
+
+ public FluidStack getFillableStack() {
+ return mFluid;
+ }
+
+ public FluidStack setFillableStack(FluidStack aFluid) {
+ mFluid = aFluid;
+ return mFluid;
+ }
+
+ /**
+ * If you override this and change the field returned, be sure to override {@link #isDrainableStackSeparate()} as
+ * well!
+ */
+ public FluidStack getDrainableStack() {
+ return mFluid;
+ }
+
+ public FluidStack setDrainableStack(FluidStack aFluid) {
+ mFluid = aFluid;
+ return mFluid;
+ }
+
+ public boolean isDrainableStackSeparate() {
+ return false;
+ }
+
+ @Override
+ public void onPreTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (aBaseMetaTileEntity.isServerSide()) {
+ if (isFluidChangingAllowed() && getFillableStack() != null && getFillableStack().amount <= 0) {
+ setFillableStack(null);
+ }
+
+ final int inputSlot = getInputSlot();
+
+ if (doesEmptyContainers()) {
+ FluidStack tFluid = GT_Utility.getFluidForFilledItem(mInventory[inputSlot], true);
+ if (tFluid != null && isFluidInputAllowed(tFluid)) {
+ if (getFillableStack() == null) {
+ if (isFluidInputAllowed(tFluid) && tFluid.amount <= getCapacity()) {
+ if (aBaseMetaTileEntity.addStackToSlot(
+ getOutputSlot(),
+ GT_Utility.getContainerForFilledItem(mInventory[inputSlot], true),
+ 1)) {
+ setFillableStack(tFluid.copy());
+ this.onEmptyingContainerWhenEmpty();
+ aBaseMetaTileEntity.decrStackSize(inputSlot, 1);
+ }
+ }
+ } else {
+ if (tFluid.isFluidEqual(getFillableStack())
+ && ((long) tFluid.amount + getFillableStack().amount) <= (long) getCapacity()) {
+ if (aBaseMetaTileEntity.addStackToSlot(
+ getOutputSlot(),
+ GT_Utility.getContainerForFilledItem(mInventory[inputSlot], true),
+ 1)) {
+ getFillableStack().amount += tFluid.amount;
+ aBaseMetaTileEntity.decrStackSize(inputSlot, 1);
+ }
+ }
+ }
+ }
+ }
+
+ if (doesFillContainers()) {
+ ItemStack tOutput = GT_Utility
+ .fillFluidContainer(getDrainableStack(), mInventory[inputSlot], false, true);
+ if (tOutput != null && aBaseMetaTileEntity.addStackToSlot(getOutputSlot(), tOutput, 1)) {
+ FluidStack tFluid = GT_Utility.getFluidForFilledItem(tOutput, true);
+ aBaseMetaTileEntity.decrStackSize(inputSlot, 1);
+ if (tFluid != null) getDrainableStack().amount -= tFluid.amount;
+ if (getDrainableStack().amount <= 0 && isFluidChangingAllowed()) setDrainableStack(null);
+ }
+ }
+ }
+ }
+
+ @Override
+ public FluidStack getFluid() {
+ return getDrainableStack();
+ }
+
+ @Override
+ public int getFluidAmount() {
+ return getDrainableStack() != null ? getDrainableStack().amount : 0;
+ }
+
+ @Override
+ public int fill(FluidStack aFluid, boolean doFill) {
+ if (aFluid == null || aFluid.getFluid()
+ .getID() <= 0 || aFluid.amount <= 0 || !canTankBeFilled() || !isFluidInputAllowed(aFluid)) return 0;
+
+ if (getFillableStack() == null || getFillableStack().getFluid()
+ .getID() <= 0) {
+ if (aFluid.amount <= getCapacity()) {
+ if (doFill) {
+ setFillableStack(aFluid.copy());
+ getBaseMetaTileEntity().markDirty();
+ }
+ return aFluid.amount;
+ }
+ if (doFill) {
+ setFillableStack(aFluid.copy());
+ getFillableStack().amount = getCapacity();
+ getBaseMetaTileEntity().markDirty();
+ }
+ return getCapacity();
+ }
+
+ if (!getFillableStack().isFluidEqual(aFluid)) return 0;
+
+ int space = getCapacity() - getFillableStack().amount;
+ if (aFluid.amount <= space) {
+ if (doFill) {
+ getFillableStack().amount += aFluid.amount;
+ getBaseMetaTileEntity().markDirty();
+ }
+ return aFluid.amount;
+ }
+ if (doFill) getFillableStack().amount = getCapacity();
+ return space;
+ }
+
+ @Override
+ public FluidStack drain(int maxDrain, boolean doDrain) {
+ if (getDrainableStack() == null || !canTankBeEmptied()) return null;
+ if (getDrainableStack().amount <= 0 && isFluidChangingAllowed()) {
+ setDrainableStack(null);
+ getBaseMetaTileEntity().markDirty();
+ return null;
+ }
+
+ int used = maxDrain;
+ if (getDrainableStack().amount < used) used = getDrainableStack().amount;
+
+ if (doDrain) {
+ getDrainableStack().amount -= used;
+ getBaseMetaTileEntity().markDirty();
+ }
+
+ FluidStack drained = getDrainableStack().copy();
+ drained.amount = used;
+
+ if (getDrainableStack().amount <= 0 && isFluidChangingAllowed()) {
+ setDrainableStack(null);
+ getBaseMetaTileEntity().markDirty();
+ }
+
+ return drained;
+ }
+
+ @Override
+ public FluidTankInfo[] getTankInfo(ForgeDirection side) {
+ if (getCapacity() <= 0 && !getBaseMetaTileEntity().hasSteamEngineUpgrade()) return new FluidTankInfo[] {};
+ if (isDrainableStackSeparate()) {
+ return new FluidTankInfo[] { new FluidTankInfo(getFillableStack(), getCapacity()),
+ new FluidTankInfo(getDrainableStack(), getCapacity()) };
+ } else {
+ return new FluidTankInfo[] { new FluidTankInfo(this) };
+ }
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return aIndex == getOutputSlot();
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return aIndex == getInputSlot();
+ }
+
+ protected void onEmptyingContainerWhenEmpty() {
+ // Do nothing
+ }
+
+ protected static final NumberFormatMUI numberFormat = new NumberFormatMUI();
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ builder.widget(
+ new DrawableWidget().setDrawable(GT_UITextures.PICTURE_SCREEN_BLACK)
+ .setPos(7, 16)
+ .setSize(71, 45))
+ .widget(
+ new DrawableWidget().setDrawable(GT_UITextures.PICTURE_GAUGE)
+ .setPos(79, 34)
+ .setSize(18, 18))
+ .widget(
+ new SlotWidget(inventoryHandler, getInputSlot())
+ .setBackground(getGUITextureSet().getItemSlot(), GT_UITextures.OVERLAY_SLOT_IN)
+ .setPos(79, 16))
+ .widget(
+ new SlotWidget(inventoryHandler, getOutputSlot()).setAccess(true, false)
+ .setBackground(getGUITextureSet().getItemSlot(), GT_UITextures.OVERLAY_SLOT_OUT)
+ .setPos(79, 52))
+ .widget(
+ createFluidSlot().setBackground(GT_UITextures.TRANSPARENT)
+ .setPos(58, 41))
+ .widget(
+ new TextWidget("Liquid Amount").setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setPos(10, 20))
+ .widget(
+ new TextWidget().setStringSupplier(() -> numberFormat.format(mFluid != null ? mFluid.amount : 0))
+ .setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setPos(10, 30));
+ }
+
+ protected FluidSlotWidget createFluidSlot() {
+ return new FluidSlotWidget(fluidTank);
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Buffer.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Buffer.java
new file mode 100644
index 0000000000..ef64d99db8
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Buffer.java
@@ -0,0 +1,563 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.GT_Values.V;
+import static gregtech.api.enums.Textures.BlockIcons.ARROW_DOWN;
+import static gregtech.api.enums.Textures.BlockIcons.ARROW_DOWN_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.ARROW_LEFT;
+import static gregtech.api.enums.Textures.BlockIcons.ARROW_LEFT_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.ARROW_RIGHT;
+import static gregtech.api.enums.Textures.BlockIcons.ARROW_RIGHT_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.ARROW_UP;
+import static gregtech.api.enums.Textures.BlockIcons.ARROW_UP_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASINGS;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPE_OUT;
+import static gregtech.api.metatileentity.BaseTileEntity.TOOLTIP_DELAY;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+import java.util.stream.IntStream;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizons.modularui.api.drawable.UITexture;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.api.widget.Widget;
+import com.gtnewhorizons.modularui.common.widget.CycleButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.SlotGroup;
+
+import gregtech.api.gui.modularui.GT_UIInfos;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.modularui.IAddUIWidgets;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_TooltipDataCache;
+import gregtech.api.util.GT_Utility;
+
+public abstract class GT_MetaTileEntity_Buffer extends GT_MetaTileEntity_TieredMachineBlock implements IAddUIWidgets {
+
+ private static final int OUTPUT_INDEX = 0;
+ private static final int ARROW_RIGHT_INDEX = 1;
+ private static final int ARROW_DOWN_INDEX = 2;
+ private static final int ARROW_LEFT_INDEX = 3;
+ private static final int ARROW_UP_INDEX = 4;
+ private static final int FRONT_INDEX = 5;
+
+ private static final String EMIT_ENERGY_TOOLTIP = "GT5U.machines.emit_energy.tooltip";
+ private static final String EMIT_REDSTONE_IF_FULL_TOOLTIP = "GT5U.machines.emit_redstone_if_full.tooltip";
+ private static final String INVERT_REDSTONE_TOOLTIP = "GT5U.machines.invert_redstone.tooltip";
+ private static final String STOCKING_MODE_TOOLTIP = "GT5U.machines.buffer_stocking_mode.tooltip";
+ private static final String SORTING_MODE_TOOLTIP = "GT5U.machines.sorting_mode.tooltip";
+ private static final int BUTTON_SIZE = 18;
+
+ public int mMaxStackSize = 64;
+ public static int MAX = 8;
+ public boolean bOutput = false, bRedstoneIfFull = false, bInvert = false, bStockingMode = false,
+ bSortStacks = false;
+ public int mSuccess = 0, mTargetStackSize = 0;
+ private int uiButtonCount = 0;
+
+ public GT_MetaTileEntity_Buffer(int aID, String aName, String aNameRegional, int aTier, int aInvSlotCount,
+ String aDescription) {
+ super(aID, aName, aNameRegional, aTier, aInvSlotCount, aDescription);
+ }
+
+ public GT_MetaTileEntity_Buffer(int aID, String aName, String aNameRegional, int aTier, int aInvSlotCount,
+ String[] aDescription) {
+ super(aID, aName, aNameRegional, aTier, aInvSlotCount, aDescription);
+ }
+
+ public GT_MetaTileEntity_Buffer(String aName, int aTier, int aInvSlotCount, String aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Buffer(String aName, int aTier, int aInvSlotCount, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+ ITexture[][][] rTextures = new ITexture[ForgeDirection.VALID_DIRECTIONS.length][17][];
+ ITexture tIcon = getOverlayIcon();
+ ITexture tOut = TextureFactory.of(OVERLAY_PIPE_OUT);
+ ITexture tUp = TextureFactory.of(
+ TextureFactory.of(ARROW_UP),
+ TextureFactory.builder()
+ .addIcon(ARROW_UP_GLOW)
+ .glow()
+ .build());
+ ITexture tDown = TextureFactory.of(
+ TextureFactory.of(ARROW_DOWN),
+ TextureFactory.builder()
+ .addIcon(ARROW_DOWN_GLOW)
+ .glow()
+ .build());
+ ITexture tLeft = TextureFactory.of(
+ TextureFactory.of(ARROW_LEFT),
+ TextureFactory.builder()
+ .addIcon(ARROW_LEFT_GLOW)
+ .glow()
+ .build());
+ ITexture tRight = TextureFactory.of(
+ TextureFactory.of(ARROW_RIGHT),
+ TextureFactory.builder()
+ .addIcon(ARROW_RIGHT_GLOW)
+ .glow()
+ .build());
+ for (int i = 0; i < rTextures[0].length; i++) {
+ rTextures[OUTPUT_INDEX][i] = new ITexture[] { MACHINE_CASINGS[mTier][i], tOut };
+ rTextures[ARROW_RIGHT_INDEX][i] = new ITexture[] { MACHINE_CASINGS[mTier][i], tRight, tIcon };
+ rTextures[ARROW_DOWN_INDEX][i] = new ITexture[] { MACHINE_CASINGS[mTier][i], tDown, tIcon };
+ rTextures[ARROW_LEFT_INDEX][i] = new ITexture[] { MACHINE_CASINGS[mTier][i], tLeft, tIcon };
+ rTextures[ARROW_UP_INDEX][i] = new ITexture[] { MACHINE_CASINGS[mTier][i], tUp, tIcon };
+ rTextures[FRONT_INDEX][i] = new ITexture[] { MACHINE_CASINGS[mTier][i], tIcon };
+ }
+ return rTextures;
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection,
+ ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) {
+ colorIndex = colorIndex + 1;
+ if (sideDirection == facingDirection) return mTextures[FRONT_INDEX][colorIndex];
+ if (sideDirection.getOpposite() == facingDirection) return mTextures[OUTPUT_INDEX][colorIndex];
+ switch (facingDirection) {
+ case DOWN -> {
+ return mTextures[ARROW_UP_INDEX][colorIndex]; // ARROW_UP
+ }
+ case UP -> {
+ return mTextures[ARROW_DOWN_INDEX][colorIndex]; // ARROW_DOWN
+ }
+ case NORTH -> {
+ switch (sideDirection) {
+ case DOWN, UP -> {
+ return mTextures[ARROW_DOWN_INDEX][colorIndex]; // ARROW_DOWN
+ }
+ case WEST -> {
+ return mTextures[ARROW_RIGHT_INDEX][colorIndex]; // ARROW_RIGHT
+ }
+ case EAST -> {
+ return mTextures[ARROW_LEFT_INDEX][colorIndex]; // ARROW_LEFT
+ }
+ default -> {}
+ }
+ }
+ case SOUTH -> {
+ switch (sideDirection) {
+ case DOWN, UP -> {
+ return mTextures[ARROW_UP_INDEX][colorIndex]; // ARROW_UP
+ }
+ case WEST -> {
+ return mTextures[ARROW_LEFT_INDEX][colorIndex]; // ARROW_LEFT
+ }
+ case EAST -> {
+ return mTextures[ARROW_RIGHT_INDEX][colorIndex]; // ARROW_RIGHT
+ }
+ default -> {}
+ }
+ }
+ case WEST -> {
+ switch (sideDirection) {
+ case UP, SOUTH -> {
+ return mTextures[ARROW_RIGHT_INDEX][colorIndex]; // ARROW_RIGHT
+ }
+ case DOWN, NORTH -> {
+ return mTextures[ARROW_LEFT_INDEX][colorIndex]; // ARROW_LEFT
+ }
+ default -> {}
+ }
+ }
+ case EAST -> {
+ switch (sideDirection) {
+ case UP, SOUTH -> {
+ return mTextures[ARROW_LEFT_INDEX][colorIndex]; // ARROW_LEFT
+ }
+ case DOWN, NORTH -> {
+ return mTextures[ARROW_RIGHT_INDEX][colorIndex]; // ARROW_RIGHT
+ }
+ default -> {}
+ }
+ }
+ default -> {}
+ }
+ return mTextures[FRONT_INDEX][colorIndex];
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return false;
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return aIndex < mInventory.length - 1;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnetInput() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnetOutput() {
+ return true;
+ }
+
+ @Override
+ public boolean isInputFacing(ForgeDirection side) {
+ return !isOutputFacing(side);
+ }
+
+ @Override
+ public boolean isOutputFacing(ForgeDirection side) {
+ return getBaseMetaTileEntity().getBackFacing() == side;
+ }
+
+ @Override
+ public boolean isTeleporterCompatible() {
+ return false;
+ }
+
+ @Override
+ public long getMinimumStoredEU() {
+ return 512L;
+ }
+
+ @Override
+ public long maxEUStore() {
+ return 512L + V[mTier] * 50L;
+ }
+
+ @Override
+ public long maxEUInput() {
+ return V[mTier];
+ }
+
+ @Override
+ public long maxEUOutput() {
+ // Return full value if we're an item and don't exist in the world for tooltip purposes
+ return getBaseMetaTileEntity().getWorld() == null || bOutput ? V[mTier] : 0L;
+ }
+
+ @Override
+ public long maxAmperesIn() {
+ return 2;
+ }
+
+ @Override
+ public long maxAmperesOut() {
+ return 2;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ public abstract ITexture getOverlayIcon();
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ return true;
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ aNBT.setBoolean("bInvert", bInvert);
+ aNBT.setBoolean("bOutput", bOutput);
+ aNBT.setBoolean("bRedstoneIfFull", bRedstoneIfFull);
+ aNBT.setBoolean("bStockingMode", bStockingMode);
+ aNBT.setInteger("mTargetStackSize", mTargetStackSize);
+ aNBT.setBoolean("bSortStacks", bSortStacks);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ bInvert = aNBT.getBoolean("bInvert");
+ bOutput = aNBT.getBoolean("bOutput");
+ bRedstoneIfFull = aNBT.getBoolean("bRedstoneIfFull");
+ bSortStacks = aNBT.getBoolean("bSortStacks");
+ bStockingMode = aNBT.getBoolean("bStockingMode");
+ mTargetStackSize = aNBT.getInteger("mTargetStackSize");
+ }
+
+ @Override
+ public void setItemNBT(NBTTagCompound aNBT) {
+ super.setItemNBT(aNBT);
+ if (mTargetStackSize > 0) aNBT.setInteger("mTargetStackSize", mTargetStackSize);
+ }
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ if (side == getBaseMetaTileEntity().getBackFacing()) {
+
+ mTargetStackSize = (byte) ((mTargetStackSize + (aPlayer.isSneaking() ? -1 : 1)) % 65);
+ if (mTargetStackSize < 0) {
+ mTargetStackSize = mMaxStackSize;
+ }
+ if (mTargetStackSize == 0) {
+ GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("098", "Do not regulate Item Stack Size"));
+ } else {
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ GT_Utility.trans("099", "Regulate Item Stack Size to: ") + mTargetStackSize);
+ }
+ }
+ }
+
+ @Override
+ public boolean onWrenchRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer entityPlayer,
+ float aX, float aY, float aZ) {
+ wrenchingSide = wrenchingSide.getOpposite();
+ if (getBaseMetaTileEntity().isValidFacing(wrenchingSide)) {
+ getBaseMetaTileEntity().setFrontFacing(wrenchingSide);
+ return true;
+ }
+ return false;
+ }
+
+ protected void handleRedstoneOutput(IGregTechTileEntity aBaseMetaTileEntity) {
+ int redstoneOutput = getRedstoneOutput();
+ Arrays.stream(ForgeDirection.VALID_DIRECTIONS)
+ .forEach(side -> aBaseMetaTileEntity.setInternalOutputRedstoneSignal(side, (byte) redstoneOutput));
+ }
+
+ protected int getRedstoneOutput() {
+ return (!bRedstoneIfFull || (bInvert ^ hasEmptySlots())) ? 0 : 15;
+ }
+
+ private boolean hasEmptySlots() {
+ return IntStream.range(0, mInventory.length)
+ .anyMatch(i -> isValidSlot(i) && mInventory[i] == null);
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTimer) {
+ if (aBaseMetaTileEntity.isAllowedToWork() && aBaseMetaTileEntity.isServerSide()
+ && (aBaseMetaTileEntity.hasWorkJustBeenEnabled() || aBaseMetaTileEntity.hasInventoryBeenModified()
+ || aTimer % 200 == 0
+ || mSuccess > 0)) {
+ mSuccess--;
+ updateSlots();
+ moveItems(aBaseMetaTileEntity, aTimer);
+ handleRedstoneOutput(aBaseMetaTileEntity);
+ }
+ }
+
+ @Override
+ public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ for (ForgeDirection side : ForgeDirection.VALID_DIRECTIONS)
+ aBaseMetaTileEntity.setInternalOutputRedstoneSignal(side, (byte) 0);
+ }
+
+ protected void moveItems(IGregTechTileEntity aBaseMetaTileEntity, long aTimer) {
+ moveItems(aBaseMetaTileEntity, aTimer, 1);
+ }
+
+ protected void moveItems(IGregTechTileEntity aBaseMetaTileEntity, long ignoredTimer, int stacks) {
+ int tCost;
+ if (bStockingMode) tCost = GT_Utility.moveMultipleItemStacks(
+ aBaseMetaTileEntity,
+ aBaseMetaTileEntity.getTileEntityAtSide(aBaseMetaTileEntity.getBackFacing()),
+ aBaseMetaTileEntity.getBackFacing(),
+ aBaseMetaTileEntity.getFrontFacing(),
+ null,
+ false,
+ mTargetStackSize == 0 ? 64 : (byte) mTargetStackSize,
+ mTargetStackSize == 0 ? 1 : (byte) mTargetStackSize,
+ (byte) 64,
+ (byte) 1,
+ stacks);
+ else tCost = GT_Utility.moveMultipleItemStacks(
+ aBaseMetaTileEntity,
+ aBaseMetaTileEntity.getTileEntityAtSide(aBaseMetaTileEntity.getBackFacing()),
+ aBaseMetaTileEntity.getBackFacing(),
+ aBaseMetaTileEntity.getFrontFacing(),
+ null,
+ false,
+ (byte) 64,
+ (byte) 1,
+ mTargetStackSize == 0 ? 64 : (byte) mTargetStackSize,
+ mTargetStackSize == 0 ? 1 : (byte) mTargetStackSize,
+ stacks);
+
+ if (tCost > 0 || aBaseMetaTileEntity.hasInventoryBeenModified()) {
+ mSuccess = 50;
+ }
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return side != aBaseMetaTileEntity.getBackFacing();
+ }
+
+ @Override
+ public boolean allowGeneralRedstoneOutput() {
+ return true;
+ }
+
+ public void updateSlots() {
+ for (int i = 0; i < mInventory.length; i++)
+ if (mInventory[i] != null && mInventory[i].stackSize <= 0) mInventory[i] = null;
+ if (bSortStacks) fillStacksIntoFirstSlots();
+ }
+
+ protected void fillStacksIntoFirstSlots() {
+ HashMap<GT_Utility.ItemId, Integer> slots = new HashMap<>(mInventory.length);
+ HashMap<GT_Utility.ItemId, ItemStack> stacks = new HashMap<>(mInventory.length);
+ List<GT_Utility.ItemId> order = new ArrayList<>(mInventory.length);
+ List<Integer> validSlots = new ArrayList<>(mInventory.length);
+ for (int i = 0; i < mInventory.length - 1; i++) {
+ if (!isValidSlot(i)) continue;
+ validSlots.add(i);
+ ItemStack s = mInventory[i];
+ if (s == null) continue;
+ GT_Utility.ItemId sID = GT_Utility.ItemId.createNoCopy(s);
+ slots.merge(sID, s.stackSize, Integer::sum);
+ if (!stacks.containsKey(sID)) stacks.put(sID, s);
+ order.add(sID);
+ mInventory[i] = null;
+ }
+ int slotindex = 0;
+ for (GT_Utility.ItemId sID : order) {
+ int toSet = slots.get(sID);
+ if (toSet == 0) continue;
+ int slot = validSlots.get(slotindex);
+ slotindex++;
+ mInventory[slot] = stacks.get(sID)
+ .copy();
+ toSet = Math.min(toSet, mInventory[slot].getMaxStackSize());
+ mInventory[slot].stackSize = toSet;
+ slots.merge(sID, toSet, (a, b) -> a - b);
+ }
+ }
+
+ @Override
+ public boolean onSolderingToolRightClick(ForgeDirection side, ForgeDirection wrenchingSide,
+ EntityPlayer entityPlayer, float aX, float aY, float aZ) {
+ if (entityPlayer.isSneaking()) {
+ // I was so proud of all this but I literally just copied code from OutputBus
+ bSortStacks = !bSortStacks;
+ GT_Utility.sendChatToPlayer(
+ entityPlayer,
+ GT_Utility.trans("200", "Sort mode: ")
+ + (bSortStacks ? GT_Utility.trans("088", "Enabled") : GT_Utility.trans("087", "Disabled")));
+ return true;
+ }
+ return super.onSolderingToolRightClick(side, wrenchingSide, entityPlayer, aX, aY, aZ);
+ }
+
+ protected void addEmitEnergyButton(ModularWindow.Builder builder) {
+ builder.widget(
+ createToggleButton(
+ () -> bOutput,
+ val -> bOutput = val,
+ GT_UITextures.OVERLAY_BUTTON_EMIT_ENERGY,
+ this::getEmitEnergyButtonTooltip));
+ }
+
+ private GT_TooltipDataCache.TooltipData getEmitEnergyButtonTooltip() {
+ return mTooltipCache.getData(
+ EMIT_ENERGY_TOOLTIP,
+ EnumChatFormatting.GREEN + GT_Utility.formatNumbers(V[mTier])
+ + " ("
+ + GT_Utility.getColoredTierNameFromTier(mTier)
+ + EnumChatFormatting.GREEN
+ + ")"
+ + EnumChatFormatting.GRAY,
+ maxAmperesOut());
+ }
+
+ protected void addEmitRedstoneIfFullButton(ModularWindow.Builder builder) {
+ builder.widget(
+ createToggleButton(
+ () -> bRedstoneIfFull,
+ val -> bRedstoneIfFull = val,
+ GT_UITextures.OVERLAY_BUTTON_EMIT_REDSTONE,
+ this::getEmitRedstoneIfFullButtonTooltip).setUpdateTooltipEveryTick(true));
+ }
+
+ private GT_TooltipDataCache.TooltipData getEmitRedstoneIfFullButtonTooltip() {
+ return mTooltipCache.getUncachedTooltipData(
+ EMIT_REDSTONE_IF_FULL_TOOLTIP,
+ StatCollector.translateToLocal(hasEmptySlots() ? "gui.yes" : "gui.no"),
+ getRedstoneOutput());
+ }
+
+ protected void addInvertRedstoneButton(ModularWindow.Builder builder) {
+ builder.widget(
+ createToggleButton(
+ () -> bInvert,
+ val -> bInvert = val,
+ GT_UITextures.OVERLAY_BUTTON_INVERT_REDSTONE,
+ () -> mTooltipCache.getData(INVERT_REDSTONE_TOOLTIP)));
+ }
+
+ protected void addStockingModeButton(ModularWindow.Builder builder) {
+ builder.widget(
+ createToggleButton(
+ () -> bStockingMode,
+ val -> bStockingMode = val,
+ GT_UITextures.OVERLAY_BUTTON_STOCKING_MODE,
+ () -> mTooltipCache.getData(STOCKING_MODE_TOOLTIP)));
+ }
+
+ protected void addSortStacksButton(ModularWindow.Builder builder) {
+ builder.widget(
+ createToggleButton(
+ () -> bSortStacks,
+ val -> bSortStacks = val,
+ GT_UITextures.OVERLAY_BUTTON_SORTING_MODE,
+ () -> mTooltipCache.getData(SORTING_MODE_TOOLTIP)));
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ buildContext.addCloseListener(() -> uiButtonCount = 0);
+ addEmitEnergyButton(builder);
+ }
+
+ protected Widget createToggleButton(Supplier<Boolean> getter, Consumer<Boolean> setter, UITexture picture,
+ Supplier<GT_TooltipDataCache.TooltipData> tooltipDataSupplier) {
+ return new CycleButtonWidget().setToggle(getter, setter)
+ .setStaticTexture(picture)
+ .setVariableBackground(GT_UITextures.BUTTON_STANDARD_TOGGLE)
+ .setTooltipShowUpDelay(TOOLTIP_DELAY)
+ .setPos(7 + (uiButtonCount++ * BUTTON_SIZE), 62)
+ .setSize(BUTTON_SIZE, BUTTON_SIZE)
+ .setGTTooltip(tooltipDataSupplier);
+ }
+
+ protected void addInventorySlots(ModularWindow.Builder builder) {
+ builder.widget(
+ SlotGroup.ofItemHandler(inventoryHandler, 9)
+ .endAtSlot(26)
+ .build()
+ .setPos(7, 4));
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_CubicMultiBlockBase.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_CubicMultiBlockBase.java
new file mode 100644
index 0000000000..efb91e3a26
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_CubicMultiBlockBase.java
@@ -0,0 +1,141 @@
+package gregtech.api.metatileentity.implementations;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.lazy;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofChain;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.onElementPass;
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose;
+import static gregtech.api.enums.GT_HatchElement.Energy;
+import static gregtech.api.enums.GT_HatchElement.InputBus;
+import static gregtech.api.enums.GT_HatchElement.InputHatch;
+import static gregtech.api.enums.GT_HatchElement.Maintenance;
+import static gregtech.api.enums.GT_HatchElement.Muffler;
+import static gregtech.api.enums.GT_HatchElement.OutputBus;
+import static gregtech.api.enums.GT_HatchElement.OutputHatch;
+
+import java.util.List;
+
+import net.minecraft.item.ItemStack;
+
+import com.google.common.collect.ImmutableList;
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.IStructureElement;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import com.gtnewhorizon.structurelib.structure.StructureDefinition;
+
+import gregtech.api.interfaces.IHatchElement;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.util.GT_StructureUtility;
+
+/**
+ * A simple 3x3x3 hollow cubic multiblock, that can be arbitrarily rotated, made of a single type of machine casing and
+ * accepts hatches everywhere. Controller will be placed in front center of the structure.
+ * <p>
+ * Note: You cannot use different casing for the same Class. Make a new subclass for it. You also should not change the
+ * casing dynamically, i.e. it should be a dumb method returning some sort of constant.
+ * <p>
+ * Implementation tips: 1. To restrict hatches, override {@link #addDynamoToMachineList(IGregTechTileEntity, int)} and
+ * its cousins instead of overriding the whole {@link #getStructureDefinition()} or change
+ * {@link #checkHatches(IGregTechTileEntity, ItemStack)}. The former is a total overkill, while the later cannot stop
+ * the structure check early. 2. To limit rotation, override {@link #getInitialAlignmentLimits()}
+ *
+ * @param <T>
+ */
+public abstract class GT_MetaTileEntity_CubicMultiBlockBase<T extends GT_MetaTileEntity_CubicMultiBlockBase<T>>
+ extends GT_MetaTileEntity_EnhancedMultiBlockBase<T> implements ISurvivalConstructable {
+
+ protected static final String STRUCTURE_PIECE_MAIN = "main";
+ protected static final ClassValue<IStructureDefinition<GT_MetaTileEntity_CubicMultiBlockBase<?>>> STRUCTURE_DEFINITION = new ClassValue<>() {
+
+ @Override
+ protected IStructureDefinition<GT_MetaTileEntity_CubicMultiBlockBase<?>> computeValue(Class<?> type) {
+ return StructureDefinition.<GT_MetaTileEntity_CubicMultiBlockBase<?>>builder()
+ .addShape(
+ STRUCTURE_PIECE_MAIN,
+ transpose(
+ new String[][] { { "hhh", "hhh", "hhh" }, { "h~h", "h-h", "hhh" }, { "hhh", "hhh", "hhh" }, }))
+ .addElement(
+ 'h',
+ ofChain(
+ lazy(
+ t -> GT_StructureUtility.<GT_MetaTileEntity_CubicMultiBlockBase<?>>buildHatchAdder()
+ .atLeastList(t.getAllowedHatches())
+ .casingIndex(t.getHatchTextureIndex())
+ .dot(1)
+ .build()),
+ onElementPass(
+ GT_MetaTileEntity_CubicMultiBlockBase::onCorrectCasingAdded,
+ lazy(GT_MetaTileEntity_CubicMultiBlockBase::getCasingElement))))
+ .build();
+ }
+ };
+ private int mCasingAmount = 0;
+
+ protected GT_MetaTileEntity_CubicMultiBlockBase(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ protected GT_MetaTileEntity_CubicMultiBlockBase(String aName) {
+ super(aName);
+ }
+
+ /**
+ * Create a simple 3x3x3 hollow cubic structure made of a single type of machine casing and accepts hatches
+ * everywhere.
+ * <p>
+ * The created definition contains a single piece named {@link #STRUCTURE_PIECE_MAIN}.
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ public IStructureDefinition<T> getStructureDefinition() {
+ return (IStructureDefinition<T>) STRUCTURE_DEFINITION.get(getClass());
+ }
+
+ @Override
+ public void construct(ItemStack aStack, boolean aHintsOnly) {
+ buildPiece(STRUCTURE_PIECE_MAIN, aStack, aHintsOnly, 1, 1, 0);
+ }
+
+ @Override
+ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ if (mMachine) return -1;
+ return survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 1, 1, 0, elementBudget, env, false, true);
+ }
+
+ @Override
+ public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ mCasingAmount = 0;
+ return checkPiece(STRUCTURE_PIECE_MAIN, 1, 1, 0) && mCasingAmount >= getRequiredCasingCount()
+ && checkHatches(aBaseMetaTileEntity, aStack);
+ }
+
+ /**
+ * Called by {@link #checkMachine(IGregTechTileEntity, ItemStack)} to check if all required hatches are present.
+ * <p>
+ * Default implementation requires EXACTLY ONE maintenance hatch to be present, and ignore all other conditions.
+ *
+ * @param aBaseMetaTileEntity the tile entity of self
+ * @param aStack The item stack inside the controller
+ * @return true if the test passes, false otherwise
+ */
+ protected boolean checkHatches(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) {
+ return mMaintenanceHatches.size() == 1;
+ }
+
+ protected abstract IStructureElement<GT_MetaTileEntity_CubicMultiBlockBase<?>> getCasingElement();
+
+ protected List<IHatchElement<? super GT_MetaTileEntity_CubicMultiBlockBase<?>>> getAllowedHatches() {
+ return ImmutableList.of(InputHatch, OutputHatch, InputBus, OutputBus, Muffler, Maintenance, Energy);
+ }
+
+ /**
+ * The hatch's texture index.
+ */
+ protected abstract int getHatchTextureIndex();
+
+ protected abstract int getRequiredCasingCount();
+
+ protected void onCorrectCasingAdded() {
+ mCasingAmount++;
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_EnhancedMultiBlockBase.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_EnhancedMultiBlockBase.java
new file mode 100644
index 0000000000..15ce78fae3
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_EnhancedMultiBlockBase.java
@@ -0,0 +1,318 @@
+package gregtech.api.metatileentity.implementations;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizon.structurelib.StructureLibAPI;
+import com.gtnewhorizon.structurelib.alignment.IAlignment;
+import com.gtnewhorizon.structurelib.alignment.IAlignmentLimits;
+import com.gtnewhorizon.structurelib.alignment.IAlignmentProvider;
+import com.gtnewhorizon.structurelib.alignment.constructable.IConstructable;
+import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
+import com.gtnewhorizon.structurelib.alignment.enumerable.Flip;
+import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation;
+import com.gtnewhorizon.structurelib.structure.IItemSource;
+import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+
+import cpw.mods.fml.common.network.NetworkRegistry;
+import gregtech.api.GregTech_API;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.shutdown.ShutDownReasonRegistry;
+
+/**
+ * Enhanced multiblock base class, featuring following improvement over {@link GT_MetaTileEntity_MultiBlockBase}
+ * <p>
+ * 1. TecTech style declarative structure check utilizing StructureLib. 2. Arbitrarily rotating the whole structure, if
+ * allowed to.
+ *
+ * @param <T> type of this
+ */
+public abstract class GT_MetaTileEntity_EnhancedMultiBlockBase<T extends GT_MetaTileEntity_EnhancedMultiBlockBase<T>>
+ extends GT_MetaTileEntity_TooltipMultiBlockBase implements IAlignment, IConstructable {
+
+ private ExtendedFacing mExtendedFacing = ExtendedFacing.DEFAULT;
+ private IAlignmentLimits mLimits = getInitialAlignmentLimits();
+
+ protected GT_MetaTileEntity_EnhancedMultiBlockBase(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ protected GT_MetaTileEntity_EnhancedMultiBlockBase(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public ExtendedFacing getExtendedFacing() {
+ return mExtendedFacing;
+ }
+
+ @Override
+ public void setExtendedFacing(ExtendedFacing newExtendedFacing) {
+ if (mExtendedFacing != newExtendedFacing) {
+ if (mMachine) stopMachine(ShutDownReasonRegistry.STRUCTURE_INCOMPLETE);
+ mExtendedFacing = newExtendedFacing;
+ final IGregTechTileEntity base = getBaseMetaTileEntity();
+ mMachine = false;
+ mUpdated = false;
+ mUpdate = 50;
+ if (getBaseMetaTileEntity().isServerSide()
+ && !GregTech_API.isDummyWorld(getBaseMetaTileEntity().getWorld())) {
+ StructureLibAPI.sendAlignment(
+ (IAlignmentProvider) base,
+ new NetworkRegistry.TargetPoint(
+ base.getWorld().provider.dimensionId,
+ base.getXCoord(),
+ base.getYCoord(),
+ base.getZCoord(),
+ 512));
+ } else {
+ base.issueTextureUpdate();
+ }
+ }
+ }
+
+ @Override
+ public final boolean isFacingValid(ForgeDirection facing) {
+ return canSetToDirectionAny(facing);
+ }
+
+ @Override
+ public boolean onWrenchRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer entityPlayer,
+ float aX, float aY, float aZ) {
+ if (wrenchingSide != getBaseMetaTileEntity().getFrontFacing())
+ return super.onWrenchRightClick(side, wrenchingSide, entityPlayer, aX, aY, aZ);
+ if (entityPlayer.isSneaking()) {
+ if (isFlipChangeAllowed()) {
+ toolSetFlip(getFlip().isHorizontallyFlipped() ? Flip.NONE : Flip.HORIZONTAL);
+ } else {
+ return false;
+ }
+ } else {
+ if (isRotationChangeAllowed()) {
+ toolSetRotation(null);
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void onFacingChange() {
+ toolSetDirection(getBaseMetaTileEntity().getFrontFacing());
+ }
+
+ @Override
+ public IAlignmentLimits getAlignmentLimits() {
+ return mLimits;
+ }
+
+ protected void setAlignmentLimits(IAlignmentLimits mLimits) {
+ this.mLimits = mLimits;
+ }
+
+ /**
+ * Due to limitation of Java type system, you might need to do an unchecked cast. HOWEVER, the returned
+ * IStructureDefinition is expected to be evaluated against current instance only, and should not be used against
+ * other instances, even for those of the same class.
+ */
+ public abstract IStructureDefinition<T> getStructureDefinition();
+
+ protected abstract GT_Multiblock_Tooltip_Builder createTooltip();
+
+ @Override
+ public String[] getStructureDescription(ItemStack stackSize) {
+ return getTooltip().getStructureHint();
+ }
+
+ protected IAlignmentLimits getInitialAlignmentLimits() {
+ return (d, r, f) -> !f.isVerticallyFliped();
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setByte(
+ "eRotation",
+ (byte) mExtendedFacing.getRotation()
+ .getIndex());
+ aNBT.setByte(
+ "eFlip",
+ (byte) mExtendedFacing.getFlip()
+ .getIndex());
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ mExtendedFacing = ExtendedFacing.of(
+ getBaseMetaTileEntity().getFrontFacing(),
+ Rotation.byIndex(aNBT.getByte("eRotation")),
+ Flip.byIndex(aNBT.getByte("eFlip")));
+ if (!getAlignmentLimits().isNewExtendedFacingValid(mExtendedFacing)) {
+ mExtendedFacing = getCorrectedAlignment(mExtendedFacing);
+ }
+ }
+
+ /**
+ * When an old {@link ExtendedFacing} is loaded upon MTE deserialization, and the loaded facing cannot pass test of
+ * current {@link #getAlignmentLimits()}, this method will be called to retrieve a corrected version. This method
+ * is currently only intended to be used as a mean to migrate alignment limits, so if you never change the alignment
+ * limit then you can probably just use the default implementation.
+ *
+ * The returned new facing must be able to pass the test of {@link #isNewExtendedFacingValid(ExtendedFacing)}
+ */
+ protected ExtendedFacing getCorrectedAlignment(ExtendedFacing aOldFacing) {
+ if (isNewExtendedFacingValid(ExtendedFacing.DEFAULT)) {
+ return ExtendedFacing.DEFAULT;
+ }
+ for (ExtendedFacing facing : ExtendedFacing.VALUES) {
+ if (facing.getFlip()
+ .isVerticallyFliped()) {
+ continue;
+ }
+ if (isNewExtendedFacingValid(facing)) {
+ return facing;
+ }
+ }
+ throw new AssertionError("No ExtendedFacing can pass the test of isNewExtendedFacingValid");
+ }
+
+ @SuppressWarnings("unchecked")
+ private IStructureDefinition<GT_MetaTileEntity_EnhancedMultiBlockBase<T>> getCastedStructureDefinition() {
+ return (IStructureDefinition<GT_MetaTileEntity_EnhancedMultiBlockBase<T>>) getStructureDefinition();
+ }
+
+ /**
+ * Explanation of the world coordinate these offset means:
+ * <p>
+ * Imagine you stand in front of the controller, with controller facing towards you not rotated or flipped.
+ * <p>
+ * The horizontalOffset would be the number of blocks on the left side of the controller, not counting controller
+ * itself. The verticalOffset would be the number of blocks on the top side of the controller, not counting
+ * controller itself. The depthOffset would be the number of blocks between you and controller, not counting
+ * controller itself.
+ * <p>
+ * All these offsets can be negative.
+ */
+ protected final boolean checkPiece(String piece, int horizontalOffset, int verticalOffset, int depthOffset) {
+ final IGregTechTileEntity tTile = getBaseMetaTileEntity();
+ return getCastedStructureDefinition().check(
+ this,
+ piece,
+ tTile.getWorld(),
+ getExtendedFacing(),
+ tTile.getXCoord(),
+ tTile.getYCoord(),
+ tTile.getZCoord(),
+ horizontalOffset,
+ verticalOffset,
+ depthOffset,
+ !mMachine);
+ }
+
+ protected final boolean buildPiece(String piece, ItemStack trigger, boolean hintOnly, int horizontalOffset,
+ int verticalOffset, int depthOffset) {
+ final IGregTechTileEntity tTile = getBaseMetaTileEntity();
+ return getCastedStructureDefinition().buildOrHints(
+ this,
+ trigger,
+ piece,
+ tTile.getWorld(),
+ getExtendedFacing(),
+ tTile.getXCoord(),
+ tTile.getYCoord(),
+ tTile.getZCoord(),
+ horizontalOffset,
+ verticalOffset,
+ depthOffset,
+ hintOnly);
+ }
+
+ @Deprecated
+ protected final int survivialBuildPiece(String piece, ItemStack trigger, int horizontalOffset, int verticalOffset,
+ int depthOffset, int elementsBudget, IItemSource source, EntityPlayerMP actor, boolean check) {
+ final IGregTechTileEntity tTile = getBaseMetaTileEntity();
+ return getCastedStructureDefinition().survivalBuild(
+ this,
+ trigger,
+ piece,
+ tTile.getWorld(),
+ getExtendedFacing(),
+ tTile.getXCoord(),
+ tTile.getYCoord(),
+ tTile.getZCoord(),
+ horizontalOffset,
+ verticalOffset,
+ depthOffset,
+ elementsBudget,
+ source,
+ actor,
+ check);
+ }
+
+ protected final int survivialBuildPiece(String piece, ItemStack trigger, int horizontalOffset, int verticalOffset,
+ int depthOffset, int elementsBudget, ISurvivalBuildEnvironment env, boolean check) {
+ final IGregTechTileEntity tTile = getBaseMetaTileEntity();
+ return getCastedStructureDefinition().survivalBuild(
+ this,
+ trigger,
+ piece,
+ tTile.getWorld(),
+ getExtendedFacing(),
+ tTile.getXCoord(),
+ tTile.getYCoord(),
+ tTile.getZCoord(),
+ horizontalOffset,
+ verticalOffset,
+ depthOffset,
+ elementsBudget,
+ env,
+ check);
+ }
+
+ @Deprecated
+ protected final int survivialBuildPiece(String piece, ItemStack trigger, int horizontalOffset, int verticalOffset,
+ int depthOffset, int elementsBudget, IItemSource source, EntityPlayerMP actor, boolean check,
+ boolean checkIfPlaced) {
+ int built = survivialBuildPiece(
+ piece,
+ trigger,
+ horizontalOffset,
+ verticalOffset,
+ depthOffset,
+ elementsBudget,
+ source,
+ actor,
+ check);
+ if (checkIfPlaced && built > 0) checkStructure(true, getBaseMetaTileEntity());
+ return built;
+ }
+
+ protected final int survivialBuildPiece(String piece, ItemStack trigger, int horizontalOffset, int verticalOffset,
+ int depthOffset, int elementsBudget, ISurvivalBuildEnvironment env, boolean check, boolean checkIfPlaced) {
+ int built = survivialBuildPiece(
+ piece,
+ trigger,
+ horizontalOffset,
+ verticalOffset,
+ depthOffset,
+ elementsBudget,
+ env,
+ check);
+ if (checkIfPlaced && built > 0) checkStructure(true, getBaseMetaTileEntity());
+ return built;
+ }
+
+ @Override
+ public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ super.onFirstTick(aBaseMetaTileEntity);
+ if (aBaseMetaTileEntity.isClientSide())
+ StructureLibAPI.queryAlignment((IAlignmentProvider) aBaseMetaTileEntity);
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_ExtendedPowerMultiBlockBase.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_ExtendedPowerMultiBlockBase.java
new file mode 100644
index 0000000000..f430114319
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_ExtendedPowerMultiBlockBase.java
@@ -0,0 +1,242 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.GT_Values.VN;
+import static gregtech.api.util.GT_Utility.filterValidMTEs;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.World;
+
+import org.jetbrains.annotations.NotNull;
+
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.logic.ProcessingLogic;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.util.GT_ExoticEnergyInputHelper;
+import gregtech.api.util.GT_OverclockCalculator;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.shutdown.ShutDownReason;
+import gregtech.api.util.shutdown.ShutDownReasonRegistry;
+
+/**
+ * Multiblock base class that allows machine to use power over int.
+ */
+public abstract class GT_MetaTileEntity_ExtendedPowerMultiBlockBase<T extends GT_MetaTileEntity_EnhancedMultiBlockBase<T>>
+ extends GT_MetaTileEntity_EnhancedMultiBlockBase<T> {
+
+ public long lEUt;
+
+ protected GT_MetaTileEntity_ExtendedPowerMultiBlockBase(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ protected GT_MetaTileEntity_ExtendedPowerMultiBlockBase(String aName) {
+ super(aName);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ // NBT can be loaded as any primitive type, so we can load it as long.
+ this.lEUt = aNBT.getLong("mEUt");
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setLong("mEUt", this.lEUt);
+ }
+
+ @Override
+ protected void calculateOverclockedNessMultiInternal(long aEUt, int aDuration, int mAmperage, long maxInputVoltage,
+ boolean perfectOC) {
+ GT_OverclockCalculator calculator = new GT_OverclockCalculator().setRecipeEUt(aEUt)
+ .setEUt(maxInputVoltage * mAmperage)
+ .setDuration(aDuration)
+ .setDurationDecreasePerOC(perfectOC ? 4.0 : 2.0)
+ .calculate();
+ lEUt = calculator.getConsumption();
+ mMaxProgresstime = calculator.getDuration();
+ }
+
+ @Override
+ public boolean onRunningTick(ItemStack aStack) {
+ if (this.lEUt > 0) {
+ addEnergyOutput((this.lEUt * mEfficiency) / 10000);
+ return true;
+ }
+ if (this.lEUt < 0) {
+ if (!drainEnergyInput(getActualEnergyUsage())) {
+ stopMachine(ShutDownReasonRegistry.POWER_LOSS);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void stopMachine(@NotNull ShutDownReason reason) {
+ this.lEUt = 0;
+ super.stopMachine(reason);
+ }
+
+ @Override
+ protected long getActualEnergyUsage() {
+ return (-this.lEUt * 10000) / Math.max(1000, mEfficiency);
+ }
+
+ public List<GT_MetaTileEntity_Hatch> getExoticAndNormalEnergyHatchList() {
+ List<GT_MetaTileEntity_Hatch> tHatches = new ArrayList<>();
+ tHatches.addAll(filterValidMTEs(mExoticEnergyHatches));
+ tHatches.addAll(filterValidMTEs(mEnergyHatches));
+ return tHatches;
+ }
+
+ @Override
+ public boolean drainEnergyInput(long aEU) {
+ return GT_ExoticEnergyInputHelper.drainEnergy(aEU, getExoticAndNormalEnergyHatchList());
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+
+ final IGregTechTileEntity tileEntity = getBaseMetaTileEntity();
+ if (tileEntity != null) {
+ if (tileEntity.isActive()) {
+ if (lEUt < 0) tag.setLong("energyUsage", getActualEnergyUsage());
+ else tag.setLong("energyUsage", -lEUt * mEfficiency / 10000);
+ }
+ }
+ }
+
+ @Override
+ protected void setProcessingLogicPower(ProcessingLogic logic) {
+ logic.setAvailableVoltage(getAverageInputVoltage());
+ logic.setAvailableAmperage(getMaxInputAmps());
+ logic.setAmperageOC(true);
+ }
+
+ @Nonnull
+ @Override
+ protected CheckRecipeResult postCheckRecipe(@Nonnull CheckRecipeResult result,
+ @Nonnull ProcessingLogic processingLogic) {
+ return result;
+ }
+
+ @Override
+ protected void setEnergyUsage(ProcessingLogic processingLogic) {
+ lEUt = processingLogic.getCalculatedEut();
+ if (lEUt > 0) {
+ lEUt = (-lEUt);
+ }
+ }
+
+ @Override
+ public String[] getInfoData() {
+ int mPollutionReduction = 0;
+ for (GT_MetaTileEntity_Hatch_Muffler tHatch : filterValidMTEs(mMufflerHatches)) {
+ mPollutionReduction = Math.max(tHatch.calculatePollutionReduction(100), mPollutionReduction);
+ }
+
+ long storedEnergy = 0;
+ long maxEnergy = 0;
+ for (GT_MetaTileEntity_Hatch tHatch : getExoticAndNormalEnergyHatchList()) {
+ storedEnergy += tHatch.getBaseMetaTileEntity()
+ .getStoredEU();
+ maxEnergy += tHatch.getBaseMetaTileEntity()
+ .getEUCapacity();
+ }
+ long voltage = getAverageInputVoltage();
+ long amps = getMaxInputAmps();
+
+ return new String[] {
+ /* 1 */ StatCollector.translateToLocal("GT5U.multiblock.Progress") + ": "
+ + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(mProgresstime / 20)
+ + EnumChatFormatting.RESET
+ + " s / "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(mMaxProgresstime / 20)
+ + EnumChatFormatting.RESET
+ + " s",
+ /* 2 */ StatCollector.translateToLocal("GT5U.multiblock.energy") + ": "
+ + EnumChatFormatting.GREEN
+ + GT_Utility.formatNumbers(storedEnergy)
+ + EnumChatFormatting.RESET
+ + " EU / "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(maxEnergy)
+ + EnumChatFormatting.RESET
+ + " EU",
+ /* 3 */ StatCollector.translateToLocal("GT5U.multiblock.usage") + ": "
+ + EnumChatFormatting.RED
+ + GT_Utility.formatNumbers(getActualEnergyUsage())
+ + EnumChatFormatting.RESET
+ + " EU/t",
+ /* 4 */ StatCollector.translateToLocal("GT5U.multiblock.mei") + ": "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(voltage)
+ + EnumChatFormatting.RESET
+ + " EU/t(*"
+ + amps
+ + " A)"
+ + StatCollector.translateToLocal("GT5U.machines.tier")
+ + ": "
+ + EnumChatFormatting.YELLOW
+ + VN[GT_Utility.getTier(voltage)]
+ + EnumChatFormatting.RESET,
+ /* 5 */ StatCollector.translateToLocal("GT5U.multiblock.problems") + ": "
+ + EnumChatFormatting.RED
+ + (getIdealStatus() - getRepairStatus())
+ + EnumChatFormatting.RESET
+ + " "
+ + StatCollector.translateToLocal("GT5U.multiblock.efficiency")
+ + ": "
+ + EnumChatFormatting.YELLOW
+ + mEfficiency / 100.0F
+ + EnumChatFormatting.RESET
+ + " %",
+ /* 6 */ StatCollector.translateToLocal("GT5U.multiblock.pollution") + ": "
+ + EnumChatFormatting.GREEN
+ + mPollutionReduction
+ + EnumChatFormatting.RESET
+ + " %" };
+ }
+
+ @Override
+ public long getMaxInputVoltage() {
+ return GT_ExoticEnergyInputHelper.getMaxInputVoltageMulti(getExoticAndNormalEnergyHatchList());
+ }
+
+ @Override
+ public long getAverageInputVoltage() {
+ return GT_ExoticEnergyInputHelper.getAverageInputVoltageMulti(getExoticAndNormalEnergyHatchList());
+ }
+
+ @Override
+ public long getMaxInputAmps() {
+ return GT_ExoticEnergyInputHelper.getMaxWorkingInputAmpsMulti(getExoticAndNormalEnergyHatchList());
+ }
+
+ @Override
+ public long getMaxInputEu() {
+ return GT_ExoticEnergyInputHelper.getTotalEuMulti(getExoticAndNormalEnergyHatchList());
+ }
+
+ @Override
+ public void clearHatches() {
+ super.clearHatches();
+ mExoticEnergyHatches.clear();
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_FilterBase.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_FilterBase.java
new file mode 100644
index 0000000000..896cb223f3
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_FilterBase.java
@@ -0,0 +1,106 @@
+package gregtech.api.metatileentity.implementations;
+
+import net.minecraft.nbt.NBTTagCompound;
+
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.util.GT_TooltipDataCache;
+
+public abstract class GT_MetaTileEntity_FilterBase extends GT_MetaTileEntity_Buffer {
+
+ private static final String INVERT_FILTER_TOOLTIP = "GT5U.machines.invert_filter.tooltip";
+ protected static final int FILTER_SLOT_INDEX = 9;
+ protected static final int NUM_INVENTORY_SLOTS = 9;
+ private static final String EMIT_REDSTONE_GRADUALLY_TOOLTIP = "GT5U" + ".machines.emit_redstone_gradually.tooltip";
+ protected boolean invertFilter = false;
+
+ public GT_MetaTileEntity_FilterBase(int aID, String aName, String aNameRegional, int aTier, int aInvSlotCount,
+ String aDescription) {
+ super(aID, aName, aNameRegional, aTier, aInvSlotCount, aDescription);
+ }
+
+ public GT_MetaTileEntity_FilterBase(int aID, String aName, String aNameRegional, int aTier, int aInvSlotCount,
+ String[] aDescription) {
+ super(aID, aName, aNameRegional, aTier, aInvSlotCount, aDescription);
+ }
+
+ public GT_MetaTileEntity_FilterBase(String aName, int aTier, int aInvSlotCount, String aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_FilterBase(String aName, int aTier, int aInvSlotCount, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return aIndex < NUM_INVENTORY_SLOTS;
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setBoolean("bInvertFilter", this.invertFilter);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ this.invertFilter = aNBT.getBoolean("bInvertFilter");
+ }
+
+ @Override
+ protected int getRedstoneOutput() {
+ if (!bRedstoneIfFull) {
+ return 0;
+ }
+ int redstoneOutput = getEmptySlots();
+ if (!bInvert) redstoneOutput = NUM_INVENTORY_SLOTS - redstoneOutput;
+ return redstoneOutput;
+ }
+
+ private int getEmptySlots() {
+ int emptySlots = 0;
+ for (int i = 0; i < NUM_INVENTORY_SLOTS; i++) {
+ if (mInventory[i] == null) ++emptySlots;
+ }
+ return emptySlots;
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ super.addUIWidgets(builder, buildContext);
+ addSortStacksButton(builder);
+ addEmitRedstoneGraduallyButton(builder);
+ addInvertRedstoneButton(builder);
+ addInvertFilterButton(builder);
+ }
+
+ private void addEmitRedstoneGraduallyButton(ModularWindow.Builder builder) {
+ builder.widget(
+ createToggleButton(
+ () -> bRedstoneIfFull,
+ val -> bRedstoneIfFull = val,
+ GT_UITextures.OVERLAY_BUTTON_EMIT_REDSTONE,
+ this::getEmitRedstoneGraduallyButtonTooltip).setUpdateTooltipEveryTick(true));
+ }
+
+ private GT_TooltipDataCache.TooltipData getEmitRedstoneGraduallyButtonTooltip() {
+ return mTooltipCache
+ .getUncachedTooltipData(EMIT_REDSTONE_GRADUALLY_TOOLTIP, getEmptySlots(), getRedstoneOutput());
+ }
+
+ private void addInvertFilterButton(ModularWindow.Builder builder) {
+ builder.widget(
+ createToggleButton(
+ () -> invertFilter,
+ val -> invertFilter = val,
+ GT_UITextures.OVERLAY_BUTTON_INVERT_FILTER,
+ () -> mTooltipCache.getData(INVERT_FILTER_TOOLTIP)));
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch.java
new file mode 100644
index 0000000000..1e974f5e9d
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch.java
@@ -0,0 +1,252 @@
+package gregtech.api.metatileentity.implementations;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import appeng.api.crafting.ICraftingIconProvider;
+import gregtech.api.enums.GT_Values;
+import gregtech.api.enums.Textures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+
+/**
+ * Handles texture changes internally. No special calls are necessary other than updateTexture in add***ToMachineList.
+ */
+public abstract class GT_MetaTileEntity_Hatch extends GT_MetaTileEntity_BasicTank implements ICraftingIconProvider {
+
+ public enum ConnectionType {
+ CABLE,
+ WIRELESS,
+ LASER
+ }
+
+ /**
+ * Uses new texture changing methods to avoid limitations of byte as texture index...
+ */
+ @Deprecated
+ public byte mMachineBlock = 0;
+
+ private byte mTexturePage = 0;
+ private byte actualTexture = 0;
+
+ private ItemStack ae2CraftingIcon;
+
+ public GT_MetaTileEntity_Hatch(int aID, String aName, String aNameRegional, int aTier, int aInvSlotCount,
+ String aDescription, ITexture... aTextures) {
+ super(aID, aName, aNameRegional, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Hatch(int aID, String aName, String aNameRegional, int aTier, int aInvSlotCount,
+ String[] aDescription, ITexture... aTextures) {
+ super(aID, aName, aNameRegional, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Hatch(String aName, int aTier, int aInvSlotCount, String aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Hatch(String aName, int aTier, int aInvSlotCount, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ public static int getSlots(int aTier) {
+ return aTier < 1 ? 1 : aTier == 1 ? 4 : aTier == 2 ? 9 : 16;
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+ return new ITexture[0][0][0];
+ }
+
+ public abstract ITexture[] getTexturesActive(ITexture aBaseTexture);
+
+ public abstract ITexture[] getTexturesInactive(ITexture aBaseTexture);
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing,
+ int colorIndex, boolean aActive, boolean redstoneLevel) {
+ int texturePointer = (byte) (actualTexture & 0x7F); // just to be sure, from my testing the 8th bit cannot be
+ // set clientside
+ int textureIndex = texturePointer | (mTexturePage << 7); // Shift seven since one page is 128 textures!
+ try {
+ if (side != aFacing) {
+ if (textureIndex > 0)
+ return new ITexture[] { Textures.BlockIcons.casingTexturePages[mTexturePage][texturePointer] };
+ else return new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][colorIndex + 1] };
+ } else {
+ if (textureIndex > 0) {
+ if (aActive)
+ return getTexturesActive(Textures.BlockIcons.casingTexturePages[mTexturePage][texturePointer]);
+ else return getTexturesInactive(
+ Textures.BlockIcons.casingTexturePages[mTexturePage][texturePointer]);
+ } else {
+ if (aActive) return getTexturesActive(Textures.BlockIcons.MACHINE_CASINGS[mTier][colorIndex + 1]);
+ else return getTexturesInactive(Textures.BlockIcons.MACHINE_CASINGS[mTier][colorIndex + 1]);
+ }
+ }
+ } catch (NullPointerException npe) {
+ return new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[0][0] };
+ }
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setByte("mMachineBlock", actualTexture);
+ aNBT.setByte("mTexturePage", mTexturePage);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ actualTexture = aNBT.getByte("mMachineBlock");
+ mTexturePage = aNBT.getByte("mTexturePage");
+
+ if (mTexturePage != 0 && GT_Values.GT.isServerSide()) actualTexture |= 0x80; // <- lets just hope no one needs
+ // the correct value for that on
+ // server
+ mMachineBlock = actualTexture;
+ }
+
+ /**
+ * Sets texture with page and index, called on add to machine list
+ *
+ * @param id (page<<7)+index of the texture
+ */
+ public final void updateTexture(int id) {
+ onValueUpdate((byte) id);
+ onTexturePageUpdate((byte) (id >> 7));
+ }
+
+ /**
+ * Sets the icon for the owning multiblock used for AE2 crafting display of attached interfaces, called on add to
+ * machine list
+ */
+ public final void updateCraftingIcon(ItemStack icon) {
+ this.ae2CraftingIcon = icon;
+ }
+
+ @Override
+ public ItemStack getMachineCraftingIcon() {
+ return this.ae2CraftingIcon;
+ }
+
+ /**
+ * Some multiblocks restrict hatches by tier. This method allows hatches to specify custom tier used for
+ * structure check, while keeping {@link #mTier} for other uses.
+ *
+ * @return Tier used for multiblock structure
+ */
+ public byte getTierForStructure() {
+ return mTier;
+ }
+
+ /**
+ * Sets texture with page and index, rather unusable, but kept FFS
+ *
+ * @param page page of texure
+ * @param index index of texure
+ */
+ @Deprecated
+ public final void updateTexture(byte page, byte index) {
+ onValueUpdate(index);
+ onTexturePageUpdate(page);
+ }
+
+ @Override
+ public final void onValueUpdate(byte aValue) {
+ actualTexture = (byte) (aValue & 0x7F);
+ mMachineBlock = actualTexture;
+ mTexturePage = 0;
+ }
+
+ /**
+ * Get the maximum amount of amperes to work with, which excludes the additional amps in for loss
+ *
+ * @return Working amps
+ */
+ public long maxWorkingAmperesIn() {
+ return maxAmperesIn();
+ }
+
+ /**
+ * Get the type of connection this hatch allows
+ *
+ * @return Connection type
+ */
+ public ConnectionType getConnectionType() {
+ return ConnectionType.CABLE;
+ }
+
+ @Override
+ public final byte getUpdateData() {
+ return (byte) (actualTexture & 0x7F);
+ }
+
+ public final void onTexturePageUpdate(byte aValue) {
+ mTexturePage = (byte) (aValue & 0x7F);
+ if (mTexturePage != 0 && getBaseMetaTileEntity().isServerSide()) { // just to be sure
+ mMachineBlock |= 0x80; // <- lets just hope no one needs the correct value for that on server
+ actualTexture = mMachineBlock;
+ }
+ // set last bit to allow working of the page reset-er to 0 in rare case when texture id is the same but page
+ // changes to 0
+ }
+
+ public final byte getTexturePage() {
+ return (byte) (mTexturePage & 0x7F);
+ }
+
+ @Override
+ public boolean doesFillContainers() {
+ return false;
+ }
+
+ @Override
+ public boolean doesEmptyContainers() {
+ return false;
+ }
+
+ @Override
+ public boolean canTankBeFilled() {
+ return false;
+ }
+
+ @Override
+ public boolean canTankBeEmptied() {
+ return false;
+ }
+
+ @Override
+ public boolean displaysItemStack() {
+ return false;
+ }
+
+ @Override
+ public boolean displaysStackSize() {
+ return false;
+ }
+
+ @Override
+ public void onPreTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { // in that method since it is usually
+ // not overriden, especially for
+ // hatches.
+ if (actualTexture != mMachineBlock) { // revert to page 0 on edition of the field - old code way
+ actualTexture = (byte) (mMachineBlock & 0x7F);
+ mMachineBlock = actualTexture; // clear last bit in mMachineBlock since now we are at page 0 after the
+ // direct field
+ // change
+ mTexturePage = 0; // assuming old code only supports page 0
+ }
+ super.onPreTick(aBaseMetaTileEntity, aTick);
+ }
+
+ // To change to other page -> use the setter method -> updateTexture
+
+ public int getCircuitSlot() {
+ return -1;
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_DataAccess.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_DataAccess.java
new file mode 100644
index 0000000000..18344082ea
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_DataAccess.java
@@ -0,0 +1,170 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_DATA_ACCESS;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+
+import gregtech.api.gui.modularui.GT_UIInfos;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.modularui.IAddUIWidgets;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_AssemblyLineUtils;
+
+public class GT_MetaTileEntity_Hatch_DataAccess extends GT_MetaTileEntity_Hatch implements IAddUIWidgets {
+
+ private int timeout = 4;
+
+ public GT_MetaTileEntity_Hatch_DataAccess(int aID, String aName, String aNameRegional, int aTier) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ 16,
+ new String[] { "Data Access for Multiblocks",
+ "Adds " + (aTier == 4 ? 4 : 16) + " extra slots for Data Sticks" });
+ }
+
+ public GT_MetaTileEntity_Hatch_DataAccess(String aName, int aTier, String aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aTier == 4 ? 4 : 16, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Hatch_DataAccess(String aName, int aTier, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aTier == 4 ? 4 : 16, aDescription, aTextures);
+ }
+
+ @Override
+ public ITexture[] getTexturesActive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_DATA_ACCESS) };
+ }
+
+ @Override
+ public ITexture[] getTexturesInactive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_DATA_ACCESS) };
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return true;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return true;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return true;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Hatch_DataAccess(mName, mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ return true;
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return mTier >= 8 && !aBaseMetaTileEntity.isActive();
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return mTier >= 8 && !aBaseMetaTileEntity.isActive();
+ }
+
+ @Override
+ public int getInventoryStackLimit() {
+ return 1;
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (aBaseMetaTileEntity.isServerSide() && aBaseMetaTileEntity.isActive()) {
+ timeout--;
+ if (timeout <= 0) {
+ aBaseMetaTileEntity.setActive(false);
+ }
+ }
+ }
+
+ public void setActive(boolean mActive) {
+ getBaseMetaTileEntity().setActive(mActive);
+ timeout = mActive ? 4 : 0;
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ if (aNBT.getByte("mSticksUpdated") != 1) {
+ for (int i = 0; i < getSizeInventory(); i++) {
+ GT_AssemblyLineUtils.processDataStick(getStackInSlot(i));
+ }
+ }
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ // reminder: remove this marker after many years
+ aNBT.setByte("mSticksUpdated", (byte) 1);
+ }
+
+ @Override
+ public void setInventorySlotContents(int aIndex, ItemStack aStack) {
+ super.setInventorySlotContents(aIndex, aStack);
+ GT_AssemblyLineUtils.processDataStick(aStack);
+ }
+
+ public List<ItemStack> getInventoryItems(Predicate<ItemStack> filter) {
+ ArrayList<ItemStack> items = new ArrayList<>();
+ IGregTechTileEntity te = getBaseMetaTileEntity();
+ for (int i = 0; i < te.getSizeInventory(); ++i) {
+ ItemStack slot = te.getStackInSlot(i);
+ if (slot != null) {
+ if (filter != null && filter.test(slot)) {
+ items.add(slot);
+ }
+ }
+ }
+ return items;
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ if (mTier == 4) {
+ getBaseMetaTileEntity()
+ .add2by2Slots(builder, getGUITextureSet().getItemSlot(), GT_UITextures.OVERLAY_SLOT_CIRCUIT);
+ } else {
+ getBaseMetaTileEntity()
+ .add4by4Slots(builder, getGUITextureSet().getItemSlot(), GT_UITextures.OVERLAY_SLOT_CIRCUIT);
+ }
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Dynamo.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Dynamo.java
new file mode 100644
index 0000000000..8e621434db
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Dynamo.java
@@ -0,0 +1,110 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.GT_Values.V;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.api.enums.Textures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+
+public class GT_MetaTileEntity_Hatch_Dynamo extends GT_MetaTileEntity_Hatch {
+
+ public GT_MetaTileEntity_Hatch_Dynamo(int aID, String aName, String aNameRegional, int aTier) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ 0,
+ new String[] { "Generating electric Energy from Multiblocks", "Puts out up to 1 Amp" });
+ }
+
+ public GT_MetaTileEntity_Hatch_Dynamo(int aID, String aName, String aNameRegional, int aTier,
+ String[] aDescription) {
+ super(aID, aName, aNameRegional, aTier, 0, aDescription);
+ }
+
+ public GT_MetaTileEntity_Hatch_Dynamo(String aName, int aTier, String aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 0, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Hatch_Dynamo(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 0, aDescription, aTextures);
+ }
+
+ @Override
+ public ITexture[] getTexturesActive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, Textures.BlockIcons.OVERLAYS_ENERGY_OUT_MULTI[mTier] };
+ }
+
+ @Override
+ public ITexture[] getTexturesInactive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, Textures.BlockIcons.OVERLAYS_ENERGY_OUT_MULTI[mTier] };
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return true;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return true;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnetOutput() {
+ return true;
+ }
+
+ @Override
+ public boolean isOutputFacing(ForgeDirection side) {
+ return side == getBaseMetaTileEntity().getFrontFacing();
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return false;
+ }
+
+ @Override
+ public long getMinimumStoredEU() {
+ return 512;
+ }
+
+ @Override
+ public long maxEUOutput() {
+ return V[mTier];
+ }
+
+ @Override
+ public long maxEUStore() {
+ return 512L + V[mTier + 1] * 2L;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Hatch_Dynamo(mName, mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Energy.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Energy.java
new file mode 100644
index 0000000000..d9be12671d
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Energy.java
@@ -0,0 +1,125 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.GT_Values.V;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.api.enums.Textures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+
+public class GT_MetaTileEntity_Hatch_Energy extends GT_MetaTileEntity_Hatch {
+
+ public GT_MetaTileEntity_Hatch_Energy(int aID, String aName, String aNameRegional, int aTier,
+ String[] aDescription) {
+ super(aID, aName, aNameRegional, aTier, 0, aDescription);
+ }
+
+ public GT_MetaTileEntity_Hatch_Energy(int aID, String aName, String aNameRegional, int aTier) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ 0,
+ new String[] { "Energy Injector for Multiblocks", "Accepts up to 2 Amps" });
+ }
+
+ public GT_MetaTileEntity_Hatch_Energy(int aID, String aName, String aNameRegional, int aTier, int aInvSlotCount,
+ String[] aDescription, ITexture... aTextures) {
+ super(aID, aName, aNameRegional, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Hatch_Energy(String aName, int aTier, String aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 0, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Hatch_Energy(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 0, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Hatch_Energy(String aName, int aTier, int aInvSlotCount, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ @Override
+ public ITexture[] getTexturesActive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, Textures.BlockIcons.OVERLAYS_ENERGY_IN_MULTI[mTier] };
+ }
+
+ @Override
+ public ITexture[] getTexturesInactive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, Textures.BlockIcons.OVERLAYS_ENERGY_IN_MULTI[mTier] };
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return true;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return true;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnetInput() {
+ return true;
+ }
+
+ @Override
+ public boolean isInputFacing(ForgeDirection side) {
+ return side == getBaseMetaTileEntity().getFrontFacing();
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return false;
+ }
+
+ @Override
+ public long getMinimumStoredEU() {
+ return 512;
+ }
+
+ @Override
+ public long maxEUInput() {
+ return V[mTier];
+ }
+
+ @Override
+ public long maxEUStore() {
+ return 512L + V[mTier] * 8L;
+ }
+
+ @Override
+ public long maxAmperesIn() {
+ return 2;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Hatch_Energy(mName, mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Input.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Input.java
new file mode 100644
index 0000000000..705a3e6248
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Input.java
@@ -0,0 +1,196 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.Textures.BlockIcons.FLUID_IN_SIGN;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPE_IN;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+
+import gregtech.GT_Mod;
+import gregtech.api.gui.modularui.GT_UIInfos;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Utility;
+
+public class GT_MetaTileEntity_Hatch_Input extends GT_MetaTileEntity_Hatch {
+
+ public RecipeMap<?> mRecipeMap = null;
+
+ public GT_MetaTileEntity_Hatch_Input(int aID, String aName, String aNameRegional, int aTier) {
+ this(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ new String[] { "Fluid Input for Multiblocks",
+ "Capacity: " + GT_Utility.formatNumbers(8000L * (1L << aTier)) + "L" });
+ }
+
+ public GT_MetaTileEntity_Hatch_Input(int aID, String aName, String aNameRegional, int aTier,
+ String[] aDescription) {
+ this(aID, 3, aName, aNameRegional, aTier, aDescription);
+ }
+
+ public GT_MetaTileEntity_Hatch_Input(int aID, int aSlot, String aName, String aNameRegional, int aTier) {
+ this(
+ aID,
+ aSlot,
+ aName,
+ aNameRegional,
+ aTier,
+ new String[] { "Fluid Input for Multiblocks", "", "Can hold " + aSlot + " types of fluid." });
+ mDescriptionArray[1] = "Capacity: " + GT_Utility.formatNumbers(getCapacityPerTank(aTier, aSlot)) + "L";
+ }
+
+ public GT_MetaTileEntity_Hatch_Input(int aID, int aSlot, String aName, String aNameRegional, int aTier,
+ String[] aDescription) {
+ super(aID, aName, aNameRegional, aTier, aSlot, aDescription);
+ }
+
+ public GT_MetaTileEntity_Hatch_Input(int aID, String aName, String aNameRegional, int aTier, int allSlotCount,
+ String[] strings) {
+ super(aID, aName, aNameRegional, aTier, allSlotCount, strings);
+ }
+
+ public int getCapacityPerTank(int aTier, int aSlot) {
+ return (int) (8000L * (1L << aTier) / aSlot);
+ }
+
+ public GT_MetaTileEntity_Hatch_Input(String aName, int aTier, String aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 3, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Hatch_Input(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 3, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Hatch_Input(String aName, int aSlots, int aTier, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aSlots, aDescription, aTextures);
+ }
+
+ @Override
+ public ITexture[] getTexturesActive(ITexture aBaseTexture) {
+ return GT_Mod.gregtechproxy.mRenderIndicatorsOnHatch
+ ? new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_IN), TextureFactory.of(FLUID_IN_SIGN) }
+ : new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_IN) };
+ }
+
+ @Override
+ public ITexture[] getTexturesInactive(ITexture aBaseTexture) {
+ return GT_Mod.gregtechproxy.mRenderIndicatorsOnHatch
+ ? new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_IN), TextureFactory.of(FLUID_IN_SIGN) }
+ : new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_IN) };
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return true;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return true;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Hatch_Input(mName, mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ if (mRecipeMap != null) {
+ aNBT.setString("recipeMap", mRecipeMap.unlocalizedName);
+ }
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ mRecipeMap = RecipeMap.getFromOldIdentifier(aNBT.getString("recipeMap"));
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ return true;
+ }
+
+ @Override
+ public boolean doesFillContainers() {
+ // return true;
+ return false;
+ }
+
+ @Override
+ public boolean doesEmptyContainers() {
+ return true;
+ }
+
+ @Override
+ public boolean canTankBeFilled() {
+ return true;
+ }
+
+ @Override
+ public boolean canTankBeEmptied() {
+ return true;
+ }
+
+ @Override
+ public boolean displaysItemStack() {
+ return true;
+ }
+
+ @Override
+ public boolean displaysStackSize() {
+ return false;
+ }
+
+ public void updateSlots() {
+ if (mInventory[getInputSlot()] != null && mInventory[getInputSlot()].stackSize <= 0)
+ mInventory[getInputSlot()] = null;
+ }
+
+ @Override
+ public boolean isFluidInputAllowed(FluidStack aFluid) {
+ return mRecipeMap == null || mRecipeMap.containsInput(aFluid);
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return side == aBaseMetaTileEntity.getFrontFacing() && aIndex == 1;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return side == aBaseMetaTileEntity.getFrontFacing() && aIndex == 0
+ && (mRecipeMap == null || mRecipeMap.containsInput(aStack)
+ || mRecipeMap.containsInput(GT_Utility.getFluidForFilledItem(aStack, true)));
+ }
+
+ @Override
+ public int getCapacity() {
+ return 8000 * (1 << mTier);
+ }
+
+ @Override
+ public int getTankPressure() {
+ return -100;
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_InputBus.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_InputBus.java
new file mode 100644
index 0000000000..8c99e813e2
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_InputBus.java
@@ -0,0 +1,318 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.Textures.BlockIcons.ITEM_IN_SIGN;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPE_IN;
+import static gregtech.api.metatileentity.BaseTileEntity.TOOLTIP_DELAY;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.StatCollector;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizons.modularui.api.drawable.UITexture;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.api.widget.Widget;
+import com.gtnewhorizons.modularui.common.widget.CycleButtonWidget;
+
+import gregtech.GT_Mod;
+import gregtech.api.gui.modularui.GT_UIInfos;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.IConfigurationCircuitSupport;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.modularui.IAddUIWidgets;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_ClientPreference;
+import gregtech.api.util.GT_OreDictUnificator;
+import gregtech.api.util.GT_TooltipDataCache;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.extensions.ArrayExt;
+
+public class GT_MetaTileEntity_Hatch_InputBus extends GT_MetaTileEntity_Hatch
+ implements IConfigurationCircuitSupport, IAddUIWidgets {
+
+ private static final String SORTING_MODE_TOOLTIP = "GT5U.machines.sorting_mode.tooltip";
+ private static final String ONE_STACK_LIMIT_TOOLTIP = "GT5U.machines.one_stack_limit.tooltip";
+ private static final int BUTTON_SIZE = 18;
+
+ public RecipeMap<?> mRecipeMap = null;
+ public boolean disableSort;
+ public boolean disableFilter = true;
+ public boolean disableLimited = true;
+ private int uiButtonCount = 0;
+
+ public GT_MetaTileEntity_Hatch_InputBus(int id, String name, String nameRegional, int tier) {
+ this(id, name, nameRegional, tier, getSlots(tier) + 1);
+ }
+
+ protected GT_MetaTileEntity_Hatch_InputBus(int id, String name, String nameRegional, int tier, int slots,
+ String[] description) {
+ super(id, name, nameRegional, tier, slots, description);
+ }
+
+ public GT_MetaTileEntity_Hatch_InputBus(int id, String name, String nameRegional, int tier, int slots) {
+ super(
+ id,
+ name,
+ nameRegional,
+ tier,
+ slots,
+ ArrayExt.of(
+ "Item Input for Multiblocks",
+ "Shift + right click with screwdriver to turn Sort mode on/off",
+ "Capacity: " + getSlots(tier) + " stack" + (getSlots(tier) >= 2 ? "s" : "")));
+ }
+
+ public GT_MetaTileEntity_Hatch_InputBus(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ this(aName, aTier, getSlots(aTier) + 1, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Hatch_InputBus(String aName, int aTier, int aSlots, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aSlots, aDescription, aTextures);
+ }
+
+ @Override
+ public ITexture[] getTexturesActive(ITexture aBaseTexture) {
+ return GT_Mod.gregtechproxy.mRenderIndicatorsOnHatch
+ ? new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_IN), TextureFactory.of(ITEM_IN_SIGN) }
+ : new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_IN) };
+ }
+
+ @Override
+ public ITexture[] getTexturesInactive(ITexture aBaseTexture) {
+ return GT_Mod.gregtechproxy.mRenderIndicatorsOnHatch
+ ? new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_IN), TextureFactory.of(ITEM_IN_SIGN) }
+ : new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_IN) };
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return true;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return true;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return aIndex != getCircuitSlot();
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Hatch_InputBus(mName, mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ return true;
+ }
+
+ @Override
+ public int getCircuitSlotX() {
+ return 153;
+ }
+
+ @Override
+ public int getCircuitSlotY() {
+ return 63;
+ }
+
+ @Override
+ public void initDefaultModes(NBTTagCompound aNBT) {
+ if (!getBaseMetaTileEntity().getWorld().isRemote) {
+ GT_ClientPreference tPreference = GT_Mod.gregtechproxy
+ .getClientPreference(getBaseMetaTileEntity().getOwnerUuid());
+ if (tPreference != null) disableFilter = !tPreference.isInputBusInitialFilterEnabled();
+ }
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTimer) {
+ if (aBaseMetaTileEntity.isServerSide() && aBaseMetaTileEntity.hasInventoryBeenModified()) {
+ updateSlots();
+ }
+ }
+
+ public void updateSlots() {
+ for (int i = 0; i < mInventory.length - 1; i++)
+ if (mInventory[i] != null && mInventory[i].stackSize <= 0) mInventory[i] = null;
+ if (!disableSort) fillStacksIntoFirstSlots();
+ }
+
+ protected void fillStacksIntoFirstSlots() {
+ final int L = mInventory.length - 1;
+ HashMap<GT_Utility.ItemId, Integer> slots = new HashMap<>(L);
+ HashMap<GT_Utility.ItemId, ItemStack> stacks = new HashMap<>(L);
+ List<GT_Utility.ItemId> order = new ArrayList<>(L);
+ List<Integer> validSlots = new ArrayList<>(L);
+ for (int i = 0; i < L; i++) {
+ if (!isValidSlot(i)) continue;
+ validSlots.add(i);
+ ItemStack s = mInventory[i];
+ if (s == null) continue;
+ GT_Utility.ItemId sID = GT_Utility.ItemId.createNoCopy(s);
+ slots.merge(sID, s.stackSize, Integer::sum);
+ if (!stacks.containsKey(sID)) stacks.put(sID, s);
+ order.add(sID);
+ mInventory[i] = null;
+ }
+ int slotindex = 0;
+ for (GT_Utility.ItemId sID : order) {
+ int toSet = slots.get(sID);
+ if (toSet == 0) continue;
+ int slot = validSlots.get(slotindex);
+ slotindex++;
+ mInventory[slot] = stacks.get(sID)
+ .copy();
+ toSet = Math.min(toSet, mInventory[slot].getMaxStackSize());
+ mInventory[slot].stackSize = toSet;
+ slots.merge(sID, toSet, (a, b) -> a - b);
+ }
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setBoolean("disableSort", disableSort);
+ aNBT.setBoolean("disableFilter", disableFilter);
+ aNBT.setBoolean("disableLimited", disableLimited);
+ if (mRecipeMap != null) {
+ aNBT.setString("recipeMap", mRecipeMap.unlocalizedName);
+ }
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ disableSort = aNBT.getBoolean("disableSort");
+ disableFilter = aNBT.getBoolean("disableFilter");
+ if (aNBT.hasKey("disableLimited")) {
+ disableLimited = aNBT.getBoolean("disableLimited");
+ }
+ mRecipeMap = RecipeMap.getFromOldIdentifier(aNBT.getString("recipeMap"));
+ }
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ if (!getBaseMetaTileEntity().getCoverInfoAtSide(side)
+ .isGUIClickable()) return;
+ if (aPlayer.isSneaking()) {
+ if (disableSort) {
+ disableSort = false;
+ } else {
+ if (disableLimited) {
+ disableLimited = false;
+ } else {
+ disableSort = true;
+ disableLimited = true;
+ }
+ }
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ StatCollector.translateToLocal("GT5U.hatch.disableSort." + disableSort) + " "
+ + StatCollector.translateToLocal("GT5U.hatch.disableLimited." + disableLimited));
+ } else {
+ disableFilter = !disableFilter;
+ GT_Utility
+ .sendChatToPlayer(aPlayer, StatCollector.translateToLocal("GT5U.hatch.disableFilter." + disableFilter));
+ }
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ if (aIndex == getCircuitSlot()) return false;
+ return side == getBaseMetaTileEntity().getFrontFacing();
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return side == getBaseMetaTileEntity().getFrontFacing() && aIndex != getCircuitSlot()
+ && (mRecipeMap == null || disableFilter || mRecipeMap.containsInput(aStack))
+ && (disableLimited || limitedAllowPutStack(aIndex, aStack));
+ }
+
+ protected boolean limitedAllowPutStack(int aIndex, ItemStack aStack) {
+ for (int i = 0; i < getSizeInventory(); i++)
+ if (GT_Utility.areStacksEqual(GT_OreDictUnificator.get_nocopy(aStack), mInventory[i])) return i == aIndex;
+ return mInventory[aIndex] == null;
+ }
+
+ @Override
+ public boolean allowSelectCircuit() {
+ return true;
+ }
+
+ @Override
+ public int getCircuitSlot() {
+ return getSlots(mTier);
+ }
+
+ private void addSortStacksButton(ModularWindow.Builder builder) {
+ builder.widget(
+ createToggleButton(
+ () -> !disableSort,
+ val -> disableSort = !val,
+ GT_UITextures.OVERLAY_BUTTON_SORTING_MODE,
+ () -> mTooltipCache.getData(SORTING_MODE_TOOLTIP)));
+ }
+
+ private void addOneStackLimitButton(ModularWindow.Builder builder) {
+ builder.widget(createToggleButton(() -> !disableLimited, val -> {
+ disableLimited = !val;
+ updateSlots();
+ }, GT_UITextures.OVERLAY_BUTTON_ONE_STACK_LIMIT, () -> mTooltipCache.getData(ONE_STACK_LIMIT_TOOLTIP)));
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ buildContext.addCloseListener(() -> uiButtonCount = 0);
+ addSortStacksButton(builder);
+ addOneStackLimitButton(builder);
+ // Remove one for ghost circuit slot
+ int slotCount = getSizeInventory();
+ if (allowSelectCircuit()) {
+ slotCount = slotCount - 1;
+ }
+ // We do this to decouple slot count from tier in here, since there is no reason to do so.
+ switch (slotCount) {
+ case 1 -> getBaseMetaTileEntity().add1by1Slot(builder);
+ case 4 -> getBaseMetaTileEntity().add2by2Slots(builder);
+ case 9 -> getBaseMetaTileEntity().add3by3Slots(builder);
+ case 16 -> getBaseMetaTileEntity().add4by4Slots(builder);
+ default -> {}
+ }
+ }
+
+ private Widget createToggleButton(Supplier<Boolean> getter, Consumer<Boolean> setter, UITexture picture,
+ Supplier<GT_TooltipDataCache.TooltipData> tooltipDataSupplier) {
+ return new CycleButtonWidget().setToggle(getter, setter)
+ .setStaticTexture(picture)
+ .setVariableBackground(GT_UITextures.BUTTON_STANDARD_TOGGLE)
+ .setTooltipShowUpDelay(TOOLTIP_DELAY)
+ .setPos(7 + (uiButtonCount++ * BUTTON_SIZE), 62)
+ .setSize(BUTTON_SIZE, BUTTON_SIZE)
+ .setGTTooltip(tooltipDataSupplier);
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Maintenance.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Maintenance.java
new file mode 100644
index 0000000000..8a4d718244
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Maintenance.java
@@ -0,0 +1,459 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_AUTOMAINTENANCE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_AUTOMAINTENANCE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_AUTOMAINTENANCE_IDLE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_AUTOMAINTENANCE_IDLE_GLOW;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_DUCTTAPE;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_MAINTENANCE;
+
+import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraftforge.common.util.FakePlayer;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizon.structurelib.StructureLibAPI;
+import com.gtnewhorizon.structurelib.alignment.IAlignment;
+import com.gtnewhorizon.structurelib.alignment.IAlignmentLimits;
+import com.gtnewhorizon.structurelib.alignment.IAlignmentProvider;
+import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
+import com.gtnewhorizon.structurelib.alignment.enumerable.Flip;
+import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.internal.wrapper.BaseSlot;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+
+import cpw.mods.fml.common.network.NetworkRegistry;
+import gregtech.GT_Mod;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.OrePrefixes;
+import gregtech.api.gui.modularui.GT_UIInfos;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.modularui.IAddUIWidgets;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_OreDictUnificator;
+import gregtech.api.util.GT_Utility;
+import ic2.core.IHasGui;
+import ic2.core.item.ItemToolbox;
+
+public class GT_MetaTileEntity_Hatch_Maintenance extends GT_MetaTileEntity_Hatch implements IAddUIWidgets, IAlignment {
+
+ private Rotation rotation = Rotation.NORMAL;
+
+ private static ItemStack[] sAutoMaintenanceInputs;
+ public boolean mWrench = false, mScrewdriver = false, mSoftHammer = false, mHardHammer = false,
+ mSolderingTool = false, mCrowbar = false, mAuto;
+
+ public GT_MetaTileEntity_Hatch_Maintenance(int aID, String aName, String aNameRegional, int aTier) {
+ super(aID, aName, aNameRegional, aTier, 1, "For maintaining Multiblocks");
+ mAuto = false;
+ }
+
+ public GT_MetaTileEntity_Hatch_Maintenance(int aID, String aName, String aNameRegional, int aTier, boolean aAuto) {
+ super(aID, aName, aNameRegional, aTier, 4, "For automatically maintaining Multiblocks");
+ mAuto = aAuto;
+ }
+
+ public GT_MetaTileEntity_Hatch_Maintenance(String aName, int aTier, String aDescription, ITexture[][][] aTextures,
+ boolean aAuto) {
+ super(aName, aTier, aAuto ? 4 : 1, aDescription, aTextures);
+ mAuto = aAuto;
+ }
+
+ public GT_MetaTileEntity_Hatch_Maintenance(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures,
+ boolean aAuto) {
+ super(aName, aTier, aAuto ? 4 : 1, aDescription, aTextures);
+ mAuto = aAuto;
+ }
+
+ private static ItemStack[] getAutoMaintenanceInputs() {
+ if (sAutoMaintenanceInputs == null) sAutoMaintenanceInputs = new ItemStack[] { ItemList.Duct_Tape.get(4),
+ GT_OreDictUnificator.get(OrePrefixes.cell, Materials.Lubricant, 2),
+ GT_OreDictUnificator.get(OrePrefixes.screw, Materials.Steel, 4),
+ GT_OreDictUnificator.get(OrePrefixes.circuit, Materials.HV, 2) };
+ return sAutoMaintenanceInputs;
+ }
+
+ @Override
+ public String[] getDescription() {
+ String[] desc;
+ if (mAuto) {
+ desc = new String[mDescriptionArray.length + 3];
+ System.arraycopy(mDescriptionArray, 0, desc, 0, mDescriptionArray.length);
+ desc[mDescriptionArray.length] = "4 Ducttape, 2 Lubricant Cells";
+ desc[mDescriptionArray.length + 1] = "4 Steel Screws, 2 HV Circuits";
+ desc[mDescriptionArray.length + 2] = "For each autorepair";
+ } else {
+ desc = new String[mDescriptionArray.length + 1];
+ System.arraycopy(mDescriptionArray, 0, desc, 0, mDescriptionArray.length);
+ desc[mDescriptionArray.length] = "Cannot be shared between Multiblocks!";
+ }
+ return desc;
+ }
+
+ @Override
+ public ITexture[] getTexturesActive(ITexture aBaseTexture) {
+ if (mAuto) return new ITexture[] { aBaseTexture, TextureFactory.builder()
+ .addIcon(OVERLAY_AUTOMAINTENANCE_IDLE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_AUTOMAINTENANCE_IDLE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] { aBaseTexture, TextureFactory.builder()
+ .addIcon(OVERLAY_MAINTENANCE)
+ .extFacing()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTexturesInactive(ITexture aBaseTexture) {
+ if (mAuto) return new ITexture[] { aBaseTexture, TextureFactory.builder()
+ .addIcon(OVERLAY_AUTOMAINTENANCE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_AUTOMAINTENANCE_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ return new ITexture[] { aBaseTexture, TextureFactory.builder()
+ .addIcon(OVERLAY_MAINTENANCE)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_DUCTTAPE)
+ .extFacing()
+ .build() };
+ }
+
+ @Override
+ public void initDefaultModes(NBTTagCompound aNBT) {
+ getBaseMetaTileEntity().setActive(true);
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return true;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return true;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return mAuto && GT_Mod.gregtechproxy.mAMHInteraction;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ if (aTileEntity.getMetaTileID() == 111)
+ return new GT_MetaTileEntity_Hatch_Maintenance(mName, mTier, mDescriptionArray, mTextures, true);
+ return new GT_MetaTileEntity_Hatch_Maintenance(mName, mTier, mDescriptionArray, mTextures, false);
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer, ForgeDirection side,
+ float aX, float aY, float aZ) {
+ if (aBaseMetaTileEntity.isClientSide()) return true;
+ if (side == aBaseMetaTileEntity.getFrontFacing()) {
+ // only allow OC robot fake player
+ if (aPlayer instanceof FakePlayer && !aPlayer.getGameProfile()
+ .getName()
+ .endsWith(".robot")) return false;
+ ItemStack tStack = aPlayer.getCurrentEquippedItem();
+ if (tStack != null) {
+ if (tStack.getItem() instanceof ItemToolbox) {
+ applyToolbox(tStack, aPlayer);
+ } else if (ItemList.Duct_Tape.isStackEqual(tStack)) {
+ mWrench = mScrewdriver = mSoftHammer = mHardHammer = mCrowbar = mSolderingTool = true;
+ getBaseMetaTileEntity().setActive(false);
+ if (--tStack.stackSize == 0) {
+ aPlayer.inventory.mainInventory[aPlayer.inventory.currentItem] = null;
+ }
+ } else GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ } else {
+ GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public void updateSlots() {
+ for (int i = 0; i < mInventory.length; i++)
+ if (mInventory[i] != null && mInventory[i].stackSize <= 0) mInventory[i] = null;
+ }
+
+ @Override
+ public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ super.onFirstTick(aBaseMetaTileEntity);
+ if (aBaseMetaTileEntity.isClientSide())
+ StructureLibAPI.queryAlignment((IAlignmentProvider) aBaseMetaTileEntity);
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ if (aBaseMetaTileEntity.isServerSide() && mAuto && aTick % 100L == 0L) {
+ aBaseMetaTileEntity.setActive(!isRecipeInputEqual(false));
+ }
+ }
+
+ @Override
+ public boolean onWrenchRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer entityPlayer,
+ float aX, float aY, float aZ) {
+ if (wrenchingSide != getBaseMetaTileEntity().getFrontFacing())
+ return super.onWrenchRightClick(side, wrenchingSide, entityPlayer, aX, aY, aZ);
+ if (!entityPlayer.isSneaking() && isRotationChangeAllowed()) {
+ toolSetRotation(null);
+ return true;
+ }
+ return false;
+ }
+
+ public boolean autoMaintainance() {
+ return isRecipeInputEqual(true);
+ }
+
+ public boolean isRecipeInputEqual(boolean aDecreaseStacksizeBySuccess) {
+ ItemStack[] mInputs = getAutoMaintenanceInputs();
+
+ int amt;
+
+ for (ItemStack tStack : mInputs) {
+ if (tStack != null) {
+ amt = tStack.stackSize;
+ boolean temp = true;
+ for (ItemStack aStack : mInventory) {
+ if ((GT_Utility.areUnificationsEqual(aStack, tStack, true)
+ || GT_Utility.areUnificationsEqual(GT_OreDictUnificator.get(false, aStack), tStack, true))) {
+ amt -= aStack.stackSize;
+ if (amt < 1) {
+ temp = false;
+ break;
+ }
+ }
+ }
+ if (temp) return false;
+ }
+ }
+
+ if (aDecreaseStacksizeBySuccess) {
+ for (ItemStack tStack : mInputs) {
+ if (tStack != null) {
+ amt = tStack.stackSize;
+ for (ItemStack aStack : mInventory) {
+ if ((GT_Utility.areUnificationsEqual(aStack, tStack, true) || GT_Utility
+ .areUnificationsEqual(GT_OreDictUnificator.get(false, aStack), tStack, true))) {
+ if (aStack.stackSize < amt) {
+ amt -= aStack.stackSize;
+ aStack.stackSize = 0;
+ } else {
+ aStack.stackSize -= amt;
+ amt = 0;
+ break;
+ }
+ }
+ }
+ }
+ }
+ mCrowbar = true;
+ mHardHammer = true;
+ mScrewdriver = true;
+ mSoftHammer = true;
+ mSolderingTool = true;
+ mWrench = true;
+ updateSlots();
+ }
+ return true;
+ }
+
+ public void onToolClick(ItemStack aStack, EntityLivingBase aPlayer, IInventory aToolboxInventory) {
+ if (aStack == null || aPlayer == null) return;
+
+ // Allow IC2 Toolbox with tools to function for maint issues.
+ if (aStack.getItem() instanceof ItemToolbox && aPlayer instanceof EntityPlayer) {
+ applyToolbox(aStack, (EntityPlayer) aPlayer);
+ return;
+ }
+
+ if (GT_Utility.isStackInList(aStack, GregTech_API.sWrenchList) && !mWrench
+ && GT_ModHandler.damageOrDechargeItem(aStack, 1, 1000, aPlayer)) mWrench = true;
+ if (GT_Utility.isStackInList(aStack, GregTech_API.sScrewdriverList) && !mScrewdriver
+ && GT_ModHandler.damageOrDechargeItem(aStack, 1, 1000, aPlayer)) mScrewdriver = true;
+ if (GT_Utility.isStackInList(aStack, GregTech_API.sSoftHammerList) && !mSoftHammer
+ && GT_ModHandler.damageOrDechargeItem(aStack, 1, 1000, aPlayer)) mSoftHammer = true;
+ if (GT_Utility.isStackInList(aStack, GregTech_API.sHardHammerList) && !mHardHammer
+ && GT_ModHandler.damageOrDechargeItem(aStack, 1, 1000, aPlayer)) mHardHammer = true;
+ if (GT_Utility.isStackInList(aStack, GregTech_API.sCrowbarList) && !mCrowbar
+ && GT_ModHandler.damageOrDechargeItem(aStack, 1, 1000, aPlayer)) mCrowbar = true;
+ if (!mSolderingTool && GT_ModHandler.useSolderingIron(aStack, aPlayer, aToolboxInventory))
+ mSolderingTool = true;
+ if (GT_OreDictUnificator.isItemStackInstanceOf(aStack, "craftingDuctTape")) {
+ mWrench = mScrewdriver = mSoftHammer = mHardHammer = mCrowbar = mSolderingTool = true;
+ getBaseMetaTileEntity().setActive(false);
+ aStack.stackSize--;
+ }
+ if (mSolderingTool && aPlayer instanceof EntityPlayerMP tPlayer) {
+ try {
+ GT_Mod.achievements.issueAchievement(tPlayer, "maintainance");
+ } catch (Exception ignored) {}
+ }
+ }
+
+ public void onToolClick(ItemStack aStack, EntityLivingBase aPlayer) {
+ onToolClick(aStack, aPlayer, null);
+ }
+
+ private void applyToolbox(ItemStack aStack, EntityPlayer aPlayer) {
+ final ItemToolbox aToolbox = (ItemToolbox) aStack.getItem();
+ final IHasGui aToolboxGUI = aToolbox.getInventory(aPlayer, aStack);
+ for (int i = 0; i < aToolboxGUI.getSizeInventory(); i++) {
+ if (aToolboxGUI.getStackInSlot(i) != null) {
+ onToolClick(aToolboxGUI.getStackInSlot(i), aPlayer, aToolboxGUI);
+ if (aToolboxGUI.getStackInSlot(i) != null && aToolboxGUI.getStackInSlot(i).stackSize <= 0)
+ aToolboxGUI.setInventorySlotContents(i, null);
+ }
+ }
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return mAuto && GT_Mod.gregtechproxy.mAMHInteraction;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ if (mAuto && GT_Mod.gregtechproxy.mAMHInteraction) {
+ for (int i = 0; i < getSizeInventory(); i++) if (GT_Utility.areStacksEqual(
+ GT_OreDictUnificator.get(false, aStack),
+ GT_OreDictUnificator.get(false, getStackInSlot(i)))) return i == aIndex;
+ for (ItemStack tInput : getAutoMaintenanceInputs())
+ if (GT_Utility.areUnificationsEqual(tInput, aStack, true)
+ || GT_Utility.areUnificationsEqual(GT_OreDictUnificator.get(false, aStack), tInput, true))
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ if (mAuto) {
+ getBaseMetaTileEntity().add2by2Slots(builder);
+ } else {
+ builder.widget(
+ new DrawableWidget().setDrawable(GT_UITextures.SLOT_MAINTENANCE)
+ .setPos(78, 33)
+ .setSize(20, 20))
+ .widget(new SlotWidget(BaseSlot.empty()) {
+
+ @Override
+ public boolean handleDragAndDrop(ItemStack draggedStack, int button) {
+ return false;
+ }
+
+ @Override
+ protected void phantomClick(ClickData clickData, ItemStack cursorStack) {
+ if (cursorStack == null) return;
+ onToolClick(cursorStack, getContext().getPlayer());
+ if (cursorStack.stackSize < 1) {
+ getContext().getPlayer().inventory.setItemStack(null);
+ }
+ if (getContext().getPlayer() instanceof EntityPlayerMP) {
+ ((EntityPlayerMP) getContext().getPlayer()).updateHeldItem();
+ }
+ }
+ }.disableShiftInsert()
+ .setBackground(GT_UITextures.TRANSPARENT)
+ .setPos(79, 34))
+ .widget(
+ new TextWidget("Click with Tool to repair.").setDefaultColor(COLOR_TEXT_GRAY.get())
+ .setPos(8, 12));
+ }
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setByte("mRotation", (byte) rotation.getIndex());
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ rotation = Rotation.byIndex(aNBT.getByte("mRotation"));
+ super.loadNBTData(aNBT);
+ }
+
+ @Override
+ public ExtendedFacing getExtendedFacing() {
+ return ExtendedFacing.of(getBaseMetaTileEntity().getFrontFacing(), rotation, Flip.NONE);
+ }
+
+ @Override
+ public void setExtendedFacing(ExtendedFacing alignment) {
+ boolean changed = false;
+ final IGregTechTileEntity base = getBaseMetaTileEntity();
+ if (base.getFrontFacing() != alignment.getDirection()) {
+ base.setFrontFacing(alignment.getDirection());
+ changed = true;
+ }
+ if (rotation != alignment.getRotation()) {
+ rotation = alignment.getRotation();
+ changed = true;
+ }
+ if (changed) {
+ if (base.isServerSide() && !GregTech_API.isDummyWorld(base.getWorld())) {
+ StructureLibAPI.sendAlignment(
+ (IAlignmentProvider) base,
+ new NetworkRegistry.TargetPoint(
+ base.getWorld().provider.dimensionId,
+ base.getXCoord(),
+ base.getYCoord(),
+ base.getZCoord(),
+ 512));
+ } else {
+ base.issueTextureUpdate();
+ }
+ }
+ }
+
+ @Override
+ public IAlignmentLimits getAlignmentLimits() {
+ return (d, r, f) -> f.isNotFlipped();
+ }
+
+ @Override
+ public boolean isFlipChangeAllowed() {
+ return false;
+ }
+
+ @Override
+ public boolean isRotationChangeAllowed() {
+ return true;
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Muffler.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Muffler.java
new file mode 100644
index 0000000000..8707d0f804
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Muffler.java
@@ -0,0 +1,208 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_MUFFLER;
+import static gregtech.api.objects.XSTR.XSTR_INSTANCE;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import gregtech.GT_Mod;
+import gregtech.api.enums.ParticleFX;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_LanguageManager;
+import gregtech.api.util.WorldSpawnedEventBuilder;
+import gregtech.common.GT_Pollution;
+
+@SuppressWarnings("unused") // Unused API is expected within scope
+public class GT_MetaTileEntity_Hatch_Muffler extends GT_MetaTileEntity_Hatch {
+
+ private static final String localizedDescFormat = GT_LanguageManager.addStringLocalization(
+ "gt.blockmachines.hatch.muffler.desc.format",
+ "Outputs the Pollution (Might cause ... things)%n" + "DO NOT OBSTRUCT THE OUTPUT!%n"
+ + "Reduces Pollution to %d%%%n"
+ + "Recovers %d%% of CO2/CO/SO2");
+ private final int pollutionReduction = calculatePollutionReduction(100);
+ private final int pollutionRecover = 100 - pollutionReduction;
+ private final String[] description = String.format(localizedDescFormat, pollutionReduction, pollutionRecover)
+ .split("\\R");
+
+ public GT_MetaTileEntity_Hatch_Muffler(int aID, String aName, String aNameRegional, int aTier) {
+ super(aID, aName, aNameRegional, aTier, 0, "");
+ }
+
+ public GT_MetaTileEntity_Hatch_Muffler(int aID, String aName, String aNameRegional, int aTier, int aInvSlotCount,
+ String[] aDescription, ITexture... aTextures) {
+ super(aID, aName, aNameRegional, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Hatch_Muffler(String aName, int aTier, String aDescription, ITexture[][][] aTextures) {
+ this(aName, aTier, new String[] { aDescription }, aTextures);
+ }
+
+ public GT_MetaTileEntity_Hatch_Muffler(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ this(aName, aTier, 0, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Hatch_Muffler(String aName, int aTier, int aInvSlotCount, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ @Override
+ public String[] getDescription() {
+ return description;
+ }
+
+ @Override
+ public ITexture[] getTexturesActive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_MUFFLER) };
+ }
+
+ @Override
+ public ITexture[] getTexturesInactive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_MUFFLER) };
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return true;
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return false;
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Hatch_Muffler(mName, mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ if (aBaseMetaTileEntity.isClientSide() && this.getBaseMetaTileEntity()
+ .isActive()) {
+ pollutionParticles(
+ this.getBaseMetaTileEntity()
+ .getWorld(),
+ ParticleFX.LARGE_SMOKE.toString());
+ }
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return true;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @SideOnly(Side.CLIENT)
+ public void pollutionParticles(World aWorld, String name) {
+ boolean chk1, chk2, chk3;
+ float ran1 = XSTR_INSTANCE.nextFloat(), ran2, ran3;
+ chk1 = ran1 * 100 < calculatePollutionReduction(100);
+ if (GT_Pollution.getPollution(getBaseMetaTileEntity()) >= GT_Mod.gregtechproxy.mPollutionSmogLimit) {
+ ran2 = XSTR_INSTANCE.nextFloat();
+ ran3 = XSTR_INSTANCE.nextFloat();
+ chk2 = ran2 * 100 < calculatePollutionReduction(100);
+ chk3 = ran3 * 100 < calculatePollutionReduction(100);
+ if (!(chk1 || chk2 || chk3)) return;
+ } else {
+ if (!chk1) return;
+ ran2 = ran3 = 0.0F;
+ chk2 = chk3 = false;
+ }
+
+ final IGregTechTileEntity aMuffler = this.getBaseMetaTileEntity();
+ final ForgeDirection aDir = aMuffler.getFrontFacing();
+ final float xPos = aDir.offsetX * 0.76F + aMuffler.getXCoord() + 0.25F;
+ final float yPos = aDir.offsetY * 0.76F + aMuffler.getYCoord() + 0.25F;
+ final float zPos = aDir.offsetZ * 0.76F + aMuffler.getZCoord() + 0.25F;
+
+ final float ySpd = aDir.offsetY * 0.1F + 0.2F + 0.1F * XSTR_INSTANCE.nextFloat();
+ final float xSpd;
+ final float zSpd;
+
+ if (aDir.offsetY == -1) {
+ final float temp = XSTR_INSTANCE.nextFloat() * 2 * (float) Math.PI;
+ xSpd = (float) Math.sin(temp) * 0.1F;
+ zSpd = (float) Math.cos(temp) * 0.1F;
+ } else {
+ xSpd = aDir.offsetX * (0.1F + 0.2F * XSTR_INSTANCE.nextFloat());
+ zSpd = aDir.offsetZ * (0.1F + 0.2F * XSTR_INSTANCE.nextFloat());
+ }
+
+ final WorldSpawnedEventBuilder.ParticleEventBuilder events = new WorldSpawnedEventBuilder.ParticleEventBuilder()
+ .setIdentifier(name)
+ .setWorld(aWorld)
+ .setMotion(xSpd, ySpd, zSpd);
+
+ if (chk1) {
+ events
+ .setPosition(
+ xPos + ran1 * 0.5F,
+ yPos + XSTR_INSTANCE.nextFloat() * 0.5F,
+ zPos + XSTR_INSTANCE.nextFloat() * 0.5F)
+ .run();
+ }
+ if (chk2) {
+ events
+ .setPosition(
+ xPos + ran2 * 0.5F,
+ yPos + XSTR_INSTANCE.nextFloat() * 0.5F,
+ zPos + XSTR_INSTANCE.nextFloat() * 0.5F)
+ .run();
+ }
+ if (chk3) {
+ events
+ .setPosition(
+ xPos + ran3 * 0.5F,
+ yPos + XSTR_INSTANCE.nextFloat() * 0.5F,
+ zPos + XSTR_INSTANCE.nextFloat() * 0.5F)
+ .run();
+ }
+ }
+
+ public int calculatePollutionReduction(int aPollution) {
+ if (mTier < 2) {
+ return aPollution;
+ }
+ return (int) ((float) aPollution * ((100F - 12.5F * ((float) mTier - 1F)) / 100F));
+ }
+
+ /**
+ * @param mte The multi-block controller's {@link MetaTileEntity} MetaTileEntity is passed so newer muffler hatches
+ * can do wacky things with the multis
+ * @return pollution success
+ */
+ public boolean polluteEnvironment(MetaTileEntity mte) {
+ if (getBaseMetaTileEntity().getAirAtSide(getBaseMetaTileEntity().getFrontFacing())) {
+ GT_Pollution.addPollution(getBaseMetaTileEntity(), calculatePollutionReduction(10000));
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_MultiInput.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_MultiInput.java
new file mode 100644
index 0000000000..267c2d0f1c
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_MultiInput.java
@@ -0,0 +1,300 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_INPUT_HATCH_2x2;
+
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.fluids.FluidTankInfo;
+
+import com.gtnewhorizons.modularui.api.ModularUITextures;
+import com.gtnewhorizons.modularui.api.math.Pos2d;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.fluid.FluidStackTank;
+import com.gtnewhorizons.modularui.common.widget.FluidSlotWidget;
+
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.modularui.IAddUIWidgets;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.render.TextureFactory;
+
+public class GT_MetaTileEntity_Hatch_MultiInput extends GT_MetaTileEntity_Hatch_Input implements IAddUIWidgets {
+
+ private final FluidStack[] mStoredFluid;
+ private final FluidStackTank[] fluidTanks;
+ public final int mCapacityPer;
+
+ public GT_MetaTileEntity_Hatch_MultiInput(int aID, int aSlot, String aName, String aNameRegional, int aTier) {
+ super(aID, aSlot, aName, aNameRegional, aTier);
+ this.mStoredFluid = new FluidStack[aSlot];
+ fluidTanks = new FluidStackTank[aSlot];
+ mCapacityPer = getCapacityPerTank(aTier, aSlot);
+ }
+
+ public GT_MetaTileEntity_Hatch_MultiInput(int aID, int aSlot, String aName, String aNameRegional, int aTier,
+ String[] aDescription) {
+ super(aID, aSlot, aName, aNameRegional, aTier, aDescription);
+ this.mStoredFluid = new FluidStack[aSlot];
+ fluidTanks = new FluidStackTank[aSlot];
+ mCapacityPer = getCapacityPerTank(aTier, aSlot);
+ }
+
+ public GT_MetaTileEntity_Hatch_MultiInput(String aName, int aSlot, int aTier, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aSlot, aTier, aDescription, aTextures);
+ this.mStoredFluid = new FluidStack[aSlot];
+ fluidTanks = new FluidStackTank[aSlot];
+ mCapacityPer = getCapacityPerTank(aTier, aSlot);
+ for (int i = 0; i < aSlot; i++) {
+ final int index = i;
+ fluidTanks[i] = new FluidStackTank(
+ () -> mStoredFluid[index],
+ fluid -> mStoredFluid[index] = fluid,
+ mCapacityPer);
+ }
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Hatch_MultiInput(mName, getMaxType(), mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ if (mStoredFluid != null) {
+ for (int i = 0; i < mStoredFluid.length; i++) {
+ if (mStoredFluid[i] != null)
+ aNBT.setTag("mFluid" + i, mStoredFluid[i].writeToNBT(new NBTTagCompound()));
+ }
+ }
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ if (mStoredFluid != null) {
+ for (int i = 0; i < mStoredFluid.length; i++) {
+ if (aNBT.hasKey("mFluid" + i)) {
+ mStoredFluid[i] = FluidStack.loadFluidStackFromNBT(aNBT.getCompoundTag("mFluid" + i));
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean displaysStackSize() {
+ return true;
+ }
+
+ public FluidStack[] getStoredFluid() {
+ return mStoredFluid;
+ }
+
+ @Override
+ public ITexture[] getTexturesActive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_INPUT_HATCH_2x2) };
+ }
+
+ @Override
+ public ITexture[] getTexturesInactive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_INPUT_HATCH_2x2) };
+ }
+
+ public int getMaxType() {
+ return mStoredFluid.length;
+ }
+
+ @Override
+ public FluidStack getFluid() {
+ for (FluidStack tFluid : mStoredFluid) {
+ if (tFluid != null && tFluid.amount > 0) return tFluid;
+ }
+ return null;
+ }
+
+ public FluidStack getFluid(int aSlot) {
+ if (mStoredFluid == null || aSlot < 0 || aSlot >= getMaxType()) return null;
+ return mStoredFluid[aSlot];
+ }
+
+ @Override
+ public int getFluidAmount() {
+ if (getFluid() != null) {
+ return getFluid().amount;
+ }
+ return 0;
+ }
+
+ @Override
+ public int getCapacity() {
+ return mCapacityPer;
+ }
+
+ public int getFirstEmptySlot() {
+ for (int i = 0; i < mStoredFluid.length; i++) {
+ if (mStoredFluid[i] == null) return i;
+ }
+ return -1;
+ }
+
+ public boolean hasFluid(FluidStack aFluid) {
+ if (aFluid == null) return false;
+ for (FluidStack tFluid : mStoredFluid) {
+ if (aFluid.isFluidEqual(tFluid)) return true;
+ }
+ return false;
+ }
+
+ public int getFluidSlot(FluidStack tFluid) {
+ if (tFluid == null) return -1;
+ for (int i = 0; i < mStoredFluid.length; i++) {
+ if (tFluid.equals(mStoredFluid[i])) return i;
+ }
+ return -1;
+ }
+
+ public int getFluidAmount(FluidStack tFluid) {
+ int tSlot = getFluidSlot(tFluid);
+ if (tSlot != -1) {
+ return mStoredFluid[tSlot].amount;
+ }
+ return 0;
+ }
+
+ public void setFluid(FluidStack aFluid, int aSlot) {
+ if (aSlot < 0 || aSlot >= getMaxType()) return;
+ mStoredFluid[aSlot] = aFluid;
+ }
+
+ public void addFluid(FluidStack aFluid, int aSlot) {
+ if (aSlot < 0 || aSlot >= getMaxType()) return;
+ if (aFluid.equals(mStoredFluid[aSlot])) mStoredFluid[aSlot].amount += aFluid.amount;
+ if (mStoredFluid[aSlot] == null) mStoredFluid[aSlot] = aFluid.copy();
+ }
+
+ @Override
+ public void onPreTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (aBaseMetaTileEntity.isServerSide()) {
+ mFluid = getFluid();
+ }
+ super.onPreTick(aBaseMetaTileEntity, aTick);
+ }
+
+ @Override
+ public int fill(FluidStack aFluid, boolean doFill) {
+ if (aFluid == null || aFluid.getFluid()
+ .getID() <= 0 || aFluid.amount <= 0 || !canTankBeFilled() || !isFluidInputAllowed(aFluid)) return 0;
+ if (!hasFluid(aFluid) && getFirstEmptySlot() != -1) {
+ int tFilled = Math.min(aFluid.amount, mCapacityPer);
+ if (doFill) {
+ FluidStack tFluid = aFluid.copy();
+ tFluid.amount = tFilled;
+ addFluid(tFluid, getFirstEmptySlot());
+ getBaseMetaTileEntity().markDirty();
+ }
+ return tFilled;
+ }
+ if (hasFluid(aFluid)) {
+ int tLeft = mCapacityPer - getFluidAmount(aFluid);
+ int tFilled = Math.min(tLeft, aFluid.amount);
+ if (doFill) {
+ FluidStack tFluid = aFluid.copy();
+ tFluid.amount = tFilled;
+ addFluid(tFluid, getFluidSlot(tFluid));
+ getBaseMetaTileEntity().markDirty();
+ }
+ return tFilled;
+ }
+ return 0;
+ }
+
+ @Override
+ public FluidStack drain(int maxDrain, boolean doDrain) {
+ if (getFluid() == null || !canTankBeEmptied()) return null;
+ if (getFluid().amount <= 0 && isFluidChangingAllowed()) {
+ setFluid(null, getFluidSlot(getFluid()));
+ getBaseMetaTileEntity().markDirty();
+ return null;
+ }
+ FluidStack tRemove = getFluid().copy();
+ tRemove.amount = Math.min(maxDrain, tRemove.amount);
+ if (doDrain) {
+ getFluid().amount -= tRemove.amount;
+ getBaseMetaTileEntity().markDirty();
+ }
+ if (getFluid() == null || getFluid().amount <= 0 && isFluidChangingAllowed()) {
+ setFluid(null, getFluidSlot(getFluid()));
+ getBaseMetaTileEntity().markDirty();
+ }
+ return tRemove;
+ }
+
+ @Override
+ public int fill(ForgeDirection from, FluidStack resource, boolean doFill) {
+ return fill(resource, doFill);
+ }
+
+ @Override
+ public FluidStack drain(ForgeDirection from, FluidStack aFluid, boolean doDrain) {
+ if (aFluid == null || !hasFluid(aFluid)) return null;
+ FluidStack tStored = mStoredFluid[getFluidSlot(aFluid)];
+ if (tStored.amount <= 0 && isFluidChangingAllowed()) {
+ setFluid(null, getFluidSlot(tStored));
+ getBaseMetaTileEntity().markDirty();
+ return null;
+ }
+ FluidStack tRemove = tStored.copy();
+ tRemove.amount = Math.min(aFluid.amount, tRemove.amount);
+ if (doDrain) {
+ tStored.amount -= tRemove.amount;
+ getBaseMetaTileEntity().markDirty();
+ }
+ if (tStored.amount <= 0 && isFluidChangingAllowed()) {
+ setFluid(null, getFluidSlot(tStored));
+ getBaseMetaTileEntity().markDirty();
+ }
+ return tRemove;
+ }
+
+ @Override
+ public FluidTankInfo[] getTankInfo(ForgeDirection from) {
+ FluidTankInfo[] FTI = new FluidTankInfo[getMaxType()];
+ for (int i = 0; i < getMaxType(); i++) {
+ FTI[i] = new FluidTankInfo(mStoredFluid[i], mCapacityPer);
+ }
+ return FTI;
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (aBaseMetaTileEntity.isServerSide() && mStoredFluid != null) {
+ for (int i = 0; i < getMaxType(); i++) {
+ if (mStoredFluid[i] != null && mStoredFluid[i].amount <= 0) {
+ mStoredFluid[i] = null;
+ }
+ }
+ }
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return aIndex >= 4;
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ final int SLOT_NUMBER = 4;
+ final Pos2d[] positions = new Pos2d[] { new Pos2d(70, 25), new Pos2d(88, 25), new Pos2d(70, 43),
+ new Pos2d(88, 43), };
+
+ for (int i = 0; i < SLOT_NUMBER; i++) {
+ builder.widget(
+ new FluidSlotWidget(fluidTanks[i]).setBackground(ModularUITextures.FLUID_SLOT)
+ .setPos(positions[i]));
+ }
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Output.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Output.java
new file mode 100644
index 0000000000..2aa2410bd1
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Output.java
@@ -0,0 +1,497 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.Textures.BlockIcons.FLUID_OUT_SIGN;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPE_OUT;
+
+import java.lang.ref.WeakReference;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidContainerRegistry;
+import net.minecraftforge.fluids.FluidRegistry;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.fluids.IFluidContainerItem;
+import net.minecraftforge.fluids.IFluidHandler;
+
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+
+import gregtech.GT_Mod;
+import gregtech.api.gui.modularui.GT_UIInfos;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.fluid.IFluidStore;
+import gregtech.api.interfaces.metatileentity.IFluidLockable;
+import gregtech.api.interfaces.modularui.IAddUIWidgets;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.gui.modularui.widget.FluidLockWidget;
+
+public class GT_MetaTileEntity_Hatch_Output extends GT_MetaTileEntity_Hatch
+ implements IFluidStore, IFluidLockable, IAddUIWidgets {
+
+ private String lockedFluidName = null;
+ private WeakReference<EntityPlayer> playerThatLockedfluid = null;
+ public byte mMode = 0;
+
+ public GT_MetaTileEntity_Hatch_Output(int aID, String aName, String aNameRegional, int aTier) {
+ super(
+ aID,
+ aName,
+ aNameRegional,
+ aTier,
+ 4,
+ new String[] { "Fluid Output for Multiblocks",
+ "Capacity: " + GT_Utility.formatNumbers(8000L * (1L << aTier)) + "L",
+ "Right click with screwdriver to restrict output",
+ "Can be restricted to put out Items and/or Steam/No Steam/1 specific Fluid",
+ "Restricted Output Hatches are given priority for Multiblock Fluid output" });
+ }
+
+ public GT_MetaTileEntity_Hatch_Output(String aName, int aTier, String aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 4, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Hatch_Output(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 4, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Hatch_Output(int aID, String aName, String aNameRegional, int aTier, String[] aDescription,
+ int inventorySize) {
+ super(aID, aName, aNameRegional, aTier, inventorySize, aDescription);
+ }
+
+ public GT_MetaTileEntity_Hatch_Output(String name, int tier, int slots, String[] description,
+ ITexture[][][] textures) {
+ super(name, tier, slots, description, textures);
+ }
+
+ @Override
+ public ITexture[] getTexturesActive(ITexture aBaseTexture) {
+ return GT_Mod.gregtechproxy.mRenderIndicatorsOnHatch
+ ? new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_OUT), TextureFactory.of(FLUID_OUT_SIGN) }
+ : new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_OUT) };
+ }
+
+ @Override
+ public ITexture[] getTexturesInactive(ITexture aBaseTexture) {
+ return GT_Mod.gregtechproxy.mRenderIndicatorsOnHatch
+ ? new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_OUT), TextureFactory.of(FLUID_OUT_SIGN) }
+ : new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_OUT) };
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return true;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return true;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public boolean isLiquidInput(ForgeDirection side) {
+ return false;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Hatch_Output(mName, mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ return true;
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ if (aBaseMetaTileEntity.isServerSide() && aBaseMetaTileEntity.isAllowedToWork() && mFluid != null) {
+ IFluidHandler tTileEntity = aBaseMetaTileEntity
+ .getITankContainerAtSide(aBaseMetaTileEntity.getFrontFacing());
+ if (tTileEntity != null) {
+ GT_Utility.moveFluid(
+ aBaseMetaTileEntity,
+ tTileEntity,
+ aBaseMetaTileEntity.getFrontFacing(),
+ Math.max(1, mFluid.amount),
+ null);
+ }
+ }
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setByte("mMode", mMode);
+ if (isFluidLocked() && lockedFluidName != null && lockedFluidName.length() != 0)
+ aNBT.setString("lockedFluidName", lockedFluidName);
+ else aNBT.removeTag("lockedFluidName");
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ mMode = aNBT.getByte("mMode");
+ if (isFluidLocked()) {
+ lockedFluidName = aNBT.getString("lockedFluidName");
+ }
+ lockedFluidName = GT_Utility.isStringInvalid(lockedFluidName) ? null : lockedFluidName;
+ }
+
+ @Override
+ public boolean doesFillContainers() {
+ return true;
+ }
+
+ @Override
+ public boolean doesEmptyContainers() {
+ return false;
+ }
+
+ @Override
+ public boolean canTankBeFilled() {
+ return true;
+ }
+
+ @Override
+ public boolean canTankBeEmptied() {
+ return true;
+ }
+
+ @Override
+ public boolean displaysItemStack() {
+ return true;
+ }
+
+ @Override
+ public boolean displaysStackSize() {
+ return false;
+ }
+
+ public int getLockedDisplaySlot() {
+ return 3;
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ // Because getStackDisplaySlot() only allow return one int, this place I only can manually set.
+ return aIndex != getStackDisplaySlot() && aIndex != getLockedDisplaySlot();
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return side == aBaseMetaTileEntity.getFrontFacing() && aIndex == 1;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return side == aBaseMetaTileEntity.getFrontFacing() && aIndex == 0;
+ }
+
+ @Override
+ public int getCapacity() {
+ return 8000 * (1 << mTier);
+ }
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ if (!getBaseMetaTileEntity().getCoverInfoAtSide(side)
+ .isGUIClickable()) return;
+ if (aPlayer.isSneaking()) {
+ mMode = (byte) ((mMode + 9) % 10);
+ } else {
+ mMode = (byte) ((mMode + 1) % 10);
+ }
+ final String inBrackets;
+ switch (mMode) {
+ case 0 -> {
+ GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("108", "Outputs misc. Fluids, Steam and Items"));
+ this.setLockedFluidName(null);
+ }
+ case 1 -> {
+ GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("109", "Outputs Steam and Items"));
+ this.setLockedFluidName(null);
+ }
+ case 2 -> {
+ GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("110", "Outputs Steam and misc. Fluids"));
+ this.setLockedFluidName(null);
+ }
+ case 3 -> {
+ GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("111", "Outputs Steam"));
+ this.setLockedFluidName(null);
+ }
+ case 4 -> {
+ GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("112", "Outputs misc. Fluids and Items"));
+ this.setLockedFluidName(null);
+ }
+ case 5 -> {
+ GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("113", "Outputs only Items"));
+ this.setLockedFluidName(null);
+ }
+ case 6 -> {
+ GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("114", "Outputs only misc. Fluids"));
+ this.setLockedFluidName(null);
+ }
+ case 7 -> {
+ GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("115", "Outputs nothing"));
+ this.setLockedFluidName(null);
+ }
+ case 8 -> {
+ playerThatLockedfluid = new WeakReference<>(aPlayer);
+ if (mFluid == null) {
+ this.setLockedFluidName(null);
+ inBrackets = GT_Utility.trans(
+ "115.3",
+ "currently none, will be locked to the next that is put in (or use fluid cell to lock)");
+ } else {
+ this.setLockedFluidName(
+ this.getDrainableStack()
+ .getFluid()
+ .getName());
+ inBrackets = this.getDrainableStack()
+ .getLocalizedName();
+ }
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ String.format(
+ "%s (%s)",
+ GT_Utility.trans("151.1", "Outputs items and 1 specific Fluid"),
+ inBrackets));
+ }
+ case 9 -> {
+ playerThatLockedfluid = new WeakReference<>(aPlayer);
+ if (mFluid == null) {
+ this.setLockedFluidName(null);
+ inBrackets = GT_Utility.trans(
+ "115.3",
+ "currently none, will be locked to the next that is put in (or use fluid cell to lock)");
+ } else {
+ this.setLockedFluidName(
+ this.getDrainableStack()
+ .getFluid()
+ .getName());
+ inBrackets = this.getDrainableStack()
+ .getLocalizedName();
+ }
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ String.format("%s (%s)", GT_Utility.trans("151.2", "Outputs 1 specific Fluid"), inBrackets));
+ }
+ }
+ }
+
+ private boolean tryToLockHatch(EntityPlayer aPlayer, ForgeDirection side) {
+ if (!getBaseMetaTileEntity().getCoverInfoAtSide(side)
+ .isGUIClickable()) return false;
+ if (!isFluidLocked()) return false;
+ final ItemStack tCurrentItem = aPlayer.inventory.getCurrentItem();
+ if (tCurrentItem == null) return false;
+ FluidStack tFluid = FluidContainerRegistry.getFluidForFilledItem(tCurrentItem);
+ if (tFluid == null && tCurrentItem.getItem() instanceof IFluidContainerItem)
+ tFluid = ((IFluidContainerItem) tCurrentItem.getItem()).getFluid(tCurrentItem);
+ if (tFluid != null) {
+ if (getLockedFluidName() != null && !getLockedFluidName().equals(
+ tFluid.getFluid()
+ .getName())) {
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ String.format(
+ "%s %s",
+ GT_Utility.trans(
+ "151.3",
+ "Hatch is locked to a different fluid. To change the locking, empty it and made it locked to the next fluid with a screwdriver. Currently locked to"),
+ StatCollector.translateToLocal(getLockedFluidName())));
+ } else {
+ setLockedFluidName(
+ tFluid.getFluid()
+ .getName());
+ if (mMode == 8) GT_Utility.sendChatToPlayer(
+ aPlayer,
+ String.format(
+ "%s (%s)",
+ GT_Utility.trans("151.1", "Outputs items and 1 specific Fluid"),
+ tFluid.getLocalizedName()));
+ else GT_Utility.sendChatToPlayer(
+ aPlayer,
+ String.format(
+ "%s (%s)",
+ GT_Utility.trans("151.2", "Outputs 1 specific Fluid"),
+ tFluid.getLocalizedName()));
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public byte getMode() {
+ return mMode;
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer, ForgeDirection side,
+ float aX, float aY, float aZ) {
+ if (tryToLockHatch(aPlayer, side)) return true;
+ return super.onRightclick(aBaseMetaTileEntity, aPlayer, side, aX, aY, aZ);
+ }
+
+ public boolean outputsSteam() {
+ return mMode < 4;
+ }
+
+ public boolean outputsLiquids() {
+ return mMode % 2 == 0 || mMode == 9;
+ }
+
+ public boolean outputsItems() {
+ return mMode % 4 < 2 && mMode != 9;
+ }
+
+ @Override
+ public String getLockedFluidName() {
+ return lockedFluidName;
+ }
+
+ @Override
+ public void setLockedFluidName(String lockedFluidName) {
+ this.lockedFluidName = lockedFluidName;
+ markDirty();
+ }
+
+ @Override
+ public void lockFluid(boolean lock) {
+ if (lock) {
+ if (!isFluidLocked()) {
+ this.mMode = 9;
+ markDirty();
+ }
+ } else {
+ this.mMode = 0;
+ setLockedFluidName(null);
+ markDirty();
+ }
+ }
+
+ @Override
+ public boolean isFluidLocked() {
+ return mMode == 8 || mMode == 9;
+ }
+
+ @Override
+ public boolean acceptsFluidLock(String name) {
+ return true;
+ }
+
+ @Override
+ public boolean isEmptyAndAcceptsAnyFluid() {
+ return mMode == 0 && getFluidAmount() == 0;
+ }
+
+ @Override
+ public boolean canStoreFluid(@Nonnull FluidStack fluidStack) {
+ if (mFluid != null && !GT_Utility.areFluidsEqual(mFluid, fluidStack)) {
+ return false;
+ }
+ if (isFluidLocked()) {
+ if (lockedFluidName == null) {
+ return true;
+ }
+ return lockedFluidName.equals(
+ fluidStack.getFluid()
+ .getName());
+ }
+ if (GT_ModHandler.isSteam(fluidStack)) {
+ return outputsSteam();
+ }
+ return outputsLiquids();
+ }
+
+ @Override
+ public int getTankPressure() {
+ return +100;
+ }
+
+ @Override
+ protected void onEmptyingContainerWhenEmpty() {
+ if (this.lockedFluidName == null && this.mFluid != null && isFluidLocked()) {
+ this.setLockedFluidName(
+ this.mFluid.getFluid()
+ .getName());
+ final EntityPlayer player;
+ if (playerThatLockedfluid == null || (player = playerThatLockedfluid.get()) == null) return;
+ GT_Utility.sendChatToPlayer(
+ player,
+ String.format(GT_Utility.trans("151.4", "Successfully locked Fluid to %s"), mFluid.getLocalizedName()));
+ playerThatLockedfluid = null;
+ }
+ }
+
+ @Override
+ public boolean isGivingInformation() {
+ return true;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ return new String[] { EnumChatFormatting.BLUE + "Output Hatch" + EnumChatFormatting.RESET, "Stored Fluid:",
+ EnumChatFormatting.GOLD + (mFluid == null ? "No Fluid" : mFluid.getLocalizedName())
+ + EnumChatFormatting.RESET,
+ EnumChatFormatting.GREEN + GT_Utility.formatNumbers(mFluid == null ? 0 : mFluid.amount)
+ + " L"
+ + EnumChatFormatting.RESET
+ + " "
+ + EnumChatFormatting.YELLOW
+ + GT_Utility.formatNumbers(getCapacity())
+ + " L"
+ + EnumChatFormatting.RESET,
+ (!isFluidLocked() || lockedFluidName == null) ? "Not Locked"
+ : ("Locked to " + StatCollector.translateToLocal(
+ FluidRegistry.getFluidStack(lockedFluidName, 1)
+ .getUnlocalizedName())) };
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ super.addUIWidgets(builder, buildContext);
+ builder.widget(
+ new DrawableWidget().setDrawable(GT_UITextures.PICTURE_SCREEN_BLACK)
+ .setPos(98, 16)
+ .setSize(71, 45))
+ .widget(new FluidLockWidget(this).setPos(149, 41))
+ .widget(
+ new TextWidget("Locked Fluid").setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setPos(101, 20))
+ .widget(TextWidget.dynamicString(() -> {
+ FluidStack fluidStack = FluidRegistry.getFluidStack(lockedFluidName, 1);
+ return fluidStack != null ? fluidStack.getLocalizedName() : "None";
+ })
+ .setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setTextAlignment(Alignment.CenterLeft)
+ .setMaxWidth(65)
+ .setPos(101, 30))
+ .widget(new FakeSyncWidget.ByteSyncer(() -> mMode, val -> mMode = val));
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_OutputBus.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_OutputBus.java
new file mode 100644
index 0000000000..5fdfa7899b
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_OutputBus.java
@@ -0,0 +1,307 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.Textures.BlockIcons.ITEM_OUT_SIGN;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPE_OUT;
+import static gregtech.api.util.GT_Utility.moveMultipleItemStacks;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.ChatComponentTranslation;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import org.jetbrains.annotations.Nullable;
+
+import com.gtnewhorizons.modularui.api.forge.ItemHandlerHelper;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+
+import gregtech.GT_Mod;
+import gregtech.api.enums.ItemList;
+import gregtech.api.gui.modularui.GT_UIInfos;
+import gregtech.api.gui.widgets.GT_PhantomItemButton;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IItemLockable;
+import gregtech.api.interfaces.modularui.IAddUIWidgets;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.render.TextureFactory;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.extensions.ArrayExt;
+
+public class GT_MetaTileEntity_Hatch_OutputBus extends GT_MetaTileEntity_Hatch implements IAddUIWidgets, IItemLockable {
+
+ private static final String DATA_STICK_DATA_TYPE = "outputBusFilter";
+ private static final String LOCKED_ITEM_NBT_KEY = "lockedItem";
+
+ protected ItemStack lockedItem = null;
+
+ public GT_MetaTileEntity_Hatch_OutputBus(int aID, String aName, String aNameRegional, int aTier) {
+ this(aID, aName, aNameRegional, aTier, getSlots(aTier));
+ }
+
+ public GT_MetaTileEntity_Hatch_OutputBus(int id, String name, String nameRegional, int tier, int slots) {
+ super(
+ id,
+ name,
+ nameRegional,
+ tier,
+ slots,
+ ArrayExt.of(
+ "Item Output for Multiblocks",
+ "Capacity: " + getSlots(tier) + " stack" + (getSlots(tier) >= 2 ? "s" : ""),
+ "Left click with data stick to save filter config",
+ "Right click with data stick to load filter config"));
+ }
+
+ public GT_MetaTileEntity_Hatch_OutputBus(int aID, String aName, String aNameRegional, int aTier,
+ String[] aDescription) {
+ super(aID, aName, aNameRegional, aTier, getSlots(aTier), aDescription);
+ }
+
+ public GT_MetaTileEntity_Hatch_OutputBus(int aID, String aName, String aNameRegional, int aTier,
+ String[] aDescription, int inventorySize) {
+ super(aID, aName, aNameRegional, aTier, inventorySize, aDescription);
+ }
+
+ public GT_MetaTileEntity_Hatch_OutputBus(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, getSlots(aTier), aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Hatch_OutputBus(String name, int tier, int slots, String[] description,
+ ITexture[][][] textures) {
+ super(name, tier, slots, description, textures);
+ }
+
+ @Override
+ public ITexture[] getTexturesActive(ITexture aBaseTexture) {
+ return GT_Mod.gregtechproxy.mRenderIndicatorsOnHatch
+ ? new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_OUT), TextureFactory.of(ITEM_OUT_SIGN) }
+ : new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_OUT) };
+ }
+
+ @Override
+ public ITexture[] getTexturesInactive(ITexture aBaseTexture) {
+ return GT_Mod.gregtechproxy.mRenderIndicatorsOnHatch
+ ? new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_OUT), TextureFactory.of(ITEM_OUT_SIGN) }
+ : new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_OUT) };
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return true;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return true;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return true;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Hatch_OutputBus(mName, mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ if (!acceptsItemLock() || !(aPlayer instanceof EntityPlayerMP)) {
+ GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ return super.onRightclick(aBaseMetaTileEntity, aPlayer);
+ }
+
+ final ItemStack dataStick = aPlayer.inventory.getCurrentItem();
+ if (!ItemList.Tool_DataStick.isStackEqual(dataStick, false, true)) {
+ GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ return super.onRightclick(aBaseMetaTileEntity, aPlayer);
+ }
+
+ if (!dataStick.hasTagCompound() || !DATA_STICK_DATA_TYPE.equals(dataStick.stackTagCompound.getString("type"))) {
+ aPlayer.addChatMessage(new ChatComponentTranslation("GT5U.machines.output_bus.invalid"));
+ return false;
+ }
+
+ final NBTTagCompound nbt = dataStick.stackTagCompound;
+ if (nbt.hasKey(LOCKED_ITEM_NBT_KEY)) {
+ lockedItem = ItemStack.loadItemStackFromNBT(nbt.getCompoundTag(LOCKED_ITEM_NBT_KEY));
+ } else {
+ lockedItem = null;
+ }
+ aPlayer.addChatMessage(new ChatComponentTranslation("GT5U.machines.output_bus.loaded"));
+ return true;
+
+ }
+
+ @Override
+ public void onLeftclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ if (!acceptsItemLock() || !(aPlayer instanceof EntityPlayerMP)) {
+ return;
+ }
+ final ItemStack dataStick = aPlayer.inventory.getCurrentItem();
+ if (!ItemList.Tool_DataStick.isStackEqual(dataStick, false, true)) {
+ return;
+ }
+
+ final NBTTagCompound nbt = new NBTTagCompound();
+ nbt.setString("type", DATA_STICK_DATA_TYPE);
+ if (lockedItem != null) {
+ nbt.setTag(LOCKED_ITEM_NBT_KEY, lockedItem.writeToNBT(new NBTTagCompound()));
+ }
+
+ dataStick.stackTagCompound = nbt;
+ dataStick.setStackDisplayName("Output Bus Configuration");
+ aPlayer.addChatMessage(new ChatComponentTranslation("GT5U.machines.output_bus.saved"));
+ }
+
+ /**
+ * Attempt to store as many items as possible into the internal inventory of this output bus. If you need atomicity
+ * you should use {@link gregtech.api.interfaces.tileentity.IHasInventory#addStackToSlot(int, ItemStack)}
+ *
+ * @param aStack Assume valid. Will be mutated. Take over the ownership. Caller should not retain a reference to
+ * this stack if the call returns true.
+ * @return true if stack is fully accepted. false is stack is partially accepted or nothing is accepted
+ */
+ public boolean storeAll(ItemStack aStack) {
+ markDirty();
+
+ if (lockedItem != null && !lockedItem.isItemEqual(aStack)) {
+ return false;
+ }
+
+ for (int i = 0, mInventoryLength = mInventory.length; i < mInventoryLength && aStack.stackSize > 0; i++) {
+ ItemStack tSlot = mInventory[i];
+ if (GT_Utility.isStackInvalid(tSlot)) {
+ int tRealStackLimit = Math.min(getInventoryStackLimit(), aStack.getMaxStackSize());
+ if (aStack.stackSize <= tRealStackLimit) {
+ mInventory[i] = aStack;
+ return true;
+ }
+ mInventory[i] = aStack.splitStack(tRealStackLimit);
+ } else {
+ int tRealStackLimit = Math.min(getInventoryStackLimit(), tSlot.getMaxStackSize());
+ if (tSlot.stackSize < tRealStackLimit && tSlot.isItemEqual(aStack)
+ && ItemStack.areItemStackTagsEqual(tSlot, aStack)) {
+ if (aStack.stackSize + tSlot.stackSize <= tRealStackLimit) {
+ mInventory[i].stackSize += aStack.stackSize;
+ return true;
+ } else {
+ // more to serve
+ aStack.stackSize -= tRealStackLimit - tSlot.stackSize;
+ mInventory[i].stackSize = tRealStackLimit;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return side == aBaseMetaTileEntity.getFrontFacing();
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onPostTick(aBaseMetaTileEntity, aTick);
+ if (aBaseMetaTileEntity.isServerSide() && aBaseMetaTileEntity.isAllowedToWork() && (aTick & 0x7) == 0) {
+ final IInventory tTileEntity = aBaseMetaTileEntity
+ .getIInventoryAtSide(aBaseMetaTileEntity.getFrontFacing());
+ if (tTileEntity != null) {
+ moveMultipleItemStacks(
+ aBaseMetaTileEntity,
+ tTileEntity,
+ aBaseMetaTileEntity.getFrontFacing(),
+ aBaseMetaTileEntity.getBackFacing(),
+ null,
+ false,
+ (byte) 64,
+ (byte) 1,
+ (byte) 64,
+ (byte) 1,
+ mInventory.length);
+ for (int i = 0; i < mInventory.length; i++)
+ if (mInventory[i] != null && mInventory[i].stackSize <= 0) mInventory[i] = null;
+ }
+ }
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ if (lockedItem != null) {
+ aNBT.setTag(LOCKED_ITEM_NBT_KEY, lockedItem.writeToNBT(new NBTTagCompound()));
+ }
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ if (aNBT.hasKey(LOCKED_ITEM_NBT_KEY)) {
+ lockedItem = ItemStack.loadItemStackFromNBT(aNBT.getCompoundTag(LOCKED_ITEM_NBT_KEY));
+ }
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ switch (mTier) {
+ case 0 -> getBaseMetaTileEntity().add1by1Slot(builder);
+ case 1 -> getBaseMetaTileEntity().add2by2Slots(builder);
+ case 2 -> getBaseMetaTileEntity().add3by3Slots(builder);
+ default -> getBaseMetaTileEntity().add4by4Slots(builder);
+ }
+
+ if (acceptsItemLock()) {
+ builder.widget(
+ new GT_PhantomItemButton(this).setPos(getGUIWidth() - 25, 40)
+ .setBackground(GT_PhantomItemButton.FILTER_BACKGROUND));
+ }
+ }
+
+ @Override
+ public void setLockedItem(@Nullable ItemStack itemStack) {
+ if (itemStack == null) {
+ clearLock();
+ } else {
+ lockedItem = ItemHandlerHelper.copyStackWithSize(itemStack, 1);
+ }
+ }
+
+ @Nullable
+ @Override
+ public ItemStack getLockedItem() {
+ return lockedItem;
+ }
+
+ @Override
+ public void clearLock() {
+ lockedItem = null;
+ }
+
+ @Override
+ public boolean isLocked() {
+ return lockedItem != null;
+ }
+
+ @Override
+ public boolean acceptsItemLock() {
+ return true;
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_QuadrupleHumongous.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_QuadrupleHumongous.java
new file mode 100644
index 0000000000..e0ab7acf95
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_QuadrupleHumongous.java
@@ -0,0 +1,27 @@
+package gregtech.api.metatileentity.implementations;
+
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.MetaTileEntity;
+
+public class GT_MetaTileEntity_Hatch_QuadrupleHumongous extends GT_MetaTileEntity_Hatch_MultiInput {
+
+ public GT_MetaTileEntity_Hatch_QuadrupleHumongous(int aID, int aSlot, String aName, String aNameRegional) {
+ super(aID, aSlot, aName, aNameRegional, 13);
+ }
+
+ public GT_MetaTileEntity_Hatch_QuadrupleHumongous(String aName, int aSlot, int aTier, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aSlot, aTier, aDescription, aTextures);
+ }
+
+ @Override
+ public int getCapacityPerTank(int aTier, int aSlot) {
+ return 500_000_000;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Hatch_QuadrupleHumongous(mName, getMaxType(), mTier, mDescriptionArray, mTextures);
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_MagHatch.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_MagHatch.java
new file mode 100644
index 0000000000..8a462d12c6
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_MagHatch.java
@@ -0,0 +1,103 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.GT_Values.AuthorFourIsTheNumber;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_EMS_HOUSING;
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_EMS_HOUSING_GLOW;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import org.apache.commons.lang3.ArrayUtils;
+
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+
+import gregtech.api.gui.modularui.GT_UIInfos;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.render.TextureFactory;
+import gregtech.common.tileentities.machines.multi.GT_MetaTileEntity_IndustrialElectromagneticSeparator;
+
+public class GT_MetaTileEntity_MagHatch extends GT_MetaTileEntity_Hatch {
+
+ public GT_MetaTileEntity_MagHatch(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional, 5, 1, "Holds electromagnet for the Magnetic Flux Exhibitor");
+ }
+
+ public GT_MetaTileEntity_MagHatch(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 1, aDescription[0], aTextures);
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return true;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return true;
+ }
+
+ @Override
+ public String[] getDescription() {
+ return ArrayUtils.addAll(this.mDescriptionArray, AuthorFourIsTheNumber);
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ builder.widget(
+ new SlotWidget(inventoryHandler, 0)
+ .setFilter(GT_MetaTileEntity_IndustrialElectromagneticSeparator::isValidElectromagnet)
+ .setAccess(true, true)
+ .setPos(79, 34));
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ return true;
+ }
+
+ @Override
+ public ITexture[] getTexturesActive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.builder()
+ .addIcon(OVERLAY_EMS_HOUSING)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_EMS_HOUSING_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public ITexture[] getTexturesInactive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, TextureFactory.builder()
+ .addIcon(OVERLAY_EMS_HOUSING)
+ .extFacing()
+ .build(),
+ TextureFactory.builder()
+ .addIcon(OVERLAY_EMS_HOUSING_GLOW)
+ .extFacing()
+ .glow()
+ .build() };
+ }
+
+ @Override
+ public int getInventoryStackLimit() {
+ return 1;
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_MagHatch(mName, mTier, mDescriptionArray, mTextures);
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_MultiBlockBase.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_MultiBlockBase.java
new file mode 100644
index 0000000000..4ffe82b1c2
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_MultiBlockBase.java
@@ -0,0 +1,2696 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.GT_Values.V;
+import static gregtech.api.enums.GT_Values.VN;
+import static gregtech.api.util.GT_Utility.filterValidMTEs;
+import static gregtech.api.util.GT_Utility.formatNumbers;
+import static mcp.mobius.waila.api.SpecialChars.GREEN;
+import static mcp.mobius.waila.api.SpecialChars.RED;
+import static mcp.mobius.waila.api.SpecialChars.RESET;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.IntConsumer;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.Constants;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.Fluid;
+import net.minecraftforge.fluids.FluidStack;
+
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.TestOnly;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.gtnewhorizons.modularui.api.NumberFormatMUI;
+import com.gtnewhorizons.modularui.api.drawable.UITexture;
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.api.math.Pos2d;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.internal.network.NetworkUtils;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+import com.gtnewhorizons.modularui.common.widget.DynamicPositionedColumn;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import gregtech.GT_Mod;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.ConfigCategories;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.enums.VoidingMode;
+import gregtech.api.gui.modularui.GT_UIInfos;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.fluid.IFluidStore;
+import gregtech.api.interfaces.metatileentity.IItemLockable;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.modularui.ControllerWithOptionalFeatures;
+import gregtech.api.interfaces.modularui.IAddGregtechLogo;
+import gregtech.api.interfaces.modularui.IAddUIWidgets;
+import gregtech.api.interfaces.modularui.IBindPlayerInventoryUI;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.items.GT_MetaGenerated_Tool;
+import gregtech.api.logic.ProcessingLogic;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.objects.GT_ItemStack;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.recipe.check.SingleRecipeCheck;
+import gregtech.api.util.GT_ClientPreference;
+import gregtech.api.util.GT_ExoticEnergyInputHelper;
+import gregtech.api.util.GT_Log;
+import gregtech.api.util.GT_OverclockCalculator;
+import gregtech.api.util.GT_ParallelHelper;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.GT_Waila;
+import gregtech.api.util.OutputHatchWrapper;
+import gregtech.api.util.VoidProtectionHelper;
+import gregtech.api.util.shutdown.ShutDownReason;
+import gregtech.api.util.shutdown.ShutDownReasonRegistry;
+import gregtech.client.GT_SoundLoop;
+import gregtech.common.GT_Pollution;
+import gregtech.common.gui.modularui.widget.CheckRecipeResultSyncer;
+import gregtech.common.gui.modularui.widget.ShutDownReasonSyncer;
+import gregtech.common.items.GT_MetaGenerated_Tool_01;
+import gregtech.common.tileentities.machines.GT_MetaTileEntity_Hatch_CraftingInput_ME;
+import gregtech.common.tileentities.machines.GT_MetaTileEntity_Hatch_InputBus_ME;
+import gregtech.common.tileentities.machines.GT_MetaTileEntity_Hatch_Input_ME;
+import gregtech.common.tileentities.machines.GT_MetaTileEntity_Hatch_OutputBus_ME;
+import gregtech.common.tileentities.machines.GT_MetaTileEntity_Hatch_Output_ME;
+import gregtech.common.tileentities.machines.IDualInputHatch;
+import gregtech.common.tileentities.machines.IDualInputInventory;
+import gregtech.common.tileentities.machines.IRecipeProcessingAwareHatch;
+import gregtech.common.tileentities.machines.ISmartInputHatch;
+import gregtech.common.tileentities.machines.multi.GT_MetaTileEntity_LargeTurbine;
+import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+public abstract class GT_MetaTileEntity_MultiBlockBase extends MetaTileEntity
+ implements ControllerWithOptionalFeatures, IAddGregtechLogo, IAddUIWidgets, IBindPlayerInventoryUI {
+
+ public static boolean disableMaintenance;
+ public boolean hasMaintenanceChecks = getDefaultHasMaintenanceChecks();
+ public boolean mMachine = false, mWrench = false, mScrewdriver = false, mSoftHammer = false, mHardHammer = false,
+ mSolderingTool = false, mCrowbar = false, mRunningOnLoad = false;
+ public boolean mStructureChanged = false;
+ public int mPollution = 0, mProgresstime = 0, mMaxProgresstime = 0, mEUt = 0, mEfficiencyIncrease = 0,
+ mStartUpCheck = 100, mRuntime = 0, mEfficiency = 0;
+ public volatile boolean mUpdated = false;
+ public int mUpdate = 0;
+ public ItemStack[] mOutputItems = null;
+ public FluidStack[] mOutputFluids = null;
+ public String mNEI;
+ public int damageFactorLow = 5;
+ public float damageFactorHigh = 0.6f;
+ public int machineMode = 0;
+ public List<UITexture> machineModeIcons = new ArrayList<UITexture>();
+
+ public boolean mLockedToSingleRecipe = getDefaultRecipeLockingMode();
+ protected boolean inputSeparation = getDefaultInputSeparationMode();
+ protected VoidingMode voidingMode = getDefaultVoidingMode();
+ protected boolean batchMode = getDefaultBatchMode();
+ private @Nonnull CheckRecipeResult checkRecipeResult = CheckRecipeResultRegistry.NONE;
+
+ protected static final String INPUT_SEPARATION_NBT_KEY = "inputSeparation";
+ protected static final String VOID_EXCESS_NBT_KEY = "voidExcess";
+ protected static final String VOIDING_MODE_NBT_KEY = "voidingMode";
+ protected static final String BATCH_MODE_NBT_KEY = "batchMode";
+ protected SingleRecipeCheck mSingleRecipeCheck = null;
+
+ public ArrayList<GT_MetaTileEntity_Hatch_Input> mInputHatches = new ArrayList<>();
+ public ArrayList<GT_MetaTileEntity_Hatch_Output> mOutputHatches = new ArrayList<>();
+ public ArrayList<GT_MetaTileEntity_Hatch_InputBus> mInputBusses = new ArrayList<>();
+ public ArrayList<GT_MetaTileEntity_Hatch_OutputBus> mOutputBusses = new ArrayList<>();
+ public ArrayList<IDualInputHatch> mDualInputHatches = new ArrayList<>();
+ public ArrayList<ISmartInputHatch> mSmartInputHatches = new ArrayList<>();
+ public ArrayList<GT_MetaTileEntity_Hatch_Dynamo> mDynamoHatches = new ArrayList<>();
+ public ArrayList<GT_MetaTileEntity_Hatch_Muffler> mMufflerHatches = new ArrayList<>();
+ public ArrayList<GT_MetaTileEntity_Hatch_Energy> mEnergyHatches = new ArrayList<>();
+ public ArrayList<GT_MetaTileEntity_Hatch_Maintenance> mMaintenanceHatches = new ArrayList<>();
+ protected List<GT_MetaTileEntity_Hatch> mExoticEnergyHatches = new ArrayList<>();
+ protected final ProcessingLogic processingLogic;
+ @SideOnly(Side.CLIENT)
+ protected GT_SoundLoop activitySoundLoop;
+
+ private long mLastWorkingTick = 0;
+ private static final int CHECK_INTERVAL = 100; // How often should we check for a new recipe on an idle machine?
+ private final int randomTickOffset = (int) (Math.random() * CHECK_INTERVAL + 1);
+
+ protected static final byte INTERRUPT_SOUND_INDEX = 8;
+ protected static final byte PROCESS_START_SOUND_INDEX = 1;
+
+ public GT_MetaTileEntity_MultiBlockBase(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional, 2);
+ this.processingLogic = null;
+ GT_MetaTileEntity_MultiBlockBase.disableMaintenance = GregTech_API.sMachineFile
+ .get(ConfigCategories.machineconfig, "MultiBlockMachines.disableMaintenance", false);
+ this.damageFactorLow = GregTech_API.sMachineFile
+ .get(ConfigCategories.machineconfig, "MultiBlockMachines.damageFactorLow", 5);
+ this.damageFactorHigh = (float) GregTech_API.sMachineFile
+ .get(ConfigCategories.machineconfig, "MultiBlockMachines.damageFactorHigh", 0.6f);
+ this.mNEI = "";
+ if (!shouldCheckMaintenance()) fixAllIssues();
+ }
+
+ public GT_MetaTileEntity_MultiBlockBase(String aName) {
+ super(aName, 2);
+ this.processingLogic = createProcessingLogic();
+ GT_MetaTileEntity_MultiBlockBase.disableMaintenance = GregTech_API.sMachineFile
+ .get(ConfigCategories.machineconfig, "MultiBlockMachines.disableMaintenance", false);
+ this.damageFactorLow = GregTech_API.sMachineFile
+ .get(ConfigCategories.machineconfig, "MultiBlockMachines.damageFactorLow", 5);
+ this.damageFactorHigh = (float) GregTech_API.sMachineFile
+ .get(ConfigCategories.machineconfig, "MultiBlockMachines.damageFactorHigh", 0.6f);
+ if (!shouldCheckMaintenance()) fixAllIssues();
+ }
+
+ @Override
+ public boolean allowCoverOnSide(ForgeDirection side, GT_ItemStack aCoverID) {
+ return side != getBaseMetaTileEntity().getFrontFacing();
+ }
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ if (supportsSingleRecipeLocking()) {
+ mLockedToSingleRecipe = !mLockedToSingleRecipe;
+ if (mLockedToSingleRecipe) {
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ GT_Utility.trans("223", "Single recipe locking enabled. Will lock to next recipe."));
+ } else {
+ GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("220", "Single recipe locking disabled."));
+ mSingleRecipeCheck = null;
+ }
+ }
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return false;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return true;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return aIndex > 0;
+ }
+
+ @Override
+ public int getProgresstime() {
+ return mProgresstime;
+ }
+
+ @Override
+ public int maxProgresstime() {
+ return mMaxProgresstime;
+ }
+
+ @Override
+ public int increaseProgress(int aProgress) {
+ return aProgress;
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ aNBT.setInteger("mEUt", mEUt);
+ aNBT.setInteger("mProgresstime", mProgresstime);
+ aNBT.setInteger("mMaxProgresstime", mMaxProgresstime);
+ aNBT.setInteger("mEfficiencyIncrease", mEfficiencyIncrease);
+ aNBT.setInteger("mEfficiency", mEfficiency);
+ aNBT.setInteger("mPollution", mPollution);
+ aNBT.setInteger("mRuntime", mRuntime);
+
+ if (supportsMachineModeSwitch()) {
+ aNBT.setInteger("machineMode", machineMode);
+ }
+
+ if (supportsSingleRecipeLocking()) {
+ aNBT.setBoolean("mLockedToSingleRecipe", mLockedToSingleRecipe);
+ if (mLockedToSingleRecipe && mSingleRecipeCheck != null)
+ aNBT.setTag("mSingleRecipeCheck", mSingleRecipeCheck.writeToNBT());
+ }
+
+ if (mOutputItems != null) {
+ aNBT.setInteger("mOutputItemsLength", mOutputItems.length);
+ for (int i = 0; i < mOutputItems.length; i++) if (mOutputItems[i] != null) {
+ GT_Utility.saveItem(aNBT, "mOutputItem" + i, mOutputItems[i]);
+ }
+ }
+ if (mOutputFluids != null) {
+ aNBT.setInteger("mOutputFluidsLength", mOutputFluids.length);
+ for (int i = 0; i < mOutputFluids.length; i++) if (mOutputFluids[i] != null) {
+ NBTTagCompound tNBT = new NBTTagCompound();
+ mOutputFluids[i].writeToNBT(tNBT);
+ aNBT.setTag("mOutputFluids" + i, tNBT);
+ }
+ }
+ aNBT.setBoolean("mWrench", mWrench);
+ aNBT.setBoolean("mScrewdriver", mScrewdriver);
+ aNBT.setBoolean("mSoftHammer", mSoftHammer);
+ aNBT.setBoolean("mHardHammer", mHardHammer);
+ aNBT.setBoolean("mSolderingTool", mSolderingTool);
+ aNBT.setBoolean("mCrowbar", mCrowbar);
+ aNBT.setBoolean(BATCH_MODE_NBT_KEY, batchMode);
+ aNBT.setBoolean(INPUT_SEPARATION_NBT_KEY, inputSeparation);
+ aNBT.setString(VOIDING_MODE_NBT_KEY, voidingMode.name);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ mEUt = aNBT.getInteger("mEUt");
+ mProgresstime = aNBT.getInteger("mProgresstime");
+ mMaxProgresstime = aNBT.getInteger("mMaxProgresstime");
+ if (mMaxProgresstime > 0) mRunningOnLoad = true;
+ mEfficiencyIncrease = aNBT.getInteger("mEfficiencyIncrease");
+ mEfficiency = aNBT.getInteger("mEfficiency");
+ mPollution = aNBT.getInteger("mPollution");
+ mRuntime = aNBT.getInteger("mRuntime");
+ if (aNBT.hasKey("machineMode")) {
+ machineMode = aNBT.getInteger("machineMode");
+ }
+ if (supportsSingleRecipeLocking()) {
+ mLockedToSingleRecipe = aNBT.getBoolean("mLockedToSingleRecipe");
+ if (mLockedToSingleRecipe && aNBT.hasKey("mSingleRecipeCheck", Constants.NBT.TAG_COMPOUND)) {
+ SingleRecipeCheck c = loadSingleRecipeChecker(aNBT.getCompoundTag("mSingleRecipeCheck"));
+ if (c != null) mSingleRecipeCheck = c;
+ // the old recipe is gone. we disable the machine to prevent making garbage in case of shared inputs
+ // maybe use a better way to inform player in the future.
+ else getBaseMetaTileEntity().disableWorking();
+ }
+ }
+ batchMode = aNBT.getBoolean(BATCH_MODE_NBT_KEY);
+ inputSeparation = aNBT.getBoolean(INPUT_SEPARATION_NBT_KEY);
+ if (aNBT.hasKey(VOIDING_MODE_NBT_KEY, Constants.NBT.TAG_STRING)) {
+ voidingMode = VoidingMode.fromName(aNBT.getString(VOIDING_MODE_NBT_KEY));
+ } else if (aNBT.hasKey(VOID_EXCESS_NBT_KEY)) {
+ // backward compatibility
+ voidingMode = aNBT.getBoolean(VOID_EXCESS_NBT_KEY) ? VoidingMode.VOID_ALL : VoidingMode.VOID_NONE;
+ }
+ if (!getAllowedVoidingModes().contains(voidingMode)) voidingMode = getDefaultVoidingMode();
+
+ int aOutputItemsLength = aNBT.getInteger("mOutputItemsLength");
+ if (aOutputItemsLength > 0) {
+ mOutputItems = new ItemStack[aOutputItemsLength];
+ for (int i = 0; i < mOutputItems.length; i++)
+ mOutputItems[i] = GT_Utility.loadItem(aNBT, "mOutputItem" + i);
+ }
+
+ int aOutputFluidsLength = aNBT.getInteger("mOutputFluidsLength");
+ if (aOutputFluidsLength > 0) {
+ mOutputFluids = new FluidStack[aOutputFluidsLength];
+ for (int i = 0; i < mOutputFluids.length; i++)
+ mOutputFluids[i] = GT_Utility.loadFluid(aNBT, "mOutputFluids" + i);
+ }
+ if (shouldCheckMaintenance()) {
+ mWrench = aNBT.getBoolean("mWrench");
+ mScrewdriver = aNBT.getBoolean("mScrewdriver");
+ mSoftHammer = aNBT.getBoolean("mSoftHammer");
+ mHardHammer = aNBT.getBoolean("mHardHammer");
+ mSolderingTool = aNBT.getBoolean("mSolderingTool");
+ mCrowbar = aNBT.getBoolean("mCrowbar");
+ } else fixAllIssues();
+ }
+
+ protected SingleRecipeCheck loadSingleRecipeChecker(NBTTagCompound aNBT) {
+ return SingleRecipeCheck.tryLoad(getRecipeMap(), aNBT);
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ return true;
+ }
+
+ @Override
+ public byte getTileEntityBaseType() {
+ return 2;
+ }
+
+ /**
+ * Set the structure as having changed, and trigger an update.
+ */
+ public void onStructureChange() {
+ mStructureChanged = true;
+ }
+
+ @Override
+ public void onMachineBlockUpdate() {
+ mUpdated = true;
+ }
+
+ /**
+ * ClearHatches as a part of structure check. If your multiblock has any hatches that need clearing override this
+ * method, call super, and clear your own hatches
+ */
+ public void clearHatches() {
+ mInputHatches.clear();
+ mInputBusses.clear();
+ mOutputHatches.clear();
+ mOutputBusses.clear();
+ mDynamoHatches.clear();
+ mEnergyHatches.clear();
+ setMufflers(false);
+ mMufflerHatches.clear();
+ mMaintenanceHatches.clear();
+ mDualInputHatches.clear();
+ mSmartInputHatches.clear();
+ }
+
+ public boolean checkStructure(boolean aForceReset) {
+ return checkStructure(aForceReset, getBaseMetaTileEntity());
+ }
+
+ public boolean checkStructure(boolean aForceReset, IGregTechTileEntity aBaseMetaTileEntity) {
+ if (!aBaseMetaTileEntity.isServerSide()) return mMachine;
+ // Only trigger an update if forced (from onPostTick, generally), or if the structure has changed
+ if ((mStructureChanged || aForceReset)) {
+ clearHatches();
+ mMachine = checkMachine(aBaseMetaTileEntity, mInventory[1]);
+ }
+ mStructureChanged = false;
+ return mMachine;
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (aBaseMetaTileEntity.isServerSide()) {
+ if (mEfficiency < 0) mEfficiency = 0;
+ if (mUpdated) {
+ // duct tape fix for too many updates on an overloaded server, causing the structure check to not run
+ if (mUpdate <= 0) mUpdate = 50;
+ mUpdated = false;
+ }
+ if (--mUpdate == 0 || --mStartUpCheck == 0) {
+ checkStructure(true, aBaseMetaTileEntity);
+ }
+
+ if (mStartUpCheck < 0) {
+ if (mMachine) {
+ checkMaintenance();
+ if (getRepairStatus() > 0) {
+ runMachine(aBaseMetaTileEntity, aTick);
+ } else if (aBaseMetaTileEntity.isAllowedToWork()) {
+ stopMachine(ShutDownReasonRegistry.NO_REPAIR);
+ }
+ } else if (aBaseMetaTileEntity.isAllowedToWork()) {
+ stopMachine(ShutDownReasonRegistry.STRUCTURE_INCOMPLETE);
+ }
+ }
+ aBaseMetaTileEntity.setErrorDisplayID(
+ (aBaseMetaTileEntity.getErrorDisplayID() & ~127) | (mWrench ? 0 : 1)
+ | (mScrewdriver ? 0 : 2)
+ | (mSoftHammer ? 0 : 4)
+ | (mHardHammer ? 0 : 8)
+ | (mSolderingTool ? 0 : 16)
+ | (mCrowbar ? 0 : 32)
+ | (mMachine ? 0 : 64));
+ aBaseMetaTileEntity.setActive(mMaxProgresstime > 0);
+ boolean active = aBaseMetaTileEntity.isActive() && mPollution > 0;
+ setMufflers(active);
+ } else {
+ if (!aBaseMetaTileEntity.hasMufflerUpgrade()) {
+ doActivitySound(getActivitySoundLoop());
+ }
+ }
+ }
+
+ @Override
+ public void onTickFail(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onTickFail(aBaseMetaTileEntity, aTick);
+ if (aBaseMetaTileEntity.isServerSide()) {
+ aBaseMetaTileEntity.disableWorking();
+ checkRecipeResult = CheckRecipeResultRegistry.CRASH;
+ }
+ }
+
+ public void checkMaintenance() {
+ if (!shouldCheckMaintenance()) return;
+
+ boolean broken = !(mWrench && mScrewdriver && mSoftHammer && mHardHammer && mSolderingTool && mCrowbar);
+ if (broken) {
+ for (GT_MetaTileEntity_Hatch_Maintenance tHatch : filterValidMTEs(mMaintenanceHatches)) {
+ if (tHatch.mAuto) tHatch.autoMaintainance();
+ if (tHatch.mWrench) mWrench = true;
+ if (tHatch.mScrewdriver) mScrewdriver = true;
+ if (tHatch.mSoftHammer) mSoftHammer = true;
+ if (tHatch.mHardHammer) mHardHammer = true;
+ if (tHatch.mSolderingTool) mSolderingTool = true;
+ if (tHatch.mCrowbar) mCrowbar = true;
+
+ tHatch.mWrench = false;
+ tHatch.mScrewdriver = false;
+ tHatch.mSoftHammer = false;
+ tHatch.mHardHammer = false;
+ tHatch.mSolderingTool = false;
+ tHatch.mCrowbar = false;
+ }
+ }
+ }
+
+ /**
+ * Starts checking recipe with some operations needed to actually run the check. Overriding this without due care
+ * may result in dupe of items, hence it's marked as final.
+ * <p>
+ * See {@link #createProcessingLogic()} or {@link #checkProcessing()} for what you want to override.
+ *
+ * @return If successfully found recipe and/or started processing
+ */
+ protected final boolean checkRecipe() {
+ startRecipeProcessing();
+ CheckRecipeResult result = checkProcessing();
+ if (!CheckRecipeResultRegistry.isRegistered(result.getID())) {
+ throw new RuntimeException(String.format("Result %s is not registered for registry", result.getID()));
+ }
+ if (result.wasSuccessful()) {
+ sendStartMultiBlockSoundLoop();
+ }
+ this.checkRecipeResult = result;
+ endRecipeProcessing();
+ // Don't use `result` here because `endRecipeProcessing()` might mutate `this.checkRecipeResult`
+ return this.checkRecipeResult.wasSuccessful();
+ }
+
+ private boolean shouldCheckRecipeThisTick(long aTick) {
+ // do a recipe check if any crafting input hatch just got pushed in items
+ boolean shouldCheck = false;
+ // check all of them (i.e. do not return early) to reset the state of all of them.
+ for (IDualInputHatch craftingInputMe : mDualInputHatches) {
+ shouldCheck |= craftingInputMe.justUpdated();
+ }
+ if (shouldCheck) return true;
+ // Do the same for Smart Input Hatches
+ for (ISmartInputHatch smartInputHatch : mSmartInputHatches) {
+ shouldCheck |= smartInputHatch.justUpdated();
+ }
+ if (shouldCheck) return true;
+
+ // Perform more frequent recipe change after the machine just shuts down.
+ long timeElapsed = aTick - mLastWorkingTick;
+
+ if (timeElapsed >= CHECK_INTERVAL) return (aTick + randomTickOffset) % CHECK_INTERVAL == 0;
+ // Batch mode should be a lot less aggressive at recipe checking
+ if (!isBatchModeEnabled()) {
+ return timeElapsed == 5 || timeElapsed == 12
+ || timeElapsed == 20
+ || timeElapsed == 30
+ || timeElapsed == 40
+ || timeElapsed == 55
+ || timeElapsed == 70
+ || timeElapsed == 85;
+ }
+ return false;
+ }
+
+ protected void runMachine(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (mMaxProgresstime > 0 && doRandomMaintenanceDamage()) {
+ if (onRunningTick(mInventory[1])) {
+ markDirty();
+ if (!polluteEnvironment(getPollutionPerTick(mInventory[1]))) {
+ stopMachine(ShutDownReasonRegistry.POLLUTION_FAIL);
+ }
+ if (mMaxProgresstime > 0 && ++mProgresstime >= mMaxProgresstime) {
+ if (mOutputItems != null) {
+ for (ItemStack tStack : mOutputItems) {
+ if (tStack != null) {
+ try {
+ GT_Mod.achievements.issueAchivementHatch(
+ aBaseMetaTileEntity.getWorld()
+ .getPlayerEntityByName(aBaseMetaTileEntity.getOwnerName()),
+ tStack);
+ } catch (Exception ignored) {}
+ addOutput(tStack);
+ }
+ }
+ mOutputItems = null;
+ }
+ if (mOutputFluids != null) {
+ addFluidOutputs(mOutputFluids);
+ if (mOutputFluids.length > 1) {
+ try {
+ GT_Mod.achievements.issueAchievement(
+ aBaseMetaTileEntity.getWorld()
+ .getPlayerEntityByName(aBaseMetaTileEntity.getOwnerName()),
+ "oilplant");
+ } catch (Exception ignored) {}
+ }
+ mOutputFluids = null;
+ }
+ mEfficiency = Math.max(
+ 0,
+ Math.min(
+ mEfficiency + mEfficiencyIncrease,
+ getMaxEfficiency(mInventory[1]) - ((getIdealStatus() - getRepairStatus()) * 1000)));
+ mOutputItems = null;
+ mProgresstime = 0;
+ mMaxProgresstime = 0;
+ mEfficiencyIncrease = 0;
+ mLastWorkingTick = aTick;
+ if (aBaseMetaTileEntity.isAllowedToWork()) {
+ checkRecipe();
+ }
+ }
+ }
+ } else {
+ // Check if the machine is enabled in the first place!
+ if (aBaseMetaTileEntity.isAllowedToWork()) {
+
+ if (shouldCheckRecipeThisTick(aTick) || aBaseMetaTileEntity.hasWorkJustBeenEnabled()
+ || aBaseMetaTileEntity.hasInventoryBeenModified()) {
+ if (checkRecipe()) {
+ markDirty();
+ }
+ }
+ if (mMaxProgresstime <= 0) mEfficiency = Math.max(0, mEfficiency - 1000);
+ }
+ }
+ }
+
+ public boolean polluteEnvironment(int aPollutionLevel) {
+ mPollution += aPollutionLevel;
+ for (GT_MetaTileEntity_Hatch_Muffler tHatch : filterValidMTEs(mMufflerHatches)) {
+ if (mPollution >= 10000) {
+ if (tHatch.polluteEnvironment(this)) {
+ mPollution -= 10000;
+ }
+ } else {
+ break;
+ }
+ }
+ return mPollution < 10000;
+ }
+
+ protected void sendStartMultiBlockSoundLoop() {
+ if (getProcessStartSound() != null) {
+ sendLoopStart(PROCESS_START_SOUND_INDEX);
+ }
+ }
+
+ @Override
+ public void doSound(byte aIndex, double aX, double aY, double aZ) {
+ super.doSound(aIndex, aX, aY, aZ);
+ switch (aIndex) {
+ case PROCESS_START_SOUND_INDEX -> {
+ if (getProcessStartSound() != null)
+ GT_Utility.doSoundAtClient(getProcessStartSound(), getTimeBetweenProcessSounds(), 1.0F, aX, aY, aZ);
+ }
+ case INTERRUPT_SOUND_INDEX -> GT_Utility
+ .doSoundAtClient(SoundResource.IC2_MACHINES_INTERRUPT_ONE, 100, 1.0F, aX, aY, aZ);
+ }
+ }
+
+ @Override
+ public void startSoundLoop(byte aIndex, double aX, double aY, double aZ) {
+ super.startSoundLoop(aIndex, aX, aY, aZ);
+ if (aIndex == PROCESS_START_SOUND_INDEX) {
+ if (getProcessStartSound() != null)
+ GT_Utility.doSoundAtClient(getProcessStartSound(), getTimeBetweenProcessSounds(), 1.0F, aX, aY, aZ);
+ }
+ }
+
+ @SideOnly(Side.CLIENT)
+ protected void doActivitySound(ResourceLocation activitySound) {
+ if (getBaseMetaTileEntity().isActive() && activitySound != null) {
+ if (activitySoundLoop == null) {
+ activitySoundLoop = new GT_SoundLoop(activitySound, getBaseMetaTileEntity(), false, true);
+ Minecraft.getMinecraft()
+ .getSoundHandler()
+ .playSound(activitySoundLoop);
+ }
+ } else {
+ if (activitySoundLoop != null) {
+ activitySoundLoop = null;
+ }
+ }
+ }
+
+ /**
+ * @return Time before the start process sound is played again
+ */
+ protected int getTimeBetweenProcessSounds() {
+ return 100;
+ }
+
+ /**
+ * @return Sound that will be played once, when the recipe check was valid
+ */
+ protected SoundResource getProcessStartSound() {
+ return null;
+ }
+
+ /**
+ * @return Sound that will be looped for as long as the machine is doing a recipe
+ */
+ @SideOnly(Side.CLIENT)
+ protected ResourceLocation getActivitySoundLoop() {
+ return null;
+ }
+
+ /**
+ * Called every tick the Machine runs
+ */
+ public boolean onRunningTick(ItemStack aStack) {
+ if (mEUt > 0) {
+ addEnergyOutput(((long) mEUt * mEfficiency) / 10000);
+ return true;
+ }
+ if (mEUt < 0) {
+ if (!drainEnergyInput(getActualEnergyUsage())) {
+ stopMachine(ShutDownReasonRegistry.POWER_LOSS);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ protected long getActualEnergyUsage() {
+ return ((long) -mEUt * 10_000) / Math.max(1000, mEfficiency);
+ }
+
+ /**
+ * Checks if this is a Correct Machine Part for this kind of Machine (Turbine Rotor for example)
+ */
+ public abstract boolean isCorrectMachinePart(ItemStack aStack);
+
+ /**
+ * @deprecated Use {@link #createProcessingLogic()} or {@link #checkProcessing()}
+ */
+ @Deprecated
+ public boolean checkRecipe(ItemStack aStack) {
+ return false;
+ }
+
+ /**
+ * Checks recipe and setup machine if it's successful.
+ * <p>
+ * For generic machine working with recipemap, use {@link #createProcessingLogic()} to make use of shared codebase.
+ */
+ @Nonnull
+ public CheckRecipeResult checkProcessing() {
+ // If no logic is found, try legacy checkRecipe
+ if (processingLogic == null) {
+ return checkRecipe(mInventory[1]) ? CheckRecipeResultRegistry.SUCCESSFUL
+ : CheckRecipeResultRegistry.NO_RECIPE;
+ }
+
+ setupProcessingLogic(processingLogic);
+
+ CheckRecipeResult result = doCheckRecipe();
+ result = postCheckRecipe(result, processingLogic);
+ // inputs are consumed at this point
+ updateSlots();
+ if (!result.wasSuccessful()) return result;
+
+ mEfficiency = (10000 - (getIdealStatus() - getRepairStatus()) * 1000);
+ mEfficiencyIncrease = 10000;
+ mMaxProgresstime = processingLogic.getDuration();
+ setEnergyUsage(processingLogic);
+
+ mOutputItems = processingLogic.getOutputItems();
+ mOutputFluids = processingLogic.getOutputFluids();
+
+ return result;
+ }
+
+ /**
+ * @return If controller slot should be considered as inputs for {@link #doCheckRecipe}
+ */
+ protected boolean canUseControllerSlotForRecipe() {
+ return true;
+ }
+
+ /**
+ * Initializes processing logic for use. Unlike {@link #createProcessingLogic}, this method is called
+ * every time checking for recipes.
+ */
+ protected void setupProcessingLogic(ProcessingLogic logic) {
+ logic.clear();
+ logic.setMachine(this);
+ logic.setRecipeMapSupplier(this::getRecipeMap);
+ logic.setVoidProtection(protectsExcessItem(), protectsExcessFluid());
+ logic.setBatchSize(isBatchModeEnabled() ? getMaxBatchSize() : 1);
+ logic.setRecipeLocking(this, isRecipeLockingEnabled());
+ setProcessingLogicPower(logic);
+ }
+
+ /**
+ * Initializes processing logic for use, specifically for power-related parameters.
+ * Unlike {@link #createProcessingLogic}, this method is called every time checking for recipes.
+ */
+ protected void setProcessingLogicPower(ProcessingLogic logic) {
+ logic.setAvailableVoltage(getAverageInputVoltage());
+ logic.setAvailableAmperage(getMaxInputAmps());
+ logic.setAmperageOC(mEnergyHatches.size() != 1);
+ }
+
+ protected boolean supportsCraftingMEBuffer() {
+ return true;
+ }
+
+ /**
+ * Iterates over hatches and tries to find recipe. Assume {@link #processingLogic} is already set up for use.
+ * If return value is successful, inputs are consumed.
+ */
+ @Nonnull
+ protected CheckRecipeResult doCheckRecipe() {
+ CheckRecipeResult result = CheckRecipeResultRegistry.NO_RECIPE;
+ // check crafting input hatches first
+ if (supportsCraftingMEBuffer()) {
+ for (IDualInputHatch dualInputHatch : mDualInputHatches) {
+ for (var it = dualInputHatch.inventories(); it.hasNext();) {
+ IDualInputInventory slot = it.next();
+ processingLogic.setInputItems(slot.getItemInputs());
+ processingLogic.setInputFluids(slot.getFluidInputs());
+ CheckRecipeResult foundResult = processingLogic.process();
+ if (foundResult.wasSuccessful()) {
+ return foundResult;
+ }
+ if (foundResult != CheckRecipeResultRegistry.NO_RECIPE) {
+ // Recipe failed in interesting way, so remember that and continue searching
+ result = foundResult;
+ }
+ }
+ }
+ }
+
+ processingLogic.setInputFluids(getStoredFluids());
+
+ if (isInputSeparationEnabled()) {
+ if (mInputBusses.isEmpty()) {
+ CheckRecipeResult foundResult = processingLogic.process();
+ if (foundResult.wasSuccessful()) {
+ return foundResult;
+ }
+ if (foundResult != CheckRecipeResultRegistry.NO_RECIPE) {
+ // Recipe failed in interesting way, so remember that and continue searching
+ result = foundResult;
+ }
+ } else {
+ for (GT_MetaTileEntity_Hatch_InputBus bus : mInputBusses) {
+ if (bus instanceof GT_MetaTileEntity_Hatch_CraftingInput_ME) {
+ continue;
+ }
+ List<ItemStack> inputItems = new ArrayList<>();
+ for (int i = bus.getSizeInventory() - 1; i >= 0; i--) {
+ ItemStack stored = bus.getStackInSlot(i);
+ if (stored != null) {
+ inputItems.add(stored);
+ }
+ }
+ if (canUseControllerSlotForRecipe() && getControllerSlot() != null) {
+ inputItems.add(getControllerSlot());
+ }
+ processingLogic.setInputItems(inputItems.toArray(new ItemStack[0]));
+ CheckRecipeResult foundResult = processingLogic.process();
+ if (foundResult.wasSuccessful()) {
+ return foundResult;
+ }
+ if (foundResult != CheckRecipeResultRegistry.NO_RECIPE) {
+ // Recipe failed in interesting way, so remember that and continue searching
+ result = foundResult;
+ }
+ }
+ }
+ } else {
+ List<ItemStack> inputItems = getStoredInputs();
+ if (canUseControllerSlotForRecipe() && getControllerSlot() != null) {
+ inputItems.add(getControllerSlot());
+ }
+ processingLogic.setInputItems(inputItems);
+ CheckRecipeResult foundResult = processingLogic.process();
+ if (foundResult.wasSuccessful()) {
+ return foundResult;
+ }
+ if (foundResult != CheckRecipeResultRegistry.NO_RECIPE) {
+ // Recipe failed in interesting way, so remember that
+ result = foundResult;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Performs additional check for {@link #processingLogic} after all the calculations are done.
+ * As many as checks should be done inside of custom {@link ProcessingLogic}, which you can specify with
+ * {@link #createProcessingLogic()}, because when this method is called, inputs might have been already consumed.
+ * However, certain checks cannot be done like that; Checking energy overflow should be suppressed for
+ * long-power machines for example.
+ *
+ * @return Modified (or not modified) result
+ */
+ @Nonnull
+ protected CheckRecipeResult postCheckRecipe(@Nonnull CheckRecipeResult result,
+ @Nonnull ProcessingLogic processingLogic) {
+ if (result.wasSuccessful() && processingLogic.getCalculatedEut() > Integer.MAX_VALUE) {
+ return CheckRecipeResultRegistry.POWER_OVERFLOW;
+ }
+ return result;
+ }
+
+ /**
+ * Called after {@link #doCheckRecipe} and {@link #postCheckRecipe} being successful.
+ * Override to set energy usage for this machine.
+ */
+ protected void setEnergyUsage(ProcessingLogic processingLogic) {
+ // getCalculatedEut() is guaranteed to not exceed int by postCheckRecipe()
+ mEUt = (int) processingLogic.getCalculatedEut();
+ if (mEUt > 0) {
+ mEUt = (-mEUt);
+ }
+ }
+
+ protected int getMaxBatchSize() {
+ return 128;
+ }
+
+ /**
+ * Checks the Machine. You have to assign the MetaTileEntities for the Hatches here.
+ */
+ public abstract boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack);
+
+ /**
+ * Gets the maximum Efficiency that spare Part can get (0 - 10000)
+ */
+ public abstract int getMaxEfficiency(ItemStack aStack);
+
+ /**
+ * Gets the pollution this Device outputs to a Muffler per tick (10000 = one Pullution Block)
+ */
+ public int getPollutionPerTick(ItemStack aStack) {
+ return getPollutionPerSecond(aStack) / 20;
+ }
+
+ /**
+ * Gets the pollution produced per second by this multiblock, default to 0. Override this with its actual value in
+ * the code of the multiblock.
+ */
+ public int getPollutionPerSecond(ItemStack aStack) {
+ return 0;
+ }
+
+ /**
+ * Gets the damage to the ItemStack, usually 0 or 1.
+ */
+ public abstract int getDamageToComponent(ItemStack aStack);
+
+ /**
+ * If it explodes when the Component has to be replaced.
+ */
+ public abstract boolean explodesOnComponentBreak(ItemStack aStack);
+
+ /**
+ * @deprecated Use {@link #stopMachine(ShutDownReason)}
+ */
+ @Deprecated
+ public void stopMachine() {
+ stopMachine(ShutDownReasonRegistry.NONE);
+ }
+
+ /**
+ * @deprecated Use {@link #stopMachine(ShutDownReason)}
+ */
+ @Deprecated
+ public void criticalStopMachine() {
+ stopMachine(ShutDownReasonRegistry.CRITICAL_NONE);
+ }
+
+ public void stopMachine(@Nonnull ShutDownReason reason) {
+ if (!ShutDownReasonRegistry.isRegistered(reason.getID())) {
+ throw new RuntimeException(String.format("Reason %s is not registered for registry", reason.getID()));
+ }
+ mOutputItems = null;
+ mOutputFluids = null;
+ mEUt = 0;
+ mEfficiency = 0;
+ mProgresstime = 0;
+ mMaxProgresstime = 0;
+ mEfficiencyIncrease = 0;
+ getBaseMetaTileEntity().disableWorking();
+ getBaseMetaTileEntity().setShutDownReason(reason);
+ getBaseMetaTileEntity().setShutdownStatus(true);
+ if (reason.wasCritical()) {
+ sendSound(INTERRUPT_SOUND_INDEX);
+ }
+ }
+
+ public int getRepairStatus() {
+ return (mWrench ? 1 : 0) + (mScrewdriver ? 1 : 0)
+ + (mSoftHammer ? 1 : 0)
+ + (mHardHammer ? 1 : 0)
+ + (mSolderingTool ? 1 : 0)
+ + (mCrowbar ? 1 : 0);
+ }
+
+ public int getIdealStatus() {
+ return 6;
+ }
+
+ public int getCurrentEfficiency(ItemStack itemStack) {
+ int maxEff = getMaxEfficiency(itemStack);
+ return maxEff - (getIdealStatus() - getRepairStatus()) * maxEff / 10;
+ }
+
+ public boolean doRandomMaintenanceDamage() {
+ if (!isCorrectMachinePart(mInventory[1])) {
+ stopMachine(ShutDownReasonRegistry.NO_MACHINE_PART);
+ return false;
+ }
+ if (shouldCheckMaintenance() && getRepairStatus() == 0) {
+ stopMachine(ShutDownReasonRegistry.NO_REPAIR);
+ return false;
+ }
+ if (mRuntime++ > 1000) {
+ mRuntime = 0;
+ if (shouldCheckMaintenance() && getBaseMetaTileEntity().getRandomNumber(6000) == 0) {
+ causeMaintenanceIssue();
+ }
+ if (mInventory[1] != null && getBaseMetaTileEntity().getRandomNumber(2) == 0
+ && !mInventory[1].getUnlocalizedName()
+ .startsWith("gt.blockmachines.basicmachine.")) {
+ if (mInventory[1].getItem() instanceof GT_MetaGenerated_Tool_01) {
+ NBTTagCompound tNBT = mInventory[1].getTagCompound();
+ ((GT_MetaGenerated_Tool) mInventory[1].getItem()).doDamage(
+ mInventory[1],
+ (long) getDamageToComponent(mInventory[1])
+ * (long) Math.min(mEUt / this.damageFactorLow, Math.pow(mEUt, this.damageFactorHigh)));
+ if (mInventory[1].stackSize == 0) mInventory[1] = null;
+ }
+ }
+ }
+ return true;
+ }
+
+ public void causeMaintenanceIssue() {
+ switch (getBaseMetaTileEntity().getRandomNumber(6)) {
+ case 0 -> mWrench = false;
+ case 1 -> mScrewdriver = false;
+ case 2 -> mSoftHammer = false;
+ case 3 -> mHardHammer = false;
+ case 4 -> mSolderingTool = false;
+ case 5 -> mCrowbar = false;
+ }
+ }
+
+ public void explodeMultiblock() {
+
+ GT_Log.exp.println(
+ "MultiBlockExplosion at: " + this.getBaseMetaTileEntity()
+ .getXCoord()
+ + " | "
+ + this.getBaseMetaTileEntity()
+ .getYCoord()
+ + " | "
+ + this.getBaseMetaTileEntity()
+ .getZCoord()
+ + " DIMID: "
+ + this.getBaseMetaTileEntity()
+ .getWorld().provider.dimensionId
+ + ".");
+
+ GT_Pollution.addPollution(getBaseMetaTileEntity(), GT_Mod.gregtechproxy.mPollutionOnExplosion);
+ mInventory[1] = null;
+ // noinspection unchecked // In this case, the inspection only indicates that the array can be abused in runtime
+ Iterable<MetaTileEntity> allHatches = Iterables.concat(
+ mInputBusses,
+ mOutputBusses,
+ mInputHatches,
+ mOutputHatches,
+ mDynamoHatches,
+ mMufflerHatches,
+ mEnergyHatches,
+ mMaintenanceHatches);
+ for (MetaTileEntity tTileEntity : allHatches) {
+ if (tTileEntity != null && tTileEntity.getBaseMetaTileEntity() != null) {
+ tTileEntity.getBaseMetaTileEntity()
+ .doExplosion(V[8]);
+ }
+ }
+ getBaseMetaTileEntity().doExplosion(V[8]);
+ }
+
+ public boolean addEnergyOutput(long aEU) {
+ if (aEU <= 0) {
+ return true;
+ }
+ if (mDynamoHatches.size() > 0) {
+ return addEnergyOutputMultipleDynamos(aEU, true);
+ }
+ return false;
+ }
+
+ public boolean addEnergyOutputMultipleDynamos(long aEU, boolean aAllowMixedVoltageDynamos) {
+ int injected = 0;
+ long totalOutput = 0;
+ long aFirstVoltageFound = -1;
+ boolean aFoundMixedDynamos = false;
+ for (GT_MetaTileEntity_Hatch_Dynamo aDynamo : filterValidMTEs(mDynamoHatches)) {
+ long aVoltage = aDynamo.maxEUOutput();
+ long aTotal = aDynamo.maxAmperesOut() * aVoltage;
+ // Check against voltage to check when hatch mixing
+ if (aFirstVoltageFound == -1) {
+ aFirstVoltageFound = aVoltage;
+ } else {
+ if (aFirstVoltageFound != aVoltage) {
+ aFoundMixedDynamos = true;
+ }
+ }
+ totalOutput += aTotal;
+ }
+
+ if (totalOutput < aEU || (aFoundMixedDynamos && !aAllowMixedVoltageDynamos)) {
+ explodeMultiblock();
+ return false;
+ }
+
+ long leftToInject;
+ long aVoltage;
+ int aAmpsToInject;
+ int aRemainder;
+ int ampsOnCurrentHatch;
+ for (GT_MetaTileEntity_Hatch_Dynamo aDynamo : filterValidMTEs(mDynamoHatches)) {
+ leftToInject = aEU - injected;
+ aVoltage = aDynamo.maxEUOutput();
+ aAmpsToInject = (int) (leftToInject / aVoltage);
+ aRemainder = (int) (leftToInject - (aAmpsToInject * aVoltage));
+ ampsOnCurrentHatch = (int) Math.min(aDynamo.maxAmperesOut(), aAmpsToInject);
+ for (int i = 0; i < ampsOnCurrentHatch; i++) {
+ aDynamo.getBaseMetaTileEntity()
+ .increaseStoredEnergyUnits(aVoltage, false);
+ }
+ injected += aVoltage * ampsOnCurrentHatch;
+ if (aRemainder > 0 && ampsOnCurrentHatch < aDynamo.maxAmperesOut()) {
+ aDynamo.getBaseMetaTileEntity()
+ .increaseStoredEnergyUnits(aRemainder, false);
+ injected += aRemainder;
+ }
+ }
+ return injected > 0;
+ }
+
+ /**
+ * Sums up voltage of energy hatches. Amperage does not matter.
+ */
+ public long getMaxInputVoltage() {
+ long rVoltage = 0;
+ for (GT_MetaTileEntity_Hatch_Energy tHatch : filterValidMTEs(mEnergyHatches))
+ rVoltage += tHatch.getBaseMetaTileEntity()
+ .getInputVoltage();
+ return rVoltage;
+ }
+
+ public long getAverageInputVoltage() {
+ return GT_ExoticEnergyInputHelper.getAverageInputVoltageMulti(mEnergyHatches);
+ }
+
+ public long getMaxInputAmps() {
+ return GT_ExoticEnergyInputHelper.getMaxWorkingInputAmpsMulti(mEnergyHatches);
+ }
+
+ public long getMaxInputEu() {
+ return GT_ExoticEnergyInputHelper.getTotalEuMulti(mEnergyHatches);
+ }
+
+ /**
+ * Sums up max input EU/t of energy hatches, amperage included.
+ */
+ public long getMaxInputPower() {
+ long eut = 0;
+ for (GT_MetaTileEntity_Hatch_Energy tHatch : filterValidMTEs(mEnergyHatches)) {
+ IGregTechTileEntity baseTile = tHatch.getBaseMetaTileEntity();
+ eut += baseTile.getInputVoltage() * baseTile.getInputAmperage();
+ }
+ return eut;
+ }
+
+ /**
+ * Returns voltage tier of energy hatches. If multiple tiers are found, returns 0.
+ */
+ public long getInputVoltageTier() {
+ long rTier = 0;
+ if (mEnergyHatches.size() > 0) {
+ rTier = mEnergyHatches.get(0)
+ .getInputTier();
+ for (int i = 1; i < mEnergyHatches.size(); i++) {
+ if (mEnergyHatches.get(i)
+ .getInputTier() != rTier) return 0;
+ }
+ }
+
+ return rTier;
+ }
+
+ /**
+ * Calcualtes the overclockedness using long integers
+ *
+ * @param aEUt - recipe EUt
+ * @param aDuration - recipe Duration
+ * @param mAmperage - should be 1 ?
+ * @param maxInputVoltage - Multiblock Max input voltage. Voltage is rounded up to higher tier voltage.
+ * @param perfectOC - If the Multiblock OCs perfectly, i.e. the large Chemical Reactor
+ */
+ protected void calculateOverclockedNessMultiInternal(long aEUt, int aDuration, int mAmperage, long maxInputVoltage,
+ boolean perfectOC) {
+ byte tier = (byte) Math.max(0, GT_Utility.getTier(maxInputVoltage));
+ GT_OverclockCalculator calculator = new GT_OverclockCalculator().setRecipeEUt(aEUt)
+ .setEUt(V[tier] * mAmperage)
+ .setDuration(aDuration)
+ .setDurationDecreasePerOC(perfectOC ? 4.0 : 2.0)
+ .calculate();
+ mEUt = (int) calculator.getConsumption();
+ mMaxProgresstime = calculator.getDuration();
+ }
+
+ @Deprecated
+ protected void calculateOverclockedNessMulti(int aEUt, int aDuration, int mAmperage, long maxInputVoltage) {
+ calculateOverclockedNessMultiInternal(aEUt, aDuration, mAmperage, maxInputVoltage, false);
+ }
+
+ protected void calculateOverclockedNessMulti(long aEUt, int aDuration, int mAmperage, long maxInputVoltage) {
+ calculateOverclockedNessMultiInternal(aEUt, aDuration, mAmperage, maxInputVoltage, false);
+ }
+
+ @Deprecated
+ protected void calculatePerfectOverclockedNessMulti(int aEUt, int aDuration, int mAmperage, long maxInputVoltage) {
+ calculateOverclockedNessMultiInternal(aEUt, aDuration, mAmperage, maxInputVoltage, true);
+ }
+
+ protected void calculatePerfectOverclockedNessMulti(long aEUt, int aDuration, int mAmperage, long maxInputVoltage) {
+ calculateOverclockedNessMultiInternal(aEUt, aDuration, mAmperage, maxInputVoltage, true);
+ }
+
+ public boolean drainEnergyInput(long aEU) {
+ if (aEU <= 0) return true;
+ for (GT_MetaTileEntity_Hatch_Energy tHatch : filterValidMTEs(mEnergyHatches)) {
+ if (tHatch.getBaseMetaTileEntity()
+ .decreaseStoredEnergyUnits(aEU, false)) return true;
+ }
+ return false;
+ }
+
+ protected static boolean dumpFluid(List<GT_MetaTileEntity_Hatch_Output> aOutputHatches, FluidStack copiedFluidStack,
+ boolean restrictiveHatchesOnly) {
+ for (GT_MetaTileEntity_Hatch_Output tHatch : filterValidMTEs(aOutputHatches)) {
+ if (restrictiveHatchesOnly && tHatch.mMode == 0) {
+ continue;
+ }
+ if (!tHatch.canStoreFluid(copiedFluidStack)) continue;
+ int tAmount = tHatch.fill(copiedFluidStack, false);
+ if (tAmount >= copiedFluidStack.amount) {
+ boolean filled = tHatch.fill(copiedFluidStack, true) >= copiedFluidStack.amount;
+ tHatch.onEmptyingContainerWhenEmpty();
+ return filled;
+ } else if (tAmount > 0) {
+ copiedFluidStack.amount = copiedFluidStack.amount - tHatch.fill(copiedFluidStack, true);
+ tHatch.onEmptyingContainerWhenEmpty();
+ }
+ }
+ return false;
+ }
+
+ public boolean addOutput(FluidStack aLiquid) {
+ if (aLiquid == null) return false;
+ FluidStack copiedFluidStack = aLiquid.copy();
+ if (!dumpFluid(mOutputHatches, copiedFluidStack, true)) {
+ dumpFluid(mOutputHatches, copiedFluidStack, false);
+ }
+ return false;
+ }
+
+ protected void addFluidOutputs(FluidStack[] mOutputFluids2) {
+ for (FluidStack outputFluidStack : mOutputFluids2) {
+ addOutput(outputFluidStack);
+ }
+ }
+
+ public boolean depleteInput(FluidStack aLiquid) {
+ return depleteInput(aLiquid, false);
+ }
+
+ public boolean depleteInput(FluidStack aLiquid, boolean simulate) {
+ if (aLiquid == null) return false;
+ for (GT_MetaTileEntity_Hatch_Input tHatch : filterValidMTEs(mInputHatches)) {
+ setHatchRecipeMap(tHatch);
+ FluidStack tLiquid = tHatch.drain(ForgeDirection.UNKNOWN, aLiquid, false);
+ if (tLiquid != null && tLiquid.amount >= aLiquid.amount) {
+ if (simulate) {
+ return true;
+ }
+ tLiquid = tHatch.drain(ForgeDirection.UNKNOWN, aLiquid, true);
+ return tLiquid != null && tLiquid.amount >= aLiquid.amount;
+ }
+ }
+ return false;
+ }
+
+ public boolean addOutput(ItemStack aStack) {
+ if (GT_Utility.isStackInvalid(aStack)) return false;
+ aStack = GT_Utility.copyOrNull(aStack);
+
+ final List<GT_MetaTileEntity_Hatch_OutputBus> filteredBuses = filterValidMTEs(mOutputBusses);
+ if (dumpItem(filteredBuses, aStack, true) || dumpItem(filteredBuses, aStack, false)) {
+ return true;
+ }
+
+ boolean outputSuccess = true;
+ // noinspection DataFlowIssue
+ while (outputSuccess && aStack.stackSize > 0) {
+ outputSuccess = false;
+ ItemStack single = aStack.splitStack(1);
+ for (GT_MetaTileEntity_Hatch_Output tHatch : filterValidMTEs(mOutputHatches)) {
+ if (!outputSuccess && tHatch.outputsItems()) {
+ if (tHatch.getBaseMetaTileEntity()
+ .addStackToSlot(1, single)) outputSuccess = true;
+ }
+ }
+ }
+ return outputSuccess;
+ }
+
+ private boolean dumpItem(List<GT_MetaTileEntity_Hatch_OutputBus> outputBuses, ItemStack itemStack,
+ boolean restrictiveBusesOnly) {
+ for (GT_MetaTileEntity_Hatch_OutputBus outputBus : outputBuses) {
+ if (restrictiveBusesOnly && !outputBus.isLocked()) {
+ continue;
+ }
+
+ if (outputBus.storeAll(itemStack)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public boolean depleteInput(ItemStack aStack) {
+ if (GT_Utility.isStackInvalid(aStack)) return false;
+ FluidStack aLiquid = GT_Utility.getFluidForFilledItem(aStack, true);
+ if (aLiquid != null) return depleteInput(aLiquid);
+ for (GT_MetaTileEntity_Hatch_Input tHatch : filterValidMTEs(mInputHatches)) {
+ setHatchRecipeMap(tHatch);
+ if (GT_Utility.areStacksEqual(
+ aStack,
+ tHatch.getBaseMetaTileEntity()
+ .getStackInSlot(0))) {
+ if (tHatch.getBaseMetaTileEntity()
+ .getStackInSlot(0).stackSize >= aStack.stackSize) {
+ tHatch.getBaseMetaTileEntity()
+ .decrStackSize(0, aStack.stackSize);
+ return true;
+ }
+ }
+ }
+ for (GT_MetaTileEntity_Hatch_InputBus tHatch : filterValidMTEs(mInputBusses)) {
+ tHatch.mRecipeMap = getRecipeMap();
+ for (int i = tHatch.getBaseMetaTileEntity()
+ .getSizeInventory() - 1; i >= 0; i--) {
+ if (GT_Utility.areStacksEqual(
+ aStack,
+ tHatch.getBaseMetaTileEntity()
+ .getStackInSlot(i))) {
+ if (tHatch.getBaseMetaTileEntity()
+ .getStackInSlot(i).stackSize >= aStack.stackSize) {
+ tHatch.getBaseMetaTileEntity()
+ .decrStackSize(i, aStack.stackSize);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ public ArrayList<ItemStack> getStoredOutputs() {
+ ArrayList<ItemStack> rList = new ArrayList<>();
+ for (GT_MetaTileEntity_Hatch_OutputBus tHatch : filterValidMTEs(mOutputBusses)) {
+ for (int i = tHatch.getBaseMetaTileEntity()
+ .getSizeInventory() - 1; i >= 0; i--) {
+ rList.add(
+ tHatch.getBaseMetaTileEntity()
+ .getStackInSlot(i));
+ }
+ }
+ return rList;
+ }
+
+ public ArrayList<FluidStack> getStoredFluids() {
+ ArrayList<FluidStack> rList = new ArrayList<>();
+ Map<Fluid, FluidStack> inputsFromME = new HashMap<>();
+ for (GT_MetaTileEntity_Hatch_Input tHatch : filterValidMTEs(mInputHatches)) {
+ setHatchRecipeMap(tHatch);
+ if (tHatch instanceof GT_MetaTileEntity_Hatch_MultiInput multiInputHatch) {
+ for (FluidStack tFluid : multiInputHatch.getStoredFluid()) {
+ if (tFluid != null) {
+ rList.add(tFluid);
+ }
+ }
+ } else if (tHatch instanceof GT_MetaTileEntity_Hatch_Input_ME meHatch) {
+ for (FluidStack fluidStack : meHatch.getStoredFluids()) {
+ if (fluidStack != null) {
+ // Prevent the same fluid from different ME hatches from being recognized
+ inputsFromME.put(fluidStack.getFluid(), fluidStack);
+ }
+ }
+ } else {
+ if (tHatch.getFillableStack() != null) {
+ rList.add(tHatch.getFillableStack());
+ }
+ }
+ }
+
+ if (!inputsFromME.isEmpty()) {
+ rList.addAll(inputsFromME.values());
+ }
+ return rList;
+ }
+
+ /**
+ * Drains fluid from the given hatch, including {@link IDualInputHatch}. Should never be used during recipe check!
+ *
+ * @param doDrain If false, fluid will not actually be consumed
+ * @return Whether the hatch contains enough fluid to drain
+ */
+ public boolean drain(GT_MetaTileEntity_Hatch hatch, FluidStack fluid, boolean doDrain) {
+ if (fluid == null || hatch == null) return false;
+ if (supportsCraftingMEBuffer() && hatch instanceof IDualInputHatch tHatch && tHatch.supportsFluids()) {
+ Optional<IDualInputInventory> inventory = tHatch.getFirstNonEmptyInventory();
+ if (inventory.isPresent()) {
+ for (FluidStack storedFluid : Lists.newArrayList(
+ inventory.get()
+ .getFluidInputs())) {
+ if (fluid.isFluidEqual(storedFluid)) {
+ if (doDrain) storedFluid.amount = Math.max(storedFluid.amount - fluid.amount, 0);
+ return storedFluid.amount >= fluid.amount;
+ }
+ }
+ }
+ }
+
+ if (hatch instanceof GT_MetaTileEntity_Hatch_Input tHatch && tHatch.isValid()) {
+ if (tHatch instanceof GT_MetaTileEntity_Hatch_Input_ME meHatch) {
+ meHatch.startRecipeProcessing();
+ FluidStack tFluid = meHatch.drain(ForgeDirection.UNKNOWN, fluid, doDrain);
+ meHatch.endRecipeProcessing(this);
+ return tFluid != null && tFluid.amount >= fluid.amount;
+ } else {
+ FluidStack tFluid = tHatch.drain(ForgeDirection.UNKNOWN, fluid, doDrain);
+ return tFluid != null && tFluid.amount >= fluid.amount;
+ }
+ }
+
+ return false;
+ }
+
+ public ArrayList<ItemStack> getStoredInputs() {
+ ArrayList<ItemStack> rList = new ArrayList<>();
+ Map<GT_Utility.ItemId, ItemStack> inputsFromME = new HashMap<>();
+ for (GT_MetaTileEntity_Hatch_InputBus tHatch : filterValidMTEs(mInputBusses)) {
+ if (tHatch instanceof GT_MetaTileEntity_Hatch_CraftingInput_ME) {
+ continue;
+ }
+ tHatch.mRecipeMap = getRecipeMap();
+ IGregTechTileEntity tileEntity = tHatch.getBaseMetaTileEntity();
+ boolean isMEBus = tHatch instanceof GT_MetaTileEntity_Hatch_InputBus_ME;
+ for (int i = tileEntity.getSizeInventory() - 1; i >= 0; i--) {
+ ItemStack itemStack = tileEntity.getStackInSlot(i);
+ if (itemStack != null) {
+ if (isMEBus) {
+ // Prevent the same item from different ME buses from being recognized
+ inputsFromME.put(GT_Utility.ItemId.createNoCopy(itemStack), itemStack);
+ } else {
+ rList.add(itemStack);
+ }
+ }
+ }
+ }
+
+ if (getStackInSlot(1) != null && getStackInSlot(1).getUnlocalizedName()
+ .startsWith("gt.integrated_circuit")) rList.add(getStackInSlot(1));
+ if (!inputsFromME.isEmpty()) {
+ rList.addAll(inputsFromME.values());
+ }
+ return rList;
+ }
+
+ /**
+ * Anything that is usually separated off in {@link #getStoredInputs()} (like crafting input bus/buffer) is also
+ * included here.
+ */
+ public ArrayList<ItemStack> getAllStoredInputs() {
+ ArrayList<ItemStack> rList = new ArrayList<>();
+
+ if (supportsCraftingMEBuffer()) {
+ for (IDualInputHatch dualInputHatch : mDualInputHatches) {
+ Iterator<? extends IDualInputInventory> inventoryIterator = dualInputHatch.inventories();
+ while (inventoryIterator.hasNext()) {
+ ItemStack[] items = inventoryIterator.next()
+ .getItemInputs();
+ if (items == null) {
+ continue;
+ }
+ for (int i = 0; i < items.length; i++) {
+ ItemStack item = items[i];
+ if (item != null) {
+ rList.add(item);
+ }
+ }
+ }
+ }
+ }
+
+ Map<GT_Utility.ItemId, ItemStack> inputsFromME = new HashMap<>();
+ for (GT_MetaTileEntity_Hatch_InputBus tHatch : filterValidMTEs(mInputBusses)) {
+ if (tHatch instanceof GT_MetaTileEntity_Hatch_CraftingInput_ME) {
+ continue;
+ }
+ tHatch.mRecipeMap = getRecipeMap();
+ IGregTechTileEntity tileEntity = tHatch.getBaseMetaTileEntity();
+ boolean isMEBus = tHatch instanceof GT_MetaTileEntity_Hatch_InputBus_ME;
+ for (int i = tileEntity.getSizeInventory() - 1; i >= 0; i--) {
+ ItemStack itemStack = tileEntity.getStackInSlot(i);
+ if (itemStack != null) {
+ if (isMEBus) {
+ // Prevent the same item from different ME buses from being recognized
+ inputsFromME.put(GT_Utility.ItemId.createNoCopy(itemStack), itemStack);
+ } else {
+ rList.add(itemStack);
+ }
+ }
+ }
+ }
+
+ if (getStackInSlot(1) != null && getStackInSlot(1).getUnlocalizedName()
+ .startsWith("gt.integrated_circuit")) rList.add(getStackInSlot(1));
+ if (!inputsFromME.isEmpty()) {
+ rList.addAll(inputsFromME.values());
+ }
+ return rList;
+ }
+
+ public Map<GT_Utility.ItemId, ItemStack> getStoredInputsFromME() {
+ Map<GT_Utility.ItemId, ItemStack> inputsFromME = new Object2ReferenceOpenHashMap<>();
+ for (GT_MetaTileEntity_Hatch_InputBus tHatch : filterValidMTEs(mInputBusses)) {
+ if (tHatch instanceof GT_MetaTileEntity_Hatch_InputBus_ME meBus) {
+ for (int i = meBus.getSizeInventory() - 1; i >= 0; i--) {
+ ItemStack itemStack = meBus.getStackInSlot(i);
+ if (itemStack != null) {
+ // Prevent the same item from different ME buses from being recognized
+ inputsFromME.put(GT_Utility.ItemId.createNoCopy(itemStack), itemStack);
+ }
+ }
+ }
+ }
+ return inputsFromME;
+ }
+
+ public Map<Fluid, FluidStack> getStoredFluidsFromME() {
+ Map<Fluid, FluidStack> fluidsFromME = new Reference2ReferenceOpenHashMap<>();
+ for (GT_MetaTileEntity_Hatch_Input tHatch : filterValidMTEs(mInputHatches)) {
+ if (tHatch instanceof GT_MetaTileEntity_Hatch_Input_ME meHatch) {
+ for (FluidStack fluid : meHatch.getStoredFluids()) {
+ if (fluid != null) {
+ // Prevent the same fluid from different ME hatches from being recognized
+ fluidsFromME.put(fluid.getFluid(), fluid);
+ }
+ }
+ }
+ }
+ return fluidsFromME;
+ }
+
+ @Override
+ public RecipeMap<?> getRecipeMap() {
+ return null;
+ }
+
+ /**
+ * Creates logic to run recipe check based on recipemap. This runs only once, on class instantiation.
+ * <p>
+ * If this machine doesn't use recipemap or does some complex things, override {@link #checkProcessing()}.
+ */
+ @ApiStatus.OverrideOnly
+ protected ProcessingLogic createProcessingLogic() {
+ return null;
+ }
+
+ public void updateSlots() {
+ for (GT_MetaTileEntity_Hatch_Input tHatch : filterValidMTEs(mInputHatches)) tHatch.updateSlots();
+ for (GT_MetaTileEntity_Hatch_InputBus tHatch : filterValidMTEs(mInputBusses)) tHatch.updateSlots();
+ }
+
+ protected void startRecipeProcessing() {
+ for (GT_MetaTileEntity_Hatch_InputBus hatch : filterValidMTEs(mInputBusses)) {
+ if (hatch instanceof IRecipeProcessingAwareHatch aware) {
+ aware.startRecipeProcessing();
+ }
+ }
+ for (GT_MetaTileEntity_Hatch_Input hatch : filterValidMTEs(mInputHatches)) {
+ if (hatch instanceof IRecipeProcessingAwareHatch aware) {
+ aware.startRecipeProcessing();
+ }
+ }
+ }
+
+ public void setResultIfFailure(CheckRecipeResult result) {
+ if (!result.wasSuccessful()) {
+ this.checkRecipeResult = result;
+ }
+ }
+
+ protected void endRecipeProcessing() {
+ for (GT_MetaTileEntity_Hatch_InputBus hatch : filterValidMTEs(mInputBusses)) {
+ if (hatch instanceof IRecipeProcessingAwareHatch aware) {
+ setResultIfFailure(aware.endRecipeProcessing(this));
+ }
+ }
+ for (GT_MetaTileEntity_Hatch_Input hatch : filterValidMTEs(mInputHatches)) {
+ if (hatch instanceof IRecipeProcessingAwareHatch aware) {
+ setResultIfFailure(aware.endRecipeProcessing(this));
+ }
+ }
+ }
+
+ public boolean addToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) return false;
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch hatch) {
+ hatch.updateTexture(aBaseCasingIndex);
+ hatch.updateCraftingIcon(this.getMachineCraftingIcon());
+ }
+ if (aMetaTileEntity instanceof IDualInputHatch hatch) {
+ hatch.updateCraftingIcon(this.getMachineCraftingIcon());
+ return mDualInputHatches.add(hatch);
+ }
+ if (aMetaTileEntity instanceof ISmartInputHatch hatch) {
+ // Only add them to be iterated if enabled for performance reasons
+ if (hatch.doFastRecipeCheck()) {
+ mSmartInputHatches.add(hatch);
+ }
+ }
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Input) {
+ setHatchRecipeMap((GT_MetaTileEntity_Hatch_Input) aMetaTileEntity);
+ return mInputHatches.add((GT_MetaTileEntity_Hatch_Input) aMetaTileEntity);
+ }
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_InputBus) {
+ ((GT_MetaTileEntity_Hatch_InputBus) aMetaTileEntity).mRecipeMap = getRecipeMap();
+ return mInputBusses.add((GT_MetaTileEntity_Hatch_InputBus) aMetaTileEntity);
+ }
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Output)
+ return mOutputHatches.add((GT_MetaTileEntity_Hatch_Output) aMetaTileEntity);
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_OutputBus)
+ return mOutputBusses.add((GT_MetaTileEntity_Hatch_OutputBus) aMetaTileEntity);
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Energy)
+ return mEnergyHatches.add((GT_MetaTileEntity_Hatch_Energy) aMetaTileEntity);
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Dynamo)
+ return mDynamoHatches.add((GT_MetaTileEntity_Hatch_Dynamo) aMetaTileEntity);
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Maintenance)
+ return mMaintenanceHatches.add((GT_MetaTileEntity_Hatch_Maintenance) aMetaTileEntity);
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Muffler)
+ return mMufflerHatches.add((GT_MetaTileEntity_Hatch_Muffler) aMetaTileEntity);
+ return false;
+ }
+
+ public boolean addMaintenanceToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) return false;
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Maintenance hatch) {
+ hatch.updateTexture(aBaseCasingIndex);
+ hatch.updateCraftingIcon(this.getMachineCraftingIcon());
+ return mMaintenanceHatches.add(hatch);
+ }
+ return false;
+ }
+
+ public boolean addEnergyInputToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) {
+ return false;
+ }
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) return false;
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Energy hatch) {
+ hatch.updateTexture(aBaseCasingIndex);
+ hatch.updateCraftingIcon(this.getMachineCraftingIcon());
+ return mEnergyHatches.add(hatch);
+ }
+ return false;
+ }
+
+ public boolean addExoticEnergyInputToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) return false;
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch hatch
+ && GT_ExoticEnergyInputHelper.isExoticEnergyInput(aMetaTileEntity)) {
+ hatch.updateTexture(aBaseCasingIndex);
+ hatch.updateCraftingIcon(this.getMachineCraftingIcon());
+ return mExoticEnergyHatches.add(hatch);
+ }
+ return false;
+ }
+
+ public boolean addDynamoToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) return false;
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Dynamo hatch) {
+ hatch.updateTexture(aBaseCasingIndex);
+ hatch.updateCraftingIcon(this.getMachineCraftingIcon());
+ return mDynamoHatches.add(hatch);
+ }
+ return false;
+ }
+
+ public boolean addMufflerToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) return false;
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Muffler hatch) {
+ hatch.updateTexture(aBaseCasingIndex);
+ hatch.updateCraftingIcon(this.getMachineCraftingIcon());
+ return mMufflerHatches.add(hatch);
+ }
+ return false;
+ }
+
+ public boolean addInputToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ return addInputBusToMachineList(aTileEntity, aBaseCasingIndex)
+ || addInputHatchToMachineList(aTileEntity, aBaseCasingIndex);
+ }
+
+ public boolean addOutputToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ return addOutputBusToMachineList(aTileEntity, aBaseCasingIndex)
+ || addOutputHatchToMachineList(aTileEntity, aBaseCasingIndex);
+ }
+
+ public boolean addInputBusToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) return false;
+ if (aMetaTileEntity instanceof IDualInputHatch hatch) {
+ if (!supportsCraftingMEBuffer()) return false;
+ hatch.updateTexture(aBaseCasingIndex);
+ hatch.updateCraftingIcon(this.getMachineCraftingIcon());
+ return mDualInputHatches.add(hatch);
+ }
+ if (aMetaTileEntity instanceof ISmartInputHatch hatch) {
+ mSmartInputHatches.add(hatch);
+ }
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_InputBus hatch) {
+ hatch.updateTexture(aBaseCasingIndex);
+ hatch.updateCraftingIcon(this.getMachineCraftingIcon());
+ hatch.mRecipeMap = getRecipeMap();
+ return mInputBusses.add(hatch);
+ }
+ return false;
+ }
+
+ public boolean addOutputBusToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) return false;
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_OutputBus hatch) {
+ hatch.updateTexture(aBaseCasingIndex);
+ hatch.updateCraftingIcon(this.getMachineCraftingIcon());
+ return mOutputBusses.add(hatch);
+ }
+ return false;
+ }
+
+ public boolean addInputHatchToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) return false;
+ if (aMetaTileEntity instanceof ISmartInputHatch hatch) {
+ mSmartInputHatches.add(hatch);
+ }
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Input hatch) {
+ hatch.updateTexture(aBaseCasingIndex);
+ hatch.updateCraftingIcon(this.getMachineCraftingIcon());
+ setHatchRecipeMap(hatch);
+ return mInputHatches.add(hatch);
+ }
+ return false;
+ }
+
+ public boolean addOutputHatchToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) {
+ if (aTileEntity == null) return false;
+ IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity();
+ if (aMetaTileEntity == null) return false;
+ if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Output hatch) {
+ hatch.updateTexture(aBaseCasingIndex);
+ hatch.updateCraftingIcon(this.getMachineCraftingIcon());
+ return mOutputHatches.add(hatch);
+ }
+ return false;
+ }
+
+ protected void setHatchRecipeMap(GT_MetaTileEntity_Hatch_Input hatch) {
+ if (filtersFluid()) {
+ hatch.mRecipeMap = getRecipeMap();
+ }
+ }
+
+ /**
+ * @return If this multi filters fluid input for hatches based on recipemap.
+ */
+ protected boolean filtersFluid() {
+ return true;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ int mPollutionReduction = 0;
+ for (GT_MetaTileEntity_Hatch_Muffler tHatch : filterValidMTEs(mMufflerHatches)) {
+ mPollutionReduction = Math.max(tHatch.calculatePollutionReduction(100), mPollutionReduction);
+ }
+
+ long storedEnergy = 0;
+ long maxEnergy = 0;
+ for (GT_MetaTileEntity_Hatch_Energy tHatch : filterValidMTEs(mEnergyHatches)) {
+ storedEnergy += tHatch.getBaseMetaTileEntity()
+ .getStoredEU();
+ maxEnergy += tHatch.getBaseMetaTileEntity()
+ .getEUCapacity();
+ }
+
+ return new String[] {
+ /* 1 */ StatCollector.translateToLocal("GT5U.multiblock.Progress") + ": "
+ + EnumChatFormatting.GREEN
+ + formatNumbers(mProgresstime / 20)
+ + EnumChatFormatting.RESET
+ + " s / "
+ + EnumChatFormatting.YELLOW
+ + formatNumbers(mMaxProgresstime / 20)
+ + EnumChatFormatting.RESET
+ + " s",
+ /* 2 */ StatCollector.translateToLocal("GT5U.multiblock.energy") + ": "
+ + EnumChatFormatting.GREEN
+ + formatNumbers(storedEnergy)
+ + EnumChatFormatting.RESET
+ + " EU / "
+ + EnumChatFormatting.YELLOW
+ + formatNumbers(maxEnergy)
+ + EnumChatFormatting.RESET
+ + " EU",
+ /* 3 */ StatCollector.translateToLocal("GT5U.multiblock.usage") + ": "
+ + EnumChatFormatting.RED
+ + formatNumbers(getActualEnergyUsage())
+ + EnumChatFormatting.RESET
+ + " EU/t",
+ /* 4 */ StatCollector.translateToLocal("GT5U.multiblock.mei") + ": "
+ + EnumChatFormatting.YELLOW
+ + formatNumbers(getMaxInputVoltage())
+ + EnumChatFormatting.RESET
+ + " EU/t(*2A) "
+ + StatCollector.translateToLocal("GT5U.machines.tier")
+ + ": "
+ + EnumChatFormatting.YELLOW
+ + VN[GT_Utility.getTier(getMaxInputVoltage())]
+ + EnumChatFormatting.RESET,
+ /* 5 */ StatCollector.translateToLocal("GT5U.multiblock.problems") + ": "
+ + EnumChatFormatting.RED
+ + (getIdealStatus() - getRepairStatus())
+ + EnumChatFormatting.RESET
+ + " "
+ + StatCollector.translateToLocal("GT5U.multiblock.efficiency")
+ + ": "
+ + EnumChatFormatting.YELLOW
+ + mEfficiency / 100.0F
+ + EnumChatFormatting.RESET
+ + " %",
+ /* 6 */ StatCollector.translateToLocal("GT5U.multiblock.pollution") + ": "
+ + EnumChatFormatting.GREEN
+ + mPollutionReduction
+ + EnumChatFormatting.RESET
+ + " %" };
+ }
+
+ @Override
+ public boolean isGivingInformation() {
+ return true;
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return supportsSlotAutomation(aIndex);
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return supportsSlotAutomation(aIndex);
+ }
+
+ protected ItemStack[] getCompactedInputs() {
+ // TODO: repalce method with a cleaner one
+ ArrayList<ItemStack> tInputList = getStoredInputs();
+ int tInputList_sS = tInputList.size();
+ for (int i = 0; i < tInputList_sS - 1; i++) {
+ for (int j = i + 1; j < tInputList_sS; j++) {
+ if (!GT_Utility.areStacksEqual(tInputList.get(i), tInputList.get(j))) continue;
+ if (tInputList.get(i).stackSize >= tInputList.get(j).stackSize) {
+ tInputList.remove(j--);
+ tInputList_sS = tInputList.size();
+ } else {
+ tInputList.remove(i--);
+ tInputList_sS = tInputList.size();
+ break;
+ }
+ }
+ }
+ return tInputList.toArray(new ItemStack[0]);
+ }
+
+ protected FluidStack[] getCompactedFluids() {
+ // TODO: repalce method with a cleaner one
+ ArrayList<FluidStack> tFluidList = getStoredFluids();
+ int tFluidList_sS = tFluidList.size();
+ for (int i = 0; i < tFluidList_sS - 1; i++) {
+ for (int j = i + 1; j < tFluidList_sS; j++) {
+ if (!GT_Utility.areFluidsEqual(tFluidList.get(i), tFluidList.get(j))) continue;
+
+ if (tFluidList.get(i).amount >= tFluidList.get(j).amount) {
+ tFluidList.remove(j--);
+ tFluidList_sS = tFluidList.size();
+ } else {
+ tFluidList.remove(i--);
+ tFluidList_sS = tFluidList.size();
+ break;
+ }
+ }
+ }
+ return tFluidList.toArray(new FluidStack[0]);
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currentTip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ final NBTTagCompound tag = accessor.getNBTData();
+
+ if (tag.getBoolean("incompleteStructure")) {
+ currentTip.add(RED + "** INCOMPLETE STRUCTURE **" + RESET);
+ }
+ String efficiency = RESET + " Efficiency: " + tag.getFloat("efficiency") + "%";
+ if (tag.getBoolean("hasProblems")) {
+ currentTip.add(RED + "** HAS PROBLEMS **" + efficiency);
+ } else if (!tag.getBoolean("incompleteStructure")) {
+ currentTip.add(GREEN + "Running Fine" + efficiency);
+ }
+
+ boolean isActive = tag.getBoolean("isActive");
+ if (isActive) {
+ long energyTier = tag.getLong("energyTier");
+ long actualEnergyUsage = tag.getLong("energyUsage");
+ if (energyTier > 0) {
+ if (actualEnergyUsage > 0) {
+ currentTip.add(
+ StatCollector.translateToLocalFormatted(
+ "GT5U.waila.energy.use_with_amperage",
+ formatNumbers(actualEnergyUsage),
+ GT_Utility.getAmperageForTier(actualEnergyUsage, (byte) energyTier),
+ GT_Utility.getColoredTierNameFromTier((byte) energyTier)));
+ } else if (actualEnergyUsage < 0) {
+ currentTip.add(
+ StatCollector.translateToLocalFormatted(
+ "GT5U.waila.energy.produce_with_amperage",
+ formatNumbers(-actualEnergyUsage),
+ GT_Utility.getAmperageForTier(-actualEnergyUsage, (byte) energyTier),
+ GT_Utility.getColoredTierNameFromTier((byte) energyTier)));
+ }
+ } else {
+ if (actualEnergyUsage > 0) {
+ currentTip.add(
+ StatCollector.translateToLocalFormatted(
+ "GT5U.waila.energy.use",
+ formatNumbers(actualEnergyUsage),
+ GT_Utility.getColoredTierNameFromVoltage(actualEnergyUsage)));
+ } else if (actualEnergyUsage < 0) {
+ currentTip.add(
+ StatCollector.translateToLocalFormatted(
+ "GT5U.waila.energy.produce",
+ formatNumbers(-actualEnergyUsage),
+ GT_Utility.getColoredTierNameFromVoltage(-actualEnergyUsage)));
+ }
+ }
+ }
+ currentTip.add(
+ GT_Waila.getMachineProgressString(isActive, tag.getInteger("maxProgress"), tag.getInteger("progress")));
+ // Show ns on the tooltip
+ if (GT_Mod.gregtechproxy.wailaAverageNS && tag.hasKey("averageNS")) {
+ int tAverageTime = tag.getInteger("averageNS");
+ currentTip.add("Average CPU load of ~" + formatNumbers(tAverageTime) + " ns");
+ }
+ super.getWailaBody(itemStack, currentTip, accessor, config);
+ }
+
+ public final void getMTEWailaBody(ItemStack itemStack, List<String> currentTip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ super.getWailaBody(itemStack, currentTip, accessor, config);
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+
+ tag.setBoolean("hasProblems", (getIdealStatus() - getRepairStatus()) > 0);
+ tag.setFloat("efficiency", mEfficiency / 100.0F);
+ tag.setInteger("progress", mProgresstime);
+ tag.setInteger("maxProgress", mMaxProgresstime);
+ tag.setBoolean("incompleteStructure", (getBaseMetaTileEntity().getErrorDisplayID() & 64) != 0);
+
+ final IGregTechTileEntity tileEntity = getBaseMetaTileEntity();
+ if (tileEntity != null) {
+ tag.setBoolean("isActive", tileEntity.isActive());
+ if (tileEntity.isActive()) {
+ if (mEUt < 0) tag.setLong("energyUsage", getActualEnergyUsage());
+ else tag.setLong("energyUsage", (long) -mEUt * mEfficiency / 10000);
+ tag.setLong("energyTier", getInputVoltageTier());
+ }
+ }
+
+ final GT_ClientPreference preference = GT_Mod.gregtechproxy.getClientPreference(player.getUniqueID());
+ if (preference != null && preference.isWailaAverageNSEnabled()) {
+ getBaseMetaTileEntity().startTimeStatistics();
+ int tAverageTime = 0;
+ int amountOfZero = 0;
+ for (int tTime : this.getBaseMetaTileEntity()
+ .getTimeStatistics()) {
+ tAverageTime += tTime;
+ if (tTime == 0) {
+ amountOfZero += 1;
+ }
+ }
+
+ // tick time zero means it has not been updated yet
+ int samples = getBaseMetaTileEntity().getTimeStatistics().length - amountOfZero;
+ if (samples > 0) {
+ tag.setInteger("averageNS", tAverageTime / samples);
+ }
+ }
+ }
+
+ protected void setMufflers(boolean state) {
+ for (GT_MetaTileEntity_Hatch_Muffler aMuffler : mMufflerHatches) {
+ final IGregTechTileEntity iGTTileEntity = aMuffler.getBaseMetaTileEntity();
+ if (iGTTileEntity != null && !iGTTileEntity.isDead()) {
+ iGTTileEntity.setActive(state);
+ }
+ }
+ }
+
+ @Override
+ public void onRemoval() {
+ super.onRemoval();
+ // Deactivate mufflers
+ setMufflers(false);
+ }
+
+ public List<GT_MetaTileEntity_Hatch> getExoticEnergyHatches() {
+ return mExoticEnergyHatches;
+ }
+
+ /**
+ * @return Returns true if there is 1 TT Energy Hatch OR up to 2 Energy Hatches
+ */
+ public boolean checkExoticAndNormalEnergyHatches() {
+ if (mExoticEnergyHatches.isEmpty() && mEnergyHatches.isEmpty()) {
+ return false;
+ }
+
+ if (!mExoticEnergyHatches.isEmpty()) {
+ if (!mEnergyHatches.isEmpty()) {
+ return false;
+ }
+
+ if (mExoticEnergyHatches.size() != 1) {
+ return false;
+ }
+ }
+
+ return mEnergyHatches.size() <= 2;
+ }
+
+ /**
+ * Checks if all the item / fluid outputs of the recipe can be outputted to the buses / hatches.
+ * If void protection is enabled, it also checks for {@link #protectsExcessItem()} and
+ * {@link #protectsExcessFluid()}, so you don't need to call them along with this method.
+ * <p>
+ * If you're using {@link GT_ParallelHelper}, it will handle void protection and return 0 parallel
+ * if all the output cannot be dumped into buses / hatches. In that case you won't use this method.
+ */
+ protected boolean canOutputAll(@Nonnull GT_Recipe recipe) {
+ return canOutputAll(recipe.mOutputs, recipe.mFluidOutputs);
+ }
+
+ /**
+ * Checks if all the items can be outputted to the output buses.
+ * If void protection is enabled, it also checks for {@link #protectsExcessItem()},
+ * so you don't need to call it along with this method.
+ */
+ @SuppressWarnings("BooleanMethodIsAlwaysInverted")
+ protected boolean canOutputAll(ItemStack[] items) {
+ return canOutputAll(items, null);
+ }
+
+ /**
+ * Checks if all the fluids can be outputted to the output hatches.
+ * If void protection is enabled, it also checks for {@link #protectsExcessFluid()},
+ * so you don't need to call it along with this method.
+ */
+ protected boolean canOutputAll(FluidStack[] fluids) {
+ return canOutputAll(null, fluids);
+ }
+
+ /**
+ * Checks if all the items / fluids can be outputted to output buses / hatches.
+ * If void protection is enabled, it also checks for {@link #protectsExcessItem()} and
+ * {@link #protectsExcessFluid()}, so you don't need to call them along with this method.
+ */
+ protected boolean canOutputAll(@Nullable ItemStack[] items, @Nullable FluidStack[] fluids) {
+ if (!protectsExcessItem() && !protectsExcessFluid()) {
+ return true;
+ }
+
+ VoidProtectionHelper voidProtectionHelper = new VoidProtectionHelper().setMachine(this)
+ .setItemOutputs(items)
+ .setFluidOutputs(fluids)
+ .build();
+ return voidProtectionHelper.getMaxParallel() > 0;
+ }
+
+ @Override
+ public boolean isAllowedToWork() {
+ return getBaseMetaTileEntity() != null && getBaseMetaTileEntity().isAllowedToWork();
+ }
+
+ @Override
+ public void disableWorking() {
+ if (getBaseMetaTileEntity() != null) {
+ getBaseMetaTileEntity().disableWorking();
+ }
+ }
+
+ @Override
+ public void enableWorking() {
+ if (getBaseMetaTileEntity() != null) {
+ getBaseMetaTileEntity().enableWorking();
+ }
+ }
+
+ public ItemStack getControllerSlot() {
+ return mInventory[getControllerSlotIndex()];
+ }
+
+ public final int getControllerSlotIndex() {
+ return 1;
+ }
+
+ // True if the slot with index aSlot may be interacted with through automation
+ protected boolean supportsSlotAutomation(int aSlot) {
+ return false;
+ }
+
+ @Override
+ public Pos2d getPowerSwitchButtonPos() {
+ return new Pos2d(174, 148);
+ }
+
+ @Override
+ public Pos2d getStructureUpdateButtonPos() {
+ return new Pos2d(174, 130);
+ }
+
+ @Override
+ public int getStructureUpdateTime() {
+ return mUpdate;
+ }
+
+ @Override
+ public void setStructureUpdateTime(int time) {
+ mUpdate = time;
+ }
+
+ @Override
+ public boolean supportsVoidProtection() {
+ return false;
+ }
+
+ @Override
+ public VoidingMode getVoidingMode() {
+ return voidingMode;
+ }
+
+ @Override
+ public void setVoidingMode(VoidingMode mode) {
+ this.voidingMode = mode;
+ }
+
+ @Override
+ public List<ItemStack> getItemOutputSlots(ItemStack[] toOutput) {
+ List<ItemStack> ret = new ArrayList<>();
+ for (final GT_MetaTileEntity_Hatch tBus : filterValidMTEs(mOutputBusses)) {
+ if (!(tBus instanceof GT_MetaTileEntity_Hatch_OutputBus_ME)) {
+ final IInventory tBusInv = tBus.getBaseMetaTileEntity();
+ for (int i = 0; i < tBusInv.getSizeInventory(); i++) {
+ final ItemStack stackInSlot = tBus.getStackInSlot(i);
+
+ if (stackInSlot == null && tBus instanceof IItemLockable lockable && lockable.isLocked()) {
+ // getItemOutputSlots is only used to calculate free room for the purposes of parallels and
+ // void protection. We can use a fake item stack here without creating weirdness in the output
+ // bus' actual inventory.
+ assert lockable.getLockedItem() != null;
+ ItemStack fakeItemStack = lockable.getLockedItem()
+ .copy();
+ fakeItemStack.stackSize = 0;
+ ret.add(fakeItemStack);
+ } else {
+ ret.add(stackInSlot);
+ }
+ }
+ }
+ }
+ return ret;
+ }
+
+ @Override
+ public List<? extends IFluidStore> getFluidOutputSlots(FluidStack[] toOutput) {
+ return filterValidMTEs(mOutputHatches);
+ }
+
+ /**
+ * Util method for DT-like structure to collect list of output hatches.
+ */
+ protected <T extends GT_MetaTileEntity_Hatch_Output> List<? extends IFluidStore> getFluidOutputSlotsByLayer(
+ FluidStack[] toOutput, List<List<T>> hatchesByLayer) {
+ List<IFluidStore> ret = new ArrayList<>();
+ for (int i = 0; i < toOutput.length; i++) {
+ if (i >= hatchesByLayer.size()) {
+ break;
+ }
+ FluidStack fluidOutputForLayer = toOutput[i];
+ for (GT_MetaTileEntity_Hatch_Output hatch : hatchesByLayer.get(i)) {
+ if (!hatch.isValid()) continue;
+ if (fluidOutputForLayer != null) {
+ ret.add(new OutputHatchWrapper(hatch, f -> GT_Utility.areFluidsEqual(f, fluidOutputForLayer)));
+ } else {
+ ret.add(hatch);
+ }
+ }
+ }
+ return ret;
+ }
+
+ @Override
+ public boolean canDumpItemToME() {
+ for (GT_MetaTileEntity_Hatch tHatch : filterValidMTEs(mOutputBusses)) {
+ if (tHatch instanceof GT_MetaTileEntity_Hatch_OutputBus_ME) {
+ if ((((GT_MetaTileEntity_Hatch_OutputBus_ME) tHatch).canAcceptItem())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean canDumpFluidToME() {
+ for (IFluidStore tHatch : getFluidOutputSlots(new FluidStack[0])) {
+ if (tHatch instanceof GT_MetaTileEntity_Hatch_Output_ME) {
+ if ((((GT_MetaTileEntity_Hatch_Output_ME) tHatch).canAcceptFluid())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Pos2d getVoidingModeButtonPos() {
+ return new Pos2d(8, 91);
+ }
+
+ @Override
+ public boolean supportsInputSeparation() {
+ return false;
+ }
+
+ @Override
+ public boolean isInputSeparationEnabled() {
+ return inputSeparation;
+ }
+
+ @Override
+ public void setInputSeparation(boolean enabled) {
+ this.inputSeparation = enabled;
+ }
+
+ @Override
+ public Pos2d getInputSeparationButtonPos() {
+ return new Pos2d(26, 91);
+ }
+
+ /**
+ * Creates the icon list for this machine. Override this and add the overlays to machineModeIcons in order.
+ */
+ public void setMachineModeIcons() {
+ machineModeIcons.add(GT_UITextures.OVERLAY_BUTTON_MACHINEMODE_DEFAULT);
+ machineModeIcons.add(GT_UITextures.OVERLAY_BUTTON_MACHINEMODE_DEFAULT);
+ }
+
+ /**
+ * Override this if you are a multi-machine and want a GUI button. You will also want to override
+ * setMachineModeIcons().
+ * Override nextMachineMode() if you have more than 2 modes.
+ */
+ @Override
+ public boolean supportsMachineModeSwitch() {
+ return false;
+ }
+
+ @Override
+ public int getMachineMode() {
+ return machineMode;
+ }
+
+ @Override
+ public UITexture getMachineModeIcon(int index) {
+ return machineModeIcons.get(index);
+ }
+
+ @Override
+ public void setMachineMode(int index) {
+ machineMode = index;
+ }
+
+ @Override
+ public int nextMachineMode() {
+ if (machineMode == 0) return 1;
+ else return 0;
+ }
+
+ @Override
+ public Pos2d getMachineModeSwitchButtonPos() {
+ return new Pos2d(80, 91);
+ }
+
+ @Override
+ public boolean supportsBatchMode() {
+ return false;
+ }
+
+ @Override
+ public boolean isBatchModeEnabled() {
+ return batchMode;
+ }
+
+ @Override
+ public void setBatchMode(boolean enabled) {
+ this.batchMode = enabled;
+ }
+
+ @Override
+ public Pos2d getBatchModeButtonPos() {
+ return new Pos2d(44, 91);
+ }
+
+ @Override
+ public boolean supportsSingleRecipeLocking() {
+ return false;
+ }
+
+ @Override
+ public boolean isRecipeLockingEnabled() {
+ return mLockedToSingleRecipe;
+ }
+
+ @Override
+ public void setRecipeLocking(boolean enabled) {
+ mLockedToSingleRecipe = enabled;
+ if (!enabled) {
+ setSingleRecipeCheck(null);
+ }
+ }
+
+ @Override
+ public void setSingleRecipeCheck(SingleRecipeCheck recipeCheck) {
+ mSingleRecipeCheck = recipeCheck;
+ }
+
+ @Override
+ public SingleRecipeCheck getSingleRecipeCheck() {
+ return mSingleRecipeCheck;
+ }
+
+ @Override
+ public Pos2d getRecipeLockingButtonPos() {
+ return new Pos2d(62, 91);
+ }
+
+ @Override
+ public int getGUIWidth() {
+ return 198;
+ }
+
+ @Override
+ public int getGUIHeight() {
+ return 192;
+ }
+
+ @Override
+ public void bindPlayerInventoryUI(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ builder.bindPlayerInventory(buildContext.getPlayer(), new Pos2d(7, 109), getGUITextureSet().getItemSlot());
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ builder.widget(
+ new DrawableWidget().setDrawable(GT_UITextures.PICTURE_SCREEN_BLACK)
+ .setPos(4, 4)
+ .setSize(190, 85));
+ final SlotWidget inventorySlot = new SlotWidget(inventoryHandler, 1);
+ builder.widget(
+ inventorySlot.setPos(173, 167)
+ .setBackground(GT_UITextures.SLOT_DARK_GRAY));
+
+ final DynamicPositionedColumn screenElements = new DynamicPositionedColumn();
+ drawTexts(screenElements, inventorySlot);
+ builder.widget(screenElements);
+
+ setMachineModeIcons();
+ builder.widget(createPowerSwitchButton(builder))
+ .widget(createVoidExcessButton(builder))
+ .widget(createInputSeparationButton(builder))
+ .widget(createModeSwitchButton(builder))
+ .widget(createBatchModeButton(builder))
+ .widget(createLockToSingleRecipeButton(builder))
+ .widget(createStructureUpdateButton(builder));
+ }
+
+ @Override
+ public void addGregTechLogo(ModularWindow.Builder builder) {}
+
+ protected boolean shouldDisplayCheckRecipeResult() {
+ return true;
+ }
+
+ public boolean shouldDisplayShutDownReason() {
+ return true;
+ }
+
+ protected final NumberFormatMUI numberFormat = new NumberFormatMUI();
+
+ protected String generateCurrentRecipeInfoString() {
+ StringBuffer ret = new StringBuffer(EnumChatFormatting.WHITE + "Progress: ");
+
+ numberFormat.setMinimumFractionDigits(2);
+ numberFormat.setMaximumFractionDigits(2);
+ numberFormat.format((double) mProgresstime / 20, ret);
+ ret.append("s / ");
+ numberFormat.format((double) mMaxProgresstime / 20, ret);
+ ret.append("s (");
+ numberFormat.setMinimumFractionDigits(1);
+ numberFormat.setMaximumFractionDigits(1);
+ numberFormat.format((double) mProgresstime / mMaxProgresstime * 100, ret);
+ ret.append("%)\n");
+ numberFormat.setMinimumFractionDigits(0);
+ numberFormat.setMaximumFractionDigits(2);
+
+ IntConsumer appendRate = (amount) -> {
+ double processPerTick = (double) amount / mMaxProgresstime * 20;
+ if (processPerTick > 1) {
+ ret.append(" (");
+ numberFormat.format(Math.round(processPerTick * 10) / 10.0, ret);
+ ret.append("/s)");
+ } else {
+ ret.append(" (");
+ numberFormat.format(Math.round(1 / processPerTick * 10) / 10.0, ret);
+ ret.append("s/ea)");
+ }
+ };
+
+ int lines = 0;
+ int MAX_LINES = 5;
+
+ if (mOutputItems != null) {
+ for (var item : mOutputItems) {
+ if (item == null) continue;
+ if (lines >= MAX_LINES) {
+ ret.append("...");
+ return ret.toString();
+ }
+ lines++;
+ ret.append(EnumChatFormatting.AQUA)
+ .append(item.getDisplayName())
+ .append(EnumChatFormatting.WHITE)
+ .append(" x ")
+ .append(EnumChatFormatting.GOLD);
+ numberFormat.format(item.stackSize, ret);
+ ret.append(EnumChatFormatting.WHITE);
+ appendRate.accept(item.stackSize);
+ ret.append('\n');
+ }
+ }
+ if (mOutputFluids != null) {
+ for (var fluid : mOutputFluids) {
+ if (fluid == null) continue;
+ if (lines >= MAX_LINES) {
+ ret.append("...");
+ return ret.toString();
+ }
+ lines++;
+ ret.append(EnumChatFormatting.AQUA)
+ .append(fluid.getLocalizedName())
+ .append(EnumChatFormatting.WHITE)
+ .append(" x ")
+ .append(EnumChatFormatting.GOLD);
+ numberFormat.format(fluid.amount, ret);
+ ret.append("L")
+ .append(EnumChatFormatting.WHITE);
+ appendRate.accept(fluid.amount);
+ ret.append('\n');
+ }
+ }
+ return ret.toString();
+ }
+
+ protected void drawTexts(DynamicPositionedColumn screenElements, SlotWidget inventorySlot) {
+ screenElements.setSynced(false)
+ .setSpace(0)
+ .setPos(10, 7);
+ if (supportsMachineModeSwitch()) {
+ screenElements.widget(
+ TextWidget.dynamicString(
+ () -> EnumChatFormatting.WHITE + GT_Utility.trans("400", "Running mode: ")
+ + EnumChatFormatting.GOLD
+ + getMachineModeName()));
+ }
+ screenElements
+ .widget(
+ new TextWidget(GT_Utility.trans("132", "Pipe is loose.")).setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setEnabled(widget -> !mWrench))
+ .widget(new FakeSyncWidget.BooleanSyncer(() -> mWrench, val -> mWrench = val));
+ screenElements
+ .widget(
+ new TextWidget(GT_Utility.trans("133", "Screws are loose.")).setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setEnabled(widget -> !mScrewdriver))
+ .widget(new FakeSyncWidget.BooleanSyncer(() -> mScrewdriver, val -> mScrewdriver = val));
+ screenElements
+ .widget(
+ new TextWidget(GT_Utility.trans("134", "Something is stuck.")).setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setEnabled(widget -> !mSoftHammer))
+ .widget(new FakeSyncWidget.BooleanSyncer(() -> mSoftHammer, val -> mSoftHammer = val));
+ screenElements
+ .widget(
+ new TextWidget(GT_Utility.trans("135", "Platings are dented.")).setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setEnabled(widget -> !mHardHammer))
+ .widget(new FakeSyncWidget.BooleanSyncer(() -> mHardHammer, val -> mHardHammer = val));
+ screenElements
+ .widget(
+ new TextWidget(GT_Utility.trans("136", "Circuitry burned out.")).setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setEnabled(widget -> !mSolderingTool))
+ .widget(new FakeSyncWidget.BooleanSyncer(() -> mSolderingTool, val -> mSolderingTool = val));
+ screenElements
+ .widget(
+ new TextWidget(GT_Utility.trans("137", "That doesn't belong there."))
+ .setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setEnabled(widget -> !mCrowbar))
+ .widget(new FakeSyncWidget.BooleanSyncer(() -> mCrowbar, val -> mCrowbar = val));
+ screenElements
+ .widget(
+ new TextWidget(GT_Utility.trans("138", "Incomplete Structure.")).setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setEnabled(widget -> !mMachine))
+ .widget(new FakeSyncWidget.BooleanSyncer(() -> mMachine, val -> mMachine = val));
+ screenElements.widget(
+ new TextWidget("Too Uncertain.").setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setEnabled(widget -> (getBaseMetaTileEntity().getErrorDisplayID() & 128) != 0));
+ screenElements.widget(
+ new TextWidget("Invalid Parameters.").setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setEnabled(widget -> (getBaseMetaTileEntity().getErrorDisplayID() & 256) != 0));
+
+ screenElements.widget(
+ new TextWidget(GT_Utility.trans("139", "Hit with Soft Mallet")).setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setEnabled(
+ widget -> getBaseMetaTileEntity().getErrorDisplayID() == 0 && !getBaseMetaTileEntity().isActive()))
+ .widget(
+ new FakeSyncWidget.IntegerSyncer(
+ () -> getBaseMetaTileEntity().getErrorDisplayID(),
+ val -> getBaseMetaTileEntity().setErrorDisplayID(val)))
+ .widget(
+ new FakeSyncWidget.BooleanSyncer(
+ () -> getBaseMetaTileEntity().isActive(),
+ val -> getBaseMetaTileEntity().setActive(val)));
+ screenElements.widget(
+ new TextWidget(GT_Utility.trans("140", "to (re-)start the Machine")).setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setEnabled(
+ widget -> getBaseMetaTileEntity().getErrorDisplayID() == 0 && !getBaseMetaTileEntity().isActive()));
+ screenElements.widget(
+ new TextWidget(GT_Utility.trans("141", "if it doesn't start.")).setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setEnabled(
+ widget -> getBaseMetaTileEntity().getErrorDisplayID() == 0 && !getBaseMetaTileEntity().isActive()));
+ screenElements.widget(
+ new TextWidget(GT_Utility.trans("142", "Running perfectly.")).setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setEnabled(
+ widget -> getBaseMetaTileEntity().getErrorDisplayID() == 0 && getBaseMetaTileEntity().isActive()));
+
+ screenElements.widget(
+ TextWidget.dynamicString(
+ () -> getBaseMetaTileEntity().getLastShutDownReason()
+ .getDisplayString())
+ .setSynced(false)
+ .setTextAlignment(Alignment.CenterLeft)
+ .setEnabled(
+ widget -> shouldDisplayShutDownReason() && !getBaseMetaTileEntity().isActive()
+ && GT_Utility.isStringValid(
+ getBaseMetaTileEntity().getLastShutDownReason()
+ .getDisplayString())
+ && getBaseMetaTileEntity().wasShutdown()))
+ .widget(
+ new ShutDownReasonSyncer(
+ () -> getBaseMetaTileEntity().getLastShutDownReason(),
+ reason -> getBaseMetaTileEntity().setShutDownReason(reason)))
+ .widget(
+ new FakeSyncWidget.BooleanSyncer(
+ () -> getBaseMetaTileEntity().wasShutdown(),
+ wasShutDown -> getBaseMetaTileEntity().setShutdownStatus(wasShutDown)));
+
+ screenElements.widget(
+ TextWidget.dynamicString(() -> checkRecipeResult.getDisplayString())
+ .setSynced(false)
+ .setTextAlignment(Alignment.CenterLeft)
+ .setEnabled(
+ widget -> shouldDisplayCheckRecipeResult()
+ && GT_Utility.isStringValid(checkRecipeResult.getDisplayString())
+ && (isAllowedToWork() || getBaseMetaTileEntity().isActive()
+ || checkRecipeResult.persistsOnShutdown())))
+ .widget(new CheckRecipeResultSyncer(() -> checkRecipeResult, (result) -> checkRecipeResult = result));
+
+ if (showRecipeTextInGUI()) {
+ // Display current recipe
+ screenElements.widget(
+ TextWidget.dynamicString(this::generateCurrentRecipeInfoString)
+ .setSynced(false)
+ .setTextAlignment(Alignment.CenterLeft)
+ .setEnabled(
+ widget -> (mOutputFluids != null && mOutputFluids.length > 0)
+ || (mOutputItems != null && mOutputItems.length > 0)))
+ .widget(
+ new FakeSyncWidget.ListSyncer<>(
+ () -> mOutputFluids != null ? Arrays.asList(mOutputFluids) : Collections.emptyList(),
+ val -> mOutputFluids = val.toArray(new FluidStack[0]),
+ NetworkUtils::writeFluidStack,
+ NetworkUtils::readFluidStack))
+ .widget(
+ new FakeSyncWidget.ListSyncer<>(
+ () -> mOutputItems != null ? Arrays.asList(mOutputItems) : Collections.emptyList(),
+ val -> mOutputItems = val.toArray(new ItemStack[0]),
+ NetworkUtils::writeItemStack,
+ NetworkUtils::readItemStack))
+ .widget(new FakeSyncWidget.IntegerSyncer(() -> mProgresstime, val -> mProgresstime = val))
+ .widget(new FakeSyncWidget.IntegerSyncer(() -> mMaxProgresstime, val -> mMaxProgresstime = val));
+ }
+
+ screenElements.widget(
+ new TextWidget(GT_Utility.trans("144", "Missing Turbine Rotor")).setDefaultColor(COLOR_TEXT_WHITE.get())
+ .setEnabled(widget -> {
+ if (getBaseMetaTileEntity().isAllowedToWork()) return false;
+ if (getBaseMetaTileEntity().getErrorDisplayID() == 0
+ && this instanceof GT_MetaTileEntity_LargeTurbine) {
+ final ItemStack tItem = inventorySlot.getMcSlot()
+ .getStack();
+ return tItem == null
+ || !(tItem.getItem() == GT_MetaGenerated_Tool_01.INSTANCE && tItem.getItemDamage() >= 170
+ && tItem.getItemDamage() <= 177);
+ }
+ return false;
+ }));
+ }
+
+ protected boolean showRecipeTextInGUI() {
+ return true;
+ }
+
+ @TestOnly
+ protected void setEnergyHatches(ArrayList<GT_MetaTileEntity_Hatch_Energy> EnergyHatches) {
+ this.mEnergyHatches = EnergyHatches;
+ }
+
+ @TestOnly
+ protected void setExoticEnergyHatches(List<GT_MetaTileEntity_Hatch> ExoticEnergyHatches) {
+ this.mExoticEnergyHatches = ExoticEnergyHatches;
+ }
+
+ public void fixAllIssues() {
+ mWrench = true;
+ mScrewdriver = true;
+ mSoftHammer = true;
+ mHardHammer = true;
+ mSolderingTool = true;
+ mCrowbar = true;
+ }
+
+ public boolean getDefaultHasMaintenanceChecks() {
+ return true;
+ }
+
+ public boolean shouldCheckMaintenance() {
+ return !disableMaintenance && hasMaintenanceChecks;
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_SpecialFilter.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_SpecialFilter.java
new file mode 100644
index 0000000000..1a71f17ec8
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_SpecialFilter.java
@@ -0,0 +1,134 @@
+package gregtech.api.metatileentity.implementations;
+
+import java.util.List;
+import java.util.function.Function;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizons.modularui.api.drawable.Text;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+import com.gtnewhorizons.modularui.common.internal.wrapper.BaseSlot;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+import com.gtnewhorizons.modularui.common.widget.SlotGroup;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+
+public abstract class GT_MetaTileEntity_SpecialFilter extends GT_MetaTileEntity_FilterBase {
+
+ private static final String ALLOW_NBT_TOOLTIP = "GT5U.machines.allow_nbt.tooltip";
+ private boolean allowNbt = false;
+
+ public GT_MetaTileEntity_SpecialFilter(int aID, String aName, String aNameRegional, int aTier,
+ String[] aDescription) {
+ // 9 buffer slot, 1 representation slot, 1 holo slot. last seems not needed...
+ super(aID, aName, aNameRegional, aTier, 11, aDescription);
+ }
+
+ public GT_MetaTileEntity_SpecialFilter(String aName, int aTier, int aInvSlotCount, String aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_SpecialFilter(String aName, int aTier, int aInvSlotCount, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aInvSlotCount, aDescription, aTextures);
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ super.saveNBTData(aNBT);
+ aNBT.setBoolean("bNBTAllowed", this.allowNbt);
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ super.loadNBTData(aNBT);
+ this.allowNbt = aNBT.getBoolean("bNBTAllowed");
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return (super.allowPutStack(aBaseMetaTileEntity, aIndex, side, aStack))
+ && ((this.allowNbt) || (!aStack.hasTagCompound()))
+ && (this.isStackAllowed(aStack) != this.invertFilter);
+ }
+
+ protected abstract boolean isStackAllowed(ItemStack aStack);
+
+ protected List<Text> getEmptySlotTooltip() {
+ return null;
+ }
+
+ protected Function<List<String>, List<String>> getItemStackReplacementTooltip() {
+ return list -> list;
+ }
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ super.addUIWidgets(builder, buildContext);
+ addAllowNbtButton(builder);
+ builder.widget(
+ new DrawableWidget().setDrawable(GT_UITextures.PICTURE_ARROW_24_WHITE.apply(27, false))
+ .setPos(6, 19)
+ .setSize(27, 24))
+ .widget(
+ new DrawableWidget().setDrawable(GT_UITextures.PICTURE_ARROW_24_BLUE.apply(42, true))
+ .setPos(53, 19)
+ .setSize(42, 24))
+ .widget(
+ new DrawableWidget().setDrawable(GT_UITextures.PICTURE_ARROW_24_RED.apply(19, true))
+ .setPos(152, 19)
+ .setSize(19, 24))
+ .widget(
+ createFilterIconSlot(BaseSlot.phantom(inventoryHandler, 9)).disableShiftInsert()
+ .setPos(34, 22)
+ .setBackground(GT_UITextures.BUTTON_STANDARD))
+ .widget(
+ SlotGroup.ofItemHandler(inventoryHandler, 3)
+ .endAtSlot(8)
+ .build()
+ .setPos(97, 4));
+ }
+
+ private void addAllowNbtButton(ModularWindow.Builder builder) {
+ builder.widget(
+ createToggleButton(
+ () -> allowNbt,
+ val -> allowNbt = val,
+ GT_UITextures.OVERLAY_BUTTON_NBT,
+ () -> mTooltipCache.getData(ALLOW_NBT_TOOLTIP)));
+ }
+
+ protected abstract SlotWidget createFilterIconSlot(BaseSlot slot);
+
+ protected abstract class FilterIconSlotWidget extends SlotWidget {
+
+ public FilterIconSlotWidget(BaseSlot slot) {
+ super(slot);
+ }
+
+ @Override
+ protected abstract void phantomClick(ClickData clickData, ItemStack cursorStack);
+
+ @Override
+ public void buildTooltip(List<Text> tooltip) {
+ super.buildTooltip(tooltip);
+ List<Text> emptySlotTooltip = getEmptySlotTooltip();
+ if (emptySlotTooltip != null) {
+ tooltip.addAll(emptySlotTooltip);
+ }
+ }
+
+ @Override
+ public Function<List<String>, List<String>> getOverwriteItemStackTooltip() {
+ return getItemStackReplacementTooltip();
+ }
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_TieredMachineBlock.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_TieredMachineBlock.java
new file mode 100644
index 0000000000..a08e49fd6c
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_TieredMachineBlock.java
@@ -0,0 +1,119 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.GT_Values.GT;
+import static gregtech.api.metatileentity.BaseTileEntity.BATTERY_SLOT_TOOLTIP;
+import static gregtech.api.metatileentity.BaseTileEntity.BATTERY_SLOT_TOOLTIP_ALT;
+import static gregtech.api.metatileentity.BaseTileEntity.TOOLTIP_DELAY;
+
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+
+import gregtech.api.enums.GT_Values;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.util.GT_Utility;
+
+public abstract class GT_MetaTileEntity_TieredMachineBlock extends MetaTileEntity {
+
+ /**
+ * Value between [0 - 9] to describe the Tier of this Machine. PLZ [0-15] works - READ! GT_Values class.
+ */
+ public final byte mTier;
+
+ /**
+ * A simple Description.
+ */
+ public final String[] mDescriptionArray;
+
+ /**
+ * Contains all Textures used by this Block.
+ */
+ public final ITexture[][][] mTextures;
+
+ public GT_MetaTileEntity_TieredMachineBlock(int aID, String aName, String aNameRegional, int aTier,
+ int aInvSlotCount, String aDescription, ITexture... aTextures) {
+ super(aID, aName, aNameRegional, aInvSlotCount);
+ mTier = (byte) Math.max(0, Math.min(aTier, 14));
+ mDescriptionArray = aDescription == null ? new String[0] : new String[] { aDescription };
+ // must always be the last call!
+ if (GT.isClientSide()) mTextures = getTextureSet(aTextures);
+ else mTextures = null;
+ }
+
+ public GT_MetaTileEntity_TieredMachineBlock(int aID, String aName, String aNameRegional, int aTier,
+ int aInvSlotCount, String[] aDescription, ITexture... aTextures) {
+ super(aID, aName, aNameRegional, aInvSlotCount);
+ mTier = (byte) Math.max(0, Math.min(aTier, 15));
+ mDescriptionArray = aDescription == null ? new String[0] : aDescription;
+
+ // must always be the last call!
+ if (GT.isClientSide()) mTextures = getTextureSet(aTextures);
+ else mTextures = null;
+ }
+
+ public GT_MetaTileEntity_TieredMachineBlock(String aName, int aTier, int aInvSlotCount, String aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aInvSlotCount);
+ mTier = (byte) aTier;
+ mDescriptionArray = aDescription == null ? new String[0] : new String[] { aDescription };
+ mTextures = aTextures;
+ }
+
+ public GT_MetaTileEntity_TieredMachineBlock(String aName, int aTier, int aInvSlotCount, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aInvSlotCount);
+ mTier = (byte) aTier;
+ mDescriptionArray = aDescription == null ? new String[0] : aDescription;
+ mTextures = aTextures;
+ }
+
+ @Override
+ public byte getTileEntityBaseType() {
+ return (byte) (Math.min(3, mTier <= 0 ? 0 : 1 + ((mTier - 1) / 4)));
+ }
+
+ @Override
+ public long getInputTier() {
+ return mTier;
+ }
+
+ @Override
+ public long getOutputTier() {
+ return mTier;
+ }
+
+ @Override
+ public String[] getDescription() {
+ return mDescriptionArray;
+ }
+
+ /**
+ * Used Client Side to get a Texture Set for this Block. Called after setting the Tier and the Description so that
+ * those two are accessible.
+ *
+ * @param aTextures is the optional Array you can give to the Constructor.
+ */
+ public abstract ITexture[][][] getTextureSet(ITexture[] aTextures);
+
+ protected SlotWidget createChargerSlot(int x, int y) {
+ final String batterySlotTooltipKey;
+ final Object[] batterySlotTooltipArgs;
+ final String pTier1 = GT_Utility.getColoredTierNameFromTier(mTier);
+ if (mTier == GT_Values.VN.length - 1) {
+ batterySlotTooltipKey = BATTERY_SLOT_TOOLTIP_ALT;
+ batterySlotTooltipArgs = new String[] { pTier1 };
+ } else {
+ batterySlotTooltipKey = BATTERY_SLOT_TOOLTIP;
+ batterySlotTooltipArgs = new String[] { pTier1, GT_Utility.getColoredTierNameFromTier((byte) (mTier + 1)) };
+ }
+ return createChargerSlot(x, y, batterySlotTooltipKey, batterySlotTooltipArgs);
+ }
+
+ protected SlotWidget createChargerSlot(int x, int y, String tooltipKey, Object[] tooltipArgs) {
+ return (SlotWidget) new SlotWidget(inventoryHandler, rechargerSlotStartIndex()).disableShiftInsert()
+ .setGTTooltip(() -> mTooltipCache.getData(tooltipKey, tooltipArgs))
+ .setTooltipShowUpDelay(TOOLTIP_DELAY)
+ .setBackground(getGUITextureSet().getItemSlot(), GT_UITextures.OVERLAY_SLOT_CHARGER)
+ .setPos(x, y);
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_TooltipMultiBlockBase.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_TooltipMultiBlockBase.java
new file mode 100644
index 0000000000..c12c7c1442
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_TooltipMultiBlockBase.java
@@ -0,0 +1,57 @@
+package gregtech.api.metatileentity.implementations;
+
+import java.util.concurrent.atomic.AtomicReferenceArray;
+
+import org.lwjgl.input.Keyboard;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.interfaces.ISecondaryDescribable;
+import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+
+/**
+ * A multiblock with tooltip {@link GT_Multiblock_Tooltip_Builder}
+ */
+public abstract class GT_MetaTileEntity_TooltipMultiBlockBase extends GT_MetaTileEntity_MultiBlockBase
+ implements ISecondaryDescribable {
+
+ private static final AtomicReferenceArray<GT_Multiblock_Tooltip_Builder> tooltips = new AtomicReferenceArray<>(
+ GregTech_API.METATILEENTITIES.length);
+
+ public GT_MetaTileEntity_TooltipMultiBlockBase(int aID, String aName, String aNameRegional) {
+ super(aID, aName, aNameRegional);
+ }
+
+ public GT_MetaTileEntity_TooltipMultiBlockBase(String aName) {
+ super(aName);
+ }
+
+ protected GT_Multiblock_Tooltip_Builder getTooltip() {
+ int tId = getBaseMetaTileEntity().getMetaTileID();
+ GT_Multiblock_Tooltip_Builder tooltip = tooltips.get(tId);
+ if (tooltip == null) {
+ tooltip = createTooltip();
+ tooltips.set(tId, tooltip);
+ }
+ return tooltip;
+ }
+
+ protected abstract GT_Multiblock_Tooltip_Builder createTooltip();
+
+ @Override
+ public String[] getDescription() {
+ return getCurrentDescription();
+ }
+
+ @Override
+ public boolean isDisplaySecondaryDescription() {
+ return Keyboard.isKeyDown(Keyboard.KEY_LSHIFT);
+ }
+
+ public String[] getPrimaryDescription() {
+ return getTooltip().getInformation();
+ }
+
+ public String[] getSecondaryDescription() {
+ return getTooltip().getStructureInformation();
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Transformer.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Transformer.java
new file mode 100644
index 0000000000..a8c26957f8
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Transformer.java
@@ -0,0 +1,325 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.GT_Values.V;
+import static mcp.mobius.waila.api.SpecialChars.BLUE;
+import static mcp.mobius.waila.api.SpecialChars.GOLD;
+import static mcp.mobius.waila.api.SpecialChars.GREEN;
+import static mcp.mobius.waila.api.SpecialChars.RED;
+import static mcp.mobius.waila.api.SpecialChars.RESET;
+
+import java.util.List;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import cofh.api.energy.IEnergyProvider;
+import cofh.api.energy.IEnergyStorage;
+import crazypants.enderio.machine.capbank.TileCapBank;
+import crazypants.enderio.machine.capbank.network.ICapBankNetwork;
+import crazypants.enderio.power.IPowerContainer;
+import gregtech.GT_Mod;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.Textures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.util.GT_Utility;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+/**
+ * NEVER INCLUDE THIS FILE IN YOUR MOD!!!
+ * <p/>
+ * This is the main construct for my Basic Machines such as the Automatic Extractor Extend this class to make a simple
+ * Machine
+ */
+public class GT_MetaTileEntity_Transformer extends GT_MetaTileEntity_TieredMachineBlock {
+
+ public GT_MetaTileEntity_Transformer(int aID, String aName, String aNameRegional, int aTier, String aDescription) {
+ super(aID, aName, aNameRegional, aTier, 0, aDescription);
+ }
+
+ public GT_MetaTileEntity_Transformer(String aName, int aTier, String aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 0, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Transformer(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 0, aDescription, aTextures);
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+ ITexture[][][] rTextures = new ITexture[12][17][];
+ for (byte i = -1; i < 16; i++) {
+ rTextures[0][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1],
+ Textures.BlockIcons.OVERLAYS_ENERGY_OUT[mTier] };
+ rTextures[1][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1],
+ Textures.BlockIcons.OVERLAYS_ENERGY_OUT[mTier] };
+ rTextures[2][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1],
+ Textures.BlockIcons.OVERLAYS_ENERGY_OUT[mTier] };
+ rTextures[3][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1],
+ Textures.BlockIcons.OVERLAYS_ENERGY_IN_MULTI[mTier] };
+ rTextures[4][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1],
+ Textures.BlockIcons.OVERLAYS_ENERGY_IN_MULTI[mTier] };
+ rTextures[5][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1],
+ Textures.BlockIcons.OVERLAYS_ENERGY_IN_MULTI[mTier] };
+ rTextures[6][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1],
+ Textures.BlockIcons.OVERLAYS_ENERGY_IN[mTier] };
+ rTextures[7][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1],
+ Textures.BlockIcons.OVERLAYS_ENERGY_IN[mTier] };
+ rTextures[8][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1],
+ Textures.BlockIcons.OVERLAYS_ENERGY_IN[mTier] };
+ rTextures[9][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1],
+ Textures.BlockIcons.OVERLAYS_ENERGY_OUT_MULTI[mTier] };
+ rTextures[10][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1],
+ Textures.BlockIcons.OVERLAYS_ENERGY_OUT_MULTI[mTier] };
+ rTextures[11][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1],
+ Textures.BlockIcons.OVERLAYS_ENERGY_OUT_MULTI[mTier] };
+ }
+ return rTextures;
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection side,
+ ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) {
+ return mTextures[Math.min(2, side.ordinal()) + (side == facingDirection ? 3 : 0)
+ + (baseMetaTileEntity.isAllowedToWork() ? 0 : 6)][colorIndex + 1];
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Transformer(mName, mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return true;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnetInput() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnetOutput() {
+ return true;
+ }
+
+ @Override
+ public boolean isInputFacing(ForgeDirection side) {
+ ForgeDirection blockFrontFacing = getBaseMetaTileEntity().getFrontFacing();
+
+ if (getBaseMetaTileEntity().isAllowedToWork()) {
+ return side == blockFrontFacing;
+ } else {
+ return side != blockFrontFacing;
+ }
+ }
+
+ @Override
+ public boolean isOutputFacing(ForgeDirection side) {
+ return !isInputFacing(side);
+ }
+
+ @Override
+ public boolean isTeleporterCompatible() {
+ return false;
+ }
+
+ @Override
+ public long getMinimumStoredEU() {
+ return V[mTier + 1];
+ }
+
+ @Override
+ public long maxEUStore() {
+ return Math.max(512L, 1L << (mTier + 2)) + V[mTier + 1] * 4L;
+ }
+
+ @Override
+ public long maxEUInput() {
+ return V[getBaseMetaTileEntity().isAllowedToWork() ? mTier + 1 : mTier];
+ }
+
+ @Override
+ public long maxEUOutput() {
+ return V[getBaseMetaTileEntity().isAllowedToWork() ? mTier : mTier + 1];
+ }
+
+ @Override
+ public long maxAmperesOut() {
+ return getBaseMetaTileEntity().isAllowedToWork() ? 4 : 1;
+ }
+
+ @Override
+ public long maxAmperesIn() {
+ return getBaseMetaTileEntity().isAllowedToWork() ? 1 : 4;
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (aBaseMetaTileEntity.isServerSide() && GregTech_API.mInputRF) {
+ aBaseMetaTileEntity.setActive(aBaseMetaTileEntity.isAllowedToWork());
+ for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
+ if (aBaseMetaTileEntity.getStoredEU() >= aBaseMetaTileEntity.getEUCapacity()) break;
+ if (!aBaseMetaTileEntity.inputEnergyFrom(side)) continue;
+ final TileEntity tTileEntity = aBaseMetaTileEntity.getTileEntityAtSide(side);
+ if (tTileEntity instanceof IEnergyProvider energyProvider
+ && energyProvider.extractEnergy(side.getOpposite(), 1, true) == 1) {
+ long tEU = ((IEnergyProvider) tTileEntity).extractEnergy(
+ side.getOpposite(),
+ GT_Utility.safeInt(maxEUInput() * 100L / GregTech_API.mRFtoEU),
+ false);
+ tEU = tEU * GregTech_API.mRFtoEU / 100;
+ aBaseMetaTileEntity.injectEnergyUnits(ForgeDirection.UNKNOWN, Math.min(tEU, maxEUInput()), 1);
+ } else if (tTileEntity instanceof IEnergyStorage energyStorage
+ && energyStorage.extractEnergy(1, true) == 1) {
+ long tEU = ((IEnergyStorage) tTileEntity)
+ .extractEnergy(GT_Utility.safeInt(maxEUInput() * 100L / GregTech_API.mRFtoEU), false);
+ tEU = tEU * GregTech_API.mRFtoEU / 100;
+ aBaseMetaTileEntity.injectEnergyUnits(ForgeDirection.UNKNOWN, Math.min(tEU, maxEUInput()), 1);
+ } else if (GregTech_API.meIOLoaded && tTileEntity instanceof IPowerContainer powerContainer
+ && powerContainer.getEnergyStored() > 0) {
+ final int storedRF = powerContainer.getEnergyStored();
+ final int extractRF = GT_Utility.safeInt(maxEUInput() * 100L / GregTech_API.mRFtoEU);
+ long tEU = 0;
+ if (tTileEntity instanceof TileCapBank capBank) {
+ ICapBankNetwork network = capBank.getNetwork();
+ if (network != null && network.getEnergyStoredL() > 0) {
+ tEU = Math.min(
+ (Math.min(
+ Math.min(network.getEnergyStoredL(), storedRF - extractRF),
+ network.getMaxOutput())) * (long) GregTech_API.mRFtoEU / 100L,
+ maxEUInput());
+ network.addEnergy(GT_Utility.safeInt(-(tEU * 100 / GregTech_API.mRFtoEU)));
+ }
+ } else {
+ if (storedRF > extractRF) {
+ powerContainer.setEnergyStored(storedRF - extractRF);
+ tEU = maxEUInput();
+ } else {
+ powerContainer.setEnergyStored(0);
+ tEU = storedRF * (long) GregTech_API.mRFtoEU / 100L;
+ }
+ }
+ aBaseMetaTileEntity
+ .injectEnergyUnits(ForgeDirection.UNKNOWN, Math.min(tEU, maxEUInput()), 1);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void saveNBTData(NBTTagCompound aNBT) {
+ //
+ }
+
+ @Override
+ public void loadNBTData(NBTTagCompound aNBT) {
+ //
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean hasAlternativeModeText() {
+ return true;
+ }
+
+ @Override
+ public String getAlternativeModeText() {
+ return (getBaseMetaTileEntity().isAllowedToWork() ? GT_Utility.trans("145", "Step Down, In: ")
+ : GT_Utility.trans("146", "Step Up, In: ")) + maxEUInput()
+ + GT_Utility.trans("148", "V ")
+ + maxAmperesIn()
+ + GT_Utility.trans("147", "A, Out: ")
+ + maxEUOutput()
+ + GT_Utility.trans("148", "V ")
+ + maxAmperesOut()
+ + GT_Utility.trans("149", "A");
+ }
+
+ @Override
+ public boolean shouldJoinIc2Enet() {
+ return true;
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ final ForgeDirection facing = getBaseMetaTileEntity().getFrontFacing();
+ final NBTTagCompound tag = accessor.getNBTData();
+ final ForgeDirection side = accessor.getSide();
+ final boolean allowedToWork = tag.getBoolean("isAllowedToWork");
+
+ final byte inputTier = GT_Utility.getTier(tag.getLong("maxEUInput"));
+ final byte outputTier = GT_Utility.getTier(tag.getLong("maxEUOutput"));
+
+ currenttip.add(
+ String.format(
+ "%s %s(%dA) -> %s(%dA)",
+ (allowedToWork ? (GREEN + "Step Down") : (RED + "Step Up")) + RESET,
+ GT_Mod.gregtechproxy.mWailaTransformerVoltageTier ? GT_Utility.getColoredTierNameFromTier(inputTier)
+ : tag.getLong("maxEUInput"),
+ tag.getLong("maxAmperesIn"),
+ GT_Mod.gregtechproxy.mWailaTransformerVoltageTier ? GT_Utility.getColoredTierNameFromTier(outputTier)
+ : tag.getLong("maxEUOutput"),
+ tag.getLong("maxAmperesOut")));
+
+ if ((side == facing && allowedToWork) || (side != facing && !allowedToWork)) {
+ currenttip.add(
+ String.format(
+ GOLD + "Input:" + RESET + " %s(%dA)",
+ GT_Mod.gregtechproxy.mWailaTransformerVoltageTier ? GT_Utility.getColoredTierNameFromTier(inputTier)
+ : tag.getLong("maxEUInput"),
+ tag.getLong("maxAmperesIn")));
+ } else {
+ currenttip.add(
+ String.format(
+ BLUE + "Output:" + RESET + " %s(%dA)",
+ GT_Mod.gregtechproxy.mWailaTransformerVoltageTier
+ ? GT_Utility.getColoredTierNameFromTier(outputTier)
+ : tag.getLong("maxEUOutput"),
+ tag.getLong("maxAmperesOut")));
+ }
+
+ super.getWailaBody(itemStack, currenttip, accessor, config);
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ super.getWailaNBTData(player, tile, tag, world, x, y, z);
+ tag.setBoolean("isAllowedToWork", getBaseMetaTileEntity().isAllowedToWork());
+ tag.setLong("maxEUInput", maxEUInput());
+ tag.setLong("maxAmperesIn", maxAmperesIn());
+ tag.setLong("maxEUOutput", maxEUOutput());
+ tag.setLong("maxAmperesOut", maxAmperesOut());
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_WetTransformer.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_WetTransformer.java
new file mode 100644
index 0000000000..f865a5ea8a
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_WetTransformer.java
@@ -0,0 +1,93 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.GT_Values.V;
+
+import net.minecraft.util.EnumChatFormatting;
+
+import org.apache.commons.lang3.ArrayUtils;
+
+import gregtech.api.enums.Textures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+
+public class GT_MetaTileEntity_WetTransformer extends GT_MetaTileEntity_Transformer {
+
+ public GT_MetaTileEntity_WetTransformer(int aID, String aName, String aNameRegional, int aTier,
+ String aDescription) {
+ super(aID, aName, aNameRegional, aTier, aDescription);
+ }
+
+ public GT_MetaTileEntity_WetTransformer(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ @Override
+ public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_WetTransformer(mName, mTier, mDescriptionArray, mTextures);
+ }
+
+ @Override
+ public ITexture[][][] getTextureSet(ITexture[] aTextures) {
+ ITexture[][][] rTextures = new ITexture[12][17][];
+ for (byte b = -1; b < 16; b++) {
+ rTextures[0][b + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][b + 1],
+ Textures.BlockIcons.OVERLAYS_ENERGY_OUT_MULTI[mTier] };
+ rTextures[1][b + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][b + 1],
+ Textures.BlockIcons.OVERLAYS_ENERGY_OUT_MULTI[mTier] };
+ rTextures[2][b + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][b + 1],
+ Textures.BlockIcons.OVERLAYS_ENERGY_OUT_MULTI[mTier] };
+ rTextures[3][b + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][b + 1],
+ Textures.BlockIcons.OVERLAYS_ENERGY_IN_POWER[mTier + 1] };
+ rTextures[4][b + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][b + 1],
+ Textures.BlockIcons.OVERLAYS_ENERGY_IN_POWER[mTier + 1] };
+ rTextures[5][b + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][b + 1],
+ Textures.BlockIcons.OVERLAYS_ENERGY_IN_POWER[mTier + 1] };
+ rTextures[6][b + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][b + 1],
+ Textures.BlockIcons.OVERLAYS_ENERGY_IN_MULTI[mTier] };
+ rTextures[7][b + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][b + 1],
+ Textures.BlockIcons.OVERLAYS_ENERGY_IN_MULTI[mTier] };
+ rTextures[8][b + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][b + 1],
+ Textures.BlockIcons.OVERLAYS_ENERGY_IN_MULTI[mTier] };
+ rTextures[9][b + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][b + 1],
+ Textures.BlockIcons.OVERLAYS_ENERGY_OUT_POWER[mTier + 1] };
+ rTextures[10][b + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][b + 1],
+ Textures.BlockIcons.OVERLAYS_ENERGY_OUT_POWER[mTier + 1] };
+ rTextures[11][b + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][b + 1],
+ Textures.BlockIcons.OVERLAYS_ENERGY_OUT_POWER[mTier + 1] };
+ }
+ return rTextures;
+ }
+
+ @Override
+ public String[] getDescription() {
+ return ArrayUtils.addAll(
+ mDescriptionArray,
+ "Accepts 16A and outputs 64A",
+ EnumChatFormatting.BLUE + "Tec"
+ + EnumChatFormatting.DARK_BLUE
+ + "Tech"
+ + EnumChatFormatting.BLUE
+ + ": Interdimensional");
+ }
+
+ @Override
+ public long getMinimumStoredEU() {
+ return V[mTier + 1];
+ }
+
+ @Override
+ public long maxEUStore() {
+ return 512L + V[mTier + 1] * 128L;
+ }
+
+ @Override
+ public long maxAmperesOut() {
+ return getBaseMetaTileEntity().isAllowedToWork() ? 64 : 16;
+ }
+
+ @Override
+ public long maxAmperesIn() {
+ return getBaseMetaTileEntity().isAllowedToWork() ? 16 : 64;
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Wireless_Dynamo.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Wireless_Dynamo.java
new file mode 100644
index 0000000000..a04f5cd986
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Wireless_Dynamo.java
@@ -0,0 +1,146 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.GT_Values.AuthorColen;
+import static gregtech.api.enums.GT_Values.V;
+import static gregtech.common.misc.WirelessNetworkManager.addEUToGlobalEnergyMap;
+import static gregtech.common.misc.WirelessNetworkManager.strongCheckOrAddUser;
+
+import java.util.UUID;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.api.enums.Textures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.interfaces.tileentity.IWirelessEnergyHatchInformation;
+import gregtech.api.metatileentity.MetaTileEntity;
+
+public class GT_MetaTileEntity_Wireless_Dynamo extends GT_MetaTileEntity_Hatch_Dynamo
+ implements IWirelessEnergyHatchInformation {
+
+ private UUID owner_uuid;
+
+ public GT_MetaTileEntity_Wireless_Dynamo(String aName, byte aTier, String[] aDescription,
+ ITexture[][][] aTextures) {
+ super(aName, aTier, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Wireless_Dynamo(int aID, String aName, String aNameRegional, int aTier) {
+ super(aID, aName, aNameRegional, aTier, new String[] { "" });
+ }
+
+ @Override
+ public ITexture[] getTexturesActive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, Textures.BlockIcons.OVERLAYS_ENERGY_IN_MULTI_WIRELESS_ON[mTier] };
+ }
+
+ @Override
+ public ITexture[] getTexturesInactive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, Textures.BlockIcons.OVERLAYS_ENERGY_IN_MULTI_WIRELESS_ON[mTier] };
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return true;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return true;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnetOutput() {
+ return false;
+ }
+
+ @Override
+ public boolean isInputFacing(ForgeDirection side) {
+ return side == getBaseMetaTileEntity().getFrontFacing();
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return false;
+ }
+
+ @Override
+ public long getMinimumStoredEU() {
+ return 2 * V[mTier];
+ }
+
+ @Override
+ public long maxEUOutput() {
+ return V[mTier];
+ }
+
+ @Override
+ public long maxEUStore() {
+ return totalStorage(V[mTier]);
+ }
+
+ @Override
+ public String[] getDescription() {
+ return new String[] { EnumChatFormatting.GRAY + "Stores energy globally in a network, up to 2^(2^31) EU.",
+ EnumChatFormatting.GRAY + "Does not connect to wires. This block accepts EU into the network.",
+ AuthorColen };
+ }
+
+ @Override
+ public long maxAmperesOut() {
+ return 2;
+ }
+
+ @Override
+ public ConnectionType getConnectionType() {
+ return ConnectionType.WIRELESS;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Wireless_Dynamo(mName, mTier, new String[] { "" }, mTextures);
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public void onPreTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onPreTick(aBaseMetaTileEntity, aTick);
+
+ if (aBaseMetaTileEntity.isServerSide()) {
+
+ // On first tick find the player name and attempt to add them to the map.
+ if (aTick == 1) {
+
+ // UUID and username of the owner.
+ owner_uuid = aBaseMetaTileEntity.getOwnerUuid();
+
+ strongCheckOrAddUser(owner_uuid);
+ }
+
+ // Every ticks_between_energy_addition ticks change the energy content of the machine.
+ if (aTick % ticks_between_energy_addition == 0L) {
+ addEUToGlobalEnergyMap(owner_uuid, getEUVar());
+ setEUVar(0L);
+ }
+ }
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Wireless_Hatch.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Wireless_Hatch.java
new file mode 100644
index 0000000000..bf624eadd7
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Wireless_Hatch.java
@@ -0,0 +1,168 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.GT_Values.AuthorColen;
+import static gregtech.api.enums.GT_Values.V;
+import static gregtech.common.misc.WirelessNetworkManager.addEUToGlobalEnergyMap;
+import static java.lang.Long.min;
+
+import java.math.BigInteger;
+import java.util.UUID;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.api.enums.Textures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.interfaces.tileentity.IWirelessEnergyHatchInformation;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.common.misc.spaceprojects.SpaceProjectManager;
+
+public class GT_MetaTileEntity_Wireless_Hatch extends GT_MetaTileEntity_Hatch_Energy
+ implements IWirelessEnergyHatchInformation {
+
+ private final BigInteger eu_transferred_per_operation = BigInteger
+ .valueOf(2 * V[mTier] * ticks_between_energy_addition);
+ private final long eu_transferred_per_operation_long = eu_transferred_per_operation.longValue();
+
+ private UUID owner_uuid;
+
+ public GT_MetaTileEntity_Wireless_Hatch(String aName, byte aTier, String[] aDescription, ITexture[][][] aTextures) {
+ super(aName, aTier, 0, aDescription, aTextures);
+ }
+
+ public GT_MetaTileEntity_Wireless_Hatch(int aID, String aName, String aNameRegional, int aTier) {
+ super(aID, aName, aNameRegional, aTier, new String[] { "" });
+ }
+
+ @Override
+ public String[] getDescription() {
+ return new String[] { EnumChatFormatting.GRAY + "Stores energy globally in a network, up to 2^(2^31) EU.",
+ EnumChatFormatting.GRAY + "Does not connect to wires. This block withdraws EU from the network.",
+ AuthorColen };
+ }
+
+ @Override
+ public ITexture[] getTexturesActive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, Textures.BlockIcons.OVERLAYS_ENERGY_IN_MULTI_WIRELESS_ON[mTier] };
+ }
+
+ @Override
+ public ITexture[] getTexturesInactive(ITexture aBaseTexture) {
+ return new ITexture[] { aBaseTexture, Textures.BlockIcons.OVERLAYS_ENERGY_IN_MULTI_WIRELESS_ON[mTier] };
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return true;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return true;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnetInput() {
+ return false;
+ }
+
+ @Override
+ public boolean isInputFacing(ForgeDirection side) {
+ return side == getBaseMetaTileEntity().getFrontFacing();
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return false;
+ }
+
+ @Override
+ public long getMinimumStoredEU() {
+ return 2 * V[mTier];
+ }
+
+ @Override
+ public long maxEUInput() {
+ return V[mTier];
+ }
+
+ @Override
+ public long maxEUStore() {
+ return totalStorage(V[mTier]);
+ }
+
+ @Override
+ public long maxAmperesIn() {
+ return 2;
+ }
+
+ @Override
+ public ConnectionType getConnectionType() {
+ return ConnectionType.WIRELESS;
+ }
+
+ @Override
+ public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {
+ return new GT_MetaTileEntity_Wireless_Hatch(mName, mTier, new String[] { "" }, mTextures);
+ }
+
+ @Override
+ public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ @Override
+ public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ super.onFirstTick(aBaseMetaTileEntity);
+
+ if (!aBaseMetaTileEntity.isServerSide()) return;
+
+ // UUID of the owner.
+ owner_uuid = aBaseMetaTileEntity.getOwnerUuid();
+
+ SpaceProjectManager.checkOrCreateTeam(owner_uuid);;
+
+ tryFetchingEnergy();
+ }
+
+ @Override
+ public void onPreTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+
+ super.onPreTick(aBaseMetaTileEntity, aTick);
+
+ if (aBaseMetaTileEntity.isServerSide()) {
+ // This is set up in a way to be as optimised as possible. If a user has a relatively plentiful energy
+ // network
+ // it should make no difference to them. Minimising the number of operations on BigInteger is essential.
+
+ // Every ticks_between_energy_addition add eu_transferred_per_operation to internal EU storage from network.
+ if (aTick % ticks_between_energy_addition == 0L) {
+ tryFetchingEnergy();
+ }
+ }
+ }
+
+ private void tryFetchingEnergy() {
+ long currentEU = getBaseMetaTileEntity().getStoredEU();
+ long maxEU = maxEUStore();
+ long euToTransfer = min(maxEU - currentEU, eu_transferred_per_operation_long);
+ if (euToTransfer <= 0) return; // nothing to transfer
+ if (!addEUToGlobalEnergyMap(owner_uuid, -euToTransfer)) return;
+ setEUVar(currentEU + euToTransfer);
+ }
+}