001 package net.minecraft.entity.passive; 002 003 import cpw.mods.fml.relauncher.Side; 004 import cpw.mods.fml.relauncher.SideOnly; 005 import net.minecraft.block.BlockCloth; 006 import net.minecraft.entity.Entity; 007 import net.minecraft.entity.EntityAgeable; 008 import net.minecraft.entity.EntityLiving; 009 import net.minecraft.entity.ai.EntityAIAttackOnCollide; 010 import net.minecraft.entity.ai.EntityAIBeg; 011 import net.minecraft.entity.ai.EntityAIFollowOwner; 012 import net.minecraft.entity.ai.EntityAIHurtByTarget; 013 import net.minecraft.entity.ai.EntityAILeapAtTarget; 014 import net.minecraft.entity.ai.EntityAILookIdle; 015 import net.minecraft.entity.ai.EntityAIMate; 016 import net.minecraft.entity.ai.EntityAIOwnerHurtByTarget; 017 import net.minecraft.entity.ai.EntityAIOwnerHurtTarget; 018 import net.minecraft.entity.ai.EntityAISwimming; 019 import net.minecraft.entity.ai.EntityAITargetNonTamed; 020 import net.minecraft.entity.ai.EntityAIWander; 021 import net.minecraft.entity.ai.EntityAIWatchClosest; 022 import net.minecraft.entity.player.EntityPlayer; 023 import net.minecraft.entity.projectile.EntityArrow; 024 import net.minecraft.item.Item; 025 import net.minecraft.item.ItemFood; 026 import net.minecraft.item.ItemStack; 027 import net.minecraft.nbt.NBTTagCompound; 028 import net.minecraft.pathfinding.PathEntity; 029 import net.minecraft.util.DamageSource; 030 import net.minecraft.util.MathHelper; 031 import net.minecraft.world.World; 032 033 public class EntityWolf extends EntityTameable 034 { 035 private float field_70926_e; 036 private float field_70924_f; 037 038 /** true is the wolf is wet else false */ 039 private boolean isShaking; 040 private boolean field_70928_h; 041 042 /** 043 * This time increases while wolf is shaking and emitting water particles. 044 */ 045 private float timeWolfIsShaking; 046 private float prevTimeWolfIsShaking; 047 048 public EntityWolf(World par1World) 049 { 050 super(par1World); 051 this.texture = "/mob/wolf.png"; 052 this.setSize(0.6F, 0.8F); 053 this.moveSpeed = 0.3F; 054 this.getNavigator().setAvoidsWater(true); 055 this.tasks.addTask(1, new EntityAISwimming(this)); 056 this.tasks.addTask(2, this.aiSit); 057 this.tasks.addTask(3, new EntityAILeapAtTarget(this, 0.4F)); 058 this.tasks.addTask(4, new EntityAIAttackOnCollide(this, this.moveSpeed, true)); 059 this.tasks.addTask(5, new EntityAIFollowOwner(this, this.moveSpeed, 10.0F, 2.0F)); 060 this.tasks.addTask(6, new EntityAIMate(this, this.moveSpeed)); 061 this.tasks.addTask(7, new EntityAIWander(this, this.moveSpeed)); 062 this.tasks.addTask(8, new EntityAIBeg(this, 8.0F)); 063 this.tasks.addTask(9, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F)); 064 this.tasks.addTask(9, new EntityAILookIdle(this)); 065 this.targetTasks.addTask(1, new EntityAIOwnerHurtByTarget(this)); 066 this.targetTasks.addTask(2, new EntityAIOwnerHurtTarget(this)); 067 this.targetTasks.addTask(3, new EntityAIHurtByTarget(this, true)); 068 this.targetTasks.addTask(4, new EntityAITargetNonTamed(this, EntitySheep.class, 16.0F, 200, false)); 069 } 070 071 /** 072 * Returns true if the newer Entity AI code should be run 073 */ 074 public boolean isAIEnabled() 075 { 076 return true; 077 } 078 079 /** 080 * Sets the active target the Task system uses for tracking 081 */ 082 public void setAttackTarget(EntityLiving par1EntityLiving) 083 { 084 super.setAttackTarget(par1EntityLiving); 085 086 if (par1EntityLiving instanceof EntityPlayer) 087 { 088 this.setAngry(true); 089 } 090 } 091 092 /** 093 * main AI tick function, replaces updateEntityActionState 094 */ 095 protected void updateAITick() 096 { 097 this.dataWatcher.updateObject(18, Integer.valueOf(this.getHealth())); 098 } 099 100 public int getMaxHealth() 101 { 102 return this.isTamed() ? 20 : 8; 103 } 104 105 protected void entityInit() 106 { 107 super.entityInit(); 108 this.dataWatcher.addObject(18, new Integer(this.getHealth())); 109 this.dataWatcher.addObject(19, new Byte((byte)0)); 110 this.dataWatcher.addObject(20, new Byte((byte)BlockCloth.getBlockFromDye(1))); 111 } 112 113 /** 114 * Plays step sound at given x, y, z for the entity 115 */ 116 protected void playStepSound(int par1, int par2, int par3, int par4) 117 { 118 this.playSound("mob.wolf.step", 0.15F, 1.0F); 119 } 120 121 @SideOnly(Side.CLIENT) 122 123 /** 124 * Returns the texture's file path as a String. 125 */ 126 public String getTexture() 127 { 128 return this.isTamed() ? "/mob/wolf_tame.png" : (this.isAngry() ? "/mob/wolf_angry.png" : super.getTexture()); 129 } 130 131 /** 132 * (abstract) Protected helper method to write subclass entity data to NBT. 133 */ 134 public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound) 135 { 136 super.writeEntityToNBT(par1NBTTagCompound); 137 par1NBTTagCompound.setBoolean("Angry", this.isAngry()); 138 par1NBTTagCompound.setByte("CollarColor", (byte)this.getCollarColor()); 139 } 140 141 /** 142 * (abstract) Protected helper method to read subclass entity data from NBT. 143 */ 144 public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound) 145 { 146 super.readEntityFromNBT(par1NBTTagCompound); 147 this.setAngry(par1NBTTagCompound.getBoolean("Angry")); 148 149 if (par1NBTTagCompound.hasKey("CollarColor")) 150 { 151 this.setCollarColor(par1NBTTagCompound.getByte("CollarColor")); 152 } 153 } 154 155 /** 156 * Determines if an entity can be despawned, used on idle far away entities 157 */ 158 protected boolean canDespawn() 159 { 160 return this.isAngry(); 161 } 162 163 /** 164 * Returns the sound this mob makes while it's alive. 165 */ 166 protected String getLivingSound() 167 { 168 return this.isAngry() ? "mob.wolf.growl" : (this.rand.nextInt(3) == 0 ? (this.isTamed() && this.dataWatcher.getWatchableObjectInt(18) < 10 ? "mob.wolf.whine" : "mob.wolf.panting") : "mob.wolf.bark"); 169 } 170 171 /** 172 * Returns the sound this mob makes when it is hurt. 173 */ 174 protected String getHurtSound() 175 { 176 return "mob.wolf.hurt"; 177 } 178 179 /** 180 * Returns the sound this mob makes on death. 181 */ 182 protected String getDeathSound() 183 { 184 return "mob.wolf.death"; 185 } 186 187 /** 188 * Returns the volume for the sounds this mob makes. 189 */ 190 protected float getSoundVolume() 191 { 192 return 0.4F; 193 } 194 195 /** 196 * Returns the item ID for the item the mob drops on death. 197 */ 198 protected int getDropItemId() 199 { 200 return -1; 201 } 202 203 /** 204 * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons 205 * use this to react to sunlight and start to burn. 206 */ 207 public void onLivingUpdate() 208 { 209 super.onLivingUpdate(); 210 211 if (!this.worldObj.isRemote && this.isShaking && !this.field_70928_h && !this.hasPath() && this.onGround) 212 { 213 this.field_70928_h = true; 214 this.timeWolfIsShaking = 0.0F; 215 this.prevTimeWolfIsShaking = 0.0F; 216 this.worldObj.setEntityState(this, (byte)8); 217 } 218 } 219 220 /** 221 * Called to update the entity's position/logic. 222 */ 223 public void onUpdate() 224 { 225 super.onUpdate(); 226 this.field_70924_f = this.field_70926_e; 227 228 if (this.func_70922_bv()) 229 { 230 this.field_70926_e += (1.0F - this.field_70926_e) * 0.4F; 231 } 232 else 233 { 234 this.field_70926_e += (0.0F - this.field_70926_e) * 0.4F; 235 } 236 237 if (this.func_70922_bv()) 238 { 239 this.numTicksToChaseTarget = 10; 240 } 241 242 if (this.isWet()) 243 { 244 this.isShaking = true; 245 this.field_70928_h = false; 246 this.timeWolfIsShaking = 0.0F; 247 this.prevTimeWolfIsShaking = 0.0F; 248 } 249 else if ((this.isShaking || this.field_70928_h) && this.field_70928_h) 250 { 251 if (this.timeWolfIsShaking == 0.0F) 252 { 253 this.playSound("mob.wolf.shake", this.getSoundVolume(), (this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F + 1.0F); 254 } 255 256 this.prevTimeWolfIsShaking = this.timeWolfIsShaking; 257 this.timeWolfIsShaking += 0.05F; 258 259 if (this.prevTimeWolfIsShaking >= 2.0F) 260 { 261 this.isShaking = false; 262 this.field_70928_h = false; 263 this.prevTimeWolfIsShaking = 0.0F; 264 this.timeWolfIsShaking = 0.0F; 265 } 266 267 if (this.timeWolfIsShaking > 0.4F) 268 { 269 float var1 = (float)this.boundingBox.minY; 270 int var2 = (int)(MathHelper.sin((this.timeWolfIsShaking - 0.4F) * (float)Math.PI) * 7.0F); 271 272 for (int var3 = 0; var3 < var2; ++var3) 273 { 274 float var4 = (this.rand.nextFloat() * 2.0F - 1.0F) * this.width * 0.5F; 275 float var5 = (this.rand.nextFloat() * 2.0F - 1.0F) * this.width * 0.5F; 276 this.worldObj.spawnParticle("splash", this.posX + (double)var4, (double)(var1 + 0.8F), this.posZ + (double)var5, this.motionX, this.motionY, this.motionZ); 277 } 278 } 279 } 280 } 281 282 @SideOnly(Side.CLIENT) 283 public boolean getWolfShaking() 284 { 285 return this.isShaking; 286 } 287 288 @SideOnly(Side.CLIENT) 289 290 /** 291 * Used when calculating the amount of shading to apply while the wolf is shaking. 292 */ 293 public float getShadingWhileShaking(float par1) 294 { 295 return 0.75F + (this.prevTimeWolfIsShaking + (this.timeWolfIsShaking - this.prevTimeWolfIsShaking) * par1) / 2.0F * 0.25F; 296 } 297 298 @SideOnly(Side.CLIENT) 299 public float getShakeAngle(float par1, float par2) 300 { 301 float var3 = (this.prevTimeWolfIsShaking + (this.timeWolfIsShaking - this.prevTimeWolfIsShaking) * par1 + par2) / 1.8F; 302 303 if (var3 < 0.0F) 304 { 305 var3 = 0.0F; 306 } 307 else if (var3 > 1.0F) 308 { 309 var3 = 1.0F; 310 } 311 312 return MathHelper.sin(var3 * (float)Math.PI) * MathHelper.sin(var3 * (float)Math.PI * 11.0F) * 0.15F * (float)Math.PI; 313 } 314 315 @SideOnly(Side.CLIENT) 316 public float getInterestedAngle(float par1) 317 { 318 return (this.field_70924_f + (this.field_70926_e - this.field_70924_f) * par1) * 0.15F * (float)Math.PI; 319 } 320 321 public float getEyeHeight() 322 { 323 return this.height * 0.8F; 324 } 325 326 /** 327 * The speed it takes to move the entityliving's rotationPitch through the faceEntity method. This is only currently 328 * use in wolves. 329 */ 330 public int getVerticalFaceSpeed() 331 { 332 return this.isSitting() ? 20 : super.getVerticalFaceSpeed(); 333 } 334 335 /** 336 * Called when the entity is attacked. 337 */ 338 public boolean attackEntityFrom(DamageSource par1DamageSource, int par2) 339 { 340 if (this.isEntityInvulnerable()) 341 { 342 return false; 343 } 344 else 345 { 346 Entity var3 = par1DamageSource.getEntity(); 347 this.aiSit.setSitting(false); 348 349 if (var3 != null && !(var3 instanceof EntityPlayer) && !(var3 instanceof EntityArrow)) 350 { 351 par2 = (par2 + 1) / 2; 352 } 353 354 return super.attackEntityFrom(par1DamageSource, par2); 355 } 356 } 357 358 public boolean attackEntityAsMob(Entity par1Entity) 359 { 360 int var2 = this.isTamed() ? 4 : 2; 361 return par1Entity.attackEntityFrom(DamageSource.causeMobDamage(this), var2); 362 } 363 364 /** 365 * Called when a player interacts with a mob. e.g. gets milk from a cow, gets into the saddle on a pig. 366 */ 367 public boolean interact(EntityPlayer par1EntityPlayer) 368 { 369 ItemStack var2 = par1EntityPlayer.inventory.getCurrentItem(); 370 371 if (this.isTamed()) 372 { 373 if (var2 != null) 374 { 375 if (Item.itemsList[var2.itemID] instanceof ItemFood) 376 { 377 ItemFood var3 = (ItemFood)Item.itemsList[var2.itemID]; 378 379 if (var3.isWolfsFavoriteMeat() && this.dataWatcher.getWatchableObjectInt(18) < 20) 380 { 381 if (!par1EntityPlayer.capabilities.isCreativeMode) 382 { 383 --var2.stackSize; 384 } 385 386 this.heal(var3.getHealAmount()); 387 388 if (var2.stackSize <= 0) 389 { 390 par1EntityPlayer.inventory.setInventorySlotContents(par1EntityPlayer.inventory.currentItem, (ItemStack)null); 391 } 392 393 return true; 394 } 395 } 396 else if (var2.itemID == Item.dyePowder.itemID) 397 { 398 int var4 = BlockCloth.getBlockFromDye(var2.getItemDamage()); 399 400 if (var4 != this.getCollarColor()) 401 { 402 this.setCollarColor(var4); 403 404 if (!par1EntityPlayer.capabilities.isCreativeMode && --var2.stackSize <= 0) 405 { 406 par1EntityPlayer.inventory.setInventorySlotContents(par1EntityPlayer.inventory.currentItem, (ItemStack)null); 407 } 408 409 return true; 410 } 411 } 412 } 413 414 if (par1EntityPlayer.username.equalsIgnoreCase(this.getOwnerName()) && !this.worldObj.isRemote && !this.isBreedingItem(var2)) 415 { 416 this.aiSit.setSitting(!this.isSitting()); 417 this.isJumping = false; 418 this.setPathToEntity((PathEntity)null); 419 } 420 } 421 else if (var2 != null && var2.itemID == Item.bone.itemID && !this.isAngry()) 422 { 423 if (!par1EntityPlayer.capabilities.isCreativeMode) 424 { 425 --var2.stackSize; 426 } 427 428 if (var2.stackSize <= 0) 429 { 430 par1EntityPlayer.inventory.setInventorySlotContents(par1EntityPlayer.inventory.currentItem, (ItemStack)null); 431 } 432 433 if (!this.worldObj.isRemote) 434 { 435 if (this.rand.nextInt(3) == 0) 436 { 437 this.setTamed(true); 438 this.setPathToEntity((PathEntity)null); 439 this.setAttackTarget((EntityLiving)null); 440 this.aiSit.setSitting(true); 441 this.setEntityHealth(20); 442 this.setOwner(par1EntityPlayer.username); 443 this.playTameEffect(true); 444 this.worldObj.setEntityState(this, (byte)7); 445 } 446 else 447 { 448 this.playTameEffect(false); 449 this.worldObj.setEntityState(this, (byte)6); 450 } 451 } 452 453 return true; 454 } 455 456 return super.interact(par1EntityPlayer); 457 } 458 459 @SideOnly(Side.CLIENT) 460 public void handleHealthUpdate(byte par1) 461 { 462 if (par1 == 8) 463 { 464 this.field_70928_h = true; 465 this.timeWolfIsShaking = 0.0F; 466 this.prevTimeWolfIsShaking = 0.0F; 467 } 468 else 469 { 470 super.handleHealthUpdate(par1); 471 } 472 } 473 474 @SideOnly(Side.CLIENT) 475 public float getTailRotation() 476 { 477 return this.isAngry() ? 1.5393804F : (this.isTamed() ? (0.55F - (float)(20 - this.dataWatcher.getWatchableObjectInt(18)) * 0.02F) * (float)Math.PI : ((float)Math.PI / 5F)); 478 } 479 480 /** 481 * Checks if the parameter is an item which this animal can be fed to breed it (wheat, carrots or seeds depending on 482 * the animal type) 483 */ 484 public boolean isBreedingItem(ItemStack par1ItemStack) 485 { 486 return par1ItemStack == null ? false : (!(Item.itemsList[par1ItemStack.itemID] instanceof ItemFood) ? false : ((ItemFood)Item.itemsList[par1ItemStack.itemID]).isWolfsFavoriteMeat()); 487 } 488 489 /** 490 * Will return how many at most can spawn in a chunk at once. 491 */ 492 public int getMaxSpawnedInChunk() 493 { 494 return 8; 495 } 496 497 /** 498 * Determines whether this wolf is angry or not. 499 */ 500 public boolean isAngry() 501 { 502 return (this.dataWatcher.getWatchableObjectByte(16) & 2) != 0; 503 } 504 505 /** 506 * Sets whether this wolf is angry or not. 507 */ 508 public void setAngry(boolean par1) 509 { 510 byte var2 = this.dataWatcher.getWatchableObjectByte(16); 511 512 if (par1) 513 { 514 this.dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 | 2))); 515 } 516 else 517 { 518 this.dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 & -3))); 519 } 520 } 521 522 /** 523 * Return this wolf's collar color. 524 */ 525 public int getCollarColor() 526 { 527 return this.dataWatcher.getWatchableObjectByte(20) & 15; 528 } 529 530 /** 531 * Set this wolf's collar color. 532 */ 533 public void setCollarColor(int par1) 534 { 535 this.dataWatcher.updateObject(20, Byte.valueOf((byte)(par1 & 15))); 536 } 537 538 /** 539 * This function is used when two same-species animals in 'love mode' breed to generate the new baby animal. 540 */ 541 public EntityWolf spawnBabyAnimal(EntityAgeable par1EntityAgeable) 542 { 543 EntityWolf var2 = new EntityWolf(this.worldObj); 544 String var3 = this.getOwnerName(); 545 546 if (var3 != null && var3.trim().length() > 0) 547 { 548 var2.setOwner(var3); 549 var2.setTamed(true); 550 } 551 552 return var2; 553 } 554 555 public void func_70918_i(boolean par1) 556 { 557 byte var2 = this.dataWatcher.getWatchableObjectByte(19); 558 559 if (par1) 560 { 561 this.dataWatcher.updateObject(19, Byte.valueOf((byte)1)); 562 } 563 else 564 { 565 this.dataWatcher.updateObject(19, Byte.valueOf((byte)0)); 566 } 567 } 568 569 /** 570 * Returns true if the mob is currently able to mate with the specified mob. 571 */ 572 public boolean canMateWith(EntityAnimal par1EntityAnimal) 573 { 574 if (par1EntityAnimal == this) 575 { 576 return false; 577 } 578 else if (!this.isTamed()) 579 { 580 return false; 581 } 582 else if (!(par1EntityAnimal instanceof EntityWolf)) 583 { 584 return false; 585 } 586 else 587 { 588 EntityWolf var2 = (EntityWolf)par1EntityAnimal; 589 return !var2.isTamed() ? false : (var2.isSitting() ? false : this.isInLove() && var2.isInLove()); 590 } 591 } 592 593 public boolean func_70922_bv() 594 { 595 return this.dataWatcher.getWatchableObjectByte(19) == 1; 596 } 597 598 public EntityAgeable createChild(EntityAgeable par1EntityAgeable) 599 { 600 return this.spawnBabyAnimal(par1EntityAgeable); 601 } 602 }