package edx.quantum.reactor import java.util import java.util.List import cpw.mods.fml.relauncher.{Side, SideOnly} import edx.core.{Electrodynamics, Reference} import edx.quantum.QuantumContent import net.minecraft.block.Block import net.minecraft.block.material.Material import net.minecraft.entity.EntityLiving import net.minecraft.entity.player.EntityPlayer import net.minecraft.init.Blocks import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound import net.minecraft.tileentity.TileEntity import net.minecraft.util.{AxisAlignedBB, ResourceLocation} import net.minecraft.world.World import net.minecraftforge.client.model.AdvancedModelLoader import net.minecraftforge.common.util.ForgeDirection import org.lwjgl.opengl.GL11 import resonantengine.api.edx.machine.{IReactor, IReactorComponent} import resonantengine.lib.grid.thermal.{GridThermal, ThermalPhysics} import resonantengine.lib.potion.PoisonRadiation import resonantengine.lib.render.RenderUtility import resonantengine.lib.render.model.ModelCube import resonantengine.lib.transform.vector.Vector3 import resonantengine.lib.utility.inventory.InventoryUtility import resonantengine.prefab.block.mixed.TileInventory import resonantengine.prefab.block.multiblock.{IMultiBlockStructure, MultiBlockHandler} import resonantengine.prefab.network.{TPacketReceiver, TPacketSender} import scala.collection.convert.wrapAll._ /** The primary reactor component cell used to build reactors with. * * @author Calclavia */ object TileReactorCell { final val radius = 2 final val meltingPoint = 3000 final val specificHeatCapacity = 1000 final val mass = ThermalPhysics.getMass(1000, 7) final val modelTop = AdvancedModelLoader.loadModel(new ResourceLocation(Reference.domain, Reference.modelPath + "reactorCellTop.tcn")) final val modelMiddle = AdvancedModelLoader.loadModel(new ResourceLocation(Reference.domain, Reference.modelPath + "reactorCellMiddle.tcn")) final val modelBottom = AdvancedModelLoader.loadModel(new ResourceLocation(Reference.domain, Reference.modelPath + "reactorCellBottom.tcn")) final val textureTop = new ResourceLocation(Reference.domain, Reference.modelPath + "reactorCellTop.png") final val textureMiddle = new ResourceLocation(Reference.domain, Reference.modelPath + "reactorCellMiddle.png") final val textureBottom = new ResourceLocation(Reference.domain, Reference.modelPath + "reactorCellBottom.png") final val textureFuel = new ResourceLocation(Reference.domain, Reference.modelPath + "fissileMaterial.png") } class TileReactorCell extends TileInventory(Material.iron) with IMultiBlockStructure[TileReactorCell] with IReactor with TPacketSender with TPacketReceiver { /** Multiblock Methods. */ private val multiBlock = new MultiBlockHandler[TileReactorCell](this) private var internalEnergy = 0d textureName = "machine" isOpaqueCube = false normalRender = false override def getSizeInventory = 1 override def onWorldJoin() { updatePositionStatus() } /** Multiblock Methods */ def updatePositionStatus() { val mainTile = getLowest mainTile.getMultiBlock.deconstruct() mainTile.getMultiBlock.construct() val top = (toVectorWorld + new Vector3(0, 1, 0)).getTileEntity(worldObj).isInstanceOf[TileReactorCell] val bottom = (toVectorWorld + new Vector3(0, -1, 0)).getTileEntity(worldObj).isInstanceOf[TileReactorCell] if (top && bottom) { setMeta(1) } else if (top) { setMeta(0) } else { setMeta(2) } } /** * @return Gets the lowest reactor cell in this "tower" */ def getLowest: TileReactorCell = { var lowest: TileReactorCell = this val checkPosition: Vector3 = toVectorWorld while (true) { val t: TileEntity = checkPosition.getTileEntity(this.worldObj) if (t.isInstanceOf[TileReactorCell]) { lowest = t.asInstanceOf[TileReactorCell] } else { return lowest } checkPosition.y -= 1 } return lowest } override def onNeighborChanged(block: Block) { updatePositionStatus() } override def update() { super.update() /** * Move the fuel rod down into the main reactor. */ if (!getMultiBlock.isPrimary) { if (getStackInSlot(0) != null) { if (getMultiBlock.get.getStackInSlot(0) == null) { getMultiBlock.get.setInventorySlotContents(0, getStackInSlot(0)) setInventorySlotContents(0, null) } } } if (!getWorld.isRemote) { /* if (getMultiBlock().isPrimary() && tank.getFluid != null && tank.getFluid.fluidID == QuantumContent.FLUID_PLASMA.getID) { val drain: FluidStack = tank.drain(FluidContainerRegistry.BUCKET_VOLUME, false) if (drain != null && drain.amount >= FluidContainerRegistry.BUCKET_VOLUME) { val spawnDir: ForgeDirection = ForgeDirection.getOrientation(worldObj.rand.nextInt(3) + 2) val spawnPos: Vector3 = position + spawnDir + spawnDir spawnPos.add(0, Math.max(worldObj.rand.nextInt(getHeight) - 1, 0), 0) if (worldObj.isAirBlock(spawnPos.xi, spawnPos.yi, spawnPos.zi)) { MinecraftForge.EVENT_BUS.post(new PlasmaEvent.SpawnPlasmaEvent(worldObj, spawnPos.xi, spawnPos.yi, spawnPos.zi, TilePlasma.plasmaMaxTemperature)) tank.drain(FluidContainerRegistry.BUCKET_VOLUME, true) } } } else {*/ /** * Fission reaction */ val fuelRod = getMultiBlock.get.getStackInSlot(0) if (fuelRod != null) { if (fuelRod.getItem.isInstanceOf[IReactorComponent]) { fuelRod.getItem.asInstanceOf[IReactorComponent].onReact(fuelRod, this) if (!worldObj.isRemote) { if (fuelRod.getItemDamage >= fuelRod.getMaxDamage) { getMultiBlock.get.setInventorySlotContents(0, null) } } /** * Radiation */ //TODO: Raycast radiation code if (ticks % 20 == 0) { if (worldObj.rand.nextFloat > 0.65) { val entities = worldObj.getEntitiesWithinAABB(classOf[EntityLiving], AxisAlignedBB.getBoundingBox(xCoord - TileReactorCell.radius * 2, yCoord - TileReactorCell.radius * 2, zCoord - TileReactorCell.radius * 2, xCoord + TileReactorCell.radius * 2, yCoord + TileReactorCell.radius * 2, zCoord + TileReactorCell.radius * 2)).asInstanceOf[List[EntityLiving]] for (entity <- entities) { PoisonRadiation.INSTANCE.poisonEntity(toVectorWorld, entity) } } } } } /** * Heats up the surroundings. Control rods absorbs neutrons, reducing the heat produced. */ val controlRodCount = ForgeDirection.VALID_DIRECTIONS.map(toVectorWorld + _).count(_.getBlock == QuantumContent.blockControlRod) GridThermal.addHeat(toVectorWorld, internalEnergy / ((controlRodCount + 1) * 0.3)) val temperature = GridThermal.getTemperature(toVectorWorld) internalEnergy = 0 /** * Play sound effects */ if (temperature >= 373) { if (world.rand.nextInt(80) == 0) { world.playSoundEffect(xCoord + 0.5F, yCoord + 0.5F, zCoord + 0.5F, "Fluid.lava", 0.5F, 2.1F + (worldObj.rand.nextFloat - worldObj.rand.nextFloat) * 0.85F) } if (world.rand.nextInt(40) == 0) { world.playSoundEffect(xCoord + 0.5F, yCoord + 0.5F, zCoord + 0.5F, "Fluid.lavapop", 0.5F, 2.6F + (worldObj.rand.nextFloat - worldObj.rand.nextFloat) * 0.8F) } if (ticks % (20 * 5) == 0) { world.playSoundEffect(this.xCoord + 0.5F, this.yCoord + 0.5F, this.zCoord + 0.5F, Reference.prefix + "reactorcell", temperature / TileReactorCell.meltingPoint, 1.0F) } } if (temperature > TileReactorCell.meltingPoint) { // meltDown() } } else { val temperature = GridThermal.getTemperature(toVectorWorld) if (world.rand.nextInt(5) == 0 && temperature >= 373) { world.spawnParticle("cloud", this.xCoord + worldObj.rand.nextInt(2), this.yCoord + 1.0F, this.zCoord + worldObj.rand.nextInt(2), 0, 0.1D, 0) world.spawnParticle("bubble", this.xCoord + worldObj.rand.nextInt(5), this.yCoord, this.zCoord + worldObj.rand.nextInt(5), 0, 0, 0) } } } override def getWorld: World = { return worldObj } def onMultiBlockChanged() { } override def getMultiBlockVectors: java.lang.Iterable[Vector3] = { val vectors: List[Vector3] = new util.ArrayList[Vector3] val checkPosition: Vector3 = toVectorWorld while (true) { val t: TileEntity = checkPosition.getTileEntity(this.worldObj) if (t.isInstanceOf[TileReactorCell]) { vectors.add(checkPosition - getPosition) } else { return vectors } checkPosition.y += 1 } return vectors } def getPosition: Vector3 = { return toVectorWorld } override def readFromNBT(nbt: NBTTagCompound) { super.readFromNBT(nbt) getMultiBlock.load(nbt) } override def writeToNBT(nbt: NBTTagCompound) { super.writeToNBT(nbt) getMultiBlock.save(nbt) } override def getInventoryStackLimit: Int = 1 /** Returns true if automation can insert the given item in the given slot from the given side. * Args: Slot, item, side */ override def canInsertItem(slot: Int, items: ItemStack, side: Int): Boolean = { return this.isItemValidForSlot(slot, items) } override def isItemValidForSlot(slotID: Int, itemStack: ItemStack): Boolean = { if (getMultiBlock.isPrimary && getMultiBlock.get.getStackInSlot(0) == null) { return itemStack.getItem.isInstanceOf[IReactorComponent] } return false } override def getMultiBlock: MultiBlockHandler[TileReactorCell] = multiBlock override def isUseableByPlayer(par1EntityPlayer: EntityPlayer): Boolean = { return if (worldObj.getTileEntity(xCoord, yCoord, zCoord) ne this) false else par1EntityPlayer.getDistanceSq(xCoord + 0.5D, yCoord + 0.5D, zCoord + 0.5D) <= 64.0D } def getInvName: String = { return getBlockType.getLocalizedName } @SideOnly(Side.CLIENT) override def getRenderBoundingBox: AxisAlignedBB = { if (getMultiBlock.isPrimary && getMultiBlock.isConstructed) { return AxisAlignedBB.getBoundingBox(x - 5, y - 5, z - 5, x + 5, y + 5, z + 5); } return super.getRenderBoundingBox } override def heat(energy: Double) { internalEnergy = Math.max(internalEnergy + energy, 0) } override def renderDynamic(pos: Vector3, frame: Float, pass: Int) { GL11.glPushMatrix() GL11.glTranslated(pos.x + 0.5, pos.y + 0.5, pos.z + 0.5) val meta = if (frame != 0) metadata else 2 val hasBelow = if (frame != 0) world.getTileEntity(x.toInt, y.toInt - 1, z.toInt).isInstanceOf[TileReactorCell] else false if (meta == 0) { RenderUtility.bind(TileReactorCell.textureBottom) TileReactorCell.modelBottom.renderAll() } else if (meta == 1) { RenderUtility.bind(TileReactorCell.textureMiddle) GL11.glTranslatef(0, 0.075f, 0) GL11.glScalef(1f, 1.15f, 1f) TileReactorCell.modelMiddle.renderAll() } else { RenderUtility.bind(TileReactorCell.textureTop) if (hasBelow) { GL11.glScalef(1f, 1.32f, 1f) } else { GL11.glTranslatef(0, 0.1f, 0) GL11.glScalef(1f, 1.2f, 1f) } if (hasBelow) { TileReactorCell.modelTop.renderAllExcept("BottomPad", "BaseDepth", "BaseWidth", "Base") } else { TileReactorCell.modelTop.renderAll() } } GL11.glPopMatrix() if (getStackInSlot(0) != null) { val height = getHeight * ((getStackInSlot(0).getMaxDamage - getStackInSlot(0).getItemDamage).toFloat / getStackInSlot(0).getMaxDamage.toFloat) GL11.glPushMatrix() GL11.glTranslated(pos.x + 0.5, pos.y + 0.5 * height, pos.z + 0.5) GL11.glScalef(0.4f, 0.9f * height, 0.4f) RenderUtility.bind(TileReactorCell.textureFuel) RenderUtility.disableLighting() ModelCube.INSTNACE.render() RenderUtility.enableLighting() GL11.glPopMatrix() } } def getHeight: Int = { var height: Int = 0 val checkPosition: Vector3 = toVectorWorld var tile: TileEntity = this while (tile.isInstanceOf[TileReactorCell]) { checkPosition.y += 1 height += 1 tile = checkPosition.getTileEntity(worldObj) } return height } /** Called when the block is right clicked by the player */ override protected def use(player: EntityPlayer, side: Int, hit: Vector3): Boolean = { if (!world.isRemote) { val tile: TileReactorCell = getMultiBlock.get() if (player.inventory.getCurrentItem != null) { if (tile.getStackInSlot(0) == null) { if (player.inventory.getCurrentItem.getItem.isInstanceOf[IReactorComponent]) { val itemStack: ItemStack = player.inventory.getCurrentItem.copy itemStack.stackSize = 1 tile.setInventorySlotContents(0, itemStack) player.inventory.decrStackSize(player.inventory.currentItem, 1) return true } } } else if (player.isSneaking && tile.getStackInSlot(0) != null) { InventoryUtility.dropItemStack(world, new Vector3(player), tile.getStackInSlot(0), 0) tile.setInventorySlotContents(0, null) return true } else { player.openGui(Electrodynamics, 0, world, tile.xCoord, tile.yCoord, tile.zCoord) } } return false } private def meltDown() { if (!world.isRemote) { world.setBlock(this.xCoord, this.yCoord, this.zCoord, Blocks.lava) world.createExplosion(null, x + 0.5, y + 0.5, z + 0.5, 3, false) } } }