001package net.minecraft.entity.passive;
002
003import cpw.mods.fml.relauncher.Side;
004import cpw.mods.fml.relauncher.SideOnly;
005import net.minecraft.block.BlockCloth;
006import net.minecraft.entity.Entity;
007import net.minecraft.entity.EntityAgeable;
008import net.minecraft.entity.EntityLiving;
009import net.minecraft.entity.ai.EntityAIAttackOnCollide;
010import net.minecraft.entity.ai.EntityAIBeg;
011import net.minecraft.entity.ai.EntityAIFollowOwner;
012import net.minecraft.entity.ai.EntityAIHurtByTarget;
013import net.minecraft.entity.ai.EntityAILeapAtTarget;
014import net.minecraft.entity.ai.EntityAILookIdle;
015import net.minecraft.entity.ai.EntityAIMate;
016import net.minecraft.entity.ai.EntityAIOwnerHurtByTarget;
017import net.minecraft.entity.ai.EntityAIOwnerHurtTarget;
018import net.minecraft.entity.ai.EntityAISwimming;
019import net.minecraft.entity.ai.EntityAITargetNonTamed;
020import net.minecraft.entity.ai.EntityAIWander;
021import net.minecraft.entity.ai.EntityAIWatchClosest;
022import net.minecraft.entity.player.EntityPlayer;
023import net.minecraft.entity.projectile.EntityArrow;
024import net.minecraft.item.Item;
025import net.minecraft.item.ItemFood;
026import net.minecraft.item.ItemStack;
027import net.minecraft.nbt.NBTTagCompound;
028import net.minecraft.pathfinding.PathEntity;
029import net.minecraft.util.DamageSource;
030import net.minecraft.util.MathHelper;
031import net.minecraft.world.World;
032
033public 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 f = (float)this.boundingBox.minY;
270                int i = (int)(MathHelper.sin((this.timeWolfIsShaking - 0.4F) * (float)Math.PI) * 7.0F);
271
272                for (int j = 0; j < i; ++j)
273                {
274                    float f1 = (this.rand.nextFloat() * 2.0F - 1.0F) * this.width * 0.5F;
275                    float f2 = (this.rand.nextFloat() * 2.0F - 1.0F) * this.width * 0.5F;
276                    this.worldObj.spawnParticle("splash", this.posX + (double)f1, (double)(f + 0.8F), this.posZ + (double)f2, 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 f2 = (this.prevTimeWolfIsShaking + (this.timeWolfIsShaking - this.prevTimeWolfIsShaking) * par1 + par2) / 1.8F;
302
303        if (f2 < 0.0F)
304        {
305            f2 = 0.0F;
306        }
307        else if (f2 > 1.0F)
308        {
309            f2 = 1.0F;
310        }
311
312        return MathHelper.sin(f2 * (float)Math.PI) * MathHelper.sin(f2 * (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 entity = par1DamageSource.getEntity();
347            this.aiSit.setSitting(false);
348
349            if (entity != null && !(entity instanceof EntityPlayer) && !(entity 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 i = this.isTamed() ? 4 : 2;
361        return par1Entity.attackEntityFrom(DamageSource.causeMobDamage(this), i);
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 itemstack = par1EntityPlayer.inventory.getCurrentItem();
370
371        if (this.isTamed())
372        {
373            if (itemstack != null)
374            {
375                if (Item.itemsList[itemstack.itemID] instanceof ItemFood)
376                {
377                    ItemFood itemfood = (ItemFood)Item.itemsList[itemstack.itemID];
378
379                    if (itemfood.isWolfsFavoriteMeat() && this.dataWatcher.getWatchableObjectInt(18) < 20)
380                    {
381                        if (!par1EntityPlayer.capabilities.isCreativeMode)
382                        {
383                            --itemstack.stackSize;
384                        }
385
386                        this.heal(itemfood.getHealAmount());
387
388                        if (itemstack.stackSize <= 0)
389                        {
390                            par1EntityPlayer.inventory.setInventorySlotContents(par1EntityPlayer.inventory.currentItem, (ItemStack)null);
391                        }
392
393                        return true;
394                    }
395                }
396                else if (itemstack.itemID == Item.dyePowder.itemID)
397                {
398                    int i = BlockCloth.getBlockFromDye(itemstack.getItemDamage());
399
400                    if (i != this.getCollarColor())
401                    {
402                        this.setCollarColor(i);
403
404                        if (!par1EntityPlayer.capabilities.isCreativeMode && --itemstack.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(itemstack))
415            {
416                this.aiSit.setSitting(!this.isSitting());
417                this.isJumping = false;
418                this.setPathToEntity((PathEntity)null);
419            }
420        }
421        else if (itemstack != null && itemstack.itemID == Item.bone.itemID && !this.isAngry())
422        {
423            if (!par1EntityPlayer.capabilities.isCreativeMode)
424            {
425                --itemstack.stackSize;
426            }
427
428            if (itemstack.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 b0 = this.dataWatcher.getWatchableObjectByte(16);
511
512        if (par1)
513        {
514            this.dataWatcher.updateObject(16, Byte.valueOf((byte)(b0 | 2)));
515        }
516        else
517        {
518            this.dataWatcher.updateObject(16, Byte.valueOf((byte)(b0 & -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 entitywolf = new EntityWolf(this.worldObj);
544        String s = this.getOwnerName();
545
546        if (s != null && s.trim().length() > 0)
547        {
548            entitywolf.setOwner(s);
549            entitywolf.setTamed(true);
550        }
551
552        return entitywolf;
553    }
554
555    public void func_70918_i(boolean par1)
556    {
557        byte b0 = 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 entitywolf = (EntityWolf)par1EntityAnimal;
589            return !entitywolf.isTamed() ? false : (entitywolf.isSitting() ? false : this.isInLove() && entitywolf.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}