package net.minecraftforge.fluids; import java.util.Random; import net.minecraft.block.material.Material; import net.minecraft.init.Blocks; import net.minecraft.util.MathHelper; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; /** * This is a cellular-automata based finite fluid block implementation. * * It is highly recommended that you use/extend this class for finite fluid blocks. * * @author OvermindDL1, KingLemming * */ public class BlockFluidFinite extends BlockFluidBase { public BlockFluidFinite(Fluid fluid, Material material) { super(fluid, material); } @Override public int getQuantaValue(IBlockAccess world, int x, int y, int z) { if (world.getBlock(x, y, z).isAir(world, x, y, z)) { return 0; } if (world.getBlock(x, y, z) != this) { return -1; } int quantaRemaining = world.getBlockMetadata(x, y, z) + 1; return quantaRemaining; } /** * Returns whether the raytracing must ignore this block. Args : metadata, stopOnLiquid */ @Override public boolean canStopRayTrace(int meta, boolean fullHit) { return fullHit && meta == quantaPerBlock - 1; } @Override public int getMaxRenderHeightMeta() { return quantaPerBlock - 1; } /** * Ticks the block if it's been scheduled */ @Override public void updateTick(World world, int x, int y, int z, Random rand) { boolean changed = false; int quantaRemaining = world.getBlockMetadata(x, y, z) + 1; // Flow vertically if possible int prevRemaining = quantaRemaining; quantaRemaining = tryToFlowVerticallyInto(world, x, y, z, quantaRemaining); if (quantaRemaining < 1) { return; } else if (quantaRemaining != prevRemaining) { changed = true; if (quantaRemaining == 1) { world.setBlockMetadataWithNotify(x, y, z, quantaRemaining - 1, 2); return; } } else if (quantaRemaining == 1) { return; } // Flow out if possible int lowerthan = quantaRemaining - 1; if (displaceIfPossible(world, x, y, z - 1)) world.setBlock(x, y, z - 1, Blocks.air); if (displaceIfPossible(world, x, y, z + 1)) world.setBlock(x, y, z + 1, Blocks.air); if (displaceIfPossible(world, x - 1, y, z )) world.setBlock(x - 1, y, z, Blocks.air); if (displaceIfPossible(world, x + 1, y, z )) world.setBlock(x + 1, y, z, Blocks.air); int north = getQuantaValueBelow(world, x, y, z - 1, lowerthan); int south = getQuantaValueBelow(world, x, y, z + 1, lowerthan); int west = getQuantaValueBelow(world, x - 1, y, z, lowerthan); int east = getQuantaValueBelow(world, x + 1, y, z, lowerthan); int total = quantaRemaining; int count = 1; if (north >= 0) { count++; total += north; } if (south >= 0) { count++; total += south; } if (west >= 0) { count++; total += west; } if (east >= 0) { ++count; total += east; } if (count == 1) { if (changed) { world.setBlockMetadataWithNotify(x, y, z, quantaRemaining - 1, 2); } return; } int each = total / count; int rem = total % count; if (north >= 0) { int newnorth = each; if (rem == count || rem > 1 && rand.nextInt(count - rem) != 0) { ++newnorth; --rem; } if (newnorth != north) { if (newnorth == 0) { world.setBlock(x, y, z - 1, Blocks.air); } else { world.setBlock(x, y, z - 1, this, newnorth - 1, 2); } world.scheduleBlockUpdate(x, y, z - 1, this, tickRate); } --count; } if (south >= 0) { int newsouth = each; if (rem == count || rem > 1 && rand.nextInt(count - rem) != 0) { ++newsouth; --rem; } if (newsouth != south) { if (newsouth == 0) { world.setBlock(x, y, z + 1, Blocks.air); } else { world.setBlock(x, y, z + 1, this, newsouth - 1, 2); } world.scheduleBlockUpdate(x, y, z + 1, this, tickRate); } --count; } if (west >= 0) { int newwest = each; if (rem == count || rem > 1 && rand.nextInt(count - rem) != 0) { ++newwest; --rem; } if (newwest != west) { if (newwest == 0) { world.setBlock(x - 1, y, z, Blocks.air); } else { world.setBlock(x - 1, y, z, this, newwest - 1, 2); } world.scheduleBlockUpdate(x - 1, y, z, this, tickRate); } --count; } if (east >= 0) { int neweast = each; if (rem == count || rem > 1 && rand.nextInt(count - rem) != 0) { ++neweast; --rem; } if (neweast != east) { if (neweast == 0) { world.setBlock(x + 1, y, z, Blocks.air); } else { world.setBlock(x + 1, y, z, this, neweast - 1, 2); } world.scheduleBlockUpdate(x + 1, y, z, this, tickRate); } --count; } if (rem > 0) { ++each; } world.setBlockMetadataWithNotify(x, y, z, each - 1, 2); } public int tryToFlowVerticallyInto(World world, int x, int y, int z, int amtToInput) { int otherY = y + densityDir; if (otherY < 0 || otherY >= world.getHeight()) { world.setBlock(x, y, z, Blocks.air); return 0; } int amt = getQuantaValueBelow(world, x, otherY, z, quantaPerBlock); if (amt >= 0) { amt += amtToInput; if (amt > quantaPerBlock) { world.setBlock(x, otherY, z, this, quantaPerBlock - 1, 3); world.scheduleBlockUpdate(x, otherY, z, this, tickRate); return amt - quantaPerBlock; } else if (amt > 0) { world.setBlock(x, otherY, z, this, amt - 1, 3); world.scheduleBlockUpdate(x, otherY, z, this, tickRate); world.setBlock(x, y, z, Blocks.air); return 0; } return amtToInput; } else { int density_other = getDensity(world, x, otherY, z); if (density_other == Integer.MAX_VALUE) { if (displaceIfPossible(world, x, otherY, z)) { world.setBlock(x, otherY, z, this, amtToInput - 1, 3); world.scheduleBlockUpdate(x, otherY, z, this, tickRate); world.setBlock(x, y, z, Blocks.air); return 0; } else { return amtToInput; } } if (densityDir < 0) { if (density_other < density) // then swap { BlockFluidBase block = (BlockFluidBase)world.getBlock(x, otherY, z); int otherData = world.getBlockMetadata(x, otherY, z); world.setBlock(x, otherY, z, this, amtToInput - 1, 3); world.setBlock(x, y, z, block, otherData, 3); world.scheduleBlockUpdate(x, otherY, z, this, tickRate); world.scheduleBlockUpdate(x, y, z, block, block.tickRate(world)); return 0; } } else { if (density_other > density) { BlockFluidBase block = (BlockFluidBase)world.getBlock(x, otherY, z); int otherData = world.getBlockMetadata(x, otherY, z); world.setBlock(x, otherY, z, this, amtToInput - 1, 3); world.setBlock(x, y, z, block, otherData, 3); world.scheduleBlockUpdate(x, otherY, z, this, tickRate); world.scheduleBlockUpdate(x, y, z, block, block.tickRate(world)); return 0; } } return amtToInput; } } /* IFluidBlock */ @Override public FluidStack drain(World world, int x, int y, int z, boolean doDrain) { if (doDrain) { world.setBlock(x, y, z, Blocks.air); } return new FluidStack(getFluid(), MathHelper.floor_float(getQuantaPercentage(world, x, y, z) * FluidContainerRegistry.BUCKET_VOLUME)); } @Override public boolean canDrain(World world, int x, int y, int z) { return true; } }