package crazypants.enderio.conduit; import java.util.ArrayList; import java.util.Collection; import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Set; import li.cil.oc.api.network.Message; import li.cil.oc.api.network.Node; import li.cil.oc.api.network.*; import mekanism.api.gas.Gas; import mekanism.api.gas.GasStack; import mods.immibis.microblocks.api.EnumPartClass; import mods.immibis.microblocks.api.EnumPosition; import mods.immibis.microblocks.api.IMicroblockCoverSystem; import mods.immibis.microblocks.api.IMicroblockSystem; import mods.immibis.microblocks.api.MicroblockAPIUtils; import mods.immibis.microblocks.api.Part; import mods.immibis.microblocks.api.PartType; import net.minecraft.block.Block; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.network.NetworkManager; import net.minecraft.network.Packet; import net.minecraft.network.play.server.S35PacketUpdateTileEntity; import net.minecraft.tileentity.TileEntity; 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 appeng.api.networking.IGridNode; import appeng.api.util.AECableType; import com.enderio.core.client.render.BoundingBox; import cpw.mods.fml.common.Optional.Method; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import crazypants.enderio.EnderIO; import crazypants.enderio.TileEntityEio; import crazypants.enderio.conduit.facade.ItemConduitFacade.FacadeType; import crazypants.enderio.conduit.gas.IGasConduit; import crazypants.enderio.conduit.geom.CollidableCache; import crazypants.enderio.conduit.geom.CollidableComponent; import crazypants.enderio.conduit.geom.ConduitConnectorType; import crazypants.enderio.conduit.geom.ConduitGeometryUtil; import crazypants.enderio.conduit.geom.Offset; import crazypants.enderio.conduit.geom.Offsets; import crazypants.enderio.conduit.item.IItemConduit; import crazypants.enderio.conduit.liquid.ILiquidConduit; import crazypants.enderio.conduit.me.IMEConduit; import crazypants.enderio.conduit.oc.IOCConduit; import crazypants.enderio.conduit.power.IPowerConduit; import crazypants.enderio.conduit.redstone.InsulatedRedstoneConduit; import crazypants.enderio.config.Config; public class TileConduitBundle extends TileEntityEio implements IConduitBundle { public static final short NBT_VERSION = 1; private final List<IConduit> conduits = new ArrayList<IConduit>(); private Block facadeId = null; private int facadeMeta = 0; private FacadeType facadeType = FacadeType.BASIC; private boolean facadeChanged; private final List<CollidableComponent> cachedCollidables = new ArrayList<CollidableComponent>(); private final List<CollidableComponent> cachedConnectors = new ArrayList<CollidableComponent>(); private boolean conduitsDirty = true; private boolean collidablesDirty = true; private boolean connectorsDirty = true; private boolean clientUpdated = false; private int lightOpacity = -1; @SideOnly(Side.CLIENT) private FacadeRenderState facadeRenderAs; private ConduitDisplayMode lastMode = ConduitDisplayMode.ALL; Object covers; public TileConduitBundle() { this.blockType = EnderIO.blockConduitBundle; initMicroblocks(); } @Override public void dirty() { conduitsDirty = true; collidablesDirty = true; } @Override public boolean shouldRenderInPass(int arg0) { if(facadeId != null && facadeId.isOpaqueCube() && !ConduitUtil.isFacadeHidden(this, EnderIO.proxy.getClientPlayer())) { return false; } return super.shouldRenderInPass(arg0); } @Override public void writeCustomNBT(NBTTagCompound nbtRoot) { NBTTagList conduitTags = new NBTTagList(); for (IConduit conduit : conduits) { NBTTagCompound conduitRoot = new NBTTagCompound(); ConduitUtil.writeToNBT(conduit, conduitRoot); conduitTags.appendTag(conduitRoot); } nbtRoot.setTag("conduits", conduitTags); if(facadeId != null) { nbtRoot.setString("facadeId", Block.blockRegistry.getNameForObject(facadeId)); nbtRoot.setString("facadeType", facadeType.name()); } else { nbtRoot.setString("facadeId", "null"); } nbtRoot.setInteger("facadeMeta", facadeMeta); nbtRoot.setShort("nbtVersion", NBT_VERSION); if (MicroblocksUtil.supportMicroblocks()) { writeMicroblocksToNBT(nbtRoot); } } @Override public void readCustomNBT(NBTTagCompound nbtRoot) { short nbtVersion = nbtRoot.getShort("nbtVersion"); conduits.clear(); cachedCollidables.clear(); NBTTagList conduitTags = (NBTTagList) nbtRoot.getTag("conduits"); if(conduitTags != null) { for (int i = 0; i < conduitTags.tagCount(); i++) { NBTTagCompound conduitTag = conduitTags.getCompoundTagAt(i); IConduit conduit = ConduitUtil.readConduitFromNBT(conduitTag, nbtVersion); if(conduit != null) { conduit.setBundle(this); conduits.add(conduit); } } } String fs = nbtRoot.getString("facadeId"); if(fs == null || "null".equals(fs)) { facadeId = null; facadeType = FacadeType.BASIC; } else { facadeId = Block.getBlockFromName(fs); if(nbtRoot.hasKey("facadeType")) { // backwards compat, never true in freshly placed bundles facadeType = FacadeType.valueOf(nbtRoot.getString("facadeType")); } } facadeMeta = nbtRoot.getInteger("facadeMeta"); if(worldObj != null && worldObj.isRemote) { clientUpdated = true; } if (MicroblocksUtil.supportMicroblocks()) { readMicroblocksFromNBT(nbtRoot); } } @Override public boolean hasFacade() { return facadeId != null; } @Override public void setFacadeId(Block blockID, boolean triggerUpdate) { this.facadeId = blockID; if(triggerUpdate) { facadeChanged = true; } } @Override public void setFacadeId(Block blockID) { setFacadeId(blockID, true); } @Override public Block getFacadeId() { return facadeId; } @Override public void setFacadeMetadata(int meta) { facadeMeta = meta; } @Override public void setFacadeType(FacadeType type) { facadeType = type; } @Override public int getFacadeMetadata() { return facadeMeta; } @Override public FacadeType getFacadeType() { return facadeType; } @Override @SideOnly(Side.CLIENT) public FacadeRenderState getFacadeRenderedAs() { if(facadeRenderAs == null) { facadeRenderAs = FacadeRenderState.NONE; } return facadeRenderAs; } @Override @SideOnly(Side.CLIENT) public void setFacadeRenderAs(FacadeRenderState state) { this.facadeRenderAs = state; } @Override public int getLightOpacity() { if((worldObj != null && !worldObj.isRemote) || lightOpacity == -1) { return hasFacade() ? facadeId.getLightOpacity() : 0; } return lightOpacity; } @Override public void setLightOpacity(int opacity) { lightOpacity = opacity; } @Override public void onChunkUnload() { for (IConduit conduit : conduits) { conduit.onChunkUnload(worldObj); } } @Override public void doUpdate() { for (IConduit conduit : conduits) { conduit.updateEntity(worldObj); } if(conduitsDirty) { doConduitsDirty(); } if(facadeChanged) { doFacadeChanged(); } //client side only, check for changes in rendering of the bundle if(worldObj.isRemote) { updateEntityClient(); } } private void doConduitsDirty() { if(!worldObj.isRemote) { worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); markDirty(); } conduitsDirty = false; } private void doFacadeChanged() { //force re-calc of lighting for both client and server ConduitUtil.forceSkylightRecalculation(worldObj, xCoord, yCoord, zCoord); //worldObj.updateAllLightTypes(xCoord, yCoord, zCoord); worldObj.func_147451_t(xCoord, yCoord, zCoord); worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); worldObj.notifyBlocksOfNeighborChange(xCoord, yCoord, zCoord, EnderIO.blockConduitBundle); facadeChanged = false; } private void updateEntityClient() { boolean markForUpdate = false; if(clientUpdated) { //TODO: This is not the correct solution here but just marking the block for a render update server side //seems to get out of sync with the client sometimes so connections are not rendered correctly markForUpdate = true; clientUpdated = false; } FacadeRenderState curRS = getFacadeRenderedAs(); FacadeRenderState rs = ConduitUtil.getRequiredFacadeRenderState(this, EnderIO.proxy.getClientPlayer()); if(Config.updateLightingWhenHidingFacades) { int curLO = getLightOpacity(); int shouldBeLO = rs == FacadeRenderState.FULL ? 255 : 0; if(curLO != shouldBeLO) { setLightOpacity(shouldBeLO); //worldObj.updateAllLightTypes(xCoord, yCoord, zCoord); worldObj.func_147451_t(xCoord, yCoord, zCoord); } } if(curRS != rs) { setFacadeRenderAs(rs); if(!ConduitUtil.forceSkylightRecalculation(worldObj, xCoord, yCoord, zCoord)) { markForUpdate = true; } } else { //can do the else as only need to update once ConduitDisplayMode curMode = ConduitDisplayMode.getDisplayMode(EnderIO.proxy.getClientPlayer().getCurrentEquippedItem()); if(curMode != lastMode) { markForUpdate = true; lastMode = curMode; } } if(markForUpdate) { worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); } } @Override public void onNeighborBlockChange(Block blockId) { boolean needsUpdate = false; for (IConduit conduit : conduits) { needsUpdate |= conduit.onNeighborBlockChange(blockId); } if(needsUpdate) { dirty(); } } @Override public void onNeighborChange(IBlockAccess world, int x, int y, int z, int tileX, int tileY, int tileZ) { boolean needsUpdate = false; for (IConduit conduit : conduits) { needsUpdate |= conduit.onNeighborChange(world, x, y, z, tileX, tileY, tileZ); } if(needsUpdate) { dirty(); } } @Override public TileConduitBundle getEntity() { return this; } @Override public boolean hasType(Class<? extends IConduit> type) { return getConduit(type) != null; } @SuppressWarnings("unchecked") @Override public <T extends IConduit> T getConduit(Class<T> type) { if(type == null) { return null; } for (IConduit conduit : conduits) { if(type.isInstance(conduit)) { return (T) conduit; } } return null; } @Override public void addConduit(IConduit conduit) { if(worldObj.isRemote) { return; } conduits.add(conduit); conduit.setBundle(this); conduit.onAddedToBundle(); dirty(); } @Override public void removeConduit(IConduit conduit) { if(conduit != null) { removeConduit(conduit, true); } } public void removeConduit(IConduit conduit, boolean notify) { if(worldObj.isRemote) { return; } conduit.onRemovedFromBundle(); conduits.remove(conduit); conduit.setBundle(null); if(notify) { dirty(); } } @Override public void onBlockRemoved() { if(worldObj.isRemote) { return; } List<IConduit> copy = new ArrayList<IConduit>(conduits); for (IConduit con : copy) { removeConduit(con, false); } dirty(); } @Override public Collection<IConduit> getConduits() { return conduits; } @Override public Set<ForgeDirection> getConnections(Class<? extends IConduit> type) { IConduit con = getConduit(type); if(con != null) { return con.getConduitConnections(); } return null; } @Override public boolean containsConnection(Class<? extends IConduit> type, ForgeDirection dir) { IConduit con = getConduit(type); if(con != null) { return con.containsConduitConnection(dir); } return false; } @Override public boolean containsConnection(ForgeDirection dir) { for (IConduit con : conduits) { if(con.containsConduitConnection(dir)) { return true; } } return false; } @Override public Set<ForgeDirection> getAllConnections() { EnumSet<ForgeDirection> result = EnumSet.noneOf(ForgeDirection.class); for (IConduit con : conduits) { result.addAll(con.getConduitConnections()); } return result; } // Geometry @Override public Offset getOffset(Class<? extends IConduit> type, ForgeDirection dir) { if(getConnectionCount(dir) < 2) { return Offset.NONE; } return Offsets.get(type, dir); } @Override public List<CollidableComponent> getCollidableComponents() { for (IConduit con : conduits) { collidablesDirty = collidablesDirty || con.haveCollidablesChangedSinceLastCall(); } if(collidablesDirty) { connectorsDirty = true; } if(!collidablesDirty && !cachedCollidables.isEmpty()) { return cachedCollidables; } cachedCollidables.clear(); for (IConduit conduit : conduits) { cachedCollidables.addAll(conduit.getCollidableComponents()); } addConnectors(cachedCollidables); collidablesDirty = false; return cachedCollidables; } @Override public List<CollidableComponent> getConnectors() { List<CollidableComponent> result = new ArrayList<CollidableComponent>(); addConnectors(result); return result; } private void addConnectors(List<CollidableComponent> result) { if(conduits.isEmpty()) { return; } for (IConduit con : conduits) { boolean b = con.haveCollidablesChangedSinceLastCall(); collidablesDirty = collidablesDirty || b; connectorsDirty = connectorsDirty || b; } if(!connectorsDirty && !cachedConnectors.isEmpty()) { result.addAll(cachedConnectors); return; } cachedConnectors.clear(); // TODO: What an unholly mess! (and it doesn't even work correctly...) List<CollidableComponent> coreBounds = new ArrayList<CollidableComponent>(); for (IConduit con : conduits) { addConduitCores(coreBounds, con); } cachedConnectors.addAll(coreBounds); result.addAll(coreBounds); // 1st algorithm List<CollidableComponent> conduitsBounds = new ArrayList<CollidableComponent>(); for (IConduit con : conduits) { conduitsBounds.addAll(con.getCollidableComponents()); addConduitCores(conduitsBounds, con); } Set<Class<IConduit>> collidingTypes = new HashSet<Class<IConduit>>(); for (CollidableComponent conCC : conduitsBounds) { for (CollidableComponent innerCC : conduitsBounds) { if(!InsulatedRedstoneConduit.COLOR_CONTROLLER_ID.equals(innerCC.data) && !InsulatedRedstoneConduit.COLOR_CONTROLLER_ID.equals(conCC.data) && conCC != innerCC && conCC.bound.intersects(innerCC.bound)) { collidingTypes.add((Class<IConduit>) conCC.conduitType); } } } //TODO: Remove the core geometries covered up by this as no point in rendering these if(!collidingTypes.isEmpty()) { List<CollidableComponent> colCores = new ArrayList<CollidableComponent>(); for (Class<IConduit> c : collidingTypes) { IConduit con = getConduit(c); if(con != null) { addConduitCores(colCores, con); } } BoundingBox bb = null; for (CollidableComponent cBB : colCores) { if(bb == null) { bb = cBB.bound; } else { bb = bb.expandBy(cBB.bound); } } if(bb != null) { bb = bb.scale(1.05, 1.05, 1.05); CollidableComponent cc = new CollidableComponent(null, bb, ForgeDirection.UNKNOWN, ConduitConnectorType.INTERNAL); result.add(cc); cachedConnectors.add(cc); } } //2nd algorithm for (IConduit con : conduits) { if(con.hasConnections()) { List<CollidableComponent> cores = new ArrayList<CollidableComponent>(); addConduitCores(cores, con); if(cores.size() > 1) { BoundingBox bb = cores.get(0).bound; float area = bb.getArea(); for (CollidableComponent cc : cores) { bb = bb.expandBy(cc.bound); } if(bb.getArea() > area * 1.5f) { bb = bb.scale(1.05, 1.05, 1.05); CollidableComponent cc = new CollidableComponent(null, bb, ForgeDirection.UNKNOWN, ConduitConnectorType.INTERNAL); result.add(cc); cachedConnectors.add(cc); } } } } // Merge all internal conduit connectors into one box BoundingBox conBB = null; for (int i = 0; i < result.size(); i++) { CollidableComponent cc = result.get(i); if (cc.conduitType == null && cc.data == ConduitConnectorType.INTERNAL) { conBB = conBB == null ? cc.bound : conBB.expandBy(cc.bound); result.remove(i); i--; cachedConnectors.remove(cc); } } if(conBB != null) { CollidableComponent cc = new CollidableComponent(null, conBB, ForgeDirection.UNKNOWN, ConduitConnectorType.INTERNAL); result.add(cc); cachedConnectors.add(cc); } // External Connectors EnumSet<ForgeDirection> externalDirs = EnumSet.noneOf(ForgeDirection.class); for (IConduit con : conduits) { Set<ForgeDirection> extCons = con.getExternalConnections(); if(extCons != null) { for (ForgeDirection dir : extCons) { if(con.getConnectionMode(dir) != ConnectionMode.DISABLED) { externalDirs.add(dir); } } } } for (ForgeDirection dir : externalDirs) { BoundingBox bb = ConduitGeometryUtil.instance.getExternalConnectorBoundingBox(dir); CollidableComponent cc = new CollidableComponent(null, bb, dir, ConduitConnectorType.EXTERNAL); result.add(cc); cachedConnectors.add(cc); } connectorsDirty = false; } private void addConduitCores(List<CollidableComponent> result, IConduit con) { CollidableCache cc = CollidableCache.instance; Class<? extends IConduit> type = con.getCollidableType(); if(con.hasConnections()) { for (ForgeDirection dir : con.getExternalConnections()) { result.addAll(cc.getCollidables(cc.createKey(type, getOffset(con.getBaseConduitType(), dir), ForgeDirection.UNKNOWN, false), con)); } for (ForgeDirection dir : con.getConduitConnections()) { result.addAll(cc.getCollidables(cc.createKey(type, getOffset(con.getBaseConduitType(), dir), ForgeDirection.UNKNOWN, false), con)); } } else { result.addAll(cc.getCollidables(cc.createKey(type, getOffset(con.getBaseConduitType(), ForgeDirection.UNKNOWN), ForgeDirection.UNKNOWN, false), con)); } } private int getConnectionCount(ForgeDirection dir) { if(dir == ForgeDirection.UNKNOWN) { return conduits.size(); } int result = 0; for (IConduit con : conduits) { if(con.containsConduitConnection(dir) || con.containsExternalConnection(dir)) { result++; } } return result; } // ------------ Power ----------------------------- @Override public int receiveEnergy(ForgeDirection from, int maxReceive, boolean simulate) { IPowerConduit pc = getConduit(IPowerConduit.class); if(pc != null) { return pc.receiveEnergy(from, maxReceive, simulate); } return 0; } @Override public int extractEnergy(ForgeDirection from, int maxExtract, boolean simulate) { IPowerConduit pc = getConduit(IPowerConduit.class); if(pc != null) { return pc.extractEnergy(from, maxExtract, simulate); } return 0; } @Override public boolean canConnectEnergy(ForgeDirection from) { IPowerConduit pc = getConduit(IPowerConduit.class); if(pc != null) { return pc.canConnectEnergy(from); } return false; } @Override public int getEnergyStored(ForgeDirection from) { IPowerConduit pc = getConduit(IPowerConduit.class); if(pc != null) { return pc.getEnergyStored(from); } return 0; } @Override public int getMaxEnergyStored(ForgeDirection from) { IPowerConduit pc = getConduit(IPowerConduit.class); if(pc != null) { return pc.getMaxEnergyStored(from); } return 0; } @Override public int getMaxEnergyRecieved(ForgeDirection dir) { IPowerConduit pc = getConduit(IPowerConduit.class); if(pc != null) { return pc.getMaxEnergyRecieved(dir); } return 0; } @Override public int getEnergyStored() { IPowerConduit pc = getConduit(IPowerConduit.class); if(pc != null) { return pc.getEnergyStored(); } return 0; } @Override public int getMaxEnergyStored() { IPowerConduit pc = getConduit(IPowerConduit.class); if(pc != null) { return pc.getMaxEnergyStored(); } return 0; } @Override public void setEnergyStored(int stored) { IPowerConduit pc = getConduit(IPowerConduit.class); if(pc != null) { pc.setEnergyStored(stored); } } //------- Liquids ----------------------------- @Override public int fill(ForgeDirection from, FluidStack resource, boolean doFill) { ILiquidConduit lc = getConduit(ILiquidConduit.class); if(lc != null) { return lc.fill(from, resource, doFill); } return 0; } @Override public FluidStack drain(ForgeDirection from, FluidStack resource, boolean doDrain) { ILiquidConduit lc = getConduit(ILiquidConduit.class); if(lc != null) { return lc.drain(from, resource, doDrain); } return null; } @Override public FluidStack drain(ForgeDirection from, int maxDrain, boolean doDrain) { ILiquidConduit lc = getConduit(ILiquidConduit.class); if(lc != null) { return lc.drain(from, maxDrain, doDrain); } return null; } @Override public boolean canFill(ForgeDirection from, Fluid fluid) { ILiquidConduit lc = getConduit(ILiquidConduit.class); if(lc != null) { return lc.canFill(from, fluid); } return false; } @Override public boolean canDrain(ForgeDirection from, Fluid fluid) { ILiquidConduit lc = getConduit(ILiquidConduit.class); if(lc != null) { return lc.canDrain(from, fluid); } return false; } @Override public FluidTankInfo[] getTankInfo(ForgeDirection from) { ILiquidConduit lc = getConduit(ILiquidConduit.class); if(lc != null) { return lc.getTankInfo(from); } return null; } // ---- TE Item Conduits @Override public ItemStack insertItem(ForgeDirection from, ItemStack item) { IItemConduit ic = getConduit(IItemConduit.class); if(ic != null) { return ic.insertItem(from, item); } return item; } // ---- Mekanism Gas Tubes @Override @Method(modid = "MekanismAPI|gas") public int receiveGas(ForgeDirection side, GasStack stack) { return receiveGas(side, stack, true); } @Override @Method(modid = "MekanismAPI|gas") public int receiveGas(ForgeDirection side, GasStack stack, boolean doTransfer) { IGasConduit gc = getConduit(IGasConduit.class); if(gc != null) { return gc.receiveGas(side, stack, doTransfer); } return 0; } @Override @Method(modid = "MekanismAPI|gas") public GasStack drawGas(ForgeDirection side, int amount) { return drawGas(side, amount, true); } @Override @Method(modid = "MekanismAPI|gas") public GasStack drawGas(ForgeDirection side, int amount, boolean doTransfer) { IGasConduit gc = getConduit(IGasConduit.class); if(gc != null) { return gc.drawGas(side, amount, doTransfer); } return null; } @Override @Method(modid = "MekanismAPI|gas") public boolean canReceiveGas(ForgeDirection side, Gas type) { IGasConduit gc = getConduit(IGasConduit.class); if(gc != null) { return gc.canReceiveGas(side, type); } return false; } @Override @Method(modid = "MekanismAPI|gas") public boolean canDrawGas(ForgeDirection side, Gas type) { IGasConduit gc = getConduit(IGasConduit.class); if(gc != null) { return gc.canDrawGas(side, type); } return false; } @Override public World getWorld() { return getWorldObj(); } private Object node; // IGridNode object, untyped to avoid crash w/o AE2 @Override @Method(modid = "appliedenergistics2") public IGridNode getGridNode(ForgeDirection dir) { if (dir == null || dir == ForgeDirection.UNKNOWN) { return (IGridNode) node; } else { IMEConduit cond = getConduit(IMEConduit.class); if (cond != null) { if (cond.getConnectionMode(dir.getOpposite()) == ConnectionMode.IN_OUT) { return (IGridNode) node; } else { return null; } } } return (IGridNode) node; } @SuppressWarnings("cast") @Override @Method(modid = "appliedenergistics2") public void setGridNode(Object node) { this.node = (IGridNode) node; } @Override @Method(modid = "appliedenergistics2") public AECableType getCableConnectionType(ForgeDirection dir) { IMEConduit cond = getConduit(IMEConduit.class); if (cond == null) { return AECableType.NONE; } else { return cond.isConnectedTo(dir) ? AECableType.SMART : AECableType.NONE; } } @Override @Method(modid = "appliedenergistics2") public void securityBreak() { } @Override public boolean displayPower() { return true; } // Immibis Microblocks private void initMicroblocks() { if (MicroblocksUtil.supportMicroblocks()) { createCovers(); } } @Method(modid = "ImmibisMicroblocks") private void createCovers() { IMicroblockSystem ims = MicroblockAPIUtils.getMicroblockSystem(); if (ims != null) { covers = ims.createMicroblockCoverSystem(this); } } @Override @Method(modid = "ImmibisMicroblocks") public boolean isPlacementBlocked(PartType<?> part, EnumPosition pos) { EnumPartClass type = part.getPartClass(); // Let's do some cheaper checks first if (type == EnumPartClass.Strip) { // No pillars if (pos == EnumPosition.PostX || pos == EnumPosition.PostY || pos == EnumPosition.PostZ) { return true; } } else if (part.getSize() < 0.25) { // Anything this small can never intersect conduits return false; } // Finally just check core BB intersections // Ignore anything that is not a core to allow blocking of connections else if (type == EnumPartClass.Panel) { List<CollidableComponent> boxes = getCollidableComponents(); BoundingBox bb = new BoundingBox(Part.getBoundingBoxFromPool(pos, part.getSize())); for (CollidableComponent c : boxes) { if (c.dir == ForgeDirection.UNKNOWN && c.bound.intersects(bb)) { return true; } } } return false; } @Override @Method(modid = "ImmibisMicroblocks") public IMicroblockCoverSystem getCoverSystem() { return (IMicroblockCoverSystem) covers; } @Method(modid = "ImmibisMicroblocks") private void writeMicroblocksToNBT(NBTTagCompound tag) { if (covers != null) { ((IMicroblockCoverSystem) covers).writeToNBT(tag); } } @Method(modid = "ImmibisMicroblocks") private void readMicroblocksFromNBT(NBTTagCompound tag) { if (covers != null) { ((IMicroblockCoverSystem) covers).readFromNBT(tag); } } @Override @Method(modid = "ImmibisMicroblocks") public Packet getDescriptionPacket() { if (covers == null) { return super.getDescriptionPacket(); } NBTTagCompound tag = new NBTTagCompound(); tag.setByteArray("C", ((IMicroblockCoverSystem) covers).writeDescriptionBytes()); writeCustomNBT(tag); return new S35PacketUpdateTileEntity(xCoord, yCoord, zCoord, 1, tag); } @Override @Method(modid = "ImmibisMicroblocks") public void onDataPacket(NetworkManager net, S35PacketUpdateTileEntity pkt) { super.onDataPacket(net, pkt); if (covers != null) { ((IMicroblockCoverSystem) covers).readDescriptionBytes(pkt.func_148857_g().getByteArray("C"), 0); } } @Override @Method(modid = "ImmibisMicroblocks") public void onMicroblocksChanged() { Set<ForgeDirection> needUpdates = EnumSet.allOf(ForgeDirection.class); needUpdates.remove(ForgeDirection.UNKNOWN); for (Part p : getCoverSystem().getAllParts()) { if (p.type.getPartClass() == EnumPartClass.Panel) { ForgeDirection dir = MicroblocksUtil.posToDir(p.pos); updateConnections(dir, true); needUpdates.remove(dir); } } for (ForgeDirection dir : needUpdates) { updateConnections(dir, false); } worldObj.notifyBlocksOfNeighborChange(xCoord, yCoord, zCoord, getBlockType()); updateBlock(); } @Method(modid = "ImmibisMicroblocks") private void updateConnections(ForgeDirection dir, boolean remove) { TileEntity neighbor = getLocation().getLocation(dir).getTileEntity(worldObj); IConduitBundle neighborBundle = (IConduitBundle) (neighbor instanceof IConduitBundle ? neighbor : null); for (IConduit c : getConduits()) { if (remove) { removeConnection(dir, c); } else if (neighborBundle != null) { addConnection(dir, c, neighborBundle.getConduit(c.getBaseConduitType())); } c.connectionsChanged(); } dir = dir.getOpposite(); if (neighbor instanceof IConduitBundle) { for (IConduit c : ((TileConduitBundle) neighbor).getConduits()) { if (remove) { removeConnection(dir, c); } else if (neighborBundle != null) { addConnection(dir, c, getConduit(c.getBaseConduitType())); } c.connectionsChanged(); } } } @Method(modid = "ImmibisMicroblocks") private void removeConnection(ForgeDirection dir, IConduit c) { if (c.getConduitConnections().contains(dir)) { c.conduitConnectionRemoved(dir); } } @Method(modid = "ImmibisMicroblocks") private void addConnection(ForgeDirection dir, IConduit c, IConduit connectingTo) { if (connectingTo != null) { if (!c.getConduitConnections().contains(dir) && connectingTo.canConnectToConduit(dir, c)) { c.conduitConnectionAdded(dir); } } } // OpenComputers @Override @Method(modid = "OpenComputersAPI|Network") public Node node() { IOCConduit cond = getConduit(IOCConduit.class); if (cond != null) { return cond.node(); } else { return null; } } @Override @Method(modid = "OpenComputersAPI|Network") public void onConnect(Node node) { IOCConduit cond = getConduit(IOCConduit.class); if (cond != null) { cond.onConnect(node); } } @Override @Method(modid = "OpenComputersAPI|Network") public void onDisconnect(Node node) { IOCConduit cond = getConduit(IOCConduit.class); if (cond != null) { cond.onDisconnect(node); } } @Override @Method(modid = "OpenComputersAPI|Network") public void onMessage(Message message) { IOCConduit cond = getConduit(IOCConduit.class); if (cond != null) { cond.onMessage(message); } } @Override @Method(modid = "OpenComputersAPI|Network") public Node sidedNode(ForgeDirection side) { IOCConduit cond = getConduit(IOCConduit.class); if (cond != null) { return cond.sidedNode(side); } else { return null; } } @Override @Method(modid = "OpenComputersAPI|Network") @SideOnly(Side.CLIENT) public boolean canConnect(ForgeDirection side) { IOCConduit cond = getConduit(IOCConduit.class); if (cond != null) { return cond.canConnect(side); } else { return false; } } @Override public void invalidate() { super.invalidate(); IOCConduit cond = getConduit(IOCConduit.class); if (cond != null) { cond.invalidate(); } } }