/* * This file is part of Applied Energistics 2. * Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved. * * Applied Energistics 2 is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Applied Energistics 2 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Applied Energistics 2. If not, see . */ package appeng.parts; import java.io.IOException; import java.util.EnumSet; import java.util.LinkedList; import java.util.List; import java.util.Random; import java.util.Set; import javax.annotation.Nullable; import io.netty.buffer.ByteBuf; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; import appeng.api.AEApi; import appeng.api.config.YesNo; import appeng.api.exceptions.FailedConnection; import appeng.api.implementations.parts.IPartCable; import appeng.api.networking.IGridHost; import appeng.api.networking.IGridNode; import appeng.api.parts.IFacadeContainer; import appeng.api.parts.IFacadePart; import appeng.api.parts.IPart; import appeng.api.parts.IPartCollisionHelper; import appeng.api.parts.IPartHost; import appeng.api.parts.IPartItem; import appeng.api.parts.LayerFlags; import appeng.api.parts.PartItemStack; import appeng.api.parts.SelectedPart; import appeng.api.util.AECableType; import appeng.api.util.AEColor; import appeng.api.util.AEPartLocation; import appeng.api.util.DimensionalCoord; import appeng.client.render.cablebus.CableBusRenderState; import appeng.client.render.cablebus.CableCoreType; import appeng.client.render.cablebus.FacadeRenderState; import appeng.core.AELog; import appeng.facade.FacadeContainer; import appeng.helpers.AEMultiTile; import appeng.me.GridConnection; import appeng.parts.networking.PartCable; import appeng.util.Platform; public class CableBusContainer extends CableBusStorage implements AEMultiTile, ICableBusContainer { private static final ThreadLocal IS_LOADING = new ThreadLocal(); private final EnumSet myLayerFlags = EnumSet.noneOf( LayerFlags.class ); private YesNo hasRedstone = YesNo.UNDECIDED; private IPartHost tcb; // TODO 1.10.2-R - does somebody seriously want to make parts TESR??? Hope not. private boolean requiresDynamicRender = false; private boolean inWorld = false; public CableBusContainer( final IPartHost host ) { this.tcb = host; } public static boolean isLoading() { final Boolean is = IS_LOADING.get(); return is != null && is; } public void setHost( final IPartHost host ) { this.tcb.clearContainer(); this.tcb = host; } public void rotateLeft() { final IPart[] newSides = new IPart[6]; newSides[AEPartLocation.UP.ordinal()] = this.getSide( AEPartLocation.UP ); newSides[AEPartLocation.DOWN.ordinal()] = this.getSide( AEPartLocation.DOWN ); newSides[AEPartLocation.EAST.ordinal()] = this.getSide( AEPartLocation.NORTH ); newSides[AEPartLocation.SOUTH.ordinal()] = this.getSide( AEPartLocation.EAST ); newSides[AEPartLocation.WEST.ordinal()] = this.getSide( AEPartLocation.SOUTH ); newSides[AEPartLocation.NORTH.ordinal()] = this.getSide( AEPartLocation.WEST ); for( final AEPartLocation dir : AEPartLocation.SIDE_LOCATIONS ) { this.setSide( dir, newSides[dir.ordinal()] ); } this.getFacadeContainer().rotateLeft(); } @Override public IFacadeContainer getFacadeContainer() { return new FacadeContainer( this ); } @Override public boolean canAddPart( ItemStack is, final AEPartLocation side ) { if( PartPlacement.isFacade( is, side ) != null ) { return true; } if( is.getItem() instanceof IPartItem ) { final IPartItem bi = (IPartItem) is.getItem(); is = is.copy(); is.stackSize = 1; final IPart bp = bi.createPartFromItemStack( is ); if( bp != null ) { if( bp instanceof IPartCable ) { boolean canPlace = true; for( final AEPartLocation d : AEPartLocation.SIDE_LOCATIONS ) { if( this.getPart( d ) != null && !this.getPart( d ).canBePlacedOn( ( (IPartCable) bp ).supportsBuses() ) ) { canPlace = false; } } if( !canPlace ) { return false; } return this.getPart( AEPartLocation.INTERNAL ) == null; } else if( !( bp instanceof IPartCable ) && side != AEPartLocation.INTERNAL ) { final IPart cable = this.getPart( AEPartLocation.INTERNAL ); if( cable != null && !bp.canBePlacedOn( ( (IPartCable) cable ).supportsBuses() ) ) { return false; } return this.getPart( side ) == null; } } } return false; } @Override public AEPartLocation addPart( ItemStack is, final AEPartLocation side, final @Nullable EntityPlayer player, final @Nullable EnumHand hand ) { if( this.canAddPart( is, side ) ) { if( is.getItem() instanceof IPartItem ) { final IPartItem bi = (IPartItem) is.getItem(); is = is.copy(); is.stackSize = 1; final IPart bp = bi.createPartFromItemStack( is ); if( bp instanceof IPartCable ) { boolean canPlace = true; for( final AEPartLocation d : AEPartLocation.SIDE_LOCATIONS ) { if( this.getPart( d ) != null && !this.getPart( d ).canBePlacedOn( ( (IPartCable) bp ).supportsBuses() ) ) { canPlace = false; } } if( !canPlace ) { return null; } if( this.getPart( AEPartLocation.INTERNAL ) != null ) { return null; } this.setCenter( (IPartCable) bp ); bp.setPartHostInfo( AEPartLocation.INTERNAL, this, this.tcb.getTile() ); if( player != null ) { bp.onPlacement( player, hand, is, side ); } if( this.inWorld ) { bp.addToWorld(); } final IGridNode cn = this.getCenter().getGridNode(); if( cn != null ) { for( final AEPartLocation ins : AEPartLocation.SIDE_LOCATIONS ) { final IPart sbp = this.getPart( ins ); if( sbp != null ) { final IGridNode sn = sbp.getGridNode(); if( sn != null ) { try { new GridConnection( cn, sn, AEPartLocation.INTERNAL ); } catch( final FailedConnection e ) { // ekk! bp.removeFromWorld(); this.setCenter( null ); return null; } } } } } this.updateConnections(); this.markForUpdate(); this.markForSave(); this.partChanged(); return AEPartLocation.INTERNAL; } else if( bp != null && !( bp instanceof IPartCable ) && side != AEPartLocation.INTERNAL ) { final IPart cable = this.getPart( AEPartLocation.INTERNAL ); if( cable != null && !bp.canBePlacedOn( ( (IPartCable) cable ).supportsBuses() ) ) { return null; } this.setSide( side, bp ); bp.setPartHostInfo( side, this, this.getTile() ); if( player != null ) { bp.onPlacement( player, hand, is, side ); } if( this.inWorld ) { bp.addToWorld(); } if( this.getCenter() != null ) { final IGridNode cn = this.getCenter().getGridNode(); final IGridNode sn = bp.getGridNode(); if( cn != null && sn != null ) { try { new GridConnection( cn, sn, AEPartLocation.INTERNAL ); } catch( final FailedConnection e ) { // ekk! bp.removeFromWorld(); this.setSide( side, null ); return null; } } } this.updateDynamicRender(); this.updateConnections(); this.markForUpdate(); this.markForSave(); this.partChanged(); return side; } } } return null; } @Override public IPart getPart( final AEPartLocation partLocation ) { if( partLocation == AEPartLocation.INTERNAL ) { return this.getCenter(); } return this.getSide( partLocation ); } @Override public IPart getPart( final EnumFacing side ) { return this.getSide( AEPartLocation.fromFacing( side ) ); } @Override public void removePart( final AEPartLocation side, final boolean suppressUpdate ) { if( side == AEPartLocation.INTERNAL ) { if( this.getCenter() != null ) { this.getCenter().removeFromWorld(); } this.setCenter( null ); } else { if( this.getSide( side ) != null ) { this.getSide( side ).removeFromWorld(); } this.setSide( side, null ); } if( !suppressUpdate ) { this.updateDynamicRender(); this.updateConnections(); this.markForUpdate(); this.markForSave(); this.partChanged(); } } @Override public void markForUpdate() { this.tcb.markForUpdate(); } @Override public DimensionalCoord getLocation() { return this.tcb.getLocation(); } @Override public TileEntity getTile() { return this.tcb.getTile(); } @Override public AEColor getColor() { if( this.getCenter() != null ) { final IPartCable c = this.getCenter(); return c.getCableColor(); } return AEColor.TRANSPARENT; } @Override public void clearContainer() { throw new UnsupportedOperationException( "Now that is silly!" ); } @Override public boolean isBlocked( final EnumFacing side ) { return this.tcb.isBlocked( side ); } @Override public SelectedPart selectPart( final Vec3d pos ) { for( final AEPartLocation side : AEPartLocation.values() ) { final IPart p = this.getPart( side ); if( p != null ) { final List boxes = new LinkedList(); final IPartCollisionHelper bch = new BusCollisionHelper( boxes, side, null, true ); p.getBoxes( bch ); for( AxisAlignedBB bb : boxes ) { bb = bb.expand( 0.002, 0.002, 0.002 ); if( bb.isVecInside( pos ) ) { return new SelectedPart( p, side ); } } } } if( AEApi.instance().partHelper().getCableRenderMode().opaqueFacades ) { final IFacadeContainer fc = this.getFacadeContainer(); for( final AEPartLocation side : AEPartLocation.SIDE_LOCATIONS ) { final IFacadePart p = fc.getFacade( side ); if( p != null ) { final List boxes = new LinkedList(); final IPartCollisionHelper bch = new BusCollisionHelper( boxes, side, null, true ); p.getBoxes( bch, null ); for( AxisAlignedBB bb : boxes ) { bb = bb.expand( 0.01, 0.01, 0.01 ); if( bb.isVecInside( pos ) ) { return new SelectedPart( p, side ); } } } } } return new SelectedPart(); } @Override public void markForSave() { this.tcb.markForSave(); } @Override public void partChanged() { if( this.getCenter() == null ) { final List facades = new LinkedList(); final IFacadeContainer fc = this.getFacadeContainer(); for( final AEPartLocation d : AEPartLocation.SIDE_LOCATIONS ) { final IFacadePart fp = fc.getFacade( d ); if( fp != null ) { facades.add( fp.getItemStack() ); fc.removeFacade( this.tcb, d ); } } if( !facades.isEmpty() ) { final TileEntity te = this.tcb.getTile(); Platform.spawnDrops( te.getWorld(), te.getPos(), facades ); } } this.tcb.partChanged(); } @Override public boolean hasRedstone( final AEPartLocation side ) { if( this.hasRedstone == YesNo.UNDECIDED ) { this.updateRedstone(); } return this.hasRedstone == YesNo.YES; } @Override public boolean isEmpty() { final IFacadeContainer fc = this.getFacadeContainer(); for( final AEPartLocation s : AEPartLocation.values() ) { final IPart part = this.getPart( s ); if( part != null ) { return false; } if( s != AEPartLocation.INTERNAL ) { final IFacadePart fp = fc.getFacade( s ); if( fp != null ) { return false; } } } return true; } @Override public Set getLayerFlags() { return this.myLayerFlags; } @Override public void cleanup() { this.tcb.cleanup(); } @Override public void notifyNeighbors() { this.tcb.notifyNeighbors(); } @Override public boolean isInWorld() { return this.inWorld; } private void updateRedstone() { final TileEntity te = this.getTile(); this.hasRedstone = te.getWorld().isBlockIndirectlyGettingPowered( te.getPos() ) != 0 ? YesNo.YES : YesNo.NO; } private void updateDynamicRender() { this.requiresDynamicRender = false; for( final AEPartLocation s : AEPartLocation.SIDE_LOCATIONS ) { final IPart p = this.getPart( s ); if( p != null ) { this.setRequiresDynamicRender( this.isRequiresDynamicRender() || p.requireDynamicRender() ); } } } /** * use for FMP */ public void updateConnections() { if( this.getCenter() != null ) { final EnumSet sides = EnumSet.allOf( EnumFacing.class ); for( final EnumFacing s : EnumFacing.VALUES ) { if( this.getPart( s ) != null || this.isBlocked( s ) ) { sides.remove( s ); } } this.getCenter().setValidSides( sides ); final IGridNode n = this.getCenter().getGridNode(); if( n != null ) { n.updateState(); } } } public void addToWorld() { if( this.inWorld ) { return; } this.inWorld = true; IS_LOADING.set( true ); final TileEntity te = this.getTile(); // start with the center, then install the side parts into the grid. for( int x = 6; x >= 0; x-- ) { final AEPartLocation s = AEPartLocation.fromOrdinal( x ); final IPart part = this.getPart( s ); if( part != null ) { part.setPartHostInfo( s, this, te ); part.addToWorld(); if( s != AEPartLocation.INTERNAL ) { final IGridNode sn = part.getGridNode(); if( sn != null ) { // this is a really stupid if statement, why was this // here? // if ( !sn.getConnections().iterator().hasNext() ) final IPart center = this.getPart( AEPartLocation.INTERNAL ); if( center != null ) { final IGridNode cn = center.getGridNode(); if( cn != null ) { try { AEApi.instance().createGridConnection( cn, sn ); } catch( final FailedConnection e ) { // ekk } } } } } } } this.partChanged(); IS_LOADING.set( false ); } public void removeFromWorld() { if( !this.inWorld ) { return; } this.inWorld = false; for( final AEPartLocation s : AEPartLocation.values() ) { final IPart part = this.getPart( s ); if( part != null ) { part.removeFromWorld(); } } this.partChanged(); } @Override public IGridNode getGridNode( final AEPartLocation side ) { final IPart part = this.getPart( side ); if( part != null ) { final IGridNode n = part.getExternalFacingNode(); if( n != null ) { return n; } } if( this.getCenter() != null ) { return this.getCenter().getGridNode(); } return null; } @Override public AECableType getCableConnectionType( final AEPartLocation dir ) { final IPart part = this.getPart( dir ); if( part instanceof IGridHost ) { final AECableType t = ( (IGridHost) part ).getCableConnectionType( dir ); if( t != null && t != AECableType.NONE ) { return t; } } if( this.getCenter() != null ) { final IPartCable c = this.getCenter(); return c.getCableConnectionType(); } return AECableType.NONE; } @Override public float getCableConnectionLength( AECableType cable ) { return getPart( AEPartLocation.INTERNAL ) instanceof IPartCable ? getPart( AEPartLocation.INTERNAL ).getCableConnectionLength( cable ) : -1; } @Override public void securityBreak() { for( final AEPartLocation d : AEPartLocation.values() ) { final IPart p = this.getPart( d ); if( p instanceof IGridHost ) { ( (IGridHost) p ).securityBreak(); } } } public Iterable getSelectedBoundingBoxesFromPool( final boolean ignoreConnections, final boolean includeFacades, final Entity e, final boolean visual ) { final List boxes = new LinkedList(); final IFacadeContainer fc = this.getFacadeContainer(); for( final AEPartLocation s : AEPartLocation.values() ) { final IPartCollisionHelper bch = new BusCollisionHelper( boxes, s, e, visual ); final IPart part = this.getPart( s ); if( part != null ) { if( ignoreConnections && part instanceof IPartCable ) { bch.addBox( 6.0, 6.0, 6.0, 10.0, 10.0, 10.0 ); } else { part.getBoxes( bch ); } } if( AEApi.instance().partHelper().getCableRenderMode().opaqueFacades || !visual ) { if( includeFacades && s != null && s != AEPartLocation.INTERNAL ) { final IFacadePart fp = fc.getFacade( s ); if( fp != null ) { fp.getBoxes( bch, e ); } } } } return boxes; } @Override public int isProvidingStrongPower( final EnumFacing side ) { final IPart part = this.getPart( side ); return part != null ? part.isProvidingStrongPower() : 0; } @Override public int isProvidingWeakPower( final EnumFacing side ) { final IPart part = this.getPart( side ); return part != null ? part.isProvidingWeakPower() : 0; } @Override public boolean canConnectRedstone( final EnumSet enumSet ) { for( final EnumFacing dir : enumSet ) { final IPart part = this.getPart( dir ); if( part != null && part.canConnectRedstone() ) { return true; } } return false; } @Override public void onEntityCollision( final Entity entity ) { for( final AEPartLocation s : AEPartLocation.values() ) { final IPart part = this.getPart( s ); if( part != null ) { part.onEntityCollision( entity ); } } } @Override public boolean activate( final EntityPlayer player, final EnumHand hand, final Vec3d pos ) { final SelectedPart p = this.selectPart( pos ); if( p != null && p.part != null ) { return p.part.onActivate( player, hand, pos ); } return false; } @Override public void onNeighborChanged() { this.hasRedstone = YesNo.UNDECIDED; for( final AEPartLocation s : AEPartLocation.values() ) { final IPart part = this.getPart( s ); if( part != null ) { part.onNeighborChanged(); } } } @Override public boolean isSolidOnSide( final EnumFacing side ) { if( side == null ) { return false; } // facades are solid.. final IFacadePart fp = this.getFacadeContainer().getFacade( AEPartLocation.fromFacing( side ) ); if( fp != null ) { return true; } // buses can be too. final IPart part = this.getPart( side ); return part != null && part.isSolid(); } @Override public boolean isLadder( final EntityLivingBase entity ) { for( final AEPartLocation side : AEPartLocation.values() ) { final IPart p = this.getPart( side ); if( p != null ) { if( p.isLadder( entity ) ) { return true; } } } return false; } @Override public void randomDisplayTick( final World world, final BlockPos pos, final Random r ) { for( final AEPartLocation side : AEPartLocation.values() ) { final IPart p = this.getPart( side ); if( p != null ) { p.randomDisplayTick( world, pos, r ); } } } @Override public int getLightValue() { int light = 0; for( final AEPartLocation d : AEPartLocation.values() ) { final IPart p = this.getPart( d ); if( p != null ) { light = Math.max( p.getLightLevel(), light ); } } return light; } public void writeToStream( final ByteBuf data ) throws IOException { int sides = 0; for( int x = 0; x < 7; x++ ) { final IPart p = this.getPart( AEPartLocation.fromOrdinal( x ) ); if( p != null ) { sides |= ( 1 << x ); } } data.writeByte( (byte) sides ); for( int x = 0; x < 7; x++ ) { final IPart p = this.getPart( AEPartLocation.fromOrdinal( x ) ); if( p != null ) { final ItemStack is = p.getItemStack( PartItemStack.NETWORK ); data.writeShort( Item.getIdFromItem( is.getItem() ) ); data.writeShort( is.getItemDamage() ); p.writeToStream( data ); } } this.getFacadeContainer().writeToStream( data ); } public boolean readFromStream( final ByteBuf data ) throws IOException { final byte sides = data.readByte(); boolean updateBlock = false; for( int x = 0; x < 7; x++ ) { AEPartLocation side = AEPartLocation.fromOrdinal( x ); if( ( ( sides & ( 1 << x ) ) == ( 1 << x ) ) ) { IPart p = this.getPart( side ); final short itemID = data.readShort(); final short dmgValue = data.readShort(); final Item myItem = Item.getItemById( itemID ); final ItemStack current = p != null ? p.getItemStack( PartItemStack.NETWORK ) : null; if( current != null && current.getItem() == myItem && current.getItemDamage() == dmgValue ) { if( p.readFromStream( data ) ) { updateBlock = true; } } else { this.removePart( side, false ); side = this.addPart( new ItemStack( myItem, 1, dmgValue ), side, null, null ); if( side != null ) { p = this.getPart( side ); p.readFromStream( data ); } else { throw new IllegalStateException( "Invalid Stream For CableBus Container." ); } } } else if( this.getPart( side ) != null ) { this.removePart( side, false ); } } if( this.getFacadeContainer().readFromStream( data ) ) { return true; } return updateBlock; } public void writeToNBT( final NBTTagCompound data ) { data.setInteger( "hasRedstone", this.hasRedstone.ordinal() ); final IFacadeContainer fc = this.getFacadeContainer(); for( final AEPartLocation s : AEPartLocation.values() ) { fc.writeToNBT( data ); final IPart part = this.getPart( s ); if( part != null ) { final NBTTagCompound def = new NBTTagCompound(); part.getItemStack( PartItemStack.WORLD ).writeToNBT( def ); final NBTTagCompound extra = new NBTTagCompound(); part.writeToNBT( extra ); data.setTag( "def:" + this.getSide( part ).ordinal(), def ); data.setTag( "extra:" + this.getSide( part ).ordinal(), extra ); } } } private AEPartLocation getSide( final IPart part ) { if( this.getCenter() == part ) { return AEPartLocation.INTERNAL; } else { for( final AEPartLocation side : AEPartLocation.SIDE_LOCATIONS ) { if( this.getSide( side ) == part ) { return side; } } } throw new IllegalStateException( "Uhh Bad Part (" + part + ") on Side." ); } public void readFromNBT( final NBTTagCompound data ) { if( data.hasKey( "hasRedstone" ) ) { this.hasRedstone = YesNo.values()[data.getInteger( "hasRedstone" )]; } for( int x = 0; x < 7; x++ ) { AEPartLocation side = AEPartLocation.fromOrdinal( x ); final NBTTagCompound def = data.getCompoundTag( "def:" + side.ordinal() ); final NBTTagCompound extra = data.getCompoundTag( "extra:" + side.ordinal() ); if( def != null && extra != null ) { IPart p = this.getPart( side ); final ItemStack iss = ItemStack.loadItemStackFromNBT( def ); if( iss == null ) { continue; } final ItemStack current = p == null ? null : p.getItemStack( PartItemStack.WORLD ); if( Platform.itemComparisons().isEqualItemType( iss, current ) ) { p.readFromNBT( extra ); } else { this.removePart( side, true ); side = this.addPart( iss, side, null, null ); if( side != null ) { p = this.getPart( side ); p.readFromNBT( extra ); } else { AELog.warn( "Invalid NBT For CableBus Container: " + iss.getItem().getClass().getName() + " is not a valid part; it was ignored." ); } } } else { this.removePart( side, false ); } } this.getFacadeContainer().readFromNBT( data ); } public List getDrops( final List drops ) { for( final AEPartLocation s : AEPartLocation.values() ) { final IPart part = this.getPart( s ); if( part != null ) { drops.add( part.getItemStack( PartItemStack.BREAK ) ); part.getDrops( drops, false ); } if( s != AEPartLocation.INTERNAL ) { final IFacadePart fp = this.getFacadeContainer().getFacade( s ); if( fp != null ) { drops.add( fp.getItemStack() ); } } } return drops; } public List getNoDrops( final List drops ) { for( final AEPartLocation s : AEPartLocation.values() ) { final IPart part = this.getPart( s ); if( part != null ) { part.getDrops( drops, false ); } } return drops; } @Override public boolean recolourBlock( final EnumFacing side, final AEColor colour, final EntityPlayer who ) { final IPart cable = this.getPart( AEPartLocation.INTERNAL ); if( cable != null ) { final IPartCable pc = (IPartCable) cable; return pc.changeColor( colour, who ); } return false; } public boolean isRequiresDynamicRender() { return this.requiresDynamicRender; } private void setRequiresDynamicRender( final boolean requiresDynamicRender ) { this.requiresDynamicRender = requiresDynamicRender; } @Override public CableBusRenderState getRenderState() { PartCable cable = (PartCable) getCenter(); CableBusRenderState renderState = new CableBusRenderState(); if( cable != null ) { renderState.setCableColor( cable.getCableColor() ); renderState.setCableType( cable.getCableConnectionType() ); renderState.setCoreType( CableCoreType.fromCableType( cable.getCableConnectionType() ) ); // Check each outgoing connection for the desired characteristics for( EnumFacing facing : EnumFacing.values() ) { // Is there a connection? if( !cable.isConnected( facing ) ) { continue; } // If there is one, check out which type it has, but default to this cable's type AECableType connectionType = cable.getCableConnectionType(); // Only use the incoming cable-type of the adjacent block, if it's not a cable bus itself // Dense cables however also respect the adjacent cable-type since their outgoing connection // point would look too big for other cable types BlockPos adjacentPos = this.getTile().getPos().offset( facing ); TileEntity adjacentTe = this.getTile().getWorld().getTileEntity( adjacentPos ); if( adjacentTe instanceof IGridHost ) { if( !( adjacentTe instanceof IPartHost ) || cable.getCableConnectionType() == AECableType.DENSE ) { IGridHost gridHost = (IGridHost) adjacentTe; connectionType = gridHost.getCableConnectionType( AEPartLocation.fromFacing( facing.getOpposite() ) ); } } // Check if the adjacent TE is a cable bus or not if( adjacentTe instanceof IPartHost ) { renderState.getCableBusAdjacent().add( facing ); } renderState.getConnectionTypes().put( facing, connectionType ); } // Collect the number of channels used per side // We have to do this even for non-smart cables since a glass cable can display a connection as smart if the adjacent tile requires it for( EnumFacing facing : EnumFacing.values() ) { int channels = cable.getChannelsOnSide( facing ); renderState.getChannelsOnSide().put( facing, channels ); } } // Determine attachments and facades for( EnumFacing facing : EnumFacing.values() ) { FacadeRenderState facadeState = getFacadeRenderState( facing ); if ( facadeState != null ) { renderState.getFacades().put( facing, facadeState ); } IPart part = getPart( facing ); if( part == null ) { continue; } // This will add the part's bounding boxes to the render state, which is required for facades AEPartLocation loc = AEPartLocation.fromFacing( facing ); IPartCollisionHelper bch = new BusCollisionHelper( renderState.getBoundingBoxes(), loc, null, true ); part.getBoxes( bch ); if( part instanceof IGridHost ) { // Some attachments want a thicker cable than glass, account for that IGridHost gridHost = (IGridHost) part; AECableType desiredType = gridHost.getCableConnectionType( AEPartLocation.INTERNAL ); if( renderState.getCoreType() == CableCoreType.GLASS && ( desiredType == AECableType.SMART || desiredType == AECableType.COVERED ) ) { renderState.setCoreType( CableCoreType.COVERED ); } int length = (int) part.getCableConnectionLength( null ); if( length > 0 && length <= 8 ) { renderState.getAttachmentConnections().put( facing, length ); } } renderState.getAttachments().put( facing, part.getStaticModels() ); } return renderState; } private FacadeRenderState getFacadeRenderState( EnumFacing side ) { // Store the "masqueraded" itemstack for the given side, if there is a facade IFacadePart facade = getFacade( side.ordinal() ); if( facade != null ) { ItemStack textureItem = facade.getTextureItem(); IBlockState blockState = facade.getBlockState(); if( blockState != null && textureItem != null ) { EnumSet openFaces = calculateFaceOpenFaces( side ); return new FacadeRenderState( blockState, openFaces, textureItem ); } } return null; } private EnumSet calculateFaceOpenFaces( EnumFacing side ) { final EnumSet out = EnumSet.of( side, side.getOpposite() ); final IFacadePart facade = getFacade( side.ordinal() ); IBlockAccess blockAccess = getTile().getWorld(); BlockPos pos = getTile().getPos(); for( final EnumFacing it : EnumFacing.values() ) { if( !out.contains( it ) && this.hasAlphaDiff( blockAccess.getTileEntity( pos.offset( it ) ), side, facade ) ) { out.add( it ); } } if( out.contains( EnumFacing.UP ) && ( side.getFrontOffsetX() != 0 || side.getFrontOffsetZ() != 0 ) ) { final IFacadePart fp = getFacade( EnumFacing.UP.ordinal() ); if( fp != null && ( fp.isTransparent() == facade.isTransparent() ) ) { out.remove( EnumFacing.UP ); } } if( out.contains( EnumFacing.DOWN ) && ( side.getFrontOffsetX() != 0 || side.getFrontOffsetZ() != 0 ) ) { final IFacadePart fp = getFacade( EnumFacing.DOWN.ordinal() ); if( fp != null && ( fp.isTransparent() == facade.isTransparent() ) ) { out.remove( EnumFacing.DOWN ); } } if( out.contains( EnumFacing.SOUTH ) && ( side.getFrontOffsetX() != 0 ) ) { final IFacadePart fp = getFacade( EnumFacing.SOUTH.ordinal() ); if( fp != null && ( fp.isTransparent() == facade.isTransparent() ) ) { out.remove( EnumFacing.SOUTH ); } } if( out.contains( EnumFacing.NORTH ) && ( side.getFrontOffsetX() != 0 ) ) { final IFacadePart fp = getFacade( EnumFacing.NORTH.ordinal() ); if( fp != null && ( fp.isTransparent() == facade.isTransparent() ) ) { out.remove( EnumFacing.NORTH ); } } return out; } private boolean hasAlphaDiff( final TileEntity tileEntity, final EnumFacing side, final IFacadePart facade ) { if( tileEntity instanceof IPartHost ) { final IPartHost ph = (IPartHost) tileEntity; final IFacadePart fp = ph.getFacadeContainer().getFacade( AEPartLocation.fromFacing( side ) ); return fp == null || ( fp.isTransparent() != facade.isTransparent() ); } return true; } }