001    package net.minecraft.src;
002    
003    import cpw.mods.fml.common.Side;
004    import cpw.mods.fml.common.asm.SideOnly;
005    import java.util.Random;
006    import net.minecraftforge.common.ForgeDirection;
007    import static net.minecraftforge.common.ForgeDirection.*;
008    
009    public class BlockFire extends Block
010    {
011        /** The chance this block will encourage nearby blocks to catch on fire */
012        private int[] chanceToEncourageFire = new int[256];
013    
014        /**
015         * This is an array indexed by block ID the larger the number in the array the more likely a block type will catch
016         * fires
017         */
018        private int[] abilityToCatchFire = new int[256];
019    
020        protected BlockFire(int par1, int par2)
021        {
022            super(par1, par2, Material.fire);
023            this.setTickRandomly(true);
024        }
025    
026        /**
027         * This method is called on a block after all other blocks gets already created. You can use it to reference and
028         * configure something on the block that needs the others ones.
029         */
030        public void initializeBlock()
031        {
032            abilityToCatchFire = Block.blockFlammability;
033            chanceToEncourageFire = Block.blockFireSpreadSpeed;
034            this.setBurnRate(Block.planks.blockID, 5, 20);
035            this.setBurnRate(Block.woodDoubleSlab.blockID, 5, 20);
036            this.setBurnRate(Block.woodSingleSlab.blockID, 5, 20);
037            this.setBurnRate(Block.fence.blockID, 5, 20);
038            this.setBurnRate(Block.stairCompactPlanks.blockID, 5, 20);
039            this.setBurnRate(Block.stairsWoodBirch.blockID, 5, 20);
040            this.setBurnRate(Block.stairsWoodSpruce.blockID, 5, 20);
041            this.setBurnRate(Block.stairsWoodJungle.blockID, 5, 20);
042            this.setBurnRate(Block.wood.blockID, 5, 5);
043            this.setBurnRate(Block.leaves.blockID, 30, 60);
044            this.setBurnRate(Block.bookShelf.blockID, 30, 20);
045            this.setBurnRate(Block.tnt.blockID, 15, 100);
046            this.setBurnRate(Block.tallGrass.blockID, 60, 100);
047            this.setBurnRate(Block.cloth.blockID, 30, 60);
048            this.setBurnRate(Block.vine.blockID, 15, 100);
049        }
050    
051        /**
052         * Sets the burn rate for a block. The larger abilityToCatchFire the more easily it will catch. The larger
053         * chanceToEncourageFire the faster it will burn and spread to other blocks. Args: blockID, chanceToEncourageFire,
054         * abilityToCatchFire
055         */
056        private void setBurnRate(int par1, int par2, int par3)
057        {
058            Block.setBurnProperties(par1, par2, par3);
059        }
060    
061        /**
062         * Returns a bounding box from the pool of bounding boxes (this means this box can change after the pool has been
063         * cleared to be reused)
064         */
065        public AxisAlignedBB getCollisionBoundingBoxFromPool(World par1World, int par2, int par3, int par4)
066        {
067            return null;
068        }
069    
070        /**
071         * Is this block (a) opaque and (b) a full 1m cube?  This determines whether or not to render the shared face of two
072         * adjacent blocks and also whether the player can attach torches, redstone wire, etc to this block.
073         */
074        public boolean isOpaqueCube()
075        {
076            return false;
077        }
078    
079        /**
080         * If this block doesn't render as an ordinary block it will return False (examples: signs, buttons, stairs, etc)
081         */
082        public boolean renderAsNormalBlock()
083        {
084            return false;
085        }
086    
087        /**
088         * The type of render function that is called for this block
089         */
090        public int getRenderType()
091        {
092            return 3;
093        }
094    
095        /**
096         * Returns the quantity of items to drop on block destruction.
097         */
098        public int quantityDropped(Random par1Random)
099        {
100            return 0;
101        }
102    
103        /**
104         * How many world ticks before ticking
105         */
106        public int tickRate()
107        {
108            return 30;
109        }
110    
111        /**
112         * Ticks the block if it's been scheduled
113         */
114        public void updateTick(World par1World, int par2, int par3, int par4, Random par5Random)
115        {
116            if (par1World.getGameRules().getGameRuleBooleanValue("doFireTick"))
117            {
118                Block base = Block.blocksList[par1World.getBlockId(par2, par3 - 1, par4)];
119                boolean var6 = (base != null && base.isFireSource(par1World, par2, par3 - 1, par4, par1World.getBlockMetadata(par2, par3 - 1, par4), UP));
120    
121                if (!this.canPlaceBlockAt(par1World, par2, par3, par4))
122                {
123                    par1World.setBlockWithNotify(par2, par3, par4, 0);
124                }
125    
126                if (!var6 && par1World.isRaining() && (par1World.canLightningStrikeAt(par2, par3, par4) || par1World.canLightningStrikeAt(par2 - 1, par3, par4) || par1World.canLightningStrikeAt(par2 + 1, par3, par4) || par1World.canLightningStrikeAt(par2, par3, par4 - 1) || par1World.canLightningStrikeAt(par2, par3, par4 + 1)))
127                {
128                    par1World.setBlockWithNotify(par2, par3, par4, 0);
129                }
130                else
131                {
132                    int var7 = par1World.getBlockMetadata(par2, par3, par4);
133    
134                    if (var7 < 15)
135                    {
136                        par1World.setBlockMetadata(par2, par3, par4, var7 + par5Random.nextInt(3) / 2);
137                    }
138    
139                    par1World.scheduleBlockUpdate(par2, par3, par4, this.blockID, this.tickRate() + par5Random.nextInt(10));
140    
141                    if (!var6 && !this.canNeighborBurn(par1World, par2, par3, par4))
142                    {
143                        if (!par1World.doesBlockHaveSolidTopSurface(par2, par3 - 1, par4) || var7 > 3)
144                        {
145                            par1World.setBlockWithNotify(par2, par3, par4, 0);
146                        }
147                    }
148                    else if (!var6 && !this.canBlockCatchFire(par1World, par2, par3 - 1, par4, UP) && var7 == 15 && par5Random.nextInt(4) == 0)
149                    {
150                        par1World.setBlockWithNotify(par2, par3, par4, 0);
151                    }
152                    else
153                    {
154                        boolean var8 = par1World.isBlockHighHumidity(par2, par3, par4);
155                        byte var9 = 0;
156    
157                        if (var8)
158                        {
159                            var9 = -50;
160                        }
161    
162                        this.tryToCatchBlockOnFire(par1World, par2 + 1, par3, par4, 300 + var9, par5Random, var7, WEST );
163                        this.tryToCatchBlockOnFire(par1World, par2 - 1, par3, par4, 300 + var9, par5Random, var7, EAST );
164                        this.tryToCatchBlockOnFire(par1World, par2, par3 - 1, par4, 250 + var9, par5Random, var7, UP   );
165                        this.tryToCatchBlockOnFire(par1World, par2, par3 + 1, par4, 250 + var9, par5Random, var7, DOWN );
166                        this.tryToCatchBlockOnFire(par1World, par2, par3, par4 - 1, 300 + var9, par5Random, var7, SOUTH);
167                        this.tryToCatchBlockOnFire(par1World, par2, par3, par4 + 1, 300 + var9, par5Random, var7, NORTH);
168    
169                        for (int var10 = par2 - 1; var10 <= par2 + 1; ++var10)
170                        {
171                            for (int var11 = par4 - 1; var11 <= par4 + 1; ++var11)
172                            {
173                                for (int var12 = par3 - 1; var12 <= par3 + 4; ++var12)
174                                {
175                                    if (var10 != par2 || var12 != par3 || var11 != par4)
176                                    {
177                                        int var13 = 100;
178    
179                                        if (var12 > par3 + 1)
180                                        {
181                                            var13 += (var12 - (par3 + 1)) * 100;
182                                        }
183    
184                                        int var14 = this.getChanceOfNeighborsEncouragingFire(par1World, var10, var12, var11);
185    
186                                        if (var14 > 0)
187                                        {
188                                            int var15 = (var14 + 40 + par1World.difficultySetting * 7) / (var7 + 30);
189    
190                                            if (var8)
191                                            {
192                                                var15 /= 2;
193                                            }
194    
195                                            if (var15 > 0 && par5Random.nextInt(var13) <= var15 && (!par1World.isRaining() || !par1World.canLightningStrikeAt(var10, var12, var11)) && !par1World.canLightningStrikeAt(var10 - 1, var12, par4) && !par1World.canLightningStrikeAt(var10 + 1, var12, var11) && !par1World.canLightningStrikeAt(var10, var12, var11 - 1) && !par1World.canLightningStrikeAt(var10, var12, var11 + 1))
196                                            {
197                                                int var16 = var7 + par5Random.nextInt(5) / 4;
198    
199                                                if (var16 > 15)
200                                                {
201                                                    var16 = 15;
202                                                }
203    
204                                                par1World.setBlockAndMetadataWithNotify(var10, var12, var11, this.blockID, var16);
205                                            }
206                                        }
207                                    }
208                                }
209                            }
210                        }
211                    }
212                }
213            }
214        }
215    
216        public boolean func_82506_l()
217        {
218            return false;
219        }
220    
221        @Deprecated
222        private void tryToCatchBlockOnFire(World par1World, int par2, int par3, int par4, int par5, Random par6Random, int par7)
223        {
224            tryToCatchBlockOnFire(par1World, par2, par3, par4, par5, par6Random, par7, UP);
225        }
226    
227        private void tryToCatchBlockOnFire(World par1World, int par2, int par3, int par4, int par5, Random par6Random, int par7, ForgeDirection face)
228        {
229            int var8 = 0;
230            Block block = Block.blocksList[par1World.getBlockId(par2, par3, par4)];
231            if (block != null)
232            {
233                var8 = block.getFlammability(par1World, par2, par3, par4, par1World.getBlockMetadata(par2, par3, par4), face);
234            }
235    
236            if (par6Random.nextInt(par5) < var8)
237            {
238                boolean var9 = par1World.getBlockId(par2, par3, par4) == Block.tnt.blockID;
239    
240                if (par6Random.nextInt(par7 + 10) < 5 && !par1World.canLightningStrikeAt(par2, par3, par4))
241                {
242                    int var10 = par7 + par6Random.nextInt(5) / 4;
243    
244                    if (var10 > 15)
245                    {
246                        var10 = 15;
247                    }
248    
249                    par1World.setBlockAndMetadataWithNotify(par2, par3, par4, this.blockID, var10);
250                }
251                else
252                {
253                    par1World.setBlockWithNotify(par2, par3, par4, 0);
254                }
255    
256                if (var9)
257                {
258                    Block.tnt.onBlockDestroyedByPlayer(par1World, par2, par3, par4, 1);
259                }
260            }
261        }
262    
263        /**
264         * Returns true if at least one block next to this one can burn.
265         */
266        private boolean canNeighborBurn(World par1World, int par2, int par3, int par4)
267        {
268            return canBlockCatchFire(par1World, par2 + 1, par3, par4, WEST ) ||
269                   canBlockCatchFire(par1World, par2 - 1, par3, par4, EAST ) ||
270                   canBlockCatchFire(par1World, par2, par3 - 1, par4, UP   ) ||
271                   canBlockCatchFire(par1World, par2, par3 + 1, par4, DOWN ) ||
272                   canBlockCatchFire(par1World, par2, par3, par4 - 1, SOUTH) ||
273                   canBlockCatchFire(par1World, par2, par3, par4 + 1, NORTH);
274        }
275    
276        /**
277         * Gets the highest chance of a neighbor block encouraging this block to catch fire
278         */
279        private int getChanceOfNeighborsEncouragingFire(World par1World, int par2, int par3, int par4)
280        {
281            byte var5 = 0;
282    
283            if (!par1World.isAirBlock(par2, par3, par4))
284            {
285                return 0;
286            }
287            else
288            {
289                int var6 = this.getChanceToEncourageFire(par1World, par2 + 1, par3, par4, var5, WEST);
290                var6 = this.getChanceToEncourageFire(par1World, par2 - 1, par3, par4, var6, EAST);
291                var6 = this.getChanceToEncourageFire(par1World, par2, par3 - 1, par4, var6, UP);
292                var6 = this.getChanceToEncourageFire(par1World, par2, par3 + 1, par4, var6, DOWN);
293                var6 = this.getChanceToEncourageFire(par1World, par2, par3, par4 - 1, var6, SOUTH);
294                var6 = this.getChanceToEncourageFire(par1World, par2, par3, par4 + 1, var6, NORTH);
295                return var6;
296            }
297        }
298    
299        /**
300         * Returns if this block is collidable (only used by Fire). Args: x, y, z
301         */
302        public boolean isCollidable()
303        {
304            return false;
305        }
306    
307        /**
308         * Checks the specified block coordinate to see if it can catch fire.  Args: blockAccess, x, y, z
309         * Deprecated for a side-sensitive version
310         */
311        @Deprecated
312        public boolean canBlockCatchFire(IBlockAccess par1IBlockAccess, int par2, int par3, int par4)
313        {
314            return canBlockCatchFire(par1IBlockAccess, par2, par3, par4, UP);
315        }
316    
317        /**
318         * Retrieves a specified block's chance to encourage their neighbors to burn and if the number is greater than the
319         * current number passed in it will return its number instead of the passed in one.  Args: world, x, y, z,
320         * curChanceToEncourageFire
321         * Deprecated for a side-sensitive version
322         */
323        @Deprecated
324        public int getChanceToEncourageFire(World par1World, int par2, int par3, int par4, int par5)
325        {
326            return getChanceToEncourageFire(par1World, par2, par3, par4, par5, UP);
327        }
328    
329        /**
330         * Checks to see if its valid to put this block at the specified coordinates. Args: world, x, y, z
331         */
332        public boolean canPlaceBlockAt(World par1World, int par2, int par3, int par4)
333        {
334            return par1World.doesBlockHaveSolidTopSurface(par2, par3 - 1, par4) || this.canNeighborBurn(par1World, par2, par3, par4);
335        }
336    
337        /**
338         * Lets the block know when one of its neighbor changes. Doesn't know which neighbor changed (coordinates passed are
339         * their own) Args: x, y, z, neighbor blockID
340         */
341        public void onNeighborBlockChange(World par1World, int par2, int par3, int par4, int par5)
342        {
343            if (!par1World.doesBlockHaveSolidTopSurface(par2, par3 - 1, par4) && !this.canNeighborBurn(par1World, par2, par3, par4))
344            {
345                par1World.setBlockWithNotify(par2, par3, par4, 0);
346            }
347        }
348    
349        /**
350         * Called whenever the block is added into the world. Args: world, x, y, z
351         */
352        public void onBlockAdded(World par1World, int par2, int par3, int par4)
353        {
354            if (par1World.provider.dimensionId > 0 || par1World.getBlockId(par2, par3 - 1, par4) != Block.obsidian.blockID || !Block.portal.tryToCreatePortal(par1World, par2, par3, par4))
355            {
356                if (!par1World.doesBlockHaveSolidTopSurface(par2, par3 - 1, par4) && !this.canNeighborBurn(par1World, par2, par3, par4))
357                {
358                    par1World.setBlockWithNotify(par2, par3, par4, 0);
359                }
360                else
361                {
362                    par1World.scheduleBlockUpdate(par2, par3, par4, this.blockID, this.tickRate() + par1World.rand.nextInt(10));
363                }
364            }
365        }
366    
367        @SideOnly(Side.CLIENT)
368    
369        /**
370         * A randomly called display update to be able to add particles or other items for display
371         */
372        public void randomDisplayTick(World par1World, int par2, int par3, int par4, Random par5Random)
373        {
374            if (par5Random.nextInt(24) == 0)
375            {
376                par1World.playSound((double)((float)par2 + 0.5F), (double)((float)par3 + 0.5F), (double)((float)par4 + 0.5F), "fire.fire", 1.0F + par5Random.nextFloat(), par5Random.nextFloat() * 0.7F + 0.3F);
377            }
378    
379            int var6;
380            float var7;
381            float var8;
382            float var9;
383    
384            if (!par1World.doesBlockHaveSolidTopSurface(par2, par3 - 1, par4) && !Block.fire.canBlockCatchFire(par1World, par2, par3 - 1, par4, UP))
385            {
386                if (Block.fire.canBlockCatchFire(par1World, par2 - 1, par3, par4, EAST))
387                {
388                    for (var6 = 0; var6 < 2; ++var6)
389                    {
390                        var7 = (float)par2 + par5Random.nextFloat() * 0.1F;
391                        var8 = (float)par3 + par5Random.nextFloat();
392                        var9 = (float)par4 + par5Random.nextFloat();
393                        par1World.spawnParticle("largesmoke", (double)var7, (double)var8, (double)var9, 0.0D, 0.0D, 0.0D);
394                    }
395                }
396    
397                if (Block.fire.canBlockCatchFire(par1World, par2 + 1, par3, par4, WEST))
398                {
399                    for (var6 = 0; var6 < 2; ++var6)
400                    {
401                        var7 = (float)(par2 + 1) - par5Random.nextFloat() * 0.1F;
402                        var8 = (float)par3 + par5Random.nextFloat();
403                        var9 = (float)par4 + par5Random.nextFloat();
404                        par1World.spawnParticle("largesmoke", (double)var7, (double)var8, (double)var9, 0.0D, 0.0D, 0.0D);
405                    }
406                }
407    
408                if (Block.fire.canBlockCatchFire(par1World, par2, par3, par4 - 1, SOUTH))
409                {
410                    for (var6 = 0; var6 < 2; ++var6)
411                    {
412                        var7 = (float)par2 + par5Random.nextFloat();
413                        var8 = (float)par3 + par5Random.nextFloat();
414                        var9 = (float)par4 + par5Random.nextFloat() * 0.1F;
415                        par1World.spawnParticle("largesmoke", (double)var7, (double)var8, (double)var9, 0.0D, 0.0D, 0.0D);
416                    }
417                }
418    
419                if (Block.fire.canBlockCatchFire(par1World, par2, par3, par4 + 1, NORTH))
420                {
421                    for (var6 = 0; var6 < 2; ++var6)
422                    {
423                        var7 = (float)par2 + par5Random.nextFloat();
424                        var8 = (float)par3 + par5Random.nextFloat();
425                        var9 = (float)(par4 + 1) - par5Random.nextFloat() * 0.1F;
426                        par1World.spawnParticle("largesmoke", (double)var7, (double)var8, (double)var9, 0.0D, 0.0D, 0.0D);
427                    }
428                }
429    
430                if (Block.fire.canBlockCatchFire(par1World, par2, par3 + 1, par4, DOWN))
431                {
432                    for (var6 = 0; var6 < 2; ++var6)
433                    {
434                        var7 = (float)par2 + par5Random.nextFloat();
435                        var8 = (float)(par3 + 1) - par5Random.nextFloat() * 0.1F;
436                        var9 = (float)par4 + par5Random.nextFloat();
437                        par1World.spawnParticle("largesmoke", (double)var7, (double)var8, (double)var9, 0.0D, 0.0D, 0.0D);
438                    }
439                }
440            }
441            else
442            {
443                for (var6 = 0; var6 < 3; ++var6)
444                {
445                    var7 = (float)par2 + par5Random.nextFloat();
446                    var8 = (float)par3 + par5Random.nextFloat() * 0.5F + 0.5F;
447                    var9 = (float)par4 + par5Random.nextFloat();
448                    par1World.spawnParticle("largesmoke", (double)var7, (double)var8, (double)var9, 0.0D, 0.0D, 0.0D);
449                }
450            }
451        }
452        
453        /**
454         * Side sensitive version that calls the block function.
455         * 
456         * @param world The current world
457         * @param x X Position
458         * @param y Y Position
459         * @param z Z Position
460         * @param face The side the fire is coming from
461         * @return True if the face can catch fire.
462         */
463        public boolean canBlockCatchFire(IBlockAccess world, int x, int y, int z, ForgeDirection face)
464        {
465            Block block = Block.blocksList[world.getBlockId(x, y, z)];
466            if (block != null)
467            {
468                return block.isFlammable(world, x, y, z, world.getBlockMetadata(x, y, z), face);
469            }
470            return false;
471        }
472    
473        /**
474         * Side sensitive version that calls the block function.
475         * 
476         * @param world The current world
477         * @param x X Position
478         * @param y Y Position
479         * @param z Z Position
480         * @param oldChance The previous maximum chance.
481         * @param face The side the fire is coming from
482         * @return The chance of the block catching fire, or oldChance if it is higher
483         */
484        public int getChanceToEncourageFire(World world, int x, int y, int z, int oldChance, ForgeDirection face)
485        {
486            int newChance = 0;
487            Block block = Block.blocksList[world.getBlockId(x, y, z)];
488            if (block != null)
489            {
490                newChance = block.getFireSpreadSpeed(world, x, y, z, world.getBlockMetadata(x, y, z), face);
491            }
492            return (newChance > oldChance ? newChance : oldChance);
493        }
494    }