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