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)
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 (par1DamageSource == DamageSource.drown)
489            {
490                return false;
491            }
492            else if (this.func_82212_n() > 0)
493            {
494                return false;
495            }
496            else
497            {
498                Entity var3;
499    
500                if (this.isArmored())
501                {
502                    var3 = par1DamageSource.getSourceOfDamage();
503    
504                    if (var3 instanceof EntityArrow)
505                    {
506                        return false;
507                    }
508                }
509    
510                var3 = par1DamageSource.getEntity();
511    
512                if (var3 != null && !(var3 instanceof EntityPlayer) && var3 instanceof EntityLiving && ((EntityLiving)var3).getCreatureAttribute() == this.getCreatureAttribute())
513                {
514                    return false;
515                }
516                else
517                {
518                    if (this.field_82222_j <= 0)
519                    {
520                        this.field_82222_j = 20;
521                    }
522    
523                    for (int var4 = 0; var4 < this.field_82224_i.length; ++var4)
524                    {
525                        this.field_82224_i[var4] += 3;
526                    }
527    
528                    return super.attackEntityFrom(par1DamageSource, par2);
529                }
530            }
531        }
532    
533        /**
534         * Drop 0-2 items of this living's type
535         */
536        protected void dropFewItems(boolean par1, int par2)
537        {
538            this.dropItem(Item.netherStar.shiftedIndex, 1);
539        }
540    
541        /**
542         * Makes the entity despawn if requirements are reached
543         */
544        protected void despawnEntity()
545        {
546            this.entityAge = 0;
547        }
548    
549        @SideOnly(Side.CLIENT)
550        public int getBrightnessForRender(float par1)
551        {
552            return 15728880;
553        }
554    
555        /**
556         * Returns true if other Entities should be prevented from moving through this Entity.
557         */
558        public boolean canBeCollidedWith()
559        {
560            return !this.isDead;
561        }
562    
563        /**
564         * Returns the health points of the dragon.
565         */
566        public int getDragonHealth()
567        {
568            return this.dataWatcher.getWatchableObjectInt(16);
569        }
570    
571        /**
572         * Called when the mob is falling. Calculates and applies fall damage.
573         */
574        protected void fall(float par1) {}
575    
576        /**
577         * adds a PotionEffect to the entity
578         */
579        public void addPotionEffect(PotionEffect par1PotionEffect) {}
580    
581        /**
582         * Returns true if the newer Entity AI code should be run
583         */
584        protected boolean isAIEnabled()
585        {
586            return true;
587        }
588    
589        public int getMaxHealth()
590        {
591            return 300;
592        }
593    
594        @SideOnly(Side.CLIENT)
595        public float func_82207_a(int par1)
596        {
597            return this.field_82221_e[par1];
598        }
599    
600        @SideOnly(Side.CLIENT)
601        public float func_82210_r(int par1)
602        {
603            return this.field_82220_d[par1];
604        }
605    
606        public int func_82212_n()
607        {
608            return this.dataWatcher.getWatchableObjectInt(20);
609        }
610    
611        public void func_82215_s(int par1)
612        {
613            this.dataWatcher.updateObject(20, Integer.valueOf(par1));
614        }
615    
616        /**
617         * Returns the target entity ID if present, or -1 if not @param par1 The target offset, should be from 0-2
618         */
619        public int getWatchedTargetId(int par1)
620        {
621            return this.dataWatcher.getWatchableObjectInt(17 + par1);
622        }
623    
624        public void func_82211_c(int par1, int par2)
625        {
626            this.dataWatcher.updateObject(17 + par1, Integer.valueOf(par2));
627        }
628    
629        /**
630         * Returns whether the wither is armored with its boss armor or not by checking whether its health is below half of
631         * its maximum.
632         */
633        public boolean isArmored()
634        {
635            return this.getDragonHealth() <= this.getMaxHealth() / 2;
636        }
637    
638        /**
639         * Get this Entity's EnumCreatureAttribute
640         */
641        public EnumCreatureAttribute getCreatureAttribute()
642        {
643            return EnumCreatureAttribute.UNDEAD;
644        }
645    }