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