electrodynamics/src/main/scala/edx/electrical/circuit/component/tesla/TileTesla.scala

643 lines
19 KiB
Scala

package edx.electrical.circuit.component.tesla
import java.util.{ArrayList, Comparator, HashSet, List, PriorityQueue, Set}
import cpw.mods.fml.common.network.ByteBufUtils
import edx.core.util.ColorUtil
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 resonantengine.core.network.discriminator.{PacketTile, PacketType}
import resonantengine.lib.content.prefab.TIO
import resonantengine.lib.grid.energy.EnergyStorage
import resonantengine.lib.grid.energy.electric.NodeElectricComponent
import resonantengine.lib.modcontent.block.ResonantTile
import resonantengine.lib.render.EnumColor
import resonantengine.lib.transform.vector.{Vector3, VectorWorld}
import resonantengine.lib.utility.{LanguageUtility, LinkUtility}
import resonantengine.prefab.block.impl.{TBlockNodeProvider, TEnergyProvider}
import resonantengine.prefab.block.multiblock.{IMultiBlockStructure, MultiBlockHandler}
import resonantengine.prefab.network.{TPacketReceiver, TPacketSender}
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 ResonantTile(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 = toVectorWorld
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 = toVectorWorld.distance(new Vector3(o1.asInstanceOf[TileEntity]))
val distance2: Double = toVectorWorld.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(toVectorWorld) < 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.toVectorWorld
heightRange = (tesla.asInstanceOf[TileTesla]).getHeight
}
val distance: Double = topTeslaVector.distance(targetVector)
Electrodynamics.proxy.renderElectricShock(this.worldObj, topTesla.toVectorWorld + 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 = toVectorWorld
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 = (toVectorWorld + 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)
}
def getMultiBlock: MultiBlockHandler[TileTesla] =
{
if (multiBlock == null) multiBlock = new MultiBlockHandler[TileTesla](this)
return multiBlock
}
/**
* 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 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(toVectorWorld, 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 = toVectorWorld
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 toVectorWorld
}
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 = ColorUtil.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 = (toVectorWorld + new Vector3(0, 1, 0)).getTileEntity(this.worldObj).isInstanceOf[TileTesla]
val isBottom: Boolean = (toVectorWorld + 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 = toVectorWorld
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)
}
}
}