    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        }
    084
    085        /**
    086         * returns if this entity triggers Block.onEntityWalking on the blocks they walk on. used for spiders and wolves to
    087         * prevent them from trampling crops
    088         */
    089        protected boolean canTriggerWalking()
    090        {
    091            return false;
    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        }
    112
    113        /**
    114         * (abstract) Protected helper method to read subclass entity data from NBT.
    115         */
    116        public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound)
    117        {
    118            super.readEntityFromNBT(par1NBTTagCompound);
    119            this.setAngry(par1NBTTagCompound.getBoolean("Angry"));
    120        }
    121
    122        /**
    123         * Determines if an entity can be despawned, used on idle far away entities
    124         */
    125        protected boolean canDespawn()
    126        {
    127            return this.isAngry();
    128        }
    129
    130        /**
    131         * Returns the sound this mob makes while it's alive.
    132         */
    133        protected String getLivingSound()
    134        {
    135            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");
    136        }
    137
    138        /**
    139         * Returns the sound this mob makes when it is hurt.
    140         */
    141        protected String getHurtSound()
    142        {
    143            return "mob.wolf.hurt";
    144        }
    145
    146        /**
    147         * Returns the sound this mob makes on death.
    148         */
    149        protected String getDeathSound()
    150        {
    151            return "mob.wolf.death";
    152        }
    153
    154        /**
    155         * Returns the volume for the sounds this mob makes.
    156         */
    157        protected float getSoundVolume()
    158        {
    159            return 0.4F;
    160        }
    161
    162        /**
    163         * Returns the item ID for the item the mob drops on death.
    164         */
    165        protected int getDropItemId()
    166        {
    167            return -1;
    168        }
    169
    170        /**
    171         * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons
    172         * use this to react to sunlight and start to burn.
    173         */
    174        public void onLivingUpdate()
    175        {
    176            super.onLivingUpdate();
    177
    178            if (!this.worldObj.isRemote && this.isShaking && !this.field_70928_h && !this.hasPath() && this.onGround)
    179            {
    180                this.field_70928_h = true;
    181                this.timeWolfIsShaking = 0.0F;
    182                this.prevTimeWolfIsShaking = 0.0F;
    183                this.worldObj.setEntityState(this, (byte)8);
    184            }
    185        }
    186
    187        /**
    188         * Called to update the entity's position/logic.
    189         */
    190        public void onUpdate()
    191        {
    192            super.onUpdate();
    193            this.field_70924_f = this.field_70926_e;
    194
    195            if (this.func_70922_bv())
    196            {
    197                this.field_70926_e += (1.0F - this.field_70926_e) * 0.4F;
    198            }
    199            else
    200            {
    201                this.field_70926_e += (0.0F - this.field_70926_e) * 0.4F;
    202            }
    203
    204            if (this.func_70922_bv())
    205            {
    206                this.numTicksToChaseTarget = 10;
    207            }
    208
    209            if (this.isWet())
    210            {
    211                this.isShaking = true;
    212                this.field_70928_h = false;
    213                this.timeWolfIsShaking = 0.0F;
    214                this.prevTimeWolfIsShaking = 0.0F;
    215            }
    216            else if ((this.isShaking || this.field_70928_h) && this.field_70928_h)
    217            {
    218                if (this.timeWolfIsShaking == 0.0F)
    219                {
    220                    this.worldObj.playSoundAtEntity(this, "mob.wolf.shake", this.getSoundVolume(), (this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F + 1.0F);
    221                }
    222
    223                this.prevTimeWolfIsShaking = this.timeWolfIsShaking;
    224                this.timeWolfIsShaking += 0.05F;
    225
    226                if (this.prevTimeWolfIsShaking >= 2.0F)
    227                {
    228                    this.isShaking = false;
    229                    this.field_70928_h = false;
    230                    this.prevTimeWolfIsShaking = 0.0F;
    231                    this.timeWolfIsShaking = 0.0F;
    232                }
    233
    234                if (this.timeWolfIsShaking > 0.4F)
    235                {
    236                    float var1 = (float)this.boundingBox.minY;
    237                    int var2 = (int)(MathHelper.sin((this.timeWolfIsShaking - 0.4F) * (float)Math.PI) * 7.0F);
    238
    239                    for (int var3 = 0; var3 < var2; ++var3)
    240                    {
    241                        float var4 = (this.rand.nextFloat() * 2.0F - 1.0F) * this.width * 0.5F;
    242                        float var5 = (this.rand.nextFloat() * 2.0F - 1.0F) * this.width * 0.5F;
    243                        this.worldObj.spawnParticle("splash", this.posX + (double)var4, (double)(var1 + 0.8F), this.posZ + (double)var5, this.motionX, this.motionY, this.motionZ);
    244                    }
    245                }
    246            }
    247        }
    248
    249        @SideOnly(Side.CLIENT)
    250        public boolean getWolfShaking()
    251        {
    252            return this.isShaking;
    253        }
    254
    255        @SideOnly(Side.CLIENT)
    256
    257        /**
    258         * Used when calculating the amount of shading to apply while the wolf is shaking.
    259         */
    260        public float getShadingWhileShaking(float par1)
    261        {
    262            return 0.75F + (this.prevTimeWolfIsShaking + (this.timeWolfIsShaking - this.prevTimeWolfIsShaking) * par1) / 2.0F * 0.25F;
    263        }
    264
    265        @SideOnly(Side.CLIENT)
    266        public float getShakeAngle(float par1, float par2)
    267        {
    268            float var3 = (this.prevTimeWolfIsShaking + (this.timeWolfIsShaking - this.prevTimeWolfIsShaking) * par1 + par2) / 1.8F;
    269
    270            if (var3 < 0.0F)
    271            {
    272                var3 = 0.0F;
    273            }
    274            else if (var3 > 1.0F)
    275            {
    276                var3 = 1.0F;
    277            }
    278
    279            return MathHelper.sin(var3 * (float)Math.PI) * MathHelper.sin(var3 * (float)Math.PI * 11.0F) * 0.15F * (float)Math.PI;
    280        }
    281
    282        @SideOnly(Side.CLIENT)
    283        public float getInterestedAngle(float par1)
    284        {
    285            return (this.field_70924_f + (this.field_70926_e - this.field_70924_f) * par1) * 0.15F * (float)Math.PI;
    286        }
    287
    288        public float getEyeHeight()
    289        {
    290            return this.height * 0.8F;
    291        }
    292
    293        /**
    294         * The speed it takes to move the entityliving's rotationPitch through the faceEntity method. This is only currently
    295         * use in wolves.
    296         */
    297        public int getVerticalFaceSpeed()
    298        {
    299            return this.isSitting() ? 20 : super.getVerticalFaceSpeed();
    300        }
    301
    302        /**
    303         * Called when the entity is attacked.
    304         */
    305        public boolean attackEntityFrom(DamageSource par1DamageSource, int par2)
    306        {
    307            Entity var3 = par1DamageSource.getEntity();
    308            this.aiSit.setSitting(false);
    309
    310            if (var3 != null && !(var3 instanceof EntityPlayer) && !(var3 instanceof EntityArrow))
    311            {
    312                par2 = (par2 + 1) / 2;
    313            }
    314
    315            return super.attackEntityFrom(par1DamageSource, par2);
    316        }
    317
    318        public boolean attackEntityAsMob(Entity par1Entity)
    319        {
    320            int var2 = this.isTamed() ? 4 : 2;
    321            return par1Entity.attackEntityFrom(DamageSource.causeMobDamage(this), var2);
    322        }
    323
    324        /**
    325         * Called when a player interacts with a mob. e.g. gets milk from a cow, gets into the saddle on a pig.
    326         */
    327        public boolean interact(EntityPlayer par1EntityPlayer)
    328        {
    329            ItemStack var2 = par1EntityPlayer.inventory.getCurrentItem();
    330
    331            if (this.isTamed())
    332            {
    333                if (var2 != null && Item.itemsList[var2.itemID] instanceof ItemFood)
    334                {
    335                    ItemFood var3 = (ItemFood)Item.itemsList[var2.itemID];
    336
    337                    if (var3.isWolfsFavoriteMeat() && this.dataWatcher.getWatchableObjectInt(18) < 20)
    338                    {
    339                        if (!par1EntityPlayer.capabilities.isCreativeMode)
    340                        {
    341                            --var2.stackSize;
    342                        }
    343
    344                        this.heal(var3.getHealAmount());
    345
    346                        if (var2.stackSize <= 0)
    347                        {
    348                            par1EntityPlayer.inventory.setInventorySlotContents(par1EntityPlayer.inventory.currentItem, (ItemStack)null);
    349                        }
    350
    351                        return true;
    352                    }
    353                }
    354
    355                if (par1EntityPlayer.username.equalsIgnoreCase(this.getOwnerName()) && !this.worldObj.isRemote && !this.isWheat(var2))
    356                {
    357                    this.aiSit.setSitting(!this.isSitting());
    358                    this.isJumping = false;
    359                    this.setPathToEntity((PathEntity)null);
    360                }
    361            }
    362            else if (var2 != null && var2.itemID == Item.bone.shiftedIndex && !this.isAngry())
    363            {
    364                if (!par1EntityPlayer.capabilities.isCreativeMode)
    365                {
    366                    --var2.stackSize;
    367                }
    368
    369                if (var2.stackSize <= 0)
    370                {
    371                    par1EntityPlayer.inventory.setInventorySlotContents(par1EntityPlayer.inventory.currentItem, (ItemStack)null);
    372                }
    373
    374                if (!this.worldObj.isRemote)
    375                {
    376                    if (this.rand.nextInt(3) == 0)
    377                    {
    378                        this.setTamed(true);
    379                        this.setPathToEntity((PathEntity)null);
    380                        this.setAttackTarget((EntityLiving)null);
    381                        this.aiSit.setSitting(true);
    382                        this.setEntityHealth(20);
    383                        this.setOwner(par1EntityPlayer.username);
    384                        this.playTameEffect(true);
    385                        this.worldObj.setEntityState(this, (byte)7);
    386                    }
    387                    else
    388                    {
    389                        this.playTameEffect(false);
    390                        this.worldObj.setEntityState(this, (byte)6);
    391                    }
    392                }
    393
    394                return true;
    395            }
    396
    397            return super.interact(par1EntityPlayer);
    398        }
    399
    400        @SideOnly(Side.CLIENT)
    401        public void handleHealthUpdate(byte par1)
    402        {
    403            if (par1 == 8)
    404            {
    405                this.field_70928_h = true;
    406                this.timeWolfIsShaking = 0.0F;
    407                this.prevTimeWolfIsShaking = 0.0F;
    408            }
    409            else
    410            {
    411                super.handleHealthUpdate(par1);
    412            }
    413        }
    414
    415        @SideOnly(Side.CLIENT)
    416        public float getTailRotation()
    417        {
    418            return this.isAngry() ? 1.5393804F : (this.isTamed() ? (0.55F - (float)(20 - this.dataWatcher.getWatchableObjectInt(18)) * 0.02F) * (float)Math.PI : ((float)Math.PI / 5F));
    419        }
    420
    421        /**
    422         * Checks if the parameter is an wheat item.
    423         */
    424        public boolean isWheat(ItemStack par1ItemStack)
    425        {
    426            return par1ItemStack == null ? false : (!(Item.itemsList[par1ItemStack.itemID] instanceof ItemFood) ? false : ((ItemFood)Item.itemsList[par1ItemStack.itemID]).isWolfsFavoriteMeat());
    427        }
    428
    429        /**
    430         * Will return how many at most can spawn in a chunk at once.
    431         */
    432        public int getMaxSpawnedInChunk()
    433        {
    434            return 8;
    435        }
    436
    437        /**
    438         * Determines whether this wolf is angry or not.
    439         */
    440        public boolean isAngry()
    441        {
    442            return (this.dataWatcher.getWatchableObjectByte(16) & 2) != 0;
    443        }
    444
    445        /**
    446         * Sets whether this wolf is angry or not.
    447         */
    448        public void setAngry(boolean par1)
    449        {
    450            byte var2 = this.dataWatcher.getWatchableObjectByte(16);
    451
    452            if (par1)
    453            {
    454                this.dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 | 2)));
    455            }
    456            else
    457            {
    458                this.dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 & -3)));
    459            }
    460        }
    461
    462        /**
    463         * This function is used when two same-species animals in 'love mode' breed to generate the new baby animal.
    464         */
    465        public EntityAnimal spawnBabyAnimal(EntityAnimal par1EntityAnimal)
    466        {
    467            EntityWolf var2 = new EntityWolf(this.worldObj);
    468            var2.setOwner(this.getOwnerName());
    469            var2.setTamed(true);
    470            return var2;
    471        }
    472
    473        public void func_70918_i(boolean par1)
    474        {
    475            byte var2 = this.dataWatcher.getWatchableObjectByte(19);
    476
    477            if (par1)
    478            {
    479                this.dataWatcher.updateObject(19, Byte.valueOf((byte)1));
    480            }
    481            else
    482            {
    483                this.dataWatcher.updateObject(19, Byte.valueOf((byte)0));
    484            }
    485        }
    486
    487        /**
    488         * Returns true if the mob is currently able to mate with the specified mob.
    489         */
    490        public boolean canMateWith(EntityAnimal par1EntityAnimal)
    491        {
    492            if (par1EntityAnimal == this)
    493            {
    494                return false;
    495            }
    496            else if (!this.isTamed())
    497            {
    498                return false;
    499            }
    500            else if (!(par1EntityAnimal instanceof EntityWolf))
    501            {
    502                return false;
    503            }
    504            else
    505            {
    506                EntityWolf var2 = (EntityWolf)par1EntityAnimal;
    507                return !var2.isTamed() ? false : (var2.isSitting() ? false : this.isInLove() && var2.isInLove());
    508            }
    509        }
    510
    511        public boolean func_70922_bv()
    512        {
    513            return this.dataWatcher.getWatchableObjectByte(19) == 1;
    514        }
    515    }