    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[] field_94439_c;
    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.stairCompactPlanks.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.func_94571_i(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.func_94571_i(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.func_94571_i(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.func_94571_i(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.setBlockAndMetadataWithNotify(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.setBlockAndMetadataWithNotify(par2, par3, par4, this.blockID, k1, 3);
    260            }
    261            else
    262            {
    263                par1World.func_94571_i(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.func_94571_i(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.func_94571_i(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    public void func_94332_a(IconRegister par1IconRegister)
    465    {
    466        this.field_94439_c = new Icon[] {par1IconRegister.func_94245_a("fire_0"), par1IconRegister.func_94245_a("fire_1")};
    467    }
    468
    469    @SideOnly(Side.CLIENT)
    470    public Icon func_94438_c(int par1)
    471    {
    472        return this.field_94439_c[par1];
    473    }
    474
    475    @SideOnly(Side.CLIENT)
    476
    477    /**
    478     * From the specified side and block metadata retrieves the blocks texture. Args: side, metadata
    479     */
    480    public Icon getBlockTextureFromSideAndMetadata(int par1, int par2)
    481    {
    482        return this.field_94439_c[0];
    483    }
    484
    485    /**
    486     * Side sensitive version that calls the block function.
    487     *
    488     * @param world The current world
    489     * @param x X Position
    490     * @param y Y Position
    491     * @param z Z Position
    492     * @param face The side the fire is coming from
    493     * @return True if the face can catch fire.
    494     */
    495    public boolean canBlockCatchFire(IBlockAccess world, int x, int y, int z, ForgeDirection face)
    496    {
    497        Block block = Block.blocksList[world.getBlockId(x, y, z)];
    498        if (block != null)
    499        {
    500            return block.isFlammable(world, x, y, z, world.getBlockMetadata(x, y, z), face);
    501        }
    502        return false;
    503    }
    504
    505    /**
    506     * Side sensitive version that calls the block function.
    507     *
    508     * @param world The current world
    509     * @param x X Position
    510     * @param y Y Position
    511     * @param z Z Position
    512     * @param oldChance The previous maximum chance.
    513     * @param face The side the fire is coming from
    514     * @return The chance of the block catching fire, or oldChance if it is higher
    515     */
    516    public int getChanceToEncourageFire(World world, int x, int y, int z, int oldChance, ForgeDirection face)
    517    {
    518        int newChance = 0;
    519        Block block = Block.blocksList[world.getBlockId(x, y, z)];
    520        if (block != null)
    521        {
    522            newChance = block.getFireSpreadSpeed(world, x, y, z, world.getBlockMetadata(x, y, z), face);
    523        }
    524        return (newChance > oldChance ? newChance : oldChance);
    525    }
    526}