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 }
084
085 /**
086 * returns if this entity triggers Block.onEntityWalking on the blocks they walk on. used for spiders and wolves to
087 * prevent them from trampling crops
088 */
089 protected boolean canTriggerWalking()
090 {
091 return false;
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 }
112
113 /**
114 * (abstract) Protected helper method to read subclass entity data from NBT.
115 */
116 public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound)
117 {
118 super.readEntityFromNBT(par1NBTTagCompound);
119 this.setAngry(par1NBTTagCompound.getBoolean("Angry"));
120 }
121
122 /**
123 * Determines if an entity can be despawned, used on idle far away entities
124 */
125 protected boolean canDespawn()
126 {
127 return this.isAngry();
128 }
129
130 /**
131 * Returns the sound this mob makes while it's alive.
132 */
133 protected String getLivingSound()
134 {
135 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");
136 }
137
138 /**
139 * Returns the sound this mob makes when it is hurt.
140 */
141 protected String getHurtSound()
142 {
143 return "mob.wolf.hurt";
144 }
145
146 /**
147 * Returns the sound this mob makes on death.
148 */
149 protected String getDeathSound()
150 {
151 return "mob.wolf.death";
152 }
153
154 /**
155 * Returns the volume for the sounds this mob makes.
156 */
157 protected float getSoundVolume()
158 {
159 return 0.4F;
160 }
161
162 /**
163 * Returns the item ID for the item the mob drops on death.
164 */
165 protected int getDropItemId()
166 {
167 return -1;
168 }
169
170 /**
171 * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons
172 * use this to react to sunlight and start to burn.
173 */
174 public void onLivingUpdate()
175 {
176 super.onLivingUpdate();
177
178 if (!this.worldObj.isRemote && this.isShaking && !this.field_70928_h && !this.hasPath() && this.onGround)
179 {
180 this.field_70928_h = true;
181 this.timeWolfIsShaking = 0.0F;
182 this.prevTimeWolfIsShaking = 0.0F;
183 this.worldObj.setEntityState(this, (byte)8);
184 }
185 }
186
187 /**
188 * Called to update the entity's position/logic.
189 */
190 public void onUpdate()
191 {
192 super.onUpdate();
193 this.field_70924_f = this.field_70926_e;
194
195 if (this.func_70922_bv())
196 {
197 this.field_70926_e += (1.0F - this.field_70926_e) * 0.4F;
198 }
199 else
200 {
201 this.field_70926_e += (0.0F - this.field_70926_e) * 0.4F;
202 }
203
204 if (this.func_70922_bv())
205 {
206 this.numTicksToChaseTarget = 10;
207 }
208
209 if (this.isWet())
210 {
211 this.isShaking = true;
212 this.field_70928_h = false;
213 this.timeWolfIsShaking = 0.0F;
214 this.prevTimeWolfIsShaking = 0.0F;
215 }
216 else if ((this.isShaking || this.field_70928_h) && this.field_70928_h)
217 {
218 if (this.timeWolfIsShaking == 0.0F)
219 {
220 this.worldObj.playSoundAtEntity(this, "mob.wolf.shake", this.getSoundVolume(), (this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F + 1.0F);
221 }
222
223 this.prevTimeWolfIsShaking = this.timeWolfIsShaking;
224 this.timeWolfIsShaking += 0.05F;
225
226 if (this.prevTimeWolfIsShaking >= 2.0F)
227 {
228 this.isShaking = false;
229 this.field_70928_h = false;
230 this.prevTimeWolfIsShaking = 0.0F;
231 this.timeWolfIsShaking = 0.0F;
232 }
233
234 if (this.timeWolfIsShaking > 0.4F)
235 {
236 float var1 = (float)this.boundingBox.minY;
237 int var2 = (int)(MathHelper.sin((this.timeWolfIsShaking - 0.4F) * (float)Math.PI) * 7.0F);
238
239 for (int var3 = 0; var3 < var2; ++var3)
240 {
241 float var4 = (this.rand.nextFloat() * 2.0F - 1.0F) * this.width * 0.5F;
242 float var5 = (this.rand.nextFloat() * 2.0F - 1.0F) * this.width * 0.5F;
243 this.worldObj.spawnParticle("splash", this.posX + (double)var4, (double)(var1 + 0.8F), this.posZ + (double)var5, this.motionX, this.motionY, this.motionZ);
244 }
245 }
246 }
247 }
248
249 @SideOnly(Side.CLIENT)
250 public boolean getWolfShaking()
251 {
252 return this.isShaking;
253 }
254
255 @SideOnly(Side.CLIENT)
256
257 /**
258 * Used when calculating the amount of shading to apply while the wolf is shaking.
259 */
260 public float getShadingWhileShaking(float par1)
261 {
262 return 0.75F + (this.prevTimeWolfIsShaking + (this.timeWolfIsShaking - this.prevTimeWolfIsShaking) * par1) / 2.0F * 0.25F;
263 }
264
265 @SideOnly(Side.CLIENT)
266 public float getShakeAngle(float par1, float par2)
267 {
268 float var3 = (this.prevTimeWolfIsShaking + (this.timeWolfIsShaking - this.prevTimeWolfIsShaking) * par1 + par2) / 1.8F;
269
270 if (var3 < 0.0F)
271 {
272 var3 = 0.0F;
273 }
274 else if (var3 > 1.0F)
275 {
276 var3 = 1.0F;
277 }
278
279 return MathHelper.sin(var3 * (float)Math.PI) * MathHelper.sin(var3 * (float)Math.PI * 11.0F) * 0.15F * (float)Math.PI;
280 }
281
282 @SideOnly(Side.CLIENT)
283 public float getInterestedAngle(float par1)
284 {
285 return (this.field_70924_f + (this.field_70926_e - this.field_70924_f) * par1) * 0.15F * (float)Math.PI;
286 }
287
288 public float getEyeHeight()
289 {
290 return this.height * 0.8F;
291 }
292
293 /**
294 * The speed it takes to move the entityliving's rotationPitch through the faceEntity method. This is only currently
295 * use in wolves.
296 */
297 public int getVerticalFaceSpeed()
298 {
299 return this.isSitting() ? 20 : super.getVerticalFaceSpeed();
300 }
301
302 /**
303 * Called when the entity is attacked.
304 */
305 public boolean attackEntityFrom(DamageSource par1DamageSource, int par2)
306 {
307 Entity var3 = par1DamageSource.getEntity();
308 this.aiSit.setSitting(false);
309
310 if (var3 != null && !(var3 instanceof EntityPlayer) && !(var3 instanceof EntityArrow))
311 {
312 par2 = (par2 + 1) / 2;
313 }
314
315 return super.attackEntityFrom(par1DamageSource, par2);
316 }
317
318 public boolean attackEntityAsMob(Entity par1Entity)
319 {
320 int var2 = this.isTamed() ? 4 : 2;
321 return par1Entity.attackEntityFrom(DamageSource.causeMobDamage(this), var2);
322 }
323
324 /**
325 * Called when a player interacts with a mob. e.g. gets milk from a cow, gets into the saddle on a pig.
326 */
327 public boolean interact(EntityPlayer par1EntityPlayer)
328 {
329 ItemStack var2 = par1EntityPlayer.inventory.getCurrentItem();
330
331 if (this.isTamed())
332 {
333 if (var2 != null && Item.itemsList[var2.itemID] instanceof ItemFood)
334 {
335 ItemFood var3 = (ItemFood)Item.itemsList[var2.itemID];
336
337 if (var3.isWolfsFavoriteMeat() && this.dataWatcher.getWatchableObjectInt(18) < 20)
338 {
339 if (!par1EntityPlayer.capabilities.isCreativeMode)
340 {
341 --var2.stackSize;
342 }
343
344 this.heal(var3.getHealAmount());
345
346 if (var2.stackSize <= 0)
347 {
348 par1EntityPlayer.inventory.setInventorySlotContents(par1EntityPlayer.inventory.currentItem, (ItemStack)null);
349 }
350
351 return true;
352 }
353 }
354
355 if (par1EntityPlayer.username.equalsIgnoreCase(this.getOwnerName()) && !this.worldObj.isRemote && !this.isWheat(var2))
356 {
357 this.aiSit.setSitting(!this.isSitting());
358 this.isJumping = false;
359 this.setPathToEntity((PathEntity)null);
360 }
361 }
362 else if (var2 != null && var2.itemID == Item.bone.shiftedIndex && !this.isAngry())
363 {
364 if (!par1EntityPlayer.capabilities.isCreativeMode)
365 {
366 --var2.stackSize;
367 }
368
369 if (var2.stackSize <= 0)
370 {
371 par1EntityPlayer.inventory.setInventorySlotContents(par1EntityPlayer.inventory.currentItem, (ItemStack)null);
372 }
373
374 if (!this.worldObj.isRemote)
375 {
376 if (this.rand.nextInt(3) == 0)
377 {
378 this.setTamed(true);
379 this.setPathToEntity((PathEntity)null);
380 this.setAttackTarget((EntityLiving)null);
381 this.aiSit.setSitting(true);
382 this.setEntityHealth(20);
383 this.setOwner(par1EntityPlayer.username);
384 this.playTameEffect(true);
385 this.worldObj.setEntityState(this, (byte)7);
386 }
387 else
388 {
389 this.playTameEffect(false);
390 this.worldObj.setEntityState(this, (byte)6);
391 }
392 }
393
394 return true;
395 }
396
397 return super.interact(par1EntityPlayer);
398 }
399
400 @SideOnly(Side.CLIENT)
401 public void handleHealthUpdate(byte par1)
402 {
403 if (par1 == 8)
404 {
405 this.field_70928_h = true;
406 this.timeWolfIsShaking = 0.0F;
407 this.prevTimeWolfIsShaking = 0.0F;
408 }
409 else
410 {
411 super.handleHealthUpdate(par1);
412 }
413 }
414
415 @SideOnly(Side.CLIENT)
416 public float getTailRotation()
417 {
418 return this.isAngry() ? 1.5393804F : (this.isTamed() ? (0.55F - (float)(20 - this.dataWatcher.getWatchableObjectInt(18)) * 0.02F) * (float)Math.PI : ((float)Math.PI / 5F));
419 }
420
421 /**
422 * Checks if the parameter is an wheat item.
423 */
424 public boolean isWheat(ItemStack par1ItemStack)
425 {
426 return par1ItemStack == null ? false : (!(Item.itemsList[par1ItemStack.itemID] instanceof ItemFood) ? false : ((ItemFood)Item.itemsList[par1ItemStack.itemID]).isWolfsFavoriteMeat());
427 }
428
429 /**
430 * Will return how many at most can spawn in a chunk at once.
431 */
432 public int getMaxSpawnedInChunk()
433 {
434 return 8;
435 }
436
437 /**
438 * Determines whether this wolf is angry or not.
439 */
440 public boolean isAngry()
441 {
442 return (this.dataWatcher.getWatchableObjectByte(16) & 2) != 0;
443 }
444
445 /**
446 * Sets whether this wolf is angry or not.
447 */
448 public void setAngry(boolean par1)
449 {
450 byte var2 = this.dataWatcher.getWatchableObjectByte(16);
451
452 if (par1)
453 {
454 this.dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 | 2)));
455 }
456 else
457 {
458 this.dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 & -3)));
459 }
460 }
461
462 /**
463 * This function is used when two same-species animals in 'love mode' breed to generate the new baby animal.
464 */
465 public EntityAnimal spawnBabyAnimal(EntityAnimal par1EntityAnimal)
466 {
467 EntityWolf var2 = new EntityWolf(this.worldObj);
468 var2.setOwner(this.getOwnerName());
469 var2.setTamed(true);
470 return var2;
471 }
472
473 public void func_70918_i(boolean par1)
474 {
475 byte var2 = this.dataWatcher.getWatchableObjectByte(19);
476
477 if (par1)
478 {
479 this.dataWatcher.updateObject(19, Byte.valueOf((byte)1));
480 }
481 else
482 {
483 this.dataWatcher.updateObject(19, Byte.valueOf((byte)0));
484 }
485 }
486
487 /**
488 * Returns true if the mob is currently able to mate with the specified mob.
489 */
490 public boolean canMateWith(EntityAnimal par1EntityAnimal)
491 {
492 if (par1EntityAnimal == this)
493 {
494 return false;
495 }
496 else if (!this.isTamed())
497 {
498 return false;
499 }
500 else if (!(par1EntityAnimal instanceof EntityWolf))
501 {
502 return false;
503 }
504 else
505 {
506 EntityWolf var2 = (EntityWolf)par1EntityAnimal;
507 return !var2.isTamed() ? false : (var2.isSitting() ? false : this.isInLove() && var2.isInLove());
508 }
509 }
510
511 public boolean func_70922_bv()
512 {
513 return this.dataWatcher.getWatchableObjectByte(19) == 1;
514 }
515 }