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            String var3 = this.getOwnerName();
518    
519            if (var3 != null && var3.trim().length() > 0)
520            {
521                var2.setOwner(var3);
522                var2.setTamed(true);
523            }
524    
525            return var2;
526        }
527    
528        public void func_70918_i(boolean par1)
529        {
530            byte var2 = this.dataWatcher.getWatchableObjectByte(19);
531    
532            if (par1)
533            {
534                this.dataWatcher.updateObject(19, Byte.valueOf((byte)1));
535            }
536            else
537            {
538                this.dataWatcher.updateObject(19, Byte.valueOf((byte)0));
539            }
540        }
541    
542        /**
543         * Returns true if the mob is currently able to mate with the specified mob.
544         */
545        public boolean canMateWith(EntityAnimal par1EntityAnimal)
546        {
547            if (par1EntityAnimal == this)
548            {
549                return false;
550            }
551            else if (!this.isTamed())
552            {
553                return false;
554            }
555            else if (!(par1EntityAnimal instanceof EntityWolf))
556            {
557                return false;
558            }
559            else
560            {
561                EntityWolf var2 = (EntityWolf)par1EntityAnimal;
562                return !var2.isTamed() ? false : (var2.isSitting() ? false : this.isInLove() && var2.isInLove());
563            }
564        }
565    
566        public boolean func_70922_bv()
567        {
568            return this.dataWatcher.getWatchableObjectByte(19) == 1;
569        }
570    
571        public EntityAgeable func_90011_a(EntityAgeable par1EntityAgeable)
572        {
573            return this.spawnBabyAnimal(par1EntityAgeable);
574        }
575    }