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 }