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.util.AxisAlignedBB;
008import net.minecraft.world.IBlockAccess;
009import net.minecraft.world.World;
010import net.minecraft.world.WorldProviderEnd;
011
012import net.minecraftforge.common.ForgeDirection;
013import static net.minecraftforge.common.ForgeDirection.*;
014
015public 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}