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 }