package com.github.jamesnorris.ablockalypse.threading.inherent;
import java.util.List;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Creature;
import com.github.jamesnorris.ablockalypse.aspect.ZAMob;
import com.github.jamesnorris.ablockalypse.behavior.Targettable;
import com.github.jamesnorris.ablockalypse.enumerated.Setting;
import com.github.jamesnorris.ablockalypse.threading.DelayedTask;
import com.github.jamesnorris.ablockalypse.threading.RepeatingTask;
import com.github.jamesnorris.ablockalypse.utility.AblockalypseUtility;
import com.github.jamesnorris.ablockalypse.utility.BukkitUtility;
import com.github.jamesnorris.mcpath.PathNode;
import com.github.jamesnorris.mcpath.Pathfinder;
public class MobTargetterTask extends RepeatingTask {
public class StandStillPreventorTask extends RepeatingTask {
private int warnLevel;
private MobTargetterTask task;
private Location lastMoved;
public StandStillPreventorTask(MobTargetterTask task, ZAMob mob) {
super(20, true);
this.task = task;
lastMoved = mob.getEntity().getLocation();
}
@Override public void run() {
if (mob == null || !mob.isValid()) {
cancel();
return;
}
Location updated = mob.getEntity().getLocation();
if (task.target == null) {
return;
}
if (BukkitUtility.locationMatch(updated, lastMoved, 1) && !BukkitUtility.locationMatch(updated, task.target.updateTarget(), 1)) {
warnLevel++;
if (warnLevel >= 5) {
task.forceSafeDistance(updated, task.target.updateTarget());
}
} else {
lastMoved = updated;
warnLevel = 0;
}
}
}
private static final int INTERVAL = 1;
private int max = (Integer) Setting.MAX_PATHFINDER_NODES.getSetting();
private double current = 0;
private ZAMob mob;
private Targettable target;
private List<PathNode> nodes;
private StandStillPreventorTask ssPrevent;
public MobTargetterTask(ZAMob mob, final Location target, boolean autorun) {
this(mob, new Targettable() {
@Override public boolean isResponsive() {
return true;
}
@Override public boolean isTargettedBy(ZAMob mob) {
return false;
}
@Override public Location updateTarget() {
return target;
}
}, autorun);
}
public MobTargetterTask(ZAMob mob, Targettable target, boolean autorun) {
super(INTERVAL, autorun);
this.mob = mob;
this.target = target;
ssPrevent = new StandStillPreventorTask(this, mob);
}
@Override public void cancel() {
ssPrevent.cancel();
super.cancel();
}
public void panic(int ticks) {
final Location tempTarget = target != null ? target.updateTarget() : mob.getEntity().getLocation();
new RepeatingTask(ticks / 10, true) {
@Override public void run() {
setTarget(BukkitUtility.getNearbyLocation(tempTarget, 5, 15, 0, 0, 5, 15));
}
};
new DelayedTask(ticks, true) {
@Override public void run() {
setTarget(tempTarget);
}
};
}
@Override public void run() {
if (mob == null || !mob.isValid()) {
cancel();
return;
}
if (target == null) {
return;
}
if (!target.isResponsive()) {
mob.retarget();
}
if (target == null) {
return;//2nd check to make sure mob.retarget() didn't set the target to null
}
World world = mob.getEntity().getWorld();
ensureSameWorld(world);
updatePath();
ensureSafeDistance();
Location to = nodes.size() > (int) current ? nodes.get((int) current).toLocation(world) : nodes.get(nodes.size() - 1).toLocation(world);
if (to == null) {
return;
}
if (nodes.size() > (int) current + 1) {
to = between(to, nodes.get((int) current + 1).toLocation(world), current % 1);
}
raise(to);
if (to.getBlock().getRelative(0, -1, 0).isEmpty()) {
return;// delay until ground is hit
}
setLocationDirection(to, target.updateTarget());
if (!target.isTargettedBy(mob)) {
if (mob.getEntity() instanceof Creature) {
// prevents wandering
((Creature) mob.getEntity()).setTarget(null);
}
mob.getEntity().teleport(to);
current += mob.getSpeed();
}
}
public void setTarget(final Location target) {
setTarget(new Targettable() {
@Override public boolean isResponsive() {
return true;
}
@Override public boolean isTargettedBy(ZAMob mob) {
return false;
}
@Override public Location updateTarget() {
return target;
}
});
}
public void setTarget(Targettable target) {
this.target = target;
if (mob != null && mob.getEntity() != null && !mob.getEntity().isDead() && target != null) {
updatePath();
}
}
private Location between(Location loc1, Location loc2, double part) {
return new Location(loc1.getWorld(), loc1.getX() + (loc2.getX() - loc1.getX()) * part, loc1.getY() + (loc2.getY() - loc1.getY()) * part, loc1.getZ() + (loc2.getZ() - loc1.getZ()) * part);
}
private void ensureSafeDistance() {
Location start = mob.getEntity().getLocation();
Location finish = target.updateTarget();
if (start.distance(finish) > max) {
forceSafeDistance(start, finish);
}
}
private void ensureSameWorld(World world) {
Location updatedTarget = target.updateTarget();
if (!world.getUID().equals(updatedTarget.getWorld().getUID())) {
Location to = mob.getEntity().getLocation().clone();
to.setWorld(updatedTarget.getWorld());
mob.getEntity().teleport(to);
}
}
private void forceSafeDistance(Location start, Location finish) {
mob.getEntity().teleport(BukkitUtility.getNearbyLocation(start, max * (6 / 8), max * (7 / 8), max * (6 / 8), max * (7 / 8), max * (6 / 8), max * (7 / 8)));
}
private void raise(Location move) {
move.setY(mob.getEntity().getLocation().getY());
}
private void setLocationDirection(Location current, Location look) {
float yaw = (float) Math.toDegrees(Math.atan((current.getZ() - look.getZ()) / (current.getX() - look.getX())));
float pitch = (float) Math.toDegrees(Math.atan((current.getY() - look.getY()) / (current.getX() - look.getX())));
current.setYaw(360 - yaw % 360/* back to notchian */);
current.setPitch(pitch);
}
private void updatePath() {
Location eLoc = mob.getEntity().getLocation();
Location updatedTarget = target.updateTarget();
if (nodes == null || updatedTarget.distance(nodes.get(nodes.size() - 1).toLocation(updatedTarget.getWorld())) >= 2 && PathNode.fromLocation(eLoc).distance(nodes.get(0)) >= 2) {
current %= 1;
nodes = new Pathfinder(eLoc, updatedTarget, AblockalypseUtility.getGoals(updatedTarget.getWorld(), mob.getLength(), mob.getWidth(), mob.getHeight(), mob.getHitBox().getRotationY())).calculate((Integer) Setting.MAX_PATHFINDER_NODES.getSetting()).getNodes();
}
}
}