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