package edx.electrical.tesla import java.util.{ArrayList, Comparator, HashSet, List, PriorityQueue, Set} import cpw.mods.fml.common.network.ByteBufUtils import edx.core.util.ResonantUtil import edx.core.{Electrodynamics, Reference, Settings} import io.netty.buffer.ByteBuf import net.minecraft.block.Block import net.minecraft.block.material.Material import net.minecraft.entity.player.EntityPlayer import net.minecraft.init.Items import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound import net.minecraft.server.MinecraftServer import net.minecraft.tileentity.TileEntity import net.minecraft.util.ChatComponentText import net.minecraft.world.World import net.minecraftforge.common.util.ForgeDirection import resonant.lib.content.prefab.TIO import resonant.lib.grid.core.TBlockNodeProvider import resonant.lib.grid.energy.EnergyStorage import resonant.lib.grid.energy.electric.NodeElectricComponent import resonant.lib.network.discriminator.{PacketTile, PacketType} import resonant.lib.network.handle.{TPacketReceiver, TPacketSender} import resonant.lib.prefab.tile.multiblock.reference.{IMultiBlockStructure, MultiBlockHandler} import resonant.lib.prefab.tile.spatial.SpatialTile import resonant.lib.prefab.tile.traits.TEnergyProvider import resonant.lib.render.EnumColor import resonant.lib.transform.vector.{Vector3, VectorWorld} import resonant.lib.utility.{LanguageUtility, LinkUtility} import scala.collection.JavaConversions._ /** * The Tesla TileEntity. * * - Redstone (Prevent Output Toggle) - Right click (Prevent Input Toggle) * * @author Calclavia * */ object TileTesla { final val DEFAULT_COLOR: Int = 12 } class TileTesla extends SpatialTile(Material.iron) with TBlockNodeProvider with IMultiBlockStructure[TileTesla] with ITesla with TPacketReceiver with TPacketSender with TEnergyProvider with TIO { final val TRANSFER_CAP: Double = 10000D /** Prevents transfer loops */ final val outputBlacklist: Set[TileTesla] = new HashSet[TileTesla] final val connectedTeslas: Set[TileTesla] = new HashSet[TileTesla] private val electricNode = new NodeElectricComponent(this) var dyeID: Int = TileTesla.DEFAULT_COLOR var canReceive: Boolean = true var attackEntities: Boolean = true /** Client side to do sparks */ var doTransfer: Boolean = true /** * Quantum Tesla */ var linked: Vector3 = null var linkDim: Int = 0 /** * Client */ var zapCounter: Int = 0 var isLinkedClient: Boolean = false var isTransfering: Boolean = false var topCache: TileTesla = null /** * Multiblock Methods. */ private var multiBlock: MultiBlockHandler[TileTesla] = null //Constructor //TODO: Dummy energy = new EnergyStorage textureName = "material_metal_side" normalRender = false isOpaqueCube = false override def start { super.start TeslaGrid.instance.register(this) } override def update { super.update if (this.getMultiBlock.isPrimary) { if (this.ticks % (4 + this.worldObj.rand.nextInt(2)) == 0 && ((this.worldObj.isRemote && isTransfering) || (this.energy.value != 0 && !this.worldObj.isBlockIndirectlyGettingPowered(this.xCoord, this.yCoord, this.zCoord)))) { val topTesla: TileTesla = this.getTopTelsa val topTeslaVector: Vector3 = toVector3 if (this.linked != null || this.isLinkedClient) { if (!this.worldObj.isRemote) { val dimWorld: World = MinecraftServer.getServer.worldServerForDimension(this.linkDim) if (dimWorld != null) { val transferTile: TileEntity = this.linked.getTileEntity(dimWorld) if (transferTile.isInstanceOf[TileTesla] && !transferTile.isInvalid) { this.transfer((transferTile.asInstanceOf[TileTesla]), Math.min(energy.value, TRANSFER_CAP)) if (this.zapCounter % 5 == 0 && Settings.SOUND_FXS) { this.worldObj.playSoundEffect(this.xCoord + 0.5, this.yCoord + 0.5, this.zCoord + 0.5, Reference.prefix + "electricshock", energy.value.toFloat / TRANSFER_CAP.asInstanceOf[Float], 1.3f - 0.5f * (this.dyeID / 16f)) } } } } else { Electrodynamics.proxy.renderElectricShock(this.worldObj, topTeslaVector.clone.add(0.5), topTeslaVector + new Vector3(0.5, java.lang.Double.POSITIVE_INFINITY, 0.5), false) } } else { val teslaToTransfer: PriorityQueue[ITesla] = new PriorityQueue[ITesla](1024, new Comparator[ITesla] { def compare(o1: ITesla, o2: ITesla): Int = { val distance1: Double = toVector3.distance(new Vector3(o1.asInstanceOf[TileEntity])) val distance2: Double = toVector3.distance(new Vector3(o2.asInstanceOf[TileEntity])) if (distance1 < distance2) { return 1 } else if (distance1 > distance2) { return -1 } return 0 } }) for (o <- TeslaGrid.instance.get) { var otherTesla = o if (new Vector3(otherTesla.asInstanceOf[TileEntity]).distance(toVector3) < this.getRange && otherTesla != this) { if (otherTesla.isInstanceOf[TileTesla]) { otherTesla = (otherTesla.asInstanceOf[TileTesla]).getMultiBlock.get } if (!connectedTeslas.contains(otherTesla) && otherTesla != this && otherTesla.canTeslaTransfer(this) && canTeslaTransfer(otherTesla.asInstanceOf[TileEntity])) { teslaToTransfer.add(otherTesla) } } } if (teslaToTransfer.size > 0) { val transferEnergy: Double = this.energy.value / teslaToTransfer.size val sentPacket: Boolean = false for (count <- 0 to 10) { if (!teslaToTransfer.isEmpty) { val tesla: ITesla = teslaToTransfer.poll if (this.zapCounter % 5 == 0 && Settings.SOUND_FXS) { this.worldObj.playSoundEffect(this.xCoord + 0.5, this.yCoord + 0.5, this.zCoord + 0.5, Reference.prefix + "electricshock", this.energy.value.asInstanceOf[Float] / TRANSFER_CAP.asInstanceOf[Float], 1.3f - 0.5f * (this.dyeID / 16f)) } var targetVector: Vector3 = new Vector3(tesla.asInstanceOf[TileEntity]) var heightRange: Int = 1 if (tesla.isInstanceOf[TileTesla]) { getMultiBlock.get.outputBlacklist.add(this) targetVector = tesla.asInstanceOf[TileTesla].getTopTelsa.toVector3 heightRange = (tesla.asInstanceOf[TileTesla]).getHeight } val distance: Double = topTeslaVector.distance(targetVector) Electrodynamics.proxy.renderElectricShock(this.worldObj, topTesla.toVector3 + new Vector3(0.5), targetVector + new Vector3(0.5, Math.random * heightRange / 3 - heightRange / 3, 0.5), EnumColor.DYES(this.dyeID).toColor) this.transfer(tesla, Math.min(transferEnergy, TRANSFER_CAP)) if (!sentPacket && transferEnergy > 0) { this.sendPacket(3) } } } } } this.zapCounter += 1 this.outputBlacklist.clear this.doTransfer = false } if (!this.worldObj.isRemote && this.energy.isLastEmpty) { this.sendPacket(2) } } this.topCache = null } def canTeslaTransfer(tileEntity: TileEntity): Boolean = { if (tileEntity.isInstanceOf[TileTesla]) { val otherTesla: TileTesla = tileEntity.asInstanceOf[TileTesla] if (!(otherTesla.getDye == dyeID || (otherTesla.getDye == TileTesla.DEFAULT_COLOR || dyeID == TileTesla.DEFAULT_COLOR))) { return false } } return canReceive && tileEntity != getMultiBlock.get && !this.outputBlacklist.contains(tileEntity) } override def getDescPacket: PacketTile = { return new PacketTile(this, this.getPacketData(1).toArray) } /** * 1 - Description Packet * 2 - Energy Update * 3 - Tesla Beam */ def getPacketData(`type`: Int): ArrayList[Any] = { val data: ArrayList[Any] = new ArrayList[Any] data.add(`type`.asInstanceOf[Byte]) if (`type` == 1) { data.add(this.dyeID) data.add(this.canReceive) data.add(this.attackEntities) data.add(this.linked != null) val nbt: NBTTagCompound = new NBTTagCompound getMultiBlock.save(nbt) data.add(nbt) } if (`type` == 2) { data.add(this.energy.value > 0) } return data } override def read(buf: ByteBuf, id: Int, packetType: PacketType) { super.read(buf, id, packetType) if (id == 1) { this.dyeID = buf.readInt this.canReceive = buf.readBoolean this.attackEntities = buf.readBoolean this.isLinkedClient = buf.readBoolean getMultiBlock.load(ByteBufUtils.readTag(buf)) } else if (id == 2) { this.isTransfering = buf.readBoolean } else if (id == 3) { this.doTransfer = true } } def teslaTransfer(e: Double, doTransfer: Boolean): Double = { var transferEnergy = e if (getMultiBlock.isPrimary) { if (doTransfer) { this.energy += transferEnergy if (this.energy.isLastEmpty) { this.sendPacket(2) } } return transferEnergy } else { if (energy.value > 0) { transferEnergy += energy.value energy.value = 0 } return getMultiBlock.get.teslaTransfer(transferEnergy, doTransfer) } } def getRange: Int = { return Math.min(4 * (this.getHeight - 1), 50) } /** * Called only on bottom. * * @return The highest Tesla coil in this tower. */ def getTopTelsa: TileTesla = { if (this.topCache != null) { return this.topCache } this.connectedTeslas.clear val checkPosition: Vector3 = toVector3 var returnTile: TileTesla = this var exit = false while (exit) { val t: TileEntity = checkPosition.getTileEntity(this.worldObj) if (t.isInstanceOf[TileTesla]) { this.connectedTeslas.add(t.asInstanceOf[TileTesla]) returnTile = t.asInstanceOf[TileTesla] checkPosition.add(0, 1, 0) } else { exit = true } } this.topCache = returnTile return returnTile } /** Gets color of the link */ def getDye: Int = dyeID /** * Called only on bottom. * * @return The highest Tesla coil in this tower. */ def getHeight: Int = { this.connectedTeslas.clear var y: Int = 0 var exit = false while (!exit) { val t: TileEntity = (toVector3 + new Vector3(0, y, 0)).getTileEntity(this.worldObj) if (t.isInstanceOf[TileTesla]) { this.connectedTeslas.add(t.asInstanceOf[TileTesla]) y += 1 } else { exit = true } } return y } override def invalidate { TeslaGrid.instance.unregister(this) super.invalidate } /** * Reads a tile entity from NBT. */ override def readFromNBT(nbt: NBTTagCompound) { super.readFromNBT(nbt) this.dyeID = nbt.getInteger("dyeID") this.canReceive = nbt.getBoolean("canReceive") this.attackEntities = nbt.getBoolean("attackEntities") if (nbt.hasKey("link_x") && nbt.hasKey("link_y") && nbt.hasKey("link_z")) { this.linked = new Vector3(nbt.getInteger("link_x"), nbt.getInteger("link_y"), nbt.getInteger("link_z")) this.linkDim = nbt.getInteger("linkDim") } getMultiBlock.load(nbt) } /** * Writes a tile entity to NBT. */ override def writeToNBT(nbt: NBTTagCompound) { super.writeToNBT(nbt) nbt.setInteger("dyeID", this.dyeID) nbt.setBoolean("canReceive", this.canReceive) nbt.setBoolean("attackEntities", this.attackEntities) if (this.linked != null) { nbt.setInteger("link_x", this.linked.x.asInstanceOf[Int]) nbt.setInteger("link_y", this.linked.y.asInstanceOf[Int]) nbt.setInteger("link_z", this.linked.z.asInstanceOf[Int]) nbt.setInteger("linkDim", this.linkDim) } getMultiBlock.save(nbt) } def getMultiBlock: MultiBlockHandler[TileTesla] = { if (multiBlock == null) multiBlock = new MultiBlockHandler[TileTesla](this) return multiBlock } def setLink(vector3: Vector3, dimID: Int, setOpponent: Boolean) { if (!worldObj.isRemote) { val otherWorld: World = MinecraftServer.getServer.worldServerForDimension(linkDim) if (setOpponent && linked != null && otherWorld != null) { val tileEntity: TileEntity = linked.getTileEntity(otherWorld) if (tileEntity.isInstanceOf[TileTesla]) { (tileEntity.asInstanceOf[TileTesla]).setLink(null, this.linkDim, false) } } linked = vector3 linkDim = dimID worldObj.markBlockForUpdate(this.xCoord, this.yCoord, this.zCoord) val newOtherWorld: World = MinecraftServer.getServer.worldServerForDimension(this.linkDim) if (setOpponent && newOtherWorld != null && this.linked != null) { val tileEntity: TileEntity = this.linked.getTileEntity(newOtherWorld) if (tileEntity.isInstanceOf[TileTesla]) { (tileEntity.asInstanceOf[TileTesla]).setLink(toVector3, this.worldObj.provider.dimensionId, false) } } } } def tryLink(vector: VectorWorld): Boolean = { if (vector != null) { if (vector.getTileEntity.isInstanceOf[TileTesla]) { setLink(vector, vector.world.provider.dimensionId, true) } return true } return false } def onMultiBlockChanged { } def getMultiBlockVectors: java.lang.Iterable[Vector3] = { val vectors: List[Vector3] = new ArrayList[Vector3] val checkPosition: Vector3 = toVector3 var exit = false while (!exit) { val t: TileEntity = checkPosition.getTileEntity(this.worldObj) if (t.isInstanceOf[TileTesla]) { vectors.add(checkPosition - getPosition) } else { exit = true } checkPosition.add(0, 1, 0) } return vectors } def getPosition: Vector3 = { return toVector3 } def getWorld: World = { return worldObj } override def setIO(dir: ForgeDirection, `type`: Int) { if (getMultiBlock.isPrimary) { super.setIO(dir, `type`) } else { getMultiBlock.get.setIO(dir, `type`) } } override def getIO(dir: ForgeDirection): Int = { if (getMultiBlock.isPrimary) { return super.getIO(dir) } return getMultiBlock.get.getIO(dir) } override def use(entityPlayer: EntityPlayer, side: Int, hit: Vector3): Boolean = { if (entityPlayer.getCurrentEquippedItem != null) { val dyeColor: Int = ResonantUtil.isDye(entityPlayer.getCurrentEquippedItem) if (dyeColor != -1) { getMultiBlock.get.setDye(dyeColor) if (!entityPlayer.capabilities.isCreativeMode) { entityPlayer.inventory.decrStackSize(entityPlayer.inventory.currentItem, 1) } return true } else if (entityPlayer.getCurrentEquippedItem.getItem eq Items.redstone) { val status: Boolean = getMultiBlock.get.toggleEntityAttack if (!entityPlayer.capabilities.isCreativeMode) { entityPlayer.inventory.decrStackSize(entityPlayer.inventory.currentItem, 1) } if (!world.isRemote) { entityPlayer.addChatMessage(new ChatComponentText(LanguageUtility.getLocal("message.tesla.toggleAttack").replace("%v", status + ""))) } return true } } else { val receiveMode: Boolean = getMultiBlock.get.toggleReceive if (!world.isRemote) { entityPlayer.addChatMessage(new ChatComponentText(LanguageUtility.getLocal("message.tesla.mode").replace("%v", receiveMode + ""))) } return true } return false } def setDye(id: Int) { this.dyeID = id this.worldObj.markBlockForUpdate(this.xCoord, this.yCoord, this.zCoord) } def toggleReceive: Boolean = { this.canReceive = !this.canReceive return canReceive } def toggleEntityAttack: Boolean = { this.attackEntities = !this.attackEntities val returnBool: Boolean = attackEntities this.worldObj.markBlockForUpdate(this.xCoord, this.yCoord, this.zCoord) return returnBool } override def configure(player: EntityPlayer, side: Int, hit: Vector3): Boolean = { val itemStack: ItemStack = player.getCurrentEquippedItem if (player.isSneaking) { if (tryLink(LinkUtility.getLink(itemStack))) { if (world.isRemote) player.addChatMessage(new ChatComponentText("Successfully linked devices.")) LinkUtility.clearLink(itemStack) } else { if (world.isRemote) player.addChatMessage(new ChatComponentText("Marked link for device.")) LinkUtility.setLink(itemStack, toVectorWorld) } return true } return super.configure(player, side, hit) } override def onNeighborChanged(id: Block) { updatePositionStatus } def updatePositionStatus { val mainTile: TileTesla = getLowestTesla mainTile.getMultiBlock.deconstruct mainTile.getMultiBlock.construct val isTop: Boolean = (toVector3 + new Vector3(0, 1, 0)).getTileEntity(this.worldObj).isInstanceOf[TileTesla] val isBottom: Boolean = (toVector3 + new Vector3(0, -1, 0)).getTileEntity(this.worldObj).isInstanceOf[TileTesla] if (isTop && isBottom) { this.worldObj.setBlockMetadataWithNotify(this.xCoord, this.yCoord, this.zCoord, 1, 3) } else if (isBottom) { this.worldObj.setBlockMetadataWithNotify(this.xCoord, this.yCoord, this.zCoord, 2, 3) } else { this.worldObj.setBlockMetadataWithNotify(this.xCoord, this.yCoord, this.zCoord, 0, 3) } } def getLowestTesla: TileTesla = { var lowest: TileTesla = this val checkPosition: Vector3 = toVector3 var exit = false while (!exit) { val t: TileEntity = checkPosition.getTileEntity(this.worldObj) if (t.isInstanceOf[TileTesla]) { lowest = t.asInstanceOf[TileTesla] } else { exit = true } checkPosition.add(0, -1, 0) } return lowest } private def transfer(tesla: ITesla, transferEnergy: Double) { if (transferEnergy > 0) { tesla.teslaTransfer(transferEnergy, true) this.teslaTransfer(-transferEnergy, true) } } }