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