ForgePatch/src/main/java/net/minecraftforge/fluids/BlockFluidClassic.java

350 lines
10 KiB
Java

package net.minecraftforge.fluids;
import java.util.Random;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.init.Blocks;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
/**
* This is a fluid block implementation which emulates vanilla Minecraft fluid behavior.
*
* It is highly recommended that you use/extend this class for "classic" fluid blocks.
*
*/
public class BlockFluidClassic extends BlockFluidBase
{
protected boolean[] isOptimalFlowDirection = new boolean[4];
protected int[] flowCost = new int[4];
protected FluidStack stack;
public BlockFluidClassic(Fluid fluid, Material material)
{
super(fluid, material);
stack = new FluidStack(fluid, FluidContainerRegistry.BUCKET_VOLUME);
}
public BlockFluidClassic setFluidStack(FluidStack stack)
{
this.stack = stack;
return this;
}
public BlockFluidClassic setFluidStackAmount(int amount)
{
this.stack.amount = amount;
return this;
}
@Override
public int getQuantaValue(IBlockAccess world, BlockPos pos)
{
IBlockState state = world.getBlockState(pos);
if (state.getBlock() == Blocks.AIR)
{
return 0;
}
if (state.getBlock() != this)
{
return -1;
}
int quantaRemaining = quantaPerBlock - state.getValue(LEVEL);
return quantaRemaining;
}
@Override
public boolean canCollideCheck(IBlockState state, boolean fullHit)
{
return fullHit && state.getValue(LEVEL) == 0;
}
@Override
public int getMaxRenderHeightMeta()
{
return 0;
}
@Override
public int getLightValue(IBlockState state, IBlockAccess world, 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(World world, BlockPos pos, IBlockState state, Random rand)
{
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)
{
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);
expQuanta = maxQuanta - 1;
}
// decay calculation
if (expQuanta != quantaRemaining)
{
quantaRemaining = expQuanta;
if (expQuanta <= 0)
{
world.setBlockToAir(pos);
}
else
{
world.setBlockState(pos, state.withProperty(LEVEL, quantaPerBlock - expQuanta), 2);
world.scheduleUpdate(pos, this, tickRate);
world.notifyNeighborsOfStateChange(pos, this);
}
}
}
// 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)))
{
flowIntoBlock(world, pos.up(densityDir), 1);
return;
}
// Flow outward if possible
int flowMeta = quantaPerBlock - quantaRemaining + 1;
if (flowMeta >= quantaPerBlock)
{
return;
}
if (isSourceBlock(world, pos) || !isFlowingVertically(world, pos))
{
if (world.getBlockState(pos.down(densityDir)).getBlock() == this)
{
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);
}
}
public boolean isFlowingVertically(IBlockAccess world, BlockPos pos)
{
return world.getBlockState(pos.up(densityDir)).getBlock() == this ||
(world.getBlockState(pos).getBlock() == this && canFlowInto(world, pos.up(densityDir)));
}
public boolean isSourceBlock(IBlockAccess world, BlockPos pos)
{
IBlockState state = world.getBlockState(pos);
return state.getBlock() == this && state.getValue(LEVEL) == 0;
}
protected boolean[] getOptimalFlowDirections(World world, BlockPos pos)
{
for (int side = 0; side < 4; side++)
{
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;
}
if (!canFlowInto(world, pos2) || isSourceBlock(world, pos2))
{
continue;
}
if (canFlowInto(world, pos2.add(0, densityDir, 0)))
{
flowCost[side] = 0;
}
else
{
flowCost[side] = calculateFlowCost(world, pos2, 1, side);
}
}
int min = flowCost[0];
for (int side = 1; side < 4; side++)
{
if (flowCost[side] < min)
{
min = flowCost[side];
}
}
for (int side = 0; side < 4; side++)
{
isOptimalFlowDirection[side] = flowCost[side] == min;
}
return isOptimalFlowDirection;
}
protected int calculateFlowCost(World world, BlockPos pos, int recurseDepth, int side)
{
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))
{
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;
}
if (!canFlowInto(world, pos2) || isSourceBlock(world, pos2))
{
continue;
}
if (canFlowInto(world, pos2.add(0, densityDir, 0)))
{
return recurseDepth;
}
if (recurseDepth >= 4)
{
continue;
}
int min = calculateFlowCost(world, pos2, recurseDepth + 1, adjSide);
if (min < cost)
{
cost = min;
}
}
return cost;
}
protected void flowIntoBlock(World world, BlockPos pos, int meta)
{
if (meta < 0) return;
if (displaceIfPossible(world, pos))
{
world.setBlockState(pos, this.getBlockState().getBaseState().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;
}
}
protected int getLargerQuanta(IBlockAccess world, BlockPos pos, int compare)
{
int quantaRemaining = getQuantaValue(world, pos);
if (quantaRemaining <= 0)
{
return compare;
}
return quantaRemaining >= compare ? quantaRemaining : compare;
}
/* IFluidBlock */
@Override
public FluidStack drain(World world, BlockPos pos, boolean doDrain)
{
if (!isSourceBlock(world, pos))
{
return null;
}
if (doDrain)
{
world.setBlockToAir(pos);
}
return stack.copy();
}
@Override
public boolean canDrain(World world, BlockPos pos)
{
return isSourceBlock(world, pos);
}
}