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 }