    001package net.minecraft.block;
    002
    003import cpw.mods.fml.relauncher.Side;
    004import cpw.mods.fml.relauncher.SideOnly;
    005import java.util.Random;
    006import net.minecraft.block.material.Material;
    007import net.minecraft.client.renderer.texture.IconRegister;
    008import net.minecraft.entity.Entity;
    009import net.minecraft.util.AxisAlignedBB;
    010import net.minecraft.util.Icon;
    011import net.minecraft.util.Vec3;
    012import net.minecraft.world.IBlockAccess;
    013import net.minecraft.world.World;
    014
    015public abstract class BlockFluid extends Block
    016{
    017    @SideOnly(Side.CLIENT)
    018    protected Icon[] theIcon;
    019
    020    protected BlockFluid(int par1, Material par2Material)
    021    {
    022        super(par1, par2Material);
    023        float f = 0.0F;
    024        float f1 = 0.0F;
    025        this.setBlockBounds(0.0F + f1, 0.0F + f, 0.0F + f1, 1.0F + f1, 1.0F + f, 1.0F + f1);
    026        this.setTickRandomly(true);
    027    }
    028
    029    public boolean getBlocksMovement(IBlockAccess par1IBlockAccess, int par2, int par3, int par4)
    030    {
    031        return this.blockMaterial != Material.lava;
    032    }
    033
    034    @SideOnly(Side.CLIENT)
    035    public int getBlockColor()
    036    {
    037        return 16777215;
    038    }
    039
    040    @SideOnly(Side.CLIENT)
    041
    042    /**
    043     * Returns a integer with hex for 0xrrggbb with this color multiplied against the blocks color. Note only called
    044     * when first determining what to render.
    045     */
    046    public int colorMultiplier(IBlockAccess par1IBlockAccess, int par2, int par3, int par4)
    047    {
    048        if (this.blockMaterial != Material.water)
    049        {
    050            return 16777215;
    051        }
    052        else
    053        {
    054            int l = 0;
    055            int i1 = 0;
    056            int j1 = 0;
    057
    058            for (int k1 = -1; k1 <= 1; ++k1)
    059            {
    060                for (int l1 = -1; l1 <= 1; ++l1)
    061                {
    062                    int i2 = par1IBlockAccess.getBiomeGenForCoords(par2 + l1, par4 + k1).getWaterColorMultiplier();
    063                    l += (i2 & 16711680) >> 16;
    064                    i1 += (i2 & 65280) >> 8;
    065                    j1 += i2 & 255;
    066                }
    067            }
    068
    069            return (l / 9 & 255) << 16 | (i1 / 9 & 255) << 8 | j1 / 9 & 255;
    070        }
    071    }
    072
    073    /**
    074     * Returns the percentage of the fluid block that is air, based on the given flow decay of the fluid.
    075     */
    076    public static float getFluidHeightPercent(int par0)
    077    {
    078        if (par0 >= 8)
    079        {
    080            par0 = 0;
    081        }
    082
    083        return (float)(par0 + 1) / 9.0F;
    084    }
    085
    086    @SideOnly(Side.CLIENT)
    087
    088    /**
    089     * From the specified side and block metadata retrieves the blocks texture. Args: side, metadata
    090     */
    091    public Icon getIcon(int par1, int par2)
    092    {
    093        return par1 != 0 && par1 != 1 ? this.theIcon[1] : this.theIcon[0];
    094    }
    095
    096    /**
    097     * Returns the amount of fluid decay at the coordinates, or -1 if the block at the coordinates is not the same
    098     * material as the fluid.
    099     */
    100    protected int getFlowDecay(World par1World, int par2, int par3, int par4)
    101    {
    102        return par1World.getBlockMaterial(par2, par3, par4) == this.blockMaterial ? par1World.getBlockMetadata(par2, par3, par4) : -1;
    103    }
    104
    105    /**
    106     * Returns the flow decay but converts values indicating falling liquid (values >=8) to their effective source block
    107     * value of zero.
    108     */
    109    protected int getEffectiveFlowDecay(IBlockAccess par1IBlockAccess, int par2, int par3, int par4)
    110    {
    111        if (par1IBlockAccess.getBlockMaterial(par2, par3, par4) != this.blockMaterial)
    112        {
    113            return -1;
    114        }
    115        else
    116        {
    117            int l = par1IBlockAccess.getBlockMetadata(par2, par3, par4);
    118
    119            if (l >= 8)
    120            {
    121                l = 0;
    122            }
    123
    124            return l;
    125        }
    126    }
    127
    128    /**
    129     * If this block doesn't render as an ordinary block it will return False (examples: signs, buttons, stairs, etc)
    130     */
    131    public boolean renderAsNormalBlock()
    132    {
    133        return false;
    134    }
    135
    136    /**
    137     * Is this block (a) opaque and (b) a full 1m cube?  This determines whether or not to render the shared face of two
    138     * adjacent blocks and also whether the player can attach torches, redstone wire, etc to this block.
    139     */
    140    public boolean isOpaqueCube()
    141    {
    142        return false;
    143    }
    144
    145    /**
    146     * Returns whether this block is collideable based on the arguments passed in Args: blockMetaData, unknownFlag
    147     */
    148    public boolean canCollideCheck(int par1, boolean par2)
    149    {
    150        return par2 && par1 == 0;
    151    }
    152
    153    /**
    154     * Returns Returns true if the given side of this block type should be rendered (if it's solid or not), if the
    155     * adjacent block is at the given coordinates. Args: blockAccess, x, y, z, side
    156     */
    157    public boolean isBlockSolid(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
    158    {
    159        Material material = par1IBlockAccess.getBlockMaterial(par2, par3, par4);
    160        return material == this.blockMaterial ? false : (par5 == 1 ? true : (material == Material.ice ? false : super.isBlockSolid(par1IBlockAccess, par2, par3, par4, par5)));
    161    }
    162
    163    @SideOnly(Side.CLIENT)
    164
    165    /**
    166     * Returns true if the given side of this block type should be rendered, if the adjacent block is at the given
    167     * coordinates.  Args: blockAccess, x, y, z, side
    168     */
    169    public boolean shouldSideBeRendered(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
    170    {
    171        Material material = par1IBlockAccess.getBlockMaterial(par2, par3, par4);
    172        return material == this.blockMaterial ? false : (par5 == 1 ? true : (material == Material.ice ? false : super.shouldSideBeRendered(par1IBlockAccess, par2, par3, par4, par5)));
    173    }
    174
    175    /**
    176     * Returns a bounding box from the pool of bounding boxes (this means this box can change after the pool has been
    177     * cleared to be reused)
    178     */
    179    public AxisAlignedBB getCollisionBoundingBoxFromPool(World par1World, int par2, int par3, int par4)
    180    {
    181        return null;
    182    }
    183
    184    /**
    185     * The type of render function that is called for this block
    186     */
    187    public int getRenderType()
    188    {
    189        return 4;
    190    }
    191
    192    /**
    193     * Returns the ID of the items to drop on destruction.
    194     */
    195    public int idDropped(int par1, Random par2Random, int par3)
    196    {
    197        return 0;
    198    }
    199
    200    /**
    201     * Returns the quantity of items to drop on block destruction.
    202     */
    203    public int quantityDropped(Random par1Random)
    204    {
    205        return 0;
    206    }
    207
    208    /**
    209     * Returns a vector indicating the direction and intensity of fluid flow.
    210     */
    211    private Vec3 getFlowVector(IBlockAccess par1IBlockAccess, int par2, int par3, int par4)
    212    {
    213        Vec3 vec3 = par1IBlockAccess.getWorldVec3Pool().getVecFromPool(0.0D, 0.0D, 0.0D);
    214        int l = this.getEffectiveFlowDecay(par1IBlockAccess, par2, par3, par4);
    215
    216        for (int i1 = 0; i1 < 4; ++i1)
    217        {
    218            int j1 = par2;
    219            int k1 = par4;
    220
    221            if (i1 == 0)
    222            {
    223                j1 = par2 - 1;
    224            }
    225
    226            if (i1 == 1)
    227            {
    228                k1 = par4 - 1;
    229            }
    230
    231            if (i1 == 2)
    232            {
    233                ++j1;
    234            }
    235
    236            if (i1 == 3)
    237            {
    238                ++k1;
    239            }
    240
    241            int l1 = this.getEffectiveFlowDecay(par1IBlockAccess, j1, par3, k1);
    242            int i2;
    243
    244            if (l1 < 0)
    245            {
    246                if (!par1IBlockAccess.getBlockMaterial(j1, par3, k1).blocksMovement())
    247                {
    248                    l1 = this.getEffectiveFlowDecay(par1IBlockAccess, j1, par3 - 1, k1);
    249
    250                    if (l1 >= 0)
    251                    {
    252                        i2 = l1 - (l - 8);
    253                        vec3 = vec3.addVector((double)((j1 - par2) * i2), (double)((par3 - par3) * i2), (double)((k1 - par4) * i2));
    254                    }
    255                }
    256            }
    257            else if (l1 >= 0)
    258            {
    259                i2 = l1 - l;
    260                vec3 = vec3.addVector((double)((j1 - par2) * i2), (double)((par3 - par3) * i2), (double)((k1 - par4) * i2));
    261            }
    262        }
    263
    264        if (par1IBlockAccess.getBlockMetadata(par2, par3, par4) >= 8)
    265        {
    266            boolean flag = false;
    267
    268            if (flag || this.isBlockSolid(par1IBlockAccess, par2, par3, par4 - 1, 2))
    269            {
    270                flag = true;
    271            }
    272
    273            if (flag || this.isBlockSolid(par1IBlockAccess, par2, par3, par4 + 1, 3))
    274            {
    275                flag = true;
    276            }
    277
    278            if (flag || this.isBlockSolid(par1IBlockAccess, par2 - 1, par3, par4, 4))
    279            {
    280                flag = true;
    281            }
    282
    283            if (flag || this.isBlockSolid(par1IBlockAccess, par2 + 1, par3, par4, 5))
    284            {
    285                flag = true;
    286            }
    287
    288            if (flag || this.isBlockSolid(par1IBlockAccess, par2, par3 + 1, par4 - 1, 2))
    289            {
    290                flag = true;
    291            }
    292
    293            if (flag || this.isBlockSolid(par1IBlockAccess, par2, par3 + 1, par4 + 1, 3))
    294            {
    295                flag = true;
    296            }
    297
    298            if (flag || this.isBlockSolid(par1IBlockAccess, par2 - 1, par3 + 1, par4, 4))
    299            {
    300                flag = true;
    301            }
    302
    303            if (flag || this.isBlockSolid(par1IBlockAccess, par2 + 1, par3 + 1, par4, 5))
    304            {
    305                flag = true;
    306            }
    307
    308            if (flag)
    309            {
    310                vec3 = vec3.normalize().addVector(0.0D, -6.0D, 0.0D);
    311            }
    312        }
    313
    314        vec3 = vec3.normalize();
    315        return vec3;
    316    }
    317
    318    /**
    319     * Can add to the passed in vector for a movement vector to be applied to the entity. Args: x, y, z, entity, vec3d
    320     */
    321    public void velocityToAddToEntity(World par1World, int par2, int par3, int par4, Entity par5Entity, Vec3 par6Vec3)
    322    {
    323        Vec3 vec31 = this.getFlowVector(par1World, par2, par3, par4);
    324        par6Vec3.xCoord += vec31.xCoord;
    325        par6Vec3.yCoord += vec31.yCoord;
    326        par6Vec3.zCoord += vec31.zCoord;
    327    }
    328
    329    /**
    330     * How many world ticks before ticking
    331     */
    332    public int tickRate(World par1World)
    333    {
    334        return this.blockMaterial == Material.water ? 5 : (this.blockMaterial == Material.lava ? (par1World.provider.hasNoSky ? 10 : 30) : 0);
    335    }
    336
    337    /**
    338     * Called whenever the block is added into the world. Args: world, x, y, z
    339     */
    340    public void onBlockAdded(World par1World, int par2, int par3, int par4)
    341    {
    342        this.checkForHarden(par1World, par2, par3, par4);
    343    }
    344
    345    /**
    346     * Lets the block know when one of its neighbor changes. Doesn't know which neighbor changed (coordinates passed are
    347     * their own) Args: x, y, z, neighbor blockID
    348     */
    349    public void onNeighborBlockChange(World par1World, int par2, int par3, int par4, int par5)
    350    {
    351        this.checkForHarden(par1World, par2, par3, par4);
    352    }
    353
    354    @SideOnly(Side.CLIENT)
    355
    356    /**
    357     * Goes straight to getLightBrightnessForSkyBlocks for Blocks, does some fancy computing for Fluids
    358     */
    359    public int getMixedBrightnessForBlock(IBlockAccess par1IBlockAccess, int par2, int par3, int par4)
    360    {
    361        int l = par1IBlockAccess.getLightBrightnessForSkyBlocks(par2, par3, par4, 0);
    362        int i1 = par1IBlockAccess.getLightBrightnessForSkyBlocks(par2, par3 + 1, par4, 0);
    363        int j1 = l & 255;
    364        int k1 = i1 & 255;
    365        int l1 = l >> 16 & 255;
    366        int i2 = i1 >> 16 & 255;
    367        return (j1 > k1 ? j1 : k1) | (l1 > i2 ? l1 : i2) << 16;
    368    }
    369
    370    @SideOnly(Side.CLIENT)
    371
    372    /**
    373     * How bright to render this block based on the light its receiving. Args: iBlockAccess, x, y, z
    374     */
    375    public float getBlockBrightness(IBlockAccess par1IBlockAccess, int par2, int par3, int par4)
    376    {
    377        float f = par1IBlockAccess.getLightBrightness(par2, par3, par4);
    378        float f1 = par1IBlockAccess.getLightBrightness(par2, par3 + 1, par4);
    379        return f > f1 ? f : f1;
    380    }
    381
    382    @SideOnly(Side.CLIENT)
    383
    384    /**
    385     * Returns which pass should this block be rendered on. 0 for solids and 1 for alpha
    386     */
    387    public int getRenderBlockPass()
    388    {
    389        return this.blockMaterial == Material.water ? 1 : 0;
    390    }
    391
    392    @SideOnly(Side.CLIENT)
    393
    394    /**
    395     * A randomly called display update to be able to add particles or other items for display
    396     */
    397    public void randomDisplayTick(World par1World, int par2, int par3, int par4, Random par5Random)
    398    {
    399        int l;
    400
    401        if (this.blockMaterial == Material.water)
    402        {
    403            if (par5Random.nextInt(10) == 0)
    404            {
    405                l = par1World.getBlockMetadata(par2, par3, par4);
    406
    407                if (l <= 0 || l >= 8)
    408                {
    409                    par1World.spawnParticle("suspended", (double)((float)par2 + par5Random.nextFloat()), (double)((float)par3 + par5Random.nextFloat()), (double)((float)par4 + par5Random.nextFloat()), 0.0D, 0.0D, 0.0D);
    410                }
    411            }
    412
    413            for (l = 0; l < 0; ++l)
    414            {
    415                int i1 = par5Random.nextInt(4);
    416                int j1 = par2;
    417                int k1 = par4;
    418
    419                if (i1 == 0)
    420                {
    421                    j1 = par2 - 1;
    422                }
    423
    424                if (i1 == 1)
    425                {
    426                    ++j1;
    427                }
    428
    429                if (i1 == 2)
    430                {
    431                    k1 = par4 - 1;
    432                }
    433
    434                if (i1 == 3)
    435                {
    436                    ++k1;
    437                }
    438
    439                if (par1World.getBlockMaterial(j1, par3, k1) == Material.air && (par1World.getBlockMaterial(j1, par3 - 1, k1).blocksMovement() || par1World.getBlockMaterial(j1, par3 - 1, k1).isLiquid()))
    440                {
    441                    float f = 0.0625F;
    442                    double d0 = (double)((float)par2 + par5Random.nextFloat());
    443                    double d1 = (double)((float)par3 + par5Random.nextFloat());
    444                    double d2 = (double)((float)par4 + par5Random.nextFloat());
    445
    446                    if (i1 == 0)
    447                    {
    448                        d0 = (double)((float)par2 - f);
    449                    }
    450
    451                    if (i1 == 1)
    452                    {
    453                        d0 = (double)((float)(par2 + 1) + f);
    454                    }
    455
    456                    if (i1 == 2)
    457                    {
    458                        d2 = (double)((float)par4 - f);
    459                    }
    460
    461                    if (i1 == 3)
    462                    {
    463                        d2 = (double)((float)(par4 + 1) + f);
    464                    }
    465
    466                    double d3 = 0.0D;
    467                    double d4 = 0.0D;
    468
    469                    if (i1 == 0)
    470                    {
    471                        d3 = (double)(-f);
    472                    }
    473
    474                    if (i1 == 1)
    475                    {
    476                        d3 = (double)f;
    477                    }
    478
    479                    if (i1 == 2)
    480                    {
    481                        d4 = (double)(-f);
    482                    }
    483
    484                    if (i1 == 3)
    485                    {
    486                        d4 = (double)f;
    487                    }
    488
    489                    par1World.spawnParticle("splash", d0, d1, d2, d3, 0.0D, d4);
    490                }
    491            }
    492        }
    493
    494        if (this.blockMaterial == Material.water && par5Random.nextInt(64) == 0)
    495        {
    496            l = par1World.getBlockMetadata(par2, par3, par4);
    497
    498            if (l > 0 && l < 8)
    499            {
    500                par1World.playSound((double)((float)par2 + 0.5F), (double)((float)par3 + 0.5F), (double)((float)par4 + 0.5F), "liquid.water", par5Random.nextFloat() * 0.25F + 0.75F, par5Random.nextFloat() * 1.0F + 0.5F, false);
    501            }
    502        }
    503
    504        double d5;
    505        double d6;
    506        double d7;
    507
    508        if (this.blockMaterial == Material.lava && par1World.getBlockMaterial(par2, par3 + 1, par4) == Material.air && !par1World.isBlockOpaqueCube(par2, par3 + 1, par4))
    509        {
    510            if (par5Random.nextInt(100) == 0)
    511            {
    512                d5 = (double)((float)par2 + par5Random.nextFloat());
    513                d7 = (double)par3 + this.maxY;
    514                d6 = (double)((float)par4 + par5Random.nextFloat());
    515                par1World.spawnParticle("lava", d5, d7, d6, 0.0D, 0.0D, 0.0D);
    516                par1World.playSound(d5, d7, d6, "liquid.lavapop", 0.2F + par5Random.nextFloat() * 0.2F, 0.9F + par5Random.nextFloat() * 0.15F, false);
    517            }
    518
    519            if (par5Random.nextInt(200) == 0)
    520            {
    521                par1World.playSound((double)par2, (double)par3, (double)par4, "liquid.lava", 0.2F + par5Random.nextFloat() * 0.2F, 0.9F + par5Random.nextFloat() * 0.15F, false);
    522            }
    523        }
    524
    525        if (par5Random.nextInt(10) == 0 && par1World.doesBlockHaveSolidTopSurface(par2, par3 - 1, par4) && !par1World.getBlockMaterial(par2, par3 - 2, par4).blocksMovement())
    526        {
    527            d5 = (double)((float)par2 + par5Random.nextFloat());
    528            d7 = (double)par3 - 1.05D;
    529            d6 = (double)((float)par4 + par5Random.nextFloat());
    530
    531            if (this.blockMaterial == Material.water)
    532            {
    533                par1World.spawnParticle("dripWater", d5, d7, d6, 0.0D, 0.0D, 0.0D);
    534            }
    535            else
    536            {
    537                par1World.spawnParticle("dripLava", d5, d7, d6, 0.0D, 0.0D, 0.0D);
    538            }
    539        }
    540    }
    541
    542    @SideOnly(Side.CLIENT)
    543
    544    /**
    545     * the sin and cos of this number determine the surface gradient of the flowing block.
    546     */
    547    public static double getFlowDirection(IBlockAccess par0IBlockAccess, int par1, int par2, int par3, Material par4Material)
    548    {
    549        Vec3 vec3 = null;
    550
    551        if (par4Material == Material.water)
    552        {
    553            vec3 = Block.waterMoving.getFlowVector(par0IBlockAccess, par1, par2, par3);
    554        }
    555
    556        if (par4Material == Material.lava)
    557        {
    558            vec3 = Block.lavaMoving.getFlowVector(par0IBlockAccess, par1, par2, par3);
    559        }
    560
    561        return vec3.xCoord == 0.0D && vec3.zCoord == 0.0D ? -1000.0D : Math.atan2(vec3.zCoord, vec3.xCoord) - (Math.PI / 2D);
    562    }
    563
    564    /**
    565     * Forces lava to check to see if it is colliding with water, and then decide what it should harden to.
    566     */
    567    private void checkForHarden(World par1World, int par2, int par3, int par4)
    568    {
    569        if (par1World.getBlockId(par2, par3, par4) == this.blockID)
    570        {
    571            if (this.blockMaterial == Material.lava)
    572            {
    573                boolean flag = false;
    574
    575                if (flag || par1World.getBlockMaterial(par2, par3, par4 - 1) == Material.water)
    576                {
    577                    flag = true;
    578                }
    579
    580                if (flag || par1World.getBlockMaterial(par2, par3, par4 + 1) == Material.water)
    581                {
    582                    flag = true;
    583                }
    584
    585                if (flag || par1World.getBlockMaterial(par2 - 1, par3, par4) == Material.water)
    586                {
    587                    flag = true;
    588                }
    589
    590                if (flag || par1World.getBlockMaterial(par2 + 1, par3, par4) == Material.water)
    591                {
    592                    flag = true;
    593                }
    594
    595                if (flag || par1World.getBlockMaterial(par2, par3 + 1, par4) == Material.water)
    596                {
    597                    flag = true;
    598                }
    599
    600                if (flag)
    601                {
    602                    int l = par1World.getBlockMetadata(par2, par3, par4);
    603
    604                    if (l == 0)
    605                    {
    606                        par1World.setBlock(par2, par3, par4, Block.obsidian.blockID);
    607                    }
    608                    else if (l <= 4)
    609                    {
    610                        par1World.setBlock(par2, par3, par4, Block.cobblestone.blockID);
    611                    }
    612
    613                    this.triggerLavaMixEffects(par1World, par2, par3, par4);
    614                }
    615            }
    616        }
    617    }
    618
    619    /**
    620     * Creates fizzing sound and smoke. Used when lava flows over block or mixes with water.
    621     */
    622    protected void triggerLavaMixEffects(World par1World, int par2, int par3, int par4)
    623    {
    624        par1World.playSoundEffect((double)((float)par2 + 0.5F), (double)((float)par3 + 0.5F), (double)((float)par4 + 0.5F), "random.fizz", 0.5F, 2.6F + (par1World.rand.nextFloat() - par1World.rand.nextFloat()) * 0.8F);
    625
    626        for (int l = 0; l < 8; ++l)
    627        {
    628            par1World.spawnParticle("largesmoke", (double)par2 + Math.random(), (double)par3 + 1.2D, (double)par4 + Math.random(), 0.0D, 0.0D, 0.0D);
    629        }
    630    }
    631
    632    @SideOnly(Side.CLIENT)
    633
    634    /**
    635     * When this method is called, your block should register all the icons it needs with the given IconRegister. This
    636     * is the only chance you get to register icons.
    637     */
    638    public void registerIcons(IconRegister par1IconRegister)
    639    {
    640        if (this.blockMaterial == Material.lava)
    641        {
    642            this.theIcon = new Icon[] {par1IconRegister.registerIcon("lava"), par1IconRegister.registerIcon("lava_flow")};
    643        }
    644        else
    645        {
    646            this.theIcon = new Icon[] {par1IconRegister.registerIcon("water"), par1IconRegister.registerIcon("water_flow")};
    647        }
    648    }
    649
    650    @SideOnly(Side.CLIENT)
    651    public static Icon func_94424_b(String par0Str)
    652    {
    653        return par0Str == "water" ? Block.waterMoving.theIcon[0] : (par0Str == "water_flow" ? Block.waterMoving.theIcon[1] : (par0Str == "lava" ? Block.lavaMoving.theIcon[0] : (par0Str == "lava_flow" ? Block.lavaMoving.theIcon[1] : null)));
    654    }
    655}