613 lines
21 KiB
Java
613 lines
21 KiB
Java
package resonantinduction.atomic.fission.reactor;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
import net.minecraft.block.Block;
|
|
import net.minecraft.entity.EntityLiving;
|
|
import net.minecraft.entity.player.EntityPlayer;
|
|
import net.minecraft.inventory.IInventory;
|
|
import net.minecraft.inventory.ISidedInventory;
|
|
import net.minecraft.item.ItemStack;
|
|
import net.minecraft.nbt.NBTTagCompound;
|
|
import net.minecraft.network.packet.Packet;
|
|
import net.minecraft.tileentity.TileEntity;
|
|
import net.minecraft.util.AxisAlignedBB;
|
|
import net.minecraft.world.World;
|
|
import net.minecraftforge.common.ForgeDirection;
|
|
import net.minecraftforge.common.MinecraftForge;
|
|
import net.minecraftforge.fluids.Fluid;
|
|
import net.minecraftforge.fluids.FluidContainerRegistry;
|
|
import net.minecraftforge.fluids.FluidStack;
|
|
import net.minecraftforge.fluids.FluidTank;
|
|
import net.minecraftforge.fluids.FluidTankInfo;
|
|
import net.minecraftforge.fluids.IFluidHandler;
|
|
import resonant.api.IReactor;
|
|
import resonant.api.IReactorComponent;
|
|
import resonant.api.event.PlasmaEvent.SpawnPlasmaEvent;
|
|
import resonant.lib.content.module.prefab.TileInventory;
|
|
import resonant.lib.multiblock.IMultiBlockStructure;
|
|
import resonant.lib.multiblock.MultiBlockHandler;
|
|
import resonant.lib.network.PacketHandler;
|
|
import resonant.lib.network.Synced;
|
|
import resonant.lib.network.Synced.SyncedInput;
|
|
import resonant.lib.network.Synced.SyncedOutput;
|
|
import resonant.lib.prefab.poison.PoisonRadiation;
|
|
import resonant.lib.thermal.ThermalGrid;
|
|
import resonant.lib.thermal.ThermalPhysics;
|
|
import resonant.lib.utility.inventory.InventoryUtility;
|
|
import resonantinduction.atomic.Atomic;
|
|
import resonantinduction.atomic.ReactorExplosion;
|
|
import resonantinduction.atomic.fusion.TilePlasma;
|
|
import resonantinduction.core.ResonantInduction;
|
|
import universalelectricity.api.UniversalElectricity;
|
|
import universalelectricity.api.vector.Vector3;
|
|
import universalelectricity.api.vector.VectorWorld;
|
|
import cpw.mods.fml.relauncher.Side;
|
|
import cpw.mods.fml.relauncher.SideOnly;
|
|
|
|
/** The primary reactor component cell used to build reactors with.
|
|
*
|
|
* @author Calclavia */
|
|
public class TileReactorCell extends TileInventory implements IMultiBlockStructure<TileReactorCell>, IInventory, IReactor, IFluidHandler, ISidedInventory
|
|
{
|
|
public static final int RADIUS = 2;
|
|
public static final int MELTING_POINT = 2000;
|
|
|
|
private final int specificHeatCapacity = 1000;
|
|
private final float mass = ThermalPhysics.getMass(1000, 7);
|
|
public FluidTank tank = new FluidTank(FluidContainerRegistry.BUCKET_VOLUME * 15);
|
|
|
|
@Synced
|
|
public float temperature = 295;
|
|
|
|
private float previousTemperature = 295;
|
|
|
|
private boolean shouldUpdate = false;
|
|
|
|
private long prevInternalEnergy = 0;
|
|
private long internalEnergy = 0;
|
|
private int meltdownCounter = 0;
|
|
private int meltdownCounterMaximum = 1000;
|
|
|
|
/** Multiblock Methods. */
|
|
private MultiBlockHandler<TileReactorCell> multiBlock;
|
|
|
|
public TileReactorCell()
|
|
{
|
|
super(UniversalElectricity.machine);
|
|
textureName = "machine";
|
|
isOpaqueCube = false;
|
|
normalRender = false;
|
|
customItemRender = true;
|
|
}
|
|
|
|
@Override
|
|
protected void onWorldJoin()
|
|
{
|
|
updatePositionStatus();
|
|
}
|
|
|
|
@Override
|
|
protected void onNeighborChanged()
|
|
{
|
|
updatePositionStatus();
|
|
}
|
|
|
|
@Override
|
|
public void initiate()
|
|
{
|
|
updatePositionStatus();
|
|
super.initiate();
|
|
}
|
|
|
|
/** Called when the block is right clicked by the player */
|
|
@Override
|
|
protected boolean use(EntityPlayer player, int side, Vector3 hit)
|
|
{
|
|
if (!world().isRemote)
|
|
{
|
|
TileReactorCell tile = getMultiBlock().get();
|
|
|
|
if (!player.isSneaking())
|
|
{
|
|
if (tile.getStackInSlot(0) != null)
|
|
{
|
|
InventoryUtility.dropItemStack(world(), new Vector3(player), tile.getStackInSlot(0), 0);
|
|
tile.setInventorySlotContents(0, null);
|
|
return true;
|
|
}
|
|
else if (player.inventory.getCurrentItem() != null)
|
|
{
|
|
if (player.inventory.getCurrentItem().getItem() instanceof IReactorComponent)
|
|
{
|
|
ItemStack itemStack = player.inventory.getCurrentItem().copy();
|
|
itemStack.stackSize = 1;
|
|
tile.setInventorySlotContents(0, itemStack);
|
|
player.inventory.decrStackSize(player.inventory.currentItem, 1);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
player.openGui(Atomic.INSTANCE, 0, world(), tile.xCoord, tile.yCoord, tile.zCoord);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
protected void markUpdate()
|
|
{
|
|
super.markUpdate();
|
|
shouldUpdate = true;
|
|
}
|
|
|
|
@Override
|
|
public void updateEntity()
|
|
{
|
|
super.updateEntity();
|
|
|
|
/** Move fuel rod down into the primary cell block if possible */
|
|
if (!getMultiBlock().isPrimary())
|
|
{
|
|
if (getStackInSlot(0) != null)
|
|
{
|
|
if (getMultiBlock().get().getStackInSlot(0) == null)
|
|
{
|
|
getMultiBlock().get().setInventorySlotContents(0, getStackInSlot(0));
|
|
setInventorySlotContents(0, null);
|
|
}
|
|
}
|
|
|
|
if (tank.getFluidAmount() > 0)
|
|
{
|
|
getMultiBlock().get().tank.fill(tank.drain(tank.getCapacity(), true), true);
|
|
}
|
|
}
|
|
|
|
if (!worldObj.isRemote)
|
|
{
|
|
if (getMultiBlock().isPrimary() && tank.getFluid() != null && tank.getFluid().fluidID == Atomic.FLUID_PLASMA.getID())
|
|
{
|
|
/** Spawn plasma */
|
|
FluidStack drain = tank.drain(FluidContainerRegistry.BUCKET_VOLUME, false);
|
|
|
|
if (drain != null && drain.amount >= FluidContainerRegistry.BUCKET_VOLUME)
|
|
{
|
|
ForgeDirection spawnDir = ForgeDirection.getOrientation(worldObj.rand.nextInt(3) + 2);
|
|
Vector3 spawnPos = new Vector3(this).translate(spawnDir, 2);
|
|
spawnPos.translate(0, Math.max(worldObj.rand.nextInt(getHeight()) - 1, 0), 0);
|
|
|
|
if (worldObj.isAirBlock(spawnPos.intX(), spawnPos.intY(), spawnPos.intZ()))
|
|
{
|
|
MinecraftForge.EVENT_BUS.post(new SpawnPlasmaEvent(worldObj, spawnPos.intX(), spawnPos.intY(), spawnPos.intZ(), TilePlasma.plasmaMaxTemperature));
|
|
tank.drain(FluidContainerRegistry.BUCKET_VOLUME, true);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
prevInternalEnergy = internalEnergy;
|
|
|
|
/** Handle cell rod interactions. */
|
|
ItemStack fuelRod = getMultiBlock().get().getStackInSlot(0);
|
|
|
|
if (fuelRod != null)
|
|
{
|
|
if (fuelRod.getItem() instanceof IReactorComponent)
|
|
{
|
|
// Activate rods.
|
|
((IReactorComponent) fuelRod.getItem()).onReact(fuelRod, this);
|
|
|
|
if (!worldObj.isRemote)
|
|
{
|
|
if (fuelRod.getItemDamage() >= fuelRod.getMaxDamage())
|
|
{
|
|
getMultiBlock().get().setInventorySlotContents(0, null);
|
|
}
|
|
}
|
|
|
|
// Emit Radiations
|
|
if (ticks % 20 == 0)
|
|
{
|
|
if (worldObj.rand.nextFloat() > 0.65)
|
|
{
|
|
List<EntityLiving> entities = worldObj.getEntitiesWithinAABB(EntityLiving.class,
|
|
AxisAlignedBB.getBoundingBox(xCoord - RADIUS * 2, yCoord - RADIUS * 2, zCoord - RADIUS * 2, xCoord + RADIUS * 2, yCoord + RADIUS * 2, zCoord + RADIUS * 2));
|
|
|
|
for (EntityLiving entity : entities)
|
|
{
|
|
PoisonRadiation.INSTANCE.poisonEntity(new Vector3(this), entity);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update the temperature from the thermal grid.
|
|
temperature = ThermalGrid.getTemperature(new VectorWorld(this));
|
|
|
|
/** Only a small percentage of the internal energy is used for temperature. */
|
|
if (internalEnergy - prevInternalEnergy > 0)
|
|
{
|
|
float deltaT = ThermalPhysics.getTemperatureForEnergy(mass, specificHeatCapacity, (long) ((internalEnergy - prevInternalEnergy) * 0.15));
|
|
|
|
/** Check control rods */
|
|
int rods = 0;
|
|
|
|
for (int i = 2; i < 6; i++)
|
|
{
|
|
Vector3 checkAdjacent = new Vector3(this).translate(ForgeDirection.getOrientation(i));
|
|
|
|
if (checkAdjacent.getBlockID(worldObj) == Atomic.blockControlRod.blockID)
|
|
{
|
|
deltaT /= 1.1;
|
|
rods++;
|
|
}
|
|
}
|
|
|
|
// Add heat to surrounding blocks in the thermal grid.
|
|
ThermalGrid.addTemperature(new VectorWorld(this), deltaT);
|
|
|
|
// Sound of lava flowing randomly plays when above temperature to boil water.
|
|
if (worldObj.rand.nextInt(80) == 0 && this.getTemperature() >= 373)
|
|
{
|
|
worldObj.playSoundEffect(this.xCoord + 0.5F, this.yCoord + 0.5F, this.zCoord + 0.5F, "Fluid.lava", 0.5F, 2.1F + (worldObj.rand.nextFloat() - worldObj.rand.nextFloat()) * 0.85F);
|
|
}
|
|
|
|
// Sounds of lava popping randomly plays when above temperature to boil water.
|
|
if (worldObj.rand.nextInt(40) == 0 && this.getTemperature() >= 373)
|
|
{
|
|
worldObj.playSoundEffect(this.xCoord + 0.5F, this.yCoord + 0.5F, this.zCoord + 0.5F, "Fluid.lavapop", 0.5F, 2.6F + (worldObj.rand.nextFloat() - worldObj.rand.nextFloat()) * 0.8F);
|
|
}
|
|
|
|
// Reactor cell plays random idle noises while operating and above temperature to boil water.
|
|
if (worldObj.getWorldTime() % (Atomic.SECOND_IN_TICKS * 5.0F) == 0 && this.getTemperature() >= 373)
|
|
{
|
|
float percentage = Math.min(this.getTemperature() / TileReactorCell.MELTING_POINT, 1.0F);
|
|
worldObj.playSoundEffect(this.xCoord + 0.5F, this.yCoord + 0.5F, this.zCoord + 0.5F, "atomicscience:reactorcell", percentage, 1.0F);
|
|
// AtomicScience.LOGGER.info("REACTOR SOUND");
|
|
}
|
|
|
|
if (previousTemperature != temperature && !shouldUpdate)
|
|
{
|
|
shouldUpdate = true;
|
|
previousTemperature = temperature;
|
|
// System.out.println("[Atomic Science] [Thermal Grid] Temperature: " + String.valueOf(previousTemperature));
|
|
}
|
|
|
|
if (previousTemperature >= MELTING_POINT && meltdownCounter < meltdownCounterMaximum)
|
|
{
|
|
shouldUpdate = true;
|
|
meltdownCounter++;
|
|
// System.out.println("[Atomic Science] [Reactor Cell] Meltdown Ticker: " + String.valueOf(temperature) + " @ " + String.valueOf(meltdownCounter) + "/" + String.valueOf(meltdownCounterMaximum));
|
|
}
|
|
|
|
if (previousTemperature >= MELTING_POINT && meltdownCounter >= meltdownCounterMaximum)
|
|
{
|
|
// System.out.println("[Atomic Science] [Reactor Cell] Meltdown Ticker: REACTOR MELTDOWN!");
|
|
meltdownCounter = 0;
|
|
meltDown();
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// Reset meltdown ticker to give the reactor more of a 'goldilocks zone'.
|
|
meltdownCounter = 0;
|
|
}
|
|
}
|
|
|
|
internalEnergy = 0;
|
|
|
|
if (isOverToxic())
|
|
{
|
|
/** Randomly leak toxic waste when it is too toxic */
|
|
VectorWorld leakPos = new VectorWorld(this).translate(worldObj.rand.nextInt(20) - 10, worldObj.rand.nextInt(20) - 10, worldObj.rand.nextInt(20) - 10);
|
|
|
|
int blockID = leakPos.getBlockID();
|
|
|
|
if (blockID == Block.grass.blockID)
|
|
{
|
|
leakPos.setBlock(worldObj, Atomic.blockRadioactive.blockID);
|
|
tank.drain(FluidContainerRegistry.BUCKET_VOLUME, true);
|
|
}
|
|
else if (blockID == 0 || Block.blocksList[blockID].isBlockReplaceable(worldObj, leakPos.intX(), leakPos.intY(), leakPos.intZ()))
|
|
{
|
|
if (tank.getFluid() != null)
|
|
{
|
|
leakPos.setBlock(worldObj, tank.getFluid().getFluid().getBlockID());
|
|
tank.drain(FluidContainerRegistry.BUCKET_VOLUME, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (ticks % 60 == 0 || shouldUpdate)
|
|
{
|
|
shouldUpdate = false;
|
|
notifyChange();
|
|
PacketHandler.sendPacketToClients(getDescriptionPacket(), worldObj, new Vector3(this), 50);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// Particles of white smoke will rise from above the reactor chamber when above water boiling temperature.
|
|
if (worldObj.rand.nextInt(5) == 0 && this.getTemperature() >= 373)
|
|
{
|
|
worldObj.spawnParticle("cloud", this.xCoord + worldObj.rand.nextInt(2), this.yCoord + 1.0F, this.zCoord + worldObj.rand.nextInt(2), 0, 0.1D, 0);
|
|
worldObj.spawnParticle("bubble", this.xCoord + worldObj.rand.nextInt(5), this.yCoord, this.zCoord + worldObj.rand.nextInt(5), 0, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean isOverToxic()
|
|
{
|
|
return tank.getFluid() != null && tank.getFluid().fluidID == Atomic.FLUID_TOXIC_WASTE.getID() && tank.getFluid().amount >= tank.getCapacity();
|
|
}
|
|
|
|
/** Multiblock Methods */
|
|
public void updatePositionStatus()
|
|
{
|
|
TileReactorCell mainTile = getLowest();
|
|
mainTile.getMultiBlock().deconstruct();
|
|
mainTile.getMultiBlock().construct();
|
|
|
|
boolean top = new Vector3(this).add(new Vector3(0, 1, 0)).getTileEntity(worldObj) instanceof TileReactorCell;
|
|
boolean bottom = new Vector3(this).add(new Vector3(0, -1, 0)).getTileEntity(worldObj) instanceof TileReactorCell;
|
|
|
|
if (top && bottom)
|
|
{
|
|
worldObj.setBlockMetadataWithNotify(xCoord, yCoord, zCoord, 1, 3);
|
|
}
|
|
else if (top)
|
|
{
|
|
worldObj.setBlockMetadataWithNotify(xCoord, yCoord, zCoord, 0, 3);
|
|
}
|
|
else
|
|
{
|
|
worldObj.setBlockMetadataWithNotify(xCoord, yCoord, zCoord, 2, 3);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onMultiBlockChanged()
|
|
{
|
|
|
|
}
|
|
|
|
@Override
|
|
public Vector3[] getMultiBlockVectors()
|
|
{
|
|
List<Vector3> vectors = new ArrayList<Vector3>();
|
|
|
|
Vector3 checkPosition = new Vector3(this);
|
|
|
|
while (true)
|
|
{
|
|
TileEntity t = checkPosition.getTileEntity(this.worldObj);
|
|
|
|
if (t instanceof TileReactorCell)
|
|
{
|
|
vectors.add(checkPosition.clone().subtract(getPosition()));
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
checkPosition.y++;
|
|
}
|
|
|
|
return vectors.toArray(new Vector3[0]);
|
|
}
|
|
|
|
public TileReactorCell getLowest()
|
|
{
|
|
TileReactorCell lowest = this;
|
|
Vector3 checkPosition = new Vector3(this);
|
|
|
|
while (true)
|
|
{
|
|
TileEntity t = checkPosition.getTileEntity(this.worldObj);
|
|
|
|
if (t instanceof TileReactorCell)
|
|
{
|
|
lowest = (TileReactorCell) t;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
checkPosition.y--;
|
|
}
|
|
|
|
return lowest;
|
|
}
|
|
|
|
@Override
|
|
public World getWorld()
|
|
{
|
|
return worldObj;
|
|
}
|
|
|
|
@Override
|
|
public Vector3 getPosition()
|
|
{
|
|
return new Vector3(this);
|
|
}
|
|
|
|
@Override
|
|
public MultiBlockHandler<TileReactorCell> getMultiBlock()
|
|
{
|
|
if (multiBlock == null)
|
|
{
|
|
multiBlock = new MultiBlockHandler<TileReactorCell>(this);
|
|
}
|
|
|
|
return multiBlock;
|
|
}
|
|
|
|
public int getHeight()
|
|
{
|
|
int height = 0;
|
|
Vector3 checkPosition = new Vector3(this);
|
|
TileEntity tile = this;
|
|
|
|
while (tile instanceof TileReactorCell)
|
|
{
|
|
checkPosition.y++;
|
|
height++;
|
|
tile = checkPosition.getTileEntity(worldObj);
|
|
}
|
|
|
|
return height;
|
|
}
|
|
|
|
@Override
|
|
public Packet getDescriptionPacket()
|
|
{
|
|
return ResonantInduction.PACKET_ANNOTATION.getPacket(this);
|
|
}
|
|
|
|
private void meltDown()
|
|
{
|
|
if (!worldObj.isRemote)
|
|
{
|
|
// No need to destroy reactor cell since explosion will do that for us.
|
|
ReactorExplosion reactorExplosion = new ReactorExplosion(worldObj, null, xCoord, yCoord, zCoord, 9f);
|
|
reactorExplosion.doExplosionA();
|
|
reactorExplosion.doExplosionB(true);
|
|
}
|
|
}
|
|
|
|
/** Reads a tile entity from NBT. */
|
|
@SyncedInput
|
|
@Override
|
|
public void readFromNBT(NBTTagCompound nbt)
|
|
{
|
|
super.readFromNBT(nbt);
|
|
temperature = nbt.getFloat("temperature");
|
|
tank.readFromNBT(nbt);
|
|
getMultiBlock().load(nbt);
|
|
}
|
|
|
|
/** Writes a tile entity to NBT. */
|
|
@SyncedOutput
|
|
@Override
|
|
public void writeToNBT(NBTTagCompound nbt)
|
|
{
|
|
super.writeToNBT(nbt);
|
|
nbt.setFloat("temperature", temperature);
|
|
tank.writeToNBT(nbt);
|
|
getMultiBlock().save(nbt);
|
|
}
|
|
|
|
@Override
|
|
public int getInventoryStackLimit()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
/** Returns true if automation can insert the given item in the given slot from the given side. Args: Slot, item, side */
|
|
@Override
|
|
public boolean canInsertItem(int slot, ItemStack items, int side)
|
|
{
|
|
return this.isItemValidForSlot(slot, items);
|
|
}
|
|
|
|
@Override
|
|
public boolean isUseableByPlayer(EntityPlayer par1EntityPlayer)
|
|
{
|
|
return worldObj.getBlockTileEntity(xCoord, yCoord, zCoord) != this ? false : par1EntityPlayer.getDistanceSq(xCoord + 0.5D, yCoord + 0.5D, zCoord + 0.5D) <= 64.0D;
|
|
}
|
|
|
|
@Override
|
|
public String getInvName()
|
|
{
|
|
return getBlockType().getLocalizedName();
|
|
}
|
|
|
|
@Override
|
|
public boolean isItemValidForSlot(int slotID, ItemStack itemStack)
|
|
{
|
|
if (getMultiBlock().isPrimary() && getMultiBlock().get().getStackInSlot(0) == null)
|
|
{
|
|
return itemStack.getItem() instanceof IReactorComponent;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/** Fluid Functions. */
|
|
@Override
|
|
public int fill(ForgeDirection from, FluidStack resource, boolean doFill)
|
|
{
|
|
return getMultiBlock().get().tank.fill(resource, doFill);
|
|
}
|
|
|
|
@Override
|
|
public FluidStack drain(ForgeDirection from, int maxDrain, boolean doDrain)
|
|
{
|
|
return tank.drain(maxDrain, doDrain);
|
|
}
|
|
|
|
@Override
|
|
public FluidStack drain(ForgeDirection from, FluidStack resource, boolean doDrain)
|
|
{
|
|
if (resource == null || !resource.isFluidEqual(tank.getFluid()))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return tank.drain(resource.amount, doDrain);
|
|
}
|
|
|
|
@Override
|
|
public boolean canFill(ForgeDirection from, Fluid fluid)
|
|
{
|
|
return fluid == Atomic.FLUID_PLASMA;
|
|
}
|
|
|
|
@Override
|
|
public boolean canDrain(ForgeDirection from, Fluid fluid)
|
|
{
|
|
return fluid == Atomic.FLUID_TOXIC_WASTE;
|
|
}
|
|
|
|
@Override
|
|
public FluidTankInfo[] getTankInfo(ForgeDirection from)
|
|
{
|
|
return new FluidTankInfo[]
|
|
{ tank.getInfo() };
|
|
}
|
|
|
|
@Override
|
|
@SideOnly(Side.CLIENT)
|
|
public AxisAlignedBB getRenderBoundingBox()
|
|
{
|
|
if (getMultiBlock().isPrimary() && getMultiBlock().isConstructed())
|
|
{
|
|
return INFINITE_EXTENT_AABB;
|
|
}
|
|
|
|
return super.getRenderBoundingBox();
|
|
}
|
|
|
|
@Override
|
|
public void heat(long energy)
|
|
{
|
|
internalEnergy = Math.max(internalEnergy + energy, 0);
|
|
}
|
|
|
|
@Override
|
|
public float getTemperature()
|
|
{
|
|
return temperature;
|
|
}
|
|
}
|