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 }