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 var1, int var2, int var3, int var4)
090 {
091 this.worldObj.playSoundAtEntity(this, "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.func_82186_bH());
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.func_82185_r(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.worldObj.playSoundAtEntity(this, "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 Entity var3 = par1DamageSource.getEntity();
314 this.aiSit.setSitting(false);
315
316 if (var3 != null && !(var3 instanceof EntityPlayer) && !(var3 instanceof EntityArrow))
317 {
318 par2 = (par2 + 1) / 2;
319 }
320
321 return super.attackEntityFrom(par1DamageSource, par2);
322 }
323
324 public boolean attackEntityAsMob(Entity par1Entity)
325 {
326 int var2 = this.isTamed() ? 4 : 2;
327 return par1Entity.attackEntityFrom(DamageSource.causeMobDamage(this), var2);
328 }
329
330 /**
331 * Called when a player interacts with a mob. e.g. gets milk from a cow, gets into the saddle on a pig.
332 */
333 public boolean interact(EntityPlayer par1EntityPlayer)
334 {
335 ItemStack var2 = par1EntityPlayer.inventory.getCurrentItem();
336
337 if (this.isTamed())
338 {
339 if (var2 != null)
340 {
341 if (Item.itemsList[var2.itemID] instanceof ItemFood)
342 {
343 ItemFood var3 = (ItemFood)Item.itemsList[var2.itemID];
344
345 if (var3.isWolfsFavoriteMeat() && this.dataWatcher.getWatchableObjectInt(18) < 20)
346 {
347 if (!par1EntityPlayer.capabilities.isCreativeMode)
348 {
349 --var2.stackSize;
350 }
351
352 this.heal(var3.getHealAmount());
353
354 if (var2.stackSize <= 0)
355 {
356 par1EntityPlayer.inventory.setInventorySlotContents(par1EntityPlayer.inventory.currentItem, (ItemStack)null);
357 }
358
359 return true;
360 }
361 }
362 else if (var2.itemID == Item.dyePowder.shiftedIndex)
363 {
364 int var4 = BlockCloth.getBlockFromDye(var2.getItemDamage());
365
366 if (var4 != this.func_82186_bH())
367 {
368 this.func_82185_r(var4);
369
370 if (!par1EntityPlayer.capabilities.isCreativeMode && var2.stackSize-- <= 0)
371 {
372 par1EntityPlayer.inventory.setInventorySlotContents(par1EntityPlayer.inventory.currentItem, (ItemStack)null);
373 }
374
375 return true;
376 }
377 }
378 }
379
380 if (par1EntityPlayer.username.equalsIgnoreCase(this.getOwnerName()) && !this.worldObj.isRemote && !this.isWheat(var2))
381 {
382 this.aiSit.setSitting(!this.isSitting());
383 this.isJumping = false;
384 this.setPathToEntity((PathEntity)null);
385 }
386 }
387 else if (var2 != null && var2.itemID == Item.bone.shiftedIndex && !this.isAngry())
388 {
389 if (!par1EntityPlayer.capabilities.isCreativeMode)
390 {
391 --var2.stackSize;
392 }
393
394 if (var2.stackSize <= 0)
395 {
396 par1EntityPlayer.inventory.setInventorySlotContents(par1EntityPlayer.inventory.currentItem, (ItemStack)null);
397 }
398
399 if (!this.worldObj.isRemote)
400 {
401 if (this.rand.nextInt(3) == 0)
402 {
403 this.setTamed(true);
404 this.setPathToEntity((PathEntity)null);
405 this.setAttackTarget((EntityLiving)null);
406 this.aiSit.setSitting(true);
407 this.setEntityHealth(20);
408 this.setOwner(par1EntityPlayer.username);
409 this.playTameEffect(true);
410 this.worldObj.setEntityState(this, (byte)7);
411 }
412 else
413 {
414 this.playTameEffect(false);
415 this.worldObj.setEntityState(this, (byte)6);
416 }
417 }
418
419 return true;
420 }
421
422 return super.interact(par1EntityPlayer);
423 }
424
425 /**
426 * Checks if the parameter is an wheat item.
427 */
428 public boolean isWheat(ItemStack par1ItemStack)
429 {
430 return par1ItemStack == null ? false : (!(Item.itemsList[par1ItemStack.itemID] instanceof ItemFood) ? false : ((ItemFood)Item.itemsList[par1ItemStack.itemID]).isWolfsFavoriteMeat());
431 }
432
433 @SideOnly(Side.CLIENT)
434 public void handleHealthUpdate(byte par1)
435 {
436 if (par1 == 8)
437 {
438 this.field_70928_h = true;
439 this.timeWolfIsShaking = 0.0F;
440 this.prevTimeWolfIsShaking = 0.0F;
441 }
442 else
443 {
444 super.handleHealthUpdate(par1);
445 }
446 }
447
448 @SideOnly(Side.CLIENT)
449 public float getTailRotation()
450 {
451 return this.isAngry() ? 1.5393804F : (this.isTamed() ? (0.55F - (float)(20 - this.dataWatcher.getWatchableObjectInt(18)) * 0.02F) * (float)Math.PI : ((float)Math.PI / 5F));
452 }
453
454 /**
455 * Will return how many at most can spawn in a chunk at once.
456 */
457 public int getMaxSpawnedInChunk()
458 {
459 return 8;
460 }
461
462 /**
463 * Determines whether this wolf is angry or not.
464 */
465 public boolean isAngry()
466 {
467 return (this.dataWatcher.getWatchableObjectByte(16) & 2) != 0;
468 }
469
470 /**
471 * Sets whether this wolf is angry or not.
472 */
473 public void setAngry(boolean par1)
474 {
475 byte var2 = this.dataWatcher.getWatchableObjectByte(16);
476
477 if (par1)
478 {
479 this.dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 | 2)));
480 }
481 else
482 {
483 this.dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 & -3)));
484 }
485 }
486
487 public int func_82186_bH()
488 {
489 return this.dataWatcher.getWatchableObjectByte(20) & 15;
490 }
491
492 public void func_82185_r(int par1)
493 {
494 this.dataWatcher.updateObject(20, Byte.valueOf((byte)(par1 & 15)));
495 }
496
497 /**
498 * This function is used when two same-species animals in 'love mode' breed to generate the new baby animal.
499 */
500 public EntityAnimal spawnBabyAnimal(EntityAnimal par1EntityAnimal)
501 {
502 EntityWolf var2 = new EntityWolf(this.worldObj);
503 var2.setOwner(this.getOwnerName());
504 var2.setTamed(true);
505 return var2;
506 }
507
508 public void func_70918_i(boolean par1)
509 {
510 byte var2 = this.dataWatcher.getWatchableObjectByte(19);
511
512 if (par1)
513 {
514 this.dataWatcher.updateObject(19, Byte.valueOf((byte)1));
515 }
516 else
517 {
518 this.dataWatcher.updateObject(19, Byte.valueOf((byte)0));
519 }
520 }
521
522 /**
523 * Returns true if the mob is currently able to mate with the specified mob.
524 */
525 public boolean canMateWith(EntityAnimal par1EntityAnimal)
526 {
527 if (par1EntityAnimal == this)
528 {
529 return false;
530 }
531 else if (!this.isTamed())
532 {
533 return false;
534 }
535 else if (!(par1EntityAnimal instanceof EntityWolf))
536 {
537 return false;
538 }
539 else
540 {
541 EntityWolf var2 = (EntityWolf)par1EntityAnimal;
542 return !var2.isTamed() ? false : (var2.isSitting() ? false : this.isInLove() && var2.isInLove());
543 }
544 }
545
546 public boolean func_70922_bv()
547 {
548 return this.dataWatcher.getWatchableObjectByte(19) == 1;
549 }
550 }