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    }