diff options
Diffstat (limited to 'src/main/java/gregtech/api/metatileentity/CoverableTileEntity.java')
-rw-r--r-- | src/main/java/gregtech/api/metatileentity/CoverableTileEntity.java | 803 |
1 files changed, 803 insertions, 0 deletions
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(); + } +} |