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 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}