Update and clean up Forge fluid render model (#4763)

This commit is contained in:
Ben Staddon 2018-07-18 04:37:20 +01:00 committed by mezz
parent ab228832f2
commit 2786cd279c
14 changed files with 620 additions and 469 deletions

View file

@ -221,7 +221,7 @@
public SoundType func_185467_w()
{
return this.field_149762_H;
@@ -934,6 +952,1345 @@
@@ -934,6 +952,1361 @@
{
}
@ -1494,6 +1494,22 @@
+ }
+
+ /**
+ * Used to determine the state 'viewed' by an entity (see
+ * {@link net.minecraft.client.renderer.ActiveRenderInfo#getBlockStateAtEntityViewpoint(World, Entity, float)}).
+ * Can be used by fluid blocks to determine if the viewpoint is within the fluid or not.
+ *
+ * @param state the state
+ * @param world the world
+ * @param pos the position
+ * @param viewpoint the viewpoint
+ * @return the block state that should be 'seen'
+ */
+ public IBlockState getStateAtViewpoint(IBlockState state, IBlockAccess world, BlockPos pos, Vec3d viewpoint)
+ {
+ return state;
+ }
+
+ /**
+ * Gets the {@link IBlockState} to place
+ * @param world The world the block is being placed in
+ * @param pos The position the block is being placed at
@ -1567,7 +1583,7 @@
public static void func_149671_p()
{
func_176215_a(0, field_176230_a, (new BlockAir()).func_149663_c("air"));
@@ -1105,7 +2462,7 @@
@@ -1105,7 +2478,7 @@
Block block11 = (new BlockQuartz()).func_149672_a(SoundType.field_185851_d).func_149711_c(0.8F).func_149663_c("quartzBlock");
func_176219_a(155, "quartz_block", block11);
func_176219_a(156, "quartz_stairs", (new BlockStairs(block11.func_176223_P().func_177226_a(BlockQuartz.field_176335_a, BlockQuartz.EnumType.DEFAULT))).func_149663_c("stairsQuartz"));
@ -1576,7 +1592,7 @@
func_176219_a(158, "dropper", (new BlockDropper()).func_149711_c(3.5F).func_149672_a(SoundType.field_185851_d).func_149663_c("dropper"));
func_176219_a(159, "stained_hardened_clay", (new BlockStainedHardenedClay()).func_149711_c(1.25F).func_149752_b(7.0F).func_149672_a(SoundType.field_185851_d).func_149663_c("clayHardenedStained"));
func_176219_a(160, "stained_glass_pane", (new BlockStainedGlassPane()).func_149711_c(0.3F).func_149672_a(SoundType.field_185853_f).func_149663_c("thinStainedGlass"));
@@ -1230,31 +2587,6 @@
@@ -1230,31 +2603,6 @@
block15.field_149783_u = flag;
}
}

View file

@ -12,6 +12,15 @@
GlStateManager.func_179111_a(2982, field_178812_b);
GlStateManager.func_179111_a(2983, field_178813_c);
GlStateManager.func_187445_a(2978, field_178814_a);
@@ -81,7 +86,7 @@
}
}
- return iblockstate;
+ return iblockstate.func_177230_c().getStateAtViewpoint(iblockstate, p_186703_0_, blockpos, vec3d);
}
public static float func_178808_b()
@@ -108,4 +113,14 @@
{
return field_74596_h;

View file

@ -134,8 +134,8 @@ public final class ModelDynBucket implements IModel
ImmutableMap<TransformType, TRSRTransformation> transformMap = PerspectiveMapWrapper.getTransforms(state);
// if the fluid is a gas wi manipulate the initial state to be rotated 180° to turn it upside down
if (flipGas && fluid != null && fluid.isGaseous())
// if the fluid is lighter than air, will manipulate the initial state to be rotated 180° to turn it upside down
if (flipGas && fluid != null && fluid.isLighterThanAir())
{
state = new ModelStateComposition(state, TRSRTransformation.blockCenterToCorner(new TRSRTransformation(null, new Quat4f(0, 0, 1, 0), null, null)));
}
@ -180,12 +180,12 @@ public final class ModelDynBucket implements IModel
}
/**
* Sets the liquid in the model.
* "fluid" - Name of the fluid in the FluidRegistry
* "flipGas" - If "true" the model will be flipped upside down if the liquid is a gas. If "false" it won't.
* Sets the fluid in the model.
* "fluid" - Name of the fluid in the fluid registry.
* "flipGas" - If "true" the model will be flipped upside down if the fluid is lighter than air. If "false" it won't.
* "applyTint" - If "true" the model will tint the fluid quads according to the fluid's base color.
* <p/>
* If the fluid can't be found, water is used
* If the fluid can't be found, water is used.
*/
@Override
public ModelDynBucket process(ImmutableMap<String, String> customData)

View file

@ -19,9 +19,11 @@
package net.minecraftforge.client.model;
import java.util.function.Function;
import java.util.Collection;
import java.util.EnumMap;
import java.util.List;
import java.util.Optional;
import javax.annotation.Nullable;
import javax.vecmath.Matrix4f;
@ -32,7 +34,6 @@ import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformType;
import net.minecraft.client.renderer.block.model.ItemOverrideList;
import net.minecraft.client.renderer.block.model.ModelRotation;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.resources.IResourceManager;
@ -51,15 +52,12 @@ import net.minecraftforge.fml.common.FMLLog;
import org.apache.commons.lang3.tuple.Pair;
import java.util.function.Function;
import java.util.Optional;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
@ -67,6 +65,7 @@ public final class ModelFluid implements IModel
{
public static final ModelFluid WATER = new ModelFluid(FluidRegistry.WATER);
public static final ModelFluid LAVA = new ModelFluid(FluidRegistry.LAVA);
private final Fluid fluid;
public ModelFluid(Fluid fluid)
@ -74,25 +73,31 @@ public final class ModelFluid implements IModel
this.fluid = fluid;
}
@Override
public Collection<ResourceLocation> getTextures()
{
return ImmutableSet.of(fluid.getStill(), fluid.getFlowing());
return fluid.getOverlay() != null
? ImmutableSet.of(fluid.getStill(), fluid.getFlowing(), fluid.getOverlay())
: ImmutableSet.of(fluid.getStill(), fluid.getFlowing());
}
@Override
public IBakedModel bake(IModelState state, VertexFormat format, Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter)
{
ImmutableMap<TransformType, TRSRTransformation> map = PerspectiveMapWrapper.getTransforms(state);
return new BakedFluid(state.apply(Optional.empty()), map, format, fluid.getColor(), bakedTextureGetter.apply(fluid.getStill()), bakedTextureGetter.apply(fluid.getFlowing()), fluid.isGaseous(), Optional.empty());
return new CachingBakedFluid(
state.apply(Optional.empty()),
PerspectiveMapWrapper.getTransforms(state),
format,
fluid.getColor(),
bakedTextureGetter.apply(fluid.getStill()),
bakedTextureGetter.apply(fluid.getFlowing()),
Optional.ofNullable(fluid.getOverlay()).map(bakedTextureGetter),
fluid.isLighterThanAir(),
Optional.empty()
);
}
@Override
public IModelState getDefaultState()
{
return ModelRotation.X0_Y0;
}
public static enum FluidLoader implements ICustomModelLoader
public enum FluidLoader implements ICustomModelLoader
{
INSTANCE;
@ -115,16 +120,12 @@ public final class ModelFluid implements IModel
}
}
private static final class BakedFluid implements IBakedModel
private static final class CachingBakedFluid extends BakedFluid
{
private static final int x[] = { 0, 0, 1, 1 };
private static final int z[] = { 0, 1, 1, 0 };
private static final float eps = 1e-3f;
private final LoadingCache<Long, BakedFluid> modelCache = CacheBuilder.newBuilder().maximumSize(200).build(new CacheLoader<Long, BakedFluid>()
{
@Override
public BakedFluid load(Long key) throws Exception
public BakedFluid load(Long key)
{
boolean statePresent = (key & 1) != 0;
key >>>= 1;
@ -135,38 +136,53 @@ public final class ModelFluid implements IModel
key >>>= 10;
}
int flowRound = (int) (key & 0x7FF) - 1024;
return new BakedFluid(transformation, transforms, format, color, still, flowing, gas, statePresent, cornerRound, flowRound);
key >>>= 11;
boolean[] overlaySides = new boolean[4];
for (int i = 0; i < 4; i++)
{
overlaySides[i] = (key & 1) != 0;
key >>>= 1;
}
return new BakedFluid(transformation, transforms, format, color, still, flowing, overlay, gas, statePresent, cornerRound, flowRound, overlaySides);
}
});
private final Optional<TRSRTransformation> transformation;
private final ImmutableMap<TransformType, TRSRTransformation> transforms;
private final VertexFormat format;
private final int color;
private final TextureAtlasSprite still, flowing;
private final boolean gas;
private final EnumMap<EnumFacing, List<BakedQuad>> faceQuads;
public BakedFluid(Optional<TRSRTransformation> transformation, ImmutableMap<TransformType, TRSRTransformation> transforms, VertexFormat format, int color, TextureAtlasSprite still, TextureAtlasSprite flowing, boolean gas, Optional<IExtendedBlockState> stateOption)
public CachingBakedFluid(Optional<TRSRTransformation> transformation, ImmutableMap<TransformType, TRSRTransformation> transforms, VertexFormat format, int color, TextureAtlasSprite still, TextureAtlasSprite flowing, Optional<TextureAtlasSprite> overlay, boolean gas, Optional<IExtendedBlockState> stateOption)
{
this(transformation, transforms, format, color, still, flowing, gas, stateOption.isPresent(), getCorners(stateOption), getFlow(stateOption));
super(transformation, transforms, format, color, still, flowing, overlay, gas, stateOption.isPresent(), getCorners(stateOption), getFlow(stateOption), getOverlay(stateOption));
}
/**
* Gets the quantized fluid levels for each corner.
*
* Each value is packed into 10 bits of the model key, so max range is [0,1024).
* The value is currently stored/interpreted as the closest multiple of 1/864.
* The divisor is chosen here to allows likely flow values to be exactly representable
* while also providing good use of the available value range.
* (For fluids with default quanta, this evenly divides the per-block intervals of 1/9 by 96)
*/
private static int[] getCorners(Optional<IExtendedBlockState> stateOption)
{
int[] cornerRound = new int[]{0, 0, 0, 0};
int[] cornerRound = {0, 0, 0, 0};
if (stateOption.isPresent())
{
IExtendedBlockState state = stateOption.get();
for (int i = 0; i < 4; i++)
{
Float level = state.getValue(BlockFluidBase.LEVEL_CORNERS[i]);
cornerRound[i] = Math.round((level == null ? 7f / 8 : level) * 768);
cornerRound[i] = Math.round((level == null ? 8f / 9f : level) * 864);
}
}
return cornerRound;
}
/**
* Gets the quantized flow direction of the fluid.
*
* This value comprises 11 bits of the model key, and is signed, so the max range is [-1024,1024).
* The value is currently stored as the angle rounded to the nearest degree.
* A value of -1000 is used to signify no flow.
*/
private static int getFlow(Optional<IExtendedBlockState> stateOption)
{
Float flow = -1000f;
@ -180,7 +196,78 @@ public final class ModelFluid implements IModel
return flowRound;
}
public BakedFluid(Optional<TRSRTransformation> transformation, ImmutableMap<TransformType, TRSRTransformation> transforms, VertexFormat format, int color, TextureAtlasSprite still, TextureAtlasSprite flowing, boolean gas, boolean statePresent, int[] cornerRound, int flowRound)
/**
* Gets the overlay texture flag for each side.
*
* This value determines if the fluid "overlay" texture should be used for that side,
* instead of the normal "flowing" texture (if applicable for that fluid).
* The sides are stored here by their regular horizontal index.
*/
private static boolean[] getOverlay(Optional<IExtendedBlockState> stateOption)
{
boolean[] overlaySides = new boolean[4];
if (stateOption.isPresent())
{
IExtendedBlockState state = stateOption.get();
for (int i = 0; i < 4; i++)
{
Boolean overlay = state.getValue(BlockFluidBase.SIDE_OVERLAYS[i]);
if (overlay != null) overlaySides[i] = overlay;
}
}
return overlaySides;
}
@Override
public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand)
{
if (side != null && state instanceof IExtendedBlockState)
{
Optional<IExtendedBlockState> exState = Optional.of((IExtendedBlockState)state);
int[] cornerRound = getCorners(exState);
int flowRound = getFlow(exState);
boolean[] overlaySides = getOverlay(exState);
long key = 0L;
for (int i = 3; i >= 0; i--)
{
key <<= 1;
key |= overlaySides[i] ? 1 : 0;
}
key <<= 11;
key |= flowRound + 1024;
for (int i = 3; i >= 0; i--)
{
key <<= 10;
key |= cornerRound[i];
}
key <<= 1;
key |= 1;
return modelCache.getUnchecked(key).getQuads(state, side, rand);
}
return super.getQuads(state, side, rand);
}
}
private static class BakedFluid implements IBakedModel
{
private static final int x[] = { 0, 0, 1, 1 };
private static final int z[] = { 0, 1, 1, 0 };
private static final float eps = 1e-3f;
protected final Optional<TRSRTransformation> transformation;
protected final ImmutableMap<TransformType, TRSRTransformation> transforms;
protected final VertexFormat format;
protected final int color;
protected final TextureAtlasSprite still, flowing;
protected final Optional<TextureAtlasSprite> overlay;
protected final boolean gas;
protected final ImmutableMap<EnumFacing, ImmutableList<BakedQuad>> faceQuads;
public BakedFluid(Optional<TRSRTransformation> transformation, ImmutableMap<TransformType, TRSRTransformation> transforms, VertexFormat format, int color, TextureAtlasSprite still, TextureAtlasSprite flowing, Optional<TextureAtlasSprite> overlay, boolean gas, boolean statePresent, int[] cornerRound, int flowRound, boolean[] sideOverlays)
{
this.transformation = transformation;
this.transforms = transforms;
@ -188,9 +275,14 @@ public final class ModelFluid implements IModel
this.color = color;
this.still = still;
this.flowing = flowing;
this.overlay = overlay;
this.gas = gas;
this.faceQuads = buildQuads(statePresent, cornerRound, flowRound, sideOverlays);
}
faceQuads = Maps.newEnumMap(EnumFacing.class);
private ImmutableMap<EnumFacing, ImmutableList<BakedQuad>> buildQuads(boolean statePresent, int[] cornerRound, int flowRound, boolean[] sideOverlays)
{
EnumMap<EnumFacing, ImmutableList<BakedQuad>> faceQuads = new EnumMap<>(EnumFacing.class);
for (EnumFacing side : EnumFacing.values())
{
faceQuads.put(side, ImmutableList.of());
@ -198,130 +290,135 @@ public final class ModelFluid implements IModel
if (statePresent)
{
// y levels
float[] y = new float[4];
boolean fullVolume = true;
for (int i = 0; i < 4; i++)
{
if(gas)
{
y[i] = 1 - cornerRound[i] / 768f;
}
else
{
y[i] = cornerRound[i] / 768f;
}
float value = cornerRound[i] / 864f;
if (value < 1f) fullVolume = false;
y[i] = gas ? 1f - value : value;
}
float flow = (float)Math.toRadians(flowRound);
// flow
boolean isFlowing = flowRound > -1000;
// top
TextureAtlasSprite topSprite = flowing;
float scale = 4;
if(flow < -17F)
{
flow = 0;
scale = 8;
topSprite = still;
}
float flow = isFlowing ? (float) Math.toRadians(flowRound) : 0f;
TextureAtlasSprite topSprite = isFlowing ? flowing : still;
float scale = isFlowing ? 4f : 8f;
float c = MathHelper.cos(flow) * scale;
float s = MathHelper.sin(flow) * scale;
EnumFacing side = gas ? EnumFacing.DOWN : EnumFacing.UP;
UnpackedBakedQuad.Builder builder;
ImmutableList.Builder<BakedQuad> topFaceBuilder = ImmutableList.builder();
for(int k = 0; k < 2; k++)
// top
EnumFacing top = gas ? EnumFacing.DOWN : EnumFacing.UP;
// base uv offset for flow direction
VertexParameter uv = i -> c * (x[i] * 2 - 1) + s * (z[i] * 2 - 1);
VertexParameter topX = i -> x[i];
VertexParameter topY = i -> y[i];
VertexParameter topZ = i -> z[i];
VertexParameter topU = i -> 8 + uv.get(i);
VertexParameter topV = i -> 8 + uv.get((i + 1) % 4);
{
builder = new UnpackedBakedQuad.Builder(format);
builder.setQuadOrientation(side);
builder.setTexture(topSprite);
builder.setQuadTint(0);
for (int i = gas ? 3 : 0; i != (gas ? -1 : 4); i += (gas ? -1 : 1))
{
int l = (k * 3) + (1 - 2 * k) * i;
putVertex(
builder, side,
x[l], y[l], z[l],
topSprite.getInterpolatedU(8 + c * (x[l] * 2 - 1) + s * (z[l] * 2 - 1)),
topSprite.getInterpolatedV(8 + c * (x[(l + 1) % 4] * 2 - 1) + s * (z[(l + 1) % 4] * 2 - 1)));
ImmutableList.Builder<BakedQuad> builder = ImmutableList.builder();
builder.add(buildQuad(top, topSprite, gas, false, topX, topY, topZ, topU, topV));
if (!fullVolume) builder.add(buildQuad(top, topSprite, !gas, true, topX, topY, topZ, topU, topV));
faceQuads.put(top, builder.build());
}
topFaceBuilder.add(builder.build());
}
faceQuads.put(side, topFaceBuilder.build());
// bottom
side = side.getOpposite();
builder = new UnpackedBakedQuad.Builder(format);
builder.setQuadOrientation(side);
builder.setTexture(still);
builder.setQuadTint(0);
for(int i = gas ? 3 : 0; i != (gas ? -1 : 4); i+= (gas ? -1 : 1))
{
putVertex(
builder, side,
z[i], gas ? 1 : 0, x[i],
still.getInterpolatedU(z[i] * 16),
still.getInterpolatedV(x[i] * 16));
}
faceQuads.put(side, ImmutableList.of(builder.build()));
EnumFacing bottom = top.getOpposite();
faceQuads.put(bottom, ImmutableList.of(
buildQuad(bottom, still, gas, false,
i -> z[i],
i -> gas ? 1 : 0,
i -> x[i],
i -> z[i] * 16,
i -> x[i] * 16
)
));
// sides
for (int i = 0; i < 4; i++)
{
side = EnumFacing.getHorizontal((5 - i) % 4);
BakedQuad q[] = new BakedQuad[2];
EnumFacing side = EnumFacing.getHorizontal((5 - i) % 4); // [W, S, E, N]
boolean useOverlay = overlay.isPresent() && sideOverlays[side.getHorizontalIndex()];
int si = i; // local var for lambda capture
for(int k = 0; k < 2; k++)
{
builder = new UnpackedBakedQuad.Builder(format);
builder.setQuadOrientation(side);
builder.setTexture(flowing);
builder.setQuadTint(0);
for(int j = 0; j < 4; j++)
{
int l = (k * 3) + (1 - 2 * k) * j;
float yl = z[l] * y[(i + x[l]) % 4];
if(gas && z[l] == 0) yl = 1;
putVertex(
builder, side,
x[(i + x[l]) % 4], yl, z[(i + x[l]) % 4],
flowing.getInterpolatedU(x[l] * 8),
flowing.getInterpolatedV((gas ? yl : 1 - yl) * 8));
}
q[k] = builder.build();
}
faceQuads.put(side, ImmutableList.of(q[0], q[1]));
VertexParameter sideX = j -> x[(si + x[j]) % 4];
VertexParameter sideY = j -> z[j] == 0 ? (gas ? 1 : 0) : y[(si + x[j]) % 4];
VertexParameter sideZ = j -> z[(si + x[j]) % 4];
VertexParameter sideU = j -> x[j] * 8;
VertexParameter sideV = j -> (gas ? sideY.get(j) : 1 - sideY.get(j)) * 8;
ImmutableList.Builder<BakedQuad> builder = ImmutableList.builder();
if (!useOverlay) builder.add(buildQuad(side, flowing, gas, true, sideX, sideY, sideZ, sideU, sideV));
builder.add(buildQuad(side, useOverlay ? overlay.get() : flowing, !gas, false, sideX, sideY, sideZ, sideU, sideV));
faceQuads.put(side, builder.build());
}
}
else
{
// 1 quad for inventory
UnpackedBakedQuad.Builder builder = new UnpackedBakedQuad.Builder(format);
builder.setQuadOrientation(EnumFacing.UP);
builder.setTexture(still);
builder.setQuadTint(0); //I dont know if we also need this in inventory, but now it should be possible to color it here as well
for(int i = 0; i < 4; i++)
{
putVertex(
builder, EnumFacing.UP,
z[i], x[i], 0,
still.getInterpolatedU(z[i] * 16),
still.getInterpolatedV(x[i] * 16));
}
faceQuads.put(EnumFacing.SOUTH, ImmutableList.of(builder.build()));
}
// inventory
faceQuads.put(EnumFacing.SOUTH, ImmutableList.of(
buildQuad(EnumFacing.UP, still, false, false,
i -> z[i],
i -> x[i],
i -> 0,
i -> z[i] * 16,
i -> x[i] * 16
)
));
}
private void putVertex(UnpackedBakedQuad.Builder builder, EnumFacing side, float x, float y, float z, float u, float v)
return ImmutableMap.copyOf(faceQuads);
}
// maps vertex index to parameter value
private interface VertexParameter
{
float get(int index);
}
private BakedQuad buildQuad(EnumFacing side, TextureAtlasSprite texture, boolean flip, boolean offset, VertexParameter x, VertexParameter y, VertexParameter z, VertexParameter u, VertexParameter v)
{
UnpackedBakedQuad.Builder builder = new UnpackedBakedQuad.Builder(format);
builder.setQuadOrientation(side);
builder.setTexture(texture);
builder.setQuadTint(0);
for (int i = 0; i < 4; i++)
{
int vertex = flip ? 3 - i : i;
putVertex(
builder, side, offset,
x.get(vertex), y.get(vertex), z.get(vertex),
texture.getInterpolatedU(u.get(vertex)),
texture.getInterpolatedV(v.get(vertex))
);
}
return builder.build();
}
private void putVertex(UnpackedBakedQuad.Builder builder, EnumFacing side, boolean offset, float x, float y, float z, float u, float v)
{
for(int e = 0; e < format.getElementCount(); e++)
{
switch(format.getElement(e).getUsage())
{
case POSITION:
float[] data = new float[]{ x - side.getDirectionVec().getX() * eps, y, z - side.getDirectionVec().getZ() * eps, 1 };
float dx = offset ? side.getDirectionVec().getX() * eps : 0f;
float dy = offset ? side.getDirectionVec().getY() * eps : 0f;
float dz = offset ? side.getDirectionVec().getZ() * eps : 0f;
float[] data = { x - dx, y - dy, z - dz, 1f };
if(transformation.isPresent() && !transformation.get().isIdentity())
{
Vector4f vec = new Vector4f(data);
@ -379,24 +476,7 @@ public final class ModelFluid implements IModel
@Override
public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand)
{
BakedFluid model = this;
if(state instanceof IExtendedBlockState)
{
IExtendedBlockState exState = (IExtendedBlockState)state;
int[] cornerRound = getCorners(Optional.of(exState));
int flowRound = getFlow(Optional.of(exState));
long key = flowRound + 1024;
for(int i = 3; i >= 0; i--)
{
key <<= 10;
key |= cornerRound[i];
}
key <<= 1;
key |= 1;
model = modelCache.getUnchecked(key);
}
if(side == null) return ImmutableList.of();
return model.faceQuads.get(side);
return side == null ? ImmutableList.of() : faceQuads.get(side);
}
@Override

View file

@ -1034,18 +1034,16 @@ public class ForgeHooks
}
else if (block instanceof BlockLiquid)
{
filled = BlockLiquid.getLiquidHeightPercent(block.getMetaFromState(state));
filled = 1.0 - (BlockLiquid.getLiquidHeightPercent(block.getMetaFromState(state)) - (1.0 / 9.0));
}
if (filled < 0)
{
filled *= -1;
//filled -= 0.11111111F; //Why this is needed.. not sure...
return eyes > pos.getY() + 1 + (1 - filled);
return eyes > pos.getY() + (filled + 1);
}
else
{
return eyes < pos.getY() + 1 + filled;
return eyes < pos.getY() + filled;
}
}

View file

@ -21,6 +21,7 @@ package net.minecraftforge.common.property;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Range;
public class PropertyFloat implements IUnlistedProperty<Float>
{
@ -29,7 +30,12 @@ public class PropertyFloat implements IUnlistedProperty<Float>
public PropertyFloat(String name)
{
this(name, Predicates.<Float>alwaysTrue());
this(name, Predicates.alwaysTrue());
}
public PropertyFloat(String name, float min, float max)
{
this(name, Range.closed(min, max));
}
public PropertyFloat(String name, Predicate<Float> validator)

View file

@ -23,18 +23,21 @@ import java.util.Map;
import java.util.Random;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import net.minecraft.block.Block;
import net.minecraft.block.BlockLiquid;
import net.minecraft.block.BlockStairs;
import net.minecraft.block.material.Material;
import net.minecraft.block.properties.IProperty;
import net.minecraft.block.properties.PropertyBool;
import net.minecraft.block.properties.PropertyInteger;
import net.minecraft.block.state.BlockFaceShape;
import net.minecraft.block.state.BlockStateContainer;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.ActiveRenderInfo;
import net.minecraft.entity.Entity;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
@ -43,12 +46,13 @@ import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.common.property.ExtendedBlockState;
import net.minecraftforge.common.property.IExtendedBlockState;
import net.minecraftforge.common.property.IUnlistedProperty;
import net.minecraftforge.common.property.Properties;
import net.minecraftforge.common.property.PropertyFloat;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
@ -109,25 +113,38 @@ public abstract class BlockFluidBase extends Block implements IFluidBlock
}
protected Map<Block, Boolean> displacements = Maps.newHashMap();
private static final class UnlistedPropertyBool extends Properties.PropertyAdapter<Boolean>
{
public UnlistedPropertyBool(String name)
{
super(PropertyBool.create(name));
}
}
public static final PropertyInteger LEVEL = PropertyInteger.create("level", 0, 15);
public static final PropertyFloat[] LEVEL_CORNERS = new PropertyFloat[4];
public static final PropertyFloat FLOW_DIRECTION = new PropertyFloat("flow_direction");
public static final ImmutableList<IUnlistedProperty<Float>> FLUID_RENDER_PROPS;
public static final PropertyFloat FLOW_DIRECTION = new PropertyFloat("flow_direction", -1000f, 1000f);
public static final UnlistedPropertyBool[] SIDE_OVERLAYS = new UnlistedPropertyBool[4];
public static final ImmutableList<IUnlistedProperty<?>> FLUID_RENDER_PROPS;
static
{
ImmutableList.Builder<IUnlistedProperty<Float>> builder = ImmutableList.builder();
ImmutableList.Builder<IUnlistedProperty<?>> builder = ImmutableList.builder();
builder.add(FLOW_DIRECTION);
for(int i = 0; i < 4; i++)
{
LEVEL_CORNERS[i] = new PropertyFloat("level_corner_" + i);
LEVEL_CORNERS[i] = new PropertyFloat("level_corner_" + i, 0f, 1f);
builder.add(LEVEL_CORNERS[i]);
SIDE_OVERLAYS[i] = new UnlistedPropertyBool("side_overlay_" + i);
builder.add(SIDE_OVERLAYS[i]);
}
FLUID_RENDER_PROPS = builder.build();
}
protected int quantaPerBlock = 8;
protected float quantaPerBlockFloat = 8F;
protected float quantaFraction = 8f / 9f;
protected int density = 1;
protected int densityDir = -1;
protected int temperature = 295;
@ -161,14 +178,17 @@ public abstract class BlockFluidBase extends Block implements IFluidBlock
this.definedFluid = fluid;
displacements.putAll(defaultDisplacements);
this.setDefaultState(blockState.getBaseState().withProperty(LEVEL, 0));
this.setDefaultState(blockState.getBaseState().withProperty(LEVEL, getMaxRenderHeightMeta()));
}
@Override
@Nonnull
protected BlockStateContainer createBlockState()
{
return new ExtendedBlockState(this, new IProperty[] { LEVEL }, FLUID_RENDER_PROPS.toArray(new IUnlistedProperty<?>[0]));
return new BlockStateContainer.Builder(this)
.add(LEVEL)
.add(FLUID_RENDER_PROPS.toArray(new IUnlistedProperty<?>[0]))
.build();
}
@Override
@ -189,6 +209,7 @@ public abstract class BlockFluidBase extends Block implements IFluidBlock
if (quantaPerBlock > 16 || quantaPerBlock < 1) quantaPerBlock = 8;
this.quantaPerBlock = quantaPerBlock;
this.quantaPerBlockFloat = quantaPerBlock;
this.quantaFraction = quantaPerBlock / (quantaPerBlock + 1f);
return this;
}
@ -225,59 +246,29 @@ public abstract class BlockFluidBase extends Block implements IFluidBlock
return this;
}
public final int getDensity()
{
return density;
}
public final int getTemperature()
{
return temperature;
}
/**
* Returns true if the block at (pos) is displaceable. Does not displace the block.
*/
public boolean canDisplace(IBlockAccess world, BlockPos pos)
{
if (world.isAirBlock(pos)) return true;
IBlockState state = world.getBlockState(pos);
if (state.getBlock() == this)
{
return false;
}
if (displacements.containsKey(state.getBlock()))
{
return displacements.get(state.getBlock());
}
Material material = state.getMaterial();
if (material.blocksMovement() || material == Material.PORTAL)
{
return false;
}
int density = getDensity(world, pos);
if (density == Integer.MAX_VALUE)
{
return true;
}
if (this.density > density)
{
return true;
}
else
{
return false;
}
}
/**
* Attempt to displace the block at (pos), return true if it was displaced.
*/
public boolean displaceIfPossible(World world, BlockPos pos)
{
if (world.isAirBlock(pos))
{
return true;
}
IBlockState state = world.getBlockState(pos);
Block block = state.getBlock();
if (block.isAir(state, world, pos))
{
return true;
}
if (block == this)
{
return false;
@ -285,17 +276,11 @@ public abstract class BlockFluidBase extends Block implements IFluidBlock
if (displacements.containsKey(block))
{
if (displacements.get(block))
{
if (state.getBlock() != Blocks.SNOW_LAYER) //Forge: Vanilla has a 'bug' where snowballs don't drop like every other block. So special case because ewww...
block.dropBlockAsItem(world, pos, state, 0);
return true;
}
return false;
return displacements.get(block);
}
Material material = state.getMaterial();
if (material.blocksMovement() || material == Material.PORTAL)
if (material.blocksMovement() || material == Material.PORTAL || material == Material.STRUCTURE_VOID)
{
return false;
}
@ -303,19 +288,31 @@ public abstract class BlockFluidBase extends Block implements IFluidBlock
int density = getDensity(world, pos);
if (density == Integer.MAX_VALUE)
{
block.dropBlockAsItem(world, pos, state, 0);
return true;
}
if (this.density > density)
{
return true;
return this.density > density;
}
else
/**
* Attempt to displace the block at (pos), return true if it was displaced.
*/
public boolean displaceIfPossible(World world, BlockPos pos)
{
return false;
boolean canDisplace = canDisplace(world, pos);
if (canDisplace)
{
IBlockState state = world.getBlockState(pos);
Block block = state.getBlock();
if (!block.isAir(state, world, pos) && !isFluid(state))
{
// Forge: Vanilla has a 'bug' where snowballs don't drop like every other block. So special case because ewww...
if (block != Blocks.SNOW_LAYER) block.dropBlockAsItem(world, pos, state, 0);
}
}
return canDisplace;
}
public abstract int getQuantaValue(IBlockAccess world, BlockPos pos);
@ -373,12 +370,7 @@ public abstract class BlockFluidBase extends Block implements IFluidBlock
@Nonnull
public Vec3d modifyAcceleration(@Nonnull World world, @Nonnull BlockPos pos, @Nonnull Entity entity, @Nonnull Vec3d vec)
{
if (densityDir > 0) return vec;
Vec3d vec_flow = this.getFlowVector(world, pos);
return vec.addVector(
vec_flow.x * (quantaPerBlock * 4),
vec_flow.y * (quantaPerBlock * 4),
vec_flow.z * (quantaPerBlock * 4));
return densityDir > 0 ? vec : vec.add(getFlowVector(world, pos));
}
@Override
@ -388,8 +380,7 @@ public abstract class BlockFluidBase extends Block implements IFluidBlock
{
return super.getLightValue(state, world, pos);
}
int data = state.getValue(LEVEL);
return (int) (data / quantaPerBlockFloat * maxScaledLight);
return (int) (getQuantaPercentage(world, pos) * maxScaledLight);
}
@Override
@ -404,16 +395,6 @@ public abstract class BlockFluidBase extends Block implements IFluidBlock
return false;
}
/* Never used...?
@Override
public float getBlockBrightness(World world, BlockPos pos)
{
float lightThis = world.getLightBrightness(pos);
float lightUp = world.getLightBrightness(x, y + 1, z);
return lightThis > lightUp ? lightThis : lightUp;
}
*/
@Override
public int getPackedLightmapCoords(@Nonnull IBlockState state, @Nonnull IBlockAccess world, @Nonnull BlockPos pos)
{
@ -450,33 +431,29 @@ public abstract class BlockFluidBase extends Block implements IFluidBlock
{
return false;
}
if(densityDir == -1 && side == EnumFacing.UP)
{
return true;
}
if(densityDir == 1 && side == EnumFacing.DOWN)
if (side == (densityDir < 0 ? EnumFacing.UP : EnumFacing.DOWN))
{
return true;
}
return super.shouldSideBeRendered(state, world, pos, side);
}
private boolean isFluid(@Nonnull IBlockState blockstate)
private static boolean isFluid(@Nonnull IBlockState blockstate)
{
return blockstate.getMaterial().isLiquid() || blockstate.getBlock() instanceof IFluidBlock;
}
@Override
@Nonnull
public IBlockState getExtendedState(@Nonnull IBlockState oldState, @Nonnull IBlockAccess worldIn, @Nonnull BlockPos pos)
public IBlockState getExtendedState(@Nonnull IBlockState oldState, @Nonnull IBlockAccess world, @Nonnull BlockPos pos)
{
IExtendedBlockState state = (IExtendedBlockState)oldState;
state = state.withProperty(FLOW_DIRECTION, (float)getFlowDirection(worldIn, pos));
state = state.withProperty(FLOW_DIRECTION, (float)getFlowDirection(world, pos));
IBlockState[][] upBlockState = new IBlockState[3][3];
float[][] height = new float[3][3];
float[][] corner = new float[2][2];
upBlockState[1][1] = worldIn.getBlockState(pos.down(densityDir));
height[1][1] = getFluidHeightForRender(worldIn, pos, upBlockState[1][1]);
upBlockState[1][1] = world.getBlockState(pos.down(densityDir));
height[1][1] = getFluidHeightForRender(world, pos, upBlockState[1][1]);
if (height[1][1] == 1)
{
for (int i = 0; i < 2; i++)
@ -495,8 +472,8 @@ public abstract class BlockFluidBase extends Block implements IFluidBlock
{
if (i != 1 || j != 1)
{
upBlockState[i][j] = worldIn.getBlockState(pos.add(i - 1, 0, j - 1).down(densityDir));
height[i][j] = getFluidHeightForRender(worldIn, pos.add(i - 1, 0, j - 1), upBlockState[i][j]);
upBlockState[i][j] = world.getBlockState(pos.add(i - 1, 0, j - 1).down(densityDir));
height[i][j] = getFluidHeightForRender(world, pos.add(i - 1, 0, j - 1), upBlockState[i][j]);
}
}
}
@ -534,6 +511,14 @@ public abstract class BlockFluidBase extends Block implements IFluidBlock
}
}
for (int i = 0; i < 4; i++)
{
EnumFacing side = EnumFacing.getHorizontal(i);
BlockPos offset = pos.offset(side);
boolean useOverlay = world.getBlockState(offset).getBlockFaceShape(world, offset, side.getOpposite()) == BlockFaceShape.SOLID;
state = state.withProperty(SIDE_OVERLAYS[i], useOverlay);
}
state = state.withProperty(LEVEL_CORNERS[0], corner[0][0]);
state = state.withProperty(LEVEL_CORNERS[1], corner[0][1]);
state = state.withProperty(LEVEL_CORNERS[2], corner[1][1]);
@ -542,24 +527,63 @@ public abstract class BlockFluidBase extends Block implements IFluidBlock
}
/* FLUID FUNCTIONS */
public static final int getDensity(IBlockAccess world, BlockPos pos)
public static int getDensity(IBlockAccess world, BlockPos pos)
{
Block block = world.getBlockState(pos).getBlock();
if (!(block instanceof BlockFluidBase))
IBlockState state = world.getBlockState(pos);
Block block = state.getBlock();
if (block instanceof BlockFluidBase)
{
return Integer.MAX_VALUE;
}
return ((BlockFluidBase)block).density;
return ((BlockFluidBase)block).getDensity();
}
public static final int getTemperature(IBlockAccess world, BlockPos pos)
{
Block block = world.getBlockState(pos).getBlock();
if (!(block instanceof BlockFluidBase))
Fluid fluid = getFluid(state);
if (fluid != null)
{
return fluid.getDensity();
}
return Integer.MAX_VALUE;
}
return ((BlockFluidBase)block).temperature;
public static int getTemperature(IBlockAccess world, BlockPos pos)
{
IBlockState state = world.getBlockState(pos);
Block block = state.getBlock();
if (block instanceof BlockFluidBase)
{
return ((BlockFluidBase)block).getTemperature();
}
Fluid fluid = getFluid(state);
if (fluid != null)
{
return fluid.getTemperature();
}
return Integer.MAX_VALUE;
}
@Nullable
private static Fluid getFluid(IBlockState state)
{
Block block = state.getBlock();
if (block instanceof IFluidBlock)
{
return ((IFluidBlock)block).getFluid();
}
if (block instanceof BlockLiquid)
{
if (state.getMaterial() == Material.WATER)
{
return FluidRegistry.WATER;
}
if (state.getMaterial() == Material.LAVA)
{
return FluidRegistry.LAVA;
}
}
return null;
}
public static double getFlowDirection(IBlockAccess world, BlockPos pos)
@ -570,7 +594,7 @@ public abstract class BlockFluidBase extends Block implements IFluidBlock
return -1000.0;
}
Vec3d vec = ((BlockFluidBase)state.getBlock()).getFlowVector(world, pos);
return vec.x == 0.0D && vec.z == 0.0D ? -1000.0D : Math.atan2(vec.z, vec.x) - Math.PI / 2D;
return vec.x == 0.0D && vec.z == 0.0D ? -1000.0D : MathHelper.atan2(vec.z, vec.x) - Math.PI / 2D;
}
public final int getQuantaValueBelow(IBlockAccess world, BlockPos pos, int belowThis)
@ -604,11 +628,9 @@ public abstract class BlockFluidBase extends Block implements IFluidBlock
float total = 0;
int count = 0;
float end = 0;
for (int i = 0; i < flow.length; i++)
{
if (flow[i] >= 14f / 16)
if (flow[i] >= quantaFraction)
{
total += flow[i] * 10;
count += 10;
@ -621,10 +643,7 @@ public abstract class BlockFluidBase extends Block implements IFluidBlock
}
}
if (end == 0)
end = total / count;
return end;
return total / count;
}
public float getFluidHeightForRender(IBlockAccess world, BlockPos pos, @Nonnull IBlockState up)
@ -632,86 +651,89 @@ public abstract class BlockFluidBase extends Block implements IFluidBlock
IBlockState here = world.getBlockState(pos);
if (here.getBlock() == this)
{
if (up.getMaterial().isLiquid() || up.getBlock() instanceof IFluidBlock)
if (isFluid(up))
{
return 1;
}
if (getMetaFromState(here) == getMaxRenderHeightMeta())
{
return 0.875F;
return quantaFraction;
}
}
if (here.getBlock() instanceof BlockLiquid)
{
return Math.min(1 - BlockLiquid.getLiquidHeightPercent(here.getValue(BlockLiquid.LEVEL)), 14f / 16);
return Math.min(1 - BlockLiquid.getLiquidHeightPercent(here.getValue(BlockLiquid.LEVEL)), quantaFraction);
}
return !here.getMaterial().isSolid() && up.getBlock() == this ? 1 : this.getQuantaPercentage(world, pos) * 0.875F;
return !here.getMaterial().isSolid() && up.getBlock() == this ? 1 : this.getQuantaPercentage(world, pos) * quantaFraction;
}
public Vec3d getFlowVector(IBlockAccess world, BlockPos pos)
{
Vec3d vec = new Vec3d(0.0D, 0.0D, 0.0D);
int decay = quantaPerBlock - getQuantaValue(world, pos);
int decay = getFlowDecay(world, pos);
for (int side = 0; side < 4; ++side)
for (EnumFacing side : EnumFacing.Plane.HORIZONTAL)
{
int x2 = pos.getX();
int z2 = pos.getZ();
switch (side)
{
case 0: --x2; break;
case 1: --z2; break;
case 2: ++x2; break;
case 3: ++z2; break;
}
BlockPos pos2 = new BlockPos(x2, pos.getY(), z2);
int otherDecay = quantaPerBlock - getQuantaValue(world, pos2);
BlockPos offset = pos.offset(side);
int otherDecay = getFlowDecay(world, offset);
if (otherDecay >= quantaPerBlock)
{
if (!world.getBlockState(pos2).getMaterial().blocksMovement())
if (!world.getBlockState(offset).getMaterial().blocksMovement())
{
otherDecay = quantaPerBlock - getQuantaValue(world, pos2.down());
if (otherDecay >= 0)
otherDecay = getFlowDecay(world, offset.up(densityDir));
if (otherDecay < quantaPerBlock)
{
int power = otherDecay - (decay - quantaPerBlock);
vec = vec.addVector((pos2.getX() - pos.getX()) * power, 0, (pos2.getZ() - pos.getZ()) * power);
vec = vec.addVector(side.getFrontOffsetX() * power, 0, side.getFrontOffsetZ() * power);
}
}
}
else if (otherDecay >= 0)
else
{
int power = otherDecay - decay;
vec = vec.addVector((pos2.getX() - pos.getX()) * power, 0, (pos2.getZ() - pos.getZ()) * power);
vec = vec.addVector(side.getFrontOffsetX() * power, 0, side.getFrontOffsetZ() * power);
}
}
if (world.getBlockState(pos.up()).getBlock() == this)
if (hasVerticalFlow(world, pos))
{
boolean flag =
isBlockSolid(world, pos.add( 0, 0, -1), EnumFacing.NORTH) ||
isBlockSolid(world, pos.add( 0, 0, 1), EnumFacing.SOUTH) ||
isBlockSolid(world, pos.add(-1, 0, 0), EnumFacing.WEST) ||
isBlockSolid(world, pos.add( 1, 0, 0), EnumFacing.EAST) ||
isBlockSolid(world, pos.add( 0, 1, -1), EnumFacing.NORTH) ||
isBlockSolid(world, pos.add( 0, 1, 1), EnumFacing.SOUTH) ||
isBlockSolid(world, pos.add(-1, 1, 0), EnumFacing.WEST) ||
isBlockSolid(world, pos.add( 1, 1, 0), EnumFacing.EAST);
if (flag)
for (EnumFacing side : EnumFacing.Plane.HORIZONTAL)
{
vec = vec.normalize().addVector(0.0D, -6.0D, 0.0D);
BlockPos offset = pos.offset(side);
if (causesDownwardCurrent(world, offset, side) || causesDownwardCurrent(world, offset.down(densityDir), side))
{
vec = vec.normalize().addVector(0.0, 6.0 * densityDir, 0.0);
break;
}
}
vec = vec.normalize();
return vec;
}
private boolean isBlockSolid(IBlockAccess world, BlockPos pos, EnumFacing face)
return vec.normalize();
}
private int getFlowDecay(IBlockAccess world, BlockPos pos)
{
return world.getBlockState(pos).getBlockFaceShape(world, pos, face) == BlockFaceShape.SOLID;
int quantaValue = getQuantaValue(world, pos);
return quantaValue > 0 && hasVerticalFlow(world, pos) ? 0 : quantaPerBlock - quantaValue;
}
private boolean hasVerticalFlow(IBlockAccess world, BlockPos pos)
{
return world.getBlockState(pos.down(densityDir)).getBlock() == this;
}
protected boolean causesDownwardCurrent(IBlockAccess world, BlockPos pos, EnumFacing face)
{
IBlockState state = world.getBlockState(pos);
Block block = state.getBlock();
if (block == this) return false;
if (face == (densityDir < 0 ? EnumFacing.UP : EnumFacing.DOWN)) return true;
if (state.getMaterial() == Material.ICE) return false;
boolean flag = isExceptBlockForAttachWithPiston(block) || block instanceof BlockStairs;
return !flag && state.getBlockFaceShape(world, pos, face) == BlockFaceShape.SOLID;
}
/* IFluidBlock */
@ -724,9 +746,13 @@ public abstract class BlockFluidBase extends Block implements IFluidBlock
@Override
public float getFilledPercentage(World world, BlockPos pos)
{
int quantaRemaining = getQuantaValue(world, pos) + 1;
float remaining = quantaRemaining / quantaPerBlockFloat;
if (remaining > 1) remaining = 1.0f;
return getFilledPercentage((IBlockAccess) world, pos);
}
public float getFilledPercentage(IBlockAccess world, BlockPos pos)
{
int quantaRemaining = getQuantaValue(world, pos);
float remaining = (quantaRemaining + 1f) / (quantaPerBlockFloat + 1f);
return remaining * (density > 0 ? 1 : -1);
}
@ -740,6 +766,13 @@ public abstract class BlockFluidBase extends Block implements IFluidBlock
@SideOnly (Side.CLIENT)
public Vec3d getFogColor(World world, BlockPos pos, IBlockState state, Entity entity, Vec3d originalColor, float partialTicks)
{
if (!isWithinFluid(world, pos, ActiveRenderInfo.projectViewFromEntity(entity, partialTicks)))
{
BlockPos otherPos = pos.down(densityDir);
IBlockState otherState = world.getBlockState(otherPos);
return otherState.getBlock().getFogColor(world, otherPos, otherState, entity, originalColor, partialTicks);
}
if (getFluid() != null)
{
int color = getFluid().getColor();
@ -748,9 +781,25 @@ public abstract class BlockFluidBase extends Block implements IFluidBlock
float blue = (color & 0xFF) / 255.0F;
return new Vec3d(red, green, blue);
}
else
{
return super.getFogColor(world, pos, state, entity, originalColor, partialTicks);
}
@Override
public IBlockState getStateAtViewpoint(IBlockState state, IBlockAccess world, BlockPos pos, Vec3d viewpoint)
{
if (!isWithinFluid(world, pos, viewpoint))
{
return world.getBlockState(pos.down(densityDir));
}
return super.getStateAtViewpoint(state, world, pos, viewpoint);
}
private boolean isWithinFluid(IBlockAccess world, BlockPos pos, Vec3d vec)
{
float filled = getFilledPercentage(world, pos);
return filled < 0 ? vec.y > pos.getY() + filled + 1
: vec.y < pos.getY() + filled;
}
}

