001    package net.minecraft.entity.passive;
002    
003    import java.util.List;
004    import net.minecraft.block.Block;
005    import net.minecraft.entity.Entity;
006    import net.minecraft.entity.EntityAgeable;
007    import net.minecraft.entity.player.EntityPlayer;
008    import net.minecraft.item.Item;
009    import net.minecraft.item.ItemStack;
010    import net.minecraft.nbt.NBTTagCompound;
011    import net.minecraft.util.DamageSource;
012    import net.minecraft.util.MathHelper;
013    import net.minecraft.world.World;
014    
015    public 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 var1 = "heart";
060    
061                if (this.inLove % 10 == 0)
062                {
063                    double var2 = this.rand.nextGaussian() * 0.02D;
064                    double var4 = this.rand.nextGaussian() * 0.02D;
065                    double var6 = this.rand.nextGaussian() * 0.02D;
066                    this.worldObj.spawnParticle(var1, 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, var2, var4, var6);
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 var3 = par1Entity.posX - this.posX;
085                    double var5 = par1Entity.posZ - this.posZ;
086                    this.rotationYaw = (float)(Math.atan2(var5, var3) * 180.0D / Math.PI) - 90.0F;
087                    this.hasAttacked = true;
088                }
089    
090                EntityPlayer var7 = (EntityPlayer)par1Entity;
091    
092                if (var7.getCurrentEquippedItem() == null || !this.isBreedingItem(var7.getCurrentEquippedItem()))
093                {
094                    this.entityToAttack = null;
095                }
096            }
097            else if (par1Entity instanceof EntityAnimal)
098            {
099                EntityAnimal var8 = (EntityAnimal)par1Entity;
100    
101                if (this.getGrowingAge() > 0 && var8.getGrowingAge() < 0)
102                {
103                    if ((double)par2 < 2.5D)
104                    {
105                        this.hasAttacked = true;
106                    }
107                }
108                else if (this.inLove > 0 && var8.inLove > 0)
109                {
110                    if (var8.entityToAttack == null)
111                    {
112                        var8.entityToAttack = this;
113                    }
114    
115                    if (var8.entityToAttack == this && (double)par2 < 3.5D)
116                    {
117                        ++var8.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 var2 = this.createChild(par1EntityAnimal);
151    
152            if (var2 != 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                var2.setGrowingAge(-24000);
163                var2.setLocationAndAngles(this.posX, this.posY, this.posZ, this.rotationYaw, this.rotationPitch);
164    
165                for (int var3 = 0; var3 < 7; ++var3)
166                {
167                    double var4 = this.rand.nextGaussian() * 0.02D;
168                    double var6 = this.rand.nextGaussian() * 0.02D;
169                    double var8 = 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, var4, var6, var8);
171                }
172    
173                this.worldObj.spawnEntityInWorld(var2);
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 var1 = 8.0F;
235                List var2;
236                int var3;
237                EntityAnimal var4;
238    
239                if (this.inLove > 0)
240                {
241                    var2 = this.worldObj.getEntitiesWithinAABB(this.getClass(), this.boundingBox.expand((double)var1, (double)var1, (double)var1));
242    
243                    for (var3 = 0; var3 < var2.size(); ++var3)
244                    {
245                        var4 = (EntityAnimal)var2.get(var3);
246    
247                        if (var4 != this && var4.inLove > 0)
248                        {
249                            return var4;
250                        }
251                    }
252                }
253                else if (this.getGrowingAge() == 0)
254                {
255                    var2 = this.worldObj.getEntitiesWithinAABB(EntityPlayer.class, this.boundingBox.expand((double)var1, (double)var1, (double)var1));
256    
257                    for (var3 = 0; var3 < var2.size(); ++var3)
258                    {
259                        EntityPlayer var5 = (EntityPlayer)var2.get(var3);
260    
261                        if (var5.getCurrentEquippedItem() != null && this.isBreedingItem(var5.getCurrentEquippedItem()))
262                        {
263                            return var5;
264                        }
265                    }
266                }
267                else if (this.getGrowingAge() > 0)
268                {
269                    var2 = this.worldObj.getEntitiesWithinAABB(this.getClass(), this.boundingBox.expand((double)var1, (double)var1, (double)var1));
270    
271                    for (var3 = 0; var3 < var2.size(); ++var3)
272                    {
273                        var4 = (EntityAnimal)var2.get(var3);
274    
275                        if (var4 != this && var4.getGrowingAge() < 0)
276                        {
277                            return var4;
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 var1 = MathHelper.floor_double(this.posX);
292            int var2 = MathHelper.floor_double(this.boundingBox.minY);
293            int var3 = MathHelper.floor_double(this.posZ);
294            return this.worldObj.getBlockId(var1, var2 - 1, var3) == Block.grass.blockID && this.worldObj.getFullBlockLightValue(var1, var2, var3) > 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 var2 = par1EntityPlayer.inventory.getCurrentItem();
336    
337            if (var2 != null && this.isBreedingItem(var2) && this.getGrowingAge() == 0)
338            {
339                if (!par1EntityPlayer.capabilities.isCreativeMode)
340                {
341                    --var2.stackSize;
342    
343                    if (var2.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 var3 = 0; var3 < 7; ++var3)
353                {
354                    double var4 = this.rand.nextGaussian() * 0.02D;
355                    double var6 = this.rand.nextGaussian() * 0.02D;
356                    double var8 = 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, var4, var6, var8);
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    }