diff --git a/src/main/java/net/citizensnpcs/api/ai/AbstractPathStrategy.java b/src/main/java/net/citizensnpcs/api/ai/AbstractPathStrategy.java new file mode 100644 index 00000000..a1502003 --- /dev/null +++ b/src/main/java/net/citizensnpcs/api/ai/AbstractPathStrategy.java @@ -0,0 +1,32 @@ +package net.citizensnpcs.api.ai; + +import net.citizensnpcs.api.ai.TargetType; +import net.citizensnpcs.api.ai.event.CancelReason; + +public abstract class AbstractPathStrategy implements PathStrategy { + private CancelReason cancelReason; + private final TargetType type; + + protected AbstractPathStrategy(TargetType type) { + this.type = type; + } + + @Override + public void clearCancelReason() { + cancelReason = null; + } + + @Override + public CancelReason getCancelReason() { + return cancelReason; + } + + @Override + public TargetType getTargetType() { + return type; + } + + protected void setCancelReason(CancelReason reason) { + cancelReason = reason; + } +} \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/api/ai/Navigator.java b/src/main/java/net/citizensnpcs/api/ai/Navigator.java index 80e8d9a1..d7c5f950 100644 --- a/src/main/java/net/citizensnpcs/api/ai/Navigator.java +++ b/src/main/java/net/citizensnpcs/api/ai/Navigator.java @@ -45,6 +45,12 @@ public interface Navigator { */ NPC getNPC(); + /** + * + * @return The current {@link PathStrategy} or null if the navigator is not pathfinding + */ + PathStrategy getPathStrategy(); + /** * Returns the current {@link Location} being navigated towards - this is not necessarily permanent and may change, * for example when pathing towards a moving {@link Entity}. May return null. diff --git a/src/main/java/net/citizensnpcs/api/ai/PathStrategy.java b/src/main/java/net/citizensnpcs/api/ai/PathStrategy.java new file mode 100644 index 00000000..1e6b13ac --- /dev/null +++ b/src/main/java/net/citizensnpcs/api/ai/PathStrategy.java @@ -0,0 +1,49 @@ +package net.citizensnpcs.api.ai; + +import org.bukkit.Location; +import org.bukkit.util.Vector; + +import net.citizensnpcs.api.ai.event.CancelReason; + +/** + * A pathfinding strategy directed at a target. Has two states: pathfinding -> cancelled represented by + * {@link #getCancelReason()}. + */ +public interface PathStrategy { + /** + * Clears the CancelReason returned by {@link #getCancelReason()} and attempts to resume pathfinding. + */ + void clearCancelReason(); + + /** + * @return The reason for the pathfinding to stop, or null if it is still continuing. + */ + CancelReason getCancelReason(); + + /** + * @return A copy of the current path, if any + */ + Iterable getPath(); + + /** + * @return Gets the target destination location + */ + Location getTargetAsLocation(); + + /** + * @return The {@link TargetType} of this strategy + */ + TargetType getTargetType(); + + /** + * Forcibly stops pathfinding. Note that this method does not necessarily set the cancel reason. + */ + void stop(); + + /** + * Updates and runs the pathfinding strategy on its current NPC and destination. + * + * @return Whether pathfinding has completed + */ + boolean update(); +} \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/api/astar/pathfinder/NeighbourGeneratorBlockExaminer.java b/src/main/java/net/citizensnpcs/api/astar/pathfinder/NeighbourGeneratorBlockExaminer.java new file mode 100644 index 00000000..81a0d5db --- /dev/null +++ b/src/main/java/net/citizensnpcs/api/astar/pathfinder/NeighbourGeneratorBlockExaminer.java @@ -0,0 +1,7 @@ +package net.citizensnpcs.api.astar.pathfinder; + +import java.util.List; + +public interface NeighbourGeneratorBlockExaminer extends BlockExaminer { + public List getNeighbours(BlockSource source, PathPoint point); +} diff --git a/src/main/java/net/citizensnpcs/api/astar/pathfinder/Path.java b/src/main/java/net/citizensnpcs/api/astar/pathfinder/Path.java index 640f4862..e5100fe8 100644 --- a/src/main/java/net/citizensnpcs/api/astar/pathfinder/Path.java +++ b/src/main/java/net/citizensnpcs/api/astar/pathfinder/Path.java @@ -4,11 +4,6 @@ import java.util.List; import java.util.ListIterator; -import net.citizensnpcs.api.astar.Agent; -import net.citizensnpcs.api.astar.Plan; -import net.citizensnpcs.api.astar.pathfinder.PathPoint.PathCallback; -import net.citizensnpcs.api.npc.NPC; - import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.World; @@ -17,8 +12,14 @@ import org.bukkit.util.Vector; import com.google.common.base.Function; +import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import net.citizensnpcs.api.astar.Agent; +import net.citizensnpcs.api.astar.Plan; +import net.citizensnpcs.api.astar.pathfinder.PathPoint.PathCallback; +import net.citizensnpcs.api.npc.NPC; + public class Path implements Plan { private int index = 0; private final PathEntry[] path; @@ -58,6 +59,15 @@ public Vector getCurrentVector() { return path[index].vector; } + public Iterable getPath() { + return Iterables.transform(Arrays.asList(path), new Function() { + @Override + public Vector apply(PathEntry input) { + return input.vector; + } + }); + } + @Override public boolean isComplete() { return index >= path.length; @@ -100,11 +110,8 @@ public void run(final NPC npc) { ListIterator vec = Lists.transform(Arrays.asList(path), new Function() { @Override public Block apply(PathEntry input) { - return npc - .getEntity() - .getWorld() - .getBlockAt(input.vector.getBlockX(), input.vector.getBlockY(), - input.vector.getBlockZ()); + return npc.getEntity().getWorld().getBlockAt(input.vector.getBlockX(), + input.vector.getBlockY(), input.vector.getBlockZ()); } }).listIterator(); if (index > 0) { diff --git a/src/main/java/net/citizensnpcs/api/astar/pathfinder/PathPoint.java b/src/main/java/net/citizensnpcs/api/astar/pathfinder/PathPoint.java index 4ccc689f..6c6d5e1e 100644 --- a/src/main/java/net/citizensnpcs/api/astar/pathfinder/PathPoint.java +++ b/src/main/java/net/citizensnpcs/api/astar/pathfinder/PathPoint.java @@ -2,14 +2,16 @@ import java.util.ListIterator; -import net.citizensnpcs.api.npc.NPC; - import org.bukkit.block.Block; import org.bukkit.util.Vector; +import net.citizensnpcs.api.npc.NPC; + public interface PathPoint { void addCallback(PathCallback callback); + PathPoint createAtOffset(Vector vector); + Vector getGoal(); PathPoint getParentPoint(); diff --git a/src/main/java/net/citizensnpcs/api/astar/pathfinder/VectorNode.java b/src/main/java/net/citizensnpcs/api/astar/pathfinder/VectorNode.java index 9702c874..14b10aa5 100644 --- a/src/main/java/net/citizensnpcs/api/astar/pathfinder/VectorNode.java +++ b/src/main/java/net/citizensnpcs/api/astar/pathfinder/VectorNode.java @@ -2,15 +2,15 @@ import java.util.List; -import net.citizensnpcs.api.astar.AStarNode; -import net.citizensnpcs.api.astar.Plan; -import net.citizensnpcs.api.astar.pathfinder.BlockExaminer.PassableState; - import org.bukkit.Location; import org.bukkit.util.Vector; import com.google.common.collect.Lists; +import net.citizensnpcs.api.astar.AStarNode; +import net.citizensnpcs.api.astar.Plan; +import net.citizensnpcs.api.astar.pathfinder.BlockExaminer.PassableState; + public class VectorNode extends AStarNode implements PathPoint { private float blockCost = -1; private final BlockSource blockSource; @@ -44,6 +44,11 @@ public Plan buildPlan() { return new Path(parents); } + @Override + public VectorNode createAtOffset(Vector mod) { + return new VectorNode(goal, mod, blockSource, examiners); + } + public float distance(VectorNode to) { return (float) location.distance(to.location); } @@ -84,7 +89,27 @@ public Vector getGoal() { @Override public Iterable getNeighbours() { + List neighbours = null; + for (BlockExaminer examiner : examiners) { + if (examiner instanceof NeighbourGeneratorBlockExaminer) { + neighbours = ((NeighbourGeneratorBlockExaminer) examiner).getNeighbours(blockSource, this); + break; + } + } + if (neighbours == null) { + neighbours = getNeighbours(blockSource, this); + } List nodes = Lists.newArrayList(); + for (PathPoint sub : neighbours) { + if (!isPassable(sub)) + continue; + nodes.add((AStarNode) sub); + } + return nodes; + } + + public List getNeighbours(BlockSource source, PathPoint point) { + List neighbours = Lists.newArrayList(); for (int x = -1; x <= 1; x++) { for (int y = -1; y <= 1; y++) { for (int z = -1; z <= 1; z++) { @@ -95,18 +120,11 @@ public Iterable getNeighbours() { Vector mod = location.clone().add(new Vector(x, y, z)); if (mod.equals(location)) continue; - VectorNode sub = getNewNode(mod); - if (!isPassable(sub)) - continue; - nodes.add(sub); + neighbours.add(point.createAtOffset(mod)); } } } - return nodes; - } - - private VectorNode getNewNode(Vector mod) { - return new VectorNode(goal, mod, blockSource, examiners); + return neighbours; } @Override