    001package net.minecraft.entity.passive;
    002
    003import java.util.List;
    004import net.minecraft.block.Block;
    005import net.minecraft.entity.Entity;
    006import net.minecraft.entity.EntityAgeable;
    007import net.minecraft.entity.player.EntityPlayer;
    008import net.minecraft.item.Item;
    009import net.minecraft.item.ItemStack;
    010import net.minecraft.nbt.NBTTagCompound;
    011import net.minecraft.util.DamageSource;
    012import net.minecraft.util.MathHelper;
    013import net.minecraft.world.World;
    014
    015public abstract class EntityAnimal extends EntityAgeable implements IAnimals
    016{
    017    public int inLove;
    018
    019    /**
    020     * This is representation of a counter for reproduction progress. (Note that this is different from the inLove which
    021     * represent being in Love-Mode)
    022     */
    023    private int breeding = 0;
    024
    025    public EntityAnimal(World par1World)
    026    {
    027        super(par1World);
    028    }
    029
    030    /**
    031     * main AI tick function, replaces updateEntityActionState
    032     */
    033    protected void updateAITick()
    034    {
    035        if (this.getGrowingAge() != 0)
    036        {
    037            this.inLove = 0;
    038        }
    039
    040        super.updateAITick();
    041    }
    042
    043    /**
    044     * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons
    045     * use this to react to sunlight and start to burn.
    046     */
    047    public void onLivingUpdate()
    048    {
    049        super.onLivingUpdate();
    050
    051        if (this.getGrowingAge() != 0)
    052        {
    053            this.inLove = 0;
    054        }
    055
    056        if (this.inLove > 0)
    057        {
    058            --this.inLove;
    059            String s = "heart";
    060
    061            if (this.inLove % 10 == 0)
    062            {
    063                double d0 = this.rand.nextGaussian() * 0.02D;
    064                double d1 = this.rand.nextGaussian() * 0.02D;
    065                double d2 = this.rand.nextGaussian() * 0.02D;
    066                this.worldObj.spawnParticle(s, this.posX + (double)(this.rand.nextFloat() * this.width * 2.0F) - (double)this.width, this.posY + 0.5D + (double)(this.rand.nextFloat() * this.height), this.posZ + (double)(this.rand.nextFloat() * this.width * 2.0F) - (double)this.width, d0, d1, d2);
    067            }
    068        }
    069        else
    070        {
    071            this.breeding = 0;
    072        }
    073    }
    074
    075    /**
    076     * Basic mob attack. Default to touch of death in EntityCreature. Overridden by each mob to define their attack.
    077     */
    078    protected void attackEntity(Entity par1Entity, float par2)
    079    {
    080        if (par1Entity instanceof EntityPlayer)
    081        {
    082            if (par2 < 3.0F)
    083            {
    084                double d0 = par1Entity.posX - this.posX;
    085                double d1 = par1Entity.posZ - this.posZ;
    086                this.rotationYaw = (float)(Math.atan2(d1, d0) * 180.0D / Math.PI) - 90.0F;
    087                this.hasAttacked = true;
    088            }
    089
    090            EntityPlayer entityplayer = (EntityPlayer)par1Entity;
    091
    092            if (entityplayer.getCurrentEquippedItem() == null || !this.isBreedingItem(entityplayer.getCurrentEquippedItem()))
    093            {
    094                this.entityToAttack = null;
    095            }
    096        }
    097        else if (par1Entity instanceof EntityAnimal)
    098        {
    099            EntityAnimal entityanimal = (EntityAnimal)par1Entity;
    100
    101            if (this.getGrowingAge() > 0 && entityanimal.getGrowingAge() < 0)
    102            {
    103                if ((double)par2 < 2.5D)
    104                {
    105                    this.hasAttacked = true;
    106                }
    107            }
    108            else if (this.inLove > 0 && entityanimal.inLove > 0)
    109            {
    110                if (entityanimal.entityToAttack == null)
    111                {
    112                    entityanimal.entityToAttack = this;
    113                }
    114
    115                if (entityanimal.entityToAttack == this && (double)par2 < 3.5D)
    116                {
    117                    ++entityanimal.inLove;
    118                    ++this.inLove;
    119                    ++this.breeding;
    120
    121                    if (this.breeding % 4 == 0)
    122                    {
    123                        this.worldObj.spawnParticle("heart", this.posX + (double)(this.rand.nextFloat() * this.width * 2.0F) - (double)this.width, this.posY + 0.5D + (double)(this.rand.nextFloat() * this.height), this.posZ + (double)(this.rand.nextFloat() * this.width * 2.0F) - (double)this.width, 0.0D, 0.0D, 0.0D);
    124                    }
    125
    126                    if (this.breeding == 60)
    127                    {
    128                        this.procreate((EntityAnimal)par1Entity);
    129                    }
    130                }
    131                else
    132                {
    133                    this.breeding = 0;
    134                }
    135            }
    136            else
    137            {
    138                this.breeding = 0;
    139                this.entityToAttack = null;
    140            }
    141        }
    142    }
    143
    144    /**
    145     * Creates a baby animal according to the animal type of the target at the actual position and spawns 'love'
    146     * particles.
    147     */
    148    private void procreate(EntityAnimal par1EntityAnimal)
    149    {
    150        EntityAgeable entityageable = this.createChild(par1EntityAnimal);
    151
    152        if (entityageable != null)
    153        {
    154            this.setGrowingAge(6000);
    155            par1EntityAnimal.setGrowingAge(6000);
    156            this.inLove = 0;
    157            this.breeding = 0;
    158            this.entityToAttack = null;
    159            par1EntityAnimal.entityToAttack = null;
    160            par1EntityAnimal.breeding = 0;
    161            par1EntityAnimal.inLove = 0;
    162            entityageable.setGrowingAge(-24000);
    163            entityageable.setLocationAndAngles(this.posX, this.posY, this.posZ, this.rotationYaw, this.rotationPitch);
    164
    165            for (int i = 0; i < 7; ++i)
    166            {
    167                double d0 = this.rand.nextGaussian() * 0.02D;
    168                double d1 = this.rand.nextGaussian() * 0.02D;
    169                double d2 = this.rand.nextGaussian() * 0.02D;
    170                this.worldObj.spawnParticle("heart", this.posX + (double)(this.rand.nextFloat() * this.width * 2.0F) - (double)this.width, this.posY + 0.5D + (double)(this.rand.nextFloat() * this.height), this.posZ + (double)(this.rand.nextFloat() * this.width * 2.0F) - (double)this.width, d0, d1, d2);
    171            }
    172
    173            this.worldObj.spawnEntityInWorld(entityageable);
    174        }
    175    }
    176
    177    /**
    178     * Called when the entity is attacked.
    179     */
    180    public boolean attackEntityFrom(DamageSource par1DamageSource, int par2)
    181    {
    182        if (this.isEntityInvulnerable())
    183        {
    184            return false;
    185        }
    186        else
    187        {
    188            this.fleeingTick = 60;
    189            this.entityToAttack = null;
    190            this.inLove = 0;
    191            return super.attackEntityFrom(par1DamageSource, par2);
    192        }
    193    }
    194
    195    /**
    196     * Takes a coordinate in and returns a weight to determine how likely this creature will try to path to the block.
    197     * Args: x, y, z
    198     */
    199    public float getBlockPathWeight(int par1, int par2, int par3)
    200    {
    201        return this.worldObj.getBlockId(par1, par2 - 1, par3) == Block.grass.blockID ? 10.0F : this.worldObj.getLightBrightness(par1, par2, par3) - 0.5F;
    202    }
    203
    204    /**
    205     * (abstract) Protected helper method to write subclass entity data to NBT.
    206     */
    207    public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound)
    208    {
    209        super.writeEntityToNBT(par1NBTTagCompound);
    210        par1NBTTagCompound.setInteger("InLove", this.inLove);
    211    }
    212
    213    /**
    214     * (abstract) Protected helper method to read subclass entity data from NBT.
    215     */
    216    public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound)
    217    {
    218        super.readEntityFromNBT(par1NBTTagCompound);
    219        this.inLove = par1NBTTagCompound.getInteger("InLove");
    220    }
    221
    222    /**
    223     * Finds the closest player within 16 blocks to attack, or null if this Entity isn't interested in attacking
    224     * (Animals, Spiders at day, peaceful PigZombies).
    225     */
    226    protected Entity findPlayerToAttack()
    227    {
    228        if (this.fleeingTick > 0)
    229        {
    230            return null;
    231        }
    232        else
    233        {
    234            float f = 8.0F;
    235            List list;
    236            int i;
    237            EntityAnimal entityanimal;
    238
    239            if (this.inLove > 0)
    240            {
    241                list = this.worldObj.getEntitiesWithinAABB(this.getClass(), this.boundingBox.expand((double)f, (double)f, (double)f));
    242
    243                for (i = 0; i < list.size(); ++i)
    244                {
    245                    entityanimal = (EntityAnimal)list.get(i);
    246
    247                    if (entityanimal != this && entityanimal.inLove > 0)
    248                    {
    249                        return entityanimal;
    250                    }
    251                }
    252            }
    253            else if (this.getGrowingAge() == 0)
    254            {
    255                list = this.worldObj.getEntitiesWithinAABB(EntityPlayer.class, this.boundingBox.expand((double)f, (double)f, (double)f));
    256
    257                for (i = 0; i < list.size(); ++i)
    258                {
    259                    EntityPlayer entityplayer = (EntityPlayer)list.get(i);
    260
    261                    if (entityplayer.getCurrentEquippedItem() != null && this.isBreedingItem(entityplayer.getCurrentEquippedItem()))
    262                    {
    263                        return entityplayer;
    264                    }
    265                }
    266            }
    267            else if (this.getGrowingAge() > 0)
    268            {
    269                list = this.worldObj.getEntitiesWithinAABB(this.getClass(), this.boundingBox.expand((double)f, (double)f, (double)f));
    270
    271                for (i = 0; i < list.size(); ++i)
    272                {
    273                    entityanimal = (EntityAnimal)list.get(i);
    274
    275                    if (entityanimal != this && entityanimal.getGrowingAge() < 0)
    276                    {
    277                        return entityanimal;
    278                    }
    279                }
    280            }
    281
    282            return null;
    283        }
    284    }
    285
    286    /**
    287     * Checks if the entity's current position is a valid location to spawn this entity.
    288     */
    289    public boolean getCanSpawnHere()
    290    {
    291        int i = MathHelper.floor_double(this.posX);
    292        int j = MathHelper.floor_double(this.boundingBox.minY);
    293        int k = MathHelper.floor_double(this.posZ);
    294        return this.worldObj.getBlockId(i, j - 1, k) == Block.grass.blockID && this.worldObj.getFullBlockLightValue(i, j, k) > 8 && super.getCanSpawnHere();
    295    }
    296
    297    /**
    298     * Get number of ticks, at least during which the living entity will be silent.
    299     */
    300    public int getTalkInterval()
    301    {
    302        return 120;
    303    }
    304
    305    /**
    306     * Determines if an entity can be despawned, used on idle far away entities
    307     */
    308    protected boolean canDespawn()
    309    {
    310        return false;
    311    }
    312
    313    /**
    314     * Get the experience points the entity currently has.
    315     */
    316    protected int getExperiencePoints(EntityPlayer par1EntityPlayer)
    317    {
    318        return 1 + this.worldObj.rand.nextInt(3);
    319    }
    320
    321    /**
    322     * Checks if the parameter is an item which this animal can be fed to breed it (wheat, carrots or seeds depending on
    323     * the animal type)
    324     */
    325    public boolean isBreedingItem(ItemStack par1ItemStack)
    326    {
    327        return par1ItemStack.itemID == Item.wheat.itemID;
    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 itemstack = par1EntityPlayer.inventory.getCurrentItem();
    336
    337        if (itemstack != null && this.isBreedingItem(itemstack) && this.getGrowingAge() == 0 && this.inLove <= 0)
    338        {
    339            if (!par1EntityPlayer.capabilities.isCreativeMode)
    340            {
    341                --itemstack.stackSize;
    342
    343                if (itemstack.stackSize <= 0)
    344                {
    345                    par1EntityPlayer.inventory.setInventorySlotContents(par1EntityPlayer.inventory.currentItem, (ItemStack)null);
    346                }
    347            }
    348
    349            this.inLove = 600;
    350            this.entityToAttack = null;
    351
    352            for (int i = 0; i < 7; ++i)
    353            {
    354                double d0 = this.rand.nextGaussian() * 0.02D;
    355                double d1 = this.rand.nextGaussian() * 0.02D;
    356                double d2 = this.rand.nextGaussian() * 0.02D;
    357                this.worldObj.spawnParticle("heart", this.posX + (double)(this.rand.nextFloat() * this.width * 2.0F) - (double)this.width, this.posY + 0.5D + (double)(this.rand.nextFloat() * this.height), this.posZ + (double)(this.rand.nextFloat() * this.width * 2.0F) - (double)this.width, d0, d1, d2);
    358            }
    359
    360            return true;
    361        }
    362        else
    363        {
    364            return super.interact(par1EntityPlayer);
    365        }
    366    }
    367
    368    /**
    369     * Returns if the entity is currently in 'love mode'.
    370     */
    371    public boolean isInLove()
    372    {
    373        return this.inLove > 0;
    374    }
    375
    376    public void resetInLove()
    377    {
    378        this.inLove = 0;
    379    }
    380
    381    /**
    382     * Returns true if the mob is currently able to mate with the specified mob.
    383     */
    384    public boolean canMateWith(EntityAnimal par1EntityAnimal)
    385    {
    386        return par1EntityAnimal == this ? false : (par1EntityAnimal.getClass() != this.getClass() ? false : this.isInLove() && par1EntityAnimal.isInLove());
    387    }
    388}