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            Block base = Block.blocksList[par1World.getBlockId(par2, par3 - 1, par4)];
117            boolean var6 = (base != null && base.isFireSource(par1World, par2, par3 - 1, par4, par1World.getBlockMetadata(par2, par3 - 1, par4), UP));
118    
119            if (!this.canPlaceBlockAt(par1World, par2, par3, par4))
120            {
121                par1World.setBlockWithNotify(par2, par3, par4, 0);
122            }
123    
124            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)))
125            {
126                par1World.setBlockWithNotify(par2, par3, par4, 0);
127            }
128            else
129            {
130                int var7 = par1World.getBlockMetadata(par2, par3, par4);
131    
132                if (var7 < 15)
133                {
134                    par1World.setBlockMetadata(par2, par3, par4, var7 + par5Random.nextInt(3) / 2);
135                }
136    
137                par1World.scheduleBlockUpdate(par2, par3, par4, this.blockID, this.tickRate() + par5Random.nextInt(10));
138    
139                if (!var6 && !this.canNeighborBurn(par1World, par2, par3, par4))
140                {
141                    if (!par1World.doesBlockHaveSolidTopSurface(par2, par3 - 1, par4) || var7 > 3)
142                    {
143                        par1World.setBlockWithNotify(par2, par3, par4, 0);
144                    }
145                }
146                else if (!var6 && !this.canBlockCatchFire(par1World, par2, par3 - 1, par4, UP) && var7 == 15 && par5Random.nextInt(4) == 0)
147                {
148                    par1World.setBlockWithNotify(par2, par3, par4, 0);
149                }
150                else
151                {
152                    boolean var8 = par1World.isBlockHighHumidity(par2, par3, par4);
153                    byte var9 = 0;
154    
155                    if (var8)
156                    {
157                        var9 = -50;
158                    }
159    
160                    this.tryToCatchBlockOnFire(par1World, par2 + 1, par3, par4, 300 + var9, par5Random, var7, WEST );
161                    this.tryToCatchBlockOnFire(par1World, par2 - 1, par3, par4, 300 + var9, par5Random, var7, EAST );
162                    this.tryToCatchBlockOnFire(par1World, par2, par3 - 1, par4, 250 + var9, par5Random, var7, UP   );
163                    this.tryToCatchBlockOnFire(par1World, par2, par3 + 1, par4, 250 + var9, par5Random, var7, DOWN );
164                    this.tryToCatchBlockOnFire(par1World, par2, par3, par4 - 1, 300 + var9, par5Random, var7, SOUTH);
165                    this.tryToCatchBlockOnFire(par1World, par2, par3, par4 + 1, 300 + var9, par5Random, var7, NORTH);
166    
167                    for (int var10 = par2 - 1; var10 <= par2 + 1; ++var10)
168                    {
169                        for (int var11 = par4 - 1; var11 <= par4 + 1; ++var11)
170                        {
171                            for (int var12 = par3 - 1; var12 <= par3 + 4; ++var12)
172                            {
173                                if (var10 != par2 || var12 != par3 || var11 != par4)
174                                {
175                                    int var13 = 100;
176    
177                                    if (var12 > par3 + 1)
178                                    {
179                                        var13 += (var12 - (par3 + 1)) * 100;
180                                    }
181    
182                                    int var14 = this.getChanceOfNeighborsEncouragingFire(par1World, var10, var12, var11);
183    
184                                    if (var14 > 0)
185                                    {
186                                        int var15 = (var14 + 40) / (var7 + 30);
187    
188                                        if (var8)
189                                        {
190                                            var15 /= 2;
191                                        }
192    
193                                        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))
194                                        {
195                                            int var16 = var7 + par5Random.nextInt(5) / 4;
196    
197                                            if (var16 > 15)
198                                            {
199                                                var16 = 15;
200                                            }
201    
202                                            par1World.setBlockAndMetadataWithNotify(var10, var12, var11, this.blockID, var16);
203                                        }
204                                    }
205                                }
206                            }
207                        }
208                    }
209                }
210            }
211        }
212    
213        private void tryToCatchBlockOnFire(World par1World, int par2, int par3, int par4, int par5, Random par6Random, int par7)
214        {
215            tryToCatchBlockOnFire(par1World, par2, par3, par4, par5, par6Random, par7, UP);
216        }
217        private void tryToCatchBlockOnFire(World par1World, int par2, int par3, int par4, int par5, Random par6Random, int par7, ForgeDirection face)
218        {
219            int var8 = 0;
220            Block block = Block.blocksList[par1World.getBlockId(par2, par3, par4)];
221            if (block != null)
222            {
223                var8 = block.getFlammability(par1World, par2, par3, par4, par1World.getBlockMetadata(par2, par3, par4), face);
224            }
225    
226            if (par6Random.nextInt(par5) < var8)
227            {
228                boolean var9 = par1World.getBlockId(par2, par3, par4) == Block.tnt.blockID;
229    
230                if (par6Random.nextInt(par7 + 10) < 5 && !par1World.canLightningStrikeAt(par2, par3, par4))
231                {
232                    int var10 = par7 + par6Random.nextInt(5) / 4;
233    
234                    if (var10 > 15)
235                    {
236                        var10 = 15;
237                    }
238    
239                    par1World.setBlockAndMetadataWithNotify(par2, par3, par4, this.blockID, var10);
240                }
241                else
242                {
243                    par1World.setBlockWithNotify(par2, par3, par4, 0);
244                }
245    
246                if (var9)
247                {
248                    Block.tnt.onBlockDestroyedByPlayer(par1World, par2, par3, par4, 1);
249                }
250            }
251        }
252    
253        /**
254         * Returns true if at least one block next to this one can burn.
255         */
256        private boolean canNeighborBurn(World par1World, int par2, int par3, int par4)
257        {
258            return canBlockCatchFire(par1World, par2 + 1, par3, par4, WEST ) ||
259                   canBlockCatchFire(par1World, par2 - 1, par3, par4, EAST ) ||
260                   canBlockCatchFire(par1World, par2, par3 - 1, par4, UP   ) ||
261                   canBlockCatchFire(par1World, par2, par3 + 1, par4, DOWN ) ||
262                   canBlockCatchFire(par1World, par2, par3, par4 - 1, SOUTH) ||
263                   canBlockCatchFire(par1World, par2, par3, par4 + 1, NORTH);
264        }
265    
266        /**
267         * Gets the highest chance of a neighbor block encouraging this block to catch fire
268         */
269        private int getChanceOfNeighborsEncouragingFire(World par1World, int par2, int par3, int par4)
270        {
271            byte var5 = 0;
272    
273            if (!par1World.isAirBlock(par2, par3, par4))
274            {
275                return 0;
276            }
277            else
278            {
279                int var6 = this.getChanceToEncourageFire(par1World, par2 + 1, par3, par4, var5, WEST);
280                var6 = this.getChanceToEncourageFire(par1World, par2 - 1, par3, par4, var6, EAST);
281                var6 = this.getChanceToEncourageFire(par1World, par2, par3 - 1, par4, var6, UP);
282                var6 = this.getChanceToEncourageFire(par1World, par2, par3 + 1, par4, var6, DOWN);
283                var6 = this.getChanceToEncourageFire(par1World, par2, par3, par4 - 1, var6, SOUTH);
284                var6 = this.getChanceToEncourageFire(par1World, par2, par3, par4 + 1, var6, NORTH);
285                return var6;
286            }
287        }
288    
289        /**
290         * Returns if this block is collidable (only used by Fire). Args: x, y, z
291         */
292        public boolean isCollidable()
293        {
294            return false;
295        }
296    
297        /**
298         * Checks the specified block coordinate to see if it can catch fire.  Args: blockAccess, x, y, z
299         * Deprecated for a side-sensitive version
300         */
301        @Deprecated
302        public boolean canBlockCatchFire(IBlockAccess par1IBlockAccess, int par2, int par3, int par4)
303        {
304            return canBlockCatchFire(par1IBlockAccess, par2, par3, par4, UP);
305        }
306    
307        /**
308         * Retrieves a specified block's chance to encourage their neighbors to burn and if the number is greater than the
309         * current number passed in it will return its number instead of the passed in one.  Args: world, x, y, z,
310         * curChanceToEncourageFire
311         * Deprecated for a side-sensitive version
312         */
313        @Deprecated
314        public int getChanceToEncourageFire(World par1World, int par2, int par3, int par4, int par5)
315        {
316            return getChanceToEncourageFire(par1World, par2, par3, par4, par5, UP);
317        }
318    
319        /**
320         * Checks to see if its valid to put this block at the specified coordinates. Args: world, x, y, z
321         */
322        public boolean canPlaceBlockAt(World par1World, int par2, int par3, int par4)
323        {
324            return par1World.doesBlockHaveSolidTopSurface(par2, par3 - 1, par4) || this.canNeighborBurn(par1World, par2, par3, par4);
325        }
326    
327        /**
328         * Lets the block know when one of its neighbor changes. Doesn't know which neighbor changed (coordinates passed are
329         * their own) Args: x, y, z, neighbor blockID
330         */
331        public void onNeighborBlockChange(World par1World, int par2, int par3, int par4, int par5)
332        {
333            if (!par1World.doesBlockHaveSolidTopSurface(par2, par3 - 1, par4) && !this.canNeighborBurn(par1World, par2, par3, par4))
334            {
335                par1World.setBlockWithNotify(par2, par3, par4, 0);
336            }
337        }
338    
339        /**
340         * Called whenever the block is added into the world. Args: world, x, y, z
341         */
342        public void onBlockAdded(World par1World, int par2, int par3, int par4)
343        {
344            if (par1World.provider.dimensionId > 0 || par1World.getBlockId(par2, par3 - 1, par4) != Block.obsidian.blockID || !Block.portal.tryToCreatePortal(par1World, par2, par3, par4))
345            {
346                if (!par1World.doesBlockHaveSolidTopSurface(par2, par3 - 1, par4) && !this.canNeighborBurn(par1World, par2, par3, par4))
347                {
348                    par1World.setBlockWithNotify(par2, par3, par4, 0);
349                }
350                else
351                {
352                    par1World.scheduleBlockUpdate(par2, par3, par4, this.blockID, this.tickRate() + par1World.rand.nextInt(10));
353                }
354            }
355        }
356    
357        @SideOnly(Side.CLIENT)
358    
359        /**
360         * A randomly called display update to be able to add particles or other items for display
361         */
362        public void randomDisplayTick(World par1World, int par2, int par3, int par4, Random par5Random)
363        {
364            if (par5Random.nextInt(24) == 0)
365            {
366                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);
367            }
368    
369            int var6;
370            float var7;
371            float var8;
372            float var9;
373    
374            if (!par1World.doesBlockHaveSolidTopSurface(par2, par3 - 1, par4) && !Block.fire.canBlockCatchFire(par1World, par2, par3 - 1, par4, UP))
375            {
376                if (Block.fire.canBlockCatchFire(par1World, par2 - 1, par3, par4, EAST))
377                {
378                    for (var6 = 0; var6 < 2; ++var6)
379                    {
380                        var7 = (float)par2 + par5Random.nextFloat() * 0.1F;
381                        var8 = (float)par3 + par5Random.nextFloat();
382                        var9 = (float)par4 + par5Random.nextFloat();
383                        par1World.spawnParticle("largesmoke", (double)var7, (double)var8, (double)var9, 0.0D, 0.0D, 0.0D);
384                    }
385                }
386    
387                if (Block.fire.canBlockCatchFire(par1World, par2 + 1, par3, par4, WEST))
388                {
389                    for (var6 = 0; var6 < 2; ++var6)
390                    {
391                        var7 = (float)(par2 + 1) - par5Random.nextFloat() * 0.1F;
392                        var8 = (float)par3 + par5Random.nextFloat();
393                        var9 = (float)par4 + par5Random.nextFloat();
394                        par1World.spawnParticle("largesmoke", (double)var7, (double)var8, (double)var9, 0.0D, 0.0D, 0.0D);
395                    }
396                }
397    
398                if (Block.fire.canBlockCatchFire(par1World, par2, par3, par4 - 1, SOUTH))
399                {
400                    for (var6 = 0; var6 < 2; ++var6)
401                    {
402                        var7 = (float)par2 + par5Random.nextFloat();
403                        var8 = (float)par3 + par5Random.nextFloat();
404                        var9 = (float)par4 + par5Random.nextFloat() * 0.1F;
405                        par1World.spawnParticle("largesmoke", (double)var7, (double)var8, (double)var9, 0.0D, 0.0D, 0.0D);
406                    }
407                }
408    
409                if (Block.fire.canBlockCatchFire(par1World, par2, par3, par4 + 1, NORTH))
410                {
411                    for (var6 = 0; var6 < 2; ++var6)
412                    {
413                        var7 = (float)par2 + par5Random.nextFloat();
414                        var8 = (float)par3 + par5Random.nextFloat();
415                        var9 = (float)(par4 + 1) - par5Random.nextFloat() * 0.1F;
416                        par1World.spawnParticle("largesmoke", (double)var7, (double)var8, (double)var9, 0.0D, 0.0D, 0.0D);
417                    }
418                }
419    
420                if (Block.fire.canBlockCatchFire(par1World, par2, par3 + 1, par4, DOWN))
421                {
422                    for (var6 = 0; var6 < 2; ++var6)
423                    {
424                        var7 = (float)par2 + par5Random.nextFloat();
425                        var8 = (float)(par3 + 1) - par5Random.nextFloat() * 0.1F;
426                        var9 = (float)par4 + par5Random.nextFloat();
427                        par1World.spawnParticle("largesmoke", (double)var7, (double)var8, (double)var9, 0.0D, 0.0D, 0.0D);
428                    }
429                }
430            }
431            else
432            {
433                for (var6 = 0; var6 < 3; ++var6)
434                {
435                    var7 = (float)par2 + par5Random.nextFloat();
436                    var8 = (float)par3 + par5Random.nextFloat() * 0.5F + 0.5F;
437                    var9 = (float)par4 + par5Random.nextFloat();
438                    par1World.spawnParticle("largesmoke", (double)var7, (double)var8, (double)var9, 0.0D, 0.0D, 0.0D);
439                }
440            }
441        }
442        
443        /**
444         * Side sensitive version that calls the block function.
445         * 
446         * @param world The current world
447         * @param x X Position
448         * @param y Y Position
449         * @param z Z Position
450         * @param face The side the fire is coming from
451         * @return True if the face can catch fire.
452         */
453        public boolean canBlockCatchFire(IBlockAccess world, int x, int y, int z, ForgeDirection face)
454        {
455            Block block = Block.blocksList[world.getBlockId(x, y, z)];
456            if (block != null)
457            {
458                return block.isFlammable(world, x, y, z, world.getBlockMetadata(x, y, z), face);
459            }
460            return false;
461        }
462    
463        /**
464         * Side sensitive version that calls the block function.
465         * 
466         * @param world The current world
467         * @param x X Position
468         * @param y Y Position
469         * @param z Z Position
470         * @param oldChance The previous maximum chance.
471         * @param face The side the fire is coming from
472         * @return The chance of the block catching fire, or oldChance if it is higher
473         */
474        public int getChanceToEncourageFire(World world, int x, int y, int z, int oldChance, ForgeDirection face)
475        {
476            int newChance = 0;
477            Block block = Block.blocksList[world.getBlockId(x, y, z)];
478            if (block != null)
479            {
480                newChance = block.getFireSpreadSpeed(world, x, y, z, world.getBlockMetadata(x, y, z), face);
481            }
482            return (newChance > oldChance ? newChance : oldChance);
483        }
484    }