    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}