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