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