package WayofTime.bloodmagic.alchemyArray; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.ai.EntityAIBase; import net.minecraft.entity.ai.EntityAITasks; import net.minecraft.entity.ai.EntityAITasks.EntityAITaskEntry; import net.minecraft.entity.monster.EntityBlaze; import net.minecraft.entity.monster.EntityEnderman; import net.minecraft.entity.monster.EntityMob; import net.minecraft.entity.monster.EntityPigZombie; import net.minecraft.entity.monster.EntitySilverfish; import net.minecraft.entity.monster.EntitySlime; import net.minecraft.entity.monster.EntitySpider; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.pathfinding.Path; import net.minecraft.pathfinding.PathFinder; import net.minecraft.pathfinding.WalkNodeProcessor; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.world.World; import net.minecraftforge.common.util.FakePlayer; import WayofTime.bloodmagic.api.Constants; import WayofTime.bloodmagic.api.alchemyCrafting.AlchemyArrayEffect; import WayofTime.bloodmagic.fakePlayer.FakePlayerBM; import WayofTime.bloodmagic.tile.TileAlchemyArray; import com.mojang.authlib.GameProfile; /** * Credits for the initial code go to Crazy Pants of EIO. */ public class AlchemyArrayEffectAttractor extends AlchemyArrayEffect { private FakePlayer target; private Set tracking = new HashSet(); private int counter = 0; private int maxMobsAttracted = 10000; private int cooldown = 50; public AlchemyArrayEffectAttractor(String key) { super(key); } @Override public boolean update(TileEntity tile, int ticksActive) { if (tile.getWorld().isRemote) { return false; } BlockPos pos = tile.getPos(); counter++; if (counter < 10) { Iterator itr = tracking.iterator(); while (itr.hasNext()) { EntityLiving ent = itr.next(); onEntityTick(pos, ent); } return false; } counter = 0; World world = tile.getWorld(); Set trackingThisTick = new HashSet(); List entsInBounds = world.getEntitiesWithinAABB(EntityLiving.class, getBounds(pos)); for (EntityLiving ent : entsInBounds) { if (!ent.isDead)// && isMobInFilter(ent)) { double x = (pos.getX() + 0.5D - ent.posX); double y = (pos.getY() + 1D - ent.posY); double z = (pos.getZ() + 0.5D - ent.posZ); double distance = Math.sqrt(x * x + y * y + z * z); if (distance < 2 && tracking.contains(ent)) { setEntityCooldown(pos, ent, cooldown); removeAssignedAITask(pos, ent); continue; } if (!canEntityBeTracked(pos, ent)) { // System.out.println("Cooldown: " + getEntityCooldown(pos, ent)); decrementEntityCooldown(pos, ent); continue; } if (tracking.contains(ent)) { trackingThisTick.add(ent); onEntityTick(pos, ent); } else if (tracking.size() < maxMobsAttracted && trackMob(pos, ent)) { trackingThisTick.add(ent); onTracked(ent); } } } for (EntityLiving e : tracking) { if (!trackingThisTick.contains(e)) { onUntracked(e); } } tracking.clear(); tracking = trackingThisTick; return false; } public boolean canEntityBeTracked(BlockPos pos, EntityLiving entity) { return getEntityCooldown(pos, entity) <= 0; } private String getPosKey(BlockPos pos) { return "BMAttractor:" + pos; } public int getEntityCooldown(BlockPos pos, EntityLiving entity) { return entity.getEntityData().getInteger(getPosKey(pos)); } public void setEntityCooldown(BlockPos pos, EntityLiving entity, int cooldown) { entity.getEntityData().setInteger(getPosKey(pos), cooldown); } public void decrementEntityCooldown(BlockPos pos, EntityLiving entity) { int cooldown = getEntityCooldown(pos, entity); if (cooldown > 0) { setEntityCooldown(pos, entity, cooldown - 1); } } public AxisAlignedBB getBounds(BlockPos pos) { return new AxisAlignedBB(pos).expand(getRange(), getRange(), getRange()); } public float getRange() { return 10; } private void onUntracked(EntityLiving e) { if (e instanceof EntityEnderman) { e.getEntityData().setBoolean("BM:tracked", false); } } private void onTracked(EntityLiving e) { if (e instanceof EntityEnderman) { e.getEntityData().setBoolean("BM:tracked", true); } } private void onEntityTick(BlockPos pos, EntityLiving ent) { if (ent instanceof EntitySlime) { ent.faceEntity(getTarget(ent.worldObj, pos), 10.0F, 20.0F); } else if (ent instanceof EntitySilverfish) { if (counter < 10) { return; } EntitySilverfish sf = (EntitySilverfish) ent; Path pathentity = getPathEntityToEntity(ent, getTarget(ent.worldObj, pos), getRange()); sf.getNavigator().setPath(pathentity, sf.getAIMoveSpeed()); } else if (ent instanceof EntityBlaze) { double x = (pos.getX() + 0.5D - ent.posX); double y = (pos.getY() + 1D - ent.posY); double z = (pos.getZ() + 0.5D - ent.posZ); double distance = Math.sqrt(x * x + y * y + z * z); if (distance > 1.25) { double speed = 0.01; ent.motionX += x / distance * speed; if (y > 0) { ent.motionY += (0.3 - ent.motionY) * 0.3; } ent.motionZ += z / distance * speed; } } else if (ent instanceof EntityPigZombie || ent instanceof EntitySpider) { forceMove(pos, ent); // ent.setAttackTarget(target); } else if (ent instanceof EntityEnderman) { ((EntityEnderman) ent).setAttackTarget(getTarget(ent.worldObj, pos)); } } private void forceMove(BlockPos pos, EntityLiving ent) { double x = (pos.getX() + 0.5D - ent.posX); double y = (pos.getY() + 1D - ent.posY); double z = (pos.getZ() + 0.5D - ent.posZ); double distance = Math.sqrt(x * x + y * y + z * z); if (distance > 2) { EntityMob mod = (EntityMob) ent; mod.faceEntity(getTarget(ent.worldObj, pos), 180, 0); mod.moveEntityWithHeading(0, 0.3f); if (mod.posY < pos.getY()) { mod.setJumping(true); } else { mod.setJumping(false); } } } public Path getPathEntityToEntity(Entity entity, Entity targetEntity, float range) { int targX = MathHelper.floor_double(targetEntity.posX); int targY = MathHelper.floor_double(targetEntity.posY + 1.0D); int targZ = MathHelper.floor_double(targetEntity.posZ); PathFinder pf = new PathFinder(new WalkNodeProcessor()); return pf.findPath(targetEntity.worldObj, (EntityLiving) entity, new BlockPos(targX, targY, targZ), range); } private boolean trackMob(BlockPos pos, EntityLiving ent) { //TODO: Figure out if this crud is needed if (useSetTarget(ent)) { ((EntityMob) ent).setAttackTarget(getTarget(ent.worldObj, pos)); return true; } else if (useSpecialCase(ent)) { return applySpecialCase(pos, ent); } else { return attractUsingAITask(pos, ent); } } private boolean useSetTarget(EntityLiving ent) { return ent instanceof EntityPigZombie || ent instanceof EntitySpider || ent instanceof EntitySilverfish; } public void removeAssignedAITask(BlockPos pos, EntityLiving ent) { Set entries = ent.tasks.taskEntries; EntityAIBase remove = null; for (EntityAITaskEntry entry : entries) { if (entry.action instanceof AttractTask) { AttractTask at = (AttractTask) entry.action; if (at.coord.equals(pos)) { remove = entry.action; } else { continue; } } } if (remove != null) { ent.tasks.removeTask(remove); } } private boolean attractUsingAITask(BlockPos pos, EntityLiving ent) { tracking.add(ent); Set entries = ent.tasks.taskEntries; // boolean hasTask = false; EntityAIBase remove = null; // boolean isTracked; for (EntityAITaskEntry entry : entries) { if (entry.action instanceof AttractTask) { AttractTask at = (AttractTask) entry.action; if (at.coord.equals(pos) || !at.continueExecuting()) { remove = entry.action; } else { return false; } } } if (remove != null) { ent.tasks.removeTask(remove); } cancelCurrentTasks(ent); ent.tasks.addTask(0, new AttractTask(ent, getTarget(ent.worldObj, pos), pos)); return true; } private void cancelCurrentTasks(EntityLiving ent) { Iterator iterator = ent.tasks.taskEntries.iterator(); List currentTasks = new ArrayList(); while (iterator.hasNext()) { EntityAITaskEntry entityaitaskentry = iterator.next(); if (entityaitaskentry != null) { if (entityaitaskentry.action instanceof AttractTask) { continue; } currentTasks.add(entityaitaskentry); } } // Only available way to stop current execution is to remove all current // tasks, then re-add them for (EntityAITaskEntry task : currentTasks) { ent.tasks.removeTask(task.action); ent.tasks.addTask(task.priority, task.action); } } private boolean applySpecialCase(BlockPos pos, EntityLiving ent) { if (ent instanceof EntitySlime) { ent.faceEntity(getTarget(ent.worldObj, pos), 10.0F, 20.0F); // ent.setAttackTarget(getTarget(ent.worldObj, pos)); return true; } else if (ent instanceof EntitySilverfish) { EntitySilverfish es = (EntitySilverfish) ent; Path pathentity = getPathEntityToEntity(ent, getTarget(ent.worldObj, pos), getRange()); es.getNavigator().setPath(pathentity, es.getAIMoveSpeed()); return true; } else if (ent instanceof EntityBlaze) { return true; } return false; } private boolean useSpecialCase(EntityLiving ent) { return ent instanceof EntitySlime || ent instanceof EntitySilverfish || ent instanceof EntityBlaze; } public FakePlayer getTarget(World world, BlockPos pos) { if (target == null) { // System.out.println("...Hi? " + pos); target = new Target(world, pos); } return target; } private class Target extends FakePlayerBM { public Target(World world, BlockPos pos) { super(world, pos, new GameProfile(null, Constants.Mod.MODID + "ArrayAttractor" + ":" + pos)); posY += 1; } } private static class AttractTask extends EntityAIBase { private EntityLiving mob; private BlockPos coord; private FakePlayer target; private int updatesSincePathing; private boolean started = false; private AttractTask(EntityLiving mob, FakePlayer target, BlockPos coord) { this.mob = mob; this.coord = coord; this.target = target; } @Override public boolean shouldExecute() { return continueExecuting(); } @Override public void resetTask() { started = false; updatesSincePathing = 0; } @Override public boolean continueExecuting() { boolean res = false; //TODO: TileEntity te = mob.worldObj.getTileEntity(coord); if (te instanceof TileAlchemyArray) { res = true; } return res; } @Override public boolean isInterruptible() { return true; } @Override public void updateTask() { if (!started || updatesSincePathing > 20) { started = true; int speed = 1; // mob.getNavigator().setAvoidsWater(false); boolean res = mob.getNavigator().tryMoveToEntityLiving(target, speed); if (!res) { mob.getNavigator().tryMoveToXYZ(target.posX, target.posY + 1, target.posZ, speed); } updatesSincePathing = 0; } else { updatesSincePathing++; } } } @Override public void writeToNBT(NBTTagCompound tag) { } @Override public void readFromNBT(NBTTagCompound tag) { } @Override public AlchemyArrayEffect getNewCopy() { return new AlchemyArrayEffectAttractor(key); } }