001    package net.minecraft.entity.boss;
002    
003    import cpw.mods.fml.relauncher.Side;
004    import cpw.mods.fml.relauncher.SideOnly;
005    import java.util.List;
006    import net.minecraft.block.Block;
007    import net.minecraft.command.IEntitySelector;
008    import net.minecraft.entity.Entity;
009    import net.minecraft.entity.EntityLiving;
010    import net.minecraft.entity.EnumCreatureAttribute;
011    import net.minecraft.entity.IRangedAttackMob;
012    import net.minecraft.entity.ai.EntityAIArrowAttack;
013    import net.minecraft.entity.ai.EntityAIHurtByTarget;
014    import net.minecraft.entity.ai.EntityAILookIdle;
015    import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
016    import net.minecraft.entity.ai.EntityAISwimming;
017    import net.minecraft.entity.ai.EntityAIWander;
018    import net.minecraft.entity.ai.EntityAIWatchClosest;
019    import net.minecraft.entity.monster.EntityMob;
020    import net.minecraft.entity.player.EntityPlayer;
021    import net.minecraft.entity.projectile.EntityArrow;
022    import net.minecraft.entity.projectile.EntityWitherSkull;
023    import net.minecraft.item.Item;
024    import net.minecraft.nbt.NBTTagCompound;
025    import net.minecraft.potion.PotionEffect;
026    import net.minecraft.util.DamageSource;
027    import net.minecraft.util.MathHelper;
028    import net.minecraft.world.World;
029    
030    public 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 var1 = this.func_82212_n();
129            return var1 > 0 && (var1 > 80 || var1 / 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 var4;
145            double var6;
146            double var8;
147    
148            if (!this.worldObj.isRemote && this.getWatchedTargetId(0) > 0)
149            {
150                Entity var1 = this.worldObj.getEntityByID(this.getWatchedTargetId(0));
151    
152                if (var1 != null)
153                {
154                    if (this.posY < var1.posY || !this.isArmored() && this.posY < var1.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 var2 = var1.posX - this.posX;
165                    var4 = var1.posZ - this.posZ;
166                    var6 = var2 * var2 + var4 * var4;
167    
168                    if (var6 > 9.0D)
169                    {
170                        var8 = (double)MathHelper.sqrt_double(var6);
171                        this.motionX += (var2 / var8 * 0.5D - this.motionX) * 0.6000000238418579D;
172                        this.motionZ += (var4 / var8 * 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 var20;
184    
185            for (var20 = 0; var20 < 2; ++var20)
186            {
187                this.field_82218_g[var20] = this.field_82221_e[var20];
188                this.field_82217_f[var20] = this.field_82220_d[var20];
189            }
190    
191            int var21;
192    
193            for (var20 = 0; var20 < 2; ++var20)
194            {
195                var21 = this.getWatchedTargetId(var20 + 1);
196                Entity var3 = null;
197    
198                if (var21 > 0)
199                {
200                    var3 = this.worldObj.getEntityByID(var21);
201                }
202    
203                if (var3 != null)
204                {
205                    var4 = this.func_82214_u(var20 + 1);
206                    var6 = this.func_82208_v(var20 + 1);
207                    var8 = this.func_82213_w(var20 + 1);
208                    double var10 = var3.posX - var4;
209                    double var12 = var3.posY + (double)var3.getEyeHeight() - var6;
210                    double var14 = var3.posZ - var8;
211                    double var16 = (double)MathHelper.sqrt_double(var10 * var10 + var14 * var14);
212                    float var18 = (float)(Math.atan2(var14, var10) * 180.0D / Math.PI) - 90.0F;
213                    float var19 = (float)(-(Math.atan2(var12, var16) * 180.0D / Math.PI));
214                    this.field_82220_d[var20] = this.func_82204_b(this.field_82220_d[var20], var19, 40.0F);
215                    this.field_82221_e[var20] = this.func_82204_b(this.field_82221_e[var20], var18, 10.0F);
216                }
217                else
218                {
219                    this.field_82221_e[var20] = this.func_82204_b(this.field_82221_e[var20], this.renderYawOffset, 10.0F);
220                }
221            }
222    
223            boolean var22 = this.isArmored();
224    
225            for (var21 = 0; var21 < 3; ++var21)
226            {
227                double var23 = this.func_82214_u(var21);
228                double var5 = this.func_82208_v(var21);
229                double var7 = this.func_82213_w(var21);
230                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);
231    
232                if (var22 && this.worldObj.rand.nextInt(4) == 0)
233                {
234                    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);
235                }
236            }
237    
238            if (this.func_82212_n() > 0)
239            {
240                for (var21 = 0; var21 < 3; ++var21)
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 var1;
250    
251            if (this.func_82212_n() > 0)
252            {
253                var1 = this.func_82212_n() - 1;
254    
255                if (var1 <= 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(var1);
262    
263                if (this.ticksExisted % 10 == 0)
264                {
265                    this.heal(10);
266                }
267            }
268            else
269            {
270                super.updateAITasks();
271                int var13;
272    
273                for (var1 = 1; var1 < 3; ++var1)
274                {
275                    if (this.ticksExisted >= this.field_82223_h[var1 - 1])
276                    {
277                        this.field_82223_h[var1 - 1] = this.ticksExisted + 10 + this.rand.nextInt(10);
278    
279                        if (this.worldObj.difficultySetting >= 2)
280                        {
281                            int var10001 = var1 - 1;
282                            int var10003 = this.field_82224_i[var1 - 1];
283                            this.field_82224_i[var10001] = this.field_82224_i[var1 - 1] + 1;
284    
285                            if (var10003 > 15)
286                            {
287                                float var2 = 10.0F;
288                                float var3 = 5.0F;
289                                double var4 = MathHelper.getRandomDoubleInRange(this.rand, this.posX - (double)var2, this.posX + (double)var2);
290                                double var6 = MathHelper.getRandomDoubleInRange(this.rand, this.posY - (double)var3, this.posY + (double)var3);
291                                double var8 = MathHelper.getRandomDoubleInRange(this.rand, this.posZ - (double)var2, this.posZ + (double)var2);
292                                this.func_82209_a(var1 + 1, var4, var6, var8, true);
293                                this.field_82224_i[var1 - 1] = 0;
294                            }
295                        }
296    
297                        var13 = this.getWatchedTargetId(var1);
298    
299                        if (var13 > 0)
300                        {
301                            Entity var15 = this.worldObj.getEntityByID(var13);
302    
303                            if (var15 != null && var15.isEntityAlive() && this.getDistanceSqToEntity(var15) <= 900.0D && this.canEntityBeSeen(var15))
304                            {
305                                this.func_82216_a(var1 + 1, (EntityLiving)var15);
306                                this.field_82223_h[var1 - 1] = this.ticksExisted + 40 + this.rand.nextInt(20);
307                                this.field_82224_i[var1 - 1] = 0;
308                            }
309                            else
310                            {
311                                this.func_82211_c(var1, 0);
312                            }
313                        }
314                        else
315                        {
316                            List var14 = this.worldObj.selectEntitiesWithinAABB(EntityLiving.class, this.boundingBox.expand(20.0D, 8.0D, 20.0D), attackEntitySelector);
317    
318                            for (int var17 = 0; var17 < 10 && !var14.isEmpty(); ++var17)
319                            {
320                                EntityLiving var5 = (EntityLiving)var14.get(this.rand.nextInt(var14.size()));
321    
322                                if (var5 != this && var5.isEntityAlive() && this.canEntityBeSeen(var5))
323                                {
324                                    if (var5 instanceof EntityPlayer)
325                                    {
326                                        if (!((EntityPlayer)var5).capabilities.disableDamage)
327                                        {
328                                            this.func_82211_c(var1, var5.entityId);
329                                        }
330                                    }
331                                    else
332                                    {
333                                        this.func_82211_c(var1, var5.entityId);
334                                    }
335    
336                                    break;
337                                }
338    
339                                var14.remove(var5);
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                        var1 = MathHelper.floor_double(this.posY);
361                        var13 = MathHelper.floor_double(this.posX);
362                        int var16 = MathHelper.floor_double(this.posZ);
363                        boolean var19 = false;
364    
365                        for (int var18 = -1; var18 <= 1; ++var18)
366                        {
367                            for (int var20 = -1; var20 <= 1; ++var20)
368                            {
369                                for (int var7 = 0; var7 <= 3; ++var7)
370                                {
371                                    int var21 = var13 + var18;
372                                    int var9 = var1 + var7;
373                                    int var10 = var16 + var20;
374                                    int var11 = this.worldObj.getBlockId(var21, var9, var10);
375    
376                                    if (var11 > 0 && var11 != Block.bedrock.blockID && var11 != Block.endPortal.blockID && var11 != Block.endPortalFrame.blockID)
377                                    {
378                                        int var12 = this.worldObj.getBlockMetadata(var21, var9, var10);
379                                        this.worldObj.playAuxSFX(2001, var21, var9, var10, var11 + (var12 << 12));
380                                        Block.blocksList[var11].dropBlockAsItem(this.worldObj, var21, var9, var10, var12, 0);
381                                        this.worldObj.setBlockWithNotify(var21, var9, var10, 0);
382                                        var19 = true;
383                                    }
384                                }
385                            }
386                        }
387    
388                        if (var19)
389                        {
390                            this.worldObj.playAuxSFXAtEntity((EntityPlayer)null, 1012, (int)this.posX, (int)this.posY, (int)this.posZ, 0);
391                        }
392                    }
393                }
394    
395                if (this.ticksExisted % 20 == 0)
396                {
397                    this.heal(1);
398                }
399            }
400        }
401    
402        public void func_82206_m()
403        {
404            this.func_82215_s(220);
405            this.setEntityHealth(this.getMaxHealth() / 3);
406        }
407    
408        /**
409         * Sets the Entity inside a web block.
410         */
411        public void setInWeb() {}
412    
413        /**
414         * Returns the current armor value as determined by a call to InventoryPlayer.getTotalArmorValue
415         */
416        public int getTotalArmorValue()
417        {
418            return 4;
419        }
420    
421        private double func_82214_u(int par1)
422        {
423            if (par1 <= 0)
424            {
425                return this.posX;
426            }
427            else
428            {
429                float var2 = (this.renderYawOffset + (float)(180 * (par1 - 1))) / 180.0F * (float)Math.PI;
430                float var3 = MathHelper.cos(var2);
431                return this.posX + (double)var3 * 1.3D;
432            }
433        }
434    
435        private double func_82208_v(int par1)
436        {
437            return par1 <= 0 ? this.posY + 3.0D : this.posY + 2.2D;
438        }
439    
440        private double func_82213_w(int par1)
441        {
442            if (par1 <= 0)
443            {
444                return this.posZ;
445            }
446            else
447            {
448                float var2 = (this.renderYawOffset + (float)(180 * (par1 - 1))) / 180.0F * (float)Math.PI;
449                float var3 = MathHelper.sin(var2);
450                return this.posZ + (double)var3 * 1.3D;
451            }
452        }
453    
454        private float func_82204_b(float par1, float par2, float par3)
455        {
456            float var4 = MathHelper.wrapAngleTo180_float(par2 - par1);
457    
458            if (var4 > par3)
459            {
460                var4 = par3;
461            }
462    
463            if (var4 < -par3)
464            {
465                var4 = -par3;
466            }
467    
468            return par1 + var4;
469        }
470    
471        private void func_82216_a(int par1, EntityLiving par2EntityLiving)
472        {
473            this.func_82209_a(par1, par2EntityLiving.posX, par2EntityLiving.posY + (double)par2EntityLiving.getEyeHeight() * 0.5D, par2EntityLiving.posZ, par1 == 0 && this.rand.nextFloat() < 0.001F);
474        }
475    
476        private void func_82209_a(int par1, double par2, double par4, double par6, boolean par8)
477        {
478            this.worldObj.playAuxSFXAtEntity((EntityPlayer)null, 1014, (int)this.posX, (int)this.posY, (int)this.posZ, 0);
479            double var9 = this.func_82214_u(par1);
480            double var11 = this.func_82208_v(par1);
481            double var13 = this.func_82213_w(par1);
482            double var15 = par2 - var9;
483            double var17 = par4 - var11;
484            double var19 = par6 - var13;
485            EntityWitherSkull var21 = new EntityWitherSkull(this.worldObj, this, var15, var17, var19);
486    
487            if (par8)
488            {
489                var21.setInvulnerable(true);
490            }
491    
492            var21.posY = var11;
493            var21.posX = var9;
494            var21.posZ = var13;
495            this.worldObj.spawnEntityInWorld(var21);
496        }
497    
498        /**
499         * Attack the specified entity using a ranged attack.
500         */
501        public void attackEntityWithRangedAttack(EntityLiving par1EntityLiving)
502        {
503            this.func_82216_a(0, par1EntityLiving);
504        }
505    
506        /**
507         * Called when the entity is attacked.
508         */
509        public boolean attackEntityFrom(DamageSource par1DamageSource, int par2)
510        {
511            if (this.isEntityInvulnerable())
512            {
513                return false;
514            }
515            else if (par1DamageSource == DamageSource.drown)
516            {
517                return false;
518            }
519            else if (this.func_82212_n() > 0)
520            {
521                return false;
522            }
523            else
524            {
525                Entity var3;
526    
527                if (this.isArmored())
528                {
529                    var3 = par1DamageSource.getSourceOfDamage();
530    
531                    if (var3 instanceof EntityArrow)
532                    {
533                        return false;
534                    }
535                }
536    
537                var3 = par1DamageSource.getEntity();
538    
539                if (var3 != null && !(var3 instanceof EntityPlayer) && var3 instanceof EntityLiving && ((EntityLiving)var3).getCreatureAttribute() == this.getCreatureAttribute())
540                {
541                    return false;
542                }
543                else
544                {
545                    if (this.field_82222_j <= 0)
546                    {
547                        this.field_82222_j = 20;
548                    }
549    
550                    for (int var4 = 0; var4 < this.field_82224_i.length; ++var4)
551                    {
552                        this.field_82224_i[var4] += 3;
553                    }
554    
555                    return super.attackEntityFrom(par1DamageSource, par2);
556                }
557            }
558        }
559    
560        /**
561         * Drop 0-2 items of this living's type. @param par1 - Whether this entity has recently been hit by a player. @param
562         * par2 - Level of Looting used to kill this mob.
563         */
564        protected void dropFewItems(boolean par1, int par2)
565        {
566            this.dropItem(Item.netherStar.itemID, 1);
567        }
568    
569        /**
570         * Makes the entity despawn if requirements are reached
571         */
572        protected void despawnEntity()
573        {
574            this.entityAge = 0;
575        }
576    
577        @SideOnly(Side.CLIENT)
578        public int getBrightnessForRender(float par1)
579        {
580            return 15728880;
581        }
582    
583        /**
584         * Returns true if other Entities should be prevented from moving through this Entity.
585         */
586        public boolean canBeCollidedWith()
587        {
588            return !this.isDead;
589        }
590    
591        /**
592         * Returns the health points of the dragon.
593         */
594        public int getDragonHealth()
595        {
596            return this.dataWatcher.getWatchableObjectInt(16);
597        }
598    
599        /**
600         * Called when the mob is falling. Calculates and applies fall damage.
601         */
602        protected void fall(float par1) {}
603    
604        /**
605         * adds a PotionEffect to the entity
606         */
607        public void addPotionEffect(PotionEffect par1PotionEffect) {}
608    
609        /**
610         * Returns true if the newer Entity AI code should be run
611         */
612        protected boolean isAIEnabled()
613        {
614            return true;
615        }
616    
617        public int getMaxHealth()
618        {
619            return 300;
620        }
621    
622        @SideOnly(Side.CLIENT)
623        public float func_82207_a(int par1)
624        {
625            return this.field_82221_e[par1];
626        }
627    
628        @SideOnly(Side.CLIENT)
629        public float func_82210_r(int par1)
630        {
631            return this.field_82220_d[par1];
632        }
633    
634        public int func_82212_n()
635        {
636            return this.dataWatcher.getWatchableObjectInt(20);
637        }
638    
639        public void func_82215_s(int par1)
640        {
641            this.dataWatcher.updateObject(20, Integer.valueOf(par1));
642        }
643    
644        /**
645         * Returns the target entity ID if present, or -1 if not @param par1 The target offset, should be from 0-2
646         */
647        public int getWatchedTargetId(int par1)
648        {
649            return this.dataWatcher.getWatchableObjectInt(17 + par1);
650        }
651    
652        public void func_82211_c(int par1, int par2)
653        {
654            this.dataWatcher.updateObject(17 + par1, Integer.valueOf(par2));
655        }
656    
657        /**
658         * Returns whether the wither is armored with its boss armor or not by checking whether its health is below half of
659         * its maximum.
660         */
661        public boolean isArmored()
662        {
663            return this.getDragonHealth() <= this.getMaxHealth() / 2;
664        }
665    
666        /**
667         * Get this Entity's EnumCreatureAttribute
668         */
669        public EnumCreatureAttribute getCreatureAttribute()
670        {
671            return EnumCreatureAttribute.UNDEAD;
672        }
673    }