diff --git a/patches/minecraft/net/minecraft/block/Block.java.patch b/patches/minecraft/net/minecraft/block/Block.java.patch index ddf28090c5..6e6969b782 100644 --- a/patches/minecraft/net/minecraft/block/Block.java.patch +++ b/patches/minecraft/net/minecraft/block/Block.java.patch @@ -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; } } diff --git a/patches/minecraft/net/minecraft/client/renderer/ActiveRenderInfo.java.patch b/patches/minecraft/net/minecraft/client/renderer/ActiveRenderInfo.java.patch index 00ffa6fc40..61eb06afb4 100644 --- a/patches/minecraft/net/minecraft/client/renderer/ActiveRenderInfo.java.patch +++ b/patches/minecraft/net/minecraft/client/renderer/ActiveRenderInfo.java.patch @@ -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; diff --git a/src/main/java/net/minecraftforge/client/model/ModelDynBucket.java b/src/main/java/net/minecraftforge/client/model/ModelDynBucket.java index 81ff45a8e4..0d248c2d8a 100644 --- a/src/main/java/net/minecraftforge/client/model/ModelDynBucket.java +++ b/src/main/java/net/minecraftforge/client/model/ModelDynBucket.java @@ -134,8 +134,8 @@ public final class ModelDynBucket implements IModel ImmutableMap 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. *

