package resonantinduction.electrical.wire.flat; import java.util.Arrays; import net.minecraft.block.Block; import net.minecraft.client.renderer.RenderBlocks; 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.Icon; import net.minecraft.util.MovingObjectPosition; import net.minecraft.world.World; import net.minecraftforge.common.ForgeDirection; import org.lwjgl.opengl.GL11; import resonantinduction.core.Utility; import resonantinduction.electrical.wire.EnumWireMaterial; import resonantinduction.electrical.wire.PartAdvancedWire; import calclavia.lib.render.EnumColor; import codechicken.lib.colour.Colour; import codechicken.lib.colour.ColourRGBA; import codechicken.lib.data.MCDataInput; import codechicken.lib.data.MCDataOutput; import codechicken.lib.lighting.LazyLightMatrix; import codechicken.lib.raytracer.IndexedCuboid6; import codechicken.lib.render.CCRenderState; import codechicken.lib.render.TextureUtils; import codechicken.lib.vec.BlockCoord; import codechicken.lib.vec.Cuboid6; import codechicken.lib.vec.Rotation; import codechicken.lib.vec.Vector3; import codechicken.multipart.JNormalOcclusion; import codechicken.multipart.MultiPartRegistry; import codechicken.multipart.NormalOcclusionTest; import codechicken.multipart.PartMap; import codechicken.multipart.TFacePart; import codechicken.multipart.TMultiPart; import codechicken.multipart.TileMultipart; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; /** * This is the base class for all wire types. It can be used for any sub type, as it contains the * base calculations necessary to create a working wire. This calculates all possible connections to * sides, around corners, and inside corners, while checking for microblock obstructions. * * @author Modified by Calclavia, MrTJP */ public class PartFlatWire extends PartAdvancedWire implements TFacePart, JNormalOcclusion { public static Cuboid6[][] selectionBounds = new Cuboid6[3][6]; public static Cuboid6[][] occlusionBounds = new Cuboid6[3][6]; static { for (int t = 0; t < 3; t++) { // Subtract the box a little because we'd like things like posts to get first hit Cuboid6 selection = new Cuboid6(0, 0, 0, 1, (t + 2) / 16D, 1).expand(-0.005); Cuboid6 occlusion = new Cuboid6(2 / 8D, 0, 2 / 8D, 6 / 8D, (t + 2) / 16D, 6 / 8D); for (int s = 0; s < 6; s++) { selectionBounds[t][s] = selection.copy().apply(Rotation.sideRotations[s].at(Vector3.center)); occlusionBounds[t][s] = occlusion.copy().apply(Rotation.sideRotations[s].at(Vector3.center)); } } } public byte side; /** * A map of the corners. * * * Currently split into 4 nybbles (from lowest) 0 = Corner connections (this wire should connect * around a corner to something external) 1 = Straight connections (this wire should connect to * something external) 2 = Internal connections (this wire should connect to something internal) * 3 = Internal open connections (this wire is not blocked by a cover/edge part and *could* * connect through side) bit 16 = connection to the centerpart 5 = Render corner connections. * Like corner connections but set to low if the other wire part is smaller than this (they * render to us not us to them) */ public int connMap; public PartFlatWire() { } public PartFlatWire(int typeID) { this(EnumWireMaterial.values()[typeID]); } public PartFlatWire(EnumWireMaterial type) { material = type; } public void preparePlacement(int side, int meta) { this.side = (byte) (side ^ 1); this.setMaterial(meta); } /** PACKET and NBT Methods */ @Override public void load(NBTTagCompound tag) { super.load(tag); this.side = tag.getByte("side"); this.connMap = tag.getInteger("connMap"); } @Override public void save(NBTTagCompound tag) { super.save(tag); tag.setByte("side", this.side); tag.setInteger("connMap", this.connMap); } @Override public void readDesc(MCDataInput packet) { super.readDesc(packet); this.side = packet.readByte(); this.connMap = packet.readInt(); } @Override public void writeDesc(MCDataOutput packet) { super.writeDesc(packet); packet.writeByte(this.side); packet.writeInt(this.connMap); } @Override public void read(MCDataInput packet) { read(packet, packet.readUByte()); } @Override public void read(MCDataInput packet, int packetID) { if (packetID == 0) { this.connMap = packet.readInt(); tile().markRender(); } else { super.read(packet, packetID); } } public void sendConnUpdate() { tile().getWriteStream(this).writeByte(0).writeInt(this.connMap); } /* WORLD EVENTS */ @Override public void onRemoved() { super.onRemoved(); if (!world().isRemote) { for (int r = 0; r < 4; r++) { if (maskConnects(r)) { if ((connMap & 1 << r) != 0) { notifyCornerChange(r); } else if ((connMap & 0x10 << r) != 0) { notifyStraightChange(r); } } } } } @Override public void onChunkLoad() { if ((connMap & 0x80000000) != 0) // compat with converters, recalc connections { if (dropIfCantStay()) { return; } connMap = 0; updateInternalConnections(); if (updateOpenConnections()) { updateExternalConnections(); } tile().markDirty(); } this.recalculateConnections(); super.onChunkLoad(); } @Override public void onAdded() { super.onAdded(); if (!world().isRemote) { updateOpenConnections(); boolean changed = updateInternalConnections(); // don't use || because it's fail fast changed |= updateExternalConnections(); if (changed) { sendConnUpdate(); } this.recalculateConnections(); } } @Override public void onPartChanged(TMultiPart part) { if (!world().isRemote) { boolean changed = updateInternalConnections(); if (updateOpenConnections()) { changed |= updateExternalConnections(); } if (changed) { sendConnUpdate(); } this.recalculateConnections(); } super.onPartChanged(part); } @Override public void onNeighborChanged() { if (!world().isRemote) { if (dropIfCantStay()) { return; } if (updateExternalConnections()) { sendConnUpdate(); } this.recalculateConnections(); } super.onNeighborChanged(); } @Override public boolean activate(EntityPlayer player, MovingObjectPosition part, ItemStack item) { if (!world().isRemote) { System.out.println(this.getNetwork()); } if (item != null) { if (item.getItem().itemID == Block.lever.blockID) { TileMultipart tile = tile(); World w = world(); if (!w.isRemote) { PartFlatSwitchWire wire = (PartFlatSwitchWire) MultiPartRegistry.createPart("resonant_induction_flat_switch_wire", false); wire.copyFrom(this); if (tile.canReplacePart(this, wire)) { tile.remPart(this); TileMultipart.addPart(w, new BlockCoord(tile), wire); if (!player.capabilities.isCreativeMode) { player.inventory.decrStackSize(player.inventory.currentItem, 1); } } } return true; } } return super.activate(player, part, item); } @Override public void recalculateConnections() { this.updateOpenConnections(); boolean[] calculatedSides = new boolean[6]; /** Check external connections. */ for (byte r = 0; r < 4; r++) { if (maskOpen(r)) { int absDir = Rotation.rotateSide(this.side, r); // Check direct connection. if (this.setExternalConnection(r, absDir)) { calculatedSides[absDir] = true; } // Check Corner Connection BlockCoord cornerPos = new BlockCoord(tile()); cornerPos.offset(absDir); if (canConnectThroughCorner(cornerPos, absDir ^ 1, this.side)) { cornerPos.offset(this.side); TileMultipart tpCorner = Utility.getMultipartTile(world(), cornerPos); if (tpCorner != null) { TMultiPart tp = tpCorner.partMap(absDir ^ 1); if (this.canConnectTo(tp, ForgeDirection.getOrientation(absDir))) { this.connections[absDir] = tp; if (tp instanceof PartFlatWire) { // We found a wire, merge networks! this.getNetwork().merge(((PartFlatWire) tp).getNetwork()); } calculatedSides[absDir] = true; continue; } } } if (!calculatedSides[absDir]) { this.disconnect(absDir); } } } /** Check internal connections. */ for (byte r = 0; r < 4; r++) { int absDir = Rotation.rotateSide(this.side, r); /** Look for an internal straight connection. */ if (tile().partMap(PartMap.edgeBetween(absDir, this.side)) == null) { TMultiPart tp = tile().partMap(absDir); if (this.canConnectTo(tp)) { // We found a wire! Merge networks! this.connections[absDir] = tp; this.getNetwork().merge(((PartFlatWire) tp).getNetwork()); continue; } } if (!calculatedSides[absDir]) { this.disconnect(absDir); } } // Connect to the face of the block the wire is placed on. this.setExternalConnection(-1, this.side); this.getNetwork().reconstruct(); } public boolean setExternalConnection(int r, int absSide) { BlockCoord pos = new BlockCoord(tile()).offset(absSide); /** Look for an external wire connection. */ TileMultipart t = Utility.getMultipartTile(world(), pos); if (t != null && r != -1) { TMultiPart tp = t.partMap(this.side); if (this.canConnectTo(tp, ForgeDirection.getOrientation(absSide))) { // Check the wire we are connecting to and see if THAT block can accept this one. int otherR = (r + 2) % 4; if (tp instanceof PartFlatWire && ((PartFlatWire) tp).canConnectTo(this, ForgeDirection.getOrientation(absSide).getOpposite()) && ((PartFlatWire) tp).maskOpen(otherR)) { // We found a wire! Merge connection. this.connections[absSide] = tp; this.getNetwork().merge(((PartFlatWire) tp).getNetwork()); return true; } } this.disconnect(absSide); } /** Look for an external energy handler. */ TileEntity tileEntity = world().getBlockTileEntity(pos.x, pos.y, pos.z); if (this.canConnectTo(tileEntity, ForgeDirection.getOrientation(absSide))) { this.connections[absSide] = tileEntity; return true; } this.disconnect(absSide); return false; } private synchronized void disconnect(int i) { if (!this.world().isRemote) { if (this.connections[i] != null) { if (this.connections[i] instanceof PartFlatWire) { PartFlatWire wire = (PartFlatWire) this.connections[i]; this.connections[i] = null; this.getNetwork().split(this, wire); } else { this.connections[i] = null; } } } } @Override public Object[] getConnections() { return this.connections; } public boolean canStay() { BlockCoord pos = new BlockCoord(tile()).offset(side); return Utility.canPlaceWireOnSide(world(), pos.x, pos.y, pos.z, ForgeDirection.getOrientation(side ^ 1), false); } public boolean dropIfCantStay() { if (!canStay()) { drop(); return true; } return false; } public void drop() { TileMultipart.dropItem(getItem(), world(), Vector3.fromTileEntityCenter(tile())); tile().remPart(this); } /** * Recalculates connections to blocks outside this space * * @return true if a new connection was added or one was removed */ protected boolean updateExternalConnections() { int newConn = 0; for (int r = 0; r < 4; r++) { if (!maskOpen(r)) { continue; } if (connectStraight(r)) { newConn |= 0x10 << r; } else { int cnrMode = connectCorner(r); if (cnrMode != 0) { newConn |= 1 << r; if (cnrMode == 2) { newConn |= 0x100000 << r;// render flag } } } } if (newConn != (connMap & 0xF000FF)) { int diff = connMap ^ newConn; connMap = connMap & ~0xF000FF | newConn; // Notify corner disconnections for (int r = 0; r < 4; r++) { if ((diff & 1 << r) != 0) { notifyCornerChange(r); } } return true; } return false; } /** * Recalculates connections to other parts within this space * * @return true if a new connection was added or one was removed */ protected boolean updateInternalConnections() { int newConn = 0; for (int r = 0; r < 4; r++) { if (connectInternal(r)) { newConn |= 0x100 << r; } } if (connectCenter()) { newConn |= 0x10000; } if (newConn != (connMap & 0x10F00)) { connMap = connMap & ~0x10F00 | newConn; return true; } return false; } /** * Recalculates connections that can be made to other parts outside of this space * * @return true if external connections should be recalculated */ protected boolean updateOpenConnections() { int newConn = 0; for (int r = 0; r < 4; r++) if (connectionOpen(r)) newConn |= 0x1000 << r; if (newConn != (connMap & 0xF000)) { connMap = connMap & ~0xF000 | newConn; return true; } return false; } public boolean connectionOpen(int r) { int absDir = Rotation.rotateSide(side, r); TMultiPart facePart = tile().partMap(absDir); if (facePart != null && (!(facePart instanceof PartFlatWire) || !canConnectTo(facePart, ForgeDirection.getOrientation(absDir)))) return false; if (tile().partMap(PartMap.edgeBetween(side, absDir)) != null) return false; return true; } /** * Return a corner connection state. 0 = No connection 1 = Physical connection 2 = Render * connection */ public int connectCorner(int r) { int absDir = Rotation.rotateSide(side, r); BlockCoord pos = new BlockCoord(tile()); pos.offset(absDir); if (!canConnectThroughCorner(pos, absDir ^ 1, side)) return 0; pos.offset(side); TileMultipart t = Utility.getMultipartTile(world(), pos); if (t != null) { TMultiPart tp = t.partMap(absDir ^ 1); if (canConnectTo(tp, ForgeDirection.getOrientation(absDir))) { if (tp instanceof PartFlatWire) { boolean b = ((PartFlatWire) tp).connectCorner(this, Rotation.rotationTo(absDir ^ 1, side ^ 1)); if (b) { // let them connect to us if (!renderThisCorner((PartFlatWire) tp)) { return 1; } return 2; } } return 2; } } return 0; } public boolean canConnectThroughCorner(BlockCoord pos, int side1, int side2) { if (world().isAirBlock(pos.x, pos.y, pos.z)) { return true; } TileMultipart t = Utility.getMultipartTile(world(), pos); if (t != null) { return t.partMap(side1) == null && t.partMap(side2) == null && t.partMap(PartMap.edgeBetween(side1, side2)) == null; } return false; } public boolean connectStraight(int r) { int absDir = Rotation.rotateSide(side, r); BlockCoord pos = new BlockCoord(tile()).offset(absDir); TileMultipart t = Utility.getMultipartTile(world(), pos); if (t != null) { TMultiPart tp = t.partMap(side); if (this.canConnectTo(tp, ForgeDirection.getOrientation(absDir))) { if (tp instanceof PartFlatWire) { return ((PartFlatWire) tp).connectStraight(this, (r + 2) % 4); } return true; } } else { TileEntity tileEntity = world().getBlockTileEntity(pos.x, pos.y, pos.z); return this.canConnectTo(tileEntity, ForgeDirection.getOrientation(absDir)); } return false; } public boolean connectInternal(int r) { int absDir = Rotation.rotateSide(side, r); if (tile().partMap(PartMap.edgeBetween(absDir, side)) != null) return false; TMultiPart tp = tile().partMap(absDir); if (this.canConnectTo(tp, ForgeDirection.getOrientation(absDir))) { return ((PartFlatWire) tp).connectInternal(this, Rotation.rotationTo(absDir, side)); } return connectInternalOverride(tp, r); } public boolean connectInternalOverride(TMultiPart p, int r) { return false; } public boolean connectCenter() { TMultiPart tp = tile().partMap(6); if (this.canConnectTo(tp)) { if (tp instanceof PartFlatWire) { return ((PartFlatWire) tp).connectInternal(this, side); } return true; } return false; } public boolean renderThisCorner(PartFlatWire part) { if (!(part instanceof PartFlatWire)) return false; PartFlatWire wire = part; if (wire.getThickness() == getThickness()) return side < wire.side; return wire.getThickness() > getThickness(); } public boolean connectCorner(PartFlatWire wire, int r) { int absDir = Rotation.rotateSide(side, r); if (this.canConnectTo(wire, ForgeDirection.getOrientation(absDir)) && maskOpen(r)) { int oldConn = connMap; connMap |= 0x1 << r; if (renderThisCorner(wire))// render connection connMap |= 0x100000 << r; if (oldConn != connMap) sendConnUpdate(); return true; } return false; } public boolean connectStraight(PartFlatWire wire, int r) { int absDir = Rotation.rotateSide(side, r); if (this.canConnectTo(wire, ForgeDirection.getOrientation(absDir)) && maskOpen(r)) { int oldConn = connMap; connMap |= 0x10 << r; if (oldConn != connMap) sendConnUpdate(); return true; } return false; } public boolean connectInternal(PartFlatWire wire, int r) { int absDir = Rotation.rotateSide(side, r); if (this.canConnectTo(wire, ForgeDirection.getOrientation(absDir))) { int oldConn = connMap; connMap |= 0x100 << r; if (oldConn != connMap) sendConnUpdate(); return true; } return false; } public boolean canConnectCorner(int r) { return true; } public void notifyCornerChange(int r) { int absDir = Rotation.rotateSide(side, r); BlockCoord pos = new BlockCoord(tile()).offset(absDir).offset(side); world().notifyBlockOfNeighborChange(pos.x, pos.y, pos.z, tile().getBlockType().blockID); } public void notifyStraightChange(int r) { int absDir = Rotation.rotateSide(side, r); BlockCoord pos = new BlockCoord(tile()).offset(absDir); world().notifyBlockOfNeighborChange(pos.x, pos.y, pos.z, tile().getBlockType().blockID); } public boolean maskConnects(int r) { return (connMap & 0x111 << r) != 0; } public boolean maskOpen(int r) { return (connMap & 0x1000 << r) != 0; } /** START TILEMULTIPART INTERACTIONS **/ @Override public float getStrength(MovingObjectPosition hit, EntityPlayer player) { return 4; } @Override public int getSlotMask() { return 1 << this.side; } @Override public Iterable getSubParts() { return Arrays.asList(new IndexedCuboid6(0, selectionBounds[getThickness()][side])); } @Override public boolean occlusionTest(TMultiPart npart) { return NormalOcclusionTest.apply(this, npart); } @Override public Iterable getOcclusionBoxes() { return Arrays.asList(occlusionBounds[getThickness()][side]); } public int getThickness() { return this.isInsulated ? 2 : 1; } @Override public int redstoneConductionMap() { return 0; } @Override public boolean solid(int arg0) { return false; } @Override public String getType() { return "resonant_induction_flat_wire"; } /** RENDERING */ @SideOnly(Side.CLIENT) public Icon getIcon() { return RenderFlatWire.flatWireTexture; } public Colour getColour() { return this.isInsulated ? new ColourRGBA(EnumColor.DYES[this.color].toColor().getRGB()) : this.getMaterial().color; } public boolean useStaticRenderer() { return true; } @Override @SideOnly(Side.CLIENT) public void renderStatic(Vector3 pos, LazyLightMatrix olm, int pass) { if (pass == 0 && useStaticRenderer()) { CCRenderState.setBrightness(world(), x(), y(), z()); RenderFlatWire.render(this, pos); CCRenderState.setColour(-1); } } @Override @SideOnly(Side.CLIENT) public void renderDynamic(Vector3 pos, float frame, int pass) { if (pass == 0 && !useStaticRenderer()) { GL11.glDisable(GL11.GL_LIGHTING); TextureUtils.bindAtlas(0); CCRenderState.useModelColours(true); CCRenderState.startDrawing(7); RenderFlatWire.render(this, pos); CCRenderState.draw(); CCRenderState.setColour(-1); GL11.glEnable(GL11.GL_LIGHTING); } } @Override @SideOnly(Side.CLIENT) public void drawBreaking(RenderBlocks renderBlocks) { CCRenderState.reset(); RenderFlatWire.renderBreakingOverlay(renderBlocks.overrideBlockTexture, this); } /** * Utility method to aid in initializing this or subclasses, usually when you need to change the * wire to another type * * @param otherCable the wire to copy from */ public void copyFrom(PartFlatWire otherCable) { this.isInsulated = otherCable.isInsulated; this.color = otherCable.color; this.connections = otherCable.connections; this.material = otherCable.material; this.side = otherCable.side; this.connMap = otherCable.connMap; this.setNetwork(otherCable.getNetwork()); this.getNetwork().setBufferFor(this, otherCable.getNetwork().getBufferOf(otherCable)); } }