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