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}