View file

@ -19,11 +19,16 @@
package net.minecraftforge.fluids;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import com.google.common.primitives.Ints;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.init.Blocks;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
@ -40,10 +45,16 @@ import javax.annotation.Nullable;
*/
public class BlockFluidClassic extends BlockFluidBase
{
protected static final List<EnumFacing> SIDES = Collections.unmodifiableList(Arrays.asList(
EnumFacing.WEST, EnumFacing.EAST, EnumFacing.NORTH, EnumFacing.SOUTH));
protected boolean[] isOptimalFlowDirection = new boolean[4];
protected int[] flowCost = new int[4];
protected boolean canCreateSources = false;
protected FluidStack stack;
public BlockFluidClassic(Fluid fluid, Material material)
{
super(fluid, material);
@ -66,7 +77,7 @@ public class BlockFluidClassic extends BlockFluidBase
public int getQuantaValue(IBlockAccess world, BlockPos pos)
{
IBlockState state = world.getBlockState(pos);
if (state.getBlock() == Blocks.AIR)
if (state.getBlock().isAir(state, world, pos))
{
return 0;
}
@ -76,8 +87,7 @@ public class BlockFluidClassic extends BlockFluidBase
return -1;
}
int quantaRemaining = quantaPerBlock - state.getValue(LEVEL);
return quantaRemaining;
return quantaPerBlock - state.getValue(LEVEL);
}
@Override
@ -92,53 +102,46 @@ public class BlockFluidClassic extends BlockFluidBase
return 0;
}
@Override
public int getLightValue(@Nonnull IBlockState state, @Nonnull IBlockAccess world, @Nonnull BlockPos pos)
{
if (maxScaledLight == 0)
{
return super.getLightValue(state, world, pos);
}
int data = quantaPerBlock - state.getValue(LEVEL) - 1;
return (int) (data / quantaPerBlockFloat * maxScaledLight);
}
@Override
public void updateTick(@Nonnull World world, @Nonnull BlockPos pos, @Nonnull IBlockState state, @Nonnull Random rand)
{
if (!isSourceBlock(world, pos) && ForgeEventFactory.canCreateFluidSource(world, pos, state, false))
{
int adjacentSourceBlocks =
(isSourceBlock(world, pos.north()) ? 1 : 0) +
(isSourceBlock(world, pos.south()) ? 1 : 0) +
(isSourceBlock(world, pos.east()) ? 1 : 0) +
(isSourceBlock(world, pos.west()) ? 1 : 0);
if (adjacentSourceBlocks >= 2 && (world.getBlockState(pos.up(densityDir)).getMaterial().isSolid() || isSourceBlock(world, pos.up(densityDir))))
world.setBlockState(pos, state.withProperty(LEVEL, 0));
}
int quantaRemaining = quantaPerBlock - state.getValue(LEVEL);
int expQuanta = -101;
// check adjacent block levels if non-source
if (quantaRemaining < quantaPerBlock)
{
if (world.getBlockState(pos.add( 0, -densityDir, 0)).getBlock() == this ||
world.getBlockState(pos.add(-1, -densityDir, 0)).getBlock() == this ||
world.getBlockState(pos.add( 1, -densityDir, 0)).getBlock() == this ||
world.getBlockState(pos.add( 0, -densityDir, -1)).getBlock() == this ||
world.getBlockState(pos.add( 0, -densityDir, 1)).getBlock() == this)
int adjacentSourceBlocks = 0;
if (ForgeEventFactory.canCreateFluidSource(world, pos, state, canCreateSources))
{
for (EnumFacing side : EnumFacing.Plane.HORIZONTAL)
{
if (isSourceBlock(world, pos.offset(side))) adjacentSourceBlocks++;
}
}
// new source block
if (adjacentSourceBlocks >= 2 && (world.getBlockState(pos.up(densityDir)).getMaterial().isSolid() || isSourceBlock(world, pos.up(densityDir))))
{
expQuanta = quantaPerBlock;
}
// unobstructed flow from 'above'
else if (world.getBlockState(pos.down(densityDir)).getBlock() == this
|| hasDownhillFlow(world, pos, EnumFacing.EAST)
|| hasDownhillFlow(world, pos, EnumFacing.WEST)
|| hasDownhillFlow(world, pos, EnumFacing.NORTH)
|| hasDownhillFlow(world, pos, EnumFacing.SOUTH))
{
expQuanta = quantaPerBlock - 1;
}
else
{
int maxQuanta = -100;
maxQuanta = getLargerQuanta(world, pos.add(-1, 0, 0), maxQuanta);
maxQuanta = getLargerQuanta(world, pos.add( 1, 0, 0), maxQuanta);
maxQuanta = getLargerQuanta(world, pos.add( 0, 0, -1), maxQuanta);
maxQuanta = getLargerQuanta(world, pos.add( 0, 0, 1), maxQuanta);
for (EnumFacing side : EnumFacing.Plane.HORIZONTAL)
{
maxQuanta = getLargerQuanta(world, pos.offset(side), maxQuanta);
}
expQuanta = maxQuanta - 1;
}
@ -159,11 +162,6 @@ public class BlockFluidClassic extends BlockFluidBase
}
}
}
// This is a "source" block, set meta to zero, and send a server only update
else if (quantaRemaining >= quantaPerBlock)
{
world.setBlockState(pos, this.getDefaultState(), 2);
}
// Flow vertically if possible
if (canDisplace(world, pos.up(densityDir)))
@ -186,13 +184,19 @@ public class BlockFluidClassic extends BlockFluidBase
flowMeta = 1;
}
boolean flowTo[] = getOptimalFlowDirections(world, pos);
if (flowTo[0]) flowIntoBlock(world, pos.add(-1, 0, 0), flowMeta);
if (flowTo[1]) flowIntoBlock(world, pos.add( 1, 0, 0), flowMeta);
if (flowTo[2]) flowIntoBlock(world, pos.add( 0, 0, -1), flowMeta);
if (flowTo[3]) flowIntoBlock(world, pos.add( 0, 0, 1), flowMeta);
for (int i = 0; i < 4; i++)
{
if (flowTo[i]) flowIntoBlock(world, pos.offset(SIDES.get(i)), flowMeta);
}
}
}
protected final boolean hasDownhillFlow(IBlockAccess world, BlockPos pos, EnumFacing direction)
{
return world.getBlockState(pos.offset(direction).down(densityDir)).getBlock() == this
&& (canFlowInto(world, pos.offset(direction))
|| canFlowInto(world, pos.down(densityDir)));
}
public boolean isFlowingVertically(IBlockAccess world, BlockPos pos)
{
@ -212,22 +216,14 @@ public class BlockFluidClassic extends BlockFluidBase
{
flowCost[side] = 1000;
BlockPos pos2 = pos;
switch (side)
{
case 0: pos2 = pos2.add(-1, 0, 0); break;
case 1: pos2 = pos2.add( 1, 0, 0); break;
case 2: pos2 = pos2.add( 0, 0, -1); break;
case 3: pos2 = pos2.add( 0, 0, 1); break;
}
BlockPos pos2 = pos.offset(SIDES.get(side));
if (!canFlowInto(world, pos2) || isSourceBlock(world, pos2))
{
continue;
}
if (canFlowInto(world, pos2.add(0, densityDir, 0)))
if (canFlowInto(world, pos2.up(densityDir)))
{
flowCost[side] = 0;
}
@ -237,14 +233,7 @@ public class BlockFluidClassic extends BlockFluidBase
}
}
int min = flowCost[0];
for (int side = 1; side < 4; side++)
{
if (flowCost[side] < min)
{
min = flowCost[side];
}
}
int min = Ints.min(flowCost);
for (int side = 0; side < 4; side++)
{
isOptimalFlowDirection[side] = flowCost[side] == min;
@ -257,44 +246,29 @@ public class BlockFluidClassic extends BlockFluidBase
int cost = 1000;
for (int adjSide = 0; adjSide < 4; adjSide++)
{
if ((adjSide == 0 && side == 1) ||
(adjSide == 1 && side == 0) ||
(adjSide == 2 && side == 3) ||
(adjSide == 3 && side == 2))
if (SIDES.get(adjSide) == SIDES.get(side).getOpposite())
{
continue;
}
BlockPos pos2 = pos;
switch (adjSide)
{
case 0: pos2 = pos2.add(-1, 0, 0); break;
case 1: pos2 = pos2.add( 1, 0, 0); break;
case 2: pos2 = pos2.add( 0, 0, -1); break;
case 3: pos2 = pos2.add( 0, 0, 1); break;
}
BlockPos pos2 = pos.offset(SIDES.get(adjSide));
if (!canFlowInto(world, pos2) || isSourceBlock(world, pos2))
{
continue;
}
if (canFlowInto(world, pos2.add(0, densityDir, 0)))
if (canFlowInto(world, pos2.up(densityDir)))
{
return recurseDepth;
}
if (recurseDepth >= 4)
if (recurseDepth >= quantaPerBlock / 2)
{
continue;
}
int min = calculateFlowCost(world, pos2, recurseDepth + 1, adjSide);
if (min < cost)
{
cost = min;
}
cost = Math.min(cost, calculateFlowCost(world, pos2, recurseDepth + 1, adjSide));
}
return cost;
}
@ -304,48 +278,13 @@ public class BlockFluidClassic extends BlockFluidBase
if (meta < 0) return;
if (displaceIfPossible(world, pos))
{
world.setBlockState(pos, this.getBlockState().getBaseState().withProperty(LEVEL, meta), 3);
world.setBlockState(pos, this.getDefaultState().withProperty(LEVEL, meta), 3);
}
}
protected boolean canFlowInto(IBlockAccess world, BlockPos pos)
{
if (world.isAirBlock(pos)) return true;
IBlockState state = world.getBlockState(pos);
if (state.getBlock() == this)
{
return true;
}
if (displacements.containsKey(state.getBlock()))
{
return displacements.get(state.getBlock());
}
Material material = state.getMaterial();
if (material.blocksMovement() ||
material == Material.WATER ||
material == Material.LAVA ||
material == Material.PORTAL)
{
return false;
}
int density = getDensity(world, pos);
if (density == Integer.MAX_VALUE)
{
return true;
}
if (this.density > density)
{
return true;
}
else
{
return false;
}
return world.getBlockState(pos).getBlock() == this || canDisplace(world, pos);
}
protected int getLargerQuanta(IBlockAccess world, BlockPos pos, int compare)

