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