001    package net.minecraft.entity.passive;
002    
003    import cpw.mods.fml.relauncher.Side;
004    import cpw.mods.fml.relauncher.SideOnly;
005    import net.minecraft.block.BlockCloth;
006    import net.minecraft.entity.Entity;
007    import net.minecraft.entity.EntityAgeable;
008    import net.minecraft.entity.EntityLiving;
009    import net.minecraft.entity.ai.EntityAIAttackOnCollide;
010    import net.minecraft.entity.ai.EntityAIBeg;
011    import net.minecraft.entity.ai.EntityAIFollowOwner;
012    import net.minecraft.entity.ai.EntityAIHurtByTarget;
013    import net.minecraft.entity.ai.EntityAILeapAtTarget;
014    import net.minecraft.entity.ai.EntityAILookIdle;
015    import net.minecraft.entity.ai.EntityAIMate;
016    import net.minecraft.entity.ai.EntityAIOwnerHurtByTarget;
017    import net.minecraft.entity.ai.EntityAIOwnerHurtTarget;
018    import net.minecraft.entity.ai.EntityAISwimming;
019    import net.minecraft.entity.ai.EntityAITargetNonTamed;
020    import net.minecraft.entity.ai.EntityAIWander;
021    import net.minecraft.entity.ai.EntityAIWatchClosest;
022    import net.minecraft.entity.player.EntityPlayer;
023    import net.minecraft.entity.projectile.EntityArrow;
024    import net.minecraft.item.Item;
025    import net.minecraft.item.ItemFood;
026    import net.minecraft.item.ItemStack;
027    import net.minecraft.nbt.NBTTagCompound;
028    import net.minecraft.pathfinding.PathEntity;
029    import net.minecraft.util.DamageSource;
030    import net.minecraft.util.MathHelper;
031    import net.minecraft.world.World;
032    
033    public class EntityWolf extends EntityTameable
034    {
035        private float field_70926_e;
036        private float field_70924_f;
037    
038        /** true is the wolf is wet else false */
039        private boolean isShaking;
040        private boolean field_70928_h;
041    
042        /**
043         * This time increases while wolf is shaking and emitting water particles.
044         */
045        private float timeWolfIsShaking;
046        private float prevTimeWolfIsShaking;
047    
048        public EntityWolf(World par1World)
049        {
050            super(par1World);
051            this.texture = "/mob/wolf.png";
052            this.setSize(0.6F, 0.8F);
053            this.moveSpeed = 0.3F;
054            this.getNavigator().setAvoidsWater(true);
055            this.tasks.addTask(1, new EntityAISwimming(this));
056            this.tasks.addTask(2, this.aiSit);
057            this.tasks.addTask(3, new EntityAILeapAtTarget(this, 0.4F));
058            this.tasks.addTask(4, new EntityAIAttackOnCollide(this, this.moveSpeed, true));
059            this.tasks.addTask(5, new EntityAIFollowOwner(this, this.moveSpeed, 10.0F, 2.0F));
060            this.tasks.addTask(6, new EntityAIMate(this, this.moveSpeed));
061            this.tasks.addTask(7, new EntityAIWander(this, this.moveSpeed));
062            this.tasks.addTask(8, new EntityAIBeg(this, 8.0F));
063            this.tasks.addTask(9, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F));
064            this.tasks.addTask(9, new EntityAILookIdle(this));
065            this.targetTasks.addTask(1, new EntityAIOwnerHurtByTarget(this));
066            this.targetTasks.addTask(2, new EntityAIOwnerHurtTarget(this));
067            this.targetTasks.addTask(3, new EntityAIHurtByTarget(this, true));
068            this.targetTasks.addTask(4, new EntityAITargetNonTamed(this, EntitySheep.class, 16.0F, 200, false));
069        }
070    
071        /**
072         * Returns true if the newer Entity AI code should be run
073         */
074        public boolean isAIEnabled()
075        {
076            return true;
077        }
078    
079        /**
080         * Sets the active target the Task system uses for tracking
081         */
082        public void setAttackTarget(EntityLiving par1EntityLiving)
083        {
084            super.setAttackTarget(par1EntityLiving);
085    
086            if (par1EntityLiving instanceof EntityPlayer)
087            {
088                this.setAngry(true);
089            }
090        }
091    
092        /**
093         * main AI tick function, replaces updateEntityActionState
094         */
095        protected void updateAITick()
096        {
097            this.dataWatcher.updateObject(18, Integer.valueOf(this.getHealth()));
098        }
099    
100        public int getMaxHealth()
101        {
102            return this.isTamed() ? 20 : 8;
103        }
104    
105        protected void entityInit()
106        {
107            super.entityInit();
108            this.dataWatcher.addObject(18, new Integer(this.getHealth()));
109            this.dataWatcher.addObject(19, new Byte((byte)0));
110            this.dataWatcher.addObject(20, new Byte((byte)BlockCloth.getBlockFromDye(1)));
111        }
112    
113        /**
114         * Plays step sound at given x, y, z for the entity
115         */
116        protected void playStepSound(int par1, int par2, int par3, int par4)
117        {
118            this.playSound("mob.wolf.step", 0.15F, 1.0F);
119        }
120    
121        @SideOnly(Side.CLIENT)
122    
123        /**
124         * Returns the texture's file path as a String.
125         */
126        public String getTexture()
127        {
128            return this.isTamed() ? "/mob/wolf_tame.png" : (this.isAngry() ? "/mob/wolf_angry.png" : super.getTexture());
129        }
130    
131        /**
132         * (abstract) Protected helper method to write subclass entity data to NBT.
133         */
134        public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound)
135        {
136            super.writeEntityToNBT(par1NBTTagCompound);
137            par1NBTTagCompound.setBoolean("Angry", this.isAngry());
138            par1NBTTagCompound.setByte("CollarColor", (byte)this.getCollarColor());
139        }
140    
141        /**
142         * (abstract) Protected helper method to read subclass entity data from NBT.
143         */
144        public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound)
145        {
146            super.readEntityFromNBT(par1NBTTagCompound);
147            this.setAngry(par1NBTTagCompound.getBoolean("Angry"));
148    
149            if (par1NBTTagCompound.hasKey("CollarColor"))
150            {
151                this.setCollarColor(par1NBTTagCompound.getByte("CollarColor"));
152            }
153        }
154    
155        /**
156         * Determines if an entity can be despawned, used on idle far away entities
157         */
158        protected boolean canDespawn()
159        {
160            return this.isAngry();
161        }
162    
163        /**
164         * Returns the sound this mob makes while it's alive.
165         */
166        protected String getLivingSound()
167        {
168            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");
169        }
170    
171        /**
172         * Returns the sound this mob makes when it is hurt.
173         */
174        protected String getHurtSound()
175        {
176            return "mob.wolf.hurt";
177        }
178    
179        /**
180         * Returns the sound this mob makes on death.
181         */
182        protected String getDeathSound()
183        {
184            return "mob.wolf.death";
185        }
186    
187        /**
188         * Returns the volume for the sounds this mob makes.
189         */
190        protected float getSoundVolume()
191        {
192            return 0.4F;
193        }
194    
195        /**
196         * Returns the item ID for the item the mob drops on death.
197         */
198        protected int getDropItemId()
199        {
200            return -1;
201        }
202    
203        /**
204         * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons
205         * use this to react to sunlight and start to burn.
206         */
207        public void onLivingUpdate()
208        {
209            super.onLivingUpdate();
210    
211            if (!this.worldObj.isRemote && this.isShaking && !this.field_70928_h && !this.hasPath() && this.onGround)
212            {
213                this.field_70928_h = true;
214                this.timeWolfIsShaking = 0.0F;
215                this.prevTimeWolfIsShaking = 0.0F;
216                this.worldObj.setEntityState(this, (byte)8);
217            }
218        }
219    
220        /**
221         * Called to update the entity's position/logic.
222         */
223        public void onUpdate()
224        {
225            super.onUpdate();
226            this.field_70924_f = this.field_70926_e;
227    
228            if (this.func_70922_bv())
229            {
230                this.field_70926_e += (1.0F - this.field_70926_e) * 0.4F;
231            }
232            else
233            {
234                this.field_70926_e += (0.0F - this.field_70926_e) * 0.4F;
235            }
236    
237            if (this.func_70922_bv())
238            {
239                this.numTicksToChaseTarget = 10;
240            }
241    
242            if (this.isWet())
243            {
244                this.isShaking = true;
245                this.field_70928_h = false;
246                this.timeWolfIsShaking = 0.0F;
247                this.prevTimeWolfIsShaking = 0.0F;
248            }
249            else if ((this.isShaking || this.field_70928_h) && this.field_70928_h)
250            {
251                if (this.timeWolfIsShaking == 0.0F)
252                {
253                    this.playSound("mob.wolf.shake", this.getSoundVolume(), (this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F + 1.0F);
254                }
255    
256                this.prevTimeWolfIsShaking = this.timeWolfIsShaking;
257                this.timeWolfIsShaking += 0.05F;
258    
259                if (this.prevTimeWolfIsShaking >= 2.0F)
260                {
261                    this.isShaking = false;
262                    this.field_70928_h = false;
263                    this.prevTimeWolfIsShaking = 0.0F;
264                    this.timeWolfIsShaking = 0.0F;
265                }
266    
267                if (this.timeWolfIsShaking > 0.4F)
268                {
269                    float var1 = (float)this.boundingBox.minY;
270                    int var2 = (int)(MathHelper.sin((this.timeWolfIsShaking - 0.4F) * (float)Math.PI) * 7.0F);
271    
272                    for (int var3 = 0; var3 < var2; ++var3)
273                    {
274                        float var4 = (this.rand.nextFloat() * 2.0F - 1.0F) * this.width * 0.5F;
275                        float var5 = (this.rand.nextFloat() * 2.0F - 1.0F) * this.width * 0.5F;
276                        this.worldObj.spawnParticle("splash", this.posX + (double)var4, (double)(var1 + 0.8F), this.posZ + (double)var5, this.motionX, this.motionY, this.motionZ);
277                    }
278                }
279            }
280        }
281    
282        @SideOnly(Side.CLIENT)
283        public boolean getWolfShaking()
284        {
285            return this.isShaking;
286        }
287    
288        @SideOnly(Side.CLIENT)
289    
290        /**
291         * Used when calculating the amount of shading to apply while the wolf is shaking.
292         */
293        public float getShadingWhileShaking(float par1)
294        {
295            return 0.75F + (this.prevTimeWolfIsShaking + (this.timeWolfIsShaking - this.prevTimeWolfIsShaking) * par1) / 2.0F * 0.25F;
296        }
297    
298        @SideOnly(Side.CLIENT)
299        public float getShakeAngle(float par1, float par2)
300        {
301            float var3 = (this.prevTimeWolfIsShaking + (this.timeWolfIsShaking - this.prevTimeWolfIsShaking) * par1 + par2) / 1.8F;
302    
303            if (var3 < 0.0F)
304            {
305                var3 = 0.0F;
306            }
307            else if (var3 > 1.0F)
308            {
309                var3 = 1.0F;
310            }
311    
312            return MathHelper.sin(var3 * (float)Math.PI) * MathHelper.sin(var3 * (float)Math.PI * 11.0F) * 0.15F * (float)Math.PI;
313        }
314    
315        @SideOnly(Side.CLIENT)
316        public float getInterestedAngle(float par1)
317        {
318            return (this.field_70924_f + (this.field_70926_e - this.field_70924_f) * par1) * 0.15F * (float)Math.PI;
319        }
320    
321        public float getEyeHeight()
322        {
323            return this.height * 0.8F;
324        }
325    
326        /**
327         * The speed it takes to move the entityliving's rotationPitch through the faceEntity method. This is only currently
328         * use in wolves.
329         */
330        public int getVerticalFaceSpeed()
331        {
332            return this.isSitting() ? 20 : super.getVerticalFaceSpeed();
333        }
334    
335        /**
336         * Called when the entity is attacked.
337         */
338        public boolean attackEntityFrom(DamageSource par1DamageSource, int par2)
339        {
340            if (this.isEntityInvulnerable())
341            {
342                return false;
343            }
344            else
345            {
346                Entity var3 = par1DamageSource.getEntity();
347                this.aiSit.setSitting(false);
348    
349                if (var3 != null && !(var3 instanceof EntityPlayer) && !(var3 instanceof EntityArrow))
350                {
351                    par2 = (par2 + 1) / 2;
352                }
353    
354                return super.attackEntityFrom(par1DamageSource, par2);
355            }
356        }
357    
358        public boolean attackEntityAsMob(Entity par1Entity)
359        {
360            int var2 = this.isTamed() ? 4 : 2;
361            return par1Entity.attackEntityFrom(DamageSource.causeMobDamage(this), var2);
362        }
363    
364        /**
365         * Called when a player interacts with a mob. e.g. gets milk from a cow, gets into the saddle on a pig.
366         */
367        public boolean interact(EntityPlayer par1EntityPlayer)
368        {
369            ItemStack var2 = par1EntityPlayer.inventory.getCurrentItem();
370    
371            if (this.isTamed())
372            {
373                if (var2 != null)
374                {
375                    if (Item.itemsList[var2.itemID] instanceof ItemFood)
376                    {
377                        ItemFood var3 = (ItemFood)Item.itemsList[var2.itemID];
378    
379                        if (var3.isWolfsFavoriteMeat() && this.dataWatcher.getWatchableObjectInt(18) < 20)
380                        {
381                            if (!par1EntityPlayer.capabilities.isCreativeMode)
382                            {
383                                --var2.stackSize;
384                            }
385    
386                            this.heal(var3.getHealAmount());
387    
388                            if (var2.stackSize <= 0)
389                            {
390                                par1EntityPlayer.inventory.setInventorySlotContents(par1EntityPlayer.inventory.currentItem, (ItemStack)null);
391                            }
392    
393                            return true;
394                        }
395                    }
396                    else if (var2.itemID == Item.dyePowder.itemID)
397                    {
398                        int var4 = BlockCloth.getBlockFromDye(var2.getItemDamage());
399    
400                        if (var4 != this.getCollarColor())
401                        {
402                            this.setCollarColor(var4);
403    
404                            if (!par1EntityPlayer.capabilities.isCreativeMode && --var2.stackSize <= 0)
405                            {
406                                par1EntityPlayer.inventory.setInventorySlotContents(par1EntityPlayer.inventory.currentItem, (ItemStack)null);
407                            }
408    
409                            return true;
410                        }
411                    }
412                }
413    
414                if (par1EntityPlayer.username.equalsIgnoreCase(this.getOwnerName()) && !this.worldObj.isRemote && !this.isBreedingItem(var2))
415                {
416                    this.aiSit.setSitting(!this.isSitting());
417                    this.isJumping = false;
418                    this.setPathToEntity((PathEntity)null);
419                }
420            }
421            else if (var2 != null && var2.itemID == Item.bone.itemID && !this.isAngry())
422            {
423                if (!par1EntityPlayer.capabilities.isCreativeMode)
424                {
425                    --var2.stackSize;
426                }
427    
428                if (var2.stackSize <= 0)
429                {
430                    par1EntityPlayer.inventory.setInventorySlotContents(par1EntityPlayer.inventory.currentItem, (ItemStack)null);
431                }
432    
433                if (!this.worldObj.isRemote)
434                {
435                    if (this.rand.nextInt(3) == 0)
436                    {
437                        this.setTamed(true);
438                        this.setPathToEntity((PathEntity)null);
439                        this.setAttackTarget((EntityLiving)null);
440                        this.aiSit.setSitting(true);
441                        this.setEntityHealth(20);
442                        this.setOwner(par1EntityPlayer.username);
443                        this.playTameEffect(true);
444                        this.worldObj.setEntityState(this, (byte)7);
445                    }
446                    else
447                    {
448                        this.playTameEffect(false);
449                        this.worldObj.setEntityState(this, (byte)6);
450                    }
451                }
452    
453                return true;
454            }
455    
456            return super.interact(par1EntityPlayer);
457        }
458    
459        @SideOnly(Side.CLIENT)
460        public void handleHealthUpdate(byte par1)
461        {
462            if (par1 == 8)
463            {
464                this.field_70928_h = true;
465                this.timeWolfIsShaking = 0.0F;
466                this.prevTimeWolfIsShaking = 0.0F;
467            }
468            else
469            {
470                super.handleHealthUpdate(par1);
471            }
472        }
473    
474        @SideOnly(Side.CLIENT)
475        public float getTailRotation()
476        {
477            return this.isAngry() ? 1.5393804F : (this.isTamed() ? (0.55F - (float)(20 - this.dataWatcher.getWatchableObjectInt(18)) * 0.02F) * (float)Math.PI : ((float)Math.PI / 5F));
478        }
479    
480        /**
481         * Checks if the parameter is an item which this animal can be fed to breed it (wheat, carrots or seeds depending on
482         * the animal type)
483         */
484        public boolean isBreedingItem(ItemStack par1ItemStack)
485        {
486            return par1ItemStack == null ? false : (!(Item.itemsList[par1ItemStack.itemID] instanceof ItemFood) ? false : ((ItemFood)Item.itemsList[par1ItemStack.itemID]).isWolfsFavoriteMeat());
487        }
488    
489        /**
490         * Will return how many at most can spawn in a chunk at once.
491         */
492        public int getMaxSpawnedInChunk()
493        {
494            return 8;
495        }
496    
497        /**
498         * Determines whether this wolf is angry or not.
499         */
500        public boolean isAngry()
501        {
502            return (this.dataWatcher.getWatchableObjectByte(16) & 2) != 0;
503        }
504    
505        /**
506         * Sets whether this wolf is angry or not.
507         */
508        public void setAngry(boolean par1)
509        {
510            byte var2 = this.dataWatcher.getWatchableObjectByte(16);
511    
512            if (par1)
513            {
514                this.dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 | 2)));
515            }
516            else
517            {
518                this.dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 & -3)));
519            }
520        }
521    
522        /**
523         * Return this wolf's collar color.
524         */
525        public int getCollarColor()
526        {
527            return this.dataWatcher.getWatchableObjectByte(20) & 15;
528        }
529    
530        /**
531         * Set this wolf's collar color.
532         */
533        public void setCollarColor(int par1)
534        {
535            this.dataWatcher.updateObject(20, Byte.valueOf((byte)(par1 & 15)));
536        }
537    
538        /**
539         * This function is used when two same-species animals in 'love mode' breed to generate the new baby animal.
540         */
541        public EntityWolf spawnBabyAnimal(EntityAgeable par1EntityAgeable)
542        {
543            EntityWolf var2 = new EntityWolf(this.worldObj);
544            String var3 = this.getOwnerName();
545    
546            if (var3 != null && var3.trim().length() > 0)
547            {
548                var2.setOwner(var3);
549                var2.setTamed(true);
550            }
551    
552            return var2;
553        }
554    
555        public void func_70918_i(boolean par1)
556        {
557            byte var2 = this.dataWatcher.getWatchableObjectByte(19);
558    
559            if (par1)
560            {
561                this.dataWatcher.updateObject(19, Byte.valueOf((byte)1));
562            }
563            else
564            {
565                this.dataWatcher.updateObject(19, Byte.valueOf((byte)0));
566            }
567        }
568    
569        /**
570         * Returns true if the mob is currently able to mate with the specified mob.
571         */
572        public boolean canMateWith(EntityAnimal par1EntityAnimal)
573        {
574            if (par1EntityAnimal == this)
575            {
576                return false;
577            }
578            else if (!this.isTamed())
579            {
580                return false;
581            }
582            else if (!(par1EntityAnimal instanceof EntityWolf))
583            {
584                return false;
585            }
586            else
587            {
588                EntityWolf var2 = (EntityWolf)par1EntityAnimal;
589                return !var2.isTamed() ? false : (var2.isSitting() ? false : this.isInLove() && var2.isInLove());
590            }
591        }
592    
593        public boolean func_70922_bv()
594        {
595            return this.dataWatcher.getWatchableObjectByte(19) == 1;
596        }
597    
598        public EntityAgeable createChild(EntityAgeable par1EntityAgeable)
599        {
600            return this.spawnBabyAnimal(par1EntityAgeable);
601        }
602    }