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