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