    001    package net.minecraft.entity.passive;
    002
    003    import cpw.mods.fml.relauncher.Side;
    004    import cpw.mods.fml.relauncher.SideOnly;
    005    import net.minecraft.block.BlockCloth;
    006    import net.minecraft.entity.Entity;
    007    import net.minecraft.entity.EntityAgeable;
    008    import net.minecraft.entity.EntityLiving;
    009    import net.minecraft.entity.ai.EntityAIAttackOnCollide;
    010    import net.minecraft.entity.ai.EntityAIBeg;
    011    import net.minecraft.entity.ai.EntityAIFollowOwner;
    012    import net.minecraft.entity.ai.EntityAIHurtByTarget;
    013    import net.minecraft.entity.ai.EntityAILeapAtTarget;
    014    import net.minecraft.entity.ai.EntityAILookIdle;
    015    import net.minecraft.entity.ai.EntityAIMate;
    016    import net.minecraft.entity.ai.EntityAIOwnerHurtByTarget;
    017    import net.minecraft.entity.ai.EntityAIOwnerHurtTarget;
    018    import net.minecraft.entity.ai.EntityAISwimming;
    019    import net.minecraft.entity.ai.EntityAITargetNonTamed;
    020    import net.minecraft.entity.ai.EntityAIWander;
    021    import net.minecraft.entity.ai.EntityAIWatchClosest;
    022    import net.minecraft.entity.player.EntityPlayer;
    023    import net.minecraft.entity.projectile.EntityArrow;
    024    import net.minecraft.item.Item;
    025    import net.minecraft.item.ItemFood;
    026    import net.minecraft.item.ItemStack;
    027    import net.minecraft.nbt.NBTTagCompound;
    028    import net.minecraft.pathfinding.PathEntity;
    029    import net.minecraft.util.DamageSource;
    030    import net.minecraft.util.MathHelper;
    031    import net.minecraft.world.World;
    032
    033    public 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.func_85030_a("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.func_85030_a("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 var1 = (float)this.boundingBox.minY;
    270                    int var2 = (int)(MathHelper.sin((this.timeWolfIsShaking - 0.4F) * (float)Math.PI) * 7.0F);
    271
    272                    for (int var3 = 0; var3 < var2; ++var3)
    273                    {
    274                        float var4 = (this.rand.nextFloat() * 2.0F - 1.0F) * this.width * 0.5F;
    275                        float var5 = (this.rand.nextFloat() * 2.0F - 1.0F) * this.width * 0.5F;
    276                        this.worldObj.spawnParticle("splash", this.posX + (double)var4, (double)(var1 + 0.8F), this.posZ + (double)var5, 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 var3 = (this.prevTimeWolfIsShaking + (this.timeWolfIsShaking - this.prevTimeWolfIsShaking) * par1 + par2) / 1.8F;
    302
    303            if (var3 < 0.0F)
    304            {
    305                var3 = 0.0F;
    306            }
    307            else if (var3 > 1.0F)
    308            {
    309                var3 = 1.0F;
    310            }
    311
    312            return MathHelper.sin(var3 * (float)Math.PI) * MathHelper.sin(var3 * (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.func_85032_ar())
    341            {
    342                return false;
    343            }
    344            else
    345            {
    346                Entity var3 = par1DamageSource.getEntity();
    347                this.aiSit.setSitting(false);
    348
    349                if (var3 != null && !(var3 instanceof EntityPlayer) && !(var3 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 var2 = this.isTamed() ? 4 : 2;
    361            return par1Entity.attackEntityFrom(DamageSource.causeMobDamage(this), var2);
    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 var2 = par1EntityPlayer.inventory.getCurrentItem();
    370
    371            if (this.isTamed())
    372            {
    373                if (var2 != null)
    374                {
    375                    if (Item.itemsList[var2.itemID] instanceof ItemFood)
    376                    {
    377                        ItemFood var3 = (ItemFood)Item.itemsList[var2.itemID];
    378
    379                        if (var3.isWolfsFavoriteMeat() && this.dataWatcher.getWatchableObjectInt(18) < 20)
    380                        {
    381                            if (!par1EntityPlayer.capabilities.isCreativeMode)
    382                            {
    383                                --var2.stackSize;
    384                            }
    385
    386                            this.heal(var3.getHealAmount());
    387
    388                            if (var2.stackSize <= 0)
    389                            {
    390                                par1EntityPlayer.inventory.setInventorySlotContents(par1EntityPlayer.inventory.currentItem, (ItemStack)null);
    391                            }
    392
    393                            return true;
    394                        }
    395                    }
    396                    else if (var2.itemID == Item.dyePowder.shiftedIndex)
    397                    {
    398                        int var4 = BlockCloth.getBlockFromDye(var2.getItemDamage());
    399
    400                        if (var4 != this.getCollarColor())
    401                        {
    402                            this.setCollarColor(var4);
    403
    404                            if (!par1EntityPlayer.capabilities.isCreativeMode && --var2.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(var2))
    415                {
    416                    this.aiSit.setSitting(!this.isSitting());
    417                    this.isJumping = false;
    418                    this.setPathToEntity((PathEntity)null);
    419                }
    420            }
    421            else if (var2 != null && var2.itemID == Item.bone.shiftedIndex && !this.isAngry())
    422            {
    423                if (!par1EntityPlayer.capabilities.isCreativeMode)
    424                {
    425                    --var2.stackSize;
    426                }
    427
    428                if (var2.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 var2 = this.dataWatcher.getWatchableObjectByte(16);
    511
    512            if (par1)
    513            {
    514                this.dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 | 2)));
    515            }
    516            else
    517            {
    518                this.dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 & -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 var2 = new EntityWolf(this.worldObj);
    544            String var3 = this.getOwnerName();
    545
    546            if (var3 != null && var3.trim().length() > 0)
    547            {
    548                var2.setOwner(var3);
    549                var2.setTamed(true);
    550            }
    551
    552            return var2;
    553        }
    554
    555        public void func_70918_i(boolean par1)
    556        {
    557            byte var2 = 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 var2 = (EntityWolf)par1EntityAnimal;
    589                return !var2.isTamed() ? false : (var2.isSitting() ? false : this.isInLove() && var2.isInLove());
    590            }
    591        }
    592
    593        public boolean func_70922_bv()
    594        {
    595            return this.dataWatcher.getWatchableObjectByte(19) == 1;
    596        }
    597
    598        public EntityAgeable func_90011_a(EntityAgeable par1EntityAgeable)
    599        {
    600            return this.spawnBabyAnimal(par1EntityAgeable);
    601        }
    602    }