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