    001package net.minecraft.entity.boss;
    002
    003import cpw.mods.fml.relauncher.Side;
    004import cpw.mods.fml.relauncher.SideOnly;
    005import java.util.List;
    006import net.minecraft.block.Block;
    007import net.minecraft.command.IEntitySelector;
    008import net.minecraft.entity.Entity;
    009import net.minecraft.entity.EntityLiving;
    010import net.minecraft.entity.EnumCreatureAttribute;
    011import net.minecraft.entity.IRangedAttackMob;
    012import net.minecraft.entity.ai.EntityAIArrowAttack;
    013import net.minecraft.entity.ai.EntityAIHurtByTarget;
    014import net.minecraft.entity.ai.EntityAILookIdle;
    015import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
    016import net.minecraft.entity.ai.EntityAISwimming;
    017import net.minecraft.entity.ai.EntityAIWander;
    018import net.minecraft.entity.ai.EntityAIWatchClosest;
    019import net.minecraft.entity.monster.EntityMob;
    020import net.minecraft.entity.player.EntityPlayer;
    021import net.minecraft.entity.projectile.EntityArrow;
    022import net.minecraft.entity.projectile.EntityWitherSkull;
    023import net.minecraft.item.Item;
    024import net.minecraft.nbt.NBTTagCompound;
    025import net.minecraft.potion.PotionEffect;
    026import net.minecraft.util.DamageSource;
    027import net.minecraft.util.MathHelper;
    028import net.minecraft.world.World;
    029
    030public class EntityWither extends EntityMob implements IBossDisplayData, IRangedAttackMob
    031{
    032    private float[] field_82220_d = new float[2];
    033    private float[] field_82221_e = new float[2];
    034    private float[] field_82217_f = new float[2];
    035    private float[] field_82218_g = new float[2];
    036    private int[] field_82223_h = new int[2];
    037    private int[] field_82224_i = new int[2];
    038    private int field_82222_j;
    039
    040    /** Selector used to determine the entities a wither boss should attack. */
    041    private static final IEntitySelector attackEntitySelector = new EntityWitherAttackFilter();
    042
    043    public EntityWither(World par1World)
    044    {
    045        super(par1World);
    046        this.setEntityHealth(this.getMaxHealth());
    047        this.texture = "/mob/wither.png";
    048        this.setSize(0.9F, 4.0F);
    049        this.isImmuneToFire = true;
    050        this.moveSpeed = 0.6F;
    051        this.getNavigator().setCanSwim(true);
    052        this.tasks.addTask(0, new EntityAISwimming(this));
    053        this.tasks.addTask(2, new EntityAIArrowAttack(this, this.moveSpeed, 40, 20.0F));
    054        this.tasks.addTask(5, new EntityAIWander(this, this.moveSpeed));
    055        this.tasks.addTask(6, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F));
    056        this.tasks.addTask(7, new EntityAILookIdle(this));
    057        this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false));
    058        this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLiving.class, 30.0F, 0, false, false, attackEntitySelector));
    059        this.experienceValue = 50;
    060    }
    061
    062    protected void entityInit()
    063    {
    064        super.entityInit();
    065        this.dataWatcher.addObject(16, new Integer(100));
    066        this.dataWatcher.addObject(17, new Integer(0));
    067        this.dataWatcher.addObject(18, new Integer(0));
    068        this.dataWatcher.addObject(19, new Integer(0));
    069        this.dataWatcher.addObject(20, new Integer(0));
    070    }
    071
    072    /**
    073     * (abstract) Protected helper method to write subclass entity data to NBT.
    074     */
    075    public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound)
    076    {
    077        super.writeEntityToNBT(par1NBTTagCompound);
    078        par1NBTTagCompound.setInteger("Invul", this.func_82212_n());
    079    }
    080
    081    /**
    082     * (abstract) Protected helper method to read subclass entity data from NBT.
    083     */
    084    public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound)
    085    {
    086        super.readEntityFromNBT(par1NBTTagCompound);
    087        this.func_82215_s(par1NBTTagCompound.getInteger("Invul"));
    088        this.dataWatcher.updateObject(16, Integer.valueOf(this.health));
    089    }
    090
    091    @SideOnly(Side.CLIENT)
    092    public float getShadowSize()
    093    {
    094        return this.height / 8.0F;
    095    }
    096
    097    /**
    098     * Returns the sound this mob makes while it's alive.
    099     */
    100    protected String getLivingSound()
    101    {
    102        return "mob.wither.idle";
    103    }
    104
    105    /**
    106     * Returns the sound this mob makes when it is hurt.
    107     */
    108    protected String getHurtSound()
    109    {
    110        return "mob.wither.hurt";
    111    }
    112
    113    /**
    114     * Returns the sound this mob makes on death.
    115     */
    116    protected String getDeathSound()
    117    {
    118        return "mob.wither.death";
    119    }
    120
    121    @SideOnly(Side.CLIENT)
    122
    123    /**
    124     * Returns the texture's file path as a String.
    125     */
    126    public String getTexture()
    127    {
    128        int i = this.func_82212_n();
    129        return i > 0 && (i > 80 || i / 5 % 2 != 1) ? "/mob/wither_invul.png" : "/mob/wither.png";
    130    }
    131
    132    /**
    133     * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons
    134     * use this to react to sunlight and start to burn.
    135     */
    136    public void onLivingUpdate()
    137    {
    138        if (!this.worldObj.isRemote)
    139        {
    140            this.dataWatcher.updateObject(16, Integer.valueOf(this.health));
    141        }
    142
    143        this.motionY *= 0.6000000238418579D;
    144        double d0;
    145        double d1;
    146        double d2;
    147
    148        if (!this.worldObj.isRemote && this.getWatchedTargetId(0) > 0)
    149        {
    150            Entity entity = this.worldObj.getEntityByID(this.getWatchedTargetId(0));
    151
    152            if (entity != null)
    153            {
    154                if (this.posY < entity.posY || !this.isArmored() && this.posY < entity.posY + 5.0D)
    155                {
    156                    if (this.motionY < 0.0D)
    157                    {
    158                        this.motionY = 0.0D;
    159                    }
    160
    161                    this.motionY += (0.5D - this.motionY) * 0.6000000238418579D;
    162                }
    163
    164                double d3 = entity.posX - this.posX;
    165                d0 = entity.posZ - this.posZ;
    166                d1 = d3 * d3 + d0 * d0;
    167
    168                if (d1 > 9.0D)
    169                {
    170                    d2 = (double)MathHelper.sqrt_double(d1);
    171                    this.motionX += (d3 / d2 * 0.5D - this.motionX) * 0.6000000238418579D;
    172                    this.motionZ += (d0 / d2 * 0.5D - this.motionZ) * 0.6000000238418579D;
    173                }
    174            }
    175        }
    176
    177        if (this.motionX * this.motionX + this.motionZ * this.motionZ > 0.05000000074505806D)
    178        {
    179            this.rotationYaw = (float)Math.atan2(this.motionZ, this.motionX) * (180F / (float)Math.PI) - 90.0F;
    180        }
    181
    182        super.onLivingUpdate();
    183        int i;
    184
    185        for (i = 0; i < 2; ++i)
    186        {
    187            this.field_82218_g[i] = this.field_82221_e[i];
    188            this.field_82217_f[i] = this.field_82220_d[i];
    189        }
    190
    191        int j;
    192
    193        for (i = 0; i < 2; ++i)
    194        {
    195            j = this.getWatchedTargetId(i + 1);
    196            Entity entity1 = null;
    197
    198            if (j > 0)
    199            {
    200                entity1 = this.worldObj.getEntityByID(j);
    201            }
    202
    203            if (entity1 != null)
    204            {
    205                d0 = this.func_82214_u(i + 1);
    206                d1 = this.func_82208_v(i + 1);
    207                d2 = this.func_82213_w(i + 1);
    208                double d4 = entity1.posX - d0;
    209                double d5 = entity1.posY + (double)entity1.getEyeHeight() - d1;
    210                double d6 = entity1.posZ - d2;
    211                double d7 = (double)MathHelper.sqrt_double(d4 * d4 + d6 * d6);
    212                float f = (float)(Math.atan2(d6, d4) * 180.0D / Math.PI) - 90.0F;
    213                float f1 = (float)(-(Math.atan2(d5, d7) * 180.0D / Math.PI));
    214                this.field_82220_d[i] = this.func_82204_b(this.field_82220_d[i], f1, 40.0F);
    215                this.field_82221_e[i] = this.func_82204_b(this.field_82221_e[i], f, 10.0F);
    216            }
    217            else
    218            {
    219                this.field_82221_e[i] = this.func_82204_b(this.field_82221_e[i], this.renderYawOffset, 10.0F);
    220            }
    221        }
    222
    223        boolean flag = this.isArmored();
    224
    225        for (j = 0; j < 3; ++j)
    226        {
    227            double d8 = this.func_82214_u(j);
    228            double d9 = this.func_82208_v(j);
    229            double d10 = this.func_82213_w(j);
    230            this.worldObj.spawnParticle("smoke", d8 + this.rand.nextGaussian() * 0.30000001192092896D, d9 + this.rand.nextGaussian() * 0.30000001192092896D, d10 + this.rand.nextGaussian() * 0.30000001192092896D, 0.0D, 0.0D, 0.0D);
    231
    232            if (flag && this.worldObj.rand.nextInt(4) == 0)
    233            {
    234                this.worldObj.spawnParticle("mobSpell", d8 + this.rand.nextGaussian() * 0.30000001192092896D, d9 + this.rand.nextGaussian() * 0.30000001192092896D, d10 + this.rand.nextGaussian() * 0.30000001192092896D, 0.699999988079071D, 0.699999988079071D, 0.5D);
    235            }
    236        }
    237
    238        if (this.func_82212_n() > 0)
    239        {
    240            for (j = 0; j < 3; ++j)
    241            {
    242                this.worldObj.spawnParticle("mobSpell", this.posX + this.rand.nextGaussian() * 1.0D, this.posY + (double)(this.rand.nextFloat() * 3.3F), this.posZ + this.rand.nextGaussian() * 1.0D, 0.699999988079071D, 0.699999988079071D, 0.8999999761581421D);
    243            }
    244        }
    245    }
    246
    247    protected void updateAITasks()
    248    {
    249        int i;
    250
    251        if (this.func_82212_n() > 0)
    252        {
    253            i = this.func_82212_n() - 1;
    254
    255            if (i <= 0)
    256            {
    257                this.worldObj.newExplosion(this, this.posX, this.posY + (double)this.getEyeHeight(), this.posZ, 7.0F, false, this.worldObj.getGameRules().getGameRuleBooleanValue("mobGriefing"));
    258                this.worldObj.func_82739_e(1013, (int)this.posX, (int)this.posY, (int)this.posZ, 0);
    259            }
    260
    261            this.func_82215_s(i);
    262
    263            if (this.ticksExisted % 10 == 0)
    264            {
    265                this.heal(10);
    266            }
    267        }
    268        else
    269        {
    270            super.updateAITasks();
    271            int j;
    272
    273            for (i = 1; i < 3; ++i)
    274            {
    275                if (this.ticksExisted >= this.field_82223_h[i - 1])
    276                {
    277                    this.field_82223_h[i - 1] = this.ticksExisted + 10 + this.rand.nextInt(10);
    278
    279                    if (this.worldObj.difficultySetting >= 2)
    280                    {
    281                        int k = i - 1;
    282                        int l = this.field_82224_i[i - 1];
    283                        this.field_82224_i[k] = this.field_82224_i[i - 1] + 1;
    284
    285                        if (l > 15)
    286                        {
    287                            float f = 10.0F;
    288                            float f1 = 5.0F;
    289                            double d0 = MathHelper.getRandomDoubleInRange(this.rand, this.posX - (double)f, this.posX + (double)f);
    290                            double d1 = MathHelper.getRandomDoubleInRange(this.rand, this.posY - (double)f1, this.posY + (double)f1);
    291                            double d2 = MathHelper.getRandomDoubleInRange(this.rand, this.posZ - (double)f, this.posZ + (double)f);
    292                            this.func_82209_a(i + 1, d0, d1, d2, true);
    293                            this.field_82224_i[i - 1] = 0;
    294                        }
    295                    }
    296
    297                    j = this.getWatchedTargetId(i);
    298
    299                    if (j > 0)
    300                    {
    301                        Entity entity = this.worldObj.getEntityByID(j);
    302
    303                        if (entity != null && entity.isEntityAlive() && this.getDistanceSqToEntity(entity) <= 900.0D && this.canEntityBeSeen(entity))
    304                        {
    305                            this.func_82216_a(i + 1, (EntityLiving)entity);
    306                            this.field_82223_h[i - 1] = this.ticksExisted + 40 + this.rand.nextInt(20);
    307                            this.field_82224_i[i - 1] = 0;
    308                        }
    309                        else
    310                        {
    311                            this.func_82211_c(i, 0);
    312                        }
    313                    }
    314                    else
    315                    {
    316                        List list = this.worldObj.selectEntitiesWithinAABB(EntityLiving.class, this.boundingBox.expand(20.0D, 8.0D, 20.0D), attackEntitySelector);
    317
    318                        for (int i1 = 0; i1 < 10 && !list.isEmpty(); ++i1)
    319                        {
    320                            EntityLiving entityliving = (EntityLiving)list.get(this.rand.nextInt(list.size()));
    321
    322                            if (entityliving != this && entityliving.isEntityAlive() && this.canEntityBeSeen(entityliving))
    323                            {
    324                                if (entityliving instanceof EntityPlayer)
    325                                {
    326                                    if (!((EntityPlayer)entityliving).capabilities.disableDamage)
    327                                    {
    328                                        this.func_82211_c(i, entityliving.entityId);
    329                                    }
    330                                }
    331                                else
    332                                {
    333                                    this.func_82211_c(i, entityliving.entityId);
    334                                }
    335
    336                                break;
    337                            }
    338
    339                            list.remove(entityliving);
    340                        }
    341                    }
    342                }
    343            }
    344
    345            if (this.getAttackTarget() != null)
    346            {
    347                this.func_82211_c(0, this.getAttackTarget().entityId);
    348            }
    349            else
    350            {
    351                this.func_82211_c(0, 0);
    352            }
    353
    354            if (this.field_82222_j > 0)
    355            {
    356                --this.field_82222_j;
    357
    358                if (this.field_82222_j == 0 && this.worldObj.getGameRules().getGameRuleBooleanValue("mobGriefing"))
    359                {
    360                    i = MathHelper.floor_double(this.posY);
    361                    j = MathHelper.floor_double(this.posX);
    362                    int j1 = MathHelper.floor_double(this.posZ);
    363                    boolean flag = false;
    364
    365                    for (int k1 = -1; k1 <= 1; ++k1)
    366                    {
    367                        for (int l1 = -1; l1 <= 1; ++l1)
    368                        {
    369                            for (int i2 = 0; i2 <= 3; ++i2)
    370                            {
    371                                int j2 = j + k1;
    372                                int k2 = i + i2;
    373                                int l2 = j1 + l1;
    374                                int i3 = this.worldObj.getBlockId(j2, k2, l2);
    375
    376                                if (i3 > 0 && i3 != Block.bedrock.blockID && i3 != Block.endPortal.blockID && i3 != Block.endPortalFrame.blockID)
    377                                {
    378                                    flag = this.worldObj.func_94578_a(j2, k2, l2, true) || flag;
    379                                }
    380                            }
    381                        }
    382                    }
    383
    384                    if (flag)
    385                    {
    386                        this.worldObj.playAuxSFXAtEntity((EntityPlayer)null, 1012, (int)this.posX, (int)this.posY, (int)this.posZ, 0);
    387                    }
    388                }
    389            }
    390
    391            if (this.ticksExisted % 20 == 0)
    392            {
    393                this.heal(1);
    394            }
    395        }
    396    }
    397
    398    public void func_82206_m()
    399    {
    400        this.func_82215_s(220);
    401        this.setEntityHealth(this.getMaxHealth() / 3);
    402    }
    403
    404    /**
    405     * Sets the Entity inside a web block.
    406     */
    407    public void setInWeb() {}
    408
    409    /**
    410     * Returns the current armor value as determined by a call to InventoryPlayer.getTotalArmorValue
    411     */
    412    public int getTotalArmorValue()
    413    {
    414        return 4;
    415    }
    416
    417    private double func_82214_u(int par1)
    418    {
    419        if (par1 <= 0)
    420        {
    421            return this.posX;
    422        }
    423        else
    424        {
    425            float f = (this.renderYawOffset + (float)(180 * (par1 - 1))) / 180.0F * (float)Math.PI;
    426            float f1 = MathHelper.cos(f);
    427            return this.posX + (double)f1 * 1.3D;
    428        }
    429    }
    430
    431    private double func_82208_v(int par1)
    432    {
    433        return par1 <= 0 ? this.posY + 3.0D : this.posY + 2.2D;
    434    }
    435
    436    private double func_82213_w(int par1)
    437    {
    438        if (par1 <= 0)
    439        {
    440            return this.posZ;
    441        }
    442        else
    443        {
    444            float f = (this.renderYawOffset + (float)(180 * (par1 - 1))) / 180.0F * (float)Math.PI;
    445            float f1 = MathHelper.sin(f);
    446            return this.posZ + (double)f1 * 1.3D;
    447        }
    448    }
    449
    450    private float func_82204_b(float par1, float par2, float par3)
    451    {
    452        float f3 = MathHelper.wrapAngleTo180_float(par2 - par1);
    453
    454        if (f3 > par3)
    455        {
    456            f3 = par3;
    457        }
    458
    459        if (f3 < -par3)
    460        {
    461            f3 = -par3;
    462        }
    463
    464        return par1 + f3;
    465    }
    466
    467    private void func_82216_a(int par1, EntityLiving par2EntityLiving)
    468    {
    469        this.func_82209_a(par1, par2EntityLiving.posX, par2EntityLiving.posY + (double)par2EntityLiving.getEyeHeight() * 0.5D, par2EntityLiving.posZ, par1 == 0 && this.rand.nextFloat() < 0.001F);
    470    }
    471
    472    private void func_82209_a(int par1, double par2, double par4, double par6, boolean par8)
    473    {
    474        this.worldObj.playAuxSFXAtEntity((EntityPlayer)null, 1014, (int)this.posX, (int)this.posY, (int)this.posZ, 0);
    475        double d3 = this.func_82214_u(par1);
    476        double d4 = this.func_82208_v(par1);
    477        double d5 = this.func_82213_w(par1);
    478        double d6 = par2 - d3;
    479        double d7 = par4 - d4;
    480        double d8 = par6 - d5;
    481        EntityWitherSkull entitywitherskull = new EntityWitherSkull(this.worldObj, this, d6, d7, d8);
    482
    483        if (par8)
    484        {
    485            entitywitherskull.setInvulnerable(true);
    486        }
    487
    488        entitywitherskull.posY = d4;
    489        entitywitherskull.posX = d3;
    490        entitywitherskull.posZ = d5;
    491        this.worldObj.spawnEntityInWorld(entitywitherskull);
    492    }
    493
    494    /**
    495     * Attack the specified entity using a ranged attack.
    496     */
    497    public void attackEntityWithRangedAttack(EntityLiving par1EntityLiving, float par2)
    498    {
    499        this.func_82216_a(0, par1EntityLiving);
    500    }
    501
    502    /**
    503     * Called when the entity is attacked.
    504     */
    505    public boolean attackEntityFrom(DamageSource par1DamageSource, int par2)
    506    {
    507        if (this.isEntityInvulnerable())
    508        {
    509            return false;
    510        }
    511        else if (par1DamageSource == DamageSource.drown)
    512        {
    513            return false;
    514        }
    515        else if (this.func_82212_n() > 0)
    516        {
    517            return false;
    518        }
    519        else
    520        {
    521            Entity entity;
    522
    523            if (this.isArmored())
    524            {
    525                entity = par1DamageSource.getSourceOfDamage();
    526
    527                if (entity instanceof EntityArrow)
    528                {
    529                    return false;
    530                }
    531            }
    532
    533            entity = par1DamageSource.getEntity();
    534
    535            if (entity != null && !(entity instanceof EntityPlayer) && entity instanceof EntityLiving && ((EntityLiving)entity).getCreatureAttribute() == this.getCreatureAttribute())
    536            {
    537                return false;
    538            }
    539            else
    540            {
    541                if (this.field_82222_j <= 0)
    542                {
    543                    this.field_82222_j = 20;
    544                }
    545
    546                for (int j = 0; j < this.field_82224_i.length; ++j)
    547                {
    548                    this.field_82224_i[j] += 3;
    549                }
    550
    551                return super.attackEntityFrom(par1DamageSource, par2);
    552            }
    553        }
    554    }
    555
    556    /**
    557     * Drop 0-2 items of this living's type. @param par1 - Whether this entity has recently been hit by a player. @param
    558     * par2 - Level of Looting used to kill this mob.
    559     */
    560    protected void dropFewItems(boolean par1, int par2)
    561    {
    562        this.dropItem(Item.netherStar.itemID, 1);
    563    }
    564
    565    /**
    566     * Makes the entity despawn if requirements are reached
    567     */
    568    protected void despawnEntity()
    569    {
    570        this.entityAge = 0;
    571    }
    572
    573    @SideOnly(Side.CLIENT)
    574    public int getBrightnessForRender(float par1)
    575    {
    576        return 15728880;
    577    }
    578
    579    /**
    580     * Returns true if other Entities should be prevented from moving through this Entity.
    581     */
    582    public boolean canBeCollidedWith()
    583    {
    584        return !this.isDead;
    585    }
    586
    587    /**
    588     * Returns the health points of the dragon.
    589     */
    590    public int getDragonHealth()
    591    {
    592        return this.dataWatcher.getWatchableObjectInt(16);
    593    }
    594
    595    /**
    596     * Called when the mob is falling. Calculates and applies fall damage.
    597     */
    598    protected void fall(float par1) {}
    599
    600    /**
    601     * adds a PotionEffect to the entity
    602     */
    603    public void addPotionEffect(PotionEffect par1PotionEffect) {}
    604
    605    /**
    606     * Returns true if the newer Entity AI code should be run
    607     */
    608    protected boolean isAIEnabled()
    609    {
    610        return true;
    611    }
    612
    613    public int getMaxHealth()
    614    {
    615        return 300;
    616    }
    617
    618    @SideOnly(Side.CLIENT)
    619    public float func_82207_a(int par1)
    620    {
    621        return this.field_82221_e[par1];
    622    }
    623
    624    @SideOnly(Side.CLIENT)
    625    public float func_82210_r(int par1)
    626    {
    627        return this.field_82220_d[par1];
    628    }
    629
    630    public int func_82212_n()
    631    {
    632        return this.dataWatcher.getWatchableObjectInt(20);
    633    }
    634
    635    public void func_82215_s(int par1)
    636    {
    637        this.dataWatcher.updateObject(20, Integer.valueOf(par1));
    638    }
    639
    640    /**
    641     * Returns the target entity ID if present, or -1 if not @param par1 The target offset, should be from 0-2
    642     */
    643    public int getWatchedTargetId(int par1)
    644    {
    645        return this.dataWatcher.getWatchableObjectInt(17 + par1);
    646    }
    647
    648    public void func_82211_c(int par1, int par2)
    649    {
    650        this.dataWatcher.updateObject(17 + par1, Integer.valueOf(par2));
    651    }
    652
    653    /**
    654     * Returns whether the wither is armored with its boss armor or not by checking whether its health is below half of
    655     * its maximum.
    656     */
    657    public boolean isArmored()
    658    {
    659        return this.getDragonHealth() <= this.getMaxHealth() / 2;
    660    }
    661
    662    /**
    663     * Get this Entity's EnumCreatureAttribute
    664     */
    665    public EnumCreatureAttribute getCreatureAttribute()
    666    {
    667        return EnumCreatureAttribute.UNDEAD;
    668    }
    669
    670    /**
    671     * Called when a player mounts an entity. e.g. mounts a pig, mounts a boat.
    672     */
    673    public void mountEntity(Entity par1Entity)
    674    {
    675        this.ridingEntity = null;
    676    }
    677}