package net.minecraft.entity.passive; import java.util.List; import net.minecraft.block.Block; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityAgeable; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.DamageSource; import net.minecraft.util.MathHelper; import net.minecraft.world.World; public abstract class EntityAnimal extends EntityAgeable implements IAnimals { public int inLove; /** * This is representation of a counter for reproduction progress. (Note that this is different from the inLove which * represent being in Love-Mode) */ private int breeding = 0; public EntityAnimal(World par1World) { super(par1World); } /** * main AI tick function, replaces updateEntityActionState */ protected void updateAITick() { if (this.getGrowingAge() != 0) { this.inLove = 0; } super.updateAITick(); } /** * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons * use this to react to sunlight and start to burn. */ public void onLivingUpdate() { super.onLivingUpdate(); if (this.getGrowingAge() != 0) { this.inLove = 0; } if (this.inLove > 0) { --this.inLove; String var1 = "heart"; if (this.inLove % 10 == 0) { double var2 = this.rand.nextGaussian() * 0.02D; double var4 = this.rand.nextGaussian() * 0.02D; double var6 = this.rand.nextGaussian() * 0.02D; 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); } } else { this.breeding = 0; } } /** * Basic mob attack. Default to touch of death in EntityCreature. Overridden by each mob to define their attack. */ protected void attackEntity(Entity par1Entity, float par2) { if (par1Entity instanceof EntityPlayer) { if (par2 < 3.0F) { double var3 = par1Entity.posX - this.posX; double var5 = par1Entity.posZ - this.posZ; this.rotationYaw = (float)(Math.atan2(var5, var3) * 180.0D / Math.PI) - 90.0F; this.hasAttacked = true; } EntityPlayer var7 = (EntityPlayer)par1Entity; if (var7.getCurrentEquippedItem() == null || !this.isBreedingItem(var7.getCurrentEquippedItem())) { this.entityToAttack = null; } } else if (par1Entity instanceof EntityAnimal) { EntityAnimal var8 = (EntityAnimal)par1Entity; if (this.getGrowingAge() > 0 && var8.getGrowingAge() < 0) { if ((double)par2 < 2.5D) { this.hasAttacked = true; } } else if (this.inLove > 0 && var8.inLove > 0) { if (var8.entityToAttack == null) { var8.entityToAttack = this; } if (var8.entityToAttack == this && (double)par2 < 3.5D) { ++var8.inLove; ++this.inLove; ++this.breeding; if (this.breeding % 4 == 0) { 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); } if (this.breeding == 60) { this.procreate((EntityAnimal)par1Entity); } } else { this.breeding = 0; } } else { this.breeding = 0; this.entityToAttack = null; } } } /** * Creates a baby animal according to the animal type of the target at the actual position and spawns 'love' * particles. */ private void procreate(EntityAnimal par1EntityAnimal) { EntityAgeable var2 = this.createChild(par1EntityAnimal); if (var2 != null) { this.setGrowingAge(6000); par1EntityAnimal.setGrowingAge(6000); this.inLove = 0; this.breeding = 0; this.entityToAttack = null; par1EntityAnimal.entityToAttack = null; par1EntityAnimal.breeding = 0; par1EntityAnimal.inLove = 0; var2.setGrowingAge(-24000); var2.setLocationAndAngles(this.posX, this.posY, this.posZ, this.rotationYaw, this.rotationPitch); for (int var3 = 0; var3 < 7; ++var3) { double var4 = this.rand.nextGaussian() * 0.02D; double var6 = this.rand.nextGaussian() * 0.02D; double var8 = this.rand.nextGaussian() * 0.02D; 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); } this.worldObj.spawnEntityInWorld(var2); } } /** * Called when the entity is attacked. */ public boolean attackEntityFrom(DamageSource par1DamageSource, int par2) { if (this.isEntityInvulnerable()) { return false; } else { this.fleeingTick = 60; this.entityToAttack = null; this.inLove = 0; return super.attackEntityFrom(par1DamageSource, par2); } } /** * Takes a coordinate in and returns a weight to determine how likely this creature will try to path to the block. * Args: x, y, z */ public float getBlockPathWeight(int par1, int par2, int par3) { return this.worldObj.getBlockId(par1, par2 - 1, par3) == Block.grass.blockID ? 10.0F : this.worldObj.getLightBrightness(par1, par2, par3) - 0.5F; } /** * (abstract) Protected helper method to write subclass entity data to NBT. */ public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound) { super.writeEntityToNBT(par1NBTTagCompound); par1NBTTagCompound.setInteger("InLove", this.inLove); } /** * (abstract) Protected helper method to read subclass entity data from NBT. */ public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound) { super.readEntityFromNBT(par1NBTTagCompound); this.inLove = par1NBTTagCompound.getInteger("InLove"); } /** * Finds the closest player within 16 blocks to attack, or null if this Entity isn't interested in attacking * (Animals, Spiders at day, peaceful PigZombies). */ protected Entity findPlayerToAttack() { if (this.fleeingTick > 0) { return null; } else { float var1 = 8.0F; List var2; int var3; EntityAnimal var4; if (this.inLove > 0) { var2 = this.worldObj.getEntitiesWithinAABB(this.getClass(), this.boundingBox.expand((double)var1, (double)var1, (double)var1)); for (var3 = 0; var3 < var2.size(); ++var3) { var4 = (EntityAnimal)var2.get(var3); if (var4 != this && var4.inLove > 0) { return var4; } } } else if (this.getGrowingAge() == 0) { var2 = this.worldObj.getEntitiesWithinAABB(EntityPlayer.class, this.boundingBox.expand((double)var1, (double)var1, (double)var1)); for (var3 = 0; var3 < var2.size(); ++var3) { EntityPlayer var5 = (EntityPlayer)var2.get(var3); if (var5.getCurrentEquippedItem() != null && this.isBreedingItem(var5.getCurrentEquippedItem())) { return var5; } } } else if (this.getGrowingAge() > 0) { var2 = this.worldObj.getEntitiesWithinAABB(this.getClass(), this.boundingBox.expand((double)var1, (double)var1, (double)var1)); for (var3 = 0; var3 < var2.size(); ++var3) { var4 = (EntityAnimal)var2.get(var3); if (var4 != this && var4.getGrowingAge() < 0) { return var4; } } } return null; } } /** * Checks if the entity's current position is a valid location to spawn this entity. */ public boolean getCanSpawnHere() { int var1 = MathHelper.floor_double(this.posX); int var2 = MathHelper.floor_double(this.boundingBox.minY); int var3 = MathHelper.floor_double(this.posZ); return this.worldObj.getBlockId(var1, var2 - 1, var3) == Block.grass.blockID && this.worldObj.getFullBlockLightValue(var1, var2, var3) > 8 && super.getCanSpawnHere(); } /** * Get number of ticks, at least during which the living entity will be silent. */ public int getTalkInterval() { return 120; } /** * Determines if an entity can be despawned, used on idle far away entities */ protected boolean canDespawn() { return false; } /** * Get the experience points the entity currently has. */ protected int getExperiencePoints(EntityPlayer par1EntityPlayer) { return 1 + this.worldObj.rand.nextInt(3); } /** * Checks if the parameter is an item which this animal can be fed to breed it (wheat, carrots or seeds depending on * the animal type) */ public boolean isBreedingItem(ItemStack par1ItemStack) { return par1ItemStack.itemID == Item.wheat.itemID; } /** * Called when a player interacts with a mob. e.g. gets milk from a cow, gets into the saddle on a pig. */ public boolean interact(EntityPlayer par1EntityPlayer) { ItemStack var2 = par1EntityPlayer.inventory.getCurrentItem(); if (var2 != null && this.isBreedingItem(var2) && this.getGrowingAge() == 0) { if (!par1EntityPlayer.capabilities.isCreativeMode) { --var2.stackSize; if (var2.stackSize <= 0) { par1EntityPlayer.inventory.setInventorySlotContents(par1EntityPlayer.inventory.currentItem, (ItemStack)null); } } this.inLove = 600; this.entityToAttack = null; for (int var3 = 0; var3 < 7; ++var3) { double var4 = this.rand.nextGaussian() * 0.02D; double var6 = this.rand.nextGaussian() * 0.02D; double var8 = this.rand.nextGaussian() * 0.02D; 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); } return true; } else { return super.interact(par1EntityPlayer); } } /** * Returns if the entity is currently in 'love mode'. */ public boolean isInLove() { return this.inLove > 0; } public void resetInLove() { this.inLove = 0; } /** * Returns true if the mob is currently able to mate with the specified mob. */ public boolean canMateWith(EntityAnimal par1EntityAnimal) { return par1EntityAnimal == this ? false : (par1EntityAnimal.getClass() != this.getClass() ? false : this.isInLove() && par1EntityAnimal.isInLove()); } }