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    private 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 getBlockTextureFromSideAndMetadata(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}