    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.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.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.getCollarColor())
    367                        {
    368                            this.setCollarColor(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.isBreedingItem(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 item which this animal can be fed to breed it (wheat, carrots or seeds depending on
    427         * the animal type)
    428         */
    429        public boolean isBreedingItem(ItemStack par1ItemStack)
    430        {
    431            return par1ItemStack == null ? false : (!(Item.itemsList[par1ItemStack.itemID] instanceof ItemFood) ? false : ((ItemFood)Item.itemsList[par1ItemStack.itemID]).isWolfsFavoriteMeat());
    432        }
    433
    434        @SideOnly(Side.CLIENT)
    435        public void handleHealthUpdate(byte par1)
    436        {
    437            if (par1 == 8)
    438            {
    439                this.field_70928_h = true;
    440                this.timeWolfIsShaking = 0.0F;
    441                this.prevTimeWolfIsShaking = 0.0F;
    442            }
    443            else
    444            {
    445                super.handleHealthUpdate(par1);
    446            }
    447        }
    448
    449        @SideOnly(Side.CLIENT)
    450        public float getTailRotation()
    451        {
    452            return this.isAngry() ? 1.5393804F : (this.isTamed() ? (0.55F - (float)(20 - this.dataWatcher.getWatchableObjectInt(18)) * 0.02F) * (float)Math.PI : ((float)Math.PI / 5F));
    453        }
    454
    455        /**
    456         * Will return how many at most can spawn in a chunk at once.
    457         */
    458        public int getMaxSpawnedInChunk()
    459        {
    460            return 8;
    461        }
    462
    463        /**
    464         * Determines whether this wolf is angry or not.
    465         */
    466        public boolean isAngry()
    467        {
    468            return (this.dataWatcher.getWatchableObjectByte(16) & 2) != 0;
    469        }
    470
    471        /**
    472         * Sets whether this wolf is angry or not.
    473         */
    474        public void setAngry(boolean par1)
    475        {
    476            byte var2 = this.dataWatcher.getWatchableObjectByte(16);
    477
    478            if (par1)
    479            {
    480                this.dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 | 2)));
    481            }
    482            else
    483            {
    484                this.dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 & -3)));
    485            }
    486        }
    487
    488        /**
    489         * Return this wolf's collar color.
    490         */
    491        public int getCollarColor()
    492        {
    493            return this.dataWatcher.getWatchableObjectByte(20) & 15;
    494        }
    495
    496        /**
    497         * Set this wolf's collar color.
    498         */
    499        public void setCollarColor(int par1)
    500        {
    501            this.dataWatcher.updateObject(20, Byte.valueOf((byte)(par1 & 15)));
    502        }
    503
    504        /**
    505         * This function is used when two same-species animals in 'love mode' breed to generate the new baby animal.
    506         */
    507        public EntityAnimal spawnBabyAnimal(EntityAnimal par1EntityAnimal)
    508        {
    509            EntityWolf var2 = new EntityWolf(this.worldObj);
    510            var2.setOwner(this.getOwnerName());
    511            var2.setTamed(true);
    512            return var2;
    513        }
    514
    515        public void func_70918_i(boolean par1)
    516        {
    517            byte var2 = this.dataWatcher.getWatchableObjectByte(19);
    518
    519            if (par1)
    520            {
    521                this.dataWatcher.updateObject(19, Byte.valueOf((byte)1));
    522            }
    523            else
    524            {
    525                this.dataWatcher.updateObject(19, Byte.valueOf((byte)0));
    526            }
    527        }
    528
    529        /**
    530         * Returns true if the mob is currently able to mate with the specified mob.
    531         */
    532        public boolean canMateWith(EntityAnimal par1EntityAnimal)
    533        {
    534            if (par1EntityAnimal == this)
    535            {
    536                return false;
    537            }
    538            else if (!this.isTamed())
    539            {
    540                return false;
    541            }
    542            else if (!(par1EntityAnimal instanceof EntityWolf))
    543            {
    544                return false;
    545            }
    546            else
    547            {
    548                EntityWolf var2 = (EntityWolf)par1EntityAnimal;
    549                return !var2.isTamed() ? false : (var2.isSitting() ? false : this.isInLove() && var2.isInLove());
    550            }
    551        }
    552
    553        public boolean func_70922_bv()
    554        {
    555            return this.dataWatcher.getWatchableObjectByte(19) == 1;
    556        }
    557    }