/* * Copyright (c) CovertJaguar, 2014 http://railcraft.info * * This code is the property of CovertJaguar * and may only be used with explicit written * permission unless otherwise specified on the * license page at http://railcraft.info/wiki/info:license. */ package mods.railcraft.common.blocks.machine.alpha.ai; import cpw.mods.fml.common.ObfuscationReflectionHelper; import java.util.Iterator; import java.util.List; import java.util.Random; import net.minecraft.entity.ai.EntityAIBase; import net.minecraft.entity.ai.EntityAIMate; import net.minecraft.entity.ai.EntityAISit; import net.minecraft.entity.EntityAgeable; import net.minecraft.entity.ai.EntityAITasks.EntityAITaskEntry; import net.minecraft.entity.passive.EntityAnimal; import net.minecraft.entity.passive.EntityCow; import net.minecraft.entity.passive.EntityOcelot; import net.minecraft.entity.passive.EntitySheep; import net.minecraft.entity.passive.EntityTameable; import net.minecraft.world.World; public class EntityAIMateBreeding extends EntityAIBase { private static final int MAX_ANIMALS = 6; private final EntityAnimal theAnimal; World theWorld; private EntityAnimal targetMate; /** * Delay preventing a baby from spawning immediately when two mate-able animals find each other. */ int spawnBabyDelay = 0; /** The speed the creature moves at during mating behavior. */ float moveSpeed; public EntityAIMateBreeding(EntityAnimal animal, float moveSpeed) { this.theAnimal = animal; this.theWorld = animal.worldObj; this.moveSpeed = moveSpeed; } /** * Returns whether the EntityAIBase should begin execution. * @return */ @Override public boolean shouldExecute() { if(!this.theAnimal.isInLove()) { return false; } List nearbyEntites = theAnimal.worldObj.getEntitiesWithinAABB(EntityAnimal.class, theAnimal.boundingBox.expand(1, 1, 1)); if(nearbyEntites.size() > MAX_ANIMALS) { return false; } targetMate = getNearbyMate(); return targetMate != null; } /** * Returns whether an in-progress EntityAIBase should continue executing * @return */ @Override public boolean continueExecuting() { return this.targetMate.isEntityAlive() && this.targetMate.isInLove() && this.spawnBabyDelay < 60; } /** * Resets the task */ @Override public void resetTask() { this.targetMate = null; this.spawnBabyDelay = 0; } /** * Updates the task */ @Override public void updateTask() { this.theAnimal.getLookHelper().setLookPositionWithEntity(this.targetMate, 10.0F, (float)this.theAnimal.getVerticalFaceSpeed()); this.theAnimal.getNavigator().tryMoveToEntityLiving(this.targetMate, this.moveSpeed); ++this.spawnBabyDelay; if(this.spawnBabyDelay == 60) { double litterSize = 1.75; if(theAnimal instanceof EntityCow || theAnimal instanceof EntitySheep) { litterSize = 0; } int babies = 1; if(litterSize > 0) { babies += (int)Math.round(Math.abs(theAnimal.getRNG().nextGaussian()) * litterSize); } for(int i = 0; i < babies; i++) { spawnBaby(); } } } public static void modifyAI(EntityAnimal animal) { boolean tame = animal instanceof EntityTameable; int matePriority = -1; int sitPriority = -1; boolean hasDespawn = false; Iterator<EntityAITaskEntry> it = ((List<EntityAITaskEntry>)animal.tasks.taskEntries).iterator(); while(it.hasNext()) { EntityAITaskEntry task = it.next(); if(tame && task.action instanceof EntityAISit) { sitPriority = task.priority; it.remove(); } else if(task.action instanceof EntityAIMate) { matePriority = task.priority; it.remove(); } else if(task.action instanceof EntityAIDespawn) { hasDespawn = true; } } if(tame) { ((EntityTameable)animal).setTamed(true); } if(!hasDespawn) { animal.tasks.addTask(0, new EntityAIDespawn(animal)); } if(matePriority > 0) { animal.tasks.addTask(matePriority, new EntityAIMateBreeding(animal, 0.25f)); if(tame) { animal.tasks.addTask(6, new EntityAISitRandom((EntityTameable)animal)); } } if(sitPriority > 0) { EntityAISitBred aiSit = new EntityAISitBred((EntityTameable)animal); animal.tasks.addTask(sitPriority, aiSit); // ObfuscationReflectionHelper.setPrivateValue(EntityTameable.class, (EntityTameable)animal, aiSit, "d", "aiSit"); ObfuscationReflectionHelper.setPrivateValue(EntityTameable.class, (EntityTameable)animal, aiSit, 0); } } /** * Loops through nearby animals and finds another animal of the same type that can be mated with. Returns the first * valid mate found. */ private EntityAnimal getNearbyMate() { float var1 = 8.0F; List var2 = this.theWorld.getEntitiesWithinAABB(this.theAnimal.getClass(), this.theAnimal.boundingBox.expand((double)var1, (double)var1, (double)var1)); Iterator entity = var2.iterator(); EntityAnimal target; do { if(!entity.hasNext()) { return null; } target = (EntityAnimal)entity.next(); } while(!canMateWith(theAnimal, target)); return target; } public boolean canMateWith(EntityAnimal animal, EntityAnimal target) { if(target == animal) { return false; } if(animal.getClass() == target.getClass()) { boolean isSitting = target instanceof EntityTameable && ((EntityTameable)target).isSitting(); return !isSitting && animal.isInLove() && target.isInLove(); } return false; } /** * Spawns a baby animal of the same type. */ private void spawnBaby() { EntityAgeable baby = this.theAnimal.createChild(this.targetMate); if(baby instanceof EntityAnimal) { this.theAnimal.setGrowingAge(3600); // 6000 this.targetMate.setGrowingAge(3600); // 6000 this.theAnimal.resetInLove(); this.targetMate.resetInLove(); baby.setGrowingAge(-12000); // -24000 modifyAI((EntityAnimal)baby); Random rand = this.theAnimal.getRNG(); if(baby instanceof EntityOcelot) { EntityOcelot cat = (EntityOcelot)baby; if(rand.nextInt(10) == 0) { cat.setTameSkin(baby.worldObj.rand.nextInt(4)); } } double x = rand.nextGaussian() * 0.2D; double z = rand.nextGaussian() * 0.2D; baby.setLocationAndAngles(this.theAnimal.posX + x, this.theAnimal.posY, this.theAnimal.posZ + z, 0.0F, 0.0F); this.theWorld.spawnEntityInWorld(baby); for(int i = 0; i < 7; ++i) { double px = rand.nextGaussian() * 0.02D; double py = rand.nextGaussian() * 0.02D; double pz = rand.nextGaussian() * 0.02D; this.theWorld.spawnParticle("heart", this.theAnimal.posX + (double)(rand.nextFloat() * this.theAnimal.width * 2.0F) - (double)this.theAnimal.width, this.theAnimal.posY + 0.5D + (double)(rand.nextFloat() * this.theAnimal.height), this.theAnimal.posZ + (double)(rand.nextFloat() * this.theAnimal.width * 2.0F) - (double)this.theAnimal.width, px, py, pz); } } } }