- * 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 customData) diff --git a/src/main/java/net/minecraftforge/client/model/ModelFluid.java b/src/main/java/net/minecraftforge/client/model/ModelFluid.java index 194df8cfa8..bde861d53e 100644 --- a/src/main/java/net/minecraftforge/client/model/ModelFluid.java +++ b/src/main/java/net/minecraftforge/client/model/ModelFluid.java @@ -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 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 bakedTextureGetter) { - ImmutableMap 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,72 +120,154 @@ 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 modelCache = CacheBuilder.newBuilder().maximumSize(200).build(new CacheLoader() { @Override - public BakedFluid load(Long key) throws Exception + public BakedFluid load(Long key) { boolean statePresent = (key & 1) != 0; key >>>= 1; int[] cornerRound = new int[4]; - for(int i = 0; i < 4; i++) + for (int i = 0; i < 4; i++) { - cornerRound[i] = (int)(key & 0x3FF); + cornerRound[i] = (int) (key & 0x3FF); key >>>= 10; } - int flowRound = (int)(key & 0x7FF) - 1024; - return new BakedFluid(transformation, transforms, format, color, still, flowing, gas, statePresent, cornerRound, flowRound); + int flowRound = (int) (key & 0x7FF) - 1024; + 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 transformation; - private final ImmutableMap transforms; - private final VertexFormat format; - private final int color; - private final TextureAtlasSprite still, flowing; - private final boolean gas; - private final EnumMap> faceQuads; - - public BakedFluid(Optional transformation, ImmutableMap transforms, VertexFormat format, int color, TextureAtlasSprite still, TextureAtlasSprite flowing, boolean gas, Optional stateOption) + public CachingBakedFluid(Optional transformation, ImmutableMap transforms, VertexFormat format, int color, TextureAtlasSprite still, TextureAtlasSprite flowing, Optional overlay, boolean gas, Optional 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 stateOption) { - int[] cornerRound = new int[]{0, 0, 0, 0}; - if(stateOption.isPresent()) + int[] cornerRound = {0, 0, 0, 0}; + if (stateOption.isPresent()) { IExtendedBlockState state = stateOption.get(); - for(int i = 0; i < 4; i++) + 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 stateOption) { Float flow = -1000f; - if(stateOption.isPresent()) + if (stateOption.isPresent()) { flow = stateOption.get().getValue(BlockFluidBase.FLOW_DIRECTION); - if(flow == null) flow = -1000f; + if (flow == null) flow = -1000f; } - int flowRound = (int)Math.round(Math.toDegrees(flow)); + int flowRound = (int) Math.round(Math.toDegrees(flow)); flowRound = MathHelper.clamp(flowRound, -1000, 1000); return flowRound; } - public BakedFluid(Optional transformation, ImmutableMap 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 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 getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand) + { + if (side != null && state instanceof IExtendedBlockState) + { + Optional 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 transformation; + protected final ImmutableMap transforms; + protected final VertexFormat format; + protected final int color; + protected final TextureAtlasSprite still, flowing; + protected final Optional overlay; + protected final boolean gas; + protected final ImmutableMap> faceQuads; + + public BakedFluid(Optional transformation, ImmutableMap transforms, VertexFormat format, int color, TextureAtlasSprite still, TextureAtlasSprite flowing, Optional overlay, boolean gas, boolean statePresent, int[] cornerRound, int flowRound, boolean[] sideOverlays) { this.transformation = transformation; this.transforms = transforms; @@ -188,140 +275,150 @@ 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); - for(EnumFacing side : EnumFacing.values()) + private ImmutableMap> buildQuads(boolean statePresent, int[] cornerRound, int flowRound, boolean[] sideOverlays) + { + EnumMap> faceQuads = new EnumMap<>(EnumFacing.class); + for (EnumFacing side : EnumFacing.values()) { faceQuads.put(side, ImmutableList.of()); } - if(statePresent) + if (statePresent) { + // y levels float[] y = new float[4]; - for(int i = 0; i < 4; i++) + 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 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))); - } - topFaceBuilder.add(builder.build()); + ImmutableList.Builder 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()); } - 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++) + 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 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 + ) + )); } + + return ImmutableMap.copyOf(faceQuads); } - private void putVertex(UnpackedBakedQuad.Builder builder, EnumFacing side, float x, float y, float z, float u, float v) + // 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 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 diff --git a/src/main/java/net/minecraftforge/common/ForgeHooks.java b/src/main/java/net/minecraftforge/common/ForgeHooks.java index 07e35e6354..76121b9899 100644 --- a/src/main/java/net/minecraftforge/common/ForgeHooks.java +++ b/src/main/java/net/minecraftforge/common/ForgeHooks.java @@ -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; } } diff --git a/src/main/java/net/minecraftforge/common/property/PropertyFloat.java b/src/main/java/net/minecraftforge/common/property/PropertyFloat.java index 884d43b750..19163fc427 100644 --- a/src/main/java/net/minecraftforge/common/property/PropertyFloat.java +++ b/src/main/java/net/minecraftforge/common/property/PropertyFloat.java @@ -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 { @@ -29,7 +30,12 @@ public class PropertyFloat implements IUnlistedProperty public PropertyFloat(String name) { - this(name, Predicates.alwaysTrue()); + this(name, Predicates.alwaysTrue()); + } + + public PropertyFloat(String name, float min, float max) + { + this(name, Range.closed(min, max)); } public PropertyFloat(String name, Predicate validator) diff --git a/src/main/java/net/minecraftforge/fluids/BlockFluidBase.java b/src/main/java/net/minecraftforge/fluids/BlockFluidBase.java index 2f7607d41d..d1b9bfc6d0 100644 --- a/src/main/java/net/minecraftforge/fluids/BlockFluidBase.java +++ b/src/main/java/net/minecraftforge/fluids/BlockFluidBase.java @@ -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 displacements = Maps.newHashMap(); + private static final class UnlistedPropertyBool extends Properties.PropertyAdapter + { + 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> 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> FLUID_RENDER_PROPS; static { - ImmutableList.Builder> builder = ImmutableList.builder(); + ImmutableList.Builder> 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,18 +288,30 @@ 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 this.density > density; + } + + /** + * Attempt to displace the block at (pos), return true if it was displaced. + */ + public boolean displaceIfPossible(World world, BlockPos pos) + { + boolean canDisplace = canDisplace(world, pos); + if (canDisplace) { - return true; - } - else - { - return false; + 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).getDensity(); } - return ((BlockFluidBase)block).density; + + Fluid fluid = getFluid(state); + if (fluid != null) + { + return fluid.getDensity(); + } + return Integer.MAX_VALUE; } - public static final int getTemperature(IBlockAccess world, BlockPos pos) + public static int getTemperature(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).getTemperature(); } - return ((BlockFluidBase)block).temperature; + + 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; + + return vec.normalize(); } - private boolean isBlockSolid(IBlockAccess world, BlockPos pos, EnumFacing face) + 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 super.getFogColor(world, pos, state, entity, originalColor, partialTicks); + 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; } } diff --git a/src/main/java/net/minecraftforge/fluids/BlockFluidClassic.java b/src/main/java/net/minecraftforge/fluids/BlockFluidClassic.java index d712a8e006..75cab406cc 100644 --- a/src/main/java/net/minecraftforge/fluids/BlockFluidClassic.java +++ b/src/main/java/net/minecraftforge/fluids/BlockFluidClassic.java @@ -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 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,14 +184,20 @@ 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) { return world.getBlockState(pos.up(densityDir)).getBlock() == this || @@ -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) diff --git a/src/main/java/net/minecraftforge/fluids/Fluid.java b/src/main/java/net/minecraftforge/fluids/Fluid.java index ee7524f041..f6f8f151bd 100644 --- a/src/main/java/net/minecraftforge/fluids/Fluid.java +++ b/src/main/java/net/minecraftforge/fluids/Fluid.java @@ -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) diff --git a/src/main/java/net/minecraftforge/fluids/FluidRegistry.java b/src/main/java/net/minecraftforge/fluids/FluidRegistry.java index 2bc2863546..f16594fe01 100644 --- a/src/main/java/net/minecraftforge/fluids/FluidRegistry.java +++ b/src/main/java/net/minecraftforge/fluids/FluidRegistry.java @@ -71,7 +71,7 @@ public abstract class FluidRegistry static boolean universalBucketEnabled = false; static Set 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"); diff --git a/src/test/java/net/minecraftforge/debug/block/MaterialFogColorTest.java b/src/test/java/net/minecraftforge/debug/block/MaterialFogColorTest.java index a5018e4a6a..c838aeb106 100644 --- a/src/test/java/net/minecraftforge/debug/block/MaterialFogColorTest.java +++ b/src/test/java/net/minecraftforge/debug/block/MaterialFogColorTest.java @@ -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) { diff --git a/src/test/java/net/minecraftforge/debug/client/model/ModelFluidTest.java b/src/test/java/net/minecraftforge/debug/client/model/ModelFluidTest.java index 77f9254f51..3153ade475 100644 --- a/src/test/java/net/minecraftforge/debug/client/model/ModelFluidTest.java +++ b/src/test/java/net/minecraftforge/debug/client/model/ModelFluidTest.java @@ -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 diff --git a/src/test/java/net/minecraftforge/debug/fluid/ColoredFluidTest.java b/src/test/java/net/minecraftforge/debug/fluid/ColoredFluidTest.java index 961e9a3eb1..d0f442385f 100644 --- a/src/test/java/net/minecraftforge/debug/fluid/ColoredFluidTest.java +++ b/src/test/java/net/minecraftforge/debug/fluid/ColoredFluidTest.java @@ -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; diff --git a/src/test/java/net/minecraftforge/debug/fluid/FluidPlacementTest.java b/src/test/java/net/minecraftforge/debug/fluid/FluidPlacementTest.java index 01842c87a4..8661fe3405 100644 --- a/src/test/java/net/minecraftforge/debug/fluid/FluidPlacementTest.java +++ b/src/test/java/net/minecraftforge/debug/fluid/FluidPlacementTest.java @@ -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();