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.Collection; 006 import java.util.HashMap; 007 import java.util.Iterator; 008 import java.util.List; 009 import java.util.Random; 010 import net.minecraftforge.common.ForgeHooks; 011 import net.minecraftforge.common.MinecraftForge; 012 import net.minecraftforge.event.entity.living.*; 013 import static net.minecraftforge.event.entity.living.LivingEvent.*; 014 015 public abstract class EntityLiving extends Entity 016 { 017 public int maxHurtResistantTime = 20; 018 public float field_70769_ao; 019 public float field_70770_ap; 020 public float renderYawOffset = 0.0F; 021 public float prevRenderYawOffset = 0.0F; 022 023 /** Entity head rotation yaw */ 024 public float rotationYawHead = 0.0F; 025 026 /** Entity head rotation yaw at previous tick */ 027 public float prevRotationYawHead = 0.0F; 028 protected float field_70768_au; 029 protected float field_70766_av; 030 protected float field_70764_aw; 031 protected float field_70763_ax; 032 protected boolean field_70753_ay = true; 033 034 /** the path for the texture of this entityLiving */ 035 protected String texture = "/mob/char.png"; 036 protected boolean field_70740_aA = true; 037 protected float field_70741_aB = 0.0F; 038 039 /** 040 * a string holding the type of entity it is currently only implemented in entityPlayer(as 'humanoid') 041 */ 042 protected String entityType = null; 043 protected float field_70743_aD = 1.0F; 044 045 /** The score value of the Mob, the amount of points the mob is worth. */ 046 protected int scoreValue = 0; 047 protected float field_70745_aF = 0.0F; 048 049 /** 050 * A factor used to determine how far this entity will move each tick if it is walking on land. Adjusted by speed, 051 * and slipperiness of the current block. 052 */ 053 public float landMovementFactor = 0.1F; 054 055 /** 056 * A factor used to determine how far this entity will move each tick if it is jumping or falling. 057 */ 058 public float jumpMovementFactor = 0.02F; 059 public float prevSwingProgress; 060 public float swingProgress; 061 protected int health = this.getMaxHealth(); 062 public int prevHealth; 063 064 /** 065 * in each step in the damage calculations, this is set to the 'carryover' that would result if someone was damaged 066 * .25 hearts (for example), and added to the damage in the next step 067 */ 068 public int carryoverDamage; 069 070 /** Number of ticks since this EntityLiving last produced its sound */ 071 private int livingSoundTime; 072 073 /** 074 * The amount of time remaining this entity should act 'hurt'. (Visual appearance of red tint) 075 */ 076 public int hurtTime; 077 078 /** What the hurt time was max set to last. */ 079 public int maxHurtTime; 080 081 /** The yaw at which this entity was last attacked from. */ 082 public float attackedAtYaw = 0.0F; 083 084 /** 085 * The amount of time remaining this entity should act 'dead', i.e. have a corpse in the world. 086 */ 087 public int deathTime = 0; 088 public int attackTime = 0; 089 public float prevCameraPitch; 090 public float cameraPitch; 091 092 /** 093 * This gets set on entity death, but never used. Looks like a duplicate of isDead 094 */ 095 protected boolean dead = false; 096 097 /** The experience points the Entity gives. */ 098 protected int experienceValue; 099 public int field_70731_aW = -1; 100 public float field_70730_aX = (float)(Math.random() * 0.8999999761581421D + 0.10000000149011612D); 101 public float prevLegYaw; 102 public float legYaw; 103 public float field_70754_ba; 104 105 /** The most recent player that has attacked this entity */ 106 protected EntityPlayer attackingPlayer = null; 107 108 /** 109 * Set to 60 when hit by the player or the player's wolf, then decrements. Used to determine whether the entity 110 * should drop items on death. 111 */ 112 protected int recentlyHit = 0; 113 114 /** is only being set, has no uses as of MC 1.1 */ 115 private EntityLiving entityLivingToAttack = null; 116 private int revengeTimer = 0; 117 private EntityLiving lastAttackingEntity = null; 118 119 /** 120 * Set to 60 when hit by the player or the player's wolf, then decrements. Used to determine whether the entity 121 * should drop items on death. 122 */ 123 public int arrowHitTempCounter = 0; 124 public int arrowHitTimer = 0; 125 protected HashMap activePotionsMap = new HashMap(); 126 127 /** Whether the DataWatcher needs to be updated with the active potions */ 128 private boolean potionsNeedUpdate = true; 129 private int field_70748_f; 130 private EntityLookHelper lookHelper; 131 private EntityMoveHelper moveHelper; 132 133 /** Entity jumping helper */ 134 private EntityJumpHelper jumpHelper; 135 private EntityBodyHelper bodyHelper; 136 private PathNavigate navigator; 137 public final EntityAITasks tasks; 138 protected final EntityAITasks targetTasks; 139 140 /** The active target the Task system uses for tracking */ 141 private EntityLiving attackTarget; 142 private EntitySenses senses; 143 private float AIMoveSpeed; 144 private ChunkCoordinates homePosition = new ChunkCoordinates(0, 0, 0); 145 146 /** If -1 there is no maximum distance */ 147 private float maximumHomeDistance = -1.0F; 148 149 /** 150 * The number of updates over which the new position and rotation are to be applied to the entity. 151 */ 152 protected int newPosRotationIncrements; 153 154 /** The new X position to be applied to the entity. */ 155 protected double newPosX; 156 157 /** The new Y position to be applied to the entity. */ 158 protected double newPosY; 159 160 /** The new Z position to be applied to the entity. */ 161 protected double newPosZ; 162 163 /** The new yaw rotation to be applied to the entity. */ 164 protected double newRotationYaw; 165 166 /** The new yaw rotation to be applied to the entity. */ 167 protected double newRotationPitch; 168 float field_70706_bo = 0.0F; 169 170 /** Amount of damage taken in last hit, in half-hearts */ 171 protected int lastDamage = 0; 172 173 /** Holds the living entity age, used to control the despawn. */ 174 protected int entityAge = 0; 175 protected float moveStrafing; 176 protected float moveForward; 177 protected float randomYawVelocity; 178 179 /** used to check whether entity is jumping. */ 180 public boolean isJumping = false; 181 protected float defaultPitch = 0.0F; 182 protected float moveSpeed = 0.7F; 183 184 /** Number of ticks since last jump */ 185 private int jumpTicks = 0; 186 187 /** This entity's current target. */ 188 private Entity currentTarget; 189 190 /** How long to keep a specific target entity */ 191 protected int numTicksToChaseTarget = 0; 192 193 public EntityLiving(World par1World) 194 { 195 super(par1World); 196 this.preventEntitySpawning = true; 197 this.tasks = new EntityAITasks(par1World != null && par1World.theProfiler != null ? par1World.theProfiler : null); 198 this.targetTasks = new EntityAITasks(par1World != null && par1World.theProfiler != null ? par1World.theProfiler : null); 199 this.lookHelper = new EntityLookHelper(this); 200 this.moveHelper = new EntityMoveHelper(this); 201 this.jumpHelper = new EntityJumpHelper(this); 202 this.bodyHelper = new EntityBodyHelper(this); 203 this.navigator = new PathNavigate(this, par1World, 16.0F); 204 this.senses = new EntitySenses(this); 205 this.field_70770_ap = (float)(Math.random() + 1.0D) * 0.01F; 206 this.setPosition(this.posX, this.posY, this.posZ); 207 this.field_70769_ao = (float)Math.random() * 12398.0F; 208 this.rotationYaw = (float)(Math.random() * Math.PI * 2.0D); 209 this.rotationYawHead = this.rotationYaw; 210 this.stepHeight = 0.5F; 211 } 212 213 public EntityLookHelper getLookHelper() 214 { 215 return this.lookHelper; 216 } 217 218 public EntityMoveHelper getMoveHelper() 219 { 220 return this.moveHelper; 221 } 222 223 public EntityJumpHelper getJumpHelper() 224 { 225 return this.jumpHelper; 226 } 227 228 public PathNavigate getNavigator() 229 { 230 return this.navigator; 231 } 232 233 /** 234 * returns the EntitySenses Object for the EntityLiving 235 */ 236 public EntitySenses getEntitySenses() 237 { 238 return this.senses; 239 } 240 241 public Random getRNG() 242 { 243 return this.rand; 244 } 245 246 public EntityLiving getAITarget() 247 { 248 return this.entityLivingToAttack; 249 } 250 251 public EntityLiving getLastAttackingEntity() 252 { 253 return this.lastAttackingEntity; 254 } 255 256 public void setLastAttackingEntity(Entity par1Entity) 257 { 258 if (par1Entity instanceof EntityLiving) 259 { 260 this.lastAttackingEntity = (EntityLiving)par1Entity; 261 } 262 } 263 264 public int getAge() 265 { 266 return this.entityAge; 267 } 268 269 public float func_70079_am() 270 { 271 return this.rotationYawHead; 272 } 273 274 @SideOnly(Side.CLIENT) 275 276 /** 277 * Sets the head's yaw rotation of the entity. 278 */ 279 public void setHeadRotationYaw(float par1) 280 { 281 this.rotationYawHead = par1; 282 } 283 284 /** 285 * the movespeed used for the new AI system 286 */ 287 public float getAIMoveSpeed() 288 { 289 return this.AIMoveSpeed; 290 } 291 292 /** 293 * set the movespeed used for the new AI system 294 */ 295 public void setAIMoveSpeed(float par1) 296 { 297 this.AIMoveSpeed = par1; 298 this.setMoveForward(par1); 299 } 300 301 public boolean attackEntityAsMob(Entity par1Entity) 302 { 303 this.setLastAttackingEntity(par1Entity); 304 return false; 305 } 306 307 /** 308 * Gets the active target the Task system uses for tracking 309 */ 310 public EntityLiving getAttackTarget() 311 { 312 return this.attackTarget; 313 } 314 315 /** 316 * Sets the active target the Task system uses for tracking 317 */ 318 public void setAttackTarget(EntityLiving par1EntityLiving) 319 { 320 this.attackTarget = par1EntityLiving; 321 ForgeHooks.onLivingSetAttackTarget(this, par1EntityLiving); 322 } 323 324 public boolean isExplosiveMob(Class par1Class) 325 { 326 return EntityCreeper.class != par1Class && EntityGhast.class != par1Class; 327 } 328 329 /** 330 * This function applies the benefits of growing back wool and faster growing up to the acting entity. (This 331 * function is used in the AIEatGrass) 332 */ 333 public void eatGrassBonus() {} 334 335 /** 336 * Returns true if entity is within home distance from current position 337 */ 338 public boolean isWithinHomeDistanceCurrentPosition() 339 { 340 return this.isWithinHomeDistance(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.posY), MathHelper.floor_double(this.posZ)); 341 } 342 343 public boolean isWithinHomeDistance(int par1, int par2, int par3) 344 { 345 return this.maximumHomeDistance == -1.0F ? true : this.homePosition.getDistanceSquared(par1, par2, par3) < this.maximumHomeDistance * this.maximumHomeDistance; 346 } 347 348 public void setHomeArea(int par1, int par2, int par3, int par4) 349 { 350 this.homePosition.set(par1, par2, par3); 351 this.maximumHomeDistance = (float)par4; 352 } 353 354 public ChunkCoordinates getHomePosition() 355 { 356 return this.homePosition; 357 } 358 359 public float getMaximumHomeDistance() 360 { 361 return this.maximumHomeDistance; 362 } 363 364 public void detachHome() 365 { 366 this.maximumHomeDistance = -1.0F; 367 } 368 369 public boolean hasHome() 370 { 371 return this.maximumHomeDistance != -1.0F; 372 } 373 374 public void setRevengeTarget(EntityLiving par1EntityLiving) 375 { 376 this.entityLivingToAttack = par1EntityLiving; 377 this.revengeTimer = this.entityLivingToAttack != null ? 60 : 0; 378 ForgeHooks.onLivingSetAttackTarget(this, par1EntityLiving); 379 } 380 381 protected void entityInit() 382 { 383 this.dataWatcher.addObject(8, Integer.valueOf(this.field_70748_f)); 384 } 385 386 /** 387 * returns true if the entity provided in the argument can be seen. (Raytrace) 388 */ 389 public boolean canEntityBeSeen(Entity par1Entity) 390 { 391 return this.worldObj.rayTraceBlocks(Vec3.getVec3Pool().getVecFromPool(this.posX, this.posY + (double)this.getEyeHeight(), this.posZ), Vec3.getVec3Pool().getVecFromPool(par1Entity.posX, par1Entity.posY + (double)par1Entity.getEyeHeight(), par1Entity.posZ)) == null; 392 } 393 394 @SideOnly(Side.CLIENT) 395 396 /** 397 * Returns the texture's file path as a String. 398 */ 399 public String getTexture() 400 { 401 return this.texture; 402 } 403 404 /** 405 * Returns true if other Entities should be prevented from moving through this Entity. 406 */ 407 public boolean canBeCollidedWith() 408 { 409 return !this.isDead; 410 } 411 412 /** 413 * Returns true if this entity should push and be pushed by other entities when colliding. 414 */ 415 public boolean canBePushed() 416 { 417 return !this.isDead; 418 } 419 420 public float getEyeHeight() 421 { 422 return this.height * 0.85F; 423 } 424 425 /** 426 * Get number of ticks, at least during which the living entity will be silent. 427 */ 428 public int getTalkInterval() 429 { 430 return 80; 431 } 432 433 /** 434 * Plays living's sound at its position 435 */ 436 public void playLivingSound() 437 { 438 String var1 = this.getLivingSound(); 439 440 if (var1 != null) 441 { 442 this.worldObj.playSoundAtEntity(this, var1, this.getSoundVolume(), this.getSoundPitch()); 443 } 444 } 445 446 /** 447 * Gets called every tick from main Entity class 448 */ 449 public void onEntityUpdate() 450 { 451 this.prevSwingProgress = this.swingProgress; 452 super.onEntityUpdate(); 453 this.worldObj.theProfiler.startSection("mobBaseTick"); 454 455 if (this.isEntityAlive() && this.rand.nextInt(1000) < this.livingSoundTime++) 456 { 457 this.livingSoundTime = -this.getTalkInterval(); 458 this.playLivingSound(); 459 } 460 461 if (this.isEntityAlive() && this.isEntityInsideOpaqueBlock()) 462 { 463 this.attackEntityFrom(DamageSource.inWall, 1); 464 } 465 466 if (this.isImmuneToFire() || this.worldObj.isRemote) 467 { 468 this.extinguish(); 469 } 470 471 if (this.isEntityAlive() && this.isInsideOfMaterial(Material.water) && !this.canBreatheUnderwater() && !this.activePotionsMap.containsKey(Integer.valueOf(Potion.waterBreathing.id))) 472 { 473 this.setAir(this.decreaseAirSupply(this.getAir())); 474 475 if (this.getAir() == -20) 476 { 477 this.setAir(0); 478 479 for (int var1 = 0; var1 < 8; ++var1) 480 { 481 float var2 = this.rand.nextFloat() - this.rand.nextFloat(); 482 float var3 = this.rand.nextFloat() - this.rand.nextFloat(); 483 float var4 = this.rand.nextFloat() - this.rand.nextFloat(); 484 this.worldObj.spawnParticle("bubble", this.posX + (double)var2, this.posY + (double)var3, this.posZ + (double)var4, this.motionX, this.motionY, this.motionZ); 485 } 486 487 this.attackEntityFrom(DamageSource.drown, 2); 488 } 489 490 this.extinguish(); 491 } 492 else 493 { 494 this.setAir(300); 495 } 496 497 this.prevCameraPitch = this.cameraPitch; 498 499 if (this.attackTime > 0) 500 { 501 --this.attackTime; 502 } 503 504 if (this.hurtTime > 0) 505 { 506 --this.hurtTime; 507 } 508 509 if (this.hurtResistantTime > 0) 510 { 511 --this.hurtResistantTime; 512 } 513 514 if (this.health <= 0) 515 { 516 this.onDeathUpdate(); 517 } 518 519 if (this.recentlyHit > 0) 520 { 521 --this.recentlyHit; 522 } 523 else 524 { 525 this.attackingPlayer = null; 526 } 527 528 if (this.lastAttackingEntity != null && !this.lastAttackingEntity.isEntityAlive()) 529 { 530 this.lastAttackingEntity = null; 531 } 532 533 if (this.entityLivingToAttack != null) 534 { 535 if (!this.entityLivingToAttack.isEntityAlive()) 536 { 537 this.setRevengeTarget((EntityLiving)null); 538 } 539 else if (this.revengeTimer > 0) 540 { 541 --this.revengeTimer; 542 } 543 else 544 { 545 this.setRevengeTarget((EntityLiving)null); 546 } 547 } 548 549 this.updatePotionEffects(); 550 this.field_70763_ax = this.field_70764_aw; 551 this.prevRenderYawOffset = this.renderYawOffset; 552 this.prevRotationYawHead = this.rotationYawHead; 553 this.prevRotationYaw = this.rotationYaw; 554 this.prevRotationPitch = this.rotationPitch; 555 this.worldObj.theProfiler.endSection(); 556 } 557 558 /** 559 * handles entity death timer, experience orb and particle creation 560 */ 561 protected void onDeathUpdate() 562 { 563 ++this.deathTime; 564 565 if (this.deathTime == 20) 566 { 567 int var1; 568 569 if (!this.worldObj.isRemote && (this.recentlyHit > 0 || this.isPlayer()) && !this.isChild()) 570 { 571 var1 = this.getExperiencePoints(this.attackingPlayer); 572 573 while (var1 > 0) 574 { 575 int var2 = EntityXPOrb.getXPSplit(var1); 576 var1 -= var2; 577 this.worldObj.spawnEntityInWorld(new EntityXPOrb(this.worldObj, this.posX, this.posY, this.posZ, var2)); 578 } 579 } 580 581 this.setDead(); 582 583 for (var1 = 0; var1 < 20; ++var1) 584 { 585 double var8 = this.rand.nextGaussian() * 0.02D; 586 double var4 = this.rand.nextGaussian() * 0.02D; 587 double var6 = this.rand.nextGaussian() * 0.02D; 588 this.worldObj.spawnParticle("explode", this.posX + (double)(this.rand.nextFloat() * this.width * 2.0F) - (double)this.width, this.posY + (double)(this.rand.nextFloat() * this.height), this.posZ + (double)(this.rand.nextFloat() * this.width * 2.0F) - (double)this.width, var8, var4, var6); 589 } 590 } 591 } 592 593 /** 594 * Decrements the entity's air supply when underwater 595 */ 596 protected int decreaseAirSupply(int par1) 597 { 598 return par1 - 1; 599 } 600 601 /** 602 * Get the experience points the entity currently has. 603 */ 604 protected int getExperiencePoints(EntityPlayer par1EntityPlayer) 605 { 606 return this.experienceValue; 607 } 608 609 /** 610 * Only use is to identify if class is an instance of player for experience dropping 611 */ 612 protected boolean isPlayer() 613 { 614 return false; 615 } 616 617 /** 618 * Spawns an explosion particle around the Entity's location 619 */ 620 public void spawnExplosionParticle() 621 { 622 for (int var1 = 0; var1 < 20; ++var1) 623 { 624 double var2 = this.rand.nextGaussian() * 0.02D; 625 double var4 = this.rand.nextGaussian() * 0.02D; 626 double var6 = this.rand.nextGaussian() * 0.02D; 627 double var8 = 10.0D; 628 this.worldObj.spawnParticle("explode", this.posX + (double)(this.rand.nextFloat() * this.width * 2.0F) - (double)this.width - var2 * var8, this.posY + (double)(this.rand.nextFloat() * this.height) - var4 * var8, this.posZ + (double)(this.rand.nextFloat() * this.width * 2.0F) - (double)this.width - var6 * var8, var2, var4, var6); 629 } 630 } 631 632 /** 633 * Handles updating while being ridden by an entity 634 */ 635 public void updateRidden() 636 { 637 super.updateRidden(); 638 this.field_70768_au = this.field_70766_av; 639 this.field_70766_av = 0.0F; 640 this.fallDistance = 0.0F; 641 } 642 643 @SideOnly(Side.CLIENT) 644 645 /** 646 * Sets the position and rotation. Only difference from the other one is no bounding on the rotation. Args: posX, 647 * posY, posZ, yaw, pitch 648 */ 649 public void setPositionAndRotation2(double par1, double par3, double par5, float par7, float par8, int par9) 650 { 651 this.yOffset = 0.0F; 652 this.newPosX = par1; 653 this.newPosY = par3; 654 this.newPosZ = par5; 655 this.newRotationYaw = (double)par7; 656 this.newRotationPitch = (double)par8; 657 this.newPosRotationIncrements = par9; 658 } 659 660 /** 661 * Called to update the entity's position/logic. 662 */ 663 public void onUpdate() 664 { 665 if (ForgeHooks.onLivingUpdate(this)) 666 { 667 return; 668 } 669 670 super.onUpdate(); 671 672 if (this.arrowHitTempCounter > 0) 673 { 674 if (this.arrowHitTimer <= 0) 675 { 676 this.arrowHitTimer = 60; 677 } 678 679 --this.arrowHitTimer; 680 681 if (this.arrowHitTimer <= 0) 682 { 683 --this.arrowHitTempCounter; 684 } 685 } 686 687 this.onLivingUpdate(); 688 double var1 = this.posX - this.prevPosX; 689 double var3 = this.posZ - this.prevPosZ; 690 float var5 = (float)(var1 * var1 + var3 * var3); 691 float var6 = this.renderYawOffset; 692 float var7 = 0.0F; 693 this.field_70768_au = this.field_70766_av; 694 float var8 = 0.0F; 695 696 if (var5 > 0.0025000002F) 697 { 698 var8 = 1.0F; 699 var7 = (float)Math.sqrt((double)var5) * 3.0F; 700 var6 = (float)Math.atan2(var3, var1) * 180.0F / (float)Math.PI - 90.0F; 701 } 702 703 if (this.swingProgress > 0.0F) 704 { 705 var6 = this.rotationYaw; 706 } 707 708 if (!this.onGround) 709 { 710 var8 = 0.0F; 711 } 712 713 this.field_70766_av += (var8 - this.field_70766_av) * 0.3F; 714 this.worldObj.theProfiler.startSection("headTurn"); 715 716 if (this.isAIEnabled()) 717 { 718 this.bodyHelper.func_75664_a(); 719 } 720 else 721 { 722 float var9 = MathHelper.wrapAngleTo180_float(var6 - this.renderYawOffset); 723 this.renderYawOffset += var9 * 0.3F; 724 float var10 = MathHelper.wrapAngleTo180_float(this.rotationYaw - this.renderYawOffset); 725 boolean var11 = var10 < -90.0F || var10 >= 90.0F; 726 727 if (var10 < -75.0F) 728 { 729 var10 = -75.0F; 730 } 731 732 if (var10 >= 75.0F) 733 { 734 var10 = 75.0F; 735 } 736 737 this.renderYawOffset = this.rotationYaw - var10; 738 739 if (var10 * var10 > 2500.0F) 740 { 741 this.renderYawOffset += var10 * 0.2F; 742 } 743 744 if (var11) 745 { 746 var7 *= -1.0F; 747 } 748 } 749 750 this.worldObj.theProfiler.endSection(); 751 this.worldObj.theProfiler.startSection("rangeChecks"); 752 753 while (this.rotationYaw - this.prevRotationYaw < -180.0F) 754 { 755 this.prevRotationYaw -= 360.0F; 756 } 757 758 while (this.rotationYaw - this.prevRotationYaw >= 180.0F) 759 { 760 this.prevRotationYaw += 360.0F; 761 } 762 763 while (this.renderYawOffset - this.prevRenderYawOffset < -180.0F) 764 { 765 this.prevRenderYawOffset -= 360.0F; 766 } 767 768 while (this.renderYawOffset - this.prevRenderYawOffset >= 180.0F) 769 { 770 this.prevRenderYawOffset += 360.0F; 771 } 772 773 while (this.rotationPitch - this.prevRotationPitch < -180.0F) 774 { 775 this.prevRotationPitch -= 360.0F; 776 } 777 778 while (this.rotationPitch - this.prevRotationPitch >= 180.0F) 779 { 780 this.prevRotationPitch += 360.0F; 781 } 782 783 while (this.rotationYawHead - this.prevRotationYawHead < -180.0F) 784 { 785 this.prevRotationYawHead -= 360.0F; 786 } 787 788 while (this.rotationYawHead - this.prevRotationYawHead >= 180.0F) 789 { 790 this.prevRotationYawHead += 360.0F; 791 } 792 793 this.worldObj.theProfiler.endSection(); 794 this.field_70764_aw += var7; 795 } 796 797 /** 798 * Heal living entity (param: amount of half-hearts) 799 */ 800 public void heal(int par1) 801 { 802 if (this.health > 0) 803 { 804 this.health += par1; 805 806 if (this.health > this.getMaxHealth()) 807 { 808 this.health = this.getMaxHealth(); 809 } 810 811 this.hurtResistantTime = this.maxHurtResistantTime / 2; 812 } 813 } 814 815 public abstract int getMaxHealth(); 816 817 public int getHealth() 818 { 819 return this.health; 820 } 821 822 public void setEntityHealth(int par1) 823 { 824 this.health = par1; 825 826 if (par1 > this.getMaxHealth()) 827 { 828 par1 = this.getMaxHealth(); 829 } 830 } 831 832 /** 833 * Called when the entity is attacked. 834 */ 835 public boolean attackEntityFrom(DamageSource par1DamageSource, int par2) 836 { 837 if (ForgeHooks.onLivingAttack(this, par1DamageSource, par2)) 838 { 839 return false; 840 } 841 842 if (this.worldObj.isRemote) 843 { 844 return false; 845 } 846 else 847 { 848 this.entityAge = 0; 849 850 if (this.health <= 0) 851 { 852 return false; 853 } 854 else if (par1DamageSource.fireDamage() && this.isPotionActive(Potion.fireResistance)) 855 { 856 return false; 857 } 858 else 859 { 860 this.legYaw = 1.5F; 861 boolean var3 = true; 862 863 if ((float)this.hurtResistantTime > (float)this.maxHurtResistantTime / 2.0F) 864 { 865 if (par2 <= this.lastDamage) 866 { 867 return false; 868 } 869 870 this.damageEntity(par1DamageSource, par2 - this.lastDamage); 871 this.lastDamage = par2; 872 var3 = false; 873 } 874 else 875 { 876 this.lastDamage = par2; 877 this.prevHealth = this.health; 878 this.hurtResistantTime = this.maxHurtResistantTime; 879 this.damageEntity(par1DamageSource, par2); 880 this.hurtTime = this.maxHurtTime = 10; 881 } 882 883 this.attackedAtYaw = 0.0F; 884 Entity var4 = par1DamageSource.getEntity(); 885 886 if (var4 != null) 887 { 888 if (var4 instanceof EntityLiving) 889 { 890 this.setRevengeTarget((EntityLiving)var4); 891 } 892 893 if (var4 instanceof EntityPlayer) 894 { 895 this.recentlyHit = 60; 896 this.attackingPlayer = (EntityPlayer)var4; 897 } 898 else if (var4 instanceof EntityWolf) 899 { 900 EntityWolf var5 = (EntityWolf)var4; 901 902 if (var5.isTamed()) 903 { 904 this.recentlyHit = 60; 905 this.attackingPlayer = null; 906 } 907 } 908 } 909 910 if (var3) 911 { 912 this.worldObj.setEntityState(this, (byte)2); 913 914 if (par1DamageSource != DamageSource.drown && par1DamageSource != DamageSource.field_76375_l) 915 { 916 this.setBeenAttacked(); 917 } 918 919 if (var4 != null) 920 { 921 double var9 = var4.posX - this.posX; 922 double var7; 923 924 for (var7 = var4.posZ - this.posZ; var9 * var9 + var7 * var7 < 1.0E-4D; var7 = (Math.random() - Math.random()) * 0.01D) 925 { 926 var9 = (Math.random() - Math.random()) * 0.01D; 927 } 928 929 this.attackedAtYaw = (float)(Math.atan2(var7, var9) * 180.0D / Math.PI) - this.rotationYaw; 930 this.knockBack(var4, par2, var9, var7); 931 } 932 else 933 { 934 this.attackedAtYaw = (float)((int)(Math.random() * 2.0D) * 180); 935 } 936 } 937 938 if (this.health <= 0) 939 { 940 if (var3) 941 { 942 this.worldObj.playSoundAtEntity(this, this.getDeathSound(), this.getSoundVolume(), this.getSoundPitch()); 943 } 944 945 this.onDeath(par1DamageSource); 946 } 947 else if (var3) 948 { 949 this.worldObj.playSoundAtEntity(this, this.getHurtSound(), this.getSoundVolume(), this.getSoundPitch()); 950 } 951 952 return true; 953 } 954 } 955 } 956 957 /** 958 * Gets the pitch of living sounds in living entities. 959 */ 960 private float getSoundPitch() 961 { 962 return this.isChild() ? (this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F + 1.5F : (this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F + 1.0F; 963 } 964 965 @SideOnly(Side.CLIENT) 966 967 /** 968 * Setups the entity to do the hurt animation. Only used by packets in multiplayer. 969 */ 970 public void performHurtAnimation() 971 { 972 this.hurtTime = this.maxHurtTime = 10; 973 this.attackedAtYaw = 0.0F; 974 } 975 976 /** 977 * Returns the current armor value as determined by a call to InventoryPlayer.getTotalArmorValue 978 */ 979 public int getTotalArmorValue() 980 { 981 return 0; 982 } 983 984 protected void damageArmor(int par1) {} 985 986 /** 987 * Reduces damage, depending on armor 988 */ 989 protected int applyArmorCalculations(DamageSource par1DamageSource, int par2) 990 { 991 if (!par1DamageSource.isUnblockable()) 992 { 993 int var3 = 25 - this.getTotalArmorValue(); 994 int var4 = par2 * var3 + this.carryoverDamage; 995 this.damageArmor(par2); 996 par2 = var4 / 25; 997 this.carryoverDamage = var4 % 25; 998 } 999 1000 return par2; 1001 } 1002 1003 /** 1004 * Reduces damage, depending on potions 1005 */ 1006 protected int applyPotionDamageCalculations(DamageSource par1DamageSource, int par2) 1007 { 1008 if (this.isPotionActive(Potion.resistance)) 1009 { 1010 int var3 = (this.getActivePotionEffect(Potion.resistance).getAmplifier() + 1) * 5; 1011 int var4 = 25 - var3; 1012 int var5 = par2 * var4 + this.carryoverDamage; 1013 par2 = var5 / 25; 1014 this.carryoverDamage = var5 % 25; 1015 } 1016 1017 return par2; 1018 } 1019 1020 /** 1021 * Deals damage to the entity. If its a EntityPlayer then will take damage from the armor first and then health 1022 * second with the reduced value. Args: damageAmount 1023 */ 1024 protected void damageEntity(DamageSource par1DamageSource, int par2) 1025 { 1026 par2 = ForgeHooks.onLivingHurt(this, par1DamageSource, par2); 1027 if (par2 <= 0) 1028 { 1029 return; 1030 } 1031 1032 par2 = this.applyArmorCalculations(par1DamageSource, par2); 1033 par2 = this.applyPotionDamageCalculations(par1DamageSource, par2); 1034 this.health -= par2; 1035 } 1036 1037 /** 1038 * Returns the volume for the sounds this mob makes. 1039 */ 1040 protected float getSoundVolume() 1041 { 1042 return 1.0F; 1043 } 1044 1045 /** 1046 * Returns the sound this mob makes while it's alive. 1047 */ 1048 protected String getLivingSound() 1049 { 1050 return null; 1051 } 1052 1053 /** 1054 * Returns the sound this mob makes when it is hurt. 1055 */ 1056 protected String getHurtSound() 1057 { 1058 return "damage.hurtflesh"; 1059 } 1060 1061 /** 1062 * Returns the sound this mob makes on death. 1063 */ 1064 protected String getDeathSound() 1065 { 1066 return "damage.hurtflesh"; 1067 } 1068 1069 /** 1070 * knocks back this entity 1071 */ 1072 public void knockBack(Entity par1Entity, int par2, double par3, double par5) 1073 { 1074 this.isAirBorne = true; 1075 float var7 = MathHelper.sqrt_double(par3 * par3 + par5 * par5); 1076 float var8 = 0.4F; 1077 this.motionX /= 2.0D; 1078 this.motionY /= 2.0D; 1079 this.motionZ /= 2.0D; 1080 this.motionX -= par3 / (double)var7 * (double)var8; 1081 this.motionY += (double)var8; 1082 this.motionZ -= par5 / (double)var7 * (double)var8; 1083 1084 if (this.motionY > 0.4000000059604645D) 1085 { 1086 this.motionY = 0.4000000059604645D; 1087 } 1088 } 1089 1090 /** 1091 * Called when the mob's health reaches 0. 1092 */ 1093 public void onDeath(DamageSource par1DamageSource) 1094 { 1095 if (ForgeHooks.onLivingDeath(this, par1DamageSource)) 1096 { 1097 return; 1098 } 1099 1100 Entity var2 = par1DamageSource.getEntity(); 1101 1102 if (this.scoreValue >= 0 && var2 != null) 1103 { 1104 var2.addToPlayerScore(this, this.scoreValue); 1105 } 1106 1107 if (var2 != null) 1108 { 1109 var2.onKillEntity(this); 1110 } 1111 1112 this.dead = true; 1113 1114 if (!this.worldObj.isRemote) 1115 { 1116 int var3 = 0; 1117 1118 if (var2 instanceof EntityPlayer) 1119 { 1120 var3 = EnchantmentHelper.getLootingModifier(((EntityPlayer)var2).inventory); 1121 } 1122 1123 captureDrops = true; 1124 capturedDrops.clear(); 1125 int var4 = 0; 1126 1127 if (!this.isChild()) 1128 { 1129 this.dropFewItems(this.recentlyHit > 0, var3); 1130 1131 if (this.recentlyHit > 0) 1132 { 1133 var4 = this.rand.nextInt(200) - var3; 1134 1135 if (var4 < 5) 1136 { 1137 this.dropRareDrop(var4 <= 0 ? 1 : 0); 1138 } 1139 } 1140 } 1141 1142 captureDrops = false; 1143 1144 if (!ForgeHooks.onLivingDrops(this, par1DamageSource, capturedDrops, var3, recentlyHit > 0, var4)) 1145 { 1146 for (EntityItem item : capturedDrops) 1147 { 1148 worldObj.spawnEntityInWorld(item); 1149 } 1150 } 1151 } 1152 1153 this.worldObj.setEntityState(this, (byte)3); 1154 } 1155 1156 protected void dropRareDrop(int par1) {} 1157 1158 /** 1159 * Drop 0-2 items of this living's type 1160 */ 1161 protected void dropFewItems(boolean par1, int par2) 1162 { 1163 int var3 = this.getDropItemId(); 1164 1165 if (var3 > 0) 1166 { 1167 int var4 = this.rand.nextInt(3); 1168 1169 if (par2 > 0) 1170 { 1171 var4 += this.rand.nextInt(par2 + 1); 1172 } 1173 1174 for (int var5 = 0; var5 < var4; ++var5) 1175 { 1176 this.dropItem(var3, 1); 1177 } 1178 } 1179 } 1180 1181 /** 1182 * Returns the item ID for the item the mob drops on death. 1183 */ 1184 protected int getDropItemId() 1185 { 1186 return 0; 1187 } 1188 1189 /** 1190 * Called when the mob is falling. Calculates and applies fall damage. 1191 */ 1192 protected void fall(float par1) 1193 { 1194 par1 = ForgeHooks.onLivingFall(this, par1); 1195 if (par1 <= 0) 1196 { 1197 return; 1198 } 1199 1200 super.fall(par1); 1201 int var2 = MathHelper.ceiling_float_int(par1 - 3.0F); 1202 1203 if (var2 > 0) 1204 { 1205 if (var2 > 4) 1206 { 1207 this.worldObj.playSoundAtEntity(this, "damage.fallbig", 1.0F, 1.0F); 1208 } 1209 else 1210 { 1211 this.worldObj.playSoundAtEntity(this, "damage.fallsmall", 1.0F, 1.0F); 1212 } 1213 1214 this.attackEntityFrom(DamageSource.fall, var2); 1215 int var3 = this.worldObj.getBlockId(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.posY - 0.20000000298023224D - (double)this.yOffset), MathHelper.floor_double(this.posZ)); 1216 1217 if (var3 > 0) 1218 { 1219 StepSound var4 = Block.blocksList[var3].stepSound; 1220 this.worldObj.playSoundAtEntity(this, var4.getStepSound(), var4.getVolume() * 0.5F, var4.getPitch() * 0.75F); 1221 } 1222 } 1223 } 1224 1225 /** 1226 * Moves the entity based on the specified heading. Args: strafe, forward 1227 */ 1228 public void moveEntityWithHeading(float par1, float par2) 1229 { 1230 double var9; 1231 1232 if (this.isInWater() && (!(this instanceof EntityPlayer) || !((EntityPlayer)this).capabilities.isFlying)) 1233 { 1234 var9 = this.posY; 1235 this.moveFlying(par1, par2, this.isAIEnabled() ? 0.04F : 0.02F); 1236 this.moveEntity(this.motionX, this.motionY, this.motionZ); 1237 this.motionX *= 0.800000011920929D; 1238 this.motionY *= 0.800000011920929D; 1239 this.motionZ *= 0.800000011920929D; 1240 this.motionY -= 0.02D; 1241 1242 if (this.isCollidedHorizontally && this.isOffsetPositionInLiquid(this.motionX, this.motionY + 0.6000000238418579D - this.posY + var9, this.motionZ)) 1243 { 1244 this.motionY = 0.30000001192092896D; 1245 } 1246 } 1247 else if (this.handleLavaMovement() && (!(this instanceof EntityPlayer) || !((EntityPlayer)this).capabilities.isFlying)) 1248 { 1249 var9 = this.posY; 1250 this.moveFlying(par1, par2, 0.02F); 1251 this.moveEntity(this.motionX, this.motionY, this.motionZ); 1252 this.motionX *= 0.5D; 1253 this.motionY *= 0.5D; 1254 this.motionZ *= 0.5D; 1255 this.motionY -= 0.02D; 1256 1257 if (this.isCollidedHorizontally && this.isOffsetPositionInLiquid(this.motionX, this.motionY + 0.6000000238418579D - this.posY + var9, this.motionZ)) 1258 { 1259 this.motionY = 0.30000001192092896D; 1260 } 1261 } 1262 else 1263 { 1264 float var3 = 0.91F; 1265 1266 if (this.onGround) 1267 { 1268 var3 = 0.54600006F; 1269 int var4 = this.worldObj.getBlockId(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.boundingBox.minY) - 1, MathHelper.floor_double(this.posZ)); 1270 1271 if (var4 > 0) 1272 { 1273 var3 = Block.blocksList[var4].slipperiness * 0.91F; 1274 } 1275 } 1276 1277 float var8 = 0.16277136F / (var3 * var3 * var3); 1278 float var5; 1279 1280 if (this.onGround) 1281 { 1282 if (this.isAIEnabled()) 1283 { 1284 var5 = this.getAIMoveSpeed(); 1285 } 1286 else 1287 { 1288 var5 = this.landMovementFactor; 1289 } 1290 1291 var5 *= var8; 1292 } 1293 else 1294 { 1295 var5 = this.jumpMovementFactor; 1296 } 1297 1298 this.moveFlying(par1, par2, var5); 1299 var3 = 0.91F; 1300 1301 if (this.onGround) 1302 { 1303 var3 = 0.54600006F; 1304 int var6 = this.worldObj.getBlockId(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.boundingBox.minY) - 1, MathHelper.floor_double(this.posZ)); 1305 1306 if (var6 > 0) 1307 { 1308 var3 = Block.blocksList[var6].slipperiness * 0.91F; 1309 } 1310 } 1311 1312 if (this.isOnLadder()) 1313 { 1314 float var10 = 0.15F; 1315 1316 if (this.motionX < (double)(-var10)) 1317 { 1318 this.motionX = (double)(-var10); 1319 } 1320 1321 if (this.motionX > (double)var10) 1322 { 1323 this.motionX = (double)var10; 1324 } 1325 1326 if (this.motionZ < (double)(-var10)) 1327 { 1328 this.motionZ = (double)(-var10); 1329 } 1330 1331 if (this.motionZ > (double)var10) 1332 { 1333 this.motionZ = (double)var10; 1334 } 1335 1336 this.fallDistance = 0.0F; 1337 1338 if (this.motionY < -0.15D) 1339 { 1340 this.motionY = -0.15D; 1341 } 1342 1343 boolean var7 = this.isSneaking() && this instanceof EntityPlayer; 1344 1345 if (var7 && this.motionY < 0.0D) 1346 { 1347 this.motionY = 0.0D; 1348 } 1349 } 1350 1351 this.moveEntity(this.motionX, this.motionY, this.motionZ); 1352 1353 if (this.isCollidedHorizontally && this.isOnLadder()) 1354 { 1355 this.motionY = 0.2D; 1356 } 1357 1358 this.motionY -= 0.08D; 1359 this.motionY *= 0.9800000190734863D; 1360 this.motionX *= (double)var3; 1361 this.motionZ *= (double)var3; 1362 } 1363 1364 this.prevLegYaw = this.legYaw; 1365 var9 = this.posX - this.prevPosX; 1366 double var12 = this.posZ - this.prevPosZ; 1367 float var11 = MathHelper.sqrt_double(var9 * var9 + var12 * var12) * 4.0F; 1368 1369 if (var11 > 1.0F) 1370 { 1371 var11 = 1.0F; 1372 } 1373 1374 this.legYaw += (var11 - this.legYaw) * 0.4F; 1375 this.field_70754_ba += this.legYaw; 1376 } 1377 1378 /** 1379 * returns true if this entity is by a ladder, false otherwise 1380 */ 1381 public boolean isOnLadder() 1382 { 1383 int var1 = MathHelper.floor_double(this.posX); 1384 int var2 = MathHelper.floor_double(this.boundingBox.minY); 1385 int var3 = MathHelper.floor_double(this.posZ); 1386 int var4 = this.worldObj.getBlockId(var1, var2, var3); 1387 return ForgeHooks.isLivingOnLadder(Block.blocksList[var4], worldObj, var1, var2, var3); 1388 } 1389 1390 /** 1391 * (abstract) Protected helper method to write subclass entity data to NBT. 1392 */ 1393 public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound) 1394 { 1395 par1NBTTagCompound.setShort("Health", (short)this.health); 1396 par1NBTTagCompound.setShort("HurtTime", (short)this.hurtTime); 1397 par1NBTTagCompound.setShort("DeathTime", (short)this.deathTime); 1398 par1NBTTagCompound.setShort("AttackTime", (short)this.attackTime); 1399 1400 if (!this.activePotionsMap.isEmpty()) 1401 { 1402 NBTTagList var2 = new NBTTagList(); 1403 Iterator var3 = this.activePotionsMap.values().iterator(); 1404 1405 while (var3.hasNext()) 1406 { 1407 PotionEffect var4 = (PotionEffect)var3.next(); 1408 NBTTagCompound var5 = new NBTTagCompound(); 1409 var5.setByte("Id", (byte)var4.getPotionID()); 1410 var5.setByte("Amplifier", (byte)var4.getAmplifier()); 1411 var5.setInteger("Duration", var4.getDuration()); 1412 var2.appendTag(var5); 1413 } 1414 1415 par1NBTTagCompound.setTag("ActiveEffects", var2); 1416 } 1417 } 1418 1419 /** 1420 * (abstract) Protected helper method to read subclass entity data from NBT. 1421 */ 1422 public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound) 1423 { 1424 if (this.health < -32768) 1425 { 1426 this.health = -32768; 1427 } 1428 1429 this.health = par1NBTTagCompound.getShort("Health"); 1430 1431 if (!par1NBTTagCompound.hasKey("Health")) 1432 { 1433 this.health = this.getMaxHealth(); 1434 } 1435 1436 this.hurtTime = par1NBTTagCompound.getShort("HurtTime"); 1437 this.deathTime = par1NBTTagCompound.getShort("DeathTime"); 1438 this.attackTime = par1NBTTagCompound.getShort("AttackTime"); 1439 1440 if (par1NBTTagCompound.hasKey("ActiveEffects")) 1441 { 1442 NBTTagList var2 = par1NBTTagCompound.getTagList("ActiveEffects"); 1443 1444 for (int var3 = 0; var3 < var2.tagCount(); ++var3) 1445 { 1446 NBTTagCompound var4 = (NBTTagCompound)var2.tagAt(var3); 1447 byte var5 = var4.getByte("Id"); 1448 byte var6 = var4.getByte("Amplifier"); 1449 int var7 = var4.getInteger("Duration"); 1450 this.activePotionsMap.put(Integer.valueOf(var5), new PotionEffect(var5, var7, var6)); 1451 } 1452 } 1453 } 1454 1455 /** 1456 * Checks whether target entity is alive. 1457 */ 1458 public boolean isEntityAlive() 1459 { 1460 return !this.isDead && this.health > 0; 1461 } 1462 1463 public boolean canBreatheUnderwater() 1464 { 1465 return false; 1466 } 1467 1468 public void setMoveForward(float par1) 1469 { 1470 this.moveForward = par1; 1471 } 1472 1473 public void setJumping(boolean par1) 1474 { 1475 this.isJumping = par1; 1476 } 1477 1478 /** 1479 * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons 1480 * use this to react to sunlight and start to burn. 1481 */ 1482 public void onLivingUpdate() 1483 { 1484 if (this.jumpTicks > 0) 1485 { 1486 --this.jumpTicks; 1487 } 1488 1489 if (this.newPosRotationIncrements > 0) 1490 { 1491 double var1 = this.posX + (this.newPosX - this.posX) / (double)this.newPosRotationIncrements; 1492 double var3 = this.posY + (this.newPosY - this.posY) / (double)this.newPosRotationIncrements; 1493 double var5 = this.posZ + (this.newPosZ - this.posZ) / (double)this.newPosRotationIncrements; 1494 double var7 = MathHelper.wrapAngleTo180_double(this.newRotationYaw - (double)this.rotationYaw); 1495 this.rotationYaw = (float)((double)this.rotationYaw + var7 / (double)this.newPosRotationIncrements); 1496 this.rotationPitch = (float)((double)this.rotationPitch + (this.newRotationPitch - (double)this.rotationPitch) / (double)this.newPosRotationIncrements); 1497 --this.newPosRotationIncrements; 1498 this.setPosition(var1, var3, var5); 1499 this.setRotation(this.rotationYaw, this.rotationPitch); 1500 } 1501 1502 if (Math.abs(this.motionX) < 0.005D) 1503 { 1504 this.motionX = 0.0D; 1505 } 1506 1507 if (Math.abs(this.motionY) < 0.005D) 1508 { 1509 this.motionY = 0.0D; 1510 } 1511 1512 if (Math.abs(this.motionZ) < 0.005D) 1513 { 1514 this.motionZ = 0.0D; 1515 } 1516 1517 this.worldObj.theProfiler.startSection("ai"); 1518 1519 if (this.isMovementBlocked()) 1520 { 1521 this.isJumping = false; 1522 this.moveStrafing = 0.0F; 1523 this.moveForward = 0.0F; 1524 this.randomYawVelocity = 0.0F; 1525 } 1526 else if (this.isClientWorld()) 1527 { 1528 if (this.isAIEnabled()) 1529 { 1530 this.worldObj.theProfiler.startSection("newAi"); 1531 this.updateAITasks(); 1532 this.worldObj.theProfiler.endSection(); 1533 } 1534 else 1535 { 1536 this.worldObj.theProfiler.startSection("oldAi"); 1537 this.updateEntityActionState(); 1538 this.worldObj.theProfiler.endSection(); 1539 this.rotationYawHead = this.rotationYaw; 1540 } 1541 } 1542 1543 this.worldObj.theProfiler.endSection(); 1544 this.worldObj.theProfiler.startSection("jump"); 1545 1546 if (this.isJumping) 1547 { 1548 if (!this.isInWater() && !this.handleLavaMovement()) 1549 { 1550 if (this.onGround && this.jumpTicks == 0) 1551 { 1552 this.jump(); 1553 this.jumpTicks = 10; 1554 } 1555 } 1556 else 1557 { 1558 this.motionY += 0.03999999910593033D; 1559 } 1560 } 1561 else 1562 { 1563 this.jumpTicks = 0; 1564 } 1565 1566 this.worldObj.theProfiler.endSection(); 1567 this.worldObj.theProfiler.startSection("travel"); 1568 this.moveStrafing *= 0.98F; 1569 this.moveForward *= 0.98F; 1570 this.randomYawVelocity *= 0.9F; 1571 float var9 = this.landMovementFactor; 1572 this.landMovementFactor *= this.getSpeedModifier(); 1573 this.moveEntityWithHeading(this.moveStrafing, this.moveForward); 1574 this.landMovementFactor = var9; 1575 this.worldObj.theProfiler.endSection(); 1576 this.worldObj.theProfiler.startSection("push"); 1577 1578 if (!this.worldObj.isRemote) 1579 { 1580 List var2 = this.worldObj.getEntitiesWithinAABBExcludingEntity(this, this.boundingBox.expand(0.20000000298023224D, 0.0D, 0.20000000298023224D)); 1581 1582 if (var2 != null && !var2.isEmpty()) 1583 { 1584 Iterator var10 = var2.iterator(); 1585 1586 while (var10.hasNext()) 1587 { 1588 Entity var4 = (Entity)var10.next(); 1589 1590 if (var4.canBePushed()) 1591 { 1592 var4.applyEntityCollision(this); 1593 } 1594 } 1595 } 1596 } 1597 1598 this.worldObj.theProfiler.endSection(); 1599 } 1600 1601 /** 1602 * Returns true if the newer Entity AI code should be run 1603 */ 1604 protected boolean isAIEnabled() 1605 { 1606 return false; 1607 } 1608 1609 /** 1610 * Returns whether the entity is in a local (client) world 1611 */ 1612 protected boolean isClientWorld() 1613 { 1614 return !this.worldObj.isRemote; 1615 } 1616 1617 /** 1618 * Dead and sleeping entities cannot move 1619 */ 1620 protected boolean isMovementBlocked() 1621 { 1622 return this.health <= 0; 1623 } 1624 1625 public boolean isBlocking() 1626 { 1627 return false; 1628 } 1629 1630 /** 1631 * Causes this entity to do an upwards motion (jumping). 1632 */ 1633 protected void jump() 1634 { 1635 this.motionY = 0.41999998688697815D; 1636 1637 if (this.isPotionActive(Potion.jump)) 1638 { 1639 this.motionY += (double)((float)(this.getActivePotionEffect(Potion.jump).getAmplifier() + 1) * 0.1F); 1640 } 1641 1642 if (this.isSprinting()) 1643 { 1644 float var1 = this.rotationYaw * 0.017453292F; 1645 this.motionX -= (double)(MathHelper.sin(var1) * 0.2F); 1646 this.motionZ += (double)(MathHelper.cos(var1) * 0.2F); 1647 } 1648 1649 this.isAirBorne = true; 1650 ForgeHooks.onLivingJump(this); 1651 } 1652 1653 /** 1654 * Determines if an entity can be despawned, used on idle far away entities 1655 */ 1656 protected boolean canDespawn() 1657 { 1658 return true; 1659 } 1660 1661 /** 1662 * Makes the entity despawn if requirements are reached 1663 */ 1664 protected void despawnEntity() 1665 { 1666 EntityPlayer var1 = this.worldObj.getClosestPlayerToEntity(this, -1.0D); 1667 1668 if (var1 != null) 1669 { 1670 double var2 = var1.posX - this.posX; 1671 double var4 = var1.posY - this.posY; 1672 double var6 = var1.posZ - this.posZ; 1673 double var8 = var2 * var2 + var4 * var4 + var6 * var6; 1674 1675 if (this.canDespawn() && var8 > 16384.0D) 1676 { 1677 this.setDead(); 1678 } 1679 1680 if (this.entityAge > 600 && this.rand.nextInt(800) == 0 && var8 > 1024.0D && this.canDespawn()) 1681 { 1682 this.setDead(); 1683 } 1684 else if (var8 < 1024.0D) 1685 { 1686 this.entityAge = 0; 1687 } 1688 } 1689 } 1690 1691 protected void updateAITasks() 1692 { 1693 ++this.entityAge; 1694 this.worldObj.theProfiler.startSection("checkDespawn"); 1695 this.despawnEntity(); 1696 this.worldObj.theProfiler.endSection(); 1697 this.worldObj.theProfiler.startSection("sensing"); 1698 this.senses.clearSensingCache(); 1699 this.worldObj.theProfiler.endSection(); 1700 this.worldObj.theProfiler.startSection("targetSelector"); 1701 this.targetTasks.onUpdateTasks(); 1702 this.worldObj.theProfiler.endSection(); 1703 this.worldObj.theProfiler.startSection("goalSelector"); 1704 this.tasks.onUpdateTasks(); 1705 this.worldObj.theProfiler.endSection(); 1706 this.worldObj.theProfiler.startSection("navigation"); 1707 this.navigator.onUpdateNavigation(); 1708 this.worldObj.theProfiler.endSection(); 1709 this.worldObj.theProfiler.startSection("mob tick"); 1710 this.updateAITick(); 1711 this.worldObj.theProfiler.endSection(); 1712 this.worldObj.theProfiler.startSection("controls"); 1713 this.worldObj.theProfiler.startSection("move"); 1714 this.moveHelper.onUpdateMoveHelper(); 1715 this.worldObj.theProfiler.endStartSection("look"); 1716 this.lookHelper.onUpdateLook(); 1717 this.worldObj.theProfiler.endStartSection("jump"); 1718 this.jumpHelper.doJump(); 1719 this.worldObj.theProfiler.endSection(); 1720 this.worldObj.theProfiler.endSection(); 1721 } 1722 1723 /** 1724 * main AI tick function, replaces updateEntityActionState 1725 */ 1726 protected void updateAITick() {} 1727 1728 protected void updateEntityActionState() 1729 { 1730 ++this.entityAge; 1731 this.despawnEntity(); 1732 this.moveStrafing = 0.0F; 1733 this.moveForward = 0.0F; 1734 float var1 = 8.0F; 1735 1736 if (this.rand.nextFloat() < 0.02F) 1737 { 1738 EntityPlayer var2 = this.worldObj.getClosestPlayerToEntity(this, (double)var1); 1739 1740 if (var2 != null) 1741 { 1742 this.currentTarget = var2; 1743 this.numTicksToChaseTarget = 10 + this.rand.nextInt(20); 1744 } 1745 else 1746 { 1747 this.randomYawVelocity = (this.rand.nextFloat() - 0.5F) * 20.0F; 1748 } 1749 } 1750 1751 if (this.currentTarget != null) 1752 { 1753 this.faceEntity(this.currentTarget, 10.0F, (float)this.getVerticalFaceSpeed()); 1754 1755 if (this.numTicksToChaseTarget-- <= 0 || this.currentTarget.isDead || this.currentTarget.getDistanceSqToEntity(this) > (double)(var1 * var1)) 1756 { 1757 this.currentTarget = null; 1758 } 1759 } 1760 else 1761 { 1762 if (this.rand.nextFloat() < 0.05F) 1763 { 1764 this.randomYawVelocity = (this.rand.nextFloat() - 0.5F) * 20.0F; 1765 } 1766 1767 this.rotationYaw += this.randomYawVelocity; 1768 this.rotationPitch = this.defaultPitch; 1769 } 1770 1771 boolean var4 = this.isInWater(); 1772 boolean var3 = this.handleLavaMovement(); 1773 1774 if (var4 || var3) 1775 { 1776 this.isJumping = this.rand.nextFloat() < 0.8F; 1777 } 1778 } 1779 1780 /** 1781 * The speed it takes to move the entityliving's rotationPitch through the faceEntity method. This is only currently 1782 * use in wolves. 1783 */ 1784 public int getVerticalFaceSpeed() 1785 { 1786 return 40; 1787 } 1788 1789 /** 1790 * Changes pitch and yaw so that the entity calling the function is facing the entity provided as an argument. 1791 */ 1792 public void faceEntity(Entity par1Entity, float par2, float par3) 1793 { 1794 double var4 = par1Entity.posX - this.posX; 1795 double var8 = par1Entity.posZ - this.posZ; 1796 double var6; 1797 1798 if (par1Entity instanceof EntityLiving) 1799 { 1800 EntityLiving var10 = (EntityLiving)par1Entity; 1801 var6 = this.posY + (double)this.getEyeHeight() - (var10.posY + (double)var10.getEyeHeight()); 1802 } 1803 else 1804 { 1805 var6 = (par1Entity.boundingBox.minY + par1Entity.boundingBox.maxY) / 2.0D - (this.posY + (double)this.getEyeHeight()); 1806 } 1807 1808 double var14 = (double)MathHelper.sqrt_double(var4 * var4 + var8 * var8); 1809 float var12 = (float)(Math.atan2(var8, var4) * 180.0D / Math.PI) - 90.0F; 1810 float var13 = (float)(-(Math.atan2(var6, var14) * 180.0D / Math.PI)); 1811 this.rotationPitch = -this.updateRotation(this.rotationPitch, var13, par3); 1812 this.rotationYaw = this.updateRotation(this.rotationYaw, var12, par2); 1813 } 1814 1815 /** 1816 * Arguments: current rotation, intended rotation, max increment. 1817 */ 1818 private float updateRotation(float par1, float par2, float par3) 1819 { 1820 float var4 = MathHelper.wrapAngleTo180_float(par2 - par1); 1821 1822 if (var4 > par3) 1823 { 1824 var4 = par3; 1825 } 1826 1827 if (var4 < -par3) 1828 { 1829 var4 = -par3; 1830 } 1831 1832 return par1 + var4; 1833 } 1834 1835 /** 1836 * Checks if the entity's current position is a valid location to spawn this entity. 1837 */ 1838 public boolean getCanSpawnHere() 1839 { 1840 return this.worldObj.checkIfAABBIsClear(this.boundingBox) && this.worldObj.getCollidingBoundingBoxes(this, this.boundingBox).isEmpty() && !this.worldObj.isAnyLiquid(this.boundingBox); 1841 } 1842 1843 /** 1844 * sets the dead flag. Used when you fall off the bottom of the world. 1845 */ 1846 protected void kill() 1847 { 1848 this.attackEntityFrom(DamageSource.outOfWorld, 4); 1849 } 1850 1851 @SideOnly(Side.CLIENT) 1852 1853 /** 1854 * Returns where in the swing animation the living entity is (from 0 to 1). Args: partialTickTime 1855 */ 1856 public float getSwingProgress(float par1) 1857 { 1858 float var2 = this.swingProgress - this.prevSwingProgress; 1859 1860 if (var2 < 0.0F) 1861 { 1862 ++var2; 1863 } 1864 1865 return this.prevSwingProgress + var2 * par1; 1866 } 1867 1868 @SideOnly(Side.CLIENT) 1869 1870 /** 1871 * interpolated position vector 1872 */ 1873 public Vec3 getPosition(float par1) 1874 { 1875 if (par1 == 1.0F) 1876 { 1877 return Vec3.getVec3Pool().getVecFromPool(this.posX, this.posY, this.posZ); 1878 } 1879 else 1880 { 1881 double var2 = this.prevPosX + (this.posX - this.prevPosX) * (double)par1; 1882 double var4 = this.prevPosY + (this.posY - this.prevPosY) * (double)par1; 1883 double var6 = this.prevPosZ + (this.posZ - this.prevPosZ) * (double)par1; 1884 return Vec3.getVec3Pool().getVecFromPool(var2, var4, var6); 1885 } 1886 } 1887 1888 /** 1889 * returns a (normalized) vector of where this entity is looking 1890 */ 1891 public Vec3 getLookVec() 1892 { 1893 return this.getLook(1.0F); 1894 } 1895 1896 /** 1897 * interpolated look vector 1898 */ 1899 public Vec3 getLook(float par1) 1900 { 1901 float var2; 1902 float var3; 1903 float var4; 1904 float var5; 1905 1906 if (par1 == 1.0F) 1907 { 1908 var2 = MathHelper.cos(-this.rotationYaw * 0.017453292F - (float)Math.PI); 1909 var3 = MathHelper.sin(-this.rotationYaw * 0.017453292F - (float)Math.PI); 1910 var4 = -MathHelper.cos(-this.rotationPitch * 0.017453292F); 1911 var5 = MathHelper.sin(-this.rotationPitch * 0.017453292F); 1912 return Vec3.getVec3Pool().getVecFromPool((double)(var3 * var4), (double)var5, (double)(var2 * var4)); 1913 } 1914 else 1915 { 1916 var2 = this.prevRotationPitch + (this.rotationPitch - this.prevRotationPitch) * par1; 1917 var3 = this.prevRotationYaw + (this.rotationYaw - this.prevRotationYaw) * par1; 1918 var4 = MathHelper.cos(-var3 * 0.017453292F - (float)Math.PI); 1919 var5 = MathHelper.sin(-var3 * 0.017453292F - (float)Math.PI); 1920 float var6 = -MathHelper.cos(-var2 * 0.017453292F); 1921 float var7 = MathHelper.sin(-var2 * 0.017453292F); 1922 return Vec3.getVec3Pool().getVecFromPool((double)(var5 * var6), (double)var7, (double)(var4 * var6)); 1923 } 1924 } 1925 1926 @SideOnly(Side.CLIENT) 1927 1928 /** 1929 * Returns render size modifier 1930 */ 1931 public float getRenderSizeModifier() 1932 { 1933 return 1.0F; 1934 } 1935 1936 @SideOnly(Side.CLIENT) 1937 1938 /** 1939 * Performs a ray trace for the distance specified and using the partial tick time. Args: distance, partialTickTime 1940 */ 1941 public MovingObjectPosition rayTrace(double par1, float par3) 1942 { 1943 Vec3 var4 = this.getPosition(par3); 1944 Vec3 var5 = this.getLook(par3); 1945 Vec3 var6 = var4.addVector(var5.xCoord * par1, var5.yCoord * par1, var5.zCoord * par1); 1946 return this.worldObj.rayTraceBlocks(var4, var6); 1947 } 1948 1949 /** 1950 * Will return how many at most can spawn in a chunk at once. 1951 */ 1952 public int getMaxSpawnedInChunk() 1953 { 1954 return 4; 1955 } 1956 1957 @SideOnly(Side.CLIENT) 1958 1959 /** 1960 * Returns the item that this EntityLiving is holding, if any. 1961 */ 1962 public ItemStack getHeldItem() 1963 { 1964 return null; 1965 } 1966 1967 @SideOnly(Side.CLIENT) 1968 public void handleHealthUpdate(byte par1) 1969 { 1970 if (par1 == 2) 1971 { 1972 this.legYaw = 1.5F; 1973 this.hurtResistantTime = this.maxHurtResistantTime; 1974 this.hurtTime = this.maxHurtTime = 10; 1975 this.attackedAtYaw = 0.0F; 1976 this.worldObj.playSoundAtEntity(this, this.getHurtSound(), this.getSoundVolume(), (this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F + 1.0F); 1977 this.attackEntityFrom(DamageSource.generic, 0); 1978 } 1979 else if (par1 == 3) 1980 { 1981 this.worldObj.playSoundAtEntity(this, this.getDeathSound(), this.getSoundVolume(), (this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F + 1.0F); 1982 this.health = 0; 1983 this.onDeath(DamageSource.generic); 1984 } 1985 else 1986 { 1987 super.handleHealthUpdate(par1); 1988 } 1989 } 1990 1991 /** 1992 * Returns whether player is sleeping or not 1993 */ 1994 public boolean isPlayerSleeping() 1995 { 1996 return false; 1997 } 1998 1999 @SideOnly(Side.CLIENT) 2000 2001 /** 2002 * Gets the Icon Index of the item currently held 2003 */ 2004 public int getItemIcon(ItemStack par1ItemStack, int par2) 2005 { 2006 return par1ItemStack.getIconIndex(); 2007 } 2008 2009 protected void updatePotionEffects() 2010 { 2011 Iterator var1 = this.activePotionsMap.keySet().iterator(); 2012 2013 while (var1.hasNext()) 2014 { 2015 Integer var2 = (Integer)var1.next(); 2016 PotionEffect var3 = (PotionEffect)this.activePotionsMap.get(var2); 2017 2018 if (!var3.onUpdate(this) && !this.worldObj.isRemote) 2019 { 2020 var1.remove(); 2021 this.onFinishedPotionEffect(var3); 2022 } 2023 } 2024 2025 int var9; 2026 2027 if (this.potionsNeedUpdate) 2028 { 2029 if (!this.worldObj.isRemote) 2030 { 2031 if (this.activePotionsMap.isEmpty()) 2032 { 2033 this.dataWatcher.updateObject(8, Integer.valueOf(0)); 2034 } 2035 else 2036 { 2037 var9 = PotionHelper.calcPotionLiquidColor(this.activePotionsMap.values()); 2038 this.dataWatcher.updateObject(8, Integer.valueOf(var9)); 2039 } 2040 } 2041 2042 this.potionsNeedUpdate = false; 2043 } 2044 2045 if (this.rand.nextBoolean()) 2046 { 2047 var9 = this.dataWatcher.getWatchableObjectInt(8); 2048 2049 if (var9 > 0) 2050 { 2051 double var10 = (double)(var9 >> 16 & 255) / 255.0D; 2052 double var5 = (double)(var9 >> 8 & 255) / 255.0D; 2053 double var7 = (double)(var9 >> 0 & 255) / 255.0D; 2054 this.worldObj.spawnParticle("mobSpell", this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height - (double)this.yOffset, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, var10, var5, var7); 2055 } 2056 } 2057 } 2058 2059 public void clearActivePotions() 2060 { 2061 Iterator var1 = this.activePotionsMap.keySet().iterator(); 2062 2063 while (var1.hasNext()) 2064 { 2065 Integer var2 = (Integer)var1.next(); 2066 PotionEffect var3 = (PotionEffect)this.activePotionsMap.get(var2); 2067 2068 if (!this.worldObj.isRemote) 2069 { 2070 var1.remove(); 2071 this.onFinishedPotionEffect(var3); 2072 } 2073 } 2074 } 2075 2076 public Collection getActivePotionEffects() 2077 { 2078 return this.activePotionsMap.values(); 2079 } 2080 2081 public boolean isPotionActive(Potion par1Potion) 2082 { 2083 return this.activePotionsMap.containsKey(Integer.valueOf(par1Potion.id)); 2084 } 2085 2086 /** 2087 * returns the PotionEffect for the supplied Potion if it is active, null otherwise. 2088 */ 2089 public PotionEffect getActivePotionEffect(Potion par1Potion) 2090 { 2091 return (PotionEffect)this.activePotionsMap.get(Integer.valueOf(par1Potion.id)); 2092 } 2093 2094 /** 2095 * adds a PotionEffect to the entity 2096 */ 2097 public void addPotionEffect(PotionEffect par1PotionEffect) 2098 { 2099 if (this.isPotionApplicable(par1PotionEffect)) 2100 { 2101 if (this.activePotionsMap.containsKey(Integer.valueOf(par1PotionEffect.getPotionID()))) 2102 { 2103 ((PotionEffect)this.activePotionsMap.get(Integer.valueOf(par1PotionEffect.getPotionID()))).combine(par1PotionEffect); 2104 this.onChangedPotionEffect((PotionEffect)this.activePotionsMap.get(Integer.valueOf(par1PotionEffect.getPotionID()))); 2105 } 2106 else 2107 { 2108 this.activePotionsMap.put(Integer.valueOf(par1PotionEffect.getPotionID()), par1PotionEffect); 2109 this.onNewPotionEffect(par1PotionEffect); 2110 } 2111 } 2112 } 2113 2114 public boolean isPotionApplicable(PotionEffect par1PotionEffect) 2115 { 2116 if (this.getCreatureAttribute() == EnumCreatureAttribute.UNDEAD) 2117 { 2118 int var2 = par1PotionEffect.getPotionID(); 2119 2120 if (var2 == Potion.regeneration.id || var2 == Potion.poison.id) 2121 { 2122 return false; 2123 } 2124 } 2125 2126 return true; 2127 } 2128 2129 /** 2130 * Returns true if this entity is undead. 2131 */ 2132 public boolean isEntityUndead() 2133 { 2134 return this.getCreatureAttribute() == EnumCreatureAttribute.UNDEAD; 2135 } 2136 2137 @SideOnly(Side.CLIENT) 2138 2139 /** 2140 * input is the potion id to remove from the current active potion effects 2141 */ 2142 public void removePotionEffect(int par1) 2143 { 2144 this.activePotionsMap.remove(Integer.valueOf(par1)); 2145 } 2146 2147 protected void onNewPotionEffect(PotionEffect par1PotionEffect) 2148 { 2149 this.potionsNeedUpdate = true; 2150 } 2151 2152 protected void onChangedPotionEffect(PotionEffect par1PotionEffect) 2153 { 2154 this.potionsNeedUpdate = true; 2155 } 2156 2157 protected void onFinishedPotionEffect(PotionEffect par1PotionEffect) 2158 { 2159 this.potionsNeedUpdate = true; 2160 } 2161 2162 /** 2163 * This method returns a value to be applied directly to entity speed, this factor is less than 1 when a slowdown 2164 * potion effect is applied, more than 1 when a haste potion effect is applied and 2 for fleeing entities. 2165 */ 2166 protected float getSpeedModifier() 2167 { 2168 float var1 = 1.0F; 2169 2170 if (this.isPotionActive(Potion.moveSpeed)) 2171 { 2172 var1 *= 1.0F + 0.2F * (float)(this.getActivePotionEffect(Potion.moveSpeed).getAmplifier() + 1); 2173 } 2174 2175 if (this.isPotionActive(Potion.moveSlowdown)) 2176 { 2177 var1 *= 1.0F - 0.15F * (float)(this.getActivePotionEffect(Potion.moveSlowdown).getAmplifier() + 1); 2178 } 2179 2180 return var1; 2181 } 2182 2183 /** 2184 * Move the entity to the coordinates informed, but keep yaw/pitch values. 2185 */ 2186 public void setPositionAndUpdate(double par1, double par3, double par5) 2187 { 2188 this.setLocationAndAngles(par1, par3, par5, this.rotationYaw, this.rotationPitch); 2189 } 2190 2191 /** 2192 * If Animal, checks if the age timer is negative 2193 */ 2194 public boolean isChild() 2195 { 2196 return false; 2197 } 2198 2199 /** 2200 * Get this Entity's EnumCreatureAttribute 2201 */ 2202 public EnumCreatureAttribute getCreatureAttribute() 2203 { 2204 return EnumCreatureAttribute.UNDEFINED; 2205 } 2206 2207 /** 2208 * Renders broken item particles using the given ItemStack 2209 */ 2210 public void renderBrokenItemStack(ItemStack par1ItemStack) 2211 { 2212 this.worldObj.playSoundAtEntity(this, "random.break", 0.8F, 0.8F + this.worldObj.rand.nextFloat() * 0.4F); 2213 2214 for (int var2 = 0; var2 < 5; ++var2) 2215 { 2216 Vec3 var3 = Vec3.getVec3Pool().getVecFromPool(((double)this.rand.nextFloat() - 0.5D) * 0.1D, Math.random() * 0.1D + 0.1D, 0.0D); 2217 var3.rotateAroundX(-this.rotationPitch * (float)Math.PI / 180.0F); 2218 var3.rotateAroundY(-this.rotationYaw * (float)Math.PI / 180.0F); 2219 Vec3 var4 = Vec3.getVec3Pool().getVecFromPool(((double)this.rand.nextFloat() - 0.5D) * 0.3D, (double)(-this.rand.nextFloat()) * 0.6D - 0.3D, 0.6D); 2220 var4.rotateAroundX(-this.rotationPitch * (float)Math.PI / 180.0F); 2221 var4.rotateAroundY(-this.rotationYaw * (float)Math.PI / 180.0F); 2222 var4 = var4.addVector(this.posX, this.posY + (double)this.getEyeHeight(), this.posZ); 2223 this.worldObj.spawnParticle("iconcrack_" + par1ItemStack.getItem().shiftedIndex, var4.xCoord, var4.yCoord, var4.zCoord, var3.xCoord, var3.yCoord + 0.05D, var3.zCoord); 2224 } 2225 } 2226 }