electrodynamics/src/main/scala/edx/quantum/machine/accelerator/EntityParticle.scala
2015-01-26 20:35:38 +08:00

300 lines
11 KiB
Scala

package edx.quantum.machine.accelerator
import java.util.List
import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData
import edx.core.{Electrodynamics, Reference}
import io.netty.buffer.ByteBuf
import net.minecraft.block.Block
import net.minecraft.entity.{Entity, EntityLiving}
import net.minecraft.nbt.NBTTagCompound
import net.minecraft.tileentity.TileEntity
import net.minecraft.util.AxisAlignedBB
import net.minecraft.world.{ChunkCoordIntPair, World}
import net.minecraftforge.common.ForgeChunkManager
import net.minecraftforge.common.ForgeChunkManager.Type
import net.minecraftforge.common.util.ForgeDirection
import resonantengine.api.tile.IElectromagnet
import resonantengine.lib.poison.PoisonRadiation
import resonantengine.lib.transform.vector.Vector3
import scala.collection.JavaConversions._
/**
* The particle entity used to determine the particle acceleration.
*/
object EntityParticle
{
/** Speed by which a particle will turn into anitmatter */
val ANITMATTER_CREATION_SPEED: Float = 0.9f
val MOVEMENT_DIRECTION_DATAWATCHER_ID: Int = 20
/**
* Checks to see if a new particle can be spawned at the location.
* @param world - world to check in
* @param pos - location to check
* @return true if the spawn location is clear and 2 electromagnets are next to the location
*/
def canSpawnParticle(world: World, pos: Vector3): Boolean =
{
val block: Block = pos.getBlock(world)
if (block == null || !block.isAir(world, pos.xi, pos.yi, pos.zi))
{
var electromagnetCount = 0
for (i <- 0 until 6)
{
val dir: ForgeDirection = ForgeDirection.getOrientation(i)
if (isElectromagnet(world, pos, dir))
{
electromagnetCount += 1
}
}
return electromagnetCount >= 2
}
return false
}
/**
* Checks to see if the block is an instance of IElectromagnet and is turned on
* @param world - world to check in
* @param position - position to look for the block/tile
* @param dir - direction to check in
* @return true if the location contains an active electromagnet block
*/
def isElectromagnet(world: World, position: Vector3, dir: ForgeDirection): Boolean =
{
val checkPos: Vector3 = position.clone.add(dir)
val tile: TileEntity = checkPos.getTileEntity(world)
if (tile.isInstanceOf[IElectromagnet])
{
return (tile.asInstanceOf[IElectromagnet]).isRunning
}
return false
}
}
class EntityParticle(par1World: World) extends Entity(par1World) with IEntityAdditionalSpawnData
{
var updateTicket: ForgeChunkManager.Ticket = null
var didParticleCollide: Boolean = false
private var lastTurn: Int = 60
private var movementVector: Vector3 = new Vector3
private var movementDirection: ForgeDirection = ForgeDirection.NORTH
//Constructor
this.setSize(0.3f, 0.3f)
this.renderDistanceWeight = 4f
this.ignoreFrustumCheck = true
def this(world: World, pos: Vector3, movementVec: Vector3, dir: ForgeDirection)
{
this(world)
this.setPosition(pos.x, pos.y, pos.z)
this.movementVector = movementVec
this.movementDirection = dir
}
override def writeSpawnData(data: ByteBuf)
{
data.writeInt(this.movementVector.xi)
data.writeInt(this.movementVector.yi)
data.writeInt(this.movementVector.zi)
data.writeInt(this.movementDirection.ordinal)
}
override def readSpawnData(data: ByteBuf)
{
this.movementVector = new Vector3(data)
this.movementDirection = ForgeDirection.getOrientation(data.readInt)
}
override def onUpdate
{
val t: TileEntity = this.worldObj.getTileEntity(this.movementVector.xi, this.movementVector.yi, this.movementVector.zi)
val tileEntity: TileAccelerator = if (t != null && t.isInstanceOf[TileAccelerator]) t.asInstanceOf[TileAccelerator] else null
var acceleration: Double = 0.0006f
if (this.ticksExisted % 10 == 0)
{
this.worldObj.playSoundAtEntity(this, Reference.prefix + "accelerator", 1f, (0.6f + (0.4 * (this.getParticleVelocity / EntityParticle.ANITMATTER_CREATION_SPEED))).asInstanceOf[Float])
}
//Sanity check
if (tileEntity == null)
{
setDead
return
}
else if (tileEntity.entityParticle == null)
{
tileEntity.entityParticle = this
}
//Force load chunks TODO calculate direction so to only load two chunks instead of 5
for (x <- -1 to 1)
{
for (z <- -1 to 1)
{
ForgeChunkManager.forceChunk(this.updateTicket, new ChunkCoordIntPair((this.posX.asInstanceOf[Int] >> 4) + x, (this.posZ.asInstanceOf[Int] >> 4) + z))
}
}
//Update data watcher
if (!this.worldObj.isRemote)
{
this.dataWatcher.updateObject(EntityParticle.MOVEMENT_DIRECTION_DATAWATCHER_ID, this.movementDirection.ordinal.asInstanceOf[Byte])
}
else
{
this.movementDirection = ForgeDirection.getOrientation(this.dataWatcher.getWatchableObjectByte(EntityParticle.MOVEMENT_DIRECTION_DATAWATCHER_ID))
}
if ((!EntityParticle.isElectromagnet(worldObj, new Vector3(this), movementDirection.getRotation(ForgeDirection.UP)) || !EntityParticle.isElectromagnet(worldObj, new Vector3(this), movementDirection.getRotation(ForgeDirection.DOWN))) && this.lastTurn <= 0)
{
acceleration = turn
this.motionX = 0
this.motionY = 0
this.motionZ = 0
this.lastTurn = 40
}
this.lastTurn -= 1
if (!EntityParticle.canSpawnParticle(this.worldObj, new Vector3(this)) || this.isCollided)
{
handleCollisionWithEntity
return
}
val dongLi: Vector3 = new Vector3
dongLi.add(this.movementDirection)
dongLi.multiply(acceleration)
this.motionX = Math.min(dongLi.x + this.motionX, EntityParticle.ANITMATTER_CREATION_SPEED)
this.motionY = Math.min(dongLi.y + this.motionY, EntityParticle.ANITMATTER_CREATION_SPEED)
this.motionZ = Math.min(dongLi.z + this.motionZ, EntityParticle.ANITMATTER_CREATION_SPEED)
this.isAirBorne = true
this.lastTickPosX = this.posX
this.lastTickPosY = this.posY
this.lastTickPosZ = this.posZ
this.moveEntity(this.motionX, this.motionY, this.motionZ)
this.setPosition(this.posX, this.posY, this.posZ)
if (this.lastTickPosX == this.posX && this.lastTickPosY == this.posY && this.lastTickPosZ == this.posZ && this.getParticleVelocity <= 0 && this.lastTurn <= 0)
{
this.setDead
}
this.worldObj.spawnParticle("portal", this.posX, this.posY, this.posZ, 0, 0, 0)
this.worldObj.spawnParticle("largesmoke", this.posX, this.posY, this.posZ, 0, 0, 0)
val radius: Float = 0.5f
// Handle collision with entities TODO turn into a ray trace call so we know what we hit
val bounds: AxisAlignedBB = AxisAlignedBB.getBoundingBox(this.posX - radius, this.posY - radius, this.posZ - radius, this.posX + radius, this.posY + radius, this.posZ + radius)
val entitiesNearby: List[_] = this.worldObj.getEntitiesWithinAABB(classOf[Entity], bounds)
if (entitiesNearby.size > 1)
{
this.handleCollisionWithEntity
return
}
}
/**
* Try to move the particle left or right depending on which side is empty.
*
* @return The new velocity.
*/
private def turn: Double =
{
//TODO rewrite to allow for up and down turning
val RELATIVE_MATRIX: Array[Array[Int]] = Array(Array(3, 2, 1, 0, 5, 4), Array(4, 5, 0, 1, 2, 3), Array(0, 1, 3, 2, 4, 5), Array(0, 1, 2, 3, 5, 4), Array(0, 1, 5, 4, 3, 2), Array(0, 1, 4, 5, 2, 3))
val zuoFangXiang: ForgeDirection = ForgeDirection.getOrientation(RELATIVE_MATRIX(this.movementDirection.ordinal)(ForgeDirection.EAST.ordinal))
val zuoBian: Vector3 = new Vector3(this).floor
zuoBian.add(zuoFangXiang)
val youFangXiang: ForgeDirection = ForgeDirection.getOrientation(RELATIVE_MATRIX(this.movementDirection.ordinal)(ForgeDirection.WEST.ordinal))
val youBian: Vector3 = new Vector3(this).floor
youBian.add(youFangXiang)
if (zuoBian.getBlock(this.worldObj) == null)
{
this.movementDirection = zuoFangXiang
}
else if (youBian.getBlock(this.worldObj) == null)
{
this.movementDirection = youFangXiang
}
else
{
setDead
return 0
}
this.setPosition(Math.floor(this.posX) + 0.5, Math.floor(this.posY) + 0.5, Math.floor(this.posZ) + 0.5)
return this.getParticleVelocity - (this.getParticleVelocity / Math.min(Math.max(70 * this.getParticleVelocity, 4), 30))
}
override def applyEntityCollision(par1Entity: Entity)
{
this.handleCollisionWithEntity
}
def handleCollisionWithEntity
{
this.worldObj.playSoundAtEntity(this, Reference.prefix + "antimatter", 1.5f, 1f - this.worldObj.rand.nextFloat * 0.3f)
if (!this.worldObj.isRemote)
{
if (this.getParticleVelocity > EntityParticle.ANITMATTER_CREATION_SPEED / 2)
{
val radius: Float = 1f
val bounds: AxisAlignedBB = AxisAlignedBB.getBoundingBox(this.posX - radius, this.posY - radius, this.posZ - radius, this.posX + radius, this.posY + radius, this.posZ + radius)
val entitiesNearby: List[_] = this.worldObj.getEntitiesWithinAABB(classOf[EntityParticle], bounds)
if (entitiesNearby.size > 0)
{
didParticleCollide = true
setDead
return
}
}
this.worldObj.createExplosion(this, this.posX, this.posY, this.posZ, this.getParticleVelocity.asInstanceOf[Float] * 2.5f, true)
}
val radius: Float = 6
val bounds: AxisAlignedBB = AxisAlignedBB.getBoundingBox(this.posX - radius, this.posY - radius, this.posZ - radius, this.posX + radius, this.posY + radius, this.posZ + radius)
val livingNearby: List[_] = this.worldObj.getEntitiesWithinAABB(classOf[EntityLiving], bounds)
for (entity <- livingNearby)
{
PoisonRadiation.INSTANCE.poisonEntity(new Vector3(entity.asInstanceOf[Entity]), entity.asInstanceOf[EntityLiving])
}
setDead
}
def getParticleVelocity: Double =
{
return Math.abs(this.motionX) + Math.abs(this.motionY) + Math.abs(this.motionZ)
}
override def setDead
{
ForgeChunkManager.releaseTicket(this.updateTicket)
super.setDead
}
protected override def entityInit
{
this.dataWatcher.addObject(EntityParticle.MOVEMENT_DIRECTION_DATAWATCHER_ID, 3.asInstanceOf[Byte])
if (this.updateTicket == null)
{
this.updateTicket = ForgeChunkManager.requestTicket(Electrodynamics, this.worldObj, Type.ENTITY)
this.updateTicket.getModData
this.updateTicket.bindEntity(this)
}
}
protected def readEntityFromNBT(nbt: NBTTagCompound)
{
this.movementVector = new Vector3(nbt.getCompoundTag("jiqi"))
ForgeDirection.getOrientation(nbt.getByte("fangXiang"))
}
protected def writeEntityToNBT(nbt: NBTTagCompound)
{
nbt.setTag("jiqi", this.movementVector.writeNBT(new NBTTagCompound))
nbt.setByte("fangXiang", this.movementDirection.ordinal.asInstanceOf[Byte])
}
}