| 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.ISalinationSolar; |
| 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.SolarEvaporationRecipe; |
| 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.FluidRegistry; |
| import net.minecraftforge.fluids.FluidStack; |
| import net.minecraftforge.fluids.FluidTank; |
| import cpw.mods.fml.relauncher.Side; |
| import cpw.mods.fml.relauncher.SideOnly; |
| |
| public class TileEntitySolarEvaporationController extends TileEntitySolarEvaporationBlock implements IActiveState, ITankManager |
| { |
| public static final int MAX_OUTPUT = 10000; |
| public static final int MAX_SOLARS = 4; |
| public static final int WARMUP = 10000; |
| |
| public FluidTank inputTank = new FluidTank(0); |
| public FluidTank outputTank = new FluidTank(MAX_OUTPUT); |
| |
| public Set<TileEntitySolarEvaporationBlock> tankParts = new HashSet<TileEntitySolarEvaporationBlock>(); |
| public ISalinationSolar[] solars = new ISalinationSolar[4]; |
| |
| public boolean temperatureSet = false; |
| |
| public double partialInput = 0; |
| public double partialOutput = 0; |
| |
| public float biomeTemp = 0; |
| public float temperature = 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 TileEntitySolarEvaporationController() |
| { |
| super("SolarEvaporationController"); |
| |
| inventory = new ItemStack[4]; |
| } |
| |
| @Override |
| public void onUpdate() |
| { |
| super.onUpdate(); |
| |
| if(!worldObj.isRemote) |
| { |
| updatedThisTick = false; |
| |
| if(ticker == 5) |
| { |
| refresh(); |
| } |
| |
| updateTemperature(); |
| manageBuckets(); |
| |
| SolarEvaporationRecipe recipe = getRecipe(); |
| |
| if(canOperate(recipe)) |
| { |
| int outputNeeded = outputTank.getCapacity()-outputTank.getFluidAmount(); |
| int inputStored = inputTank.getFluidAmount(); |
| |
| partialInput += Math.min(inputTank.getFluidAmount(), getTemperature()*recipe.recipeInput.ingredient.amount); |
| |
| 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; |
| } |
| } |
| |
| 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 SolarEvaporationRecipe getRecipe() |
| { |
| return RecipeHandler.getSolarEvaporationRecipe(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.SOLAR_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()); |
| } |
| |
| temperature = Math.min(getMaxTemperature(), getTemperature()); |
| } |
| else { |
| clearStructure(); |
| } |
| } |
| } |
| } |
| |
| public boolean canOperate(SolarEvaporationRecipe recipe) |
| { |
| if(!structured || height < 3 || height > 18 || inputTank.getFluid() == null || getTempMultiplier() == 0) |
| { |
| return false; |
| } |
| |
| if(recipe != null && recipe.canOperate(inputTank, outputTank)) |
| { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| private void manageBuckets() |
| { |
| if(inventory[2] != null) |
| { |
| if(outputTank.getFluid() != null && outputTank.getFluid().amount >= FluidContainerRegistry.BUCKET_VOLUME) |
| { |
| if(FluidContainerRegistry.isEmptyContainer(inventory[2])) |
| { |
| ItemStack tempStack = FluidContainerRegistry.fillFluidContainer(outputTank.getFluid(), inventory[2]); |
| |
| if(tempStack != null) |
| { |
| if(inventory[3] == null) |
| { |
| outputTank.drain(FluidContainerRegistry.BUCKET_VOLUME, true); |
| |
| inventory[3] = tempStack; |
| inventory[2].stackSize--; |
| |
| if(inventory[2].stackSize <= 0) |
| { |
| inventory[2] = null; |
| } |
| |
| markDirty(); |
| } |
| else if(tempStack.isItemEqual(inventory[3]) && tempStack.getMaxStackSize() > inventory[3].stackSize) |
| { |
| outputTank.drain(FluidContainerRegistry.BUCKET_VOLUME, true); |
| |
| inventory[3].stackSize++; |
| inventory[2].stackSize--; |
| |
| if(inventory[2].stackSize <= 0) |
| { |
| inventory[2] = null; |
| } |
| |
| markDirty(); |
| } |
| } |
| } |
| } |
| } |
| |
| if(structured) |
| { |
| if(FluidContainerRegistry.isFilledContainer(inventory[0])) |
| { |
| FluidStack itemFluid = FluidContainerRegistry.getFluidForFilledItem(inventory[0]); |
| |
| if((inputTank.getFluid() == null && itemFluid.amount <= getMaxFluid()) || inputTank.getFluid().amount+itemFluid.amount <= getMaxFluid()) |
| { |
| if(!hasRecipe(itemFluid.getFluid()) || (inputTank.getFluid() != null && !inputTank.getFluid().isFluidEqual(itemFluid))) |
| { |
| return; |
| } |
| |
| ItemStack containerItem = inventory[0].getItem().getContainerItem(inventory[0]); |
| |
| boolean filled = false; |
| |
| if(containerItem != null) |
| { |
| if(inventory[1] == null || (inventory[1].isItemEqual(containerItem) && inventory[1].stackSize+1 <= containerItem.getMaxStackSize())) |
| { |
| inventory[0] = null; |
| |
| if(inventory[1] == null) |
| { |
| inventory[1] = containerItem; |
| } |
| else { |
| inventory[1].stackSize++; |
| } |
| |
| filled = true; |
| } |
| } |
| else { |
| inventory[0].stackSize--; |
| |
| if(inventory[0].stackSize == 0) |
| { |
| inventory[0] = null; |
| } |
| |
| filled = true; |
| } |
| |
| if(filled) |
| { |
| inputTank.fill(itemFluid, true); |
| } |
| } |
| } |
| } |
| } |
| |
| private void updateTemperature() |
| { |
| float max = getMaxTemperature(); |
| float incr = (max/WARMUP)*getTempMultiplier(); |
| |
| if(getTempMultiplier() == 0) |
| { |
| temperature = Math.max(0, getTemperature()-(max/WARMUP)); |
| } |
| else { |
| temperature = Math.min(max, getTemperature()+incr); |
| } |
| } |
| |
| public float getTemperature() |
| { |
| return temperature; |
| } |
| |
| public float getMaxTemperature() |
| { |
| if(!structured) |
| { |
| return 0; |
| } |
| |
| return 1 + (height-3)*0.5F; |
| } |
| |
| public float getTempMultiplier() |
| { |
| if(!temperatureSet) |
| { |
| biomeTemp = worldObj.getBiomeGenForCoordsBody(xCoord, zCoord).getFloatTemperature(xCoord, yCoord, zCoord); |
| temperatureSet = true; |
| } |
| |
| return biomeTemp*((float)getActiveSolars()/MAX_SOLARS); |
| } |
| |
| public int getActiveSolars() |
| { |
| if(worldObj.isRemote) |
| { |
| return clientSolarAmount; |
| } |
| |
| int ret = 0; |
| |
| for(ISalinationSolar solar : solars) |
| { |
| if(solar != null && solar.seesSun()) |
| { |
| ret++; |
| } |
| } |
| |
| return ret; |
| } |
| |
| public boolean buildStructure() |
| { |
| ForgeDirection right = MekanismUtils.getRight(facing); |
| |
| height = 0; |
| controllerConflict = false; |
| updatedThisTick = true; |
| |
| if(!scanBottomLayer()) |
| { |
| height = 0; |
| return false; |
| } |
| |
| Coord4D startPoint = Coord4D.get(this).getFromSide(right); |
| startPoint = isLeftOnFace ? startPoint.getFromSide(right) : startPoint; |
| |
| while(startPoint.getFromSide(ForgeDirection.UP).getTileEntity(worldObj) instanceof TileEntitySolarEvaporationBlock) |
| { |
| startPoint.step(ForgeDirection.UP); |
| } |
| |
| int middle = 0; |
| |
| Coord4D middlePointer = startPoint.getFromSide(ForgeDirection.DOWN); |
| |
| while(scanMiddleLayer(middlePointer)) |
| { |
| middlePointer = middlePointer.getFromSide(ForgeDirection.DOWN); |
| middle++; |
| } |
| |
| if(height < 3 || height > 18 || middle != height-2) |
| { |
| height = 0; |
| return false; |
| } |
| |
| structured = scanTopLayer(startPoint); |
| height = structured ? height : 0; |
| |
| markDirty(); |
| |
| return structured; |
| } |
| |
| public boolean scanTopLayer(Coord4D current) |
| { |
| ForgeDirection left = MekanismUtils.getLeft(facing); |
| ForgeDirection back = MekanismUtils.getBack(facing); |
| |
| for(int x = 0; x < 4; x++) |
| { |
| for(int z = 0; z < 4; z++) |
| { |
| Coord4D pointer = current.getFromSide(left, x).getFromSide(back, z); |
| |
| int corner = getCorner(x, z); |
| |
| if(corner != -1) |
| { |
| if(addSolarPanel(pointer.getTileEntity(worldObj), corner)) |
| { |
| continue; |
| } |
| else if(!addTankPart(pointer.getTileEntity(worldObj))) |
| { |
| return false; |
| } |
| } |
| else { |
| if((x == 1 || x == 2) && (z == 1 || z == 2)) |
| { |
| if(!pointer.isAirBlock(worldObj)) |
| { |
| return false; |
| } |
| } |
| else { |
| TileEntity pointerTile = pointer.getTileEntity(worldObj); |
| |
| if(!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 scanMiddleLayer(Coord4D current) |
| { |
| ForgeDirection left = MekanismUtils.getLeft(facing); |
| ForgeDirection back = MekanismUtils.getBack(facing); |
| |
| for(int x = 0; x < 4; x++) |
| { |
| for(int z = 0; z < 4; z++) |
| { |
| Coord4D pointer = current.getFromSide(left, x).getFromSide(back, z); |
| |
| if((x == 1 || x == 2) && (z == 1 || z == 2)) |
| { |
| if(!pointer.isAirBlock(worldObj)) |
| { |
| return false; |
| } |
| } |
| else { |
| TileEntity pointerTile = pointer.getTileEntity(worldObj); |
| |
| if(!addTankPart(pointerTile)) |
| { |
| return false; |
| } |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| public boolean scanBottomLayer() |
| { |
| height = 1; |
| Coord4D baseBlock = Coord4D.get(this); |
| |
| while(baseBlock.getFromSide(ForgeDirection.DOWN).getTileEntity(worldObj) instanceof TileEntitySolarEvaporationBlock) |
| { |
| baseBlock.step(ForgeDirection.DOWN); |
| height++; |
| } |
| |
| ForgeDirection left = MekanismUtils.getLeft(facing); |
| ForgeDirection right = MekanismUtils.getRight(facing); |
| |
| if(!scanBottomRow(baseBlock)) |
| { |
| return false; |
| }; |
| |
| if(!scanBottomRow(baseBlock.getFromSide(left))) |
| { |
| return false; |
| }; |
| |
| if(!scanBottomRow(baseBlock.getFromSide(right))) |
| { |
| return false; |
| }; |
| |
| boolean twoLeft = scanBottomRow(baseBlock.getFromSide(left).getFromSide(left)); |
| boolean twoRight = scanBottomRow(baseBlock.getFromSide(right).getFromSide(right)); |
| |
| if(twoLeft == twoRight) |
| { |
| return false; |
| } |
| |
| isLeftOnFace = twoRight; |
| |
| return true; |
| } |
| |
| /** |
| * Scans the bottom row of this multiblock, going in a line across the base. |
| * @param start |
| * @return |
| */ |
| public boolean scanBottomRow(Coord4D start) |
| { |
| ForgeDirection back = MekanismUtils.getBack(facing); |
| Coord4D current = start; |
| |
| for(int i = 1; i <= 4; i++) |
| { |
| TileEntity tile = current.getTileEntity(worldObj); |
| |
| if(!addTankPart(tile)) |
| { |
| return false; |
| } |
| |
| current = current.getFromSide(back); |
| } |
| |
| return true; |
| } |
| |
| public boolean addTankPart(TileEntity tile) |
| { |
| if(tile instanceof TileEntitySolarEvaporationBlock && (tile == this || !(tile instanceof TileEntitySolarEvaporationController))) |
| { |
| if(tile != this) |
| { |
| ((TileEntitySolarEvaporationBlock)tile).addToStructure(this); |
| tankParts.add((TileEntitySolarEvaporationBlock)tile); |
| } |
| |
| return true; |
| } |
| else { |
| if(tile != this && tile instanceof TileEntitySolarEvaporationController) |
| { |
| controllerConflict = true; |
| } |
| |
| return false; |
| } |
| } |
| |
| public boolean addSolarPanel(TileEntity tile, int i) |
| { |
| if(tile instanceof ISalinationSolar && !tile.isInvalid()) |
| { |
| solars[i] = (ISalinationSolar)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)(getMaxTemperature() == 0 ? 0 : getTemperature()*i/getMaxTemperature()); |
| } |
| |
| 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(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(); |
| |
| 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 TileEntitySolarEvaporationBlock; |
| } |
| }); |
| } |
| } |
| |
| 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().fluidID); |
| data.add(inputTank.getFluid().amount); |
| } |
| else { |
| data.add(false); |
| } |
| |
| if(outputTank.getFluid() != null) |
| { |
| data.add(true); |
| data.add(outputTank.getFluid().fluidID); |
| 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); |
| |
| 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(TileEntitySolarEvaporationBlock tankPart : tankParts) |
| { |
| tankPart.controllerGone(); |
| } |
| |
| tankParts.clear(); |
| solars = new ISalinationSolar[] {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 false; |
| } |
| |
| @Override |
| public boolean lightUpdate() |
| { |
| return false; |
| } |
| |
| @Override |
| public Object[] getTanks() |
| { |
| return new Object[] {inputTank, outputTank}; |
| } |
| } |