diff --git a/api/src/main/java/com/bartlomiejpluta/base/api/move/Direction.java b/api/src/main/java/com/bartlomiejpluta/base/api/move/Direction.java index cd24f39e..ac757cb9 100644 --- a/api/src/main/java/com/bartlomiejpluta/base/api/move/Direction.java +++ b/api/src/main/java/com/bartlomiejpluta/base/api/move/Direction.java @@ -50,4 +50,22 @@ public enum Direction { return UP; } } + + public static Direction ofVector(int x, int y) { + // X Versor = [1, 0] + // dot = 1 * vector.x + 0 * vector.y = vector.x + // det = 1 * vector.y - 0 * vector.x = vector.y + // angle = atan2(det, dot) = atan2(vector.y, vector.x) + float angle = atan2(y, x); + + if (-PI / 4 < angle && angle < PI / 4) { + return RIGHT; + } else if (PI / 4 <= angle && angle <= 3 * PI / 4) { + return DOWN; + } else if (3 * PI / 4 < angle && angle < 5 * PI / 4) { + return LEFT; + } else { + return UP; + } + } } diff --git a/api/src/main/java/com/bartlomiejpluta/base/lib/ai/FollowEntityAI.java b/api/src/main/java/com/bartlomiejpluta/base/lib/ai/FollowEntityAI.java index c598bb2f..024d0787 100644 --- a/api/src/main/java/com/bartlomiejpluta/base/lib/ai/FollowEntityAI.java +++ b/api/src/main/java/com/bartlomiejpluta/base/lib/ai/FollowEntityAI.java @@ -30,12 +30,12 @@ public class FollowEntityAI implements AI { var distance = npc.manhattanDistance(target); if (!npc.isMoving() && 1 < distance && distance < range && accumulator >= recalculateInterval) { - var path = pathFinder.findPath(layer, npc.getCoordinates(), target.getCoordinates()); + var path = pathFinder.findSequence(layer, npc.getCoordinates(), target.getCoordinates()); if (!path.isEmpty()) { accumulator = recalculateInterval; - var node = path.getLast().sub(npc.getCoordinates(), new Vector2i()); + var node = path.getFirst().sub(npc.getCoordinates(), new Vector2i()); var direction = Direction.ofVector(node); var movement = npc.prepareMovement(direction); layer.pushMovement(movement); diff --git a/api/src/main/java/com/bartlomiejpluta/base/lib/ai/FollowPathAI.java b/api/src/main/java/com/bartlomiejpluta/base/lib/ai/FollowPathAI.java index c7c90b3f..f9061508 100644 --- a/api/src/main/java/com/bartlomiejpluta/base/lib/ai/FollowPathAI.java +++ b/api/src/main/java/com/bartlomiejpluta/base/lib/ai/FollowPathAI.java @@ -3,52 +3,18 @@ package com.bartlomiejpluta.base.lib.ai; import com.bartlomiejpluta.base.api.ai.AI; import com.bartlomiejpluta.base.api.ai.NPC; import com.bartlomiejpluta.base.api.map.layer.object.ObjectLayer; -import com.bartlomiejpluta.base.api.move.Direction; -import com.bartlomiejpluta.base.util.path.NPCPath; +import com.bartlomiejpluta.base.util.path.Path; import com.bartlomiejpluta.base.util.path.PathExecutor; -public class FollowPathAI implements AI { - private final PathExecutor executor; - private final NPCPath path; +public class FollowPathAI implements AI { + private final PathExecutor executor; - public FollowPathAI(NPC npc) { - this(npc, null); - } - - public FollowPathAI(NPC npc, Integer repeat) { - var path = new NPCPath(); + public FollowPathAI(T npc, Integer repeat, Path path) { this.executor = new PathExecutor<>(npc, repeat, path); - this.path = path; } @Override public void nextActivity(ObjectLayer layer, float dt) { executor.execute(layer, dt); } - - - public FollowPathAI move(Direction direction) { - path.move(direction); - return this; - } - - public FollowPathAI move(Direction direction, boolean ignore) { - path.move(direction, ignore); - return this; - } - - public FollowPathAI turn(Direction direction) { - path.turn(direction); - return this; - } - - public FollowPathAI wait(float seconds) { - path.wait(seconds); - return this; - } - - public FollowPathAI run(Runnable runnable) { - path.run(runnable); - return this; - } } diff --git a/api/src/main/java/com/bartlomiejpluta/base/util/path/NPCPath.java b/api/src/main/java/com/bartlomiejpluta/base/util/path/NPCPath.java index 65ad7c21..6722ad6c 100644 --- a/api/src/main/java/com/bartlomiejpluta/base/util/path/NPCPath.java +++ b/api/src/main/java/com/bartlomiejpluta/base/util/path/NPCPath.java @@ -9,4 +9,40 @@ public class NPCPath extends Path { path.add(new TurnSegment<>(direction)); return this; } + + @Override + public NPCPath add(PathSegment segment) { + super.add(segment); + return this; + } + + @Override + public NPCPath addFirst(PathSegment segment) { + super.addFirst(segment); + return this; + } + + @Override + public NPCPath move(Direction direction) { + super.move(direction); + return this; + } + + @Override + public NPCPath move(Direction direction, boolean ignore) { + super.move(direction, ignore); + return this; + } + + @Override + public NPCPath wait(float seconds) { + super.wait(seconds); + return this; + } + + @Override + public NPCPath run(Runnable runnable) { + super.run(runnable); + return this; + } } diff --git a/api/src/main/java/com/bartlomiejpluta/base/util/path/Path.java b/api/src/main/java/com/bartlomiejpluta/base/util/path/Path.java index c128b579..a24dbe5e 100644 --- a/api/src/main/java/com/bartlomiejpluta/base/util/path/Path.java +++ b/api/src/main/java/com/bartlomiejpluta/base/util/path/Path.java @@ -18,6 +18,11 @@ public class Path { return this; } + public Path addFirst(PathSegment segment) { + path.add(0, segment); + return this; + } + public Path move(Direction direction) { path.add(new MoveSegment<>(direction)); return this; diff --git a/api/src/main/java/com/bartlomiejpluta/base/util/pathfinder/AstarPathFinder.java b/api/src/main/java/com/bartlomiejpluta/base/util/pathfinder/AstarPathFinder.java index 70048b6a..ba8f201c 100644 --- a/api/src/main/java/com/bartlomiejpluta/base/util/pathfinder/AstarPathFinder.java +++ b/api/src/main/java/com/bartlomiejpluta/base/util/pathfinder/AstarPathFinder.java @@ -1,6 +1,10 @@ package com.bartlomiejpluta.base.util.pathfinder; import com.bartlomiejpluta.base.api.map.layer.object.ObjectLayer; +import com.bartlomiejpluta.base.api.move.Direction; +import com.bartlomiejpluta.base.api.move.Movable; +import com.bartlomiejpluta.base.util.path.MoveSegment; +import com.bartlomiejpluta.base.util.path.Path; import org.joml.Vector2i; import org.joml.Vector2ic; @@ -13,6 +17,7 @@ import java.util.function.Function; import static java.lang.Math.abs; public class AstarPathFinder implements PathFinder { + private static final LinkedList EMPTY_LINKED_LIST = new LinkedList<>(); /* * We are interested in following adjacent @@ -38,7 +43,51 @@ public class AstarPathFinder implements PathFinder { } @Override - public LinkedList findPath(ObjectLayer layer, Vector2ic start, Vector2ic end) { + public Path findPath(ObjectLayer layer, T start, Vector2ic end) { + return astar(layer, start.getCoordinates(), end, this::recreatePath); + } + + private Path recreatePath(Node node) { + if (node == null) { + return new Path<>(); + } + + var path = new Path(); + var current = node; + + while (current.parent != null) { + path.addFirst(new MoveSegment<>(Direction.ofVector( + current.position.x() - current.parent.position.x(), + current.position.y() - current.parent.position.y() + ))); + current = current.parent; + } + + return path; + } + + @Override + public LinkedList findSequence(ObjectLayer layer, Vector2ic start, Vector2ic end) { + return astar(layer, start, end, this::recreateSequence); + } + + private LinkedList recreateSequence(Node node) { + if (node == null) { + return EMPTY_LINKED_LIST; + } + + var list = new LinkedList(); + var current = node; + + while (current.parent != null) { + list.addFirst(current.position); + current = current.parent; + } + + return list; + } + + private

P astar(ObjectLayer layer, Vector2ic start, Vector2ic end, Function pathProducer) { var columns = layer.getMap().getColumns(); var rows = layer.getMap().getRows(); @@ -65,7 +114,7 @@ public class AstarPathFinder implements PathFinder { // It determines the maximum algorithm depth // It's not the part of model A* algorithm. if (closed.size() > maxNodes) { - return new LinkedList<>(); + return pathProducer.apply(null); } // We are retrieving the node with the **smallest** f score @@ -78,7 +127,7 @@ public class AstarPathFinder implements PathFinder { // If we found the node with f score and it is // actually an end node, we have most likely found a best path if (current.equals(endNode)) { - return recreatePath(current); + return pathProducer.apply(current); } adjacent: @@ -156,25 +205,13 @@ public class AstarPathFinder implements PathFinder { // If open list is empty and we didn't reach the end node // it means that the path probably does not exist at all - return new LinkedList<>(); + return pathProducer.apply(null); } private float manhattanDistance(Vector2ic a, Vector2ic b) { return (abs(a.x() - b.x()) + abs(a.y() - b.y())); } - private LinkedList recreatePath(Node node) { - var current = node; - var list = new LinkedList(); - - while (current.parent != null) { - list.add(current.position); - current = current.parent; - } - - return list; - } - private static class Node implements Comparable { public Node parent; public final Vector2ic position; diff --git a/api/src/main/java/com/bartlomiejpluta/base/util/pathfinder/PathFinder.java b/api/src/main/java/com/bartlomiejpluta/base/util/pathfinder/PathFinder.java index 9deb1cf7..99c8f623 100644 --- a/api/src/main/java/com/bartlomiejpluta/base/util/pathfinder/PathFinder.java +++ b/api/src/main/java/com/bartlomiejpluta/base/util/pathfinder/PathFinder.java @@ -1,10 +1,14 @@ package com.bartlomiejpluta.base.util.pathfinder; import com.bartlomiejpluta.base.api.map.layer.object.ObjectLayer; +import com.bartlomiejpluta.base.api.move.Movable; +import com.bartlomiejpluta.base.util.path.Path; import org.joml.Vector2ic; import java.util.LinkedList; public interface PathFinder { - LinkedList findPath(ObjectLayer layer, Vector2ic start, Vector2ic end); + Path findPath(ObjectLayer layer, T start, Vector2ic end); + + LinkedList findSequence(ObjectLayer layer, Vector2ic start, Vector2ic end); }