electrodynamics/src/main/scala/edx/electrical/tesla/TileTesla.scala
2015-01-26 18:13:56 +08:00

643 lines
18 KiB
Scala

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)
}
}
}