aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/gregtech/api/metatileentity
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/gregtech/api/metatileentity')
-rw-r--r--src/main/java/gregtech/api/metatileentity/BaseMetaPipeEntity.java1416
-rw-r--r--src/main/java/gregtech/api/metatileentity/BaseMetaTileEntity.java2538
-rw-r--r--src/main/java/gregtech/api/metatileentity/BaseTileEntity.java979
-rw-r--r--src/main/java/gregtech/api/metatileentity/CommonMetaTileEntity.java340
-rw-r--r--src/main/java/gregtech/api/metatileentity/CoverableTileEntity.java803
-rw-r--r--src/main/java/gregtech/api/metatileentity/GregTechTileClientEvents.java13
-rw-r--r--src/main/java/gregtech/api/metatileentity/MetaPipeEntity.java1029
-rw-r--r--src/main/java/gregtech/api/metatileentity/MetaTileEntity.java1326
-rw-r--r--src/main/java/gregtech/api/metatileentity/TileIC2EnergySink.java119
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Cable.java679
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Fluid.java991
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Frame.java150
-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.java441
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicGenerator.java344
-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.java1568
-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.java820
-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.java348
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Buffer.java568
-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.java157
-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.java201
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_InputBus.java323
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Maintenance.java464
-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.java305
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Output.java502
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_OutputBus.java202
-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_MultiBlockBase.java2540
-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.java126
-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_Wireless_Dynamo.java146
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Wireless_Hatch.java168
46 files changed, 22971 insertions, 0 deletions
diff --git a/src/main/java/gregtech/api/metatileentity/BaseMetaPipeEntity.java b/src/main/java/gregtech/api/metatileentity/BaseMetaPipeEntity.java
new file mode 100644
index 0000000000..1dc0e34d53
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/BaseMetaPipeEntity.java
@@ -0,0 +1,1416 @@
+package gregtech.api.metatileentity;
+
+import static gregtech.GT_Mod.GT_FML_LOGGER;
+import static gregtech.api.enums.GT_Values.NW;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+
+import net.minecraft.block.Block;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.init.Items;
+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.Fluid;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.fluids.FluidTankInfo;
+import net.minecraftforge.fluids.IFluidHandler;
+
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.GT_Values;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.enums.Textures;
+import gregtech.api.graphs.Lock;
+import gregtech.api.graphs.Node;
+import gregtech.api.graphs.paths.NodePath;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IConnectable;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IDebugableTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.interfaces.tileentity.IPipeRenderedTileEntity;
+import gregtech.api.net.GT_Packet_TileEntity;
+import gregtech.api.objects.GT_ItemStack;
+import gregtech.api.util.GT_CoverBehaviorBase;
+import gregtech.api.util.GT_Log;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_OreDictUnificator;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.covers.CoverInfo;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+/**
+ * NEVER INCLUDE THIS FILE IN YOUR MOD!!!
+ * <p/>
+ * This is the main TileEntity for EVERYTHING.
+ */
+public class BaseMetaPipeEntity extends CommonMetaTileEntity
+ implements IGregTechTileEntity, IPipeRenderedTileEntity, IDebugableTileEntity {
+
+ public byte mConnections = IConnectable.NO_CONNECTION;
+ protected MetaPipeEntity mMetaTileEntity;
+ private final int[] mTimeStatistics = new int[GregTech_API.TICKS_FOR_LAG_AVERAGING];
+ private boolean hasTimeStatisticsStarted;
+ private boolean mWorkUpdate = false, mWorks = true;
+ private byte mColor = 0, oColor = 0, oStrongRedstone = 0, oRedstoneData = 63, oTextureData = 0, oUpdateData = 0,
+ mLagWarningCount = 0;
+ private int oX = 0, oY = 0, oZ = 0, mTimeStatisticsIndex = 0;
+ protected Node node;
+ protected NodePath nodePath;
+
+ public Node getNode() {
+ return node;
+ }
+
+ public void setNode(Node node) {
+ this.node = node;
+ }
+
+ public NodePath getNodePath() {
+ return nodePath;
+ }
+
+ public void setNodePath(NodePath nodePath) {
+ this.nodePath = nodePath;
+ }
+
+ public void addToLock(TileEntity tileEntity, ForgeDirection side) {
+ if (node != null) {
+ final Lock lock = node.locks[side.ordinal()];
+ if (lock != null) {
+ lock.addTileEntity(tileEntity);
+ }
+ } else if (nodePath != null) {
+ nodePath.lock.addTileEntity(tileEntity);
+ }
+ }
+
+ public void removeFromLock(TileEntity tileEntity, ForgeDirection side) {
+ if (node != null) {
+ final Lock lock = node.locks[side.ordinal()];
+ if (lock != null) {
+ lock.removeTileEntity(tileEntity);
+ }
+ } else if (nodePath != null) {
+ nodePath.lock.removeTileEntity(tileEntity);
+ }
+ }
+
+ public void reloadLocks() {
+ final IMetaTileEntity meta = getMetaTileEntity();
+ if (meta instanceof MetaPipeEntity) {
+ ((MetaPipeEntity) meta).reloadLocks();
+ }
+ }
+
+ public BaseMetaPipeEntity() {}
+
+ @Override
+ public void writeToNBT(NBTTagCompound aNBT) {
+ try {
+ super.writeToNBT(aNBT);
+ } catch (Throwable e) {
+ GT_FML_LOGGER.error("Encountered CRITICAL ERROR while saving MetaTileEntity", e);
+ }
+ try {
+ aNBT.setInteger("mID", mID);
+ writeCoverNBT(aNBT, false);
+ aNBT.setByte("mConnections", mConnections);
+ aNBT.setByte("mColor", mColor);
+ aNBT.setBoolean("mWorks", !mWorks);
+ } catch (Throwable e) {
+ GT_FML_LOGGER.error("Encountered CRITICAL ERROR while saving MetaTileEntity", e);
+ }
+ saveMetaTileNBT(aNBT);
+ }
+
+ @Override
+ public void readFromNBT(NBTTagCompound aNBT) {
+ super.readFromNBT(aNBT);
+ setInitialValuesAsNBT(aNBT, (short) 0);
+ }
+
+ @Override
+ public void setInitialValuesAsNBT(NBTTagCompound aNBT, short aID) {
+ if (aNBT == null) {
+ if (aID > 0) mID = aID;
+ else mID = mID > 0 ? mID : 0;
+ if (mID != 0) createNewMetatileEntity(mID);
+ } else {
+ if (aID <= 0) mID = (short) aNBT.getInteger("mID");
+ else mID = aID;
+ mConnections = aNBT.getByte("mConnections");
+ mColor = aNBT.getByte("mColor");
+ mWorks = !aNBT.getBoolean("mWorks");
+
+ if (mSidedRedstone.length != 6) mSidedRedstone = new byte[] { 0, 0, 0, 0, 0, 0 };
+
+ readCoverNBT(aNBT);
+ loadMetaTileNBT(aNBT);
+ }
+ }
+
+ @Override
+ public void updateEntity() {
+ super.updateEntity();
+
+ if (!hasValidMetaTileEntity()) {
+ if (mMetaTileEntity == null) return;
+ mMetaTileEntity.setBaseMetaTileEntity(this);
+ }
+
+ long tTime;
+ if (hasTimeStatisticsStarted) {
+ tTime = System.nanoTime();
+ } else {
+ tTime = 0;
+ }
+ try {
+ if (hasValidMetaTileEntity()) {
+ if (mTickTimer++ == 0) {
+ oX = xCoord;
+ oY = yCoord;
+ oZ = zCoord;
+ if (isServerSide()) checkDropCover();
+ else {
+ requestCoverDataIfNeeded();
+ }
+ worldObj.markTileEntityChunkModified(xCoord, yCoord, zCoord, this);
+ mMetaTileEntity.onFirstTick(this);
+ if (!hasValidMetaTileEntity()) return;
+ }
+
+ if (isClientSide()) {
+ if (mColor != oColor) {
+ mMetaTileEntity.onColorChangeClient(oColor = mColor);
+ issueTextureUpdate();
+ }
+
+ if (mNeedsUpdate) {
+ worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
+ mNeedsUpdate = false;
+ }
+ }
+ if (isServerSide() && mTickTimer > 10) {
+ if (!doCoverThings()) return;
+
+ final byte oldConnections = mConnections;
+ // Mask-out connection direction bits to keep only Foam related connections
+ mConnections = (byte) (mMetaTileEntity.mConnections | (mConnections & ~IConnectable.CONNECTED_ALL));
+ // If foam not hardened, tries roll chance to harden
+ if ((mConnections & IConnectable.HAS_FOAM) == IConnectable.HAS_FRESHFOAM
+ && getRandomNumber(1000) == 0) {
+ mConnections = (byte) ((mConnections & ~IConnectable.HAS_FRESHFOAM)
+ | IConnectable.HAS_HARDENEDFOAM);
+ }
+ if (mTickTimer > 12 && oldConnections != mConnections)
+ GregTech_API.causeCableUpdate(worldObj, xCoord, yCoord, zCoord);
+ }
+ mMetaTileEntity.onPreTick(this, mTickTimer);
+ if (!hasValidMetaTileEntity()) return;
+ if (isServerSide()) {
+ if (mTickTimer == 10) {
+ updateCoverBehavior();
+ issueBlockUpdate();
+ joinEnet();
+ }
+
+ if (xCoord != oX || yCoord != oY || zCoord != oZ) {
+ oX = xCoord;
+ oY = yCoord;
+ oZ = zCoord;
+ issueClientUpdate();
+ clearTileEntityBuffer();
+ }
+ }
+
+ mMetaTileEntity.onPostTick(this, mTickTimer);
+ if (!hasValidMetaTileEntity()) return;
+
+ if (isServerSide()) {
+ if (mTickTimer % 10 == 0) {
+ sendClientData();
+ }
+
+ if (mTickTimer > 10) {
+ if (mConnections != oTextureData) sendBlockEvent((byte) 0, oTextureData = mConnections);
+ byte tData = mMetaTileEntity.getUpdateData();
+ if (tData != oUpdateData) sendBlockEvent((byte) 1, oUpdateData = tData);
+ if (mColor != oColor) sendBlockEvent((byte) 2, oColor = mColor);
+ tData = (byte) (((mSidedRedstone[0] > 0) ? 1 : 0) | ((mSidedRedstone[1] > 0) ? 2 : 0)
+ | ((mSidedRedstone[2] > 0) ? 4 : 0)
+ | ((mSidedRedstone[3] > 0) ? 8 : 0)
+ | ((mSidedRedstone[4] > 0) ? 16 : 0)
+ | ((mSidedRedstone[5] > 0) ? 32 : 0));
+ if (tData != oRedstoneData) sendBlockEvent((byte) 3, oRedstoneData = tData);
+ }
+
+ if (mNeedsBlockUpdate) {
+ updateNeighbours(mStrongRedstone, oStrongRedstone);
+ oStrongRedstone = mStrongRedstone;
+ mNeedsBlockUpdate = false;
+ }
+ }
+ }
+ } catch (Throwable e) {
+ e.printStackTrace();
+ e.printStackTrace(GT_Log.err);
+ }
+
+ if (isServerSide() && hasTimeStatisticsStarted && hasValidMetaTileEntity()) {
+ tTime = System.nanoTime() - tTime;
+ mTimeStatisticsIndex = (mTimeStatisticsIndex + 1) % mTimeStatistics.length;
+ mTimeStatistics[mTimeStatisticsIndex] = (int) tTime;
+ if (tTime > 0 && tTime > (GregTech_API.MILLISECOND_THRESHOLD_UNTIL_LAG_WARNING * 1000000L)
+ && mTickTimer > 1000
+ && getMetaTileEntity().doTickProfilingMessageDuringThisTick()
+ && mLagWarningCount++ < 10)
+ GT_FML_LOGGER.warn(
+ "WARNING: Possible Lag Source at [" + xCoord
+ + ","
+ + yCoord
+ + ","
+ + zCoord
+ + "] in Dimension "
+ + worldObj.provider.dimensionId
+ + " with "
+ + tTime
+ + " ns caused by an instance of "
+ + getMetaTileEntity().getClass());
+ }
+
+ mWorkUpdate = mInventoryChanged = false;
+ }
+
+ private void sendClientData() {
+ if (mSendClientData) {
+ NW.sendPacketToAllPlayersInRange(
+ worldObj,
+ new GT_Packet_TileEntity(
+ xCoord,
+ (short) yCoord,
+ zCoord,
+ mID,
+ getCoverInfoAtSide(ForgeDirection.DOWN).getCoverID(),
+ getCoverInfoAtSide(ForgeDirection.UP).getCoverID(),
+ getCoverInfoAtSide(ForgeDirection.NORTH).getCoverID(),
+ getCoverInfoAtSide(ForgeDirection.SOUTH).getCoverID(),
+ getCoverInfoAtSide(ForgeDirection.WEST).getCoverID(),
+ getCoverInfoAtSide(ForgeDirection.EAST).getCoverID(),
+ oTextureData = mConnections,
+ oUpdateData = hasValidMetaTileEntity() ? mMetaTileEntity.getUpdateData() : 0,
+ oRedstoneData = (byte) (((mSidedRedstone[0] > 0) ? 1 : 0) | ((mSidedRedstone[1] > 0) ? 2 : 0)
+ | ((mSidedRedstone[2] > 0) ? 4 : 0)
+ | ((mSidedRedstone[3] > 0) ? 8 : 0)
+ | ((mSidedRedstone[4] > 0) ? 16 : 0)
+ | ((mSidedRedstone[5] > 0) ? 32 : 0)),
+ oColor = mColor),
+ xCoord,
+ zCoord);
+ mSendClientData = false;
+ }
+ sendCoverDataIfNeeded();
+ }
+
+ public final void receiveMetaTileEntityData(short aID, int aCover0, int aCover1, int aCover2, int aCover3,
+ int aCover4, int aCover5, byte aTextureData, byte aUpdateData, byte aRedstoneData, byte aColorData) {
+ issueTextureUpdate();
+ if (aID > 0 && mID != aID) {
+ mID = aID;
+ createNewMetatileEntity(mID);
+ }
+
+ setCoverIDAtSide(ForgeDirection.DOWN, aCover0);
+ setCoverIDAtSide(ForgeDirection.UP, aCover1);
+ setCoverIDAtSide(ForgeDirection.NORTH, aCover2);
+ setCoverIDAtSide(ForgeDirection.SOUTH, aCover3);
+ setCoverIDAtSide(ForgeDirection.WEST, aCover4);
+ setCoverIDAtSide(ForgeDirection.EAST, aCover5);
+
+ receiveClientEvent(GregTechTileClientEvents.CHANGE_COMMON_DATA, aTextureData);
+ receiveClientEvent(GregTechTileClientEvents.CHANGE_CUSTOM_DATA, aUpdateData);
+ receiveClientEvent(GregTechTileClientEvents.CHANGE_COLOR, aColorData);
+ receiveClientEvent(GregTechTileClientEvents.CHANGE_REDSTONE_OUTPUT, aRedstoneData);
+ }
+
+ @Override
+ public boolean receiveClientEvent(int aEventID, int aValue) {
+ super.receiveClientEvent(aEventID, aValue);
+
+ if (hasValidMetaTileEntity()) {
+ try {
+ mMetaTileEntity.receiveClientEvent((byte) aEventID, (byte) aValue);
+ } catch (Throwable e) {
+ GT_FML_LOGGER.error("Encountered Exception while receiving Data from the Server", e);
+ }
+ }
+
+ if (isClientSide()) {
+ issueTextureUpdate();
+ switch (aEventID) {
+ case GregTechTileClientEvents.CHANGE_COMMON_DATA -> mConnections = (byte) aValue;
+ case GregTechTileClientEvents.CHANGE_CUSTOM_DATA -> {
+ if (hasValidMetaTileEntity()) mMetaTileEntity.onValueUpdate((byte) aValue);
+ }
+ case GregTechTileClientEvents.CHANGE_COLOR -> {
+ if (aValue > 16 || aValue < 0) aValue = 0;
+ mColor = (byte) aValue;
+ }
+ case GregTechTileClientEvents.CHANGE_REDSTONE_OUTPUT -> {
+ mSidedRedstone[0] = (byte) ((aValue & 1) == 1 ? 15 : 0);
+ mSidedRedstone[1] = (byte) ((aValue & 2) == 2 ? 15 : 0);
+ mSidedRedstone[2] = (byte) ((aValue & 4) == 4 ? 15 : 0);
+ mSidedRedstone[3] = (byte) ((aValue & 8) == 8 ? 15 : 0);
+ mSidedRedstone[4] = (byte) ((aValue & 16) == 16 ? 15 : 0);
+ mSidedRedstone[5] = (byte) ((aValue & 32) == 32 ? 15 : 0);
+ }
+ case GregTechTileClientEvents.DO_SOUND -> {
+ if (hasValidMetaTileEntity() && mTickTimer > 20)
+ mMetaTileEntity.doSound((byte) aValue, xCoord + 0.5, yCoord + 0.5, zCoord + 0.5);
+ }
+ case GregTechTileClientEvents.START_SOUND_LOOP -> {
+ if (hasValidMetaTileEntity() && mTickTimer > 20)
+ mMetaTileEntity.startSoundLoop((byte) aValue, xCoord + 0.5, yCoord + 0.5, zCoord + 0.5);
+ }
+ case GregTechTileClientEvents.STOP_SOUND_LOOP -> {
+ if (hasValidMetaTileEntity() && mTickTimer > 20)
+ mMetaTileEntity.stopSoundLoop((byte) aValue, xCoord + 0.5, yCoord + 0.5, zCoord + 0.5);
+ }
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public ArrayList<String> getDebugInfo(EntityPlayer aPlayer, int aLogLevel) {
+ final ArrayList<String> tList = new ArrayList<>();
+ if (aLogLevel > 3) {
+ tList.add(
+ "Meta-ID: " + EnumChatFormatting.BLUE
+ + mID
+ + EnumChatFormatting.RESET
+ + (hasValidMetaTileEntity() ? EnumChatFormatting.GREEN + " valid" + EnumChatFormatting.RESET
+ : EnumChatFormatting.RED + " invalid" + EnumChatFormatting.RESET)
+ + (mMetaTileEntity == null
+ ? EnumChatFormatting.RED + " MetaTileEntity == null!" + EnumChatFormatting.RESET
+ : " "));
+ }
+ if (aLogLevel > 1) {
+ if (hasTimeStatisticsStarted) {
+ double tAverageTime = 0;
+ double tWorstTime = 0;
+ int amountOfZero = 0;
+ for (int tTime : mTimeStatistics) {
+ tAverageTime += tTime;
+ if (tTime > tWorstTime) {
+ tWorstTime = tTime;
+ }
+ if (tTime == 0) {
+ amountOfZero += 1;
+ }
+ }
+ // tick time zero means it has not been updated yet
+ int samples = mTimeStatistics.length - amountOfZero;
+ if (samples > 0) {
+ tList.add(
+ "Average CPU-load of ~" + (tAverageTime / samples)
+ + "ns since "
+ + samples
+ + " ticks with worst time of "
+ + tWorstTime
+ + "ns.");
+ }
+ } else {
+ startTimeStatistics();
+ tList.add("Just started tick time statistics.");
+ }
+ if (mLagWarningCount > 0) {
+ tList.add(
+ "Caused " + (mLagWarningCount >= 10 ? "more than 10" : mLagWarningCount)
+ + " Lag Spike Warnings (anything taking longer than "
+ + GregTech_API.MILLISECOND_THRESHOLD_UNTIL_LAG_WARNING
+ + "ms) on the Server.");
+ }
+ if (mMetaTileEntity != null) {
+ tList.add(
+ "Is" + (mMetaTileEntity.isAccessAllowed(aPlayer) ? " "
+ : EnumChatFormatting.RED + " not " + EnumChatFormatting.RESET) + "accessible for you");
+ }
+ }
+ if (joinedIc2Enet) tList.add("Joined IC2 ENet");
+
+ return mMetaTileEntity != null ? mMetaTileEntity.getSpecialDebugInfo(this, aPlayer, aLogLevel, tList)
+ : new ArrayList<>();
+ }
+
+ @Override
+ public boolean isGivingInformation() {
+ if (canAccessData()) return mMetaTileEntity.isGivingInformation();
+ return false;
+ }
+
+ @Override
+ public ForgeDirection getBackFacing() {
+ return getFrontFacing().getOpposite();
+ }
+
+ @Override
+ public ForgeDirection getFrontFacing() {
+ return ForgeDirection.UNKNOWN;
+ }
+
+ @Override
+ public void setFrontFacing(ForgeDirection aFacing) {
+ doEnetUpdate();
+ }
+
+ @Override
+ public int getSizeInventory() {
+ if (canAccessData()) return mMetaTileEntity.getSizeInventory();
+ return 0;
+ }
+
+ @Override
+ public ItemStack getStackInSlot(int aIndex) {
+ if (canAccessData()) return mMetaTileEntity.getStackInSlot(aIndex);
+ return null;
+ }
+
+ @Override
+ public void setInventorySlotContents(int aIndex, ItemStack aStack) {
+ markDirty();
+ mInventoryChanged = true;
+ if (canAccessData()) mMetaTileEntity
+ .setInventorySlotContents(aIndex, worldObj.isRemote ? aStack : GT_OreDictUnificator.setStack(true, aStack));
+ }
+
+ @Override
+ public String getInventoryName() {
+ if (canAccessData()) return mMetaTileEntity.getInventoryName();
+ if (GregTech_API.METATILEENTITIES[mID] != null) return GregTech_API.METATILEENTITIES[mID].getInventoryName();
+ return "";
+ }
+
+ @Override
+ public int getInventoryStackLimit() {
+ if (canAccessData()) return mMetaTileEntity.getInventoryStackLimit();
+ return 64;
+ }
+
+ @Override
+ public void openInventory() {
+ /* Do nothing */
+ }
+
+ @Override
+ public void closeInventory() {
+ /* Do nothing */
+ }
+
+ @Override
+ public boolean isUseableByPlayer(EntityPlayer aPlayer) {
+ return hasValidMetaTileEntity() && mTickTimer > 1
+ && getTileEntityOffset(0, 0, 0) == this
+ && aPlayer.getDistanceSq(xCoord + 0.5, yCoord + 0.5, zCoord + 0.5) < 64
+ && mMetaTileEntity.isAccessAllowed(aPlayer);
+ }
+
+ @Override
+ public void validate() {
+ super.validate();
+ mTickTimer = 0;
+ }
+
+ @Override
+ public void invalidate() {
+ tileEntityInvalid = false;
+ if (hasValidMetaTileEntity()) {
+ mMetaTileEntity.onRemoval();
+ mMetaTileEntity.setBaseMetaTileEntity(null);
+ }
+ leaveEnet();
+ super.invalidate();
+ }
+
+ @Override
+ public boolean hasCustomInventoryName() {
+ return false;
+ }
+
+ @Override
+ public ItemStack getStackInSlotOnClosing(int slot) {
+ ItemStack stack = getStackInSlot(slot);
+ if (stack != null) setInventorySlotContents(slot, null);
+ return stack;
+ }
+
+ /**
+ * Checks validity of meta tile and delegates to it
+ */
+ @Override
+ public void onMachineBlockUpdate() {
+ if (canAccessData()) mMetaTileEntity.onMachineBlockUpdate();
+ }
+
+ /**
+ * Checks validity of meta tile and delegates to it
+ */
+ @Override
+ public boolean isMachineBlockUpdateRecursive() {
+ return canAccessData() && mMetaTileEntity.isMachineBlockUpdateRecursive();
+ }
+
+ @Override
+ public int getProgress() {
+ return canAccessData() ? mMetaTileEntity.getProgresstime() : 0;
+ }
+
+ @Override
+ public int getMaxProgress() {
+ return canAccessData() ? mMetaTileEntity.maxProgresstime() : 0;
+ }
+
+ @Override
+ public boolean increaseProgress(int aProgressAmountInTicks) {
+ return canAccessData() && mMetaTileEntity.increaseProgress(aProgressAmountInTicks) != aProgressAmountInTicks;
+ }
+
+ @Override
+ public boolean hasThingsToDo() {
+ return getMaxProgress() > 0;
+ }
+
+ @Override
+ public void enableWorking() {
+ if (!mWorks) mWorkUpdate = true;
+ mWorks = true;
+ reloadLocks();
+ }
+
+ @Override
+ public void disableWorking() {
+ mWorks = false;
+ reloadLocks();
+ }
+
+ @Override
+ public boolean isAllowedToWork() {
+ return mWorks;
+ }
+
+ @Override
+ public boolean hasWorkJustBeenEnabled() {
+ return mWorkUpdate;
+ }
+
+ @Override
+ public byte getWorkDataValue() {
+ return 0;
+ }
+
+ @Override
+ public void setWorkDataValue(byte aValue) {
+ /* Do nothing */
+ }
+
+ @Override
+ public int getMetaTileID() {
+ return mID;
+ }
+
+ @Override
+ public int setMetaTileID(short aID) {
+ return mID = aID;
+ }
+
+ @Override
+ public boolean isActive() {
+ return false;
+ }
+
+ @Override
+ public void setActive(boolean aActive) {
+ /* Do nothing */
+ }
+
+ @Override
+ public long getTimer() {
+ return mTickTimer;
+ }
+
+ @Override
+ public boolean decreaseStoredEnergyUnits(long aEnergy, boolean aIgnoreTooLessEnergy) {
+ return false;
+ }
+
+ @Override
+ public boolean increaseStoredEnergyUnits(long aEnergy, boolean aIgnoreTooMuchEnergy) {
+ return false;
+ }
+
+ @Override
+ public boolean inputEnergyFrom(ForgeDirection side) {
+ return false;
+ }
+
+ @Override
+ public boolean inputEnergyFrom(ForgeDirection side, boolean waitForActive) {
+ return false;
+ }
+
+ @Override
+ public boolean outputsEnergyTo(ForgeDirection side) {
+ return false;
+ }
+
+ @Override
+ public boolean outputsEnergyTo(ForgeDirection side, boolean waitForActive) {
+ return false;
+ }
+
+ @Override
+ public long getOutputAmperage() {
+ return 0;
+ }
+
+ @Override
+ public long getOutputVoltage() {
+ return 0;
+ }
+
+ @Override
+ public long getInputAmperage() {
+ return 0;
+ }
+
+ @Override
+ public long getInputVoltage() {
+ return 0;
+ }
+
+ @Override
+ public long getUniversalEnergyStored() {
+ return Math.max(getStoredEU(), getStoredSteam());
+ }
+
+ @Override
+ public long getUniversalEnergyCapacity() {
+ return Math.max(getEUCapacity(), getSteamCapacity());
+ }
+
+ @Override
+ public long getStoredEU() {
+ return 0;
+ }
+
+ @Override
+ public long getEUCapacity() {
+ return 0;
+ }
+
+ @Override
+ public ITexture[] getTexture(Block aBlock, ForgeDirection side) {
+ final ITexture rIcon = getCoverTexture(side);
+ if (rIcon != null) return new ITexture[] { rIcon };
+ return getTextureUncovered(side);
+ }
+
+ @Override
+ public ITexture[] getTextureCovered(ForgeDirection side) {
+ final ITexture coverTexture = getCoverTexture(side);
+ final ITexture[] textureUncovered = getTextureUncovered(side);
+ final ITexture[] textureCovered;
+ if (coverTexture != null) {
+ textureCovered = Arrays.copyOf(textureUncovered, textureUncovered.length + 1);
+ textureCovered[textureUncovered.length] = coverTexture;
+ return textureCovered;
+ } else {
+ return textureUncovered;
+ }
+ }
+
+ @Override
+ public ITexture[] getTextureUncovered(ForgeDirection sideDirection) {
+ if ((mConnections & IConnectable.HAS_FRESHFOAM) != 0) return Textures.BlockIcons.FRESHFOAM;
+ if ((mConnections & IConnectable.HAS_HARDENEDFOAM) != 0) return Textures.BlockIcons.HARDENEDFOAMS[mColor];
+ if ((mConnections & IConnectable.HAS_FOAM) != 0) return Textures.BlockIcons.ERROR_RENDERING;
+ int tConnections = mConnections;
+ if (tConnections == IConnectable.CONNECTED_WEST || tConnections == IConnectable.CONNECTED_EAST)
+ tConnections = IConnectable.CONNECTED_WEST | IConnectable.CONNECTED_EAST;
+ else if (tConnections == IConnectable.CONNECTED_DOWN || tConnections == IConnectable.CONNECTED_UP)
+ tConnections = IConnectable.CONNECTED_DOWN | IConnectable.CONNECTED_UP;
+ else if (tConnections == IConnectable.CONNECTED_NORTH || tConnections == IConnectable.CONNECTED_SOUTH)
+ tConnections = IConnectable.CONNECTED_NORTH | IConnectable.CONNECTED_SOUTH;
+ if (hasValidMetaTileEntity()) return mMetaTileEntity.getTexture(
+ this,
+ sideDirection,
+ tConnections,
+ mColor - 1,
+ tConnections == 0 || (tConnections & sideDirection.flag) != 0,
+ getOutputRedstoneSignal(sideDirection) > 0);
+ return Textures.BlockIcons.ERROR_RENDERING;
+ }
+
+ @Override
+ protected boolean hasValidMetaTileEntity() {
+ return mMetaTileEntity != null && mMetaTileEntity.getBaseMetaTileEntity() == this;
+ }
+
+ @Override
+ public void doExplosion(long aAmount) {
+ if (canAccessData()) {
+ mMetaTileEntity.onExplosion();
+ mMetaTileEntity.doExplosion(aAmount);
+ }
+ }
+
+ @Override
+ public ArrayList<ItemStack> getDrops() {
+ final ItemStack rStack = new ItemStack(GregTech_API.sBlockMachines, 1, mID);
+ final NBTTagCompound tNBT = new NBTTagCompound();
+
+ writeCoverNBT(tNBT, true);
+
+ if (hasValidMetaTileEntity()) mMetaTileEntity.setItemNBT(tNBT);
+ if (!tNBT.hasNoTags()) rStack.setTagCompound(tNBT);
+
+ onBaseTEDestroyed();
+ return new ArrayList<>(Collections.singletonList(rStack));
+ }
+
+ @Override
+ public boolean shouldDropItemAt(int index) {
+ return this.mMetaTileEntity == null || this.mMetaTileEntity.shouldDropItemAt(index);
+ }
+
+ @Override
+ public boolean onRightclick(EntityPlayer aPlayer, ForgeDirection side, float aX, float aY, float aZ) {
+ if (isClientSide()) {
+ // Configure Cover, sneak can also be: screwdriver, wrench, side cutter, soldering iron
+ if (aPlayer.isSneaking()) {
+ final ForgeDirection tSide = (getCoverIDAtSide(side) == 0)
+ ? GT_Utility.determineWrenchingSide(side, aX, aY, aZ)
+ : side;
+ return (getCoverInfoAtSide(tSide).hasCoverGUI());
+ } else if (getCoverBehaviorAtSideNew(side).onCoverRightclickClient(side, this, aPlayer, aX, aY, aZ)) {
+ return true;
+ }
+ }
+ if (isServerSide()) {
+ final ItemStack tCurrentItem = aPlayer.inventory.getCurrentItem();
+ if (tCurrentItem != null) {
+ if (getColorization() >= 0
+ && GT_Utility.areStacksEqual(new ItemStack(Items.water_bucket, 1), tCurrentItem)) {
+ mMetaTileEntity.markDirty();
+ tCurrentItem.func_150996_a(Items.bucket);
+ setColorization((byte) -1);
+ return true;
+ }
+ final ForgeDirection tSide = GT_Utility.determineWrenchingSide(side, aX, aY, aZ);
+ if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sWrenchList)) {
+
+ if (mMetaTileEntity.onWrenchRightClick(side, tSide, aPlayer, aX, aY, aZ, tCurrentItem)) {
+ mMetaTileEntity.markDirty();
+ GT_ModHandler.damageOrDechargeItem(tCurrentItem, 1, 1000, aPlayer);
+ GT_Utility.sendSoundToPlayers(
+ worldObj,
+ SoundResource.IC2_TOOLS_WRENCH,
+ 1.0F,
+ -1,
+ xCoord,
+ yCoord,
+ zCoord);
+ }
+ return true;
+ }
+ if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sScrewdriverList)) {
+ if (getCoverIDAtSide(side) == 0 && getCoverIDAtSide(tSide) != 0) {
+ if (GT_ModHandler.damageOrDechargeItem(tCurrentItem, 1, 200, aPlayer)) {
+ setCoverDataAtSide(
+ tSide,
+ getCoverInfoAtSide(tSide).onCoverScrewdriverClick(aPlayer, 0.5F, 0.5F, 0.5F));
+ mMetaTileEntity.onScrewdriverRightClick(tSide, aPlayer, aX, aY, aZ, tCurrentItem);
+ mMetaTileEntity.markDirty();
+ GT_Utility.sendSoundToPlayers(
+ worldObj,
+ SoundResource.IC2_TOOLS_WRENCH,
+ 1.0F,
+ -1,
+ xCoord,
+ yCoord,
+ zCoord);
+ }
+ } else {
+ if (GT_ModHandler.damageOrDechargeItem(tCurrentItem, 1, 1000, aPlayer)) {
+ setCoverDataAtSide(
+ side,
+ getCoverInfoAtSide(side).onCoverScrewdriverClick(aPlayer, aX, aY, aZ));
+ mMetaTileEntity.onScrewdriverRightClick(side, aPlayer, aX, aY, aZ, tCurrentItem);
+ mMetaTileEntity.markDirty();
+ GT_Utility.sendSoundToPlayers(
+ worldObj,
+ SoundResource.IC2_TOOLS_WRENCH,
+ 1.0F,
+ -1,
+ xCoord,
+ yCoord,
+ zCoord);
+ }
+ }
+ return true;
+ }
+
+ if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sHardHammerList)) {
+ return true;
+ }
+
+ if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sSoftHammerList)) {
+ if (GT_ModHandler.damageOrDechargeItem(tCurrentItem, 1, 1000, aPlayer)) {
+ if (mWorks) disableWorking();
+ else enableWorking();
+ mMetaTileEntity.markDirty();
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ GT_Utility.trans("090", "Machine Processing: ")
+ + (isAllowedToWork() ? GT_Utility.trans("088", "Enabled")
+ : GT_Utility.trans("087", "Disabled")));
+ GT_Utility.sendSoundToPlayers(
+ worldObj,
+ SoundResource.IC2_TOOLS_RUBBER_TRAMPOLINE,
+ 1.0F,
+ -1,
+ xCoord,
+ yCoord,
+ zCoord);
+ }
+ return true;
+ }
+
+ if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sWireCutterList)) {
+ if (mMetaTileEntity.onWireCutterRightClick(side, tSide, aPlayer, aX, aY, aZ, tCurrentItem)) {
+ mMetaTileEntity.markDirty();
+ // logic handled internally
+ GT_Utility.sendSoundToPlayers(
+ worldObj,
+ SoundResource.IC2_TOOLS_WRENCH,
+ 1.0F,
+ -1,
+ xCoord,
+ yCoord,
+ zCoord);
+ }
+ doEnetUpdate();
+ return true;
+ }
+
+ if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sSolderingToolList)) {
+ if (mMetaTileEntity.onSolderingToolRightClick(side, tSide, aPlayer, aX, aY, aZ, tCurrentItem)) {
+ mMetaTileEntity.markDirty();
+ // logic handled internally
+ GT_Utility.sendSoundToPlayers(
+ worldObj,
+ SoundResource.IC2_TOOLS_BATTERY_USE,
+ 1.0F,
+ -1,
+ xCoord,
+ yCoord,
+ zCoord);
+ } else if (GT_ModHandler.useSolderingIron(tCurrentItem, aPlayer)) {
+ mMetaTileEntity.markDirty();
+ mStrongRedstone ^= tSide.flag;
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ GT_Utility.trans("091", "Redstone Output at Side ") + tSide
+ + GT_Utility.trans("092", " set to: ")
+ + ((mStrongRedstone & tSide.flag) != 0 ? GT_Utility.trans("093", "Strong")
+ : GT_Utility.trans("094", "Weak")));
+ GT_Utility.sendSoundToPlayers(
+ worldObj,
+ SoundResource.IC2_TOOLS_BATTERY_USE,
+ 3.0F,
+ -1,
+ xCoord,
+ yCoord,
+ zCoord);
+ issueBlockUpdate();
+ }
+ doEnetUpdate();
+ return true;
+ }
+
+ ForgeDirection coverSide = side;
+ if (getCoverIDAtSide(side) == 0) coverSide = tSide;
+
+ final CoverInfo coverInfo = getCoverInfoAtSide(coverSide);
+
+ if (coverInfo.getCoverID() == 0) {
+ if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sCovers.keySet())) {
+ final GT_CoverBehaviorBase<?> coverBehavior = GregTech_API.getCoverBehaviorNew(tCurrentItem);
+ if (coverBehavior.isCoverPlaceable(coverSide, tCurrentItem, this)
+ && mMetaTileEntity.allowCoverOnSide(coverSide, new GT_ItemStack(tCurrentItem))) {
+
+ setCoverItemAtSide(coverSide, tCurrentItem);
+ coverBehavior.onPlayerAttach(aPlayer, tCurrentItem, this, side);
+
+ mMetaTileEntity.markDirty();
+ if (!aPlayer.capabilities.isCreativeMode) tCurrentItem.stackSize--;
+ GT_Utility.sendSoundToPlayers(
+ worldObj,
+ SoundResource.IC2_TOOLS_WRENCH,
+ 1.0F,
+ -1,
+ xCoord,
+ yCoord,
+ zCoord);
+ }
+ return true;
+ }
+ } else {
+ if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sCrowbarList)) {
+ if (GT_ModHandler.damageOrDechargeItem(tCurrentItem, 1, 1000, aPlayer)) {
+ GT_Utility.sendSoundToPlayers(
+ worldObj,
+ SoundResource.RANDOM_BREAK,
+ 1.0F,
+ -1,
+ xCoord,
+ yCoord,
+ zCoord);
+ dropCover(coverSide, side, false);
+ mMetaTileEntity.markDirty();
+ }
+ return true;
+ }
+ }
+ } else if (aPlayer.isSneaking()) { // Sneak click, no tool -> open cover config or turn back.
+ side = (getCoverIDAtSide(side) == 0) ? GT_Utility.determineWrenchingSide(side, aX, aY, aZ) : side;
+ final CoverInfo coverInfo = getCoverInfoAtSide(side);
+ return coverInfo.isValid() && coverInfo.onCoverShiftRightClick(aPlayer);
+ }
+
+ if (getCoverInfoAtSide(side).onCoverRightClick(aPlayer, aX, aY, aZ)) return true;
+ }
+
+ if (!getCoverInfoAtSide(side).isGUIClickable()) return false;
+
+ try {
+ if (!aPlayer.isSneaking() && hasValidMetaTileEntity()) {
+ final boolean handled = mMetaTileEntity.onRightclick(this, aPlayer, side, aX, aY, aZ);
+ if (handled) {
+ mMetaTileEntity.markDirty();
+ }
+ return handled;
+ }
+ } catch (Throwable e) {
+ GT_FML_LOGGER.error("Encountered Exception while right clicking TileEntity", e);
+ }
+
+ return false;
+ }
+
+ @Override
+ public void onLeftclick(EntityPlayer aPlayer) {
+ try {
+ if (aPlayer != null && hasValidMetaTileEntity()) mMetaTileEntity.onLeftclick(this, aPlayer);
+ } catch (Throwable e) {
+ GT_FML_LOGGER.error("Encountered Exception while left clicking TileEntity", e);
+ }
+ }
+
+ @Override
+ public boolean isDigitalChest() {
+ return false;
+ }
+
+ @Override
+ public ItemStack[] getStoredItemData() {
+ return null;
+ }
+
+ @Override
+ public void setItemCount(int aCount) {
+ //
+ }
+
+ @Override
+ public int getMaxItemCount() {
+ return 0;
+ }
+
+ /**
+ * Can put aStack into Slot
+ */
+ @Override
+ public boolean isItemValidForSlot(int aIndex, ItemStack aStack) {
+ return canAccessData() && mMetaTileEntity.isItemValidForSlot(aIndex, aStack);
+ }
+
+ /**
+ * returns all valid Inventory Slots, no matter which Side (Unless it's covered). The Side Stuff is done in the
+ * following two Functions.
+ */
+ @Override
+ public int[] getAccessibleSlotsFromSide(int ordinalSide) {
+ final CoverInfo coverInfo = getCoverInfoAtSide(ForgeDirection.getOrientation(ordinalSide));
+ if (canAccessData() && (coverInfo.letsItemsOut(-1) || coverInfo.letsItemsIn(-1)))
+ return mMetaTileEntity.getAccessibleSlotsFromSide(ordinalSide);
+ return GT_Values.emptyIntArray;
+ }
+
+ /**
+ * Can put aStack into Slot at Side
+ */
+ @Override
+ public boolean canInsertItem(int aIndex, ItemStack aStack, int ordinalSide) {
+ return canAccessData() && getCoverInfoAtSide(ForgeDirection.getOrientation(ordinalSide)).letsItemsIn(aIndex)
+ && mMetaTileEntity.canInsertItem(aIndex, aStack, ordinalSide);
+ }
+
+ /**
+ * Can pull aStack out of Slot from Side
+ */
+ @Override
+ public boolean canExtractItem(int aIndex, ItemStack aStack, int ordinalSide) {
+ final ForgeDirection side = ForgeDirection.getOrientation(ordinalSide);
+ return canAccessData()
+ && getCoverBehaviorAtSideNew(side)
+ .letsItemsOut(side, getCoverIDAtSide(side), getComplexCoverDataAtSide(side), aIndex, this)
+ && mMetaTileEntity.canExtractItem(aIndex, aStack, ordinalSide);
+ }
+
+ @Override
+ public boolean isUpgradable() {
+ return false;
+ }
+
+ @Override
+ public boolean isSteamEngineUpgradable() {
+ return isUpgradable() && !hasSteamEngineUpgrade() && getSteamCapacity() > 0;
+ }
+
+ @Override
+ public boolean addSteamEngineUpgrade() {
+ if (isSteamEngineUpgradable()) {
+ issueBlockUpdate();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean hasSteamEngineUpgrade() {
+ return false;
+ }
+
+ @Override
+ public void setGenericRedstoneOutput(boolean aOnOff) {
+ // Do nothing
+ }
+
+ @Override
+ public int getErrorDisplayID() {
+ return 0;
+ }
+
+ @Override
+ public void setErrorDisplayID(int aErrorID) {
+ //
+ }
+
+ @Override
+ public IMetaTileEntity getMetaTileEntity() {
+ return hasValidMetaTileEntity() ? mMetaTileEntity : null;
+ }
+
+ @Override
+ public void setMetaTileEntity(IMetaTileEntity aMetaTileEntity) {
+ mMetaTileEntity = (MetaPipeEntity) aMetaTileEntity;
+ }
+
+ @Override
+ public void setLightValue(byte aLightValue) {
+ //
+ }
+
+ @Override
+ public long getAverageElectricInput() {
+ return 0;
+ }
+
+ @Override
+ public long getAverageElectricOutput() {
+ return 0;
+ }
+
+ @Override
+ public String getOwnerName() {
+ return "Player";
+ }
+
+ @Override
+ public String setOwnerName(String aName) {
+ return "Player";
+ }
+
+ @Override
+ public UUID getOwnerUuid() {
+ return GT_Utility.defaultUuid;
+ }
+
+ @Override
+ public void setOwnerUuid(UUID uuid) {}
+
+ @Override
+ public byte getComparatorValue(ForgeDirection side) {
+ return canAccessData() ? mMetaTileEntity.getComparatorValue(side) : 0;
+ }
+
+ @Override
+ public ItemStack decrStackSize(int aIndex, int aAmount) {
+ if (canAccessData()) {
+ mInventoryChanged = true;
+ return mMetaTileEntity.decrStackSize(aIndex, aAmount);
+ }
+ return null;
+ }
+
+ @Override
+ public long injectEnergyUnits(ForgeDirection side, long aVoltage, long aAmperage) {
+ if (canAccessData()) return mMetaTileEntity.injectEnergyUnits(side, aVoltage, aAmperage);
+ return 0;
+ }
+
+ @Override
+ public boolean drainEnergyUnits(ForgeDirection side, long aVoltage, long aAmperage) {
+ return false;
+ }
+
+ @Override
+ public boolean acceptsRotationalEnergy(ForgeDirection side) {
+ if (!canAccessData() || getCoverIDAtSide(side) != 0) return false;
+ return mMetaTileEntity.acceptsRotationalEnergy(side);
+ }
+
+ @Override
+ public boolean injectRotationalEnergy(ForgeDirection side, long aSpeed, long aEnergy) {
+ if (!canAccessData() || getCoverIDAtSide(side) != 0) return false;
+ return mMetaTileEntity.injectRotationalEnergy(side, aSpeed, aEnergy);
+ }
+
+ private boolean canMoveFluidOnSide(ForgeDirection side, Fluid fluid, boolean isFill) {
+ if (side == ForgeDirection.UNKNOWN) return true;
+
+ final IFluidHandler tTileEntity = getITankContainerAtSide(side);
+ // Only require a connection if there's something to connect to - Allows fluid cells & buckets to interact with
+ // the pipe
+ if (tTileEntity != null && !mMetaTileEntity.isConnectedAtSide(side)) return false;
+
+ if (isFill && mMetaTileEntity.isLiquidInput(side) && getCoverInfoAtSide(side).letsFluidIn(fluid)) return true;
+
+ return !isFill && mMetaTileEntity.isLiquidOutput(side) && getCoverInfoAtSide(side).letsFluidOut(fluid);
+ }
+
+ @Override
+ public int fill(ForgeDirection side, FluidStack aFluidStack, boolean doFill) {
+ if (mTickTimer > 5 && canAccessData()
+ && canMoveFluidOnSide(side, aFluidStack == null ? null : aFluidStack.getFluid(), true))
+ return mMetaTileEntity.fill(side, aFluidStack, doFill);
+ return 0;
+ }
+
+ @Override
+ public FluidStack drain(ForgeDirection side, int maxDrain, boolean doDrain) {
+ if (mTickTimer > 5 && canAccessData()
+ && canMoveFluidOnSide(
+ side,
+ mMetaTileEntity.getFluid() == null ? null
+ : mMetaTileEntity.getFluid()
+ .getFluid(),
+ false))
+ return mMetaTileEntity.drain(side, maxDrain, doDrain);
+ return null;
+ }
+
+ @Override
+ public FluidStack drain(ForgeDirection side, FluidStack aFluidStack, boolean doDrain) {
+ if (mTickTimer > 5 && canAccessData()
+ && canMoveFluidOnSide(side, aFluidStack == null ? null : aFluidStack.getFluid(), false))
+ return mMetaTileEntity.drain(side, aFluidStack, doDrain);
+ return null;
+ }
+
+ @Override
+ public boolean canFill(ForgeDirection side, Fluid aFluid) {
+ if (mTickTimer > 5 && canAccessData() && canMoveFluidOnSide(side, aFluid, true))
+ return mMetaTileEntity.canFill(side, aFluid);
+ return false;
+ }
+
+ @Override
+ public boolean canDrain(ForgeDirection side, Fluid aFluid) {
+ if (mTickTimer > 5 && canAccessData() && canMoveFluidOnSide(side, aFluid, false))
+ return mMetaTileEntity.canDrain(side, aFluid);
+ return false;
+ }
+
+ @Override
+ public FluidTankInfo[] getTankInfo(ForgeDirection side) {
+ final CoverInfo coverInfo = getCoverInfoAtSide(side);
+ if (canAccessData()
+ && (side == ForgeDirection.UNKNOWN || (mMetaTileEntity.isLiquidInput(side) && coverInfo.letsFluidIn(null))
+ || (mMetaTileEntity.isLiquidOutput(side) && coverInfo.letsFluidOut(null))
+ // Doesn't need to be connected to get Tank Info -- otherwise things can't connect
+ )) return mMetaTileEntity.getTankInfo(side);
+ return new FluidTankInfo[] {};
+ }
+
+ @Override
+ public boolean addStackToSlot(int aIndex, ItemStack aStack) {
+ if (GT_Utility.isStackInvalid(aStack)) return true;
+ if (aIndex < 0 || aIndex >= getSizeInventory()) return false;
+ final ItemStack tStack = getStackInSlot(aIndex);
+ if (GT_Utility.isStackInvalid(tStack)) {
+ setInventorySlotContents(aIndex, aStack);
+ return true;
+ }
+ aStack = GT_OreDictUnificator.get(aStack);
+ if (GT_Utility.areStacksEqual(tStack, aStack)
+ && tStack.stackSize + aStack.stackSize <= Math.min(aStack.getMaxStackSize(), getInventoryStackLimit())) {
+ markDirty();
+ tStack.stackSize += aStack.stackSize;
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean addStackToSlot(int aIndex, ItemStack aStack, int aAmount) {
+ return addStackToSlot(aIndex, GT_Utility.copyAmount(aAmount, aStack));
+ }
+
+ @Override
+ public byte getColorization() {
+ return (byte) (mColor - 1);
+ }
+
+ @Override
+ public byte setColorization(byte aColor) {
+ if (aColor > 15 || aColor < -1) aColor = -1;
+ mColor = (byte) (aColor + 1);
+ if (canAccessData()) mMetaTileEntity.onColorChangeServer(aColor);
+ return mColor;
+ }
+
+ @Override
+ public float getThickNess() {
+ if (canAccessData()) return mMetaTileEntity.getThickNess();
+ return 1.0F;
+ }
+
+ public boolean renderInside(ForgeDirection side) {
+ if (canAccessData()) return mMetaTileEntity.renderInside(side);
+ return false;
+ }
+
+ @Override
+ public float getBlastResistance(ForgeDirection side) {
+ return canAccessData() ? Math.max(0, getMetaTileEntity().getExplosionResistance(side)) : 5.0F;
+ }
+
+ @Override
+ public void onBlockDestroyed() {
+ if (canAccessData()) getMetaTileEntity().onBlockDestroyed();
+ }
+
+ @Override
+ public boolean isMufflerUpgradable() {
+ return false;
+ }
+
+ @Override
+ public boolean addMufflerUpgrade() {
+ return false;
+ }
+
+ @Override
+ public boolean hasMufflerUpgrade() {
+ return false;
+ }
+
+ @Override
+ public boolean isUniversalEnergyStored(long aEnergyAmount) {
+ return getUniversalEnergyStored() >= aEnergyAmount;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ if (canAccessData()) return getMetaTileEntity().getInfoData();
+ return new String[] {};
+ }
+
+ @Override
+ public byte getConnections() {
+ return mConnections;
+ }
+
+ public void onNeighborBlockChange(int aX, int aY, int aZ) {
+ if (canAccessData()) {
+ final IMetaTileEntity meta = getMetaTileEntity();
+ if (meta instanceof MetaPipeEntity) {
+ // Trigger a checking of connections in case someone placed down a block that the pipe/wire shouldn't be
+ // connected to.
+ // However; don't do it immediately in case the world isn't finished loading
+ // (This caused issues with AE2 GTEU p2p connections.
+ ((MetaPipeEntity) meta).setCheckConnections();
+ }
+ }
+ }
+
+ @Override
+ public int getLightOpacity() {
+ return mMetaTileEntity == null ? 0 : mMetaTileEntity.getLightOpacity();
+ }
+
+ @Override
+ public void addCollisionBoxesToList(World aWorld, int aX, int aY, int aZ, AxisAlignedBB inputAABB,
+ List<AxisAlignedBB> outputAABB, Entity collider) {
+ mMetaTileEntity.addCollisionBoxesToList(aWorld, aX, aY, aZ, inputAABB, outputAABB, collider);
+ }
+
+ @Override
+ public AxisAlignedBB getCollisionBoundingBoxFromPool(World aWorld, int aX, int aY, int aZ) {
+ return mMetaTileEntity.getCollisionBoundingBoxFromPool(aWorld, aX, aY, aZ);
+ }
+
+ @Override
+ public void onEntityCollidedWithBlock(World aWorld, int aX, int aY, int aZ, Entity collider) {
+ mMetaTileEntity.onEntityCollidedWithBlock(aWorld, aX, aY, aZ, collider);
+ }
+
+ @Override
+ public int[] getTimeStatistics() {
+ return mTimeStatistics;
+ }
+
+ @Override
+ public void startTimeStatistics() {
+ hasTimeStatisticsStarted = true;
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currentTip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ super.getWailaBody(itemStack, currentTip, accessor, config);
+ mMetaTileEntity.getWailaBody(itemStack, currentTip, accessor, config);
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/BaseMetaTileEntity.java b/src/main/java/gregtech/api/metatileentity/BaseMetaTileEntity.java
new file mode 100644
index 0000000000..bf6358c884
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/BaseMetaTileEntity.java
@@ -0,0 +1,2538 @@
+package gregtech.api.metatileentity;
+
+import static gregtech.GT_Mod.GT_FML_LOGGER;
+import static gregtech.api.enums.GT_Values.NW;
+import static gregtech.api.enums.GT_Values.V;
+import static gregtech.api.objects.XSTR.XSTR_INSTANCE;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockFire;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.item.EntityItem;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.init.Items;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.AxisAlignedBB;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.EnumSkyBlock;
+import net.minecraft.world.World;
+import net.minecraft.world.biome.BiomeGenBase;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.Fluid;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.fluids.FluidTankInfo;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.gtnewhorizon.structurelib.alignment.IAlignment;
+import com.gtnewhorizon.structurelib.alignment.IAlignmentProvider;
+import com.gtnewhorizon.structurelib.alignment.constructable.IConstructable;
+import com.gtnewhorizon.structurelib.alignment.constructable.IConstructableProvider;
+
+import appeng.api.networking.IGridNode;
+import appeng.api.networking.security.IActionHost;
+import appeng.api.util.AECableType;
+import appeng.api.util.DimensionalCoord;
+import appeng.helpers.ICustomNameObject;
+import appeng.me.helpers.AENetworkProxy;
+import appeng.me.helpers.IGridProxyable;
+import appeng.tile.TileEvent;
+import appeng.tile.events.TileEventType;
+import cpw.mods.fml.relauncher.ReflectionHelper;
+import gregtech.GT_Mod;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.GT_Values;
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.enums.Textures;
+import gregtech.api.graphs.GenerateNodeMap;
+import gregtech.api.graphs.GenerateNodeMapPower;
+import gregtech.api.graphs.Node;
+import gregtech.api.interfaces.ICleanroom;
+import gregtech.api.interfaces.ICleanroomReceiver;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IDebugableTileEntity;
+import gregtech.api.interfaces.tileentity.IEnergyConnected;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.interfaces.tileentity.IGregtechWailaProvider;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine;
+import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch;
+import gregtech.api.net.GT_Packet_TileEntity;
+import gregtech.api.objects.GT_ItemStack;
+import gregtech.api.objects.blockupdate.BlockUpdateHandler;
+import gregtech.api.util.GT_CoverBehaviorBase;
+import gregtech.api.util.GT_Log;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_OreDictUnificator;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.shutdown.ShutDownReason;
+import gregtech.api.util.shutdown.ShutDownReasonRegistry;
+import gregtech.common.GT_Pollution;
+import gregtech.common.covers.CoverInfo;
+import ic2.api.Direction;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+/**
+ * NEVER INCLUDE THIS FILE IN YOUR MOD!!!
+ * <p/>
+ * This is the main TileEntity for EVERYTHING.
+ */
+public class BaseMetaTileEntity extends CommonMetaTileEntity
+ implements IGregTechTileEntity, IActionHost, IGridProxyable, IAlignmentProvider, IConstructableProvider,
+ IDebugableTileEntity, IGregtechWailaProvider, ICleanroomReceiver, ICustomNameObject {
+
+ private static final Field ENTITY_ITEM_HEALTH_FIELD = ReflectionHelper
+ .findField(EntityItem.class, "health", "field_70291_e");
+ private final boolean[] mActiveEUInputs = new boolean[] { false, false, false, false, false, false };
+ private final boolean[] mActiveEUOutputs = new boolean[] { false, false, false, false, false, false };
+ private final int[] mTimeStatistics = new int[GregTech_API.TICKS_FOR_LAG_AVERAGING];
+ private boolean hasTimeStatisticsStarted;
+ public long mLastSoundTick = 0;
+ public boolean mWasShutdown = false;
+ public @Nonnull ShutDownReason lastShutDownReason = ShutDownReasonRegistry.NONE;
+ protected MetaTileEntity mMetaTileEntity;
+ protected long mStoredEnergy = 0, mStoredSteam = 0;
+ protected int mAverageEUInputIndex = 0, mAverageEUOutputIndex = 0;
+ protected boolean mReleaseEnergy = false;
+ protected final long[] mAverageEUInput = new long[] { 0, 0, 0, 0, 0 };
+ protected final long[] mAverageEUOutput = new long[] { 0, 0, 0, 0, 0 };
+ private boolean mHasEnoughEnergy = true, mRunningThroughTick = false, mInputDisabled = false,
+ mOutputDisabled = false, mMuffler = false, mLockUpgrade = false;
+ private boolean mActive = false, mWorkUpdate = false, mSteamConverter = false, mWorks = true;
+ private boolean oRedstone = false;
+ private byte mColor = 0, oColor = 0, oStrongRedstone = 0, oRedstoneData = 63, oTextureData = 0, oUpdateData = 0,
+ oTexturePage = 0;
+ private byte oLightValueClient = 0, oLightValue = -1, mLightValue = 0, mOtherUpgrades = 0, mWorkData = 0;
+ private ForgeDirection mFacing = ForgeDirection.DOWN, oFacing = ForgeDirection.DOWN;
+ private int mDisplayErrorCode = 0, oX = 0, oY = 0, oZ = 0, mTimeStatisticsIndex = 0, mLagWarningCount = 0;
+ private long oOutput = 0, mAcceptedAmperes = Long.MAX_VALUE;
+ private long mLastCheckTick = 0;
+ private String mOwnerName = "";
+ private UUID mOwnerUuid = GT_Utility.defaultUuid;
+ private NBTTagCompound mRecipeStuff = new NBTTagCompound();
+ private int cableUpdateDelay = 30;
+
+ public BaseMetaTileEntity() {}
+
+ @Override
+ public void writeToNBT(NBTTagCompound aNBT) {
+ try {
+ super.writeToNBT(aNBT);
+ } catch (Throwable e) {
+ GT_FML_LOGGER.error("Encountered CRITICAL ERROR while saving MetaTileEntity.", e);
+ }
+ try {
+ aNBT.setInteger("mID", mID);
+ aNBT.setLong("mStoredSteam", mStoredSteam);
+ aNBT.setLong("mStoredEnergy", mStoredEnergy);
+ writeCoverNBT(aNBT, false);
+ aNBT.setByte("mColor", mColor);
+ aNBT.setByte("mLightValue", mLightValue);
+ aNBT.setByte("mOtherUpgrades", mOtherUpgrades);
+ aNBT.setByte("mWorkData", mWorkData);
+ aNBT.setShort("mFacing", (short) mFacing.ordinal());
+ aNBT.setString("mOwnerName", mOwnerName);
+ aNBT.setString("mOwnerUuid", mOwnerUuid == null ? "" : mOwnerUuid.toString());
+ aNBT.setBoolean("mLockUpgrade", mLockUpgrade);
+ aNBT.setBoolean("mMuffler", mMuffler);
+ aNBT.setBoolean("mSteamConverter", mSteamConverter);
+ aNBT.setBoolean("mActive", mActive);
+ aNBT.setBoolean("mWorks", !mWorks);
+ aNBT.setBoolean("mInputDisabled", mInputDisabled);
+ aNBT.setBoolean("mOutputDisabled", mOutputDisabled);
+ aNBT.setTag("GT.CraftingComponents", mRecipeStuff);
+ } catch (Throwable e) {
+ GT_FML_LOGGER.error("Encountered CRITICAL ERROR while saving MetaTileEntity.", e);
+ }
+ saveMetaTileNBT(aNBT);
+ }
+
+ @Override
+ public void readFromNBT(NBTTagCompound aNBT) {
+ super.readFromNBT(aNBT);
+ setInitialValuesAsNBT(aNBT, (short) 0);
+ }
+
+ @Override
+ public void setInitialValuesAsNBT(NBTTagCompound aNBT, short aID) {
+ if (aNBT == null) {
+ if (aID > 0) mID = aID;
+ else mID = mID > 0 ? mID : 0;
+ if (mID != 0) createNewMetatileEntity(mID);
+ mSidedRedstone = (hasValidMetaTileEntity() && mMetaTileEntity.hasSidedRedstoneOutputBehavior()
+ ? new byte[] { 0, 0, 0, 0, 0, 0 }
+ : new byte[] { 15, 15, 15, 15, 15, 15 });
+ } else {
+ if (aID <= 0) mID = (short) aNBT.getInteger("mID");
+ else mID = aID;
+ mStoredSteam = aNBT.getLong("mStoredSteam");
+ mStoredEnergy = aNBT.getLong("mStoredEnergy");
+ mColor = aNBT.getByte("mColor");
+ mLightValue = aNBT.getByte("mLightValue");
+ mWorkData = aNBT.getByte("mWorkData");
+ mFacing = oFacing = ForgeDirection.getOrientation(aNBT.getShort("mFacing"));
+ mOwnerName = aNBT.getString("mOwnerName");
+ try {
+ mOwnerUuid = UUID.fromString(aNBT.getString("mOwnerUuid"));
+ } catch (IllegalArgumentException e) {
+ mOwnerUuid = null;
+ }
+ mLockUpgrade = aNBT.getBoolean("mLockUpgrade");
+ mMuffler = aNBT.getBoolean("mMuffler");
+ mSteamConverter = aNBT.getBoolean("mSteamConverter");
+ mActive = aNBT.getBoolean("mActive");
+ mWorks = !aNBT.getBoolean("mWorks");
+ mInputDisabled = aNBT.getBoolean("mInputDisabled");
+ mOutputDisabled = aNBT.getBoolean("mOutputDisabled");
+ mOtherUpgrades = (byte) (aNBT.getByte("mOtherUpgrades") + aNBT.getByte("mBatteries")
+ + aNBT.getByte("mLiBatteries"));
+
+ mRecipeStuff = aNBT.getCompoundTag("GT.CraftingComponents");
+ final int nbtVersion = aNBT.getInteger("nbtVersion");
+ readCoverNBT(aNBT);
+ loadMetaTileNBT(aNBT);
+ }
+
+ if (mSidedRedstone.length != 6)
+ if (hasValidMetaTileEntity() && mMetaTileEntity.hasSidedRedstoneOutputBehavior())
+ mSidedRedstone = new byte[] { 0, 0, 0, 0, 0, 0 };
+ else mSidedRedstone = new byte[] { 15, 15, 15, 15, 15, 15 };
+
+ updateCoverBehavior();
+ }
+
+ /**
+ * Used for ticking special BaseMetaTileEntities, which need that for Energy Conversion It's called right before
+ * onPostTick()
+ */
+ public void updateStatus() {
+ //
+ }
+
+ /**
+ * Called when trying to charge Items
+ */
+ public void chargeItem(ItemStack aStack) {
+ decreaseStoredEU(
+ GT_ModHandler.chargeElectricItem(
+ aStack,
+ (int) Math.min(Integer.MAX_VALUE, getStoredEU()),
+ (int) Math.min(Integer.MAX_VALUE, mMetaTileEntity.getOutputTier()),
+ false,
+ false),
+ true);
+ }
+
+ /**
+ * Called when trying to discharge Items
+ */
+ public void dischargeItem(ItemStack aStack) {
+ increaseStoredEnergyUnits(
+ GT_ModHandler.dischargeElectricItem(
+ aStack,
+ (int) Math.min(Integer.MAX_VALUE, getEUCapacity() - getStoredEU()),
+ (int) Math.min(Integer.MAX_VALUE, mMetaTileEntity.getInputTier()),
+ false,
+ false,
+ false),
+ true);
+ }
+
+ protected boolean isRainPossible() {
+ BiomeGenBase biome = getBiome();
+ // see net.minecraft.client.renderer.EntityRenderer.renderRainSnow
+ return biome.rainfall > 0 && (biome.canSpawnLightningBolt() || biome.getEnableSnow());
+ }
+
+ /**
+ * Check if this is exposed to rain
+ *
+ * @return True if exposed to rain, else false
+ */
+ public boolean isRainExposed() {
+ final int precipitationHeightAtSide2 = worldObj.getPrecipitationHeight(xCoord, zCoord - 1);
+ final int precipitationHeightAtSide3 = worldObj.getPrecipitationHeight(xCoord, zCoord + 1);
+ final int precipitationHeightAtSide4 = worldObj.getPrecipitationHeight(xCoord - 1, zCoord);
+ final int precipitationHeightAtSide5 = worldObj.getPrecipitationHeight(xCoord + 1, zCoord);
+ return (getCoverIDAtSide(ForgeDirection.UP) == 0
+ && worldObj.getPrecipitationHeight(xCoord, zCoord) - 2 < yCoord)
+ || (getCoverIDAtSide(ForgeDirection.NORTH) == 0 && precipitationHeightAtSide2 - 1 < yCoord
+ && precipitationHeightAtSide2 > -1)
+ || (getCoverIDAtSide(ForgeDirection.SOUTH) == 0 && precipitationHeightAtSide3 - 1 < yCoord
+ && precipitationHeightAtSide3 > -1)
+ || (getCoverIDAtSide(ForgeDirection.WEST) == 0 && precipitationHeightAtSide4 - 1 < yCoord
+ && precipitationHeightAtSide4 > -1)
+ || (getCoverIDAtSide(ForgeDirection.EAST) == 0 && precipitationHeightAtSide5 - 1 < yCoord
+ && precipitationHeightAtSide5 > -1);
+ }
+
+ @Override
+ public void updateEntity() {
+ super.updateEntity();
+
+ if (!hasValidMetaTileEntity()) {
+ if (mMetaTileEntity == null) return;
+ mMetaTileEntity.setBaseMetaTileEntity(this);
+ }
+
+ mRunningThroughTick = true;
+ long tTime;
+ if (hasTimeStatisticsStarted) {
+ tTime = System.nanoTime();
+ } else {
+ tTime = 0;
+ }
+ final boolean aSideServer = isServerSide();
+ final boolean aSideClient = isClientSide();
+
+ try {
+ if (hasValidMetaTileEntity()) {
+ if (mTickTimer++ == 0) {
+ oX = xCoord;
+ oY = yCoord;
+ oZ = zCoord;
+ if (aSideServer) {
+ checkDropCover();
+ } else {
+ requestCoverDataIfNeeded();
+ }
+ worldObj.markTileEntityChunkModified(xCoord, yCoord, zCoord, this);
+ mMetaTileEntity.onFirstTick(this);
+ if (!hasValidMetaTileEntity()) {
+ mRunningThroughTick = false;
+ return;
+ }
+ }
+ if (aSideClient) {
+ if (mColor != oColor) {
+ mMetaTileEntity.onColorChangeClient(oColor = mColor);
+ issueTextureUpdate();
+ }
+
+ if (mLightValue != oLightValueClient) {
+ worldObj.setLightValue(EnumSkyBlock.Block, xCoord, yCoord, zCoord, mLightValue);
+ worldObj.updateLightByType(EnumSkyBlock.Block, xCoord, yCoord, zCoord);
+ worldObj.updateLightByType(EnumSkyBlock.Block, xCoord + 1, yCoord, zCoord);
+ worldObj.updateLightByType(EnumSkyBlock.Block, xCoord - 1, yCoord, zCoord);
+ worldObj.updateLightByType(EnumSkyBlock.Block, xCoord, yCoord + 1, zCoord);
+ worldObj.updateLightByType(EnumSkyBlock.Block, xCoord, yCoord - 1, zCoord);
+ worldObj.updateLightByType(EnumSkyBlock.Block, xCoord, yCoord, zCoord + 1);
+ worldObj.updateLightByType(EnumSkyBlock.Block, xCoord, yCoord, zCoord - 1);
+ oLightValueClient = mLightValue;
+ issueTextureUpdate();
+ }
+
+ if (mNeedsUpdate) {
+ if (GT_Mod.gregtechproxy.mUseBlockUpdateHandler) {
+ BlockUpdateHandler.Instance.enqueueBlockUpdate(worldObj, getLocation());
+ } else {
+ worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
+ }
+ mNeedsUpdate = false;
+ }
+ }
+ if (aSideServer && mTickTimer > 10) {
+ if (!doCoverThings()) {
+ mRunningThroughTick = false;
+ return;
+ }
+ }
+ if (aSideServer) {
+ if (++mAverageEUInputIndex >= mAverageEUInput.length) mAverageEUInputIndex = 0;
+ if (++mAverageEUOutputIndex >= mAverageEUOutput.length) mAverageEUOutputIndex = 0;
+
+ mAverageEUInput[mAverageEUInputIndex] = 0;
+ mAverageEUOutput[mAverageEUOutputIndex] = 0;
+ }
+
+ mMetaTileEntity.onPreTick(this, mTickTimer);
+
+ if (!hasValidMetaTileEntity()) {
+ mRunningThroughTick = false;
+ return;
+ }
+ if (aSideServer) {
+ if (mRedstone != oRedstone || mTickTimer == 10) {
+ updateCoverBehavior();
+ oRedstone = mRedstone;
+ issueBlockUpdate();
+ }
+ if (mTickTimer == 10) joinEnet();
+
+ if (xCoord != oX || yCoord != oY || zCoord != oZ) {
+ oX = xCoord;
+ oY = yCoord;
+ oZ = zCoord;
+ issueClientUpdate();
+ clearTileEntityBuffer();
+ }
+
+ if (mFacing != oFacing) {
+ oFacing = mFacing;
+ checkDropCover();
+ issueBlockUpdate();
+ }
+
+ if (mTickTimer > 20 && mMetaTileEntity.isElectric()) {
+ mAcceptedAmperes = 0;
+
+ if (getOutputVoltage() != oOutput) {
+ oOutput = getOutputVoltage();
+ }
+
+ if (mMetaTileEntity.isEnetOutput() || mMetaTileEntity.isEnetInput()) {
+ for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
+ final int ordinalSide = side.ordinal();
+ boolean temp = isEnergyInputSide(side);
+ if (temp != mActiveEUInputs[ordinalSide]) {
+ mActiveEUInputs[ordinalSide] = temp;
+ }
+ temp = isEnergyOutputSide(side);
+ if (temp != mActiveEUOutputs[ordinalSide]) {
+ mActiveEUOutputs[ordinalSide] = temp;
+ }
+ }
+ }
+
+ if (mMetaTileEntity.isEnetOutput() && oOutput > 0) {
+ final long tOutputVoltage = Math
+ .max(oOutput, oOutput + (1L << Math.max(0, GT_Utility.getTier(oOutput) - 1)));
+ final long tUsableAmperage = Math.min(
+ getOutputAmperage(),
+ (getStoredEU() - mMetaTileEntity.getMinimumStoredEU()) / tOutputVoltage);
+ if (tUsableAmperage > 0) {
+ final long tEU = tOutputVoltage
+ * Util.emitEnergyToNetwork(oOutput, tUsableAmperage, this);
+ mAverageEUOutput[mAverageEUOutputIndex] += tEU;
+ decreaseStoredEU(tEU, true);
+ }
+ }
+ if (getEUCapacity() > 0) {
+ if (GregTech_API.sMachineFireExplosions && getRandomNumber(1000) == 0) {
+ final Block tBlock = getBlockAtSide(ForgeDirection.getOrientation(getRandomNumber(6)));
+ if (tBlock instanceof BlockFire) doEnergyExplosion();
+ }
+
+ if (!hasValidMetaTileEntity()) {
+ mRunningThroughTick = false;
+ return;
+ }
+
+ if (GregTech_API.sMachineRainExplosions) {
+ if (mMetaTileEntity.willExplodeInRain()) {
+ if (getRandomNumber(1000) == 0 && isRainPossible()) {
+ // Short-circuit so raincheck happens before isRainExposed,
+ // saves sme TPS since rain exposed check can be slow
+ // This logic can be compressed further by only checking for
+ // isRainExposed once IF we can guarantee it never thunders without
+ // raining, but I don't know if this is true or not.
+ if (worldObj.isRaining() && isRainExposed()) {
+ if (getRandomNumber(10) == 0) {
+ try {
+ GT_Mod.achievements.issueAchievement(
+ this.getWorldObj()
+ .getPlayerEntityByName(mOwnerName),
+ "badweather");
+ } catch (Exception ignored) {}
+ GT_Log.exp.println(
+ "Machine at: " + this.getXCoord()
+ + " | "
+ + this.getYCoord()
+ + " | "
+ + this.getZCoord()
+ + " DIMID: "
+ + this.worldObj.provider.dimensionId
+ + " explosion due to rain!");
+ doEnergyExplosion();
+ } else {
+ GT_Log.exp.println(
+ "Machine at: " + this.getXCoord()
+ + " | "
+ + this.getYCoord()
+ + " | "
+ + this.getZCoord()
+ + " DIMID: "
+ + this.worldObj.provider.dimensionId
+ + " set to Fire due to rain!");
+ setOnFire();
+ }
+ }
+ if (!hasValidMetaTileEntity()) {
+ mRunningThroughTick = false;
+ return;
+ }
+ if (GregTech_API.sMachineThunderExplosions && worldObj.isThundering()
+ && getRandomNumber(3) == 0
+ && isRainExposed()) {
+ try {
+ GT_Mod.achievements.issueAchievement(
+ this.getWorldObj()
+ .getPlayerEntityByName(mOwnerName),
+ "badweather");
+ } catch (Exception ignored) {}
+ GT_Log.exp.println(
+ "Machine at: " + this.getXCoord()
+ + " | "
+ + this.getYCoord()
+ + " | "
+ + this.getZCoord()
+ + " DIMID: "
+ + this.worldObj.provider.dimensionId
+ + " explosion due to Thunderstorm!");
+ doEnergyExplosion();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (!hasValidMetaTileEntity()) {
+ mRunningThroughTick = false;
+ return;
+ }
+ }
+ if (aSideServer) {
+ if (mMetaTileEntity.dechargerSlotCount() > 0 && getStoredEU() < getEUCapacity()) {
+ for (int i = mMetaTileEntity.dechargerSlotStartIndex(),
+ k = mMetaTileEntity.dechargerSlotCount() + i; i < k; i++) {
+ if (mMetaTileEntity.mInventory[i] != null && getStoredEU() < getEUCapacity()) {
+ dischargeItem(mMetaTileEntity.mInventory[i]);
+ if (ic2.api.info.Info.itemEnergy.getEnergyValue(mMetaTileEntity.mInventory[i]) > 0) {
+ if ((getStoredEU()
+ + ic2.api.info.Info.itemEnergy.getEnergyValue(mMetaTileEntity.mInventory[i]))
+ < getEUCapacity()) {
+ increaseStoredEnergyUnits(
+ (long) ic2.api.info.Info.itemEnergy
+ .getEnergyValue(mMetaTileEntity.mInventory[i]),
+ false);
+ mMetaTileEntity.mInventory[i].stackSize--;
+ mInventoryChanged = true;
+ }
+ }
+ if (mMetaTileEntity.mInventory[i].stackSize <= 0) {
+ mMetaTileEntity.mInventory[i] = null;
+ mInventoryChanged = true;
+ }
+ }
+ }
+ }
+ }
+ if (aSideServer) {
+ if (mMetaTileEntity.rechargerSlotCount() > 0 && getStoredEU() > 0) {
+ for (int i = mMetaTileEntity.rechargerSlotStartIndex(),
+ k = mMetaTileEntity.rechargerSlotCount() + i; i < k; i++) {
+ if (getStoredEU() > 0 && mMetaTileEntity.mInventory[i] != null) {
+ chargeItem(mMetaTileEntity.mInventory[i]);
+ if (mMetaTileEntity.mInventory[i].stackSize <= 0) {
+ mMetaTileEntity.mInventory[i] = null;
+ mInventoryChanged = true;
+ }
+ }
+ }
+ }
+ }
+ updateStatus();
+ if (!hasValidMetaTileEntity()) {
+ mRunningThroughTick = false;
+ return;
+ }
+ mMetaTileEntity.onPostTick(this, mTickTimer);
+ if (!hasValidMetaTileEntity()) {
+ mRunningThroughTick = false;
+ return;
+ }
+ if (aSideServer) {
+ if (mTickTimer > 20 && cableUpdateDelay == 0) {
+ generatePowerNodes();
+ }
+ cableUpdateDelay--;
+ if (mTickTimer % 10 == 0) {
+ sendClientData();
+ }
+
+ if (mTickTimer > 10) {
+ byte tData = (byte) ((mFacing.ordinal() & 7) | (mActive ? 8 : 0)
+ | (mRedstone ? 16 : 0)
+ | (mLockUpgrade ? 32 : 0)
+ | (mWorks ? 64 : 0)
+ | (mMuffler ? 128 : 0));
+ if (tData != oTextureData)
+ sendBlockEvent(GregTechTileClientEvents.CHANGE_COMMON_DATA, oTextureData = tData);
+
+ tData = mMetaTileEntity.getUpdateData();
+ if (tData != oUpdateData)
+ sendBlockEvent(GregTechTileClientEvents.CHANGE_CUSTOM_DATA, oUpdateData = tData);
+ if (mMetaTileEntity instanceof GT_MetaTileEntity_Hatch) {
+ tData = ((GT_MetaTileEntity_Hatch) mMetaTileEntity).getTexturePage();
+ if (tData != oTexturePage) sendBlockEvent(
+ GregTechTileClientEvents.CHANGE_CUSTOM_DATA,
+ (byte) ((oTexturePage = tData) | 0x80)); // set last bit as a flag for page
+ }
+ if (mColor != oColor) sendBlockEvent(GregTechTileClientEvents.CHANGE_COLOR, oColor = mColor);
+ tData = (byte) (((mSidedRedstone[0] > 0) ? 1 : 0) | ((mSidedRedstone[1] > 0) ? 2 : 0)
+ | ((mSidedRedstone[2] > 0) ? 4 : 0)
+ | ((mSidedRedstone[3] > 0) ? 8 : 0)
+ | ((mSidedRedstone[4] > 0) ? 16 : 0)
+ | ((mSidedRedstone[5] > 0) ? 32 : 0));
+ if (tData != oRedstoneData)
+ sendBlockEvent(GregTechTileClientEvents.CHANGE_REDSTONE_OUTPUT, oRedstoneData = tData);
+ if (mLightValue != oLightValue) {
+ worldObj.setLightValue(EnumSkyBlock.Block, xCoord, yCoord, zCoord, mLightValue);
+ worldObj.updateLightByType(EnumSkyBlock.Block, xCoord, yCoord, zCoord);
+ worldObj.updateLightByType(EnumSkyBlock.Block, xCoord + 1, yCoord, zCoord);
+ worldObj.updateLightByType(EnumSkyBlock.Block, xCoord - 1, yCoord, zCoord);
+ worldObj.updateLightByType(EnumSkyBlock.Block, xCoord, yCoord + 1, zCoord);
+ worldObj.updateLightByType(EnumSkyBlock.Block, xCoord, yCoord - 1, zCoord);
+ worldObj.updateLightByType(EnumSkyBlock.Block, xCoord, yCoord, zCoord + 1);
+ worldObj.updateLightByType(EnumSkyBlock.Block, xCoord, yCoord, zCoord - 1);
+ issueTextureUpdate();
+ sendBlockEvent(GregTechTileClientEvents.CHANGE_LIGHT, oLightValue = mLightValue);
+ }
+ }
+
+ if (mNeedsBlockUpdate) {
+ updateNeighbours(mStrongRedstone, oStrongRedstone);
+ oStrongRedstone = mStrongRedstone;
+ mNeedsBlockUpdate = false;
+ }
+ }
+ }
+ } catch (Throwable e) {
+ e.printStackTrace();
+ e.printStackTrace(GT_Log.err);
+ try {
+ mMetaTileEntity.onTickFail(this, mTickTimer);
+ } catch (Throwable ex) {
+ ex.printStackTrace();
+ ex.printStackTrace(GT_Log.err);
+ }
+ }
+
+ if (aSideServer && hasTimeStatisticsStarted && hasValidMetaTileEntity()) {
+ tTime = System.nanoTime() - tTime;
+ mTimeStatisticsIndex = (mTimeStatisticsIndex + 1) % mTimeStatistics.length;
+ mTimeStatistics[mTimeStatisticsIndex] = (int) tTime;
+ if (tTime > 0 && tTime > (GregTech_API.MILLISECOND_THRESHOLD_UNTIL_LAG_WARNING * 1_000_000L)
+ && mTickTimer > 1000
+ && getMetaTileEntity().doTickProfilingMessageDuringThisTick()
+ && mLagWarningCount++ < 10)
+ GT_FML_LOGGER.warn(
+ "WARNING: Possible Lag Source at [" + xCoord
+ + ", "
+ + yCoord
+ + ", "
+ + zCoord
+ + "] in Dimension "
+ + worldObj.provider.dimensionId
+ + " with "
+ + tTime
+ + "ns caused by an instance of "
+ + getMetaTileEntity().getClass());
+ }
+
+ mWorkUpdate = mInventoryChanged = mRunningThroughTick = false;
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currentTip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ if (hasValidMetaTileEntity()) {
+ getMetaTileEntity().getWailaBody(itemStack, currentTip, accessor, 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);
+ if (hasValidMetaTileEntity()) {
+ getMetaTileEntity().getWailaNBTData(player, tile, tag, world, x, y, z);
+ }
+ }
+
+ private void sendClientData() {
+ if (mSendClientData) {
+ NW.sendPacketToAllPlayersInRange(
+ worldObj,
+ new GT_Packet_TileEntity(
+ xCoord,
+ (short) yCoord,
+ zCoord,
+ mID,
+ getCoverInfoAtSide(ForgeDirection.DOWN).getCoverID(),
+ getCoverInfoAtSide(ForgeDirection.UP).getCoverID(),
+ getCoverInfoAtSide(ForgeDirection.NORTH).getCoverID(),
+ getCoverInfoAtSide(ForgeDirection.SOUTH).getCoverID(),
+ getCoverInfoAtSide(ForgeDirection.WEST).getCoverID(),
+ getCoverInfoAtSide(ForgeDirection.EAST).getCoverID(),
+ oTextureData = (byte) ((mFacing.ordinal() & 7) | (mActive ? 8 : 0)
+ | (mRedstone ? 16 : 0)
+ | (mLockUpgrade ? 32 : 0)
+ | (mWorks ? 64 : 0)),
+ oTexturePage = (hasValidMetaTileEntity() && mMetaTileEntity instanceof GT_MetaTileEntity_Hatch)
+ ? ((GT_MetaTileEntity_Hatch) mMetaTileEntity).getTexturePage()
+ : 0,
+ oUpdateData = hasValidMetaTileEntity() ? mMetaTileEntity.getUpdateData() : 0,
+ oRedstoneData = (byte) (((mSidedRedstone[0] > 0) ? 1 : 0) | ((mSidedRedstone[1] > 0) ? 2 : 0)
+ | ((mSidedRedstone[2] > 0) ? 4 : 0)
+ | ((mSidedRedstone[3] > 0) ? 8 : 0)
+ | ((mSidedRedstone[4] > 0) ? 16 : 0)
+ | ((mSidedRedstone[5] > 0) ? 32 : 0)),
+ oColor = mColor),
+ xCoord,
+ zCoord);
+ mSendClientData = false;
+ }
+ sendCoverDataIfNeeded();
+ }
+
+ public final void receiveMetaTileEntityData(short aID, int aCover0, int aCover1, int aCover2, int aCover3,
+ int aCover4, int aCover5, byte aTextureData, byte aTexturePage, byte aUpdateData, byte aRedstoneData,
+ byte aColorData) {
+ issueTextureUpdate();
+ if (mID != aID && aID > 0) {
+ mID = aID;
+ createNewMetatileEntity(mID);
+ }
+
+ setCoverIDAtSide(ForgeDirection.DOWN, aCover0);
+ setCoverIDAtSide(ForgeDirection.UP, aCover1);
+ setCoverIDAtSide(ForgeDirection.NORTH, aCover2);
+ setCoverIDAtSide(ForgeDirection.SOUTH, aCover3);
+ setCoverIDAtSide(ForgeDirection.WEST, aCover4);
+ setCoverIDAtSide(ForgeDirection.EAST, aCover5);
+
+ receiveClientEvent(GregTechTileClientEvents.CHANGE_COMMON_DATA, aTextureData);
+ receiveClientEvent(GregTechTileClientEvents.CHANGE_CUSTOM_DATA, aUpdateData & 0x7F);
+ receiveClientEvent(GregTechTileClientEvents.CHANGE_CUSTOM_DATA, aTexturePage | 0x80);
+ receiveClientEvent(GregTechTileClientEvents.CHANGE_COLOR, aColorData);
+ receiveClientEvent(GregTechTileClientEvents.CHANGE_REDSTONE_OUTPUT, aRedstoneData);
+ }
+
+ @Override
+ public boolean receiveClientEvent(int aEventID, int aValue) {
+ super.receiveClientEvent(aEventID, aValue);
+
+ if (hasValidMetaTileEntity()) {
+ try {
+ mMetaTileEntity.receiveClientEvent((byte) aEventID, (byte) aValue);
+ } catch (Throwable e) {
+ GT_Log.err.println(
+ "Encountered Exception while receiving Data from the Server, the Client should've been crashed by now, but I prevented that. Please report immediately to GregTech Intergalactical!!!");
+ e.printStackTrace(GT_Log.err);
+ }
+ }
+
+ if (isClientSide()) {
+ issueTextureUpdate();
+ switch (aEventID) {
+ case GregTechTileClientEvents.CHANGE_COMMON_DATA -> {
+ mFacing = ForgeDirection.getOrientation((byte) (aValue & 7));
+ mActive = ((aValue & 8) != 0);
+ mRedstone = ((aValue & 16) != 0);
+ // mLockUpgrade = ((aValue&32) != 0);
+ mWorks = ((aValue & 64) != 0);
+ mMuffler = ((aValue & 128) != 0);
+ }
+ case GregTechTileClientEvents.CHANGE_CUSTOM_DATA -> {
+ if (hasValidMetaTileEntity()) {
+ if ((aValue & 0x80) == 0) // Is texture index
+ mMetaTileEntity.onValueUpdate((byte) (aValue & 0x7F));
+ else if (mMetaTileEntity instanceof GT_MetaTileEntity_Hatch) // is texture page and hatch
+ ((GT_MetaTileEntity_Hatch) mMetaTileEntity).onTexturePageUpdate((byte) (aValue & 0x7F));
+ }
+ }
+ case GregTechTileClientEvents.CHANGE_COLOR -> {
+ if (aValue > 16 || aValue < 0) aValue = 0;
+ mColor = (byte) aValue;
+ }
+ case GregTechTileClientEvents.CHANGE_REDSTONE_OUTPUT -> {
+ mSidedRedstone[0] = (byte) ((aValue & 1) == 1 ? 15 : 0);
+ mSidedRedstone[1] = (byte) ((aValue & 2) == 2 ? 15 : 0);
+ mSidedRedstone[2] = (byte) ((aValue & 4) == 4 ? 15 : 0);
+ mSidedRedstone[3] = (byte) ((aValue & 8) == 8 ? 15 : 0);
+ mSidedRedstone[4] = (byte) ((aValue & 16) == 16 ? 15 : 0);
+ mSidedRedstone[5] = (byte) ((aValue & 32) == 32 ? 15 : 0);
+ }
+ case GregTechTileClientEvents.DO_SOUND -> {
+ if (hasValidMetaTileEntity() && mTickTimer > 20)
+ mMetaTileEntity.doSound((byte) aValue, xCoord + 0.5, yCoord + 0.5, zCoord + 0.5);
+ }
+ case GregTechTileClientEvents.START_SOUND_LOOP -> {
+ if (hasValidMetaTileEntity() && mTickTimer > 20)
+ mMetaTileEntity.startSoundLoop((byte) aValue, xCoord + 0.5, yCoord + 0.5, zCoord + 0.5);
+ }
+ case GregTechTileClientEvents.STOP_SOUND_LOOP -> {
+ if (hasValidMetaTileEntity() && mTickTimer > 20)
+ mMetaTileEntity.stopSoundLoop((byte) aValue, xCoord + 0.5, yCoord + 0.5, zCoord + 0.5);
+ }
+ case GregTechTileClientEvents.CHANGE_LIGHT -> mLightValue = (byte) aValue;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public ArrayList<String> getDebugInfo(EntityPlayer aPlayer, int aLogLevel) {
+ final ArrayList<String> tList = new ArrayList<>();
+ if (aLogLevel > 2) {
+ tList.add(
+ "Meta-ID: " + EnumChatFormatting.BLUE
+ + mID
+ + EnumChatFormatting.RESET
+ + (canAccessData() ? EnumChatFormatting.GREEN + " valid" + EnumChatFormatting.RESET
+ : EnumChatFormatting.RED + " invalid" + EnumChatFormatting.RESET)
+ + (mMetaTileEntity == null
+ ? EnumChatFormatting.RED + " MetaTileEntity == null!" + EnumChatFormatting.RESET
+ : " "));
+ }
+ if (aLogLevel > 1 && mMetaTileEntity != null) {
+ if (hasTimeStatisticsStarted) {
+ double tAverageTime = 0;
+ double tWorstTime = 0;
+ int amountOfZero = 0;
+ for (int tTime : mTimeStatistics) {
+ tAverageTime += tTime;
+ if (tTime > tWorstTime) {
+ tWorstTime = tTime;
+ }
+ if (tTime == 0) {
+ amountOfZero += 1;
+ }
+ // Uncomment this line to print out tick-by-tick times.
+ // tList.add("tTime " + tTime);
+ }
+ // tick time zero means it has not been updated yet
+ int samples = mTimeStatistics.length - amountOfZero;
+ if (samples > 0) {
+ tList.add(
+ "Average CPU load of ~" + GT_Utility.formatNumbers(tAverageTime / samples)
+ + "ns over "
+ + GT_Utility.formatNumbers(samples)
+ + " ticks with worst time of "
+ + GT_Utility.formatNumbers(tWorstTime)
+ + "ns.");
+ }
+ } else {
+ startTimeStatistics();
+ tList.add("Just started tick time statistics.");
+ }
+ tList.add(
+ "Recorded " + GT_Utility.formatNumbers(mMetaTileEntity.mSoundRequests)
+ + " sound requests in "
+ + GT_Utility.formatNumbers(mTickTimer - mLastCheckTick)
+ + " ticks.");
+ mLastCheckTick = mTickTimer;
+ mMetaTileEntity.mSoundRequests = 0;
+ if (mLagWarningCount > 0) {
+ tList.add(
+ "Caused " + (mLagWarningCount >= 10 ? "more than 10" : mLagWarningCount)
+ + " Lag Spike Warnings (anything taking longer than "
+ + GregTech_API.MILLISECOND_THRESHOLD_UNTIL_LAG_WARNING
+ + "ms) on the Server.");
+ }
+ tList.add(
+ "Is" + (mMetaTileEntity.isAccessAllowed(aPlayer) ? " "
+ : EnumChatFormatting.RED + " not " + EnumChatFormatting.RESET) + "accessible for you");
+ }
+ if (aLogLevel > 0) {
+ if (getSteamCapacity() > 0 && hasSteamEngineUpgrade()) tList.add(
+ GT_Utility.formatNumbers(getStoredSteam()) + " of "
+ + GT_Utility.formatNumbers(getSteamCapacity())
+ + " Steam");
+ tList.add(
+ "Machine is " + (mActive ? EnumChatFormatting.GREEN + "active" + EnumChatFormatting.RESET
+ : EnumChatFormatting.RED + "inactive" + EnumChatFormatting.RESET));
+ if (!mHasEnoughEnergy) tList
+ .add(EnumChatFormatting.RED + "ATTENTION: This Device needs more power." + EnumChatFormatting.RESET);
+ }
+ if (joinedIc2Enet) tList.add("Joined IC2 ENet");
+ return mMetaTileEntity.getSpecialDebugInfo(this, aPlayer, aLogLevel, tList);
+ }
+
+ @Override
+ public boolean isGivingInformation() {
+ if (canAccessData()) return mMetaTileEntity.isGivingInformation();
+ return false;
+ }
+
+ @Override
+ public ForgeDirection getBackFacing() {
+ return mFacing.getOpposite();
+ }
+
+ @Override
+ public ForgeDirection getFrontFacing() {
+ return mFacing;
+ }
+
+ @Override
+ public void setFrontFacing(ForgeDirection aFacing) {
+ if (isValidFacing(aFacing)) {
+ mFacing = aFacing;
+ mMetaTileEntity.onFacingChange();
+
+ doEnetUpdate();
+ cableUpdateDelay = 10;
+
+ if (mMetaTileEntity.shouldTriggerBlockUpdate()) {
+ // If we're triggering a block update this will call onMachineBlockUpdate()
+ GregTech_API.causeMachineUpdate(worldObj, xCoord, yCoord, zCoord);
+ } else {
+ // If we're not trigger a cascading one, call the update here.
+ onMachineBlockUpdate();
+ }
+ }
+ }
+
+ @Override
+ public int getSizeInventory() {
+ if (canAccessData()) return mMetaTileEntity.getSizeInventory();
+ return 0;
+ }
+
+ @Override
+ public ItemStack getStackInSlot(int aIndex) {
+ if (canAccessData()) return mMetaTileEntity.getStackInSlot(aIndex);
+ return null;
+ }
+
+ @Override
+ public void setInventorySlotContents(int aIndex, ItemStack aStack) {
+ mInventoryChanged = true;
+ if (canAccessData()) {
+ markDirty();
+ mMetaTileEntity.setInventorySlotContents(
+ aIndex,
+ worldObj.isRemote ? aStack : GT_OreDictUnificator.setStack(true, aStack));
+ }
+ }
+
+ @Override
+ public String getInventoryName() {
+ if (canAccessData()) return mMetaTileEntity.getInventoryName();
+ if (GregTech_API.METATILEENTITIES[mID] != null) return GregTech_API.METATILEENTITIES[mID].getInventoryName();
+ return "";
+ }
+
+ @Override
+ public int getInventoryStackLimit() {
+ if (canAccessData()) return mMetaTileEntity.getInventoryStackLimit();
+ return 64;
+ }
+
+ @Override
+ public void openInventory() {
+ if (canAccessData()) mMetaTileEntity.onOpenGUI();
+ }
+
+ @Override
+ public void closeInventory() {
+ if (canAccessData()) mMetaTileEntity.onCloseGUI();
+ }
+
+ @Override
+ public boolean isUseableByPlayer(EntityPlayer aPlayer) {
+ return canAccessData() && playerOwnsThis(aPlayer, false)
+ && mTickTimer > 1
+ && getTileEntityOffset(0, 0, 0) == this
+ && aPlayer.getDistanceSq(xCoord + 0.5, yCoord + 0.5, zCoord + 0.5) < 64
+ && mMetaTileEntity.isAccessAllowed(aPlayer);
+ }
+
+ @Override
+ public void validate() {
+ super.validate();
+ mTickTimer = 0;
+ }
+
+ @Override
+ public void invalidate() {
+ tileEntityInvalid = false;
+ leaveEnet();
+ if (canAccessData()) {
+ invalidateAE();
+ mMetaTileEntity.onRemoval();
+ mMetaTileEntity.setBaseMetaTileEntity(null);
+ }
+ super.invalidate();
+ }
+
+ @Override
+ public void onChunkUnload() {
+ if (canAccessData()) {
+ mMetaTileEntity.onUnload();
+ }
+
+ super.onChunkUnload();
+ onChunkUnloadAE();
+ }
+
+ @Override
+ public boolean hasCustomInventoryName() {
+ return false;
+ }
+
+ @Override
+ public ItemStack getStackInSlotOnClosing(int slot) {
+ final ItemStack stack = getStackInSlot(slot);
+ if (stack != null) setInventorySlotContents(slot, null);
+ return stack;
+ }
+
+ /**
+ * Checks validity of meta tile and delegates to it
+ */
+ @Override
+ public void onMachineBlockUpdate() {
+ if (canAccessData()) mMetaTileEntity.onMachineBlockUpdate();
+ cableUpdateDelay = 10;
+ }
+
+ /**
+ * Checks validity of meta tile and delegates to it
+ */
+ @Override
+ public boolean isMachineBlockUpdateRecursive() {
+ return canAccessData() && mMetaTileEntity.isMachineBlockUpdateRecursive();
+ }
+
+ @Override
+ public int getProgress() {
+ return canAccessData() ? mMetaTileEntity.getProgresstime() : 0;
+ }
+
+ @Override
+ public int getMaxProgress() {
+ return canAccessData() ? mMetaTileEntity.maxProgresstime() : 0;
+ }
+
+ @Override
+ public boolean increaseProgress(int aProgressAmountInTicks) {
+ return canAccessData() && mMetaTileEntity.increaseProgress(aProgressAmountInTicks) != aProgressAmountInTicks;
+ }
+
+ @Override
+ public boolean hasThingsToDo() {
+ return getMaxProgress() > 0;
+ }
+
+ @Override
+ public void enableWorking() {
+ if (!mWorks) mWorkUpdate = true;
+ mWorks = true;
+ mWasShutdown = false;
+ }
+
+ @Override
+ public void disableWorking() {
+ mWorks = false;
+ if (hasValidMetaTileEntity()) {
+ mMetaTileEntity.onDisableWorking();
+ }
+ }
+
+ @Override
+ public boolean isAllowedToWork() {
+ return mWorks;
+ }
+
+ @Override
+ public boolean hasWorkJustBeenEnabled() {
+ return mWorkUpdate;
+ }
+
+ @Override
+ public byte getWorkDataValue() {
+ return mWorkData;
+ }
+
+ @Override
+ public void setWorkDataValue(byte aValue) {
+ mWorkData = aValue;
+ }
+
+ @Override
+ public int getMetaTileID() {
+ return mID;
+ }
+
+ @Override
+ public int setMetaTileID(short aID) {
+ return mID = aID;
+ }
+
+ @Override
+ public boolean isActive() {
+ return mActive;
+ }
+
+ @Override
+ public void setActive(boolean aActive) {
+ mActive = aActive;
+ if (hasValidMetaTileEntity()) {
+ mMetaTileEntity.onSetActive(aActive);
+ }
+ }
+
+ @Override
+ public long getTimer() {
+ return mTickTimer;
+ }
+
+ @Override
+ public boolean decreaseStoredEnergyUnits(long aEnergy, boolean aIgnoreTooLessEnergy) {
+ if (!canAccessData()) return false;
+ return mHasEnoughEnergy = decreaseStoredEU(aEnergy, aIgnoreTooLessEnergy) || decreaseStoredSteam(aEnergy, false)
+ || (aIgnoreTooLessEnergy && (decreaseStoredSteam(aEnergy, true)));
+ }
+
+ @Override
+ public boolean increaseStoredEnergyUnits(long aEnergy, boolean aIgnoreTooMuchEnergy) {
+ if (!canAccessData()) return false;
+ if (getStoredEU() < getEUCapacity() || aIgnoreTooMuchEnergy) {
+ setStoredEU(mMetaTileEntity.getEUVar() + aEnergy);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean inputEnergyFrom(ForgeDirection side) {
+ return inputEnergyFrom(side, true);
+ }
+
+ @Override
+ public boolean inputEnergyFrom(ForgeDirection side, boolean waitForActive) {
+ if (side == ForgeDirection.UNKNOWN) return true;
+ if (isServerSide() && waitForActive) return mActiveEUInputs[side.ordinal()] && !mReleaseEnergy;
+ return isEnergyInputSide(side);
+ }
+
+ @Override
+ public boolean outputsEnergyTo(ForgeDirection side) {
+ return outputsEnergyTo(side, true);
+ }
+
+ @Override
+ public boolean outputsEnergyTo(ForgeDirection side, boolean waitForActive) {
+ if (side == ForgeDirection.UNKNOWN) return true;
+ if (isServerSide() && waitForActive) return (mActiveEUOutputs[side.ordinal()]) || mReleaseEnergy;
+ return isEnergyOutputSide(side);
+ }
+
+ @Override
+ public boolean isEnetOutput() {
+ return mMetaTileEntity != null && mMetaTileEntity.isEnetOutput();
+ }
+
+ @Override
+ public boolean isEnetInput() {
+ return mMetaTileEntity != null && mMetaTileEntity.isEnetInput();
+ }
+
+ public void generatePowerNodes() {
+ if (isServerSide() && (isEnetInput() || isEnetOutput())) {
+ final int time = MinecraftServer.getServer()
+ .getTickCounter();
+ for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
+ if (outputsEnergyTo(side, false) || inputEnergyFrom(side, false)) {
+ final IGregTechTileEntity TE = getIGregTechTileEntityAtSide(side);
+ if (TE instanceof BaseMetaPipeEntity) {
+ final Node node = ((BaseMetaPipeEntity) TE).getNode();
+ if (node == null) {
+ new GenerateNodeMapPower((BaseMetaPipeEntity) TE);
+ } else if (node.mCreationTime != time) {
+ GenerateNodeMap.clearNodeMap(node, -1);
+ new GenerateNodeMapPower((BaseMetaPipeEntity) TE);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public long getOutputAmperage() {
+ if (canAccessData() && mMetaTileEntity.isElectric()) return mMetaTileEntity.maxAmperesOut();
+ return 0;
+ }
+
+ @Override
+ public long getOutputVoltage() {
+ if (canAccessData() && mMetaTileEntity.isElectric() && mMetaTileEntity.isEnetOutput())
+ return mMetaTileEntity.maxEUOutput();
+ return 0;
+ }
+
+ @Override
+ public long getInputAmperage() {
+ if (canAccessData() && mMetaTileEntity.isElectric()) return mMetaTileEntity.maxAmperesIn();
+ return 0;
+ }
+
+ @Override
+ public long getInputVoltage() {
+ if (canAccessData() && mMetaTileEntity.isElectric()) return mMetaTileEntity.maxEUInput();
+ return Integer.MAX_VALUE;
+ }
+
+ @Override
+ public boolean increaseStoredSteam(long aEnergy, boolean aIgnoreTooMuchEnergy) {
+ if (!canAccessData()) return false;
+ if (mMetaTileEntity.getSteamVar() < getSteamCapacity() || aIgnoreTooMuchEnergy) {
+ setStoredSteam(mMetaTileEntity.getSteamVar() + aEnergy);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public long getUniversalEnergyStored() {
+ return Math.max(getStoredEU(), getStoredSteam());
+ }
+
+ @Override
+ public long getUniversalEnergyCapacity() {
+ return Math.max(getEUCapacity(), getSteamCapacity());
+ }
+
+ @Override
+ public long getStoredEU() {
+ if (canAccessData()) return Math.min(mMetaTileEntity.getEUVar(), getEUCapacity());
+ return 0;
+ }
+
+ @Override
+ public long getEUCapacity() {
+ if (canAccessData()) return mMetaTileEntity.maxEUStore();
+ return 0;
+ }
+
+ @Override
+ public long getStoredSteam() {
+ if (canAccessData()) return Math.min(mMetaTileEntity.getSteamVar(), getSteamCapacity());
+ return 0;
+ }
+
+ @Override
+ public long getSteamCapacity() {
+ if (canAccessData()) return mMetaTileEntity.maxSteamStore();
+ return 0;
+ }
+
+ @Override
+ public ITexture[] getTexture(Block aBlock, ForgeDirection side) {
+ final ITexture coverTexture = getCoverTexture(side);
+ final ITexture[] textureUncovered = hasValidMetaTileEntity()
+ ? mMetaTileEntity
+ .getTexture(this, side, mFacing, (byte) (mColor - 1), mActive, getOutputRedstoneSignal(side) > 0)
+ : Textures.BlockIcons.ERROR_RENDERING;
+ final ITexture[] textureCovered;
+ if (coverTexture != null) {
+ textureCovered = Arrays.copyOf(textureUncovered, textureUncovered.length + 1);
+ textureCovered[textureUncovered.length] = coverTexture;
+ return textureCovered;
+ } else {
+ return textureUncovered;
+ }
+ }
+
+ private boolean isEnergyInputSide(ForgeDirection side) {
+ if (side != ForgeDirection.UNKNOWN) {
+ if (!getCoverInfoAtSide(side).letsEnergyIn()) return false;
+ if (isInvalid() || mReleaseEnergy) return false;
+ if (canAccessData() && mMetaTileEntity.isElectric() && mMetaTileEntity.isEnetInput())
+ return mMetaTileEntity.isInputFacing(side);
+ }
+ return false;
+ }
+
+ private boolean isEnergyOutputSide(ForgeDirection side) {
+ if (side != ForgeDirection.UNKNOWN) {
+ if (!getCoverInfoAtSide(side).letsEnergyOut()) return false;
+ if (isInvalid() || mReleaseEnergy) return mReleaseEnergy;
+ if (canAccessData() && mMetaTileEntity.isElectric() && mMetaTileEntity.isEnetOutput())
+ return mMetaTileEntity.isOutputFacing(side);
+ }
+ return false;
+ }
+
+ @Override
+ protected boolean hasValidMetaTileEntity() {
+ return mMetaTileEntity != null && mMetaTileEntity.getBaseMetaTileEntity() == this;
+ }
+
+ @Override
+ protected boolean canAccessData() {
+ return !isDead && hasValidMetaTileEntity();
+ }
+
+ public boolean setStoredEU(long aEnergy) {
+ if (!canAccessData()) return false;
+ if (aEnergy < 0) aEnergy = 0;
+ mMetaTileEntity.setEUVar(aEnergy);
+ return true;
+ }
+
+ public boolean setStoredSteam(long aEnergy) {
+ if (!canAccessData()) return false;
+ if (aEnergy < 0) aEnergy = 0;
+ mMetaTileEntity.setSteamVar(aEnergy);
+ return true;
+ }
+
+ public boolean decreaseStoredEU(long aEnergy, boolean aIgnoreTooLessEnergy) {
+ if (!canAccessData()) {
+ return false;
+ }
+ if (mMetaTileEntity.getEUVar() - aEnergy >= 0 || aIgnoreTooLessEnergy) {
+ setStoredEU(mMetaTileEntity.getEUVar() - aEnergy);
+ if (mMetaTileEntity.getEUVar() < 0) {
+ setStoredEU(0);
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public boolean decreaseStoredSteam(long aEnergy, boolean aIgnoreTooLessEnergy) {
+ if (!canAccessData()) return false;
+ if (mMetaTileEntity.getSteamVar() - aEnergy >= 0 || aIgnoreTooLessEnergy) {
+ setStoredSteam(mMetaTileEntity.getSteamVar() - aEnergy);
+ if (mMetaTileEntity.getSteamVar() < 0) {
+ setStoredSteam(0);
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public boolean playerOwnsThis(EntityPlayer aPlayer, boolean aCheckPrecicely) {
+ if (!canAccessData()) return false;
+ if (aCheckPrecicely || privateAccess() || (mOwnerName.length() == 0))
+ if ((mOwnerName.length() == 0) && isServerSide()) {
+ setOwnerName(aPlayer.getDisplayName());
+ setOwnerUuid(aPlayer.getUniqueID());
+ } else return !privateAccess() || aPlayer.getDisplayName()
+ .equals("Player") || mOwnerName.equals("Player") || mOwnerName.equals(aPlayer.getDisplayName());
+ return true;
+ }
+
+ public boolean privateAccess() {
+ if (!canAccessData()) return mLockUpgrade;
+ return mLockUpgrade || mMetaTileEntity.ownerControl();
+ }
+
+ @Nullable
+ @Override
+ public ICleanroom getCleanroom() {
+ if (canAccessData()) {
+ return mMetaTileEntity.getCleanroom();
+ }
+ return null;
+ }
+
+ @Override
+ public void setCleanroom(ICleanroom cleanroom) {
+ if (canAccessData()) {
+ mMetaTileEntity.setCleanroom(cleanroom);
+ }
+ }
+
+ public void doEnergyExplosion() {
+ if (getUniversalEnergyCapacity() > 0 && getUniversalEnergyStored() >= getUniversalEnergyCapacity() / 5) {
+ GT_Log.exp.println(
+ "Energy Explosion, injected " + getUniversalEnergyStored()
+ + "EU >= "
+ + getUniversalEnergyCapacity() / 5D
+ + "Capacity of the Machine!");
+
+ doExplosion(
+ oOutput * (getUniversalEnergyStored() >= getUniversalEnergyCapacity() ? 4
+ : getUniversalEnergyStored() >= getUniversalEnergyCapacity() / 2 ? 2 : 1));
+ GT_Mod.achievements.issueAchievement(
+ this.getWorldObj()
+ .getPlayerEntityByName(mOwnerName),
+ "electricproblems");
+ }
+ }
+
+ @Override
+ public void doExplosion(long aAmount) {
+ if (canAccessData()) {
+ // This is only for Electric Machines
+ if (GregTech_API.sMachineWireFire && mMetaTileEntity.isElectric()) {
+ try {
+ mReleaseEnergy = true;
+ IEnergyConnected.Util.emitEnergyToNetwork(V[5], Math.max(1, getStoredEU() / V[5]), this);
+ } catch (Exception ignored) {}
+ }
+ mReleaseEnergy = false;
+ // Normal Explosion Code
+ mMetaTileEntity.onExplosion();
+ if (GT_Mod.gregtechproxy.mExplosionItemDrop) {
+ for (int i = 0; i < this.getSizeInventory(); i++) {
+ final ItemStack tItem = this.getStackInSlot(i);
+ if ((tItem != null) && (tItem.stackSize > 0) && (this.isValidSlot(i))) {
+ dropItems(tItem);
+ this.setInventorySlotContents(i, null);
+ }
+ }
+ }
+ if (mRecipeStuff != null) {
+ for (int i = 0; i < 9; i++) {
+ if (this.getRandomNumber(100) < 50) {
+ dropItems(GT_Utility.loadItem(mRecipeStuff, "Ingredient." + i));
+ }
+ }
+ }
+
+ GT_Pollution.addPollution((TileEntity) this, GT_Mod.gregtechproxy.mPollutionOnExplosion);
+ mMetaTileEntity.doExplosion(aAmount);
+ }
+ }
+
+ public void dropItems(ItemStack tItem) {
+ if (tItem == null) return;
+ final EntityItem tItemEntity = new EntityItem(
+ this.worldObj,
+ this.xCoord + XSTR_INSTANCE.nextFloat() * 0.8F + 0.1F,
+ this.yCoord + XSTR_INSTANCE.nextFloat() * 0.8F + 0.1F,
+ this.zCoord + XSTR_INSTANCE.nextFloat() * 0.8F + 0.1F,
+ new ItemStack(tItem.getItem(), tItem.stackSize, tItem.getItemDamage()));
+ if (tItem.hasTagCompound()) {
+ tItemEntity.getEntityItem()
+ .setTagCompound(
+ (NBTTagCompound) tItem.getTagCompound()
+ .copy());
+ }
+ tItemEntity.motionX = (XSTR_INSTANCE.nextGaussian() * 0.0500000007450581D);
+ tItemEntity.motionY = (XSTR_INSTANCE.nextGaussian() * 0.0500000007450581D + 0.2000000029802322D);
+ tItemEntity.motionZ = (XSTR_INSTANCE.nextGaussian() * 0.0500000007450581D);
+ tItemEntity.hurtResistantTime = 999999;
+ tItemEntity.lifespan = 60000;
+ try {
+ ENTITY_ITEM_HEALTH_FIELD.setInt(tItemEntity, 99999999);
+ } catch (Exception ignored) {}
+ this.worldObj.spawnEntityInWorld(tItemEntity);
+ tItem.stackSize = 0;
+ }
+
+ @Override
+ public ArrayList<ItemStack> getDrops() {
+ final ItemStack rStack = new ItemStack(GregTech_API.sBlockMachines, 1, mID);
+ final NBTTagCompound tNBT = new NBTTagCompound();
+ if (mRecipeStuff != null && !mRecipeStuff.hasNoTags()) tNBT.setTag("GT.CraftingComponents", mRecipeStuff);
+ if (mMuffler) tNBT.setBoolean("mMuffler", mMuffler);
+ if (mLockUpgrade) tNBT.setBoolean("mLockUpgrade", mLockUpgrade);
+ if (mSteamConverter) tNBT.setBoolean("mSteamConverter", mSteamConverter);
+ if (mColor > 0) tNBT.setByte("mColor", mColor);
+ if (mOtherUpgrades > 0) tNBT.setByte("mOtherUpgrades", mOtherUpgrades);
+
+ writeCoverNBT(tNBT, true);
+
+ if (hasValidMetaTileEntity()) mMetaTileEntity.setItemNBT(tNBT);
+ if (!tNBT.hasNoTags()) rStack.setTagCompound(tNBT);
+
+ onBaseTEDestroyed();
+ return new ArrayList<>(Collections.singletonList(rStack));
+ }
+
+ @Override
+ public boolean shouldDropItemAt(int index) {
+ return this.mMetaTileEntity == null || this.mMetaTileEntity.shouldDropItemAt(index);
+ }
+
+ public int getUpgradeCount() {
+ return (mMuffler ? 1 : 0) + (mLockUpgrade ? 1 : 0) + (mSteamConverter ? 1 : 0) + mOtherUpgrades;
+ }
+
+ @Override
+ public boolean onRightclick(EntityPlayer aPlayer, ForgeDirection side, float aX, float aY, float aZ) {
+ if (isClientSide()) {
+ // Configure Cover, sneak can also be: screwdriver, wrench, side cutter, soldering iron
+ if (aPlayer.isSneaking()) {
+ final ForgeDirection tSide = (getCoverIDAtSide(side) == 0)
+ ? GT_Utility.determineWrenchingSide(side, aX, aY, aZ)
+ : side;
+ return (getCoverBehaviorAtSideNew(tSide).hasCoverGUI());
+ } else if (getCoverBehaviorAtSideNew(side).onCoverRightclickClient(side, this, aPlayer, aX, aY, aZ)) {
+ return true;
+ }
+
+ if (!getCoverInfoAtSide(side).isGUIClickable()) return false;
+ }
+
+ if (isServerSide()) {
+ if (!privateAccess() || aPlayer.getDisplayName()
+ .equalsIgnoreCase(getOwnerName())) {
+ final ItemStack tCurrentItem = aPlayer.inventory.getCurrentItem();
+ if (tCurrentItem != null) {
+ if (getColorization() >= 0
+ && GT_Utility.areStacksEqual(new ItemStack(Items.water_bucket, 1), tCurrentItem)) {
+ tCurrentItem.func_150996_a(Items.bucket);
+ setColorization((byte) (getColorization() >= 16 ? -2 : -1));
+ return true;
+ }
+ if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sWrenchList)) {
+ if (aPlayer.isSneaking() && mMetaTileEntity instanceof GT_MetaTileEntity_BasicMachine
+ && ((GT_MetaTileEntity_BasicMachine) mMetaTileEntity)
+ .setMainFacing(GT_Utility.determineWrenchingSide(side, aX, aY, aZ))) {
+ GT_ModHandler.damageOrDechargeItem(tCurrentItem, 1, 1000, aPlayer);
+ GT_Utility.sendSoundToPlayers(
+ worldObj,
+ SoundResource.IC2_TOOLS_WRENCH,
+ 1.0F,
+ -1,
+ xCoord,
+ yCoord,
+ zCoord);
+ cableUpdateDelay = 10;
+ } else if (mMetaTileEntity.onWrenchRightClick(
+ side,
+ GT_Utility.determineWrenchingSide(side, aX, aY, aZ),
+ aPlayer,
+ aX,
+ aY,
+ aZ,
+ tCurrentItem)) {
+ GT_ModHandler.damageOrDechargeItem(tCurrentItem, 1, 1000, aPlayer);
+ GT_Utility.sendSoundToPlayers(
+ worldObj,
+ SoundResource.IC2_TOOLS_WRENCH,
+ 1.0F,
+ -1,
+ xCoord,
+ yCoord,
+ zCoord);
+ cableUpdateDelay = 10;
+ }
+ return true;
+ }
+
+ if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sScrewdriverList)) {
+ if (GT_ModHandler.damageOrDechargeItem(tCurrentItem, 1, 200, aPlayer)) {
+ setCoverDataAtSide(
+ side,
+ getCoverBehaviorAtSideNew(side).onCoverScrewdriverClick(
+ side,
+ getCoverIDAtSide(side),
+ getComplexCoverDataAtSide(side),
+ this,
+ aPlayer,
+ aX,
+ aY,
+ aZ));
+ mMetaTileEntity.onScrewdriverRightClick(side, aPlayer, aX, aY, aZ, tCurrentItem);
+ GT_Utility.sendSoundToPlayers(
+ worldObj,
+ SoundResource.IC2_TOOLS_WRENCH,
+ 1.0F,
+ -1,
+ xCoord,
+ yCoord,
+ zCoord);
+ }
+ return true;
+ }
+
+ if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sHardHammerList)) {
+ if (GT_ModHandler.damageOrDechargeItem(tCurrentItem, 1, 1000, aPlayer)) {
+ mInputDisabled = !mInputDisabled;
+ if (mInputDisabled) mOutputDisabled = !mOutputDisabled;
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ GT_Utility.trans("086", "Auto-Input: ") + (mInputDisabled
+ ? GT_Utility.trans("087", "Disabled")
+ : GT_Utility.trans("088", "Enabled") + GT_Utility.trans("089", " Auto-Output: ")
+ + (mOutputDisabled ? GT_Utility.trans("087", "Disabled")
+ : GT_Utility.trans("088", "Enabled"))));
+ GT_Utility.sendSoundToPlayers(
+ worldObj,
+ SoundResource.RANDOM_ANVIL_USE,
+ 1.0F,
+ -1,
+ xCoord,
+ yCoord,
+ zCoord);
+ }
+ return true;
+ }
+
+ if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sSoftHammerList)) {
+ if (GT_ModHandler.damageOrDechargeItem(tCurrentItem, 1, 1000, aPlayer)) {
+ if (mWorks) disableWorking();
+ else enableWorking();
+ {
+ String tChat = GT_Utility.trans("090", "Machine Processing: ")
+ + (isAllowedToWork() ? GT_Utility.trans("088", "Enabled")
+ : GT_Utility.trans("087", "Disabled"));
+ if (getMetaTileEntity() != null && getMetaTileEntity().hasAlternativeModeText())
+ tChat = getMetaTileEntity().getAlternativeModeText();
+ GT_Utility.sendChatToPlayer(aPlayer, tChat);
+ }
+ GT_Utility.sendSoundToPlayers(
+ worldObj,
+ SoundResource.IC2_TOOLS_RUBBER_TRAMPOLINE,
+ 1.0F,
+ -1,
+ xCoord,
+ yCoord,
+ zCoord);
+ }
+ return true;
+ }
+
+ if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sSolderingToolList)) {
+ final ForgeDirection tSide = GT_Utility.determineWrenchingSide(side, aX, aY, aZ);
+ if (mMetaTileEntity.onSolderingToolRightClick(side, tSide, aPlayer, aX, aY, aZ, tCurrentItem)) {
+ // logic handled internally
+ GT_Utility.sendSoundToPlayers(
+ worldObj,
+ SoundResource.IC2_TOOLS_BATTERY_USE,
+ 1.0F,
+ -1,
+ xCoord,
+ yCoord,
+ zCoord);
+ } else if (GT_ModHandler.useSolderingIron(tCurrentItem, aPlayer)) {
+ mStrongRedstone ^= tSide.flag;
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ GT_Utility.trans("091", "Redstone Output at Side ") + tSide
+ + GT_Utility.trans("092", " set to: ")
+ + ((mStrongRedstone & tSide.flag) != 0 ? GT_Utility.trans("093", "Strong")
+ : GT_Utility.trans("094", "Weak")));
+ GT_Utility.sendSoundToPlayers(
+ worldObj,
+ SoundResource.IC2_TOOLS_BATTERY_USE,
+ 3.0F,
+ -1,
+ xCoord,
+ yCoord,
+ zCoord);
+ issueBlockUpdate();
+ }
+ doEnetUpdate();
+ cableUpdateDelay = 10;
+ return true;
+ }
+
+ if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sWireCutterList)) {
+ final ForgeDirection tSide = GT_Utility.determineWrenchingSide(side, aX, aY, aZ);
+ if (mMetaTileEntity.onWireCutterRightClick(side, tSide, aPlayer, aX, aY, aZ, tCurrentItem)) {
+ // logic handled internally
+ GT_Utility.sendSoundToPlayers(
+ worldObj,
+ SoundResource.IC2_TOOLS_WRENCH,
+ 1.0F,
+ -1,
+ xCoord,
+ yCoord,
+ zCoord);
+ }
+ doEnetUpdate();
+ cableUpdateDelay = 10;
+ return true;
+ }
+
+ ForgeDirection coverSide = side;
+ if (getCoverIDAtSide(side) == 0) coverSide = GT_Utility.determineWrenchingSide(side, aX, aY, aZ);
+
+ if (getCoverIDAtSide(coverSide) == 0) {
+ if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sCovers.keySet())) {
+ final GT_CoverBehaviorBase<?> coverBehavior = GregTech_API
+ .getCoverBehaviorNew(tCurrentItem);
+ if (coverBehavior.isCoverPlaceable(coverSide, tCurrentItem, this)
+ && mMetaTileEntity.allowCoverOnSide(coverSide, new GT_ItemStack(tCurrentItem))) {
+
+ setCoverItemAtSide(coverSide, tCurrentItem);
+ coverBehavior.onPlayerAttach(aPlayer, tCurrentItem, this, coverSide);
+
+ if (!aPlayer.capabilities.isCreativeMode) tCurrentItem.stackSize--;
+ GT_Utility.sendSoundToPlayers(
+ worldObj,
+ SoundResource.IC2_TOOLS_WRENCH,
+ 1.0F,
+ -1,
+ xCoord,
+ yCoord,
+ zCoord);
+ sendClientData();
+ }
+ return true;
+ }
+ } else {
+ if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sCrowbarList)) {
+ if (GT_ModHandler.damageOrDechargeItem(tCurrentItem, 1, 1000, aPlayer)) {
+ GT_Utility.sendSoundToPlayers(
+ worldObj,
+ SoundResource.RANDOM_BREAK,
+ 1.0F,
+ -1,
+ xCoord,
+ yCoord,
+ zCoord);
+ dropCover(coverSide, side, false);
+ }
+ return true;
+ } else if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sJackhammerList)) {
+ // Configuration of delicate electronics calls for a tool with precision and subtlety.
+ if (GT_ModHandler.damageOrDechargeItem(tCurrentItem, 1, 1000, aPlayer)) {
+ final CoverInfo info = getCoverInfoAtSide(coverSide);
+ if (info != CoverInfo.EMPTY_INFO) {
+ final GT_CoverBehaviorBase<?> behavior = info.getCoverBehavior();
+ if (behavior.allowsTickRateAddition()) {
+ info.onCoverJackhammer(aPlayer);
+ GT_Utility.sendSoundToPlayers(
+ worldObj,
+ SoundResource.IC2_TOOLS_DRILL_DRILL_SOFT,
+ 1.0F,
+ 1,
+ xCoord,
+ yCoord,
+ zCoord);
+
+ } else {
+ GT_Utility.sendChatToPlayer(
+ aPlayer,
+ StatCollector.translateToLocal("gt.cover.info.chat.tick_rate_not_allowed"));
+ }
+ return true;
+ }
+ }
+ }
+ }
+ // End item != null
+ } else if (aPlayer.isSneaking()) { // Sneak click, no tool -> open cover config if possible.
+ side = (getCoverIDAtSide(side) == 0) ? GT_Utility.determineWrenchingSide(side, aX, aY, aZ) : side;
+ return getCoverIDAtSide(side) > 0 && getCoverBehaviorAtSideNew(side).onCoverShiftRightClick(
+ side,
+ getCoverIDAtSide(side),
+ getComplexCoverDataAtSide(side),
+ this,
+ aPlayer);
+ }
+
+ if (getCoverBehaviorAtSideNew(side).onCoverRightClick(
+ side,
+ getCoverIDAtSide(side),
+ getComplexCoverDataAtSide(side),
+ this,
+ aPlayer,
+ aX,
+ aY,
+ aZ)) return true;
+
+ if (!getCoverInfoAtSide(side).isGUIClickable()) return false;
+
+ if (isUpgradable() && tCurrentItem != null) {
+ if (ItemList.Upgrade_Muffler.isStackEqual(aPlayer.inventory.getCurrentItem())) {
+ if (addMufflerUpgrade()) {
+ GT_Utility.sendSoundToPlayers(
+ worldObj,
+ SoundResource.RANDOM_CLICK,
+ 1.0F,
+ -1,
+ xCoord,
+ yCoord,
+ zCoord);
+ if (!aPlayer.capabilities.isCreativeMode) aPlayer.inventory.getCurrentItem().stackSize--;
+ }
+ return true;
+ }
+ if (ItemList.Upgrade_Lock.isStackEqual(aPlayer.inventory.getCurrentItem())) {
+ if (isUpgradable() && !mLockUpgrade) {
+ mLockUpgrade = true;
+ setOwnerName(aPlayer.getDisplayName());
+ setOwnerUuid(aPlayer.getUniqueID());
+ GT_Utility.sendSoundToPlayers(
+ worldObj,
+ SoundResource.RANDOM_CLICK,
+ 1.0F,
+ -1,
+ xCoord,
+ yCoord,
+ zCoord);
+ if (!aPlayer.capabilities.isCreativeMode) aPlayer.inventory.getCurrentItem().stackSize--;
+ }
+ return true;
+ }
+ }
+ }
+ }
+
+ try {
+ if (!aPlayer.isSneaking() && hasValidMetaTileEntity())
+ return mMetaTileEntity.onRightclick(this, aPlayer, side, aX, aY, aZ);
+ } catch (Throwable e) {
+ GT_Log.err.println(
+ "Encountered Exception while rightclicking TileEntity, the Game should've crashed now, but I prevented that. Please report immediately to GregTech Intergalactical!!!");
+ e.printStackTrace(GT_Log.err);
+ e.printStackTrace();
+ }
+
+ return false;
+ }
+
+ @Override
+ public void onLeftclick(EntityPlayer aPlayer) {
+ try {
+ if (aPlayer != null && hasValidMetaTileEntity()) mMetaTileEntity.onLeftclick(this, aPlayer);
+ } catch (Throwable e) {
+ GT_Log.err.println(
+ "Encountered Exception while leftclicking TileEntity, the Game should've crashed now, but I prevented that. Please report immediately to GregTech Intergalactical!!!");
+ e.printStackTrace(GT_Log.err);
+ }
+ }
+
+ @Override
+ public boolean isDigitalChest() {
+ if (canAccessData()) return mMetaTileEntity.isDigitalChest();
+ return false;
+ }
+
+ @Override
+ public ItemStack[] getStoredItemData() {
+ if (canAccessData()) return mMetaTileEntity.getStoredItemData();
+ return null;
+ }
+
+ @Override
+ public void setItemCount(int aCount) {
+ if (canAccessData()) mMetaTileEntity.setItemCount(aCount);
+ }
+
+ @Override
+ public int getMaxItemCount() {
+ if (canAccessData()) return mMetaTileEntity.getMaxItemCount();
+ return 0;
+ }
+
+ /**
+ * Can put aStack into Slot
+ */
+ @Override
+ public boolean isItemValidForSlot(int aIndex, ItemStack aStack) {
+ return canAccessData() && mMetaTileEntity.isItemValidForSlot(aIndex, aStack);
+ }
+
+ /**
+ * returns all valid Inventory Slots, no matter which Side (Unless it's covered). The Side Stuff is done in the
+ * following two Functions.
+ */
+ @Override
+ public int[] getAccessibleSlotsFromSide(int ordinalSide) {
+ final CoverInfo coverInfo = getCoverInfoAtSide(ForgeDirection.getOrientation(ordinalSide));
+ if (canAccessData() && (coverInfo.letsItemsOut(-1) || coverInfo.letsItemsIn(-1)))
+ return mMetaTileEntity.getAccessibleSlotsFromSide(ordinalSide);
+ return GT_Values.emptyIntArray;
+ }
+
+ /**
+ * Can put aStack into Slot at Side
+ */
+ @Override
+ public boolean canInsertItem(int slotIndex, ItemStack stack, int ordinalSide) {
+ return canAccessData() && (mRunningThroughTick || !mInputDisabled)
+ && getCoverInfoAtSide(ForgeDirection.getOrientation(ordinalSide)).letsItemsIn(slotIndex)
+ && mMetaTileEntity.canInsertItem(slotIndex, stack, ordinalSide);
+ }
+
+ /**
+ * Can pull stack out of Slot from Side
+ */
+ @Override
+ public boolean canExtractItem(int slotIndex, ItemStack stack, int ordinalSide) {
+ final ForgeDirection side = ForgeDirection.getOrientation(ordinalSide);
+ return canAccessData() && (mRunningThroughTick || !mOutputDisabled)
+ && getCoverBehaviorAtSideNew(side)
+ .letsItemsOut(side, getCoverIDAtSide(side), getComplexCoverDataAtSide(side), slotIndex, this)
+ && mMetaTileEntity.canExtractItem(slotIndex, stack, ordinalSide);
+ }
+
+ @Override
+ public boolean isUpgradable() {
+ return canAccessData() && getUpgradeCount() < 8;
+ }
+
+ @Override
+ public byte getGeneralRS(ForgeDirection side) {
+ if (mMetaTileEntity == null) return 0;
+ return mMetaTileEntity.allowGeneralRedstoneOutput() ? mSidedRedstone[side.ordinal()] : 0;
+ }
+
+ @Override
+ public boolean isSteamEngineUpgradable() {
+ return isUpgradable() && !hasSteamEngineUpgrade() && getSteamCapacity() > 0;
+ }
+
+ @Override
+ public boolean addSteamEngineUpgrade() {
+ if (isSteamEngineUpgradable()) {
+ issueBlockUpdate();
+ mSteamConverter = true;
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean hasSteamEngineUpgrade() {
+ if (canAccessData() && mMetaTileEntity.isSteampowered()) return true;
+ return mSteamConverter;
+ }
+
+ @Override
+ public boolean hasMufflerUpgrade() {
+ return mMuffler;
+ }
+
+ @Override
+ public boolean isMufflerUpgradable() {
+ return isUpgradable() && !hasMufflerUpgrade();
+ }
+
+ @Override
+ public boolean addMufflerUpgrade() {
+ if (isMufflerUpgradable()) return mMuffler = true;
+ return false;
+ }
+
+ @Override
+ public void markInventoryBeenModified() {
+ mInventoryChanged = true;
+ }
+
+ @Override
+ public int getErrorDisplayID() {
+ return mDisplayErrorCode;
+ }
+
+ @Override
+ public void setErrorDisplayID(int aErrorID) {
+ mDisplayErrorCode = aErrorID;
+ }
+
+ @Override
+ public IMetaTileEntity getMetaTileEntity() {
+ return hasValidMetaTileEntity() ? mMetaTileEntity : null;
+ }
+
+ @Override
+ public void setMetaTileEntity(IMetaTileEntity aMetaTileEntity) {
+ if (aMetaTileEntity instanceof MetaTileEntity || aMetaTileEntity == null)
+ mMetaTileEntity = (MetaTileEntity) aMetaTileEntity;
+ else {
+ GT_FML_LOGGER.error(
+ "Unknown meta tile entity set! Class {}, inventory name {}.",
+ aMetaTileEntity.getClass(),
+ aMetaTileEntity.getInventoryName());
+ }
+ }
+
+ public byte getLightValue() {
+ return mLightValue;
+ }
+
+ @Override
+ public void setLightValue(byte aLightValue) {
+ mLightValue = (byte) (aLightValue & 15);
+ }
+
+ @Override
+ public long getAverageElectricInput() {
+ long rEU = 0;
+ for (int i = 0; i < mAverageEUInput.length; ++i) if (i != mAverageEUInputIndex) rEU += mAverageEUInput[i];
+ return rEU / (mAverageEUInput.length - 1);
+ }
+
+ @Override
+ public long getAverageElectricOutput() {
+ long rEU = 0;
+ for (int i = 0; i < mAverageEUOutput.length; ++i) if (i != mAverageEUOutputIndex) rEU += mAverageEUOutput[i];
+ return rEU / (mAverageEUOutput.length - 1);
+ }
+
+ @Override
+ protected void updateOutputRedstoneSignal(ForgeDirection side) {
+ if (mMetaTileEntity.hasSidedRedstoneOutputBehavior()) {
+ setOutputRedstoneSignal(side, (byte) 0);
+ } else {
+ setOutputRedstoneSignal(side, (byte) 15);
+ }
+ }
+
+ @Override
+ public String getOwnerName() {
+ if (GT_Utility.isStringInvalid(mOwnerName)) return "Player";
+ return mOwnerName;
+ }
+
+ @Override
+ public String setOwnerName(String aName) {
+ if (GT_Utility.isStringInvalid(aName)) return mOwnerName = "Player";
+ return mOwnerName = aName;
+ }
+
+ @Override
+ public UUID getOwnerUuid() {
+ return mOwnerUuid;
+ }
+
+ @Override
+ public void setOwnerUuid(UUID uuid) {
+ mOwnerUuid = uuid;
+ }
+
+ @Override
+ public byte getComparatorValue(ForgeDirection side) {
+ return canAccessData() ? mMetaTileEntity.getComparatorValue(side) : 0;
+ }
+
+ @Override
+ public ItemStack decrStackSize(int aIndex, int aAmount) {
+ if (canAccessData()) {
+ mInventoryChanged = true;
+ return mMetaTileEntity.decrStackSize(aIndex, aAmount);
+ }
+ return null;
+ }
+
+ @Override
+ public long injectEnergyUnits(ForgeDirection side, long aVoltage, long aAmperage) {
+ if (!canAccessData() || !mMetaTileEntity.isElectric()
+ || !inputEnergyFrom(side)
+ || aAmperage <= 0
+ || aVoltage <= 0
+ || getStoredEU() >= getEUCapacity()
+ || mMetaTileEntity.maxAmperesIn() <= mAcceptedAmperes) return 0;
+ if (aVoltage > getInputVoltage()) {
+ GT_Log.exp
+ .println("Energy Explosion, injected " + aVoltage + "EU/t in a " + getInputVoltage() + "EU/t Machine!");
+ doExplosion(aVoltage);
+ return 0;
+ }
+ if (increaseStoredEnergyUnits(
+ aVoltage * (aAmperage = Math.min(
+ aAmperage,
+ Math.min(
+ mMetaTileEntity.maxAmperesIn() - mAcceptedAmperes,
+ 1 + ((getEUCapacity() - getStoredEU()) / aVoltage)))),
+ true)) {
+ mAverageEUInput[mAverageEUInputIndex] += aVoltage * aAmperage;
+ mAcceptedAmperes += aAmperage;
+ return aAmperage;
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean drainEnergyUnits(ForgeDirection side, long aVoltage, long aAmperage) {
+ if (!canAccessData() || !mMetaTileEntity.isElectric()
+ || !outputsEnergyTo(side)
+ || getStoredEU() - (aVoltage * aAmperage) < mMetaTileEntity.getMinimumStoredEU()) return false;
+ if (decreaseStoredEU(aVoltage * aAmperage, false)) {
+ mAverageEUOutput[mAverageEUOutputIndex] += aVoltage * aAmperage;
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean acceptsRotationalEnergy(ForgeDirection side) {
+ if (!canAccessData() || getCoverIDAtSide(side) != 0) return false;
+ return mMetaTileEntity.acceptsRotationalEnergy(side);
+ }
+
+ @Override
+ public boolean injectRotationalEnergy(ForgeDirection side, long aSpeed, long aEnergy) {
+ if (!canAccessData() || getCoverIDAtSide(side) != 0) return false;
+ return mMetaTileEntity.injectRotationalEnergy(side, aSpeed, aEnergy);
+ }
+
+ @Override
+ public int fill(ForgeDirection side, FluidStack aFluid, boolean doFill) {
+ if (mTickTimer > 5 && canAccessData()
+ && (mRunningThroughTick || !mInputDisabled)
+ && (side == ForgeDirection.UNKNOWN || (mMetaTileEntity.isLiquidInput(side)
+ && getCoverInfoAtSide(side).letsFluidIn(aFluid == null ? null : aFluid.getFluid()))))
+ return mMetaTileEntity.fill(side, aFluid, doFill);
+ return 0;
+ }
+
+ @Override
+ public FluidStack drain(ForgeDirection side, int maxDrain, boolean doDrain) {
+ if (mTickTimer > 5 && canAccessData()
+ && (mRunningThroughTick || !mOutputDisabled)
+ && (side == ForgeDirection.UNKNOWN
+ || (mMetaTileEntity.isLiquidOutput(side) && getCoverInfoAtSide(side).letsFluidOut(
+ mMetaTileEntity.getFluid() == null ? null
+ : mMetaTileEntity.getFluid()
+ .getFluid()))))
+ return mMetaTileEntity.drain(side, maxDrain, doDrain);
+ return null;
+ }
+
+ @Override
+ public FluidStack drain(ForgeDirection side, FluidStack aFluid, boolean doDrain) {
+ if (mTickTimer > 5 && canAccessData()
+ && (mRunningThroughTick || !mOutputDisabled)
+ && (side == ForgeDirection.UNKNOWN || (mMetaTileEntity.isLiquidOutput(side)
+ && getCoverInfoAtSide(side).letsFluidOut(aFluid == null ? null : aFluid.getFluid()))))
+ return mMetaTileEntity.drain(side, aFluid, doDrain);
+ return null;
+ }
+
+ @Override
+ public boolean canFill(ForgeDirection side, Fluid aFluid) {
+ if (mTickTimer > 5 && canAccessData()
+ && (mRunningThroughTick || !mInputDisabled)
+ && (side == ForgeDirection.UNKNOWN
+ || (mMetaTileEntity.isLiquidInput(side) && getCoverInfoAtSide(side).letsFluidIn(aFluid))))
+ return mMetaTileEntity.canFill(side, aFluid);
+ return false;
+ }
+
+ @Override
+ public boolean canDrain(ForgeDirection side, Fluid aFluid) {
+ if (mTickTimer > 5 && canAccessData()
+ && (mRunningThroughTick || !mOutputDisabled)
+ && (side == ForgeDirection.UNKNOWN
+ || (mMetaTileEntity.isLiquidOutput(side) && getCoverInfoAtSide(side).letsFluidOut(aFluid))))
+ return mMetaTileEntity.canDrain(side, aFluid);
+ return false;
+ }
+
+ @Override
+ public FluidTankInfo[] getTankInfo(ForgeDirection side) {
+ if (canAccessData() && (side == ForgeDirection.UNKNOWN
+ || (mMetaTileEntity.isLiquidInput(side) && getCoverInfoAtSide(side).letsFluidIn(null))
+ || (mMetaTileEntity.isLiquidOutput(side) && getCoverInfoAtSide(side).letsFluidOut(null))))
+ return mMetaTileEntity.getTankInfo(side);
+ return new FluidTankInfo[] {};
+ }
+
+ public double getOutputEnergyUnitsPerTick() {
+ return oOutput;
+ }
+
+ public boolean isTeleporterCompatible(ForgeDirection side) {
+ return canAccessData() && mMetaTileEntity.isTeleporterCompatible();
+ }
+
+ public double demandedEnergyUnits() {
+ if (mReleaseEnergy || !canAccessData() || !mMetaTileEntity.isEnetInput()) return 0;
+ return getEUCapacity() - getStoredEU();
+ }
+
+ public double injectEnergyUnits(ForgeDirection aDirection, double aAmount) {
+ return injectEnergyUnits(aDirection, (int) aAmount, 1) > 0 ? 0 : aAmount;
+ }
+
+ public boolean acceptsEnergyFrom(TileEntity aEmitter, ForgeDirection aDirection) {
+ return inputEnergyFrom(aDirection);
+ }
+
+ public boolean emitsEnergyTo(TileEntity aReceiver, ForgeDirection aDirection) {
+ return outputsEnergyTo(aDirection);
+ }
+
+ public double getOfferedEnergy() {
+ return (canAccessData() && getStoredEU() - mMetaTileEntity.getMinimumStoredEU() >= oOutput)
+ ? Math.max(0, oOutput)
+ : 0;
+ }
+
+ public void drawEnergy(double amount) {
+ mAverageEUOutput[mAverageEUOutputIndex] += amount;
+ decreaseStoredEU((int) amount, true);
+ }
+
+ public int injectEnergy(ForgeDirection aForgeDirection, int aAmount) {
+ return injectEnergyUnits(aForgeDirection, aAmount, 1) > 0 ? 0 : aAmount;
+ }
+
+ public int addEnergy(int aEnergy) {
+ if (!canAccessData()) return 0;
+ if (aEnergy > 0) increaseStoredEnergyUnits(aEnergy, true);
+ else decreaseStoredEU(-aEnergy, true);
+ return (int) Math.min(Integer.MAX_VALUE, mMetaTileEntity.getEUVar());
+ }
+
+ public boolean isAddedToEnergyNet() {
+ return false;
+ }
+
+ public int demandsEnergy() {
+ if (mReleaseEnergy || !canAccessData() || !mMetaTileEntity.isEnetInput()) return 0;
+ return getCapacity() - getStored();
+ }
+
+ public int getCapacity() {
+ return (int) Math.min(Integer.MAX_VALUE, getEUCapacity());
+ }
+
+ public int getStored() {
+ return (int) Math.min(Integer.MAX_VALUE, Math.min(getStoredEU(), getCapacity()));
+ }
+
+ public void setStored(int aEU) {
+ if (canAccessData()) setStoredEU(aEU);
+ }
+
+ public int getMaxSafeInput() {
+ return (int) Math.min(Integer.MAX_VALUE, getInputVoltage());
+ }
+
+ public int getMaxEnergyOutput() {
+ if (mReleaseEnergy) return Integer.MAX_VALUE;
+ return getOutput();
+ }
+
+ public int getOutput() {
+ return (int) Math.min(Integer.MAX_VALUE, oOutput);
+ }
+
+ public int injectEnergy(Direction aDirection, int aAmount) {
+ return injectEnergyUnits(aDirection.toForgeDirection(), aAmount, 1) > 0 ? 0 : aAmount;
+ }
+
+ public boolean isTeleporterCompatible(Direction ignoredDirection) {
+ return canAccessData() && mMetaTileEntity.isTeleporterCompatible();
+ }
+
+ public boolean acceptsEnergyFrom(TileEntity ignoredTileEntity, Direction aDirection) {
+ return inputEnergyFrom(aDirection.toForgeDirection());
+ }
+
+ public boolean emitsEnergyTo(TileEntity ignoredTileEntity, Direction aDirection) {
+ return outputsEnergyTo(aDirection.toForgeDirection());
+ }
+
+ @Override
+ public boolean addStackToSlot(int slotIndex, ItemStack stack) {
+ if (GT_Utility.isStackInvalid(stack)) return true;
+ if (slotIndex < 0 || slotIndex >= getSizeInventory()) return false;
+ final ItemStack toStack = getStackInSlot(slotIndex);
+ if (GT_Utility.isStackInvalid(toStack)) {
+ setInventorySlotContents(slotIndex, stack);
+ return true;
+ }
+ final ItemStack fromStack = GT_OreDictUnificator.get(stack);
+ if (GT_Utility.areStacksEqual(toStack, fromStack) && toStack.stackSize + fromStack.stackSize
+ <= Math.min(fromStack.getMaxStackSize(), getInventoryStackLimit())) {
+ toStack.stackSize += fromStack.stackSize;
+ markDirty();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean addStackToSlot(int aIndex, ItemStack aStack, int aAmount) {
+ return addStackToSlot(aIndex, GT_Utility.copyAmount(aAmount, aStack));
+ }
+
+ @Override
+ public byte getColorization() {
+ return (byte) (mColor - 1);
+ }
+
+ @Override
+ public byte setColorization(byte aColor) {
+ if (aColor > 15 || aColor < -1) aColor = -1;
+ mColor = (byte) (aColor + 1);
+ if (canAccessData()) mMetaTileEntity.onColorChangeServer(aColor);
+ return mColor;
+ }
+
+ @Override
+ public float getBlastResistance(ForgeDirection side) {
+ return canAccessData() ? Math.max(0, getMetaTileEntity().getExplosionResistance(side)) : 10.0F;
+ }
+
+ @Override
+ public void onBlockDestroyed() {
+ if (canAccessData()) getMetaTileEntity().onBlockDestroyed();
+ }
+
+ @Override
+ public boolean isUniversalEnergyStored(long aEnergyAmount) {
+ if (getUniversalEnergyStored() >= aEnergyAmount) return true;
+ mHasEnoughEnergy = false;
+ return false;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ {
+ if (canAccessData()) return getMetaTileEntity().getInfoData();
+ return new String[] {};
+ }
+ }
+
+ @Override
+ public int getLightOpacity() {
+ return mMetaTileEntity == null ? getLightValue() > 0 ? 0 : 255 : mMetaTileEntity.getLightOpacity();
+ }
+
+ @Override
+ public void addCollisionBoxesToList(World aWorld, int aX, int aY, int aZ, AxisAlignedBB inputAABB,
+ List<AxisAlignedBB> outputAABB, Entity collider) {
+ mMetaTileEntity.addCollisionBoxesToList(aWorld, aX, aY, aZ, inputAABB, outputAABB, collider);
+ }
+
+ @Override
+ public AxisAlignedBB getCollisionBoundingBoxFromPool(World aWorld, int aX, int aY, int aZ) {
+ return mMetaTileEntity.getCollisionBoundingBoxFromPool(aWorld, aX, aY, aZ);
+ }
+
+ @Override
+ public void onEntityCollidedWithBlock(World aWorld, int aX, int aY, int aZ, Entity collider) {
+ mMetaTileEntity.onEntityCollidedWithBlock(aWorld, aX, aY, aZ, collider);
+ }
+
+ /**
+ * Shifts the machine Inventory index according to the change in Input/Output Slots. This is NOT done automatically.
+ * If you want to change slot count for a machine this method needs to be adapted. Currently this method only works
+ * for GT_MetaTileEntity_BasicMachine
+ *
+ * @param slotIndex The original Inventory index
+ * @param nbtVersion The GregTech version in which the original Inventory Index was saved.
+ * @return The corrected Inventory index
+ */
+ @Override
+ protected int migrateInventoryIndex(int slotIndex, int nbtVersion) {
+ final int oldInputSize;
+ final int newInputSize;
+ final int oldOutputSize;
+ final int newOutputSize;
+ final int chemistryUpdateVersion = GT_Mod.calculateTotalGTVersion(509, 31);
+ final int configCircuitAdditionVersion = GT_Mod.calculateTotalGTVersion(509, 40);
+ final int wireAdditionVersion = GT_Mod.calculateTotalGTVersion(509, 41);
+ final int disassemblerRemoveVersion = GT_Mod.calculateTotalGTVersion(509, 42, 44);
+ if (nbtVersion < 1000000) nbtVersion *= 1000;
+ // 4 is old GT_MetaTileEntity_BasicMachine.OTHER_SLOT_COUNT
+ if (nbtVersion < configCircuitAdditionVersion && getMetaTileEntity() instanceof GT_MetaTileEntity_BasicMachine
+ && slotIndex >= 4) slotIndex += 1;
+ if (mID >= 211 && mID <= 218) { // Assembler
+ if (nbtVersion < chemistryUpdateVersion) {
+ oldInputSize = 2;
+ oldOutputSize = 1;
+ } else {
+ return slotIndex;
+ }
+ newInputSize = 6;
+ newOutputSize = 1;
+
+ } else if (mID >= 421 && mID <= 428) { // Chemical Reactor
+ if (nbtVersion < chemistryUpdateVersion) {
+ oldInputSize = 2;
+ oldOutputSize = 1;
+ } else {
+ return slotIndex;
+ }
+ newInputSize = 2;
+ newOutputSize = 2;
+
+ } else if (mID >= 531 && mID <= 538) { // Distillery
+ if (nbtVersion < chemistryUpdateVersion) {
+ oldInputSize = 1;
+ oldOutputSize = 0;
+ } else {
+ return slotIndex;
+ }
+ newInputSize = 1;
+ newOutputSize = 1;
+ } else if (mID >= 581 && mID <= 588) { // Mixer
+ if (nbtVersion < chemistryUpdateVersion) {
+ oldInputSize = 4;
+ oldOutputSize = 1;
+ } else {
+ return slotIndex;
+ }
+ newInputSize = 6;
+ newOutputSize = 1;
+
+ } else if (mID >= 351 && mID <= 355 || mID >= 11050 && mID <= 11056) { // wire mill
+ if (nbtVersion < wireAdditionVersion) {
+ oldInputSize = 1;
+ oldOutputSize = 1;
+ } else {
+ return slotIndex;
+ }
+ newInputSize = 2;
+ newOutputSize = 1;
+
+ } else if (mID >= 654 && mID <= 655 || mID >= 11070 && mID <= 11076) { // arc furnace
+ if (nbtVersion < disassemblerRemoveVersion) {
+ oldInputSize = 1;
+ oldOutputSize = 4;
+ } else {
+ return slotIndex;
+ }
+ newInputSize = 1;
+ newOutputSize = 9;
+
+ } else {
+ return slotIndex;
+ }
+
+ int indexShift = 0;
+ if (slotIndex >= GT_MetaTileEntity_BasicMachine.OTHER_SLOT_COUNT + oldInputSize) {
+ indexShift += newInputSize - oldInputSize;
+ }
+ if (slotIndex >= GT_MetaTileEntity_BasicMachine.OTHER_SLOT_COUNT + oldInputSize + oldOutputSize) {
+ indexShift += newOutputSize - oldOutputSize;
+ }
+ return slotIndex + indexShift;
+ }
+
+ @Override
+ public IGridNode getGridNode(ForgeDirection forgeDirection) {
+ final AENetworkProxy gp = getProxy();
+ return gp != null ? gp.getNode() : null;
+ }
+
+ @Override
+ public AECableType getCableConnectionType(ForgeDirection forgeDirection) {
+ return mMetaTileEntity == null ? AECableType.NONE : mMetaTileEntity.getCableConnectionType(forgeDirection);
+ }
+
+ @Override
+ public void securityBreak() {}
+
+ @Override
+ public IGridNode getActionableNode() {
+ final AENetworkProxy gp = getProxy();
+ return gp != null ? gp.getNode() : null;
+ }
+
+ @Override
+ public AENetworkProxy getProxy() {
+ return mMetaTileEntity == null ? null : mMetaTileEntity.getProxy();
+ }
+
+ @Override
+ public DimensionalCoord getLocation() {
+ return new DimensionalCoord(this);
+ }
+
+ @Override
+ public void gridChanged() {
+ if (mMetaTileEntity != null) mMetaTileEntity.gridChanged();
+ }
+
+ @TileEvent(TileEventType.WORLD_NBT_READ)
+ public void readFromNBT_AENetwork(final NBTTagCompound data) {
+ final AENetworkProxy gp = getProxy();
+ if (gp != null) getProxy().readFromNBT(data);
+ }
+
+ @TileEvent(TileEventType.WORLD_NBT_WRITE)
+ public void writeToNBT_AENetwork(final NBTTagCompound data) {
+ final AENetworkProxy gp = getProxy();
+ if (gp != null) gp.writeToNBT(data);
+ }
+
+ void onChunkUnloadAE() {
+ final AENetworkProxy gp = getProxy();
+ if (gp != null) gp.onChunkUnload();
+ }
+
+ void invalidateAE() {
+ final AENetworkProxy gp = getProxy();
+ if (gp != null) gp.invalidate();
+ }
+
+ @Override
+ public boolean wasShutdown() {
+ return mWasShutdown;
+ }
+
+ @Override
+ public void setShutdownStatus(boolean newStatus) {
+ mWasShutdown = newStatus;
+ }
+
+ @Override
+ public void setShutDownReason(@NotNull ShutDownReason reason) {
+ lastShutDownReason = reason;
+ }
+
+ @Override
+ public @NotNull ShutDownReason getLastShutDownReason() {
+ return lastShutDownReason;
+ }
+
+ @Override
+ public IAlignment getAlignment() {
+ return getMetaTileEntity() instanceof IAlignmentProvider
+ ? ((IAlignmentProvider) getMetaTileEntity()).getAlignment()
+ : getMetaTileEntity() instanceof IAlignment ? (IAlignment) getMetaTileEntity() : null;
+ }
+
+ @Nullable
+ @Override
+ public IConstructable getConstructable() {
+ return getMetaTileEntity() instanceof IConstructable ? (IConstructable) getMetaTileEntity() : null;
+ }
+
+ @Override
+ public int[] getTimeStatistics() {
+ return mTimeStatistics;
+ }
+
+ @Override
+ public void startTimeStatistics() {
+ hasTimeStatisticsStarted = true;
+ }
+
+ @Nullable
+ @Override
+ public List<ItemStack> getItemsForHoloGlasses() {
+ if (canAccessData()) {
+ return mMetaTileEntity.getItemsForHoloGlasses();
+ }
+ return null;
+ }
+
+ @Override
+ public String getCustomName() {
+ return getMetaTileEntity() instanceof ICustomNameObject customNameObject ? customNameObject.getCustomName()
+ : null;
+ }
+
+ @Override
+ public boolean hasCustomName() {
+ return getMetaTileEntity() instanceof ICustomNameObject customNameObject && customNameObject.hasCustomName();
+ }
+
+ @Override
+ public void setCustomName(String name) {
+ if (getMetaTileEntity() instanceof ICustomNameObject customNameObject) customNameObject.setCustomName(name);
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/BaseTileEntity.java b/src/main/java/gregtech/api/metatileentity/BaseTileEntity.java
new file mode 100644
index 0000000000..d8b0086f0f
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/BaseTileEntity.java
@@ -0,0 +1,979 @@
+package gregtech.api.metatileentity;
+
+import static gregtech.api.enums.GT_Values.COMPASS_DIRECTIONS;
+import static gregtech.api.enums.GT_Values.GT;
+import static gregtech.api.enums.GT_Values.NW;
+import static gregtech.api.enums.GT_Values.SIDE_DOWN;
+import static gregtech.api.enums.GT_Values.SIDE_UP;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Supplier;
+
+import javax.annotation.Nullable;
+
+import net.minecraft.block.Block;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.init.Blocks;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.ChunkCoordinates;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.MathHelper;
+import net.minecraft.util.StatCollector;
+import net.minecraft.world.World;
+import net.minecraft.world.biome.BiomeGenBase;
+import net.minecraft.world.chunk.Chunk;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.IFluidHandler;
+
+import com.gtnewhorizons.modularui.api.drawable.IDrawable;
+import com.gtnewhorizons.modularui.api.drawable.ItemDrawable;
+import com.gtnewhorizons.modularui.api.forge.ItemStackHandler;
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.api.screen.ITileWithModularUI;
+import com.gtnewhorizons.modularui.api.screen.ModularUIContext;
+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.internal.wrapper.BaseSlot;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+import com.gtnewhorizons.modularui.common.widget.MultiChildWidget;
+import com.gtnewhorizons.modularui.common.widget.SlotGroup;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+import com.gtnewhorizons.modularui.common.widget.TextWidget;
+
+import cpw.mods.fml.common.FMLCommonHandler;
+import cpw.mods.fml.relauncher.Side;
+import gregtech.GT_Mod;
+import gregtech.api.enums.Dyes;
+import gregtech.api.enums.GT_Values;
+import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.gui.modularui.GUITextureSet;
+import gregtech.api.interfaces.IConfigurationCircuitSupport;
+import gregtech.api.interfaces.modularui.IAddGregtechLogo;
+import gregtech.api.interfaces.modularui.IAddInventorySlots;
+import gregtech.api.interfaces.modularui.IGetGUITextureSet;
+import gregtech.api.interfaces.tileentity.IGTEnet;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.interfaces.tileentity.IHasWorldObjectAndCoords;
+import gregtech.api.interfaces.tileentity.IIC2Enet;
+import gregtech.api.net.GT_Packet_Block_Event;
+import gregtech.api.net.GT_Packet_SetConfigurationCircuit;
+import gregtech.api.util.GT_TooltipDataCache;
+import gregtech.api.util.GT_Util;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.gui.modularui.uifactory.SelectItemUIFactory;
+import ic2.api.energy.event.EnergyTileLoadEvent;
+import ic2.api.energy.event.EnergyTileUnloadEvent;
+
+/**
+ * The Functions my old TileEntities and my BaseMetaTileEntities have in common.
+ * <p/>
+ * Basically everything a TileEntity should have.
+ */
+public abstract class BaseTileEntity extends TileEntity implements IHasWorldObjectAndCoords, IIC2Enet, IGTEnet,
+ ITileWithModularUI, IAddGregtechLogo, IGetGUITextureSet, IAddInventorySlots {
+
+ protected boolean mInventoryChanged = false;
+
+ /**
+ * Buffers adjacent TileEntities for faster access
+ * <p/>
+ * "this" means that there is no TileEntity, while "null" means that it doesn't know if there is even a TileEntity
+ * and still needs to check that if needed.
+ */
+ private final TileEntity[] mBufferedTileEntities = new TileEntity[6];
+ /**
+ * If this TileEntity checks for the Chunk to be loaded before returning World based values. The AdvPump hacks this
+ * to false to ensure everything runs properly even when far Chunks are not actively loaded. But anything else
+ * should not cause worfin' Chunks, uhh I mean orphan Chunks.
+ */
+ public boolean ignoreUnloadedChunks = true;
+ /**
+ * This Variable checks if this TileEntity is dead, because Minecraft is too stupid to have proper TileEntity
+ * unloading.
+ */
+ public boolean isDead = false;
+
+ private final ChunkCoordinates mReturnedCoordinates = new ChunkCoordinates();
+
+ public static ForgeDirection getSideForPlayerPlacing(Entity aPlayer, ForgeDirection defaultFacing,
+ boolean[] aAllowedFacings) {
+ if (aPlayer != null) {
+ if (aPlayer.rotationPitch >= 65 && aAllowedFacings[SIDE_UP]) return ForgeDirection.UP;
+ if (aPlayer.rotationPitch <= -65 && aAllowedFacings[SIDE_DOWN]) return ForgeDirection.DOWN;
+ final byte rFacing = COMPASS_DIRECTIONS[MathHelper.floor_double(0.5D + 4.0F * aPlayer.rotationYaw / 360.0F)
+ & 0x3];
+ if (aAllowedFacings[rFacing]) return ForgeDirection.getOrientation(rFacing);
+ }
+ for (final ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) if (aAllowedFacings[dir.ordinal()]) return dir;
+ return defaultFacing;
+ }
+
+ private void clearNullMarkersFromTileEntityBuffer() {
+ for (int i = 0; i < mBufferedTileEntities.length; i++)
+ if (mBufferedTileEntities[i] == this) mBufferedTileEntities[i] = null;
+ }
+
+ /**
+ * Called automatically when the Coordinates of this TileEntity have been changed
+ */
+ protected final void clearTileEntityBuffer() {
+ Arrays.fill(mBufferedTileEntities, null);
+ }
+
+ @Override
+ public final World getWorld() {
+ return worldObj;
+ }
+
+ @Override
+ public final int getXCoord() {
+ return xCoord;
+ }
+
+ @Override
+ public final short getYCoord() {
+ return (short) yCoord;
+ }
+
+ @Override
+ public final int getZCoord() {
+ return zCoord;
+ }
+
+ @Override
+ public ChunkCoordinates getCoords() {
+ mReturnedCoordinates.posX = xCoord;
+ mReturnedCoordinates.posY = yCoord;
+ mReturnedCoordinates.posZ = zCoord;
+ return mReturnedCoordinates;
+ }
+
+ @Override
+ public final int getOffsetX(ForgeDirection side, int aMultiplier) {
+ return xCoord + side.offsetX * aMultiplier;
+ }
+
+ @Override
+ public final short getOffsetY(ForgeDirection side, int aMultiplier) {
+ return (short) (yCoord + side.offsetY * aMultiplier);
+ }
+
+ @Override
+ public final int getOffsetZ(ForgeDirection side, int aMultiplier) {
+ return zCoord + side.offsetZ * aMultiplier;
+ }
+
+ @Override
+ public final boolean isServerSide() {
+ if (worldObj == null) {
+ return FMLCommonHandler.instance()
+ .getEffectiveSide() == Side.SERVER;
+ }
+ return !worldObj.isRemote;
+ }
+
+ @Override
+ public final boolean isClientSide() {
+ if (worldObj == null) {
+ return FMLCommonHandler.instance()
+ .getEffectiveSide() == Side.CLIENT;
+ }
+ return worldObj.isRemote;
+ }
+
+ @Override
+ @Deprecated
+ public final boolean openGUI(EntityPlayer aPlayer) {
+ return openGUI(aPlayer, 0);
+ }
+
+ @Override
+ @Deprecated
+ public final boolean openGUI(EntityPlayer aPlayer, int aID) {
+ if (aPlayer == null) return false;
+ aPlayer.openGui(GT, aID, worldObj, xCoord, yCoord, zCoord);
+ return true;
+ }
+
+ @Override
+ public boolean isInvalidTileEntity() {
+ return isInvalid();
+ }
+
+ @Override
+ public int getRandomNumber(int aRange) {
+ return ThreadLocalRandom.current()
+ .nextInt(aRange);
+ }
+
+ @Override
+ public final BiomeGenBase getBiome(int aX, int aZ) {
+ return worldObj.getBiomeGenForCoords(aX, aZ);
+ }
+
+ @Override
+ public final BiomeGenBase getBiome() {
+ return getBiome(xCoord, zCoord);
+ }
+
+ @Override
+ public final Block getBlockOffset(int aX, int aY, int aZ) {
+ return getBlock(xCoord + aX, yCoord + aY, zCoord + aZ);
+ }
+
+ @Override
+ public final Block getBlockAtSide(ForgeDirection side) {
+ return getBlockAtSideAndDistance(side, 1);
+ }
+
+ @Override
+ public final Block getBlockAtSideAndDistance(ForgeDirection side, int aDistance) {
+ return getBlock(getOffsetX(side, aDistance), getOffsetY(side, aDistance), getOffsetZ(side, aDistance));
+ }
+
+ @Override
+ public final byte getMetaIDOffset(int aX, int aY, int aZ) {
+ return getMetaID(xCoord + aX, yCoord + aY, zCoord + aZ);
+ }
+
+ @Override
+ public final byte getMetaIDAtSide(ForgeDirection side) {
+ return getMetaIDAtSideAndDistance(side, 1);
+ }
+
+ @Override
+ public final byte getMetaIDAtSideAndDistance(ForgeDirection side, int aDistance) {
+ return getMetaID(getOffsetX(side, aDistance), getOffsetY(side, aDistance), getOffsetZ(side, aDistance));
+ }
+
+ @Override
+ public final byte getLightLevelOffset(int aX, int aY, int aZ) {
+ return getLightLevel(xCoord + aX, yCoord + aY, zCoord + aZ);
+ }
+
+ @Override
+ public final byte getLightLevelAtSide(ForgeDirection side) {
+ return getLightLevelAtSideAndDistance(side, 1);
+ }
+
+ @Override
+ public final byte getLightLevelAtSideAndDistance(ForgeDirection side, int aDistance) {
+ return getLightLevel(getOffsetX(side, aDistance), getOffsetY(side, aDistance), getOffsetZ(side, aDistance));
+ }
+
+ @Override
+ public final boolean getOpacityOffset(int aX, int aY, int aZ) {
+ return getOpacity(xCoord + aX, yCoord + aY, zCoord + aZ);
+ }
+
+ @Override
+ public final boolean getOpacityAtSide(ForgeDirection side) {
+ return getOpacityAtSideAndDistance(side, 1);
+ }
+
+ @Override
+ public final boolean getOpacityAtSideAndDistance(ForgeDirection side, int aDistance) {
+ return getOpacity(getOffsetX(side, aDistance), getOffsetY(side, aDistance), getOffsetZ(side, aDistance));
+ }
+
+ @Override
+ public final boolean getSkyOffset(int aX, int aY, int aZ) {
+ return getSky(xCoord + aX, yCoord + aY, zCoord + aZ);
+ }
+
+ @Override
+ public final boolean getSkyAtSide(ForgeDirection side) {
+ return getSkyAtSideAndDistance(side, 1);
+ }
+
+ @Override
+ public final boolean getSkyAtSideAndDistance(ForgeDirection side, int aDistance) {
+ return getSky(getOffsetX(side, aDistance), getOffsetY(side, aDistance), getOffsetZ(side, aDistance));
+ }
+
+ @Override
+ public final boolean getAirOffset(int aX, int aY, int aZ) {
+ return getAir(xCoord + aX, yCoord + aY, zCoord + aZ);
+ }
+
+ @Override
+ public final boolean getAirAtSide(ForgeDirection side) {
+ return getAirAtSideAndDistance(side, 1);
+ }
+
+ @Override
+ public final boolean getAirAtSideAndDistance(ForgeDirection side, int aDistance) {
+ return getAir(getOffsetX(side, aDistance), getOffsetY(side, aDistance), getOffsetZ(side, aDistance));
+ }
+
+ @Override
+ public final TileEntity getTileEntityOffset(int aX, int aY, int aZ) {
+ return getTileEntity(xCoord + aX, yCoord + aY, zCoord + aZ);
+ }
+
+ @Override
+ public final TileEntity getTileEntityAtSideAndDistance(ForgeDirection side, int aDistance) {
+ if (aDistance == 1) return getTileEntityAtSide(side);
+ return getTileEntity(getOffsetX(side, aDistance), getOffsetY(side, aDistance), getOffsetZ(side, aDistance));
+ }
+
+ @Override
+ public final IInventory getIInventory(int aX, int aY, int aZ) {
+ final TileEntity tTileEntity = getTileEntity(aX, aY, aZ);
+ if (tTileEntity instanceof IInventory) return (IInventory) tTileEntity;
+ return null;
+ }
+
+ @Override
+ public final IInventory getIInventoryOffset(int aX, int aY, int aZ) {
+ final TileEntity tTileEntity = getTileEntityOffset(aX, aY, aZ);
+ if (tTileEntity instanceof IInventory) return (IInventory) tTileEntity;
+ return null;
+ }
+
+ @Override
+ public final IInventory getIInventoryAtSide(ForgeDirection side) {
+ final TileEntity tTileEntity = getTileEntityAtSide(side);
+ if (tTileEntity instanceof IInventory) return (IInventory) tTileEntity;
+ return null;
+ }
+
+ @Override
+ public final IInventory getIInventoryAtSideAndDistance(ForgeDirection side, int aDistance) {
+ final TileEntity tTileEntity = getTileEntityAtSideAndDistance(side, aDistance);
+ if (tTileEntity instanceof IInventory) return (IInventory) tTileEntity;
+ return null;
+ }
+
+ @Override
+ public final IFluidHandler getITankContainer(int aX, int aY, int aZ) {
+ final TileEntity tTileEntity = getTileEntity(aX, aY, aZ);
+ if (tTileEntity instanceof IFluidHandler) return (IFluidHandler) tTileEntity;
+ return null;
+ }
+
+ @Override
+ public final IFluidHandler getITankContainerOffset(int aX, int aY, int aZ) {
+ final TileEntity tTileEntity = getTileEntityOffset(aX, aY, aZ);
+ if (tTileEntity instanceof IFluidHandler) return (IFluidHandler) tTileEntity;
+ return null;
+ }
+
+ @Override
+ public final IFluidHandler getITankContainerAtSide(ForgeDirection side) {
+ final TileEntity tTileEntity = getTileEntityAtSide(side);
+ if (tTileEntity instanceof IFluidHandler) return (IFluidHandler) tTileEntity;
+ return null;
+ }
+
+ @Override
+ public final IFluidHandler getITankContainerAtSideAndDistance(ForgeDirection side, int aDistance) {
+ final TileEntity tTileEntity = getTileEntityAtSideAndDistance(side, aDistance);
+ if (tTileEntity instanceof IFluidHandler) return (IFluidHandler) tTileEntity;
+ return null;
+ }
+
+ @Override
+ public final IGregTechTileEntity getIGregTechTileEntity(int aX, int aY, int aZ) {
+ final TileEntity tTileEntity = getTileEntity(aX, aY, aZ);
+ if (tTileEntity instanceof IGregTechTileEntity) return (IGregTechTileEntity) tTileEntity;
+ return null;
+ }
+
+ @Override
+ public final IGregTechTileEntity getIGregTechTileEntityOffset(int aX, int aY, int aZ) {
+ final TileEntity tTileEntity = getTileEntityOffset(aX, aY, aZ);
+ if (tTileEntity instanceof IGregTechTileEntity) return (IGregTechTileEntity) tTileEntity;
+ return null;
+ }
+
+ @Override
+ public final IGregTechTileEntity getIGregTechTileEntityAtSide(ForgeDirection side) {
+ final TileEntity tTileEntity = getTileEntityAtSide(side);
+ if (tTileEntity instanceof IGregTechTileEntity) return (IGregTechTileEntity) tTileEntity;
+ return null;
+ }
+
+ @Override
+ public final IGregTechTileEntity getIGregTechTileEntityAtSideAndDistance(ForgeDirection side, int aDistance) {
+ final TileEntity tTileEntity = getTileEntityAtSideAndDistance(side, aDistance);
+ if (tTileEntity instanceof IGregTechTileEntity) return (IGregTechTileEntity) tTileEntity;
+ return null;
+ }
+
+ @Override
+ public final Block getBlock(int aX, int aY, int aZ) {
+ if (ignoreUnloadedChunks && crossedChunkBorder(aX, aZ) && !worldObj.blockExists(aX, aY, aZ)) return Blocks.air;
+ return worldObj.getBlock(aX, aY, aZ);
+ }
+
+ public Block getBlock(ChunkCoordinates aCoords) {
+ if (worldObj == null) return Blocks.air;
+ if (ignoreUnloadedChunks && crossedChunkBorder(aCoords)
+ && !worldObj.blockExists(aCoords.posX, aCoords.posY, aCoords.posZ)) return Blocks.air;
+ return worldObj.getBlock(aCoords.posX, aCoords.posY, aCoords.posZ);
+ }
+
+ @Override
+ public final byte getMetaID(int aX, int aY, int aZ) {
+ if (ignoreUnloadedChunks && crossedChunkBorder(aX, aZ) && !worldObj.blockExists(aX, aY, aZ)) return 0;
+ return (byte) worldObj.getBlockMetadata(aX, aY, aZ);
+ }
+
+ @Override
+ public final byte getLightLevel(int aX, int aY, int aZ) {
+ if (ignoreUnloadedChunks && crossedChunkBorder(aX, aZ) && !worldObj.blockExists(aX, aY, aZ)) return 0;
+ return (byte) (worldObj.getLightBrightness(aX, aY, aZ) * 15);
+ }
+
+ @Override
+ public final boolean getSky(int aX, int aY, int aZ) {
+ if (ignoreUnloadedChunks && crossedChunkBorder(aX, aZ) && !worldObj.blockExists(aX, aY, aZ)) return true;
+ return worldObj.canBlockSeeTheSky(aX, aY, aZ);
+ }
+
+ @Override
+ public final boolean getOpacity(int aX, int aY, int aZ) {
+ if (ignoreUnloadedChunks && crossedChunkBorder(aX, aZ) && !worldObj.blockExists(aX, aY, aZ)) return false;
+ return GT_Utility.isOpaqueBlock(worldObj, aX, aY, aZ);
+ }
+
+ @Override
+ public final boolean getAir(int aX, int aY, int aZ) {
+ if (ignoreUnloadedChunks && crossedChunkBorder(aX, aZ) && !worldObj.blockExists(aX, aY, aZ)) return true;
+ return GT_Utility.isBlockAir(worldObj, aX, aY, aZ);
+ }
+
+ @Override
+ public TileEntity getTileEntity(int aX, int aY, int aZ) {
+ if (ignoreUnloadedChunks && crossedChunkBorder(aX, aZ) && !worldObj.blockExists(aX, aY, aZ)) return null;
+ return worldObj.getTileEntity(aX, aY, aZ);
+ }
+
+ @Override
+ public final TileEntity getTileEntityAtSide(ForgeDirection side) {
+ final int ordinalSide = side.ordinal();
+ if (side == ForgeDirection.UNKNOWN || mBufferedTileEntities[ordinalSide] == this) return null;
+ final int tX = getOffsetX(side, 1);
+ final int tY = getOffsetY(side, 1);
+ final int tZ = getOffsetZ(side, 1);
+ if (crossedChunkBorder(tX, tZ)) {
+ mBufferedTileEntities[ordinalSide] = null;
+ if (ignoreUnloadedChunks && !worldObj.blockExists(tX, tY, tZ)) return null;
+ }
+ if (mBufferedTileEntities[ordinalSide] == null) {
+ mBufferedTileEntities[ordinalSide] = worldObj.getTileEntity(tX, tY, tZ);
+ if (mBufferedTileEntities[ordinalSide] == null) {
+ mBufferedTileEntities[ordinalSide] = this;
+ return null;
+ }
+ return mBufferedTileEntities[ordinalSide];
+ }
+ if (mBufferedTileEntities[ordinalSide].isInvalid()) {
+ mBufferedTileEntities[ordinalSide] = null;
+ return getTileEntityAtSide(side);
+ }
+ if (mBufferedTileEntities[ordinalSide].xCoord == tX && mBufferedTileEntities[ordinalSide].yCoord == tY
+ && mBufferedTileEntities[ordinalSide].zCoord == tZ) {
+ return mBufferedTileEntities[ordinalSide];
+ }
+ return null;
+ }
+
+ @Override
+ public void writeToNBT(NBTTagCompound aNBT) {
+ super.writeToNBT(aNBT);
+ }
+
+ @Override
+ public boolean isDead() {
+ return isDead || isInvalidTileEntity();
+ }
+
+ @Override
+ public void validate() {
+ clearNullMarkersFromTileEntityBuffer();
+ super.validate();
+ }
+
+ @Override
+ public void invalidate() {
+ leaveEnet();
+ clearNullMarkersFromTileEntityBuffer();
+ super.invalidate();
+ }
+
+ @Override
+ public void onChunkUnload() {
+ leaveEnet();
+ clearNullMarkersFromTileEntityBuffer();
+ super.onChunkUnload();
+ isDead = true;
+ }
+
+ @Override
+ public void updateEntity() {
+ // Well if the TileEntity gets ticked it is alive.
+ isDead = false;
+ }
+
+ public final void onAdjacentBlockChange(int ignoredAX, int ignoredAY, int ignoredAZ) {
+ clearNullMarkersFromTileEntityBuffer();
+ }
+
+ public void updateNeighbours(int mStrongRedstone, int oStrongRedstone) {
+ final Block thisBlock = getBlockOffset(0, 0, 0);
+ for (final ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) {
+ final int x1 = xCoord + dir.offsetX, y1 = yCoord + dir.offsetY, z1 = zCoord + dir.offsetZ;
+
+ if (worldObj.blockExists(x1, y1, z1)) {
+ worldObj.notifyBlockOfNeighborChange(x1, y1, z1, thisBlock);
+
+ // update if it was / is strong powered.
+ if (((((mStrongRedstone | oStrongRedstone) >>> dir.ordinal()) & 1) != 0)
+ && getBlock(x1, y1, z1).isNormalCube()) {
+ final int skipUpdateSide = dir.getOpposite()
+ .ordinal(); // Don't update this block. Still updates
+ // diagonal blocks twice if conditions
+ // meet.
+
+ for (final ForgeDirection dir2 : ForgeDirection.VALID_DIRECTIONS) {
+ final int x2 = x1 + dir2.offsetX, y2 = y1 + dir2.offsetY, z2 = z1 + dir2.offsetZ;
+ if (dir2.ordinal() != skipUpdateSide && worldObj.blockExists(x2, y2, z2))
+ worldObj.notifyBlockOfNeighborChange(x2, y2, z2, thisBlock);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public final void sendBlockEvent(byte aID, byte aValue) {
+ NW.sendPacketToAllPlayersInRange(
+ worldObj,
+ new GT_Packet_Block_Event(xCoord, (short) yCoord, zCoord, aID, aValue),
+ xCoord,
+ zCoord);
+ }
+
+ protected boolean crossedChunkBorder(int aX, int aZ) {
+ return aX >> 4 != xCoord >> 4 || aZ >> 4 != zCoord >> 4;
+ }
+
+ public final boolean crossedChunkBorder(ChunkCoordinates aCoords) {
+ return aCoords.posX >> 4 != xCoord >> 4 || aCoords.posZ >> 4 != zCoord >> 4;
+ }
+
+ public final void setOnFire() {
+ GT_Utility.setCoordsOnFire(worldObj, xCoord, yCoord, zCoord, false);
+ }
+
+ public final void setToFire() {
+ worldObj.setBlock(xCoord, yCoord, zCoord, Blocks.fire);
+ }
+
+ @Override
+ public void markDirty() {
+ // Avoid sending neighbor updates, just mark the chunk as dirty to make sure it gets saved
+ final Chunk chunk = worldObj.getChunkFromBlockCoords(xCoord, zCoord);
+ if (chunk != null) {
+ chunk.setChunkModified();
+ }
+ }
+
+ /**
+ * Gets items to be displayed for HoloInventory mod.
+ *
+ * @return null if default implementation should be used, i.e. {@link IInventory#getStackInSlot}.
+ * Otherwise, a list of items to be displayed. Null element may be contained.
+ */
+ @Nullable
+ public List<ItemStack> getItemsForHoloGlasses() {
+ return null;
+ }
+
+ @Deprecated
+ public String trans(String aKey, String aEnglish) {
+ return GT_Utility.trans(aKey, aEnglish);
+ }
+
+ protected Supplier<Boolean> getValidator() {
+ return () -> !this.isDead();
+ }
+
+ public boolean useModularUI() {
+ return false;
+ }
+
+ /*
+ * IC2 Energy Compat
+ */
+ protected TileIC2EnergySink ic2EnergySink = null;
+ protected boolean joinedIc2Enet = false;
+
+ protected void createIc2Sink() {
+ if (ic2EnergySink == null && isServerSide() && shouldJoinIc2Enet()) {
+ ic2EnergySink = new TileIC2EnergySink((IGregTechTileEntity) this);
+ }
+ }
+
+ @Override
+ public void doEnetUpdate() {
+ leaveEnet();
+ joinEnet();
+ }
+
+ protected void joinEnet() {
+ if (joinedIc2Enet || !shouldJoinIc2Enet()) return;
+
+ if (ic2EnergySink == null) createIc2Sink();
+
+ if (ic2EnergySink != null) {
+ MinecraftForge.EVENT_BUS.post(new EnergyTileLoadEvent(ic2EnergySink));
+ joinedIc2Enet = true;
+ }
+ }
+
+ protected void leaveEnet() {
+ if (joinedIc2Enet && ic2EnergySink != null && isServerSide()) {
+ joinedIc2Enet = false;
+ MinecraftForge.EVENT_BUS.post(new EnergyTileUnloadEvent(ic2EnergySink));
+ }
+ }
+
+ // === GUI stuff ===
+
+ public ItemStackHandler getInventoryHandler() {
+ return null;
+ }
+
+ protected GT_TooltipDataCache mTooltipCache = new GT_TooltipDataCache();
+
+ // Tooltip localization keys
+ public static final String BATTERY_SLOT_TOOLTIP = "GT5U.machines.battery_slot.tooltip",
+ BATTERY_SLOT_TOOLTIP_ALT = "GT5U.machines.battery_slot.tooltip.alternative",
+ UNUSED_SLOT_TOOLTIP = "GT5U.machines.unused_slot.tooltip",
+ SPECIAL_SLOT_TOOLTIP = "GT5U.machines.special_slot.tooltip",
+ STALLED_STUTTERING_TOOLTIP = "GT5U.machines.stalled_stuttering.tooltip",
+ STALLED_VENT_TOOLTIP = "GT5U.machines.stalled_vent.tooltip",
+ FLUID_TRANSFER_TOOLTIP = "GT5U.machines.fluid_transfer.tooltip",
+ ITEM_TRANSFER_TOOLTIP = "GT5U.machines.item_transfer.tooltip", POWER_SOURCE_KEY = "GT5U.machines.powersource.",
+ BUTTON_FORBIDDEN_TOOLTIP = "GT5U.gui.button.forbidden",
+ NEI_TRANSFER_STEAM_TOOLTIP = "GT5U.machines.nei_transfer.steam.tooltip",
+ NEI_TRANSFER_VOLTAGE_TOOLTIP = "GT5U.machines.nei_transfer.voltage.tooltip";
+
+ public static final int TOOLTIP_DELAY = 5;
+
+ /**
+ * Override this to add {@link com.gtnewhorizons.modularui.api.widget.Widget}s for your UI.
+ */
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {}
+
+ public void bindPlayerInventoryUI(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ builder.bindPlayerInventory(buildContext.getPlayer(), 7, getGUITextureSet().getItemSlot());
+ }
+
+ public String getLocalName() {
+ return "Unknown";
+ }
+
+ protected void addTitleToUI(ModularWindow.Builder builder) {
+ addTitleToUI(builder, getLocalName());
+ }
+
+ protected void addTitleToUI(ModularWindow.Builder builder, String title) {
+ if (GT_Mod.gregtechproxy.mTitleTabStyle == 2) {
+ addTitleItemIconStyle(builder, title);
+ } else {
+ addTitleTextStyle(builder, title);
+ }
+ }
+
+ protected void addTitleTextStyle(ModularWindow.Builder builder, String title) {
+ final int TAB_PADDING = 3;
+ final int TITLE_PADDING = 2;
+ int titleWidth = 0, titleHeight = 0;
+ if (NetworkUtils.isClient()) {
+ final FontRenderer fontRenderer = Minecraft.getMinecraft().fontRenderer;
+ final List<String> titleLines = fontRenderer
+ .listFormattedStringToWidth(title, getGUIWidth() - (TAB_PADDING + TITLE_PADDING) * 2);
+ titleWidth = titleLines.size() > 1 ? getGUIWidth() - (TAB_PADDING + TITLE_PADDING) * 2
+ : fontRenderer.getStringWidth(title);
+ // noinspection PointlessArithmeticExpression
+ titleHeight = titleLines.size() * fontRenderer.FONT_HEIGHT + (titleLines.size() - 1) * 1;
+ }
+
+ final DrawableWidget tab = new DrawableWidget();
+ final TextWidget text = new TextWidget(title).setDefaultColor(getTitleColor())
+ .setTextAlignment(Alignment.CenterLeft)
+ .setMaxWidth(titleWidth);
+ if (GT_Mod.gregtechproxy.mTitleTabStyle == 1) {
+ tab.setDrawable(getGUITextureSet().getTitleTabAngular())
+ .setPos(0, -(titleHeight + TAB_PADDING) + 1)
+ .setSize(getGUIWidth(), titleHeight + TAB_PADDING * 2);
+ text.setPos(TAB_PADDING + TITLE_PADDING, -titleHeight + TAB_PADDING);
+ } else {
+ tab.setDrawable(getGUITextureSet().getTitleTabDark())
+ .setPos(0, -(titleHeight + TAB_PADDING * 2) + 1)
+ .setSize(titleWidth + (TAB_PADDING + TITLE_PADDING) * 2, titleHeight + TAB_PADDING * 2 - 1);
+ text.setPos(TAB_PADDING + TITLE_PADDING, -titleHeight);
+ }
+ builder.widget(tab)
+ .widget(text);
+ }
+
+ protected void addTitleItemIconStyle(ModularWindow.Builder builder, String title) {
+ builder.widget(
+ new MultiChildWidget().addChild(
+ new DrawableWidget().setDrawable(getGUITextureSet().getTitleTabNormal())
+ .setPos(0, 0)
+ .setSize(24, 24))
+ .addChild(
+ new ItemDrawable(getStackForm(1)).asWidget()
+ .setPos(4, 4))
+ .addTooltip(title)
+ .setTooltipShowUpDelay(TOOLTIP_DELAY)
+ .setPos(0, -24 + 3));
+ }
+
+ @Override
+ public GUITextureSet getGUITextureSet() {
+ return GUITextureSet.DEFAULT;
+ }
+
+ protected int getTitleColor() {
+ return COLOR_TITLE.get();
+ }
+
+ @Override
+ public void addGregTechLogo(ModularWindow.Builder builder) {
+ builder.widget(
+ new DrawableWidget().setDrawable(getGUITextureSet().getGregTechLogo())
+ .setSize(17, 17)
+ .setPos(152, 63));
+ }
+
+ protected int getGUIWidth() {
+ return 176;
+ }
+
+ protected int getGUIHeight() {
+ return 166;
+ }
+
+ protected boolean doesBindPlayerInventory() {
+ return true;
+ }
+
+ @Override
+ public void add1by1Slot(ModularWindow.Builder builder, IDrawable... background) {
+ final ItemStackHandler inventoryHandler = getInventoryHandler();
+ if (inventoryHandler == null) return;
+
+ if (background.length == 0) {
+ background = new IDrawable[] { getGUITextureSet().getItemSlot() };
+ }
+ builder.widget(
+ SlotGroup.ofItemHandler(inventoryHandler, 1)
+ .startFromSlot(0)
+ .endAtSlot(0)
+ .background(background)
+ .build()
+ .setPos(79, 34));
+ }
+
+ @Override
+ public void add2by2Slots(ModularWindow.Builder builder, IDrawable... background) {
+ final ItemStackHandler inventoryHandler = getInventoryHandler();
+ if (inventoryHandler == null) return;
+
+ if (background.length == 0) {
+ background = new IDrawable[] { getGUITextureSet().getItemSlot() };
+ }
+ builder.widget(
+ SlotGroup.ofItemHandler(inventoryHandler, 2)
+ .startFromSlot(0)
+ .endAtSlot(3)
+ .background(background)
+ .build()
+ .setPos(70, 25));
+ }
+
+ @Override
+ public void add3by3Slots(ModularWindow.Builder builder, IDrawable... background) {
+ final ItemStackHandler inventoryHandler = getInventoryHandler();
+ if (inventoryHandler == null) return;
+
+ if (background.length == 0) {
+ background = new IDrawable[] { getGUITextureSet().getItemSlot() };
+ }
+ builder.widget(
+ SlotGroup.ofItemHandler(inventoryHandler, 3)
+ .startFromSlot(0)
+ .endAtSlot(8)
+ .background(background)
+ .build()
+ .setPos(61, 16));
+ }
+
+ @Override
+ public void add4by4Slots(ModularWindow.Builder builder, IDrawable... background) {
+ final ItemStackHandler inventoryHandler = getInventoryHandler();
+ if (inventoryHandler == null) return;
+
+ if (background.length == 0) {
+ background = new IDrawable[] { getGUITextureSet().getItemSlot() };
+ }
+ builder.widget(
+ SlotGroup.ofItemHandler(inventoryHandler, 4)
+ .startFromSlot(0)
+ .endAtSlot(15)
+ .background(background)
+ .build()
+ .setPos(52, 7));
+ }
+
+ public void addCoverTabs(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ /* Do nothing */
+ }
+
+ public IConfigurationCircuitSupport getConfigurationCircuitSupport() {
+ if (!(this instanceof IConfigurationCircuitSupport)) return null;
+ return (IConfigurationCircuitSupport) this;
+ }
+
+ protected void addConfigurationCircuitSlot(ModularWindow.Builder builder) {
+ final ItemStackHandler inventoryHandler = getInventoryHandler();
+ if (inventoryHandler == null) return;
+
+ if (!(this instanceof IInventory inv)) return;
+
+ final IConfigurationCircuitSupport ccs = getConfigurationCircuitSupport();
+ if (ccs == null) return;
+
+ final AtomicBoolean dialogOpened = new AtomicBoolean(false);
+ builder.widget(new SlotWidget(new BaseSlot(inventoryHandler, ccs.getCircuitSlot(), true)) {
+
+ @Override
+ protected void phantomClick(ClickData clickData, ItemStack cursorStack) {
+ final ItemStack newCircuit;
+ if (clickData.shift) {
+ if (clickData.mouseButton == 0) {
+ if (NetworkUtils.isClient() && !dialogOpened.get()) {
+ openSelectCircuitDialog(getContext(), dialogOpened);
+ }
+ return;
+ } else {
+ newCircuit = null;
+ }
+ } else {
+ final List<ItemStack> tCircuits = ccs.getConfigurationCircuits();
+ final int index = GT_Utility.findMatchingStackInList(tCircuits, cursorStack);
+ if (index < 0) {
+ int curIndex = GT_Utility
+ .findMatchingStackInList(tCircuits, inv.getStackInSlot(ccs.getCircuitSlot())) + 1;
+ if (clickData.mouseButton == 0) {
+ curIndex += 1;
+ } else {
+ curIndex -= 1;
+ }
+ curIndex = Math.floorMod(curIndex, tCircuits.size() + 1) - 1;
+ newCircuit = curIndex < 0 ? null : tCircuits.get(curIndex);
+ } else {
+ // set to whatever it is
+ newCircuit = tCircuits.get(index);
+ }
+ }
+ inv.setInventorySlotContents(ccs.getCircuitSlot(), newCircuit);
+ }
+
+ @Override
+ protected void phantomScroll(int direction) {
+ phantomClick(new ClickData(direction > 0 ? 1 : 0, false, false, false));
+ }
+
+ @Override
+ public List<String> getExtraTooltip() {
+ return Arrays.asList(
+ EnumChatFormatting.DARK_GRAY + EnumChatFormatting.getTextWithoutFormattingCodes(
+ StatCollector.translateToLocal("GT5U.machines.select_circuit.tooltip.1")),
+ EnumChatFormatting.DARK_GRAY + EnumChatFormatting.getTextWithoutFormattingCodes(
+ StatCollector.translateToLocal("GT5U.machines.select_circuit.tooltip.2")),
+ EnumChatFormatting.DARK_GRAY + EnumChatFormatting.getTextWithoutFormattingCodes(
+ StatCollector.translateToLocal("GT5U.machines.select_circuit.tooltip.3")));
+ }
+ }.setOverwriteItemStackTooltip(list -> {
+ list.removeIf(
+ line -> line.contains(StatCollector.translateToLocal("gt.integrated_circuit.tooltip.0"))
+ || line.contains(StatCollector.translateToLocal("gt.integrated_circuit.tooltip.1")));
+ return list;
+ })
+ .disableShiftInsert()
+ .setHandlePhantomActionClient(true)
+ .setBackground(getGUITextureSet().getItemSlot(), GT_UITextures.OVERLAY_SLOT_INT_CIRCUIT)
+ .setGTTooltip(() -> mTooltipCache.getData("GT5U.machines.select_circuit.tooltip"))
+ .setTooltipShowUpDelay(TOOLTIP_DELAY)
+ .setPos(ccs.getCircuitSlotX() - 1, ccs.getCircuitSlotY() - 1));
+ }
+
+ protected void openSelectCircuitDialog(ModularUIContext uiContext, AtomicBoolean dialogOpened) {
+ final IConfigurationCircuitSupport ccs = getConfigurationCircuitSupport();
+ if (ccs == null) return;
+
+ if (!(this instanceof IInventory inv)) return;
+
+ final List<ItemStack> circuits = ccs.getConfigurationCircuits();
+ uiContext.openClientWindow(
+ player -> new SelectItemUIFactory(
+ StatCollector.translateToLocal("GT5U.machines.select_circuit"),
+ getStackForm(0),
+ this::onCircuitSelected,
+ circuits,
+ GT_Utility.findMatchingStackInList(circuits, inv.getStackInSlot(ccs.getCircuitSlot())))
+ .setAnotherWindow(true, dialogOpened)
+ .setGuiTint(getGUIColorization())
+ .setCurrentGetter(() -> inv.getStackInSlot(ccs.getCircuitSlot()))
+ .createWindow(new UIBuildContext(player)));
+ }
+
+ protected void onCircuitSelected(ItemStack selected) {
+ final IConfigurationCircuitSupport ccs = getConfigurationCircuitSupport();
+ if (ccs == null) return;
+
+ if (!(this instanceof IInventory inv)) return;
+
+ GT_Values.NW.sendToServer(new GT_Packet_SetConfigurationCircuit(this, selected));
+ // we will not do any validation on client side
+ // it doesn't get to actually decide what inventory contains anyway
+ inv.setInventorySlotContents(ccs.getCircuitSlot(), selected);
+ }
+
+ protected int getTextColorOrDefault(String textType, int defaultColor) {
+ return defaultColor;
+ }
+
+ protected Supplier<Integer> COLOR_TITLE = () -> getTextColorOrDefault("title", 0x404040);
+ protected Supplier<Integer> COLOR_TITLE_WHITE = () -> getTextColorOrDefault("title_white", 0xfafaff);
+ protected Supplier<Integer> COLOR_TEXT_WHITE = () -> getTextColorOrDefault("text_white", 0xfafaff);
+ protected Supplier<Integer> COLOR_TEXT_GRAY = () -> getTextColorOrDefault("text_gray", 0x404040);
+ protected Supplier<Integer> COLOR_TEXT_RED = () -> getTextColorOrDefault("text_red", 0xff0000);
+
+ public int getGUIColorization() {
+ return GT_Util.getRGBaInt(Dyes.dyeWhite.getRGBA());
+ }
+
+ public ItemStack getStackForm(long aAmount) {
+ return null;
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/CommonMetaTileEntity.java b/src/main/java/gregtech/api/metatileentity/CommonMetaTileEntity.java
new file mode 100644
index 0000000000..5a2e88b242
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/CommonMetaTileEntity.java
@@ -0,0 +1,340 @@
+package gregtech.api.metatileentity;
+
+import static gregtech.GT_Mod.GT_FML_LOGGER;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+import net.minecraft.network.Packet;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizons.modularui.api.forge.ItemStackHandler;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+
+import appeng.api.crafting.ICraftingIconProvider;
+import gregtech.GT_Mod;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.ItemList;
+import gregtech.api.gui.modularui.GUITextureSet;
+import gregtech.api.interfaces.IConfigurationCircuitSupport;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.modularui.IAddGregtechLogo;
+import gregtech.api.interfaces.modularui.IAddUIWidgets;
+import gregtech.api.interfaces.modularui.IBindPlayerInventoryUI;
+import gregtech.api.interfaces.modularui.IGetTitleColor;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.objects.GT_ItemStack;
+import gregtech.api.util.GT_Log;
+import gregtech.api.util.GT_Utility;
+
+public abstract class CommonMetaTileEntity extends CoverableTileEntity
+ implements IGregTechTileEntity, ICraftingIconProvider {
+
+ protected boolean mNeedsBlockUpdate = true, mNeedsUpdate = true, mSendClientData = false, mInventoryChanged = false;
+
+ protected boolean createNewMetatileEntity(short aID) {
+ if (aID <= 0 || aID >= GregTech_API.METATILEENTITIES.length || GregTech_API.METATILEENTITIES[aID] == null) {
+ GT_Log.err.println("MetaID " + aID + " not loadable => locking TileEntity!");
+ } else {
+ if (hasValidMetaTileEntity()) getMetaTileEntity().setBaseMetaTileEntity(null);
+ GregTech_API.METATILEENTITIES[aID].newMetaEntity(this)
+ .setBaseMetaTileEntity(this);
+ mTickTimer = 0;
+ mID = aID;
+ return true;
+ }
+ return false;
+ }
+
+ protected void saveMetaTileNBT(NBTTagCompound aNBT) {
+ try {
+ if (hasValidMetaTileEntity()) {
+ aNBT.setInteger("nbtVersion", GT_Mod.NBT_VERSION);
+ final NBTTagList tItemList = new NBTTagList();
+ for (int i = 0; i < getMetaTileEntity().getRealInventory().length; i++) {
+ final ItemStack tStack = getMetaTileEntity().getRealInventory()[i];
+ if (tStack != null) {
+ final NBTTagCompound tTag = new NBTTagCompound();
+ tTag.setInteger("IntSlot", i);
+ tStack.writeToNBT(tTag);
+ tItemList.appendTag(tTag);
+ }
+ }
+ aNBT.setTag("Inventory", tItemList);
+
+ try {
+ getMetaTileEntity().saveNBTData(aNBT);
+ } catch (Throwable e) {
+ GT_FML_LOGGER.error("Encountered CRITICAL ERROR while saving MetaTileEntity.");
+ GT_Mod.logStackTrace(e);
+ }
+ }
+ } catch (Throwable e) {
+ GT_FML_LOGGER.error("Encountered CRITICAL ERROR while saving MetaTileEntity.");
+ GT_Mod.logStackTrace(e);
+ }
+ }
+
+ protected void loadMetaTileNBT(NBTTagCompound aNBT) {
+ final int nbtVersion = aNBT.getInteger("nbtVersion");
+ if (mID != 0 && createNewMetatileEntity(mID)) {
+ final NBTTagList tItemList = aNBT.getTagList("Inventory", 10);
+ for (int i = 0; i < tItemList.tagCount(); i++) {
+ final NBTTagCompound tTag = tItemList.getCompoundTagAt(i);
+ final int tSlot = migrateInventoryIndex(tTag.getInteger("IntSlot"), nbtVersion);
+ if (tSlot >= 0 && tSlot < getMetaTileEntity().getRealInventory().length) {
+ ItemStack loadedStack = GT_Utility.loadItem(tTag);
+ // We move away from fluid display item in TEs
+ if (loadedStack != null && loadedStack.getItem() == ItemList.Display_Fluid.getItem()) {
+ loadedStack = null;
+ }
+ getMetaTileEntity().getRealInventory()[tSlot] = loadedStack;
+ }
+ }
+
+ try {
+ getMetaTileEntity().loadNBTData(aNBT);
+ } catch (Throwable e) {
+ GT_FML_LOGGER.error("Encountered Exception while loading MetaTileEntity.");
+ GT_Mod.logStackTrace(e);
+ }
+ }
+ }
+
+ /**
+ * Shifts the machine Inventory index according to the change in Input/Output Slots. Default implementation does not
+ * do anything to the slotIndex.
+ */
+ protected int migrateInventoryIndex(int slotIndex, int nbtVersion) {
+ return slotIndex;
+ }
+
+ @Override
+ public void markDirty() {
+ super.markDirty();
+ mInventoryChanged = true;
+ }
+
+ @Override
+ public boolean hasInventoryBeenModified() {
+ return mInventoryChanged;
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ if (canAccessData()) return getMetaTileEntity().isValidSlot(aIndex);
+ return false;
+ }
+
+ @Override
+ public Packet getDescriptionPacket() {
+ issueClientUpdate();
+ return null;
+ }
+
+ @Override
+ public void issueTextureUpdate() {
+ mNeedsUpdate = true;
+ }
+
+ @Override
+ public void issueClientUpdate() {
+ mSendClientData = true;
+ }
+
+ @Override
+ public void issueBlockUpdate() {
+ mNeedsBlockUpdate = true;
+ }
+
+ @Override
+ public boolean isValidFacing(ForgeDirection side) {
+ if (canAccessData()) return getMetaTileEntity().isFacingValid(side);
+ return false;
+ }
+
+ protected boolean canAccessData() {
+ return !isDead && hasValidMetaTileEntity();
+ }
+
+ protected abstract boolean hasValidMetaTileEntity();
+
+ @Override
+ public String[] getDescription() {
+ if (canAccessData()) return getMetaTileEntity().getDescription();
+ return new String[0];
+ }
+
+ @Override
+ public boolean isStillValid() {
+ return hasValidMetaTileEntity();
+ }
+
+ @Override
+ public boolean allowCoverOnSide(ForgeDirection side, GT_ItemStack aCoverID) {
+ return hasValidMetaTileEntity() && getMetaTileEntity().allowCoverOnSide(side, aCoverID);
+ }
+
+ @Override
+ public void issueCoverUpdate(ForgeDirection side) {
+ super.issueCoverUpdate(side);
+ issueClientUpdate();
+ }
+
+ /*
+ * IC2 Energy Compat
+ */
+ @Override
+ public boolean shouldJoinIc2Enet() {
+ final IMetaTileEntity meta = getMetaTileEntity();
+ return meta != null && meta.shouldJoinIc2Enet();
+ }
+
+ /*
+ * Modular UI Support
+ */
+
+ @Override
+ public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ if (hasValidMetaTileEntity() && getMetaTileEntity() instanceof IAddUIWidgets) {
+ ((IAddUIWidgets) getMetaTileEntity()).addUIWidgets(builder, buildContext);
+ return;
+ }
+ super.addUIWidgets(builder, buildContext);
+ }
+
+ @Override
+ public void bindPlayerInventoryUI(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ if (hasValidMetaTileEntity() && getMetaTileEntity() instanceof IBindPlayerInventoryUI) {
+ ((IBindPlayerInventoryUI) getMetaTileEntity()).bindPlayerInventoryUI(builder, buildContext);
+ return;
+ }
+ super.bindPlayerInventoryUI(builder, buildContext);
+ }
+
+ @Override
+ public IConfigurationCircuitSupport getConfigurationCircuitSupport() {
+ if (hasValidMetaTileEntity() && getMetaTileEntity() instanceof IConfigurationCircuitSupport) {
+ return (IConfigurationCircuitSupport) getMetaTileEntity();
+ }
+ return null;
+ }
+
+ @Override
+ public ItemStackHandler getInventoryHandler() {
+ if (hasValidMetaTileEntity()) {
+ return getMetaTileEntity().getInventoryHandler();
+ }
+ return null;
+ }
+
+ @Override
+ public boolean useModularUI() {
+ return hasValidMetaTileEntity() && getMetaTileEntity().useModularUI();
+ }
+
+ @Override
+ public String getLocalName() {
+ if (hasValidMetaTileEntity()) return getMetaTileEntity().getLocalName();
+ return super.getLocalName();
+ }
+
+ @Override
+ protected int getGUIWidth() {
+ if (hasValidMetaTileEntity()) return getMetaTileEntity().getGUIWidth();
+
+ return super.getGUIWidth();
+ }
+
+ @Override
+ protected int getGUIHeight() {
+ if (hasValidMetaTileEntity()) return getMetaTileEntity().getGUIHeight();
+
+ return super.getGUIHeight();
+ }
+
+ @Override
+ protected boolean doesBindPlayerInventory() {
+ if (hasValidMetaTileEntity()) return getMetaTileEntity().doesBindPlayerInventory();
+
+ return super.doesBindPlayerInventory();
+ }
+
+ @Override
+ public void addGregTechLogo(ModularWindow.Builder builder) {
+ if (hasValidMetaTileEntity() && getMetaTileEntity() instanceof IAddGregtechLogo) {
+ ((IAddGregtechLogo) getMetaTileEntity()).addGregTechLogo(builder);
+ return;
+ }
+ super.addGregTechLogo(builder);
+ }
+
+ @Override
+ public ItemStack getStackForm(long aAmount) {
+ if (hasValidMetaTileEntity()) {
+ return getMetaTileEntity().getStackForm(aAmount);
+ }
+ return super.getStackForm(aAmount);
+ }
+
+ @Override
+ public int getTitleColor() {
+ if (hasValidMetaTileEntity() && getMetaTileEntity() instanceof IGetTitleColor) {
+ return ((IGetTitleColor) getMetaTileEntity()).getTitleColor();
+ }
+ return super.getTitleColor();
+ }
+
+ @Override
+ public int getGUIColorization() {
+ if (hasValidMetaTileEntity()) {
+ return getMetaTileEntity().getGUIColorization();
+ }
+ return super.getGUIColorization();
+ }
+
+ @Override
+ protected int getTextColorOrDefault(String textType, int defaultColor) {
+ if (hasValidMetaTileEntity()) {
+ return getMetaTileEntity().getTextColorOrDefault(textType, defaultColor);
+ }
+ return defaultColor;
+ }
+
+ @Override
+ public GUITextureSet getGUITextureSet() {
+ if (hasValidMetaTileEntity()) {
+ return getMetaTileEntity().getGUITextureSet();
+ }
+ return super.getGUITextureSet();
+ }
+
+ @Override
+ public ItemStack getMachineCraftingIcon() {
+ return getMetaTileEntity() != null ? getMetaTileEntity().getMachineCraftingIcon() : null;
+ }
+
+ @Override
+ public ModularWindow createWindow(UIBuildContext buildContext) {
+ if (!useModularUI()) return null;
+
+ buildContext.setValidator(getValidator());
+ final ModularWindow.Builder builder = ModularWindow.builder(getGUIWidth(), getGUIHeight());
+ builder.setBackground(getGUITextureSet().getMainBackground());
+ builder.setGuiTint(getGUIColorization());
+ if (doesBindPlayerInventory()) {
+ bindPlayerInventoryUI(builder, buildContext);
+ }
+ addUIWidgets(builder, buildContext);
+ addTitleToUI(builder);
+ addCoverTabs(builder, buildContext);
+ final IConfigurationCircuitSupport csc = getConfigurationCircuitSupport();
+ if (csc != null && csc.allowSelectCircuit()) {
+ addConfigurationCircuitSlot(builder);
+ } else {
+ addGregTechLogo(builder);
+ }
+ return builder.build();
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/CoverableTileEntity.java b/src/main/java/gregtech/api/metatileentity/CoverableTileEntity.java
new file mode 100644
index 0000000000..277b79c777
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/CoverableTileEntity.java
@@ -0,0 +1,803 @@
+package gregtech.api.metatileentity;
+
+import static gregtech.api.enums.GT_Values.E;
+import static gregtech.api.enums.GT_Values.NW;
+import static gregtech.api.util.GT_LanguageManager.FACES;
+import static gregtech.api.util.GT_LanguageManager.getTranslation;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.IntStream;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.entity.item.EntityItem;
+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.nbt.NBTTagList;
+import net.minecraft.network.PacketBuffer;
+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 net.minecraftforge.fluids.FluidRegistry;
+
+import org.jetbrains.annotations.NotNull;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.io.ByteStreams;
+import com.gtnewhorizons.modularui.api.drawable.IDrawable;
+import com.gtnewhorizons.modularui.api.drawable.ItemDrawable;
+import com.gtnewhorizons.modularui.api.math.MainAxisAlignment;
+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.ButtonWidget;
+import com.gtnewhorizons.modularui.common.widget.Column;
+import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
+import com.gtnewhorizons.modularui.common.widget.MultiChildWidget;
+
+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.GT_Values;
+import gregtech.api.enums.Textures;
+import gregtech.api.gui.modularui.GUITextureSet;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.tileentity.ICoverable;
+import gregtech.api.interfaces.tileentity.IGregtechWailaProvider;
+import gregtech.api.net.GT_Packet_RequestCoverData;
+import gregtech.api.net.GT_Packet_SendCoverData;
+import gregtech.api.net.GT_Packet_TileEntityCoverGUI;
+import gregtech.api.objects.GT_ItemStack;
+import gregtech.api.util.GT_CoverBehavior;
+import gregtech.api.util.GT_CoverBehaviorBase;
+import gregtech.api.util.ISerializableObject;
+import gregtech.common.GT_Client;
+import gregtech.common.covers.CoverInfo;
+import gregtech.common.covers.GT_Cover_Fluidfilter;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+public abstract class CoverableTileEntity extends BaseTileEntity implements ICoverable, IGregtechWailaProvider {
+
+ public static final String[] COVER_DATA_NBT_KEYS = Arrays.stream(ForgeDirection.VALID_DIRECTIONS)
+ .mapToInt(Enum::ordinal)
+ .mapToObj(i -> "mCoverData" + i)
+ .toArray(String[]::new);
+
+ // New Cover Information
+ protected final CoverInfo[] coverInfos = new CoverInfo[] { null, null, null, null, null, null };
+ private byte validCoversMask;
+
+ protected byte[] mSidedRedstone = new byte[] { 15, 15, 15, 15, 15, 15 };
+ protected boolean mRedstone = false;
+ protected byte mStrongRedstone = 0;
+
+ protected short mID = 0;
+ public long mTickTimer = 0;
+ private Map<ForgeDirection, ISerializableObject> clientCoverData = new HashMap<>();
+
+ protected void writeCoverNBT(NBTTagCompound aNBT, boolean isDrop) {
+ final NBTTagList tList = new NBTTagList();
+ final int[] coverSides = new int[] { 0, 0, 0, 0, 0, 0 };
+
+ for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
+ final CoverInfo coverInfo = getCoverInfoAtSide(side);
+ if (!coverInfo.isValid()) continue;
+
+ // Backwards compat, in case of a revert... for now
+ tList.appendTag(coverInfo.writeToNBT(new NBTTagCompound()));
+ aNBT.setTag(
+ COVER_DATA_NBT_KEYS[side.ordinal()],
+ coverInfo.getCoverData()
+ .saveDataToNBT());
+ }
+ if (tList.tagCount() > 0) {
+ aNBT.setTag(GT_Values.NBT.COVERS, tList);
+ // Backwards compat, in case of a revert... for now
+ aNBT.setIntArray("mCoverSides", coverSides);
+ }
+
+ if (mStrongRedstone > 0) aNBT.setByte("mStrongRedstone", mStrongRedstone);
+
+ if (!isDrop) {
+ aNBT.setByteArray("mRedstoneSided", mSidedRedstone);
+ aNBT.setBoolean("mRedstone", mRedstone);
+ }
+ }
+
+ protected void readCoverNBT(NBTTagCompound aNBT) {
+ mRedstone = aNBT.getBoolean("mRedstone");
+ mSidedRedstone = aNBT.hasKey("mRedstoneSided") ? aNBT.getByteArray("mRedstoneSided")
+ : new byte[] { 15, 15, 15, 15, 15, 15 };
+ mStrongRedstone = aNBT.getByte("mStrongRedstone");
+
+ if (aNBT.hasKey(GT_Values.NBT.COVERS)) {
+ readCoverInfoNBT(aNBT);
+ } else if (aNBT.hasKey("mCoverSides")) {
+ readLegacyCoverInfoNBT(aNBT);
+ }
+ }
+
+ public void readCoverInfoNBT(NBTTagCompound aNBT) {
+ final NBTTagList tList = aNBT.getTagList(GT_Values.NBT.COVERS, 10);
+ for (byte i = 0; i < tList.tagCount(); i++) {
+ final NBTTagCompound tNBT = tList.getCompoundTagAt(i);
+ final CoverInfo coverInfo = new CoverInfo(this, tNBT);
+ this.setCoverInfoAtSide(coverInfo.getSide(), coverInfo);
+ if (coverInfo.isDataNeededOnClient()) issueCoverUpdate(ForgeDirection.getOrientation(i));
+ }
+ }
+
+ public void readLegacyCoverInfoNBT(NBTTagCompound aNBT) {
+ final int[] coverIDs = aNBT.hasKey("mCoverSides") ? aNBT.getIntArray("mCoverSides")
+ : new int[] { 0, 0, 0, 0, 0, 0 };
+ final boolean hasOldCoverData = (aNBT.hasKey("mCoverData", 11) && aNBT.getIntArray("mCoverData").length == 6);
+ final int[] tOldData = hasOldCoverData ? aNBT.getIntArray("mCoverData") : new int[] {};
+
+ for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
+ final int ordinalSide = side.ordinal();
+ if (coverIDs[ordinalSide] == 0) continue;
+
+ final CoverInfo coverInfo = new CoverInfo(side, coverIDs[ordinalSide], this, null);
+ final GT_CoverBehaviorBase<?> coverBehavior = coverInfo.getCoverBehavior();
+ if (coverBehavior == GregTech_API.sNoBehavior) continue;
+
+ ISerializableObject coverData = null;
+ if (hasOldCoverData) {
+ if (coverBehavior instanceof GT_Cover_Fluidfilter) {
+ final String filterKey = String.format("fluidFilter%d", ordinalSide);
+ if (aNBT.hasKey(filterKey)) {
+ coverData = coverInfo.getCoverBehavior()
+ .createDataObject(
+ (tOldData[ordinalSide] & 7)
+ | (FluidRegistry.getFluidID(aNBT.getString(filterKey)) << 3));
+ }
+ } else {
+ coverData = coverBehavior.createDataObject(tOldData[ordinalSide]);
+ }
+ } else {
+ if (aNBT.hasKey(COVER_DATA_NBT_KEYS[ordinalSide]))
+ coverData = coverBehavior.createDataObject(aNBT.getTag(COVER_DATA_NBT_KEYS[ordinalSide]));
+ }
+
+ if (coverData != null) coverInfo.setCoverData(coverData);
+ setCoverInfoAtSide(side, coverInfo);
+ if (coverInfo.isDataNeededOnClient()) issueCoverUpdate(side);
+ }
+ }
+
+ public abstract boolean isStillValid();
+
+ protected boolean doCoverThings() {
+ byte validCoversMask = this.validCoversMask;
+ if (validCoversMask == 0) return true;
+
+ for (int i = Integer.numberOfTrailingZeros(validCoversMask); i < 6; i++) {
+ if (((validCoversMask >>> i) & 1) == 0) continue;
+ if (!tickCoverAtSide(ForgeDirection.VALID_DIRECTIONS[i])) return false;
+ }
+
+ return true;
+ }
+
+ public boolean tickCoverAtSide(ForgeDirection side) {
+ return tickCoverAtSide(side, mTickTimer);
+ }
+
+ /**
+ * @return {@code false} if the tile is no longer valid after ticking the cover
+ */
+ public boolean tickCoverAtSide(ForgeDirection side, long aTickTimer) {
+ final CoverInfo coverInfo = getCoverInfoAtSide(side);
+ if (!coverInfo.isValid()) return true;
+ final int tCoverTickRate = coverInfo.getTickRate();
+ if (tCoverTickRate > 0 && aTickTimer % tCoverTickRate == 0) {
+ final byte tRedstone = coverInfo.isRedstoneSensitive(aTickTimer) ? getInputRedstoneSignal(side) : 0;
+ coverInfo.setCoverData(coverInfo.doCoverThings(aTickTimer, tRedstone));
+ return isStillValid();
+ }
+
+ return true;
+ }
+
+ public abstract boolean allowCoverOnSide(ForgeDirection side, GT_ItemStack aCoverID);
+
+ protected void checkDropCover() {
+ for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
+ final int coverId = getCoverIDAtSide(side);
+ if (coverId != 0 && !allowCoverOnSide(side, new GT_ItemStack(coverId))) dropCover(side, side, true);
+ }
+ }
+
+ protected void updateCoverBehavior() {
+ for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
+ final CoverInfo coverInfo = getCoverInfoAtSide(side);
+ if (coverInfo.isValid()) coverInfo.updateCoverBehavior();
+ }
+ }
+
+ @Override
+ public void issueCoverUpdate(ForgeDirection side) {
+ // If we've got a null worldObj we're getting called as a part of readingNBT from a non tickable MultiTileEntity
+ // on chunk load before the world is set, so we'll want to send a cover update.
+ final CoverInfo coverInfo = getCoverInfoAtSide(side);
+ if (worldObj == null || (isServerSide() && coverInfo.isDataNeededOnClient())) coverInfo.setNeedsUpdate(true);
+ }
+
+ public final ITexture getCoverTexture(ForgeDirection side) {
+ final CoverInfo coverInfo = getCoverInfoAtSide(side);
+ if (!coverInfo.isValid()) return null;
+ if (GT_Mod.instance.isClientSide() && (GT_Client.hideValue & 0x1) != 0) {
+ return Textures.BlockIcons.HIDDEN_TEXTURE[0]; // See through
+ }
+ final ITexture coverTexture = (!(this instanceof BaseMetaPipeEntity)) ? coverInfo.getSpecialCoverFGTexture()
+ : coverInfo.getSpecialCoverTexture();
+
+ return coverTexture != null ? coverTexture : GregTech_API.sCovers.get(new GT_ItemStack(getCoverIDAtSide(side)));
+ }
+
+ protected void requestCoverDataIfNeeded() {
+ if (worldObj == null || !worldObj.isRemote) return;
+ for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
+ final CoverInfo coverInfo = getCoverInfoAtSide(side);
+ if (coverInfo.isDataNeededOnClient()) NW.sendToServer(new GT_Packet_RequestCoverData(coverInfo, this));
+ }
+ }
+
+ @Override
+ public void setCoverIdAndDataAtSide(ForgeDirection side, int aId, ISerializableObject aData) {
+ if (setCoverIDAtSideNoUpdate(side, aId, aData)) {
+ issueCoverUpdate(side);
+ issueBlockUpdate();
+ }
+ }
+
+ @Override
+ public void setCoverIDAtSide(ForgeDirection side, int aID) {
+ setCoverIdAndDataAtSide(side, aID, null);
+ }
+
+ @Override
+ public boolean setCoverIDAtSideNoUpdate(ForgeDirection side, int aID) {
+ return setCoverIDAtSideNoUpdate(side, aID, null);
+ }
+
+ public boolean setCoverIDAtSideNoUpdate(ForgeDirection side, int aID, ISerializableObject aData) {
+ final CoverInfo oldCoverInfo = getCoverInfoAtSide(side);
+ if (side != ForgeDirection.UNKNOWN && oldCoverInfo.getCoverID() != aID) {
+ if (aID == 0 && isClientSide()) oldCoverInfo.onDropped();
+ setCoverInfoAtSide(side, new CoverInfo(side, aID, this, aData));
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ @Deprecated
+ public void setCoverDataAtSide(ForgeDirection side, int aData) {
+ final CoverInfo coverInfo = getCoverInfoAtSide(side);
+ if (coverInfo.isValid() && coverInfo.getCoverData() instanceof ISerializableObject.LegacyCoverData)
+ coverInfo.setCoverData(new ISerializableObject.LegacyCoverData(aData));
+ }
+
+ @Override
+ public void setCoverDataAtSide(ForgeDirection side, ISerializableObject aData) {
+ final CoverInfo coverInfo = getCoverInfoAtSide(side);
+ if (coverInfo.isValid() && coverInfo.getCoverBehavior()
+ .cast(aData) != null) coverInfo.setCoverData(aData);
+ }
+
+ @Override
+ @Deprecated
+ public GT_CoverBehavior getCoverBehaviorAtSide(ForgeDirection side) {
+ final GT_CoverBehaviorBase<?> behavior = getCoverInfoAtSide(side).getCoverBehavior();
+ if (behavior instanceof GT_CoverBehavior) return (GT_CoverBehavior) behavior;
+ return GregTech_API.sNoBehavior;
+ }
+
+ @Override
+ public void setCoverItemAtSide(ForgeDirection side, ItemStack aCover) {
+ GregTech_API.getCoverBehaviorNew(aCover)
+ .placeCover(side, aCover, this);
+ }
+
+ @Override
+ public int getCoverIDAtSide(ForgeDirection side) {
+ return getCoverInfoAtSide(side).getCoverID();
+ }
+
+ @Override
+ public ItemStack getCoverItemAtSide(ForgeDirection side) {
+ return getCoverInfoAtSide(side).getDisplayStack();
+ }
+
+ @Override
+ public boolean canPlaceCoverIDAtSide(ForgeDirection side, int aID) {
+ return getCoverIDAtSide(side) == 0;
+ }
+
+ @Override
+ public boolean canPlaceCoverItemAtSide(ForgeDirection side, ItemStack aCover) {
+ return getCoverIDAtSide(side) == 0;
+ }
+
+ @Override
+ @Deprecated
+ public int getCoverDataAtSide(ForgeDirection side) {
+ final ISerializableObject coverData = getCoverInfoAtSide(side).getCoverData();
+ if (coverData instanceof ISerializableObject.LegacyCoverData) {
+ return ((ISerializableObject.LegacyCoverData) coverData).get();
+ }
+ return 0;
+ }
+
+ @Override
+ public ISerializableObject getComplexCoverDataAtSide(ForgeDirection side) {
+ return getCoverInfoAtSide(side).getCoverData();
+ }
+
+ @Override
+ public GT_CoverBehaviorBase<?> getCoverBehaviorAtSideNew(ForgeDirection side) {
+ return getCoverInfoAtSide(side).getCoverBehavior();
+ }
+
+ public final void setCoverInfoAtSide(ForgeDirection side, CoverInfo coverInfo) {
+ if (side != ForgeDirection.UNKNOWN) {
+ coverInfos[side.ordinal()] = coverInfo;
+
+ validCoversMask &= (byte) ~side.flag;
+ if (coverInfo.isValid()) validCoversMask |= side.flag;
+ }
+ }
+
+ @Override
+ public final CoverInfo getCoverInfoAtSide(ForgeDirection side) {
+ final int ordinalSide = side.ordinal();
+ if (side != ForgeDirection.UNKNOWN) {
+ CoverInfo coverInfo = coverInfos[ordinalSide];
+ if (coverInfo == null) coverInfo = (coverInfos[ordinalSide] = new CoverInfo(side, this));
+ return coverInfo;
+ }
+ return CoverInfo.EMPTY_INFO;
+ }
+
+ public void clearCoverInfoAtSide(ForgeDirection side) {
+ if (side != ForgeDirection.UNKNOWN) {
+ setCoverIDAtSide(side, 0);
+ }
+ }
+
+ @Override
+ public boolean dropCover(ForgeDirection side, ForgeDirection droppedSide, boolean aForced) {
+ final CoverInfo coverInfo = getCoverInfoAtSide(side);
+ if (!coverInfo.isValid()) return false;
+ if (!coverInfo.onCoverRemoval(aForced) && !aForced) return false;
+ final ItemStack tStack = coverInfo.getDrop();
+ if (tStack != null) {
+ coverInfo.onDropped();
+ final EntityItem tEntity = new EntityItem(
+ worldObj,
+ getOffsetX(droppedSide, 1) + 0.5,
+ getOffsetY(droppedSide, 1) + 0.5,
+ getOffsetZ(droppedSide, 1) + 0.5,
+ tStack);
+ tEntity.motionX = 0;
+ tEntity.motionY = 0;
+ tEntity.motionZ = 0;
+ worldObj.spawnEntityInWorld(tEntity);
+ }
+ clearCoverInfoAtSide(side);
+ updateOutputRedstoneSignal(side);
+
+ return true;
+ }
+
+ protected void onBaseTEDestroyed() {
+ for (ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
+ final CoverInfo coverInfo = getCoverInfoAtSide(side);
+ if (coverInfo.isValid()) coverInfo.onBaseTEDestroyed();
+ }
+ }
+
+ @Override
+ public void setOutputRedstoneSignal(ForgeDirection side, byte strength) {
+ final byte cappedStrength = (byte) Math.min(Math.max(0, strength), 15);
+ if (side == ForgeDirection.UNKNOWN) return;
+
+ final int ordinalSide = side.ordinal();
+ if (mSidedRedstone[ordinalSide] != cappedStrength || (mStrongRedstone & (1 << ordinalSide)) > 0) {
+ mSidedRedstone[ordinalSide] = cappedStrength;
+ issueBlockUpdate();
+ }
+ }
+
+ @Override
+ public void setStrongOutputRedstoneSignal(ForgeDirection side, byte strength) {
+ mStrongRedstone |= (byte) side.flag;
+ setOutputRedstoneSignal(side, strength);
+ }
+
+ @Override
+ public void setInternalOutputRedstoneSignal(ForgeDirection side, byte aStrength) {
+ if (!getCoverBehaviorAtSideNew(side)
+ .manipulatesSidedRedstoneOutput(side, getCoverIDAtSide(side), getComplexCoverDataAtSide(side), this))
+ setOutputRedstoneSignal(side, aStrength);
+ }
+
+ @Override
+ public boolean getRedstone() {
+ return Arrays.stream(ForgeDirection.VALID_DIRECTIONS)
+ .anyMatch(this::getRedstone);
+ }
+
+ @Override
+ public boolean getRedstone(ForgeDirection side) {
+ return getInternalInputRedstoneSignal(side) > 0;
+ }
+
+ @Override
+ public byte getStrongestRedstone() {
+ return Arrays.stream(ForgeDirection.VALID_DIRECTIONS)
+ .map(this::getInternalInputRedstoneSignal)
+ .max(Comparator.comparing(Byte::valueOf))
+ .orElse((byte) 0);
+ }
+
+ @Override
+ public byte getStrongOutputRedstoneSignal(ForgeDirection side) {
+ final int ordinalSide = side.ordinal();
+ return side != ForgeDirection.UNKNOWN && (mStrongRedstone & (1 << ordinalSide)) != 0
+ ? (byte) (mSidedRedstone[ordinalSide] & 15)
+ : 0;
+ }
+
+ @Override
+ public void setGenericRedstoneOutput(boolean aOnOff) {
+ mRedstone = aOnOff;
+ }
+
+ @Override
+ public byte getInternalInputRedstoneSignal(ForgeDirection side) {
+ return (byte) (getCoverBehaviorAtSideNew(side).getRedstoneInput(
+ side,
+ getInputRedstoneSignal(side),
+ getCoverIDAtSide(side),
+ getComplexCoverDataAtSide(side),
+ this) & 15);
+ }
+
+ @Override
+ public byte getInputRedstoneSignal(ForgeDirection side) {
+ return (byte) (worldObj
+ .getIndirectPowerLevelTo(getOffsetX(side, 1), getOffsetY(side, 1), getOffsetZ(side, 1), side.ordinal())
+ & 15);
+ }
+
+ @Override
+ public byte getOutputRedstoneSignal(ForgeDirection side) {
+ return getCoverBehaviorAtSideNew(side)
+ .manipulatesSidedRedstoneOutput(side, getCoverIDAtSide(side), getComplexCoverDataAtSide(side), this)
+ ? mSidedRedstone[side.ordinal()]
+ : getGeneralRS(side);
+ }
+
+ protected void updateOutputRedstoneSignal(ForgeDirection side) {
+ setOutputRedstoneSignal(side, (byte) 0);
+ }
+
+ @Override
+ public void receiveCoverData(ForgeDirection coverSide, int aCoverID, int aCoverData) {
+ if (coverSide == ForgeDirection.UNKNOWN) return;
+ final CoverInfo oldCoverInfo = getCoverInfoAtSide(coverSide);
+ if (!oldCoverInfo.isValid()) return;
+
+ setCoverIDAtSideNoUpdate(coverSide, aCoverID);
+ setCoverDataAtSide(coverSide, aCoverData);
+ }
+
+ @Override
+ public void receiveCoverData(ForgeDirection coverSide, int aCoverID, ISerializableObject aCoverData,
+ EntityPlayerMP aPlayer) {
+ if (coverSide == ForgeDirection.UNKNOWN) return;
+
+ final CoverInfo oldCoverInfo = getCoverInfoAtSide(coverSide);
+
+ if (!oldCoverInfo.isValid()) return;
+ oldCoverInfo.preDataChanged(aCoverID, aCoverData);
+ setCoverIDAtSideNoUpdate(coverSide, aCoverID, aCoverData);
+ setCoverDataAtSide(coverSide, aCoverData);
+
+ if (isClientSide()) {
+ getCoverInfoAtSide(coverSide).onDataChanged();
+ }
+ }
+
+ protected void sendCoverDataIfNeeded() {
+ if (worldObj == null || worldObj.isRemote) return;
+ for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
+ final CoverInfo coverInfo = getCoverInfoAtSide(side);
+ if (coverInfo.needsUpdate()) {
+ NW.sendPacketToAllPlayersInRange(
+ worldObj,
+ new GT_Packet_SendCoverData(coverInfo, this),
+ xCoord,
+ zCoord);
+ coverInfo.setNeedsUpdate(false);
+ }
+ }
+ }
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currentTip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ final NBTTagCompound tag = accessor.getNBTData();
+ final ForgeDirection currentFacing = accessor.getSide();
+
+ final NBTTagList tList = tag.getTagList(GT_Values.NBT.COVERS, 10);
+ for (byte i = 0; i < tList.tagCount(); i++) {
+ final NBTTagCompound tNBT = tList.getCompoundTagAt(i);
+ final CoverInfo coverInfo = new CoverInfo(this, tNBT);
+ if (!coverInfo.isValid() || coverInfo.getCoverBehavior() == GregTech_API.sNoBehavior) continue;
+
+ final ItemStack coverStack = coverInfo.getDisplayStack();
+ if (coverStack != null) {
+ currentTip.add(
+ StatCollector.translateToLocalFormatted(
+ "GT5U.waila.cover",
+ currentFacing == coverInfo.getSide()
+ ? StatCollector.translateToLocal("GT5U.waila.cover.current_facing")
+ : StatCollector.translateToLocal(
+ "GT5U.interface.coverTabs." + coverInfo.getSide()
+ .toString()
+ .toLowerCase()),
+ coverStack.getDisplayName()));
+ final String behaviorDesc = coverInfo.getBehaviorDescription();
+ if (!Objects.equals(behaviorDesc, E)) currentTip.add(behaviorDesc);
+ }
+ }
+
+ // No super implementation
+ // super.getWailaBody(itemStack, currenttip, accessor, config);
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ // No super implementation
+ // super.getWailaNBTData(player, tile, tag, world, x, y, z);
+
+ // While we have some cover data on the client (enough to render it); we don't have all the information we want,
+ // such as details on the fluid filter, so send it all here.
+ writeCoverNBT(tag, false);
+ }
+
+ /**
+ * Add installed cover information, generally called from ItemBlock
+ *
+ * @param aNBT - NBTTagCompound from the stack
+ * @param aList - List to add the information to
+ */
+ public static void addInstalledCoversInformation(NBTTagCompound aNBT, List<String> aList) {
+ if (aNBT == null || aList == null) return;
+ final NBTTagList tList = aNBT.getTagList(GT_Values.NBT.COVERS, 10);
+ for (byte i = 0; i < tList.tagCount(); i++) {
+ final NBTTagCompound tNBT = tList.getCompoundTagAt(i);
+ final CoverInfo coverInfo = new CoverInfo(null, tNBT);
+ if (!coverInfo.isValid() || coverInfo.getCoverBehavior() == GregTech_API.sNoBehavior) continue;
+
+ final ItemStack coverStack = coverInfo.getDisplayStack();
+ if (coverStack != null) {
+ aList.add(
+ String.format(
+ "Cover on %s side: %s",
+ getTranslation(
+ FACES[coverInfo.getSide()
+ .ordinal()]),
+ coverStack.getDisplayName()));
+ }
+ }
+
+ if (aNBT.hasKey("mCoverSides")) {
+ final int[] mCoverSides = aNBT.getIntArray("mCoverSides");
+ if (mCoverSides != null && mCoverSides.length == 6) {
+ for (final ForgeDirection tSide : ForgeDirection.VALID_DIRECTIONS) {
+ final int i = tSide.ordinal();
+ final int coverId = mCoverSides[i];
+ if (coverId == 0) continue;
+ final GT_CoverBehaviorBase<?> behavior = GregTech_API.getCoverBehaviorNew(coverId);
+ if (behavior == null || behavior == GregTech_API.sNoBehavior) continue;
+ if (!aNBT.hasKey(CoverableTileEntity.COVER_DATA_NBT_KEYS[i])) continue;
+ final ISerializableObject dataObject = behavior
+ .createDataObject(aNBT.getTag(CoverableTileEntity.COVER_DATA_NBT_KEYS[i]));
+ final ItemStack coverStack = behavior.getDisplayStack(coverId, dataObject);
+ if (coverStack != null) {
+ aList.add(
+ String
+ .format("Cover on %s side: %s", getTranslation(FACES[i]), coverStack.getDisplayName()));
+ }
+ }
+ }
+ }
+ }
+
+ protected ModularWindow createCoverWindow(EntityPlayer player, ForgeDirection side) {
+ return getCoverInfoAtSide(side).createWindow(player);
+ }
+
+ protected static final int COVER_WINDOW_ID_START = 1;
+
+ @Override
+ public void addCoverTabs(ModularWindow.Builder builder, UIBuildContext buildContext) {
+ final int COVER_TAB_LEFT = -16, COVER_TAB_TOP = 1, COVER_TAB_HEIGHT = 20, COVER_TAB_WIDTH = 18,
+ COVER_TAB_SPACING = 2, ICON_SIZE = 16;
+ final boolean flipHorizontally = GT_Mod.gregtechproxy.mCoverTabsFlipped;
+
+ final Column columnWidget = new Column();
+ builder.widget(columnWidget);
+ final int xPos = flipHorizontally ? (getGUIWidth() - COVER_TAB_LEFT - COVER_TAB_WIDTH) : COVER_TAB_LEFT;
+ if (GT_Mod.gregtechproxy.mCoverTabsVisible) {
+ columnWidget.setPos(xPos, COVER_TAB_TOP)
+ .setEnabled(
+ widget -> ((Column) widget).getChildren()
+ .stream()
+ .anyMatch(Widget::isEnabled));
+ } else {
+ columnWidget.setEnabled(false);
+ }
+ columnWidget.setAlignment(MainAxisAlignment.SPACE_BETWEEN)
+ .setSpace(COVER_TAB_SPACING);
+
+ for (ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS) {
+ buildContext.addSyncedWindow(
+ direction.ordinal() + COVER_WINDOW_ID_START,
+ player -> createCoverWindow(player, direction));
+ columnWidget.addChild(new MultiChildWidget().addChild(new ButtonWidget() {
+
+ @Override
+ public IDrawable[] getBackground() {
+ final List<IDrawable> backgrounds = new ArrayList<>();
+ final GUITextureSet tabIconSet = getGUITextureSet();
+
+ if (getCoverBehaviorAtSideNew(direction).hasCoverGUI()) {
+ if (isHovering()) {
+ backgrounds.add(
+ flipHorizontally ? tabIconSet.getCoverTabHighlightFlipped()
+ : tabIconSet.getCoverTabHighlight());
+ } else {
+ backgrounds.add(
+ flipHorizontally ? tabIconSet.getCoverTabNormalFlipped()
+ : tabIconSet.getCoverTabNormal());
+ }
+ } else {
+ backgrounds.add(
+ flipHorizontally ? tabIconSet.getCoverTabDisabledFlipped()
+ : tabIconSet.getCoverTabDisabled());
+ }
+ return backgrounds.toArray(new IDrawable[] {});
+ }
+ }.setOnClick((clickData, widget) -> onTabClicked(clickData, widget, direction))
+ .dynamicTooltip(() -> getCoverTabTooltip(direction, clientCoverData.get(direction)))
+ .setSize(COVER_TAB_WIDTH, COVER_TAB_HEIGHT))
+ .addChild(
+ new ItemDrawable(() -> getCoverItemAtSide(direction)).asWidget()
+ .setPos(
+ (COVER_TAB_WIDTH - ICON_SIZE) / 2 + (flipHorizontally ? -1 : 1),
+ (COVER_TAB_HEIGHT - ICON_SIZE) / 2))
+ .setEnabled(widget -> getCoverItemAtSide(direction) != null));
+ }
+
+ builder.widget(
+ new FakeSyncWidget<>(
+ this::collectCoverData,
+ data -> clientCoverData = data,
+ this::writeClientCoverData,
+ this::readClientCoverData));
+ }
+
+ @SideOnly(Side.CLIENT)
+ protected List<String> getCoverTabTooltip(ForgeDirection side, ISerializableObject coverData) {
+ final String[] SIDE_TOOLTIPS = new String[] { "GT5U.interface.coverTabs.down", "GT5U.interface.coverTabs.up",
+ "GT5U.interface.coverTabs.north", "GT5U.interface.coverTabs.south", "GT5U.interface.coverTabs.west",
+ "GT5U.interface.coverTabs.east" };
+ final CoverInfo coverInfo = getCoverInfoAtSide(side);
+ final ItemStack coverItem = coverInfo.getDisplayStack();
+ if (coverItem == null) return Collections.emptyList();
+ final boolean coverHasGUI = coverInfo.hasCoverGUI();
+
+ final List<String> tooltip = coverItem.getTooltip(Minecraft.getMinecraft().thePlayer, true);
+ final ImmutableList.Builder<String> builder = ImmutableList.builder();
+ builder.add(
+ (coverHasGUI ? EnumChatFormatting.UNDERLINE : EnumChatFormatting.DARK_GRAY)
+ + StatCollector.translateToLocal(SIDE_TOOLTIPS[side.ordinal()])
+ + (coverHasGUI ? EnumChatFormatting.RESET + ": " : ": " + EnumChatFormatting.RESET)
+ + tooltip.get(0));
+ builder.addAll(coverInfo.getAdditionalTooltip(coverData));
+ builder.addAll(
+ IntStream.range(1, tooltip.size())
+ .mapToObj(index -> EnumChatFormatting.GRAY + tooltip.get(index))
+ .iterator());
+ return builder.build();
+ }
+
+ protected void onTabClicked(Widget.ClickData ignoredClickData, Widget widget, ForgeDirection side) {
+ if (isClientSide()) return;
+ final CoverInfo coverInfo = getCoverInfoAtSide(side);
+ if (coverInfo.useModularUI()) {
+ widget.getContext()
+ .openSyncedWindow(side.ordinal() + COVER_WINDOW_ID_START);
+ } else {
+ final GT_Packet_TileEntityCoverGUI packet = new GT_Packet_TileEntityCoverGUI(
+ coverInfo,
+ getWorld().provider.dimensionId,
+ widget.getContext()
+ .getPlayer()
+ .getEntityId(),
+ 0);
+ GT_Values.NW.sendToPlayer(
+ packet,
+ (EntityPlayerMP) widget.getContext()
+ .getPlayer());
+ }
+ }
+
+ @NotNull
+ private Map<ForgeDirection, ISerializableObject> collectCoverData() {
+ final ImmutableMap.Builder<ForgeDirection, ISerializableObject> builder = ImmutableMap.builder();
+ for (final ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS) {
+ final CoverInfo coverInfo = getCoverInfoAtSide(direction);
+ if (coverInfo.isValid()) {
+ builder.put(direction, coverInfo.getCoverData());
+ }
+ }
+
+ return builder.build();
+ }
+
+ private void writeClientCoverData(@NotNull PacketBuffer buffer,
+ @NotNull Map<ForgeDirection, ISerializableObject> dataMap) {
+ buffer.writeInt(dataMap.size());
+ dataMap.forEach((direction, serializableObject) -> {
+ final ByteBuf individualBuffer = Unpooled.buffer();
+ serializableObject.writeToByteBuf(individualBuffer);
+
+ buffer.writeByte(direction.ordinal());
+ buffer.writeInt(individualBuffer.array().length);
+ buffer.writeBytes(individualBuffer.array());
+ });
+ }
+
+ @NotNull
+ private Map<ForgeDirection, ISerializableObject> readClientCoverData(@NotNull PacketBuffer buffer) {
+ ImmutableMap.Builder<ForgeDirection, ISerializableObject> builder = ImmutableMap.builder();
+ final int size = buffer.readInt();
+ for (int i = 0; i < size; i++) {
+ final ForgeDirection direction = ForgeDirection.getOrientation(buffer.readByte());
+ final int length = buffer.readInt();
+ final byte[] object = buffer.readBytes(length)
+ .array();
+
+ // noinspection UnstableApiUsage
+ builder.put(
+ direction,
+ getCoverInfoAtSide(direction).getCoverBehavior()
+ .createDataObject()
+ .readFromPacket(ByteStreams.newDataInput(object), null));
+ }
+
+ return builder.build();
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/GregTechTileClientEvents.java b/src/main/java/gregtech/api/metatileentity/GregTechTileClientEvents.java
new file mode 100644
index 0000000000..2f560c5f15
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/GregTechTileClientEvents.java
@@ -0,0 +1,13 @@
+package gregtech.api.metatileentity;
+
+public final class GregTechTileClientEvents {
+
+ public static final byte CHANGE_COMMON_DATA = 0;
+ public static final byte CHANGE_CUSTOM_DATA = 1;
+ public static final byte CHANGE_COLOR = 2;
+ public static final byte CHANGE_REDSTONE_OUTPUT = 3;
+ public static final byte DO_SOUND = 4;
+ public static final byte START_SOUND_LOOP = 5;
+ public static final byte STOP_SOUND_LOOP = 6;
+ public static final byte CHANGE_LIGHT = 7;
+}
diff --git a/src/main/java/gregtech/api/metatileentity/MetaPipeEntity.java b/src/main/java/gregtech/api/metatileentity/MetaPipeEntity.java
new file mode 100644
index 0000000000..a7e05355a4
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/MetaPipeEntity.java
@@ -0,0 +1,1029 @@
+package gregtech.api.metatileentity;
+
+import static gregtech.api.enums.GT_Values.GT;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Objects;
+
+import net.minecraft.block.Block;
+import net.minecraft.client.renderer.RenderBlocks;
+import net.minecraft.client.renderer.texture.IIconRegister;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.InventoryPlayer;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.AxisAlignedBB;
+import net.minecraft.world.IBlockAccess;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.Fluid;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.fluids.FluidTankInfo;
+
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import gnu.trove.list.TIntList;
+import gnu.trove.list.array.TIntArrayList;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.Dyes;
+import gregtech.api.enums.GT_Values;
+import gregtech.api.enums.Textures;
+import gregtech.api.interfaces.ITexture;
+import gregtech.api.interfaces.metatileentity.IConnectable;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IColoredTileEntity;
+import gregtech.api.interfaces.tileentity.ICoverable;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.objects.GT_ItemStack;
+import gregtech.api.util.GT_Config;
+import gregtech.api.util.GT_CoverBehavior;
+import gregtech.api.util.GT_CoverBehaviorBase;
+import gregtech.api.util.GT_LanguageManager;
+import gregtech.api.util.GT_Util;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.ISerializableObject;
+import gregtech.api.util.WorldSpawnedEventBuilder;
+import gregtech.common.GT_Client;
+import gregtech.common.covers.CoverInfo;
+
+/**
+ * NEVER INCLUDE THIS FILE IN YOUR MOD!!!
+ * <p/>
+ * Extend this Class to add a new MetaPipe Call the Constructor with the desired ID at the load-phase (not preload and
+ * also not postload!) Implement the newMetaEntity-Method to return a new ready instance of your MetaTileEntity
+ * <p/>
+ * Call the Constructor like the following example inside the Load Phase, to register it. "new
+ * GT_MetaTileEntity_E_Furnace(54, "GT_E_Furnace", "Automatic E-Furnace");"
+ */
+public abstract class MetaPipeEntity implements IMetaTileEntity, IConnectable {
+
+ /**
+ * The Inventory of the MetaTileEntity. Amount of Slots can be larger than 256. HAYO!
+ */
+ public final ItemStack[] mInventory;
+ /**
+ * This variable tells, which directions the Block is connected to. It is a Bitmask.
+ */
+ public byte mConnections = 0;
+
+ protected boolean mCheckConnections = false;
+ /**
+ * Only assigned for the MetaTileEntity in the List! Also only used to get the localized Name for the ItemStack and
+ * for getInvName.
+ */
+ public String mName;
+
+ public boolean doTickProfilingInThisTick = true;
+ /**
+ * accessibility to this Field is no longer given, see below
+ */
+ private IGregTechTileEntity mBaseMetaTileEntity;
+
+ /**
+ * This registers your Machine at the List. Use only ID's larger than 2048 - the ones lower are reserved by GT.
+ * See also the list in the API package - it has a description that contains all the reservations.
+ * <p>
+ * The constructor can be overloaded as follows:
+ * <blockquote>
+ *
+ * <pre>
+ *
+ * public GT_MetaTileEntity_EBench(int id, String name, String nameRegional) {
+ * super(id, name, nameRegional);
+ * }
+ * </pre>
+ *
+ * </blockquote>
+ *
+ * @param aID the machine ID
+ */
+ public MetaPipeEntity(int aID, String aBasicName, String aRegionalName, int aInvSlotCount) {
+ this(aID, aBasicName, aRegionalName, aInvSlotCount, true);
+ }
+
+ public MetaPipeEntity(int aID, String aBasicName, String aRegionalName, int aInvSlotCount, boolean aAddInfo) {
+ if (GregTech_API.sPostloadStarted || !GregTech_API.sPreloadStarted)
+ throw new IllegalAccessError("This Constructor has to be called in the load Phase");
+ if (GregTech_API.METATILEENTITIES[aID] == null) {
+ GregTech_API.METATILEENTITIES[aID] = this;
+ } else {
+ throw new IllegalArgumentException("MetaMachine-Slot Nr. " + aID + " is already occupied!");
+ }
+ mName = aBasicName.replaceAll(" ", "_")
+ .toLowerCase(Locale.ENGLISH);
+ setBaseMetaTileEntity(new BaseMetaPipeEntity());
+ getBaseMetaTileEntity().setMetaTileID((short) aID);
+ GT_LanguageManager.addStringLocalization("gt.blockmachines." + mName + ".name", aRegionalName);
+ mInventory = new ItemStack[aInvSlotCount];
+
+ if (aAddInfo && GT.isClientSide()) {
+ addInfo(aID);
+ }
+ }
+
+ protected final void addInfo(int aID) {
+ if (!GT.isClientSide()) return;
+
+ ItemStack tStack = new ItemStack(GregTech_API.sBlockMachines, 1, aID);
+ Objects.requireNonNull(tStack.getItem())
+ .addInformation(tStack, null, new ArrayList<>(), true);
+ }
+
+ /**
+ * This is the normal Constructor.
+ */
+ public MetaPipeEntity(String aName, int aInvSlotCount) {
+ mInventory = new ItemStack[aInvSlotCount];
+ mName = aName;
+ }
+
+ /**
+ * For Pipe Rendering
+ */
+ public abstract float getThickNess();
+
+ /**
+ * For Pipe Rendering
+ */
+ public abstract boolean renderInside(ForgeDirection side);
+
+ public boolean isDisplaySecondaryDescription() {
+ return false;
+ }
+
+ @Override
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection side, ForgeDirection facing,
+ int colorIndex, boolean active, boolean redstoneLevel) {
+ return Textures.BlockIcons.ERROR_RENDERING;
+ }
+
+ public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection, int connections,
+ int colorIndex, boolean active, boolean redstoneLevel) {
+ return Textures.BlockIcons.ERROR_RENDERING;
+ }
+
+ @Override
+ public IGregTechTileEntity getBaseMetaTileEntity() {
+ return mBaseMetaTileEntity;
+ }
+
+ @Override
+ public void setBaseMetaTileEntity(IGregTechTileEntity aBaseMetaTileEntity) {
+ if (mBaseMetaTileEntity != null && aBaseMetaTileEntity == null) {
+ mBaseMetaTileEntity.getMetaTileEntity()
+ .inValidate();
+ mBaseMetaTileEntity.setMetaTileEntity(null);
+ }
+ mBaseMetaTileEntity = aBaseMetaTileEntity;
+ if (mBaseMetaTileEntity != null) {
+ mBaseMetaTileEntity.setMetaTileEntity(this);
+ }
+ }
+
+ @Override
+ public ItemStack getStackForm(long aAmount) {
+ return new ItemStack(GregTech_API.sBlockMachines, (int) aAmount, getBaseMetaTileEntity().getMetaTileID());
+ }
+
+ public boolean isCoverOnSide(BaseMetaPipeEntity aPipe, EntityLivingBase aEntity) {
+ ForgeDirection side = ForgeDirection.UNKNOWN;
+ double difference = aEntity.posY - (double) aPipe.yCoord;
+ if (difference > 0.6 && difference < 0.99) {
+ side = ForgeDirection.UP;
+ }
+ if (difference < -1.5 && difference > -1.99) {
+ side = ForgeDirection.DOWN;
+ }
+ difference = aEntity.posZ - (double) aPipe.zCoord;
+ if (difference < -0.05 && difference > -0.4) {
+ side = ForgeDirection.NORTH;
+ }
+ if (difference > 1.05 && difference < 1.4) {
+ side = ForgeDirection.SOUTH;
+ }
+ difference = aEntity.posX - (double) aPipe.xCoord;
+ if (difference < -0.05 && difference > -0.4) {
+ side = ForgeDirection.WEST;
+ }
+ if (difference > 1.05 && difference < 1.4) {
+ side = ForgeDirection.EAST;
+ }
+ boolean tCovered = side != ForgeDirection.UNKNOWN && mBaseMetaTileEntity.getCoverIDAtSide(side) > 0;
+ if (isConnectedAtSide(side)) {
+ tCovered = true;
+ }
+ // GT_FML_LOGGER.info("Cover: "+mBaseMetaTileEntity.getCoverIDAtSide(aSide));
+ // toDo: filter cover ids that actually protect against temperature (rubber/plastic maybe?, more like asbestos)
+ return tCovered;
+ }
+
+ @Override
+ public void onServerStart() {
+ /* Do nothing */
+ }
+
+ @Override
+ public void onWorldSave(File aSaveDirectory) {
+ /* Do nothing */
+ }
+
+ @Override
+ public void onWorldLoad(File aSaveDirectory) {
+ /* Do nothing */
+ }
+
+ @Override
+ public void onConfigLoad(GT_Config aConfig) {
+ /* Do nothing */
+ }
+
+ @Override
+ public void setItemNBT(NBTTagCompound aNBT) {
+ /* Do nothing */
+ }
+
+ @Override
+ @SideOnly(Side.CLIENT)
+ public void registerIcons(IIconRegister aBlockIconRegister) {
+ /* Do nothing */
+ }
+
+ @Override
+ public boolean allowCoverOnSide(ForgeDirection side, GT_ItemStack aCoverID) {
+ return true;
+ }
+
+ @Deprecated
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+ /* Do nothing */
+ }
+
+ @Deprecated
+ public boolean onWrenchRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer entityPlayer,
+ float aX, float aY, float aZ) {
+ return false;
+ }
+
+ @Deprecated
+ public boolean onWireCutterRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer,
+ float aX, float aY, float aZ) {
+ return false;
+ }
+
+ @Deprecated
+ public boolean onSolderingToolRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer,
+ float aX, float aY, float aZ) {
+ return false;
+ }
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ,
+ ItemStack aTool) {
+ onScrewdriverRightClick(side, aPlayer, aX, aY, aZ);
+ }
+
+ @Override
+ public boolean onWrenchRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer, float aX,
+ float aY, float aZ, ItemStack aTool) {
+ return onWrenchRightClick(side, wrenchingSide, aPlayer, aX, aY, aZ);
+ }
+
+ @Override
+ public boolean onWireCutterRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer,
+ float aX, float aY, float aZ, ItemStack aTool) {
+ return onWireCutterRightClick(side, wrenchingSide, aPlayer, aX, aY, aZ);
+ }
+
+ @Override
+ public boolean onSolderingToolRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer,
+ float aX, float aY, float aZ, ItemStack aTool) {
+ return onSolderingToolRightClick(side, wrenchingSide, aPlayer, aX, aY, aZ);
+ }
+
+ @Override
+ public void onExplosion() {
+ /* Do nothing */
+ }
+
+ @Override
+ public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ /* Do nothing */
+ }
+
+ @Override
+ public void onPreTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ /* Do nothing */
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (aBaseMetaTileEntity.isClientSide() && GT_Client.changeDetected == 4) {
+ /*
+ * Client tick counter that is set to 5 on hiding pipes and covers. It triggers a texture update next client
+ * tick when reaching 4, with provision for 3 more update tasks, spreading client change detection related
+ * work and network traffic on different ticks, until it reaches 0.
+ */
+ aBaseMetaTileEntity.issueTextureUpdate();
+ }
+ }
+
+ @Override
+ public void inValidate() {
+ /* Do nothing */
+ }
+
+ @Override
+ public void onRemoval() {
+ /* Do nothing */
+ }
+
+ @Override
+ public void initDefaultModes(NBTTagCompound aNBT) {
+ /* Do nothing */
+ }
+
+ /**
+ * When a GUI is opened
+ */
+ public void onOpenGUI() {
+ /* Do nothing */
+ }
+
+ /**
+ * When a GUI is closed
+ */
+ public void onCloseGUI() {
+ /* Do nothing */
+ }
+
+ /**
+ * Called when a Player rightclicks the Machine. Sneaky rightclicks are not getting passed to this!
+ */
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer, ForgeDirection side,
+ float aX, float aY, float aZ) {
+ return false;
+ }
+
+ @Override
+ public void onLeftclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ /* Do nothing */
+ }
+
+ @Override
+ public void onValueUpdate(byte aValue) {
+ /* Do nothing */
+ }
+
+ @Override
+ public byte getUpdateData() {
+ return 0;
+ }
+
+ @Override
+ public void doSound(byte aIndex, double aX, double aY, double aZ) {
+ /* Do nothing */
+ }
+
+ @Override
+ public void startSoundLoop(byte aIndex, double aX, double aY, double aZ) {
+ /* Do nothing */
+ }
+
+ @Override
+ public void stopSoundLoop(byte aValue, double aX, double aY, double aZ) {
+ /* Do nothing */
+ }
+
+ @Override
+ public final void sendSound(byte aIndex) {
+ if (!getBaseMetaTileEntity().hasMufflerUpgrade())
+ getBaseMetaTileEntity().sendBlockEvent(GregTechTileClientEvents.DO_SOUND, aIndex);
+ }
+
+ @Override
+ public final void sendLoopStart(byte aIndex) {
+ if (!getBaseMetaTileEntity().hasMufflerUpgrade())
+ getBaseMetaTileEntity().sendBlockEvent(GregTechTileClientEvents.START_SOUND_LOOP, aIndex);
+ }
+
+ @Override
+ public final void sendLoopEnd(byte aIndex) {
+ if (!getBaseMetaTileEntity().hasMufflerUpgrade())
+ getBaseMetaTileEntity().sendBlockEvent(GregTechTileClientEvents.STOP_SOUND_LOOP, aIndex);
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return false;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return true;
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return true;
+ }
+
+ @Override
+ public boolean shouldDropItemAt(int index) {
+ return true;
+ }
+
+ @Override
+ public boolean setStackToZeroInsteadOfNull(int aIndex) {
+ return false;
+ }
+
+ @Override
+ public ArrayList<String> getSpecialDebugInfo(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer,
+ int aLogLevel, ArrayList<String> aList) {
+ return aList;
+ }
+
+ @Override
+ public boolean isLiquidInput(ForgeDirection side) {
+ return false;
+ }
+
+ @Override
+ public boolean isLiquidOutput(ForgeDirection side) {
+ return false;
+ }
+
+ /**
+ * gets the contained Liquid
+ */
+ @Override
+ public FluidStack getFluid() {
+ return null;
+ }
+
+ /**
+ * tries to fill this Tank
+ */
+ @Override
+ public int fill(FluidStack resource, boolean doFill) {
+ return 0;
+ }
+
+ /**
+ * tries to empty this Tank
+ */
+ @Override
+ public FluidStack drain(int maxDrain, boolean doDrain) {
+ return null;
+ }
+
+ /**
+ * Tank pressure
+ */
+ public int getTankPressure() {
+ return 0;
+ }
+
+ /**
+ * Liquid Capacity
+ */
+ @Override
+ public int getCapacity() {
+ return 0;
+ }
+
+ /**
+ * Progress this machine has already made
+ */
+ public int getProgresstime() {
+ return 0;
+ }
+
+ /**
+ * Progress this Machine has to do to produce something
+ */
+ public int maxProgresstime() {
+ return 0;
+ }
+
+ /**
+ * Increases the Progress, returns the overflown Progress.
+ */
+ public int increaseProgress(int aProgress) {
+ return 0;
+ }
+
+ @Override
+ public void onMachineBlockUpdate() {
+ /* Do nothing */
+ }
+
+ @Override
+ public void receiveClientEvent(byte aEventID, byte aValue) {
+ /* Do nothing */
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return false;
+ }
+
+ @Override
+ public byte getComparatorValue(ForgeDirection side) {
+ return 0;
+ }
+
+ @Override
+ public boolean acceptsRotationalEnergy(ForgeDirection side) {
+ return false;
+ }
+
+ @Override
+ public boolean injectRotationalEnergy(ForgeDirection side, long aSpeed, long aEnergy) {
+ return false;
+ }
+
+ @Override
+ public String getSpecialVoltageToolTip() {
+ return null;
+ }
+
+ @Override
+ public boolean isGivingInformation() {
+ return false;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ return new String[] {};
+ }
+
+ public boolean isDigitalChest() {
+ return false;
+ }
+
+ public ItemStack[] getStoredItemData() {
+ return null;
+ }
+
+ public void setItemCount(int aCount) {
+ /* Do nothing */
+ }
+
+ public int getMaxItemCount() {
+ return 0;
+ }
+
+ @Override
+ public int getSizeInventory() {
+ return mInventory.length;
+ }
+
+ @Override
+ public ItemStack getStackInSlot(int slotIndex) {
+ if (slotIndex >= 0 && slotIndex < mInventory.length) return mInventory[slotIndex];
+ return null;
+ }
+
+ @Override
+ public void setInventorySlotContents(int aIndex, ItemStack aStack) {
+ if (aIndex >= 0 && aIndex < mInventory.length) mInventory[aIndex] = aStack;
+ }
+
+ @Override
+ public String getInventoryName() {
+ if (GregTech_API.METATILEENTITIES[getBaseMetaTileEntity().getMetaTileID()] != null)
+ return GregTech_API.METATILEENTITIES[getBaseMetaTileEntity().getMetaTileID()].getMetaName();
+ return "";
+ }
+
+ @Override
+ public int getInventoryStackLimit() {
+ return 64;
+ }
+
+ @Override
+ public boolean isItemValidForSlot(int aIndex, ItemStack aStack) {
+ return getBaseMetaTileEntity().isValidSlot(aIndex);
+ }
+
+ @Override
+ public ItemStack decrStackSize(int aIndex, int aAmount) {
+ ItemStack tStack = getStackInSlot(aIndex), rStack = GT_Utility.copyOrNull(tStack);
+ if (tStack != null) {
+ if (tStack.stackSize <= aAmount) {
+ if (setStackToZeroInsteadOfNull(aIndex)) tStack.stackSize = 0;
+ else setInventorySlotContents(aIndex, null);
+ } else {
+ rStack = tStack.splitStack(aAmount);
+ if (tStack.stackSize == 0 && !setStackToZeroInsteadOfNull(aIndex))
+ setInventorySlotContents(aIndex, null);
+ }
+ }
+ return rStack;
+ }
+
+ @Override
+ public int[] getAccessibleSlotsFromSide(int ordinalSide) {
+ final TIntList tList = new TIntArrayList();
+ final IGregTechTileEntity tTileEntity = getBaseMetaTileEntity();
+ final CoverInfo tileCoverInfo = tTileEntity.getCoverInfoAtSide(ForgeDirection.getOrientation(ordinalSide));
+ final boolean tSkip = tileCoverInfo.letsItemsIn(-2) || tileCoverInfo.letsItemsOut(-2);
+ for (int i = 0; i < getSizeInventory(); i++) {
+ if (isValidSlot(i) && (tSkip || tileCoverInfo.letsItemsOut(i) || tileCoverInfo.letsItemsIn(i))) {
+ tList.add(i);
+ }
+ }
+ return tList.toArray();
+ }
+
+ @Override
+ public boolean canInsertItem(int slotIndex, ItemStack itemStack, int ordinalSide) {
+ return isValidSlot(slotIndex) && itemStack != null
+ && slotIndex < mInventory.length
+ && (mInventory[slotIndex] == null || GT_Utility.areStacksEqual(itemStack, mInventory[slotIndex]))
+ && allowPutStack(getBaseMetaTileEntity(), slotIndex, ForgeDirection.getOrientation(ordinalSide), itemStack);
+ }
+
+ @Override
+ public boolean canExtractItem(int slotIndex, ItemStack itemStack, int ordinalSide) {
+ return isValidSlot(slotIndex) && itemStack != null
+ && slotIndex < mInventory.length
+ && allowPullStack(
+ getBaseMetaTileEntity(),
+ slotIndex,
+ ForgeDirection.getOrientation(ordinalSide),
+ itemStack);
+ }
+
+ @Override
+ public boolean canFill(ForgeDirection side, Fluid aFluid) {
+ return fill(side, new FluidStack(aFluid, 1), false) == 1;
+ }
+
+ @Override
+ public boolean canDrain(ForgeDirection side, Fluid aFluid) {
+ return drain(side, new FluidStack(aFluid, 1), false) != null;
+ }
+
+ @Override
+ public FluidTankInfo[] getTankInfo(ForgeDirection side) {
+ if (getCapacity() <= 0 && !getBaseMetaTileEntity().hasSteamEngineUpgrade()) return new FluidTankInfo[] {};
+ return new FluidTankInfo[] { getInfo() };
+ }
+
+ public int fill_default(ForgeDirection side, FluidStack aFluid, boolean doFill) {
+ return fill(aFluid, doFill);
+ }
+
+ @Override
+ public int fill(ForgeDirection side, FluidStack aFluid, boolean doFill) {
+ return fill_default(side, aFluid, doFill);
+ }
+
+ @Override
+ public FluidStack drain(ForgeDirection side, FluidStack aFluid, boolean doDrain) {
+ if (getFluid() != null && aFluid != null && getFluid().isFluidEqual(aFluid))
+ return drain(aFluid.amount, doDrain);
+ return null;
+ }
+
+ @Override
+ public FluidStack drain(ForgeDirection side, int maxDrain, boolean doDrain) {
+ return drain(maxDrain, doDrain);
+ }
+
+ @Override
+ public int getFluidAmount() {
+ return 0;
+ }
+
+ @Override
+ public FluidTankInfo getInfo() {
+ return new FluidTankInfo(this);
+ }
+
+ @Override
+ public String getMetaName() {
+ return mName;
+ }
+
+ @Override
+ public ItemStack getStackInSlotOnClosing(int i) {
+ return null;
+ }
+
+ @Override
+ public boolean doTickProfilingMessageDuringThisTick() {
+ return doTickProfilingInThisTick;
+ }
+
+ @Override
+ public boolean isUseableByPlayer(EntityPlayer entityplayer) {
+ return false;
+ }
+
+ @Override
+ public boolean connectsToItemPipe(ForgeDirection side) {
+ return false;
+ }
+
+ @Override
+ public void openInventory() {
+ //
+ }
+
+ @Override
+ public void closeInventory() {
+ //
+ }
+
+ @Override
+ public Object getServerGUI(int aID, InventoryPlayer aPlayerInventory, IGregTechTileEntity aBaseMetaTileEntity) {
+ return null;
+ }
+
+ @Override
+ public Object getClientGUI(int aID, InventoryPlayer aPlayerInventory, IGregTechTileEntity aBaseMetaTileEntity) {
+ return null;
+ }
+
+ @Override
+ public float getExplosionResistance(ForgeDirection side) {
+ return (mConnections & IConnectable.HAS_FOAM) != 0 ? 50.0F : 5.0F;
+ }
+
+ @Override
+ public ItemStack[] getRealInventory() {
+ return mInventory;
+ }
+
+ @Override
+ public boolean hasCustomInventoryName() {
+ return false;
+ }
+
+ @Override
+ public void markDirty() {
+ //
+ }
+
+ @Override
+ public void onColorChangeServer(byte aColor) {
+ setCheckConnections();
+ }
+
+ @Override
+ public void onColorChangeClient(byte aColor) {
+ // Do nothing apparently
+ }
+
+ public void setCheckConnections() {
+ mCheckConnections = true;
+ }
+
+ public long injectEnergyUnits(ForgeDirection side, long aVoltage, long aAmperage) {
+ return 0;
+ }
+
+ @Override
+ @SideOnly(Side.CLIENT)
+ public boolean renderInInventory(Block aBlock, int aMeta, RenderBlocks aRenderer) {
+ return false;
+ }
+
+ @Override
+ @SideOnly(Side.CLIENT)
+ public boolean renderInWorld(IBlockAccess aWorld, int aX, int aY, int aZ, Block aBlock, RenderBlocks aRenderer) {
+ return false;
+ }
+
+ @Override
+ public void doExplosion(long aExplosionPower) {
+ float tStrength = GT_Values.getExplosionPowerForVoltage(aExplosionPower);
+ int tX = getBaseMetaTileEntity().getXCoord(), tY = getBaseMetaTileEntity().getYCoord(),
+ tZ = getBaseMetaTileEntity().getZCoord();
+ World tWorld = getBaseMetaTileEntity().getWorld();
+ tWorld.setBlock(tX, tY, tZ, Blocks.air);
+ if (GregTech_API.sMachineExplosions) {
+ new WorldSpawnedEventBuilder.ExplosionEffectEventBuilder().setStrength(tStrength)
+ .setSmoking(true)
+ .setPosition(tX + 0.5, tY + 0.5, tZ + 0.5)
+ .setWorld(tWorld)
+ .run();
+ }
+ }
+
+ @Override
+ public int getLightOpacity() {
+ return 0;
+ }
+
+ @Override
+ public void addCollisionBoxesToList(World aWorld, int aX, int aY, int aZ, AxisAlignedBB inputAABB,
+ List<AxisAlignedBB> outputAABB, Entity collider) {
+ AxisAlignedBB axisalignedbb1 = getCollisionBoundingBoxFromPool(aWorld, aX, aY, aZ);
+ if (axisalignedbb1 != null && inputAABB.intersectsWith(axisalignedbb1)) outputAABB.add(axisalignedbb1);
+ }
+
+ @Override
+ public AxisAlignedBB getCollisionBoundingBoxFromPool(World aWorld, int aX, int aY, int aZ) {
+ return AxisAlignedBB.getBoundingBox(aX, aY, aZ, aX + 1, aY + 1, aZ + 1);
+ }
+
+ @Override
+ public void onEntityCollidedWithBlock(World aWorld, int aX, int aY, int aZ, Entity collider) {
+ //
+ }
+
+ @Override
+ public void onCreated(ItemStack aStack, World aWorld, EntityPlayer aPlayer) {
+ //
+ }
+
+ @Override
+ public boolean allowGeneralRedstoneOutput() {
+ return false;
+ }
+
+ @Override
+ public boolean hasAlternativeModeText() {
+ return false;
+ }
+
+ @Override
+ public String getAlternativeModeText() {
+ return "";
+ }
+
+ @Deprecated
+ public String trans(String aKey, String aEnglish) {
+ return GT_Utility.trans(aKey, aEnglish);
+ }
+
+ protected boolean connectableColor(TileEntity tTileEntity) {
+ // Determine if two entities are connectable based on their colorization:
+ // Uncolored can connect to anything
+ // If both are colored they must be the same color to connect.
+ if (tTileEntity instanceof IColoredTileEntity) {
+ if (getBaseMetaTileEntity().getColorization() >= 0) {
+ final byte tColor = ((IColoredTileEntity) tTileEntity).getColorization();
+ return tColor < 0 || tColor == getBaseMetaTileEntity().getColorization();
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public int connect(ForgeDirection side) {
+ if (side == ForgeDirection.UNKNOWN) return 0;
+
+ final ForgeDirection oppositeSide = side.getOpposite();
+ final IGregTechTileEntity baseMetaTile = getBaseMetaTileEntity();
+ if (baseMetaTile == null || !baseMetaTile.isServerSide()) return 0;
+
+ final CoverInfo coverInfo = baseMetaTile.getCoverInfoAtSide(side);
+
+ final boolean alwaysLookConnected = coverInfo.alwaysLookConnected();
+ final boolean letsIn = letsIn(coverInfo);
+ final boolean letsOut = letsOut(coverInfo);
+
+ // Careful - tTileEntity might be null, and that's ok -- so handle it
+ final TileEntity tTileEntity = baseMetaTile.getTileEntityAtSide(side);
+ if (!connectableColor(tTileEntity)) return 0;
+
+ if ((alwaysLookConnected || letsIn || letsOut)) {
+ // Are we trying to connect to a pipe? let's do it!
+ final IMetaTileEntity tPipe = tTileEntity instanceof IGregTechTileEntity
+ ? ((IGregTechTileEntity) tTileEntity).getMetaTileEntity()
+ : null;
+ if (getClass().isInstance(tPipe) || (tPipe != null && tPipe.getClass()
+ .isInstance(this))) {
+ connectAtSide(side);
+ if (!((IConnectable) tPipe).isConnectedAtSide(oppositeSide)) {
+ // Make sure pipes all get together -- connect back to us if we're connecting to a pipe
+ ((IConnectable) tPipe).connect(oppositeSide);
+ }
+ return 1;
+ } else if ((getGT6StyleConnection() && baseMetaTile.getAirAtSide(side)) || canConnect(side, tTileEntity)) {
+ // Allow open connections to Air, if the GT6 style pipe/cables are enabled, so that it'll connect to
+ // the next block placed down next to it
+ connectAtSide(side);
+ return 1;
+ }
+ if (!baseMetaTile.getWorld()
+ .getChunkProvider()
+ .chunkExists(baseMetaTile.getOffsetX(side, 1) >> 4, baseMetaTile.getOffsetZ(side, 1) >> 4)) {
+ // Target chunk unloaded
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ protected void checkConnections() {
+ // Verify connections around us. If GT6 style cables are not enabled then revert to old behavior and try
+ // connecting to everything around us
+ for (ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
+ if ((!getGT6StyleConnection() || isConnectedAtSide(side)) && connect(side) == 0) {
+ disconnect(side);
+ }
+ }
+ mCheckConnections = false;
+ }
+
+ private void connectAtSide(ForgeDirection side) {
+ mConnections |= side.flag;
+ }
+
+ @Override
+ public void disconnect(ForgeDirection side) {
+ if (side == ForgeDirection.UNKNOWN) return;
+ mConnections &= ~side.flag;
+ final ForgeDirection oppositeSide = side.getOpposite();
+ IGregTechTileEntity tTileEntity = getBaseMetaTileEntity().getIGregTechTileEntityAtSide(side);
+ IMetaTileEntity tPipe = tTileEntity == null ? null : tTileEntity.getMetaTileEntity();
+ if ((this.getClass()
+ .isInstance(tPipe)
+ || (tPipe != null && tPipe.getClass()
+ .isInstance(this)))
+ && ((IConnectable) tPipe).isConnectedAtSide(oppositeSide)) {
+ ((IConnectable) tPipe).disconnect(oppositeSide);
+ }
+ }
+
+ @Override
+ public boolean isConnectedAtSide(ForgeDirection sideDirection) {
+ return (mConnections & sideDirection.flag) != 0;
+ }
+
+ public boolean letsIn(GT_CoverBehavior coverBehavior, ForgeDirection side, int aCoverID, int aCoverVariable,
+ ICoverable aTileEntity) {
+ return false;
+ }
+
+ public boolean letsIn(CoverInfo coverInfo) {
+ return false;
+ }
+
+ public boolean letsOut(GT_CoverBehavior coverBehavior, ForgeDirection side, int aCoverID, int aCoverVariable,
+ ICoverable aTileEntity) {
+ return false;
+ }
+
+ public boolean letsOut(CoverInfo coverInfo) {
+ return false;
+ }
+
+ public boolean letsIn(GT_CoverBehaviorBase<?> coverBehavior, ForgeDirection side, int aCoverID,
+ ISerializableObject aCoverVariable, ICoverable aTileEntity) {
+ return false;
+ }
+
+ public boolean letsOut(GT_CoverBehaviorBase<?> coverBehavior, ForgeDirection side, int aCoverID,
+ ISerializableObject aCoverVariable, ICoverable aTileEntity) {
+ return false;
+ }
+
+ public boolean canConnect(ForgeDirection side, TileEntity tTileEntity) {
+ return false;
+ }
+
+ public boolean getGT6StyleConnection() {
+ return false;
+ }
+
+ @Override
+ public boolean shouldJoinIc2Enet() {
+ return false;
+ }
+
+ @Override
+ public boolean isMachineBlockUpdateRecursive() {
+ return false;
+ }
+
+ public void reloadLocks() {}
+
+ @Override
+ public int getGUIColorization() {
+ Dyes dye = Dyes.dyeWhite;
+ if (GregTech_API.sColoredGUI) {
+ if (GregTech_API.sMachineMetalGUI) {
+ dye = Dyes.MACHINE_METAL;
+ } else if (getBaseMetaTileEntity() != null) {
+ dye = Dyes.getDyeFromIndex(getBaseMetaTileEntity().getColorization());
+ }
+ }
+ return GT_Util.getRGBInt(dye.getRGBA());
+ }
+}
diff --git a/src/main/java/gregtech/api/metatileentity/MetaTileEntity.java b/src/main/java/gregtech/api/metatileentity/MetaTileEntity.java
new file mode 100644
index 0000000000..0a56f8467f
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/MetaTileEntity.java
@@ -0,0 +1,1326 @@
+package gregtech.api.metatileentity;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.function.Supplier;
+
+import net.minecraft.block.Block;
+import net.minecraft.client.renderer.RenderBlocks;
+import net.minecraft.client.renderer.texture.IIconRegister;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.entity.player.InventoryPlayer;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.AxisAlignedBB;
+import net.minecraft.world.IBlockAccess;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.Fluid;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.fluids.FluidTankInfo;
+
+import org.jetbrains.annotations.Nullable;
+
+import com.gtnewhorizons.modularui.api.forge.ItemStackHandler;
+
+import appeng.api.implementations.IPowerChannelState;
+import appeng.api.networking.energy.IEnergyGrid;
+import appeng.api.networking.pathing.IPathingGrid;
+import appeng.api.util.AECableType;
+import appeng.core.localization.WailaText;
+import appeng.me.helpers.AENetworkProxy;
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import gnu.trove.list.TIntList;
+import gnu.trove.list.array.TIntArrayList;
+import gregtech.api.GregTech_API;
+import gregtech.api.enums.Dyes;
+import gregtech.api.enums.GT_Values;
+import gregtech.api.enums.SoundResource;
+import gregtech.api.enums.SteamVariant;
+import gregtech.api.gui.GT_GUIColorOverride;
+import gregtech.api.gui.modularui.GUITextureSet;
+import gregtech.api.interfaces.ICleanroom;
+import gregtech.api.interfaces.ICleanroomReceiver;
+import gregtech.api.interfaces.IConfigurationCircuitSupport;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaPipeEntity_Cable;
+import gregtech.api.objects.GT_ItemStack;
+import gregtech.api.util.GT_Config;
+import gregtech.api.util.GT_LanguageManager;
+import gregtech.api.util.GT_Log;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_TooltipDataCache;
+import gregtech.api.util.GT_Util;
+import gregtech.api.util.GT_Utility;
+import gregtech.common.GT_Client;
+import gregtech.common.covers.CoverInfo;
+import mcp.mobius.waila.api.IWailaConfigHandler;
+import mcp.mobius.waila.api.IWailaDataAccessor;
+
+/**
+ * NEVER INCLUDE THIS FILE IN YOUR MOD!!!
+ * <p/>
+ * Extend this Class to add a new MetaMachine Call the Constructor with the desired ID at the load-phase (not preload
+ * and also not postload!) Implement the newMetaEntity-Method to return a new ready instance of your MetaTileEntity
+ * <p/>
+ * Call the Constructor like the following example inside the Load Phase, to register it. "new
+ * GT_MetaTileEntity_E_Furnace(54, "GT_E_Furnace", "Automatic E-Furnace");"
+ */
+@SuppressWarnings("unused")
+public abstract class MetaTileEntity implements IMetaTileEntity, ICleanroomReceiver {
+
+ /**
+ * Only assigned for the MetaTileEntity in the List! Also only used to get the localized Name for the ItemStack and
+ * for getInvName.
+ */
+ public final String mName;
+ /**
+ * The Inventory of the MetaTileEntity. Amount of Slots can be larger than 256. HAYO!
+ */
+ public final ItemStack[] mInventory;
+
+ /**
+ * Inventory wrapper for ModularUI
+ */
+ public final ItemStackHandler inventoryHandler;
+
+ protected GT_GUIColorOverride colorOverride;
+ protected GT_TooltipDataCache mTooltipCache = new GT_TooltipDataCache();
+
+ @Override
+ public ItemStackHandler getInventoryHandler() {
+ return inventoryHandler;
+ }
+
+ public boolean doTickProfilingInThisTick = true;
+
+ private ICleanroom cleanroom;
+
+ /**
+ * accessibility to this Field is no longer given, see below
+ */
+ private IGregTechTileEntity mBaseMetaTileEntity;
+
+ public long mSoundRequests = 0;
+
+ /**
+ * This registers your Machine at the List. Use only ID's larger than 2048 - the ones lower are reserved by GT.
+ * See also the list in the API package - it has a description that contains all the reservations.
+ * <p>
+ * The constructor can be overloaded as follows:
+ * <blockquote>
+ *
+ * <pre>
+ *
+ * public GT_MetaTileEntity_EBench(int id, String name, String nameRegional) {
+ * super(id, name, nameRegional);
+ * }
+ * </pre>
+ *
+ * </blockquote>
+ *
+ * @param aID the machine ID
+ */
+ public MetaTileEntity(int aID, String aBasicName, String aRegionalName, int aInvSlotCount) {
+ if (GregTech_API.sPostloadStarted || !GregTech_API.sPreloadStarted)
+ throw new IllegalAccessError("This Constructor has to be called in the load Phase");
+ if (GregTech_API.METATILEENTITIES[aID] == null) {
+ GregTech_API.METATILEENTITIES[aID] = this;
+ } else {
+ throw new IllegalArgumentException("MetaMachine-Slot Nr. " + aID + " is already occupied!");
+ }
+ mName = aBasicName.replace(" ", "_")
+ .toLowerCase(Locale.ENGLISH);
+ setBaseMetaTileEntity(GregTech_API.constructBaseMetaTileEntity());
+ getBaseMetaTileEntity().setMetaTileID((short) aID);
+ GT_LanguageManager.addStringLocalization("gt.blockmachines." + mName + ".name", aRegionalName);
+ mInventory = new ItemStack[aInvSlotCount];
+ inventoryHandler = new ItemStackHandler(mInventory);
+ }
+
+ /**
+ * This is the normal Constructor.
+ */
+ public MetaTileEntity(String aName, int aInvSlotCount) {
+ mInventory = new ItemStack[aInvSlotCount];
+ mName = aName;
+ inventoryHandler = new ItemStackHandler(mInventory);
+ colorOverride = GT_GUIColorOverride.get(getGUITextureSet().getMainBackground().location);
+ }
+
+ /**
+ * This method will only be called on client side
+ *
+ * @return whether the secondary description should be display. default is false
+ */
+ @Deprecated
+ public boolean isDisplaySecondaryDescription() {
+ return false;
+ }
+
+ @Override
+ public IGregTechTileEntity getBaseMetaTileEntity() {
+ return mBaseMetaTileEntity;
+ }
+
+ @Override
+ public void setBaseMetaTileEntity(IGregTechTileEntity aBaseMetaTileEntity) {
+ if (mBaseMetaTileEntity != null && aBaseMetaTileEntity == null) {
+ mBaseMetaTileEntity.getMetaTileEntity()
+ .inValidate();
+ mBaseMetaTileEntity.setMetaTileEntity(null);
+ }
+ mBaseMetaTileEntity = aBaseMetaTileEntity;
+ if (mBaseMetaTileEntity != null) {
+ mBaseMetaTileEntity.setMetaTileEntity(this);
+ }
+ }
+
+ public boolean isValid() {
+ return getBaseMetaTileEntity() != null && getBaseMetaTileEntity().getMetaTileEntity() == this
+ && !getBaseMetaTileEntity().isDead();
+ }
+
+ @Override
+ public ItemStack getStackForm(long aAmount) {
+ return new ItemStack(GregTech_API.sBlockMachines, (int) aAmount, getBaseMetaTileEntity().getMetaTileID());
+ }
+
+ @Override
+ public String getLocalName() {
+ return GT_LanguageManager.getTranslation("gt.blockmachines." + mName + ".name");
+ }
+
+ @Override
+ public void onServerStart() {
+ /* Do nothing */
+ }
+
+ @Override
+ public void onWorldSave(File aSaveDirectory) {
+ /* Do nothing */
+ }
+
+ @Override
+ public void onWorldLoad(File aSaveDirectory) {
+ /* Do nothing */
+ }
+
+ @Override
+ public void onConfigLoad(GT_Config aConfig) {
+ /* Do nothing */
+ }
+
+ @Override
+ public void setItemNBT(NBTTagCompound aNBT) {
+ /* Do nothing */
+ }
+
+ @Override
+ @SideOnly(Side.CLIENT)
+ public void registerIcons(IIconRegister aBlockIconRegister) {
+ /* Do nothing */
+ }
+
+ @Override
+ public boolean allowCoverOnSide(ForgeDirection side, GT_ItemStack aStack) {
+ return true;
+ }
+
+ @Override
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ,
+ ItemStack aTool) {
+ onScrewdriverRightClick(side, aPlayer, aX, aY, aZ);
+ }
+
+ @Override
+ public boolean onWrenchRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer entityPlayer,
+ float aX, float aY, float aZ, ItemStack aTool) {
+
+ // glue
+ if (onWrenchRightClick(side, wrenchingSide, entityPlayer, aX, aY, aZ)) {
+ return true;
+ }
+ if (getBaseMetaTileEntity().isValidFacing(wrenchingSide)) {
+ getBaseMetaTileEntity().setFrontFacing(wrenchingSide);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onWireCutterRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer,
+ float aX, float aY, float aZ, ItemStack aTool) {
+ // glue
+ if (onWireCutterRightClick(side, wrenchingSide, aPlayer, aX, aY, aZ)) {
+ return true;
+ }
+ if (!aPlayer.isSneaking()) return false;
+ final ForgeDirection oppositeSide = wrenchingSide.getOpposite();
+ final TileEntity tTileEntity = getBaseMetaTileEntity().getTileEntityAtSide(wrenchingSide);
+ if ((tTileEntity instanceof IGregTechTileEntity gtTE)
+ && (gtTE.getMetaTileEntity() instanceof GT_MetaPipeEntity_Cable)) {
+
+ // The tile entity we're facing is a cable, let's try to connect to it
+ return gtTE.getMetaTileEntity()
+ .onWireCutterRightClick(wrenchingSide, oppositeSide, aPlayer, aX, aY, aZ, aTool);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onSolderingToolRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer,
+ float aX, float aY, float aZ, ItemStack aTool) {
+
+ // glue
+ if (onSolderingToolRightClick(side, wrenchingSide, aPlayer, aX, aY, aZ)) {
+ return true;
+ }
+
+ if (!aPlayer.isSneaking()) return false;
+ final ForgeDirection oppositeSide = wrenchingSide.getOpposite();
+ TileEntity tTileEntity = getBaseMetaTileEntity().getTileEntityAtSide(wrenchingSide);
+ if ((tTileEntity instanceof IGregTechTileEntity gtTE)
+ && (gtTE.getMetaTileEntity() instanceof GT_MetaPipeEntity_Cable)) {
+
+ // The tile entity we're facing is a cable, let's try to connect to it
+ return gtTE.getMetaTileEntity()
+ .onSolderingToolRightClick(wrenchingSide, oppositeSide, aPlayer, aX, aY, aZ, aTool);
+ }
+ return false;
+ }
+
+ @Deprecated
+ public boolean onSolderingToolRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer,
+ float aX, float aY, float aZ) {
+ return false;
+ }
+
+ @Deprecated
+ public boolean onWireCutterRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer,
+ float aX, float aY, float aZ) {
+ return false;
+ }
+
+ @Deprecated
+ public boolean onWrenchRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer, float aX,
+ float aY, float aZ) {
+ return false;
+ }
+
+ @Deprecated
+ public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) {
+
+ }
+
+ @Override
+ public void onExplosion() {
+ GT_Log.exp.println(
+ "Machine at " + this.getBaseMetaTileEntity()
+ .getXCoord()
+ + " | "
+ + this.getBaseMetaTileEntity()
+ .getYCoord()
+ + " | "
+ + this.getBaseMetaTileEntity()
+ .getZCoord()
+ + " DIMID: "
+ + this.getBaseMetaTileEntity()
+ .getWorld().provider.dimensionId
+ + " exploded.");
+ }
+
+ @Override
+ public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ /* Do nothing */
+ }
+
+ @Override
+ public void onPreTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ /* Do nothing */
+ }
+
+ @Override
+ public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (aBaseMetaTileEntity.isClientSide() && GT_Client.changeDetected == 4) {
+ /*
+ * Client tick counter that is set to 5 on hiding pipes and covers. It triggers a texture update next client
+ * tick when reaching 4, with provision for 3 more update tasks, spreading client change detection related
+ * work and network traffic on different ticks, until it reaches 0.
+ */
+ aBaseMetaTileEntity.issueTextureUpdate();
+ }
+ }
+
+ public void onTickFail(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {}
+
+ public void onSetActive(boolean active) {}
+
+ public void onDisableWorking() {}
+
+ @Override
+ public void inValidate() {
+ /* Do nothing */
+ }
+
+ @Override
+ public void onRemoval() {
+ /* Do nothing */
+ }
+
+ @Override
+ public void initDefaultModes(NBTTagCompound aNBT) {
+ /* Do nothing */
+ }
+
+ /**
+ * When a GUI is opened
+ */
+ public void onOpenGUI() {
+ /* Do nothing */
+ }
+
+ /**
+ * When a GUI is closed
+ */
+ public void onCloseGUI() {
+ /* Do nothing */
+ }
+
+ /**
+ * a Player rightclicks the Machine Sneaky rightclicks are not getting passed to this!
+ */
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ return false;
+ }
+
+ @Override
+ public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer, ForgeDirection side,
+ float aX, float aY, float aZ) {
+ return onRightclick(aBaseMetaTileEntity, aPlayer);
+ }
+
+ @Override
+ public void onLeftclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
+ /* Do nothing */
+ }
+
+ @Override
+ public void onValueUpdate(byte aValue) {
+ /* Do nothing */
+ }
+
+ @Override
+ public byte getUpdateData() {
+ return 0;
+ }
+
+ @Override
+ public void doSound(byte aIndex, double aX, double aY, double aZ) {
+ /* Do nothing */
+ }
+
+ @Override
+ public void startSoundLoop(byte aIndex, double aX, double aY, double aZ) {
+ /* Do nothing */
+ }
+
+ @Override
+ public void stopSoundLoop(byte aValue, double aX, double aY, double aZ) {
+ /* Do nothing */
+ }
+
+ @Nullable
+ @Override
+ public ICleanroom getCleanroom() {
+ return cleanroom;
+ }
+
+ @Override
+ public void setCleanroom(ICleanroom cleanroom) {
+ this.cleanroom = cleanroom;
+ }
+
+ @Override
+ public final void sendSound(byte aIndex) {
+ if (!getBaseMetaTileEntity().hasMufflerUpgrade())
+ getBaseMetaTileEntity().sendBlockEvent(GregTechTileClientEvents.DO_SOUND, aIndex);
+ }
+
+ @Override
+ public final void sendLoopStart(byte aIndex) {
+ if (!getBaseMetaTileEntity().hasMufflerUpgrade())
+ getBaseMetaTileEntity().sendBlockEvent(GregTechTileClientEvents.START_SOUND_LOOP, aIndex);
+ mSoundRequests++;
+ }
+
+ @Override
+ public final void sendLoopEnd(byte aIndex) {
+ if (!getBaseMetaTileEntity().hasMufflerUpgrade())
+ getBaseMetaTileEntity().sendBlockEvent(GregTechTileClientEvents.STOP_SOUND_LOOP, aIndex);
+ }
+
+ /**
+ * @return true if this Device emits Energy at all
+ */
+ public boolean isElectric() {
+ return true;
+ }
+
+ /**
+ * @return true if this Device emits Energy at all
+ */
+ public boolean isPneumatic() {
+ return false;
+ }
+
+ /**
+ * @return true if this Device emits Energy at all
+ */
+ public boolean isSteampowered() {
+ return false;
+ }
+
+ /**
+ * @return what type of texture does this machine use for GUI, i.e. Bronze, Steel, or Primitive
+ */
+ public SteamVariant getSteamVariant() {
+ return SteamVariant.NONE;
+ }
+
+ /**
+ * @return true if this Device emits Energy at all
+ */
+ public boolean isEnetOutput() {
+ return false;
+ }
+
+ /**
+ * @return true if this Device consumes Energy at all
+ */
+ public boolean isEnetInput() {
+ return false;
+ }
+
+ /**
+ * @return the amount of EU, which can be stored in this Device. Default is 0 EU.
+ */
+ public long maxEUStore() {
+ return 0;
+ }
+
+ /**
+ * @return the amount of EU/t, which can be accepted by this Device before it explodes.
+ */
+ public long maxEUInput() {
+ return 0;
+ }
+
+ /**
+ * @return the amount of EU/t, which can be outputted by this Device.
+ */
+ public long maxEUOutput() {
+ return 0;
+ }
+
+ /**
+ * @return the amount of E-net Impulses of the maxEUOutput size, which can be outputted by this Device. Default is 1
+ * Pulse, this shouldn't be set to smaller Values than 1, as it won't output anything in that Case!
+ */
+ public long maxAmperesOut() {
+ return 1;
+ }
+
+ /**
+ * How many Amperes this Block can suck at max. Surpassing this value won't blow it up.
+ */
+ public long maxAmperesIn() {
+ return 1;
+ }
+
+ /**
+ * @return true if that Side is an Output.
+ */
+ public boolean isOutputFacing(ForgeDirection side) {
+ return false;
+ }
+
+ /**
+ * @return true if that Side is an Input.
+ */
+ public boolean isInputFacing(ForgeDirection side) {
+ return false;
+ }
+
+ /**
+ * @return true if Transformer Upgrades increase Packet Amount.
+ */
+ public boolean isTransformingLowEnergy() {
+ return true;
+ }
+
+ @Override
+ public boolean isFacingValid(ForgeDirection facing) {
+ return false;
+ }
+
+ @Override
+ public boolean isAccessAllowed(EntityPlayer aPlayer) {
+ return false;
+ }
+
+ @Override
+ public boolean isValidSlot(int aIndex) {
+ return true;
+ }
+
+ @Override
+ public boolean shouldDropItemAt(int index) {
+ return true;
+ }
+
+ @Override
+ public boolean setStackToZeroInsteadOfNull(int aIndex) {
+ return false;
+ }
+
+ /**
+ * This is used to get the internal Energy. I use this for the IDSU.
+ */
+ public long getEUVar() {
+ return ((BaseMetaTileEntity) mBaseMetaTileEntity).mStoredEnergy;
+ }
+
+ /**
+ * This is used to set the internal Energy to the given Parameter. I use this for the IDSU.
+ */
+ public void setEUVar(long aEnergy) {
+ if (aEnergy != ((BaseMetaTileEntity) mBaseMetaTileEntity).mStoredEnergy) {
+ markDirty();
+ ((BaseMetaTileEntity) mBaseMetaTileEntity).mStoredEnergy = aEnergy;
+ }
+ }
+
+ /**
+ * This is used to get the internal Steam Energy.
+ */
+ public long getSteamVar() {
+ return ((BaseMetaTileEntity) mBaseMetaTileEntity).mStoredSteam;
+ }
+
+ /**
+ * This is used to set the internal Steam Energy to the given Parameter.
+ */
+ public void setSteamVar(long aSteam) {
+ if (((BaseMetaTileEntity) mBaseMetaTileEntity).mStoredSteam != aSteam) {
+ markDirty();
+ ((BaseMetaTileEntity) mBaseMetaTileEntity).mStoredSteam = aSteam;
+ }
+ }
+
+ /**
+ * @return the amount of Steam, which can be stored in this Device. Default is 0 EU.
+ */
+ public long maxSteamStore() {
+ return 0;
+ }
+
+ /**
+ * @return the amount of EU, which this Device stores before starting to emit Energy. useful if you don't want to
+ * emit stored Energy until a certain Level is reached.
+ */
+ public long getMinimumStoredEU() {
+ return 512;
+ }
+
+ /**
+ * Determines the Tier of the Machine, used for de-charging Tools.
+ */
+ public long getInputTier() {
+ return GT_Utility.getTier(getBaseMetaTileEntity().getInputVoltage());
+ }
+
+ /**
+ * Determines the Tier of the Machine, used for charging Tools.
+ */
+ public long getOutputTier() {
+ return GT_Utility.getTier(getBaseMetaTileEntity().getOutputVoltage());
+ }
+
+ /**
+ * gets the first RechargerSlot
+ */
+ public int rechargerSlotStartIndex() {
+ return 0;
+ }
+
+ /**
+ * gets the amount of RechargerSlots
+ */
+ public int rechargerSlotCount() {
+ return 0;
+ }
+
+ /**
+ * gets the first DechargerSlot
+ */
+ public int dechargerSlotStartIndex() {
+ return 0;
+ }
+
+ /**
+ * gets the amount of DechargerSlots
+ */
+ public int dechargerSlotCount() {
+ return 0;
+ }
+
+ /**
+ * gets if this is protected from other Players per default or not
+ */
+ public boolean ownerControl() {
+ return false;
+ }
+
+ @Override
+ public ArrayList<String> getSpecialDebugInfo(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer,
+ int aLogLevel, ArrayList<String> aList) {
+ return aList;
+ }
+
+ @Override
+ public boolean isLiquidInput(ForgeDirection side) {
+ return true;
+ }
+
+ @Override
+ public boolean isLiquidOutput(ForgeDirection side) {
+ return true;
+ }
+
+ /**
+ * gets the contained Liquid
+ */
+ @Override
+ public FluidStack getFluid() {
+ return null;
+ }
+
+ /**
+ * tries to fill this Tank
+ */
+ @Override
+ public int fill(FluidStack resource, boolean doFill) {
+ return 0;
+ }
+
+ /**
+ * tries to empty this Tank
+ */
+ @Override
+ public FluidStack drain(int maxDrain, boolean doDrain) {
+ return null;
+ }
+
+ /**
+ * Tank pressure
+ */
+ public int getTankPressure() {
+ return 0;
+ }
+
+ /**
+ * Liquid Capacity
+ */
+ @Override
+ public int getCapacity() {
+ return 0;
+ }
+
+ /**
+ * Actual fluid capacity. If your machine has void-overflow feature, you'll want to override this method to make
+ * sure correct capacity is shown on GUI.
+ */
+ public int getRealCapacity() {
+ return getCapacity();
+ }
+
+ @Override
+ public void onMachineBlockUpdate() {
+ /* Do nothing */
+ }
+
+ @Override
+ public void receiveClientEvent(byte aEventID, byte aValue) {
+ /* Do nothing */
+ }
+
+ @Override
+ public boolean isSimpleMachine() {
+ return false;
+ }
+
+ /**
+ * If this accepts up to 4 Overclockers
+ */
+ public boolean isOverclockerUpgradable() {
+ return false;
+ }
+
+ /**
+ * If this accepts Transformer Upgrades
+ */
+ public boolean isTransformerUpgradable() {
+ return false;
+ }
+
+ /**
+ * Progress this machine has already made
+ */
+ public int getProgresstime() {
+ return 0;
+ }
+
+ /**
+ * Progress this Machine has to do to produce something
+ */
+ public int maxProgresstime() {
+ return 0;
+ }
+
+ /**
+ * Increases the Progress, returns the overflown Progress.
+ */
+ public int increaseProgress(int aProgress) {
+ return 0;
+ }
+
+ /**
+ * If this TileEntity makes use of Sided Redstone behaviors. Determines only, if the Output Redstone Array is
+ * getting filled with 0 for true, or 15 for false.
+ */
+ public boolean hasSidedRedstoneOutputBehavior() {
+ return false;
+ }
+
+ /**
+ * When the Facing gets changed.
+ */
+ public void onFacingChange() {
+ /* Do nothing */
+ }
+
+ /**
+ * if the IC2 Teleporter can drain from this.
+ */
+ public boolean isTeleporterCompatible() {
+ return isEnetOutput() && getBaseMetaTileEntity().getOutputVoltage() >= 128
+ && getBaseMetaTileEntity().getUniversalEnergyCapacity() >= 500000;
+ }
+
+ /**
+ * Flag if this MTE will exploding when its raining
+ *
+ * @return True if this will explode in rain, else false
+ */
+ public boolean willExplodeInRain() {
+ return true;
+ }
+
+ /**
+ * Gets the Output for the comparator on the given Side
+ */
+ @Override
+ public byte getComparatorValue(ForgeDirection side) {
+ return 0;
+ }
+
+ @Override
+ public boolean acceptsRotationalEnergy(ForgeDirection side) {
+ return false;
+ }
+
+ @Override
+ public boolean injectRotationalEnergy(ForgeDirection side, long aSpeed, long aEnergy) {
+ return false;
+ }
+
+ @Override
+ public String getSpecialVoltageToolTip() {
+ return null;
+ }
+
+ @Override
+ public boolean isGivingInformation() {
+ return false;
+ }
+
+ @Override
+ public String[] getInfoData() {
+ return new String[] {};
+ }
+
+ public boolean isDigitalChest() {
+ return false;
+ }
+
+ public ItemStack[] getStoredItemData() {
+ return null;
+ }
+
+ public void setItemCount(int aCount) {
+ /* Do nothing */
+ }
+
+ public int getMaxItemCount() {
+ return 0;
+ }
+
+ @Override
+ public int getSizeInventory() {
+ return mInventory.length;
+ }
+
+ @Override
+ public ItemStack getStackInSlot(int aIndex) {
+ if (aIndex >= 0 && aIndex < mInventory.length) return mInventory[aIndex];
+ return null;
+ }
+
+ @Override
+ public void setInventorySlotContents(int aIndex, ItemStack aStack) {
+ markDirty();
+ if (this instanceof IConfigurationCircuitSupport ccs) {
+ if (ccs.allowSelectCircuit() && aIndex == ccs.getCircuitSlot() && aStack != null) {
+ mInventory[aIndex] = GT_Utility.copyAmount(0, aStack);
+ return;
+ }
+ }
+ if (aIndex >= 0 && aIndex < mInventory.length) mInventory[aIndex] = aStack;
+ }
+
+ @Override
+ public String getInventoryName() {
+ if (GregTech_API.METATILEENTITIES[getBaseMetaTileEntity().getMetaTileID()] != null)
+ return GregTech_API.METATILEENTITIES[getBaseMetaTileEntity().getMetaTileID()].getMetaName();
+ return "";
+ }
+
+ @Override
+ public int getInventoryStackLimit() {
+ return 64;
+ }
+
+ @Override
+ public boolean isItemValidForSlot(int aIndex, ItemStack aStack) {
+ return getBaseMetaTileEntity().isValidSlot(aIndex);
+ }
+
+ @Override
+ public ItemStack decrStackSize(int aIndex, int aAmount) {
+ ItemStack tStack = getStackInSlot(aIndex), rStack = GT_Utility.copyOrNull(tStack);
+ if (tStack != null) {
+ if (tStack.stackSize <= aAmount) {
+ if (setStackToZeroInsteadOfNull(aIndex)) {
+ tStack.stackSize = 0;
+ markDirty();
+ } else setInventorySlotContents(aIndex, null);
+ } else {
+ rStack = tStack.splitStack(aAmount);
+ markDirty();
+ if (tStack.stackSize == 0 && !setStackToZeroInsteadOfNull(aIndex))
+ setInventorySlotContents(aIndex, null);
+ }
+ }
+ return rStack;
+ }
+
+ @Override
+ public int[] getAccessibleSlotsFromSide(int ordinalSide) {
+ final TIntList tList = new TIntArrayList();
+ final IGregTechTileEntity tTileEntity = getBaseMetaTileEntity();
+ final CoverInfo tileCoverInfo = tTileEntity.getCoverInfoAtSide(ForgeDirection.getOrientation(ordinalSide));
+ final boolean tSkip = tileCoverInfo.letsItemsIn(-2) || tileCoverInfo.letsItemsOut(-2);
+ for (int i = 0; i < getSizeInventory(); i++) {
+ if (isValidSlot(i) && (tSkip || tileCoverInfo.letsItemsOut(i) || tileCoverInfo.letsItemsIn(i))) {
+ tList.add(i);
+ }
+ }
+ return tList.toArray();
+ }
+
+ @Override
+ public boolean canInsertItem(int aIndex, ItemStack aStack, int ordinalSide) {
+ return isValidSlot(aIndex) && aStack != null
+ && aIndex < mInventory.length
+ && (mInventory[aIndex] == null || GT_Utility.areStacksEqual(aStack, mInventory[aIndex]))
+ && allowPutStack(getBaseMetaTileEntity(), aIndex, ForgeDirection.getOrientation(ordinalSide), aStack);
+ }
+
+ @Override
+ public boolean canExtractItem(int aIndex, ItemStack aStack, int ordinalSide) {
+ return isValidSlot(aIndex) && aStack != null
+ && aIndex < mInventory.length
+ && allowPullStack(getBaseMetaTileEntity(), aIndex, ForgeDirection.getOrientation(ordinalSide), aStack);
+ }
+
+ @Override
+ public boolean canFill(ForgeDirection side, Fluid aFluid) {
+ return fill(side, new FluidStack(aFluid, 1), false) == 1;
+ }
+
+ @Override
+ public boolean canDrain(ForgeDirection side, Fluid aFluid) {
+ return drain(side, new FluidStack(aFluid, 1), false) != null;
+ }
+
+ @Override
+ public FluidTankInfo[] getTankInfo(ForgeDirection side) {
+ if (getCapacity() <= 0 && !getBaseMetaTileEntity().hasSteamEngineUpgrade()) return new FluidTankInfo[] {};
+ return new FluidTankInfo[] { getInfo() };
+ }
+
+ public int fill_default(ForgeDirection side, FluidStack aFluid, boolean doFill) {
+ int filled = fill(aFluid, doFill);
+ if (filled > 0) {
+ markDirty();
+ }
+ return filled;
+ }
+
+ @Override
+ public int fill(ForgeDirection side, FluidStack aFluid, boolean doFill) {
+ if (getBaseMetaTileEntity().hasSteamEngineUpgrade() && GT_ModHandler.isSteam(aFluid) && aFluid.amount > 1) {
+ int tSteam = (int) Math.min(
+ Integer.MAX_VALUE,
+ Math.min(
+ aFluid.amount / 2,
+ getBaseMetaTileEntity().getSteamCapacity() - getBaseMetaTileEntity().getStoredSteam()));
+ if (tSteam > 0) {
+ markDirty();
+ if (doFill) getBaseMetaTileEntity().increaseStoredSteam(tSteam, true);
+ return tSteam * 2;
+ }
+ } else {
+ return fill_default(side, aFluid, doFill);
+ }
+ return 0;
+ }
+
+ @Override
+ public FluidStack drain(ForgeDirection side, FluidStack aFluid, boolean doDrain) {
+ if (getFluid() != null && aFluid != null && getFluid().isFluidEqual(aFluid))
+ return drain(aFluid.amount, doDrain);
+ return null;
+ }
+
+ @Override
+ public FluidStack drain(ForgeDirection side, int maxDrain, boolean doDrain) {
+ return drain(maxDrain, doDrain);
+ }
+
+ @Override
+ public int getFluidAmount() {
+ return 0;
+ }
+
+ @Override
+ public FluidTankInfo getInfo() {
+ return new FluidTankInfo(this);
+ }
+
+ @Override
+ public String getMetaName() {
+ return mName;
+ }
+
+ @Override
+ public ItemStack getStackInSlotOnClosing(int i) {
+ return null;
+ }
+
+ @Override
+ public boolean hasCustomInventoryName() {
+ return false;
+ }
+
+ @Override
+ public boolean doTickProfilingMessageDuringThisTick() {
+ return doTickProfilingInThisTick;
+ }
+
+ @Override
+ public void markDirty() {
+ if (mBaseMetaTileEntity != null) {
+ mBaseMetaTileEntity.markDirty();
+ }
+ }
+
+ @Override
+ public boolean isUseableByPlayer(EntityPlayer entityplayer) {
+ return false;
+ }
+
+ @Override
+ public void openInventory() {
+ //
+ }
+
+ @Override
+ public void closeInventory() {
+ //
+ }
+
+ /**
+ * @deprecated Use ModularUI
+ */
+ @Deprecated
+ @Override
+ public Object getServerGUI(int aID, InventoryPlayer aPlayerInventory, IGregTechTileEntity aBaseMetaTileEntity) {
+ return null;
+ }
+
+ /**
+ * @deprecated Use ModularUI
+ */
+ @Deprecated
+ @Override
+ public Object getClientGUI(int aID, InventoryPlayer aPlayerInventory, IGregTechTileEntity aBaseMetaTileEntity) {
+ return null;
+ }
+
+ @Override
+ public boolean connectsToItemPipe(ForgeDirection side) {
+ return false;
+ }
+
+ @Override
+ public float getExplosionResistance(ForgeDirection side) {
+ return 10.0F;
+ }
+
+ @Override
+ public ItemStack[] getRealInventory() {
+ return mInventory;
+ }
+
+ @Override
+ public void onColorChangeServer(byte aColor) {
+ final IGregTechTileEntity meta = getBaseMetaTileEntity();
+ final int aX = meta.getXCoord(), aY = meta.getYCoord(), aZ = meta.getZCoord();
+ for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
+ // Flag surrounding pipes/cables to revaluate their connection with us if we got painted
+ final TileEntity tTileEntity = meta.getTileEntityAtSide(side);
+ if (tTileEntity instanceof BaseMetaPipeEntity pipe) {
+ pipe.onNeighborBlockChange(aX, aY, aZ);
+ }
+ }
+ }
+
+ @Override
+ public void onColorChangeClient(byte aColor) {
+ // Do nothing apparently
+ }
+
+ @Override
+ @SideOnly(Side.CLIENT)
+ public boolean renderInInventory(Block aBlock, int aMeta, RenderBlocks aRenderer) {
+ return false;
+ }
+
+ @Override
+ @SideOnly(Side.CLIENT)
+ public boolean renderInWorld(IBlockAccess aWorld, int aX, int aY, int aZ, Block aBlock, RenderBlocks aRenderer) {
+ return false;
+ }
+
+ @Override
+ public void doExplosion(long aExplosionPower) {
+ float tStrength = GT_Values.getExplosionPowerForVoltage(aExplosionPower);
+ final int tX = getBaseMetaTileEntity().getXCoord();
+ final int tY = getBaseMetaTileEntity().getYCoord();
+ final int tZ = getBaseMetaTileEntity().getZCoord();
+ final World tWorld = getBaseMetaTileEntity().getWorld();
+ GT_Utility.sendSoundToPlayers(tWorld, SoundResource.IC2_MACHINES_MACHINE_OVERLOAD, 1.0F, -1, tX, tY, tZ);
+ tWorld.setBlock(tX, tY, tZ, Blocks.air);
+ if (GregTech_API.sMachineExplosions)
+ tWorld.createExplosion(null, tX + 0.5, tY + 0.5, tZ + 0.5, tStrength, true);
+ }
+
+ @Override
+ public int getLightOpacity() {
+ return ((BaseMetaTileEntity) getBaseMetaTileEntity()).getLightValue() > 0 ? 0 : 255;
+ }
+
+ @Override
+ public void addCollisionBoxesToList(World aWorld, int aX, int aY, int aZ, AxisAlignedBB inputAABB,
+ List<AxisAlignedBB> outputAABB, Entity collider) {
+ AxisAlignedBB axisalignedbb1 = getCollisionBoundingBoxFromPool(aWorld, aX, aY, aZ);
+ if (axisalignedbb1 != null && inputAABB.intersectsWith(axisalignedbb1)) outputAABB.add(axisalignedbb1);
+ }
+
+ @Override
+ public AxisAlignedBB getCollisionBoundingBoxFromPool(World aWorld, int aX, int aY, int aZ) {
+ return AxisAlignedBB.getBoundingBox(aX, aY, aZ, aX + 1, aY + 1, aZ + 1);
+ }
+
+ @Override
+ public void onEntityCollidedWithBlock(World aWorld, int aX, int aY, int aZ, Entity collider) {
+ //
+ }
+
+ @Override
+ public void onCreated(ItemStack aStack, World aWorld, EntityPlayer aPlayer) {
+ //
+ }
+
+ @Override
+ public boolean allowGeneralRedstoneOutput() {
+ return false;
+ }
+
+ @Deprecated
+ public String trans(String aKey, String aEnglish) {
+ return GT_Utility.trans(aKey, aEnglish);
+ }
+
+ @Override
+ public boolean hasAlternativeModeText() {
+ return false;
+ }
+
+ @Override
+ public String getAlternativeModeText() {
+ return "";
+ }
+
+ @Override
+ public boolean shouldJoinIc2Enet() {
+ return false;
+ }
+
+ public boolean shouldTriggerBlockUpdate() {
+ return false;
+ }
+
+ // === AE2 compat ===
+
+ public AECableType getCableConnectionType(ForgeDirection forgeDirection) {
+ return AECableType.NONE;
+ }
+
+ public AENetworkProxy getProxy() {
+ return null;
+ }
+
+ public void gridChanged() {}
+
+ @Override
+ public ItemStack getMachineCraftingIcon() {
+ final IGregTechTileEntity mte = getBaseMetaTileEntity();
+ if (mte == null) {
+ return null;
+ }
+ return mte.getDrops()
+ .stream()
+ .findAny()
+ .orElse(null);
+ }
+
+ // === Waila compat ===
+
+ @Override
+ public void getWailaBody(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor,
+ IWailaConfigHandler config) {
+ currenttip.add(
+ String.format(
+ "Facing: %s",
+ mBaseMetaTileEntity.getFrontFacing()
+ .name()));
+
+ if (this instanceof IPowerChannelState state) {
+ final NBTTagCompound tag = accessor.getNBTData();
+ final boolean isActive = tag.getBoolean("isActive");
+ final boolean isPowered = tag.getBoolean("isPowered");
+ final boolean isBooting = tag.getBoolean("isBooting");
+ currenttip.add(WailaText.getPowerState(isActive, isPowered, isBooting));
+ }
+ }
+
+ @Override
+ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y,
+ int z) {
+ if (this instanceof IPowerChannelState state) {
+ final boolean isActive = state.isActive();
+ final boolean isPowered = state.isPowered();
+ final boolean isBooting = state.isBooting();
+ tag.setBoolean("isActive", isActive);
+ tag.setBoolean("isPowered", isPowered);
+ tag.setBoolean("isBooting", isBooting);
+ }
+ }
+
+ protected String getAEDiagnostics() {
+ try {
+ if (getProxy() == null) return "(proxy)";
+ if (getProxy().getNode() == null) return "(node)";
+ if (getProxy().getNode()
+ .getGrid() == null) return "(grid)";
+ if (!getProxy().getNode()
+ .meetsChannelRequirements()) return "(channels)";
+ IPathingGrid pg = getProxy().getNode()
+ .getGrid()
+ .getCache(IPathingGrid.class);
+ if (!pg.isNetworkBooting()) return "(booting)";
+ IEnergyGrid eg = getProxy().getNode()
+ .getGrid()
+ .getCache(IEnergyGrid.class);
+ if (!eg.isNetworkPowered()) return "(power)";
+ } catch (Throwable ex) {
+ ex.printStackTrace();
+ }
+ return "";
+ }
+
+ @Override
+ public GUITextureSet getGUITextureSet() {
+ return GUITextureSet.DEFAULT;
+ }
+
+ @Override
+ public int getGUIColorization() {
+ Dyes dye = Dyes.dyeWhite;
+ if (this.colorOverride.sLoaded()) {
+ if (this.colorOverride.sGuiTintingEnabled() && getBaseMetaTileEntity() != null) {
+ dye = Dyes.getDyeFromIndex(getBaseMetaTileEntity().getColorization());
+ return this.colorOverride.getGuiTintOrDefault(dye.mName, GT_Util.getRGBInt(dye.getRGBA()));
+ }
+ } else if (GregTech_API.sColoredGUI) {
+ if (GregTech_API.sMachineMetalGUI) {
+ dye = Dyes.MACHINE_METAL;
+ } else if (getBaseMetaTileEntity() != null) {
+ dye = Dyes.getDyeFromIndex(getBaseMetaTileEntity().getColorization());
+ }
+ }
+ return GT_Util.getRGBInt(dye.getRGBA());
+ }
+
+ @Override
+ public int getTextColorOrDefault(String textType, int defaultColor) {
+ return colorOverride.getTextColorOrDefault(textType, defaultColor);
+ }
+
+ protected Supplier<Integer> COLOR_TITLE = () -> getTextColorOrDefault("title", 0x404040);
+ protected Supplier<Integer> COLOR_TITLE_WHITE = () -> getTextColorOrDefault("title_white", 0xfafaff);
+ protected Supplier<Integer> COLOR_TEXT_WHITE = () -> getTextColorOrDefault("text_white", 0xfafaff);
+ protected Supplier<Integer> COLOR_TEXT_GRAY = () -> getTextColorOrDefault("text_gray", 0x404040);
+ protected Supplier<Integer> COLOR_TEXT_RED = () -> getTextColorOrDefault("text_red", 0xff0000);
+}
diff --git a/src/main/java/gregtech/api/metatileentity/TileIC2EnergySink.java b/src/main/java/gregtech/api/metatileentity/TileIC2EnergySink.java
new file mode 100644
index 0000000000..6fecb840b4
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/TileIC2EnergySink.java
@@ -0,0 +1,119 @@
+package gregtech.api.metatileentity;
+
+import net.minecraft.tileentity.TileEntity;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.google.common.collect.Sets;
+
+import gregtech.api.interfaces.metatileentity.IConnectable;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntityCable;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.metatileentity.implementations.GT_MetaPipeEntity_Cable;
+import gregtech.api.util.GT_Utility;
+import ic2.api.energy.tile.IEnergySink;
+
+public class TileIC2EnergySink extends TileEntity implements IEnergySink {
+
+ private final IGregTechTileEntity myMeta;
+ private GT_MetaPipeEntity_Cable cableMeta = null;
+
+ public TileIC2EnergySink(IGregTechTileEntity meta) {
+ if (meta == null) throw new NullPointerException("no null metas");
+ myMeta = meta;
+ final IMetaTileEntity metaTile = myMeta.getMetaTileEntity();
+ if (metaTile instanceof IMetaTileEntityCable) {
+ cableMeta = (GT_MetaPipeEntity_Cable) metaTile;
+ }
+ setWorldObj(meta.getWorld());
+ xCoord = meta.getXCoord();
+ yCoord = meta.getYCoord();
+ zCoord = meta.getZCoord();
+ }
+ /*
+ * IC2 enet compat - IEnergySink
+ */
+
+ /**
+ * Determine how much energy the sink accepts. Note that you cannot modify the energy net from this method.
+ * <p>
+ * Make sure that {@link TileIC2EnergySink#injectEnergy} accepts energy if this function returns anything positive.
+ *
+ * @return max accepted input in eu
+ */
+ @Override
+ public double getDemandedEnergy() {
+ if (cableMeta != null) {
+ /*
+ * We don't want everything to join the enet, treating the cable as a conductor, so we join it as a link.
+ * We don't want to traverse all cables connected to this, like we would during distribution, to see if it
+ * actually needs any EU, so we just always say we want it all. If there are more than two things attached
+ * and one of them is a GT cable that doesn't have anywhere to send its energy, the distribution will be a
+ * bit weird. In that case, use only one cable or a transformer.
+ */
+ return (cableMeta.mVoltage * cableMeta.mAmperage);
+ } else return myMeta.getEUCapacity() - myMeta.getStoredEU();
+ }
+
+ /**
+ * Gets the tier of this energy sink. 1 = LV, 2 = MV, 3 = HV, 4 = EV etc.
+ *
+ * @return tier of this energy sink or {@link Integer#MAX_VALUE} to allow any voltage
+ */
+ @Override
+ public int getSinkTier() {
+ return GT_Utility.getTier(cableMeta != null ? cableMeta.mVoltage : myMeta.getInputVoltage());
+ }
+
+ /**
+ * Transfer energy to the sink.
+ * <p>
+ * It's highly recommended to accept all energy by letting the internal buffer overflow to increase the performance
+ * and accuracy of the distribution simulation.
+ *
+ * @param directionFrom direction from which the energy comes from
+ * @param amount energy to be transferred
+ * @return Energy not consumed (leftover)
+ */
+ @Override
+ public double injectEnergy(ForgeDirection directionFrom, double amount, double voltage) {
+
+ final long amps = (long) Math
+ .max(amount / (cableMeta != null ? cableMeta.mVoltage : myMeta.getInputVoltage() * 1.0), 1.0);
+ final long euPerAmp = (long) (amount / (amps * 1.0));
+
+ final IMetaTileEntity metaTile = myMeta.getMetaTileEntity();
+ if (metaTile == null) return amount;
+
+ final long usedAmps;
+ if (cableMeta != null) {
+ usedAmps = ((IMetaTileEntityCable) metaTile).transferElectricity(
+ directionFrom,
+ Math.min(euPerAmp, cableMeta.mVoltage),
+ amps,
+ Sets.newHashSet((TileEntity) myMeta));
+
+ } else usedAmps = myMeta.injectEnergyUnits(directionFrom, Math.min(euPerAmp, myMeta.getInputVoltage()), amps);
+ return amount - (usedAmps * euPerAmp);
+
+ // transferElectricity for cables
+ }
+
+ /**
+ * Determine if this acceptor can accept current from an adjacent emitter in a direction.
+ * <p>
+ * The TileEntity in the emitter parameter is what was originally added to the energy net, which may be normal
+ * in-world TileEntity, a delegate or an IMetaDelegate.
+ *
+ * @param emitter energy emitter, may also be null or an IMetaDelegate
+ * @param direction direction the energy is being received from
+ */
+ @Override
+ public boolean acceptsEnergyFrom(TileEntity emitter, ForgeDirection direction) {
+ final IMetaTileEntity metaTile = myMeta.getMetaTileEntity();
+ if (metaTile instanceof IMetaTileEntityCable
+ && (direction == ForgeDirection.UNKNOWN || ((IConnectable) metaTile).isConnectedAtSide(direction)))
+ return true;
+ else return myMeta.inputEnergyFrom(direction, false);
+ }
+}
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..1477f989f8
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Cable.java
@@ -0,0 +1,679 @@
+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 long mTransferredVoltage = 0;
+
+ @Deprecated
+ public int mTransferredAmperageLast20 = 0, mTransferredAmperageLast20OK = 0, mTransferredAmperageOK = 0;
+ @Deprecated
+ public long mTransferredVoltageLast20 = 0, mTransferredVoltageLast20OK = 0, mTransferredVoltageOK = 0;
+
+ public long mRestRF;
+ public int mOverheat;
+ public static short mMaxOverheat = (short) (GT_Mod.gregtechproxy.mWireHeatingTicks * 100);
+
+ private long lastWorldTick;
+
+ 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 onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) {
+ if (aBaseMetaTileEntity.isServerSide()) {
+ lastWorldTick = aBaseMetaTileEntity.getWorld()
+ .getTotalWorldTime() - 1;
+ // sets initial value -1 since it is
+ // in the same tick as first on post
+ // tick
+ }
+ }
+
+ @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..f604f1583f
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Fluid.java
@@ -0,0 +1,991 @@
+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);
+ }
+
+ @Deprecated
+ public GT_MetaPipeEntity_Fluid(String aName, float aThickNess, Materials aMaterial, int aCapacity,
+ int aHeatResistance, boolean aGasProof) {
+ this(aName, aThickNess, aMaterial, aCapacity, aHeatResistance, aGasProof, 1);
+ }
+
+ 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));
+ }
+
+ @Deprecated
+ protected static ITexture getRestrictorTexture(byte borderMask) {
+ return getRestrictorTexture((int) borderMask);
+ }
+
+ 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..399c536b9f
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Frame.java
@@ -0,0 +1,150 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.recipe.RecipeMaps.assemblerRecipes;
+import static gregtech.api.util.GT_RecipeBuilder.SECONDS;
+import static gregtech.api.util.GT_RecipeBuilder.TICKS;
+import static gregtech.api.util.GT_Utility.calculateRecipeEU;
+
+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.GT_Values;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.OrePrefixes;
+import gregtech.api.enums.SubTag;
+import gregtech.api.enums.TierEU;
+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;
+import gregtech.api.util.GT_ModHandler;
+import gregtech.api.util.GT_ModHandler.RecipeBits;
+import gregtech.api.util.GT_OreDictUnificator;
+import gregtech.api.util.GT_Utility;
+
+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;
+
+ GT_OreDictUnificator.registerOre(OrePrefixes.frameGt, aMaterial, getStackForm(1));
+ if (aMaterial.getProcessingMaterialTierEU() < TierEU.IV) {
+ GT_ModHandler.addCraftingRecipe(
+ getStackForm(2),
+ RecipeBits.NOT_REMOVABLE | GT_ModHandler.RecipeBits.BUFFERED,
+ new Object[] { "SSS", "SwS", "SSS", 'S', OrePrefixes.stick.get(mMaterial) });
+ }
+
+ if (!aMaterial.contains(SubTag.NO_RECIPES)
+ && GT_OreDictUnificator.get(OrePrefixes.stick, aMaterial, 1) != null) {
+ // Auto generate frame box recipe in an assembler.
+ GT_Values.RA.stdBuilder()
+ .itemInputs(
+ GT_OreDictUnificator.get(OrePrefixes.stick, aMaterial, 4),
+ GT_Utility.getIntegratedCircuit(4))
+ .itemOutputs(getStackForm(1))
+ .duration(3 * SECONDS + 4 * TICKS)
+ .eut(calculateRecipeEU(aMaterial, 7))
+ .addTo(assemblerRecipes);
+ }
+ }
+
+ 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..ece64e2c1d
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicBatteryBuffer.java
@@ -0,0 +1,441 @@
+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 boolean useModularUI() {
+ 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..897f9dad6f
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicGenerator.java
@@ -0,0 +1,344 @@
+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;
+ }
+
+ @Override
+ public boolean useModularUI() {
+ return true;
+ }
+}
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..4709c82776
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine.java
@@ -0,0 +1,1568 @@
+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 boolean useModularUI() {
+ return getRecipeMap() != null;
+ }
+
+ @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..8bba0fee25
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine_GT_Recipe.java
@@ -0,0 +1,820 @@
+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.enums.Mods.BartWorks;
+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;
+import ic2.core.Ic2Items;
+
+/**
+ * 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 -> BartWorks.isModLoaded() ? "blockGlass" + VN[aTier] : Ic2Items.reinforcedGlass;
+ default -> BartWorks.isModLoaded() ? "blockGlass" + VN[8] : Ic2Items.reinforcedGlass;
+ };
+
+ 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..c810614161
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicTank.java
@@ -0,0 +1,348 @@
+package gregtech.api.metatileentity.implementations;
+
+import net.minecraft.entity.player.InventoryPlayer;
+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.GT_Container_BasicTank;
+import gregtech.api.gui.GT_GUIContainer_BasicTank;
+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;
+ }
+
+ @Deprecated
+ public FluidStack getDisplayedFluid() {
+ return getDrainableStack();
+ }
+
+ @Deprecated
+ @Override
+ public Object getServerGUI(int aID, InventoryPlayer aPlayerInventory, IGregTechTileEntity aBaseMetaTileEntity) {
+ return new GT_Container_BasicTank(aPlayerInventory, aBaseMetaTileEntity);
+ }
+
+ @Deprecated
+ @Override
+ public Object getClientGUI(int aID, InventoryPlayer aPlayerInventory, IGregTechTileEntity aBaseMetaTileEntity) {
+ return new GT_GUIContainer_BasicTank(aPlayerInventory, aBaseMetaTileEntity, getLocalName());
+ }
+
+ @Override
+ public void onPreTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ if (aBaseMetaTileEntity.isServerSide()) {
+ if (isFluidChangingAllowed() && getFillableStack() != null && getFillableStack().amount <= 0)
+ setFillableStack(null);
+
+ if (doesEmptyContainers()) {
+ FluidStack tFluid = GT_Utility.getFluidForFilledItem(mInventory[getInputSlot()], true);
+ if (tFluid != null && isFluidInputAllowed(tFluid)) {
+ if (getFillableStack() == null) {
+ if (isFluidInputAllowed(tFluid) && tFluid.amount <= getCapacity()) {
+ if (aBaseMetaTileEntity.addStackToSlot(
+ getOutputSlot(),
+ GT_Utility.getContainerForFilledItem(mInventory[getInputSlot()], true),
+ 1)) {
+ setFillableStack(tFluid.copy());
+ this.onEmptyingContainerWhenEmpty();
+ aBaseMetaTileEntity.decrStackSize(getInputSlot(), 1);
+ }
+ }
+ } else {
+ if (tFluid.isFluidEqual(getFillableStack())
+ && ((long) tFluid.amount + getFillableStack().amount) <= (long) getCapacity()) {
+ if (aBaseMetaTileEntity.addStackToSlot(
+ getOutputSlot(),
+ GT_Utility.getContainerForFilledItem(mInventory[getInputSlot()], true),
+ 1)) {
+ getFillableStack().amount += tFluid.amount;
+ aBaseMetaTileEntity.decrStackSize(getInputSlot(), 1);
+ }
+ }
+ }
+ }
+ }
+
+ if (doesFillContainers()) {
+ ItemStack tOutput = GT_Utility
+ .fillFluidContainer(getDrainableStack(), mInventory[getInputSlot()], false, true);
+ if (tOutput != null && aBaseMetaTileEntity.addStackToSlot(getOutputSlot(), tOutput, 1)) {
+ FluidStack tFluid = GT_Utility.getFluidForFilledItem(tOutput, true);
+ aBaseMetaTileEntity.decrStackSize(getInputSlot(), 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..1fc347da1f
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Buffer.java
@@ -0,0 +1,568 @@
+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);
+ }
+
+ @Override
+ public boolean useModularUI() {
+ return true;
+ }
+
+ 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..c7f0e6cdd5
--- /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 = 100;
+ 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..b7f34f264c
--- /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 ? 2 : 1)
+ .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..122dcfa746
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_DataAccess.java
@@ -0,0 +1,157 @@
+package gregtech.api.metatileentity.implementations;
+
+import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_DATA_ACCESS;
+
+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);
+ }
+
+ @Override
+ public boolean useModularUI() {
+ return true;
+ }
+
+ @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..18aef371b6
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Input.java
@@ -0,0 +1,201 @@
+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;
+ }
+
+ @Override
+ public boolean useModularUI() {
+ return true;
+ }
+}
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..f4c4eb6a14
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_InputBus.java
@@ -0,0 +1,323 @@
+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" : "")));
+ }
+
+ @Deprecated
+ // having too many constructors is bad, don't be so lazy, use GT_MetaTileEntity_Hatch_InputBus(String, int,
+ // String[], ITexture[][][])
+ public GT_MetaTileEntity_Hatch_InputBus(String aName, int aTier, String aDescription, ITexture[][][] aTextures) {
+ this(aName, aTier, ArrayExt.of(aDescription), aTextures);
+ }
+
+ 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);
+ }
+
+ @Override
+ public boolean useModularUI() {
+ return true;
+ }
+
+ 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);
+ switch (mTier) {
+ case 0 -> getBaseMetaTileEntity().add1by1Slot(builder);
+ case 1 -> getBaseMetaTileEntity().add2by2Slots(builder);
+ case 2 -> getBaseMetaTileEntity().add3by3Slots(builder);
+ default -> getBaseMetaTileEntity().add4by4Slots(builder);
+ }
+ }
+
+ 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..e1b5ee8f03
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Maintenance.java
@@ -0,0 +1,464 @@
+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.Advanced, 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 boolean useModularUI() {
+ return true;
+ }
+
+ @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..cea2e25baa
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_MultiInput.java
@@ -0,0 +1,305 @@
+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 boolean useModularUI() {
+ return true;
+ }
+
+ @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..fd83f6899b
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Output.java
@@ -0,0 +1,502 @@
+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 boolean useModularUI() {
+ return true;
+ }
+
+ @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..3bd92c6871
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_OutputBus.java
@@ -0,0 +1,202 @@
+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.inventory.IInventory;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
+
+import gregtech.GT_Mod;
+import gregtech.api.gui.modularui.GT_UIInfos;
+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_Utility;
+import gregtech.api.util.extensions.ArrayExt;
+
+public class GT_MetaTileEntity_Hatch_OutputBus extends GT_MetaTileEntity_Hatch implements IAddUIWidgets {
+
+ 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" : "")));
+ }
+
+ 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);
+ }
+
+ @Deprecated
+ // having too many constructors is bad, don't be so lazy, use GT_MetaTileEntity_Hatch_OutputBus(String, int,
+ // String[], ITexture[][][])
+ public GT_MetaTileEntity_Hatch_OutputBus(String aName, int aTier, String aDescription, ITexture[][][] aTextures) {
+ this(aName, aTier, getSlots(aTier), ArrayExt.of(aDescription), aTextures);
+ }
+
+ 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) {
+ GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
+ return true;
+ }
+
+ /**
+ * 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();
+ 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 boolean useModularUI() {
+ return true;
+ }
+
+ @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);
+ }
+ }
+}
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_MultiBlockBase.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_MultiBlockBase.java
new file mode 100644
index 0000000000..32ea708773
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_MultiBlockBase.java
@@ -0,0 +1,2540 @@
+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 org.lwjgl.input.Keyboard;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.gtnewhorizons.modularui.api.NumberFormatMUI;
+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.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.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 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 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<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;
+
+ 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 = "";
+ }
+
+ 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);
+ }
+
+ @Override
+ public boolean isDisplaySecondaryDescription() {
+ return Keyboard.isKeyDown(Keyboard.KEY_LSHIFT);
+ }
+
+ @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 (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 (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);
+ }
+
+ mWrench = aNBT.getBoolean("mWrench");
+ mScrewdriver = aNBT.getBoolean("mScrewdriver");
+ mSoftHammer = aNBT.getBoolean("mSoftHammer");
+ mHardHammer = aNBT.getBoolean("mHardHammer");
+ mSolderingTool = aNBT.getBoolean("mSolderingTool");
+ mCrowbar = aNBT.getBoolean("mCrowbar");
+ }
+
+ 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();
+ }
+
+ 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;
+ }
+ }
+
+ private void checkMaintenance() {
+ if (disableMaintenance) {
+ mWrench = true;
+ mScrewdriver = true;
+ mSoftHammer = true;
+ mHardHammer = true;
+ mSolderingTool = true;
+ mCrowbar = true;
+
+ return;
+ }
+ for (GT_MetaTileEntity_Hatch_Maintenance tHatch : filterValidMTEs(mMaintenanceHatches)) {
+ if (tHatch.mAuto && !(mWrench && mScrewdriver && mSoftHammer && mHardHammer && mSolderingTool && mCrowbar))
+ 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;
+
+ // Perform more frequent recipe change after the machine just shuts down.
+ long timeElapsed = aTick - mLastWorkingTick;
+
+ if (timeElapsed >= 100) return aTick % 100 == 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 {
+ if (shouldCheckRecipeThisTick(aTick) || aBaseMetaTileEntity.hasWorkJustBeenEnabled()
+ || aBaseMetaTileEntity.hasInventoryBeenModified()) {
+
+ if (aBaseMetaTileEntity.isAllowedToWork()) {
+ 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 (getRepairStatus() == 0) {
+ stopMachine(ShutDownReasonRegistry.NO_REPAIR);
+ return false;
+ }
+ if (mRuntime++ > 1000) {
+ mRuntime = 0;
+ if (getBaseMetaTileEntity().getRandomNumber(6000) == 0) {
+ 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;
+ }
+ }
+ 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 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 ? 2 : 1)
+ .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);
+ for (GT_MetaTileEntity_Hatch_OutputBus tHatch : filterValidMTEs(mOutputBusses)) {
+ if (tHatch.storeAll(aStack)) {
+ return true;
+ }
+ }
+ boolean outputSuccess = true;
+ 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;
+ }
+
+ 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 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 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 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 false;
+ }
+
+ @Override
+ public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
+ ItemStack aStack) {
+ return false;
+ }
+
+ 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);
+ }
+ currentTip.add(
+ (tag.getBoolean("hasProblems") ? (RED + "** HAS PROBLEMS **") : GREEN + "Running Fine") + RESET
+ + " Efficiency: "
+ + tag.getFloat("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[1];
+ }
+
+ @Override
+ public Pos2d getPowerSwitchButtonPos() {
+ return new Pos2d(174, 148);
+ }
+
+ @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)) {
+ final IInventory tBusInv = tBus.getBaseMetaTileEntity();
+ for (int i = 0; i < tBusInv.getSizeInventory(); i++) {
+ ret.add(tBus.getStackInSlot(i));
+ }
+ }
+ 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) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean canDumpFluidToME() {
+ for (IFluidStore tHatch : getFluidOutputSlots(new FluidStack[0])) {
+ if (tHatch instanceof GT_MetaTileEntity_Hatch_Output_ME) {
+ 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);
+ }
+
+ @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 boolean useModularUI() {
+ return true;
+ }
+
+ @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);
+
+ builder.widget(createPowerSwitchButton(builder))
+ .widget(createVoidExcessButton(builder))
+ .widget(createInputSeparationButton(builder))
+ .widget(createBatchModeButton(builder))
+ .widget(createLockToSingleRecipeButton(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);
+
+ 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;
+ }
+}
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..dd21d6f412
--- /dev/null
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_TieredMachineBlock.java
@@ -0,0 +1,126 @@
+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;
+
+ @Deprecated
+ public final String mDescription;
+
+ /**
+ * 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 };
+ mDescription = mDescriptionArray.length > 0 ? mDescriptionArray[0] : "";
+ // 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;
+ mDescription = mDescriptionArray.length > 0 ? mDescriptionArray[0] : "";
+
+ // 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 };
+ mDescription = mDescriptionArray.length > 0 ? mDescriptionArray[0] : "";
+ 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;
+ mDescription = mDescriptionArray.length > 0 ? mDescriptionArray[0] : "";
+ 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_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);
+ }
+}