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 }