View file

@ -67,6 +67,9 @@ public class Fluid
protected final ResourceLocation still;
protected final ResourceLocation flowing;
@Nullable
protected final ResourceLocation overlay;
private SoundEvent fillSound;
private SoundEvent emptySound;
@ -108,8 +111,6 @@ public class Fluid
/**
* This indicates if the fluid is gaseous.
*
* Useful for rendering the fluid in containers and the world.
*
* Generally this is associated with negative density fluids.
*/
protected boolean isGaseous;
@ -140,22 +141,38 @@ public class Fluid
public Fluid(String fluidName, ResourceLocation still, ResourceLocation flowing, Color color)
{
this(fluidName, still, flowing);
this(fluidName, still, flowing, null, color);
}
public Fluid(String fluidName, ResourceLocation still, ResourceLocation flowing, @Nullable ResourceLocation overlay, Color color)
{
this(fluidName, still, flowing, overlay);
this.setColor(color);
}
public Fluid(String fluidName, ResourceLocation still, ResourceLocation flowing, int color)
{
this(fluidName, still, flowing);
this(fluidName, still, flowing, null, color);
}
public Fluid(String fluidName, ResourceLocation still, ResourceLocation flowing, @Nullable ResourceLocation overlay, int color)
{
this(fluidName, still, flowing, overlay);
this.setColor(color);
}
public Fluid(String fluidName, ResourceLocation still, ResourceLocation flowing)
{
this(fluidName, still, flowing, (ResourceLocation) null);
}
public Fluid(String fluidName, ResourceLocation still, ResourceLocation flowing, @Nullable ResourceLocation overlay)
{
this.fluidName = fluidName.toLowerCase(Locale.ENGLISH);
this.unlocalizedName = fluidName;
this.still = still;
this.flowing = flowing;
this.overlay = overlay;
}
public Fluid setUnlocalizedName(String unlocalizedName)
@ -253,6 +270,16 @@ public class Fluid
return block != null;
}
public final boolean isLighterThanAir()
{
int density = this.density;
if (block instanceof BlockFluidBase)
{
density = ((BlockFluidBase) block).getDensity();
}
return density <= 0;
}
/**
* Determines if this fluid should vaporize in dimensions where water vaporizes when placed.
* To preserve the intentions of vanilla, fluids that can turn lava into obsidian should vaporize.
@ -360,6 +387,12 @@ public class Fluid
return flowing;
}
@Nullable
public ResourceLocation getOverlay()
{
return overlay;
}
public SoundEvent getFillSound()
{
if(fillSound == null)

View file

@ -71,7 +71,7 @@ public abstract class FluidRegistry
static boolean universalBucketEnabled = false;
static Set<Fluid> bucketFluids = Sets.newHashSet();
public static final Fluid WATER = new Fluid("water", new ResourceLocation("blocks/water_still"), new ResourceLocation("blocks/water_flow")) {
public static final Fluid WATER = new Fluid("water", new ResourceLocation("blocks/water_still"), new ResourceLocation("blocks/water_flow"), new ResourceLocation("blocks/water_overlay")) {
@Override
public String getLocalizedName(FluidStack fs) {
return I18n.translateToLocal("tile.water.name");

View file

@ -65,7 +65,7 @@ public class MaterialFogColorTest
}
}
public static final Fluid SLIME = new Fluid("slime", new ResourceLocation(MODID, "slime_still"), new ResourceLocation(MODID, "slime_flow")) {
public static final Fluid SLIME = new Fluid("slime", new ResourceLocation(MODID, "slime_still"), new ResourceLocation(MODID, "slime_flow"), new ResourceLocation(MODID, "slime_overlay")) {
@Override
public int getColor()
{
@ -99,7 +99,7 @@ public class MaterialFogColorTest
if (ENABLED)
{
event.getRegistry().register((new BlockFluidClassic(SLIME, Material.WATER)).setRegistryName(RES_LOC).setUnlocalizedName(RES_LOC.toString()));
Fluid fluid = new Fluid("fog_test", Blocks.WATER.getRegistryName(), Blocks.FLOWING_WATER.getRegistryName());
Fluid fluid = new Fluid("fog_test", new ResourceLocation("blocks/water_still"), new ResourceLocation("blocks/water_flow"), new ResourceLocation("blocks/water_overlay"));
FluidRegistry.registerFluid(fluid);
Block fluidBlock = new BlockFluidClassic(fluid, Material.WATER)
{

View file

@ -150,7 +150,7 @@ public class ModelFluidTest
private TestFluid()
{
super(name, new ResourceLocation("blocks/water_still"), new ResourceLocation("blocks/water_flow"));
super(name, new ResourceLocation("blocks/water_still"), new ResourceLocation("blocks/water_flow"), new ResourceLocation("blocks/water_overlay"));
}
@Override

View file

@ -52,7 +52,7 @@ public class ColoredFluidTest
FluidRegistry.enableUniversalBucket();
}
}
public static final Fluid SLIME = new Fluid("slime", new ResourceLocation(MODID, "slime_still"), new ResourceLocation(MODID, "slime_flow")).setColor(COLOR);
public static final Fluid SLIME = new Fluid("slime", new ResourceLocation(MODID, "slime_still"), new ResourceLocation(MODID, "slime_flow"), new ResourceLocation(MODID, "slime_overlay")).setColor(COLOR);
@ObjectHolder("slime")
public static final BlockFluidBase SLIME_BLOCK = null;

View file

@ -43,6 +43,7 @@ import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.debug.client.model.ModelFluidTest;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.fluids.BlockFluidBase;
import net.minecraftforge.fluids.BlockFluidFinite;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidRegistry;
@ -92,7 +93,7 @@ public class FluidPlacementTest
event.getRegistry().registerAll(
EmptyFluidContainer.instance,
FluidContainer.instance,
new ItemBlock(FiniteFluidBlock.instance).setRegistryName(FiniteFluidBlock.instance.getRegistryName())
new FluidItemBlock(FiniteFluidBlock.instance).setRegistryName(FiniteFluidBlock.instance.getRegistryName())
);
MinecraftForge.EVENT_BUS.register(FluidContainer.instance);
}
@ -132,7 +133,7 @@ public class FluidPlacementTest
private FiniteFluid()
{
super(name, new ResourceLocation("blocks/water_still"), new ResourceLocation("blocks/water_flow"));
super(name, new ResourceLocation("blocks/water_still"), new ResourceLocation("blocks/water_flow"), new ResourceLocation("blocks/water_overlay"));
}
@Override
@ -162,6 +163,26 @@ public class FluidPlacementTest
}
}
public static final class FluidItemBlock extends ItemBlock
{
FluidItemBlock(BlockFluidBase block)
{
super(block);
}
@Override
public BlockFluidBase getBlock()
{
return (BlockFluidBase) super.getBlock();
}
@Override
public int getMetadata(int damage)
{
return getBlock().getMaxRenderHeightMeta();
}
}
public static final class EmptyFluidContainer extends ItemBucket
{
public static final EmptyFluidContainer instance = new EmptyFluidContainer();