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 this.fleeingTick = 60;
179 this.entityToAttack = null;
180 this.inLove = 0;
181 return super.attackEntityFrom(par1DamageSource, par2);
182 }
183
184 /**
185 * Takes a coordinate in and returns a weight to determine how likely this creature will try to path to the block.
186 * Args: x, y, z
187 */
188 public float getBlockPathWeight(int par1, int par2, int par3)
189 {
190 return this.worldObj.getBlockId(par1, par2 - 1, par3) == Block.grass.blockID ? 10.0F : this.worldObj.getLightBrightness(par1, par2, par3) - 0.5F;
191 }
192
193 /**
194 * (abstract) Protected helper method to write subclass entity data to NBT.
195 */
196 public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound)
197 {
198 super.writeEntityToNBT(par1NBTTagCompound);
199 par1NBTTagCompound.setInteger("InLove", this.inLove);
200 }
201
202 /**
203 * (abstract) Protected helper method to read subclass entity data from NBT.
204 */
205 public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound)
206 {
207 super.readEntityFromNBT(par1NBTTagCompound);
208 this.inLove = par1NBTTagCompound.getInteger("InLove");
209 }
210
211 /**
212 * Finds the closest player within 16 blocks to attack, or null if this Entity isn't interested in attacking
213 * (Animals, Spiders at day, peaceful PigZombies).
214 */
215 protected Entity findPlayerToAttack()
216 {
217 if (this.fleeingTick > 0)
218 {
219 return null;
220 }
221 else
222 {
223 float var1 = 8.0F;
224 List var2;
225 Iterator var3;
226 EntityAnimal var4;
227
228 if (this.inLove > 0)
229 {
230 var2 = this.worldObj.getEntitiesWithinAABB(this.getClass(), this.boundingBox.expand((double)var1, (double)var1, (double)var1));
231 var3 = var2.iterator();
232
233 while (var3.hasNext())
234 {
235 var4 = (EntityAnimal)var3.next();
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 var3 = var2.iterator();
247
248 while (var3.hasNext())
249 {
250 EntityPlayer var5 = (EntityPlayer)var3.next();
251
252 if (var5.getCurrentEquippedItem() != null && this.isBreedingItem(var5.getCurrentEquippedItem()))
253 {
254 return var5;
255 }
256 }
257 }
258 else if (this.getGrowingAge() > 0)
259 {
260 var2 = this.worldObj.getEntitiesWithinAABB(this.getClass(), this.boundingBox.expand((double)var1, (double)var1, (double)var1));
261 var3 = var2.iterator();
262
263 while (var3.hasNext())
264 {
265 var4 = (EntityAnimal)var3.next();
266
267 if (var4 != this && var4.getGrowingAge() < 0)
268 {
269 return var4;
270 }
271 }
272 }
273
274 return null;
275 }
276 }
277
278 /**
279 * Checks if the entity's current position is a valid location to spawn this entity.
280 */
281 public boolean getCanSpawnHere()
282 {
283 int var1 = MathHelper.floor_double(this.posX);
284 int var2 = MathHelper.floor_double(this.boundingBox.minY);
285 int var3 = MathHelper.floor_double(this.posZ);
286 return this.worldObj.getBlockId(var1, var2 - 1, var3) == Block.grass.blockID && this.worldObj.getFullBlockLightValue(var1, var2, var3) > 8 && super.getCanSpawnHere();
287 }
288
289 /**
290 * Get number of ticks, at least during which the living entity will be silent.
291 */
292 public int getTalkInterval()
293 {
294 return 120;
295 }
296
297 /**
298 * Determines if an entity can be despawned, used on idle far away entities
299 */
300 protected boolean canDespawn()
301 {
302 return false;
303 }
304
305 /**
306 * Get the experience points the entity currently has.
307 */
308 protected int getExperiencePoints(EntityPlayer par1EntityPlayer)
309 {
310 return 1 + this.worldObj.rand.nextInt(3);
311 }
312
313 /**
314 * Checks if the parameter is an item which this animal can be fed to breed it (wheat, carrots or seeds depending on
315 * the animal type)
316 */
317 public boolean isBreedingItem(ItemStack par1ItemStack)
318 {
319 return par1ItemStack.itemID == Item.wheat.shiftedIndex;
320 }
321
322 /**
323 * Called when a player interacts with a mob. e.g. gets milk from a cow, gets into the saddle on a pig.
324 */
325 public boolean interact(EntityPlayer par1EntityPlayer)
326 {
327 ItemStack var2 = par1EntityPlayer.inventory.getCurrentItem();
328
329 if (var2 != null && this.isBreedingItem(var2) && this.getGrowingAge() == 0)
330 {
331 if (!par1EntityPlayer.capabilities.isCreativeMode)
332 {
333 --var2.stackSize;
334
335 if (var2.stackSize <= 0)
336 {
337 par1EntityPlayer.inventory.setInventorySlotContents(par1EntityPlayer.inventory.currentItem, (ItemStack)null);
338 }
339 }
340
341 this.inLove = 600;
342 this.entityToAttack = null;
343
344 for (int var3 = 0; var3 < 7; ++var3)
345 {
346 double var4 = this.rand.nextGaussian() * 0.02D;
347 double var6 = this.rand.nextGaussian() * 0.02D;
348 double var8 = this.rand.nextGaussian() * 0.02D;
349 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);
350 }
351
352 return true;
353 }
354 else
355 {
356 return super.interact(par1EntityPlayer);
357 }
358 }
359
360 /**
361 * Returns if the entity is currently in 'love mode'.
362 */
363 public boolean isInLove()
364 {
365 return this.inLove > 0;
366 }
367
368 public void resetInLove()
369 {
370 this.inLove = 0;
371 }
372
373 /**
374 * Returns true if the mob is currently able to mate with the specified mob.
375 */
376 public boolean canMateWith(EntityAnimal par1EntityAnimal)
377 {
378 return par1EntityAnimal == this ? false : (par1EntityAnimal.getClass() != this.getClass() ? false : this.isInLove() && par1EntityAnimal.isInLove());
379 }
380 }