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}