package com.carpentersblocks.renderer.helper;
import static net.minecraftforge.common.util.ForgeDirection.EAST;
import static net.minecraftforge.common.util.ForgeDirection.NORTH;
import static net.minecraftforge.common.util.ForgeDirection.SOUTH;
import static net.minecraftforge.common.util.ForgeDirection.WEST;
import net.minecraft.block.Block;
import net.minecraft.block.BlockLiquid;
import net.minecraft.block.material.Material;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.item.ItemStack;
import net.minecraft.util.IIcon;
import net.minecraft.util.MathHelper;
import net.minecraft.world.IBlockAccess;
import net.minecraftforge.client.MinecraftForgeClient;
import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.fluids.BlockFluidBase;
import net.minecraftforge.fluids.IFluidBlock;
import net.minecraftforge.fluids.RenderBlockFluid;
import org.apache.logging.log4j.Level;
import com.carpentersblocks.tileentity.TEBase;
import com.carpentersblocks.util.BlockProperties;
import com.carpentersblocks.util.ModLogger;
import com.carpentersblocks.util.registry.FeatureRegistry;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class RoutableFluidsHelper {
public final static Class[] liquidClasses = { BlockLiquid.class, IFluidBlock.class};
private final static int CALLER_SUN = 0;
private final static int CALLER_SEC = 1;
private static int callMethod = -1;
/**
* Returns the most optimal available class for use in rendering
* routable fluids.
*
* @return a class
*/
public static Class getCallerClass()
{
if (callMethod < 0)
{
try {
sun.reflect.Reflection.getCallerClass(2);
callMethod = CALLER_SUN;
} catch (Exception E) {
try {
new SecurityManager() { Class clazz = getClassContext()[2]; };
callMethod = CALLER_SEC;
} catch (Exception E1) {
FeatureRegistry.enableRoutableFluids = false;
ModLogger.log(Level.WARN, "Routable fluids failed: %s", E1.getMessage());
};
};
}
switch (callMethod)
{
case CALLER_SUN:
return sun.reflect.Reflection.getCallerClass(4);
case CALLER_SEC:
return new SecurityManager() { Class clazz = getClassContext()[4]; }.clazz;
default:
return null;
}
}
/**
* Renders routable fluid.
*
* @param TE the {@link TEBase}
* @param renderBlocks the {@link RenderBlocks}
* @param x the x coordinate
* @param y the y coordinate
* @param z the z coordinate
* @return true if fluid rendered in space
*/
public static boolean render(TEBase TE, RenderBlocks renderBlocks, int x, int y, int z)
{
// Do not render if fluid is above block
Class clazz_YP = renderBlocks.blockAccess.getBlock(x, y + 1, z).getClass();
for (Class clazz : liquidClasses) {
if (clazz.isAssignableFrom(clazz_YP)) {
return false;
}
}
ItemStack itemStack = getFluidBlock(renderBlocks.blockAccess, x, y, z);
if (itemStack != null) {
Block block = BlockProperties.toBlock(itemStack);
int metadata = itemStack.getItemDamage();
if (block.getRenderBlockPass() == MinecraftForgeClient.getRenderPass())
{
if (!block.hasTileEntity(metadata))
{
if (block instanceof BlockLiquid) {
renderLiquidSurface(TE, renderBlocks, itemStack, x, y, z);
} else {
RenderBlockFluid.instance.renderWorldBlock(renderBlocks.blockAccess, x, y, z, block, 0, renderBlocks);
}
return true;
}
}
}
return false;
}
/**
* Gets nearby, routable fluid block.
*
* @param blockAccess the {@link IBlockAccess}
* @param x the x coordinate
* @param y the y coordinate
* @param z the z coordinate
* @return a nearby fluid {@link ItemStack}, or null if no routable fluid exists
*/
public static ItemStack getFluidBlock(IBlockAccess blockAccess, int x, int y, int z)
{
int[][] offsetXZ = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}, {-1, -1}, {-1, 1}, {1, 1}, {1, -1}};
ForgeDirection[][][] route = {
{ { NORTH } },
{ { SOUTH } },
{ { WEST } },
{ { EAST } },
{ { NORTH, SOUTH, WEST }, { WEST, EAST, NORTH } },
{ { SOUTH, NORTH, WEST }, { WEST, EAST, SOUTH } },
{ { SOUTH, NORTH, EAST }, { EAST, WEST, SOUTH } },
{ { NORTH, SOUTH, EAST }, { EAST, WEST, NORTH } },
};
for (int idx = 0; idx < offsetXZ.length; ++idx) {
Block block = blockAccess.getBlock(x + offsetXZ[idx][0], y, z + offsetXZ[idx][1]);
Class clazz_offset = block.getClass();
boolean isLiquid = false;
for (Class clazz : liquidClasses) {
if (clazz.isAssignableFrom(clazz_offset)) {
isLiquid = true;
break;
}
}
if (isLiquid) {
if (idx < 4) {
if (!blockAccess.isSideSolid(x, y, z, route[idx][0][0], false)) {
return new ItemStack(block, blockAccess.getBlockMetadata(x + offsetXZ[idx][0], y, z + offsetXZ[idx][1]));
}
} else {
for (int routeIdx = 0; routeIdx < 2; ++routeIdx) {
if (!blockAccess.isSideSolid(x, y, z, route[idx][routeIdx][0], false)) {
int[] bridgeXZ = { x + route[idx][routeIdx][0].offsetX, z + route[idx][routeIdx][0].offsetZ };
if (!blockAccess.isSideSolid(bridgeXZ[0], y, bridgeXZ[1], route[idx][routeIdx][1], false) && !blockAccess.isSideSolid(bridgeXZ[0], y, bridgeXZ[1], route[idx][routeIdx][2], false)) {
return new ItemStack(block, blockAccess.getBlockMetadata(x + offsetXZ[idx][0], y, z + offsetXZ[idx][1]));
}
}
}
}
}
}
return null;
}
/**
* Performs the rendering of the liquid surface.
*
* @param TE
* @param renderBlocks
* @param itemStack
* @param x
* @param y
* @param z
*/
public static void renderLiquidSurface(TEBase TE, RenderBlocks renderBlocks, ItemStack itemStack, int x, int y, int z)
{
Tessellator tessellator = Tessellator.instance;
Block block = BlockProperties.toBlock(itemStack);
Material material = block.getMaterial();
double offset = 0.0010000000474974513D;
IIcon icon = renderBlocks.getBlockIconFromSideAndMetadata(block, 1, itemStack.getItemDamage());
float flowDir = -1000;
if (block instanceof BlockLiquid) {
flowDir = (float) BlockLiquid.getFlowDirection(renderBlocks.blockAccess, x, y, z, material);
} else if (block instanceof BlockFluidBase) {
flowDir = (float) BlockFluidBase.getFlowDirection(renderBlocks.blockAccess, x, y, z);
}
if (flowDir > -999.0F) {
icon = renderBlocks.getBlockIconFromSideAndMetadata(block, 2, itemStack.getItemDamage());
}
double u_XZNN;
double u_XZNP;
double u_XZPP;
double u_XZPN;
double v_XZNN;
double v_XZNP;
double v_XZPP;
double v_XZPN;
if (flowDir < -999.0F)
{
u_XZNN = icon.getInterpolatedU(0.0D);
v_XZNN = icon.getInterpolatedV(0.0D);
u_XZNP = u_XZNN;
v_XZNP = icon.getInterpolatedV(16.0D);
u_XZPP = icon.getInterpolatedU(16.0D);
v_XZPP = v_XZNP;
u_XZPN = u_XZPP;
v_XZPN = v_XZNN;
}
else
{
float sinDir = MathHelper.sin(flowDir) * 0.25F;
float cosDir = MathHelper.cos(flowDir) * 0.25F;
u_XZNN = icon.getInterpolatedU(8.0F + (-cosDir - sinDir) * 16.0F);
v_XZNN = icon.getInterpolatedV(8.0F + (-cosDir + sinDir) * 16.0F);
u_XZNP = icon.getInterpolatedU(8.0F + (-cosDir + sinDir) * 16.0F);
v_XZNP = icon.getInterpolatedV(8.0F + (cosDir + sinDir) * 16.0F);
u_XZPP = icon.getInterpolatedU(8.0F + (cosDir + sinDir) * 16.0F);
v_XZPP = icon.getInterpolatedV(8.0F + (cosDir - sinDir) * 16.0F);
u_XZPN = icon.getInterpolatedU(8.0F + (cosDir - sinDir) * 16.0F);
v_XZPN = icon.getInterpolatedV(8.0F + (-cosDir - sinDir) * 16.0F);
}
double height_XZNN = renderBlocks.getLiquidHeight(x, y, z, material) - offset;
double height_XZNP = renderBlocks.getLiquidHeight(x, y, z + 1, material) - offset;
double height_XZPN = renderBlocks.getLiquidHeight(x + 1, y, z, material) - offset;
double height_XZPP = renderBlocks.getLiquidHeight(x + 1, y, z + 1, material) - offset;
int colorMultiplier = block.colorMultiplier(renderBlocks.blockAccess, x, y, z);
float red = (colorMultiplier >> 16 & 255) / 255.0F;
float green = (colorMultiplier >> 8 & 255) / 255.0F;
float blue = (colorMultiplier & 255) / 255.0F;
tessellator.setBrightness(block.getMixedBrightnessForBlock(renderBlocks.blockAccess, x, y, z));
tessellator.setColorOpaque_F(red, green, blue);
tessellator.addVertexWithUV(x, y + height_XZNN, z, u_XZNN, v_XZNN);
tessellator.addVertexWithUV(x, y + height_XZNP, z + 1, u_XZNP, v_XZNP);
tessellator.addVertexWithUV(x + 1, y + height_XZPP, z + 1, u_XZPP, v_XZPP);
tessellator.addVertexWithUV(x + 1, y + height_XZPN, z, u_XZPN, v_XZPN);
tessellator.addVertexWithUV(x, y + height_XZNN, z, u_XZNN, v_XZNN);
tessellator.addVertexWithUV(x + 1, y + height_XZPN, z, u_XZPN, v_XZPN);
tessellator.addVertexWithUV(x + 1, y + height_XZPP, z + 1, u_XZPP, v_XZPP);
tessellator.addVertexWithUV(x, y + height_XZNP, z + 1, u_XZNP, v_XZNP);
}
}