    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 par1, int par2, int par3, int par4)
    090        {
    091            this.func_85030_a("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.func_85030_a("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            if (this.func_85032_ar())
    314            {
    315                return false;
    316            }
    317            else
    318            {
    319                Entity var3 = par1DamageSource.getEntity();
    320                this.aiSit.setSitting(false);
    321
    322                if (var3 != null && !(var3 instanceof EntityPlayer) && !(var3 instanceof EntityArrow))
    323                {
    324                    par2 = (par2 + 1) / 2;
    325                }
    326
    327                return super.attackEntityFrom(par1DamageSource, par2);
    328            }
    329        }
    330
    331        public boolean attackEntityAsMob(Entity par1Entity)
    332        {
    333            int var2 = this.isTamed() ? 4 : 2;
    334            return par1Entity.attackEntityFrom(DamageSource.causeMobDamage(this), var2);
    335        }
    336
    337        /**
    338         * Called when a player interacts with a mob. e.g. gets milk from a cow, gets into the saddle on a pig.
    339         */
    340        public boolean interact(EntityPlayer par1EntityPlayer)
    341        {
    342            ItemStack var2 = par1EntityPlayer.inventory.getCurrentItem();
    343
    344            if (this.isTamed())
    345            {
    346                if (var2 != null)
    347                {
    348                    if (Item.itemsList[var2.itemID] instanceof ItemFood)
    349                    {
    350                        ItemFood var3 = (ItemFood)Item.itemsList[var2.itemID];
    351
    352                        if (var3.isWolfsFavoriteMeat() && this.dataWatcher.getWatchableObjectInt(18) < 20)
    353                        {
    354                            if (!par1EntityPlayer.capabilities.isCreativeMode)
    355                            {
    356                                --var2.stackSize;
    357                            }
    358
    359                            this.heal(var3.getHealAmount());
    360
    361                            if (var2.stackSize <= 0)
    362                            {
    363                                par1EntityPlayer.inventory.setInventorySlotContents(par1EntityPlayer.inventory.currentItem, (ItemStack)null);
    364                            }
    365
    366                            return true;
    367                        }
    368                    }
    369                    else if (var2.itemID == Item.dyePowder.shiftedIndex)
    370                    {
    371                        int var4 = BlockCloth.getBlockFromDye(var2.getItemDamage());
    372
    373                        if (var4 != this.getCollarColor())
    374                        {
    375                            this.setCollarColor(var4);
    376
    377                            if (!par1EntityPlayer.capabilities.isCreativeMode && --var2.stackSize <= 0)
    378                            {
    379                                par1EntityPlayer.inventory.setInventorySlotContents(par1EntityPlayer.inventory.currentItem, (ItemStack)null);
    380                            }
    381
    382                            return true;
    383                        }
    384                    }
    385                }
    386
    387                if (par1EntityPlayer.username.equalsIgnoreCase(this.getOwnerName()) && !this.worldObj.isRemote && !this.isBreedingItem(var2))
    388                {
    389                    this.aiSit.setSitting(!this.isSitting());
    390                    this.isJumping = false;
    391                    this.setPathToEntity((PathEntity)null);
    392                }
    393            }
    394            else if (var2 != null && var2.itemID == Item.bone.shiftedIndex && !this.isAngry())
    395            {
    396                if (!par1EntityPlayer.capabilities.isCreativeMode)
    397                {
    398                    --var2.stackSize;
    399                }
    400
    401                if (var2.stackSize <= 0)
    402                {
    403                    par1EntityPlayer.inventory.setInventorySlotContents(par1EntityPlayer.inventory.currentItem, (ItemStack)null);
    404                }
    405
    406                if (!this.worldObj.isRemote)
    407                {
    408                    if (this.rand.nextInt(3) == 0)
    409                    {
    410                        this.setTamed(true);
    411                        this.setPathToEntity((PathEntity)null);
    412                        this.setAttackTarget((EntityLiving)null);
    413                        this.aiSit.setSitting(true);
    414                        this.setEntityHealth(20);
    415                        this.setOwner(par1EntityPlayer.username);
    416                        this.playTameEffect(true);
    417                        this.worldObj.setEntityState(this, (byte)7);
    418                    }
    419                    else
    420                    {
    421                        this.playTameEffect(false);
    422                        this.worldObj.setEntityState(this, (byte)6);
    423                    }
    424                }
    425
    426                return true;
    427            }
    428
    429            return super.interact(par1EntityPlayer);
    430        }
    431
    432        /**
    433         * Checks if the parameter is an item which this animal can be fed to breed it (wheat, carrots or seeds depending on
    434         * the animal type)
    435         */
    436        public boolean isBreedingItem(ItemStack par1ItemStack)
    437        {
    438            return par1ItemStack == null ? false : (!(Item.itemsList[par1ItemStack.itemID] instanceof ItemFood) ? false : ((ItemFood)Item.itemsList[par1ItemStack.itemID]).isWolfsFavoriteMeat());
    439        }
    440
    441        @SideOnly(Side.CLIENT)
    442        public void handleHealthUpdate(byte par1)
    443        {
    444            if (par1 == 8)
    445            {
    446                this.field_70928_h = true;
    447                this.timeWolfIsShaking = 0.0F;
    448                this.prevTimeWolfIsShaking = 0.0F;
    449            }
    450            else
    451            {
    452                super.handleHealthUpdate(par1);
    453            }
    454        }
    455
    456        @SideOnly(Side.CLIENT)
    457        public float getTailRotation()
    458        {
    459            return this.isAngry() ? 1.5393804F : (this.isTamed() ? (0.55F - (float)(20 - this.dataWatcher.getWatchableObjectInt(18)) * 0.02F) * (float)Math.PI : ((float)Math.PI / 5F));
    460        }
    461
    462        /**
    463         * Will return how many at most can spawn in a chunk at once.
    464         */
    465        public int getMaxSpawnedInChunk()
    466        {
    467            return 8;
    468        }
    469
    470        /**
    471         * Determines whether this wolf is angry or not.
    472         */
    473        public boolean isAngry()
    474        {
    475            return (this.dataWatcher.getWatchableObjectByte(16) & 2) != 0;
    476        }
    477
    478        /**
    479         * Sets whether this wolf is angry or not.
    480         */
    481        public void setAngry(boolean par1)
    482        {
    483            byte var2 = this.dataWatcher.getWatchableObjectByte(16);
    484
    485            if (par1)
    486            {
    487                this.dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 | 2)));
    488            }
    489            else
    490            {
    491                this.dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 & -3)));
    492            }
    493        }
    494
    495        /**
    496         * Return this wolf's collar color.
    497         */
    498        public int getCollarColor()
    499        {
    500            return this.dataWatcher.getWatchableObjectByte(20) & 15;
    501        }
    502
    503        /**
    504         * Set this wolf's collar color.
    505         */
    506        public void setCollarColor(int par1)
    507        {
    508            this.dataWatcher.updateObject(20, Byte.valueOf((byte)(par1 & 15)));
    509        }
    510
    511        /**
    512         * This function is used when two same-species animals in 'love mode' breed to generate the new baby animal.
    513         */
    514        public EntityAnimal spawnBabyAnimal(EntityAnimal par1EntityAnimal)
    515        {
    516            EntityWolf var2 = new EntityWolf(this.worldObj);
    517            var2.setOwner(this.getOwnerName());
    518            var2.setTamed(true);
    519            return var2;
    520        }
    521
    522        public void func_70918_i(boolean par1)
    523        {
    524            byte var2 = this.dataWatcher.getWatchableObjectByte(19);
    525
    526            if (par1)
    527            {
    528                this.dataWatcher.updateObject(19, Byte.valueOf((byte)1));
    529            }
    530            else
    531            {
    532                this.dataWatcher.updateObject(19, Byte.valueOf((byte)0));
    533            }
    534        }
    535
    536        /**
    537         * Returns true if the mob is currently able to mate with the specified mob.
    538         */
    539        public boolean canMateWith(EntityAnimal par1EntityAnimal)
    540        {
    541            if (par1EntityAnimal == this)
    542            {
    543                return false;
    544            }
    545            else if (!this.isTamed())
    546            {
    547                return false;
    548            }
    549            else if (!(par1EntityAnimal instanceof EntityWolf))
    550            {
    551                return false;
    552            }
    553            else
    554            {
    555                EntityWolf var2 = (EntityWolf)par1EntityAnimal;
    556                return !var2.isTamed() ? false : (var2.isSitting() ? false : this.isInLove() && var2.isInLove());
    557            }
    558        }
    559
    560        public boolean func_70922_bv()
    561        {
    562            return this.dataWatcher.getWatchableObjectByte(19) == 1;
    563        }
    564    }