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 }