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    }