blob: 9590681d73c5c01b3aaeed0b12acadac2caac42a [file] [log] [blame] [raw]
package mekanism.common.tile;
import io.netty.buffer.ByteBuf;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import mekanism.api.Coord4D;
import mekanism.api.IEvaporationSolar;
import mekanism.api.MekanismConfig.general;
import mekanism.api.Range4D;
import mekanism.client.SparkleAnimation.INodeChecker;
import mekanism.common.Mekanism;
import mekanism.common.base.IActiveState;
import mekanism.common.base.ITankManager;
import mekanism.common.content.tank.TankUpdateProtocol;
import mekanism.common.network.PacketTileEntity.TileEntityMessage;
import mekanism.common.recipe.RecipeHandler;
import mekanism.common.recipe.RecipeHandler.Recipe;
import mekanism.common.recipe.inputs.FluidInput;
import mekanism.common.recipe.machines.ThermalEvaporationRecipe;
import mekanism.common.util.FluidContainerUtils;
import mekanism.common.util.FluidContainerUtils.FluidChecker;
import mekanism.common.util.MekanismUtils;
import net.minecraft.block.Block;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.AxisAlignedBB;
import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidContainerRegistry;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidTank;
import net.minecraftforge.fluids.IFluidContainerItem;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public class TileEntityThermalEvaporationController extends TileEntityThermalEvaporationBlock implements IActiveState, ITankManager
{
public static final int MAX_OUTPUT = 10000;
public static final int MAX_SOLARS = 4;
public static final int MAX_HEIGHT = 18;
public FluidTank inputTank = new FluidTank(0);
public FluidTank outputTank = new FluidTank(MAX_OUTPUT);
public Set<Coord4D> tankParts = new HashSet<Coord4D>();
public IEvaporationSolar[] solars = new IEvaporationSolar[4];
public boolean temperatureSet = false;
public double partialInput = 0;
public double partialOutput = 0;
public float biomeTemp = 0;
public float temperature = 0;
public float heatToAbsorb = 0;
public float lastGain = 0;
public int height = 0;
public boolean structured = false;
public boolean controllerConflict = false;
public boolean isLeftOnFace;
public boolean updatedThisTick = false;
public int clientSolarAmount;
public boolean cacheStructure = false;
public float prevScale;
public float totalLoss = 0;
public TileEntityThermalEvaporationController()
{
super("ThermalEvaporationController");
inventory = new ItemStack[4];
}
@Override
public void onUpdate()
{
super.onUpdate();
if(!worldObj.isRemote)
{
updatedThisTick = false;
if(ticker == 5)
{
refresh();
}
if(structured)
{
updateTemperature();
}
manageBuckets();
ThermalEvaporationRecipe recipe = getRecipe();
if(canOperate(recipe))
{
int outputNeeded = outputTank.getCapacity()-outputTank.getFluidAmount();
int inputStored = inputTank.getFluidAmount();
double tempMult = Math.max(0, getTemperature())*general.evaporationTempMultiplier;
double inputToUse = (tempMult*recipe.recipeInput.ingredient.amount)*((float)height/(float)MAX_HEIGHT);
inputToUse = Math.min(inputTank.getFluidAmount(), inputToUse);
lastGain = (float)inputToUse/(float)recipe.recipeInput.ingredient.amount;
partialInput += inputToUse;
if(partialInput >= 1)
{
int inputInt = (int)Math.floor(partialInput);
inputTank.drain(inputInt, true);
partialInput %= 1;
partialOutput += ((double)inputInt)/recipe.recipeInput.ingredient.amount;
}
if(partialOutput >= 1)
{
int outputInt = (int)Math.floor(partialOutput);
outputTank.fill(new FluidStack(recipe.recipeOutput.output.getFluid(), outputInt), true);
partialOutput %= 1;
}
}
else {
lastGain = 0;
}
if(structured)
{
if(Math.abs((float)inputTank.getFluidAmount()/inputTank.getCapacity()-prevScale) > 0.01)
{
Mekanism.packetHandler.sendToReceivers(new TileEntityMessage(Coord4D.get(this), getNetworkedData(new ArrayList())), new Range4D(Coord4D.get(this)));
prevScale = (float)inputTank.getFluidAmount()/inputTank.getCapacity();
}
}
}
}
public ThermalEvaporationRecipe getRecipe()
{
return RecipeHandler.getThermalEvaporationRecipe(new FluidInput(inputTank.getFluid()));
}
@Override
public void onChunkUnload()
{
super.onChunkUnload();
refresh();
}
@Override
public void onNeighborChange(Block block)
{
super.onNeighborChange(block);
refresh();
}
public boolean hasRecipe(Fluid fluid)
{
if(fluid == null)
{
return false;
}
return Recipe.THERMAL_EVAPORATION_PLANT.containsRecipe(fluid);
}
protected void refresh()
{
if(!worldObj.isRemote)
{
if(!updatedThisTick)
{
boolean prev = structured;
clearStructure();
structured = buildStructure();
if(structured != prev)
{
Mekanism.packetHandler.sendToReceivers(new TileEntityMessage(Coord4D.get(this), getNetworkedData(new ArrayList())), new Range4D(Coord4D.get(this)));
}
if(structured)
{
inputTank.setCapacity(getMaxFluid());
if(inputTank.getFluid() != null)
{
inputTank.getFluid().amount = Math.min(inputTank.getFluid().amount, getMaxFluid());
}
}
else {
clearStructure();
}
}
}
}
public boolean canOperate(ThermalEvaporationRecipe recipe)
{
if(!structured || height < 3 || height > MAX_HEIGHT || inputTank.getFluid() == null)
{
return false;
}
if(recipe != null && recipe.canOperate(inputTank, outputTank))
{
return true;
}
return false;
}
private void manageBuckets()
{
if(inventory[2] != null && outputTank.getFluid() != null)
{
if(inventory[2].getItem() instanceof IFluidContainerItem)
{
FluidContainerUtils.handleContainerItemFill(this, outputTank, 2, 3);
}
else if(FluidContainerRegistry.isEmptyContainer(inventory[2]))
{
FluidContainerUtils.handleRegistryItemFill(this, outputTank, 2, 3);
}
}
if(structured)
{
if(inventory[0] != null)
{
if(inventory[0].getItem() instanceof IFluidContainerItem)
{
FluidContainerUtils.handleContainerItemEmpty(this, inputTank, 0, 1, new FluidChecker() {
@Override
public boolean isValid(Fluid f)
{
return hasRecipe(f);
}
});
}
else if(FluidContainerRegistry.isFilledContainer(inventory[0]))
{
FluidContainerUtils.handleRegistryItemEmpty(this, inputTank, 0, 1, new FluidChecker() {
@Override
public boolean isValid(Fluid f)
{
return hasRecipe(f);
}
});
}
}
}
}
private void updateTemperature()
{
if(!temperatureSet)
{
biomeTemp = worldObj.getBiomeGenForCoordsBody(xCoord, zCoord).getFloatTemperature(xCoord, yCoord, zCoord);
temperatureSet = true;
}
heatToAbsorb += getActiveSolars()*general.evaporationSolarMultiplier;
temperature += heatToAbsorb/(float)height;
float biome = biomeTemp-0.5F;
float base = biome > 0 ? biome*20 : biomeTemp*40;
if(Math.abs(temperature-base) < 0.001)
{
temperature = base;
}
float incr = (float)Math.sqrt(Math.abs(temperature-base))*(float)general.evaporationHeatDissipation;
if(temperature > base)
{
incr = -incr;
}
float prev = temperature;
temperature = (float)Math.min(general.evaporationMaxTemp, temperature + incr/(float)height);
if(incr < 0)
{
totalLoss = prev-temperature;
}
else {
totalLoss = 0;
}
heatToAbsorb = 0;
MekanismUtils.saveChunk(this);
}
public float getTemperature()
{
return temperature;
}
public int getActiveSolars()
{
if(worldObj.isRemote)
{
return clientSolarAmount;
}
int ret = 0;
for(IEvaporationSolar solar : solars)
{
if(solar != null && solar.seesSun())
{
ret++;
}
}
return ret;
}
public boolean buildStructure()
{
ForgeDirection right = MekanismUtils.getRight(facing);
ForgeDirection left = MekanismUtils.getLeft(facing);
height = 0;
controllerConflict = false;
updatedThisTick = true;
Coord4D startPoint = Coord4D.get(this);
while(startPoint.getFromSide(ForgeDirection.UP).getTileEntity(worldObj) instanceof TileEntityThermalEvaporationBlock)
{
startPoint.step(ForgeDirection.UP);
}
Coord4D test = startPoint.getFromSide(ForgeDirection.DOWN).getFromSide(right, 2);
isLeftOnFace = test.getTileEntity(worldObj) instanceof TileEntityThermalEvaporationBlock;
startPoint = startPoint.getFromSide(left, isLeftOnFace ? 1 : 2);
if(!scanTopLayer(startPoint))
{
return false;
}
height = 1;
Coord4D middlePointer = startPoint.getFromSide(ForgeDirection.DOWN);
while(scanLowerLayer(middlePointer))
{
middlePointer = middlePointer.getFromSide(ForgeDirection.DOWN);
}
if(height < 3 || height > MAX_HEIGHT)
{
height = 0;
return false;
}
structured = true;
markDirty();
return true;
}
public boolean scanTopLayer(Coord4D current)
{
ForgeDirection right = MekanismUtils.getRight(facing);
ForgeDirection back = MekanismUtils.getBack(facing);
for(int x = 0; x < 4; x++)
{
for(int z = 0; z < 4; z++)
{
Coord4D pointer = current.getFromSide(right, x).getFromSide(back, z);
TileEntity pointerTile = pointer.getTileEntity(worldObj);
int corner = getCorner(x, z);
if(corner != -1)
{
if(addSolarPanel(pointer.getTileEntity(worldObj), corner))
{
continue;
}
else if(pointer.getFromSide(ForgeDirection.UP).getTileEntity(worldObj) instanceof TileEntityThermalEvaporationBlock || !addTankPart(pointerTile))
{
return false;
}
}
else {
if((x == 1 || x == 2) && (z == 1 || z == 2))
{
if(!pointer.isAirBlock(worldObj))
{
return false;
}
}
else {
if(pointer.getFromSide(ForgeDirection.UP).getTileEntity(worldObj) instanceof TileEntityThermalEvaporationBlock || !addTankPart(pointerTile))
{
return false;
}
}
}
}
}
return true;
}
public int getMaxFluid()
{
return height*4*TankUpdateProtocol.FLUID_PER_TANK;
}
public int getCorner(int x, int z)
{
if(x == 0 && z == 0)
{
return 0;
}
else if(x == 0 && z == 3)
{
return 1;
}
else if(x == 3 && z == 0)
{
return 2;
}
else if(x == 3 && z == 3)
{
return 3;
}
return -1;
}
public boolean scanLowerLayer(Coord4D current)
{
ForgeDirection right = MekanismUtils.getRight(facing);
ForgeDirection back = MekanismUtils.getBack(facing);
boolean foundCenter = false;
for(int x = 0; x < 4; x++)
{
for(int z = 0; z < 4; z++)
{
Coord4D pointer = current.getFromSide(right, x).getFromSide(back, z);
TileEntity pointerTile = pointer.getTileEntity(worldObj);
if((x == 1 || x == 2) && (z == 1 || z == 2))
{
if(pointerTile instanceof TileEntityThermalEvaporationBlock)
{
if(!foundCenter)
{
if(x == 1 && z == 1)
{
foundCenter = true;
}
else {
height = -1;
return false;
}
}
}
else {
if(foundCenter || !pointer.isAirBlock(worldObj))
{
height = -1;
return false;
}
}
}
else {
if(!addTankPart(pointerTile))
{
height = -1;
return false;
}
}
}
}
height++;
return !foundCenter;
}
public boolean addTankPart(TileEntity tile)
{
if(tile instanceof TileEntityThermalEvaporationBlock && (tile == this || !(tile instanceof TileEntityThermalEvaporationController)))
{
if(tile != this)
{
((TileEntityThermalEvaporationBlock)tile).addToStructure(Coord4D.get(this));
tankParts.add(Coord4D.get(tile));
}
return true;
}
else {
if(tile != this && tile instanceof TileEntityThermalEvaporationController)
{
controllerConflict = true;
}
return false;
}
}
public boolean addSolarPanel(TileEntity tile, int i)
{
if(tile instanceof IEvaporationSolar && !tile.isInvalid())
{
solars[i] = (IEvaporationSolar)tile;
return true;
}
else {
return false;
}
}
public int getScaledInputLevel(int i)
{
return getMaxFluid() > 0 ? (inputTank.getFluid() != null ? inputTank.getFluid().amount*i / getMaxFluid() : 0) : 0;
}
public int getScaledOutputLevel(int i)
{
return outputTank.getFluid() != null ? outputTank.getFluid().amount*i / MAX_OUTPUT : 0;
}
public int getScaledTempLevel(int i)
{
return (int)(i*Math.min(1, getTemperature()/general.evaporationMaxTemp));
}
public Coord4D getRenderLocation()
{
if(!structured)
{
return null;
}
ForgeDirection right = MekanismUtils.getRight(facing);
Coord4D startPoint = Coord4D.get(this).getFromSide(right);
startPoint = isLeftOnFace ? startPoint.getFromSide(right) : startPoint;
startPoint = startPoint.getFromSide(right.getOpposite()).getFromSide(MekanismUtils.getBack(facing));
startPoint.translate(0, -(height-2), 0);
return startPoint;
}
@Override
public void handlePacketData(ByteBuf dataStream)
{
super.handlePacketData(dataStream);
if(worldObj.isRemote)
{
if(dataStream.readBoolean())
{
inputTank.setFluid(new FluidStack(dataStream.readInt(), dataStream.readInt()));
}
else {
inputTank.setFluid(null);
}
if(dataStream.readBoolean())
{
outputTank.setFluid(new FluidStack(dataStream.readInt(), dataStream.readInt()));
}
else {
outputTank.setFluid(null);
}
boolean prev = structured;
structured = dataStream.readBoolean();
controllerConflict = dataStream.readBoolean();
clientSolarAmount = dataStream.readInt();
height = dataStream.readInt();
temperature = dataStream.readFloat();
biomeTemp = dataStream.readFloat();
isLeftOnFace = dataStream.readBoolean();
lastGain = dataStream.readFloat();
totalLoss = dataStream.readFloat();
if(structured != prev)
{
inputTank.setCapacity(getMaxFluid());
worldObj.func_147479_m(xCoord, yCoord, zCoord);
if(structured)
{
Mekanism.proxy.doGenericSparkle(this, new INodeChecker() {
@Override
public boolean isNode(TileEntity tile)
{
return tile instanceof TileEntityThermalEvaporationBlock;
}
});
}
}
MekanismUtils.updateBlock(worldObj, xCoord, yCoord, zCoord);
}
}
@Override
public ArrayList getNetworkedData(ArrayList data)
{
super.getNetworkedData(data);
if(inputTank.getFluid() != null)
{
data.add(true);
data.add(inputTank.getFluid().getFluidID());
data.add(inputTank.getFluid().amount);
}
else {
data.add(false);
}
if(outputTank.getFluid() != null)
{
data.add(true);
data.add(outputTank.getFluid().getFluidID());
data.add(outputTank.getFluid().amount);
}
else {
data.add(false);
}
data.add(structured);
data.add(controllerConflict);
data.add(getActiveSolars());
data.add(height);
data.add(temperature);
data.add(biomeTemp);
data.add(isLeftOnFace);
data.add(lastGain);
data.add(totalLoss);
return data;
}
@Override
public void readFromNBT(NBTTagCompound nbtTags)
{
super.readFromNBT(nbtTags);
inputTank.readFromNBT(nbtTags.getCompoundTag("waterTank"));
outputTank.readFromNBT(nbtTags.getCompoundTag("brineTank"));
temperature = nbtTags.getFloat("temperature");
partialInput = nbtTags.getDouble("partialWater");
partialOutput = nbtTags.getDouble("partialBrine");
}
@Override
public void writeToNBT(NBTTagCompound nbtTags)
{
super.writeToNBT(nbtTags);
nbtTags.setTag("waterTank", inputTank.writeToNBT(new NBTTagCompound()));
nbtTags.setTag("brineTank", outputTank.writeToNBT(new NBTTagCompound()));
nbtTags.setFloat("temperature", temperature);
nbtTags.setDouble("partialWater", partialInput);
nbtTags.setDouble("partialBrine", partialOutput);
}
@Override
public boolean canSetFacing(int side)
{
return side != 0 && side != 1;
}
public void clearStructure()
{
for(Coord4D tankPart : tankParts)
{
TileEntity tile = tankPart.getTileEntity(worldObj);
if(tile instanceof TileEntityThermalEvaporationBlock)
{
((TileEntityThermalEvaporationBlock)tile).controllerGone();
}
}
tankParts.clear();
solars = new IEvaporationSolar[] {null, null, null, null};
}
@Override
@SideOnly(Side.CLIENT)
public AxisAlignedBB getRenderBoundingBox()
{
return INFINITE_EXTENT_AABB;
}
@Override
public boolean getActive()
{
return structured;
}
@Override
public void setActive(boolean active) {}
@Override
public boolean renderUpdate()
{
return true;
}
@Override
public boolean lightUpdate()
{
return false;
}
@Override
public Object[] getTanks()
{
return new Object[] {inputTank, outputTank};
}
}