package openblocks.client.renderer.tileentity.tank;
import com.google.common.collect.Maps;
import java.util.Map;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.fluids.FluidStack;
import openblocks.common.tileentity.TileEntityTank;
import openmods.liquids.GenericTank;
import openmods.utils.Diagonal;
public class TankRenderLogic {
private static class TankConnections implements ITankConnections {
private final GenericTank tank;
private final Map<Diagonal, DiagonalConnection> diagonalConnections;
private final Map<ForgeDirection, HorizontalConnection> horizontalConnections;
private final VerticalConnection topConnection;
private final VerticalConnection bottomConnection;
public TankConnections(GenericTank tank, Map<Diagonal, DiagonalConnection> diagonalConnections, Map<ForgeDirection, HorizontalConnection> horizontalConnections, VerticalConnection topConnection, VerticalConnection bottomConnection) {
this.tank = tank;
this.diagonalConnections = diagonalConnections;
this.horizontalConnections = horizontalConnections;
this.topConnection = topConnection;
this.bottomConnection = bottomConnection;
}
@Override
public VerticalConnection getTopConnection() {
return topConnection;
}
@Override
public VerticalConnection getBottomConnection() {
return bottomConnection;
}
@Override
public HorizontalConnection getHorizontalConnection(ForgeDirection dir) {
return horizontalConnections.get(dir);
}
@Override
public DiagonalConnection getDiagonalConnection(Diagonal dir) {
return diagonalConnections.get(dir);
}
public void updateFluid(FluidStack fluidStack) {
for (Map.Entry<Diagonal, DiagonalConnection> e : diagonalConnections.entrySet())
e.getValue().updateFluid(e.getKey().getOpposite(), fluidStack);
for (Map.Entry<ForgeDirection, HorizontalConnection> e : horizontalConnections.entrySet())
e.getValue().updateFluid(e.getKey().getOpposite(), fluidStack);
topConnection.updateBottomFluid(fluidStack, tank.getSpace() == 0);
bottomConnection.updateTopFluid(fluidStack);
}
private static boolean checkConsistency(RenderConnection connection, int x, int y, int z, ForgeDirection dir) {
return connection != null && connection.isPositionEqualTo(x, y, z, dir);
}
private static boolean checkConsistency(RenderConnection connection, int x, int y, int z, Diagonal dir) {
return connection != null && connection.isPositionEqualTo(x, y, z, dir);
}
private boolean checkHorizontalConsistency(int x, int y, int z, ForgeDirection dir) {
return checkConsistency(horizontalConnections.get(dir), x, y, z, dir);
}
private boolean checkDiagonalConsistency(int x, int y, int z, Diagonal dir) {
return checkConsistency(diagonalConnections.get(dir), x, y, z, dir);
}
public boolean checkConsistency(int x, int y, int z) {
return checkConsistency(topConnection, x, y, z, ForgeDirection.UP) &&
checkConsistency(bottomConnection, x, y, z, ForgeDirection.DOWN) &&
checkHorizontalConsistency(x, y, z, ForgeDirection.NORTH) &&
checkHorizontalConsistency(x, y, z, ForgeDirection.SOUTH) &&
checkHorizontalConsistency(x, y, z, ForgeDirection.EAST) &&
checkHorizontalConsistency(x, y, z, ForgeDirection.WEST) &&
checkDiagonalConsistency(x, y, z, Diagonal.NE) &&
checkDiagonalConsistency(x, y, z, Diagonal.NW) &&
checkDiagonalConsistency(x, y, z, Diagonal.SE) &&
checkDiagonalConsistency(x, y, z, Diagonal.SW);
}
public void detach() {
for (Map.Entry<Diagonal, DiagonalConnection> e : diagonalConnections.entrySet())
e.getValue().clearFluid(e.getKey().getOpposite());
for (Map.Entry<ForgeDirection, HorizontalConnection> e : horizontalConnections.entrySet())
e.getValue().clearFluid(e.getKey().getOpposite());
if (topConnection != null) {
topConnection.clearBottomFluid();
}
if (bottomConnection != null) {
bottomConnection.clearTopFluid();
}
}
}
private static class TankRenderFluidData implements ITankRenderFluidData {
private final TankConnections connections;
private final GenericTank tank;
private final float phase;
public TankRenderFluidData(TankConnections connections, GenericTank tank, float phase) {
this.connections = connections;
this.tank = tank;
this.phase = phase;
}
private static boolean isConnected(GridConnection connection) {
return connection != null? connection.isConnected() : false;
}
@Override
public boolean shouldRenderFluidWall(ForgeDirection side) {
switch (side) {
case DOWN:
return !isConnected(connections.getBottomConnection());
case UP:
return !isConnected(connections.getTopConnection());
case EAST:
case WEST:
case NORTH:
case SOUTH: {
return !isConnected(connections.getHorizontalConnection(side));
}
default:
return true;
}
}
@Override
public boolean hasFluid() {
return tank.getFluidAmount() > 0;
}
@Override
public FluidStack getFluid() {
return tank.getFluid();
}
@Override
public float getCenterFluidLevel(float time) {
final float raw = (float)tank.getFluidAmount() / tank.getCapacity();
final float waving = TankRenderUtils.calculateWaveAmplitude(time, phase) + raw;
return TankRenderUtils.clampLevel(waving);
}
@Override
public float getCornerFluidLevel(Diagonal corner, float time) {
final DiagonalConnection diagonal = connections.getDiagonalConnection(corner);
return diagonal != null? diagonal.getRenderHeight(corner.getOpposite(), time) : getCenterFluidLevel(time);
}
}
private final GenericTank tank;
private int x;
private int y;
private int z;
private World world;
private TankConnections connections;
private TankRenderFluidData renderData;
public TankRenderLogic(GenericTank tank) {
this.tank = tank;
}
private DoubledCoords createCoords(ForgeDirection dir) {
return new DoubledCoords(x, y, z, dir);
}
private DoubledCoords createCoords(Diagonal dir) {
return new DoubledCoords(x, y, z, dir);
}
private ITankConnections getNeighbourTank(int x, int y, int z) {
TileEntity te = TankRenderUtils.getTileEntitySafe(world, x, y, z);
return (te instanceof TileEntityTank)? ((TileEntityTank)te).getTankConnections() : null;
}
private ITankConnections getNeighbourTank(ForgeDirection dir) {
return getNeighbourTank(x + dir.offsetX, y + dir.offsetY, z + dir.offsetZ);
}
private ITankConnections getNeighbourTank(Diagonal dir) {
return getNeighbourTank(x + dir.offsetX, y + dir.offsetY, z + dir.offsetZ);
}
private DiagonalConnection selectDiagonalConnection(ITankConnections tankCW, ITankConnections tankD, ITankConnections tankCCW, Diagonal dir) {
final Diagonal start = dir;
dir = dir.rotateCW();
if (tankCW != null) return tankCW.getDiagonalConnection(dir);
dir = dir.rotateCW();
if (tankD != null) return tankD.getDiagonalConnection(dir);
dir = dir.rotateCW();
if (tankCCW != null) return tankCCW.getDiagonalConnection(dir);
return new DiagonalConnection(TankRenderUtils.calculatePhase(x, y, z, start), createCoords(start));
}
private void tryCornerConnection(Map<Diagonal, DiagonalConnection> diagonalConnections, ITankConnections tankCW, ITankConnections tankD, ITankConnections tankCCW, Diagonal dir) {
final DiagonalConnection connection = selectDiagonalConnection(tankCW, tankD, tankCCW, dir);
diagonalConnections.put(dir, connection);
}
private void tryHorizontalConnection(Map<ForgeDirection, HorizontalConnection> horizontalConnections, ITankConnections neighbour, ForgeDirection dir) {
final HorizontalConnection connection = (neighbour != null)? neighbour.getHorizontalConnection(dir.getOpposite()) : new HorizontalConnection(createCoords(dir));
horizontalConnections.put(dir, connection);
}
private VerticalConnection tryBottomConnection(ITankConnections neighbour) {
return neighbour != null? neighbour.getTopConnection() : new VerticalConnection(createCoords(ForgeDirection.DOWN));
}
private VerticalConnection tryTopConnection(ITankConnections neighbour) {
return neighbour != null? neighbour.getBottomConnection() : new VerticalConnection(createCoords(ForgeDirection.UP));
}
private TankConnections updateConnections() {
final ITankConnections tankN = getNeighbourTank(ForgeDirection.NORTH);
final ITankConnections tankS = getNeighbourTank(ForgeDirection.SOUTH);
final ITankConnections tankW = getNeighbourTank(ForgeDirection.WEST);
final ITankConnections tankE = getNeighbourTank(ForgeDirection.EAST);
final ITankConnections tankNE = getNeighbourTank(Diagonal.NE);
final ITankConnections tankNW = getNeighbourTank(Diagonal.NW);
final ITankConnections tankSE = getNeighbourTank(Diagonal.SE);
final ITankConnections tankSW = getNeighbourTank(Diagonal.SW);
final ITankConnections tankT = getNeighbourTank(ForgeDirection.UP);
final ITankConnections tankB = getNeighbourTank(ForgeDirection.DOWN);
final VerticalConnection topConnection = tryTopConnection(tankT);
final VerticalConnection bottomConnection = tryBottomConnection(tankB);
final Map<Diagonal, DiagonalConnection> diagonalConnections = Maps.newEnumMap(Diagonal.class);
final Map<ForgeDirection, HorizontalConnection> horizontalConnections = Maps.newEnumMap(ForgeDirection.class);
tryHorizontalConnection(horizontalConnections, tankN, ForgeDirection.NORTH);
tryHorizontalConnection(horizontalConnections, tankS, ForgeDirection.SOUTH);
tryHorizontalConnection(horizontalConnections, tankW, ForgeDirection.WEST);
tryHorizontalConnection(horizontalConnections, tankE, ForgeDirection.EAST);
tryCornerConnection(diagonalConnections, tankN, tankNW, tankW, Diagonal.NW);
tryCornerConnection(diagonalConnections, tankW, tankSW, tankS, Diagonal.SW);
tryCornerConnection(diagonalConnections, tankE, tankNE, tankN, Diagonal.NE);
tryCornerConnection(diagonalConnections, tankS, tankSE, tankE, Diagonal.SE);
return new TankConnections(tank, diagonalConnections, horizontalConnections, topConnection, bottomConnection);
}
public void initialize(World world, int x, int y, int z) {
this.world = world;
this.x = x;
this.y = y;
this.z = z;
if (this.connections != null) connections.detach();
if (world == null) {
this.connections = null;
this.renderData = null;
} else {
float phase = TankRenderUtils.calculatePhase(x, y, z);
this.connections = updateConnections();
this.renderData = new TankRenderFluidData(connections, tank, phase);
}
}
public void validateConnections(World world, int x, int y, int z) {
if (world != this.world || connections == null || !connections.checkConsistency(x, y, z))
initialize(world, x, y, z);
}
public void invalidateConnections() {
if (this.connections != null) connections.detach();
this.connections = null;
this.renderData = null;
}
public void updateFluid(FluidStack stack) {
if (connections != null) connections.updateFluid(stack);
}
public ITankRenderFluidData getTankRenderData() {
return renderData;
}
public ITankConnections getTankConnections() {
return connections;
}
}