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