WIP: [Feature]: Enemy Waves Part1 #17
6 changed files with 155 additions and 15 deletions
|
@ -1,5 +1,7 @@
|
||||||
package de.towerdefence.server.game;
|
package de.towerdefence.server.game;
|
||||||
|
|
||||||
|
import org.joml.Vector2i;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@ -7,6 +9,5 @@ import lombok.Getter;
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class Enemy {
|
public class Enemy {
|
||||||
protected int id;
|
protected int id;
|
||||||
protected int x;
|
protected Vector2i pos;
|
||||||
protected int y;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,9 @@ import de.towerdefence.server.game.callbacks.PlayerHitpointsCallback;
|
||||||
import de.towerdefence.server.game.exeptions.InvalidPlacementException;
|
import de.towerdefence.server.game.exeptions.InvalidPlacementException;
|
||||||
import de.towerdefence.server.game.exeptions.InvalidPlacementReason;
|
import de.towerdefence.server.game.exeptions.InvalidPlacementReason;
|
||||||
import de.towerdefence.server.game.exeptions.WaveInProgressException;
|
import de.towerdefence.server.game.exeptions.WaveInProgressException;
|
||||||
|
import de.towerdefence.server.game.pathfinding.AStar;
|
||||||
|
import de.towerdefence.server.game.pathfinding.NoPathException;
|
||||||
|
import de.towerdefence.server.game.pathfinding.OutOfBoundsException;
|
||||||
import de.towerdefence.server.player.Player;
|
import de.towerdefence.server.player.Player;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.joml.Vector2i;
|
import org.joml.Vector2i;
|
||||||
|
@ -82,20 +85,37 @@ public class GameSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Enemy> moveEnemies() {
|
private List<Enemy> moveEnemies() {
|
||||||
// List<Enemy> changedEnemies = moveEnemies();
|
List<Enemy> changedEnemies = moveEnemies();
|
||||||
|
|
||||||
// TODO: Implement Moving of Enemies (possibly through A*)
|
for (int x = 0; x < enemies.length; x++) {
|
||||||
throw new RuntimeException("NOT IMPLEMENTED");
|
Enemy[] column = enemies[x];
|
||||||
|
for (int y = 0; y < column.length; y++) {
|
||||||
// return changedEnemies;
|
Enemy enemy = column[y];
|
||||||
|
if (changedEnemies.contains(enemy)) {
|
||||||
}
|
continue;
|
||||||
|
}
|
||||||
/**
|
AStar a = new AStar((Vector2i pos) -> {
|
||||||
* @return the next position to go to
|
return pos.x >= 0
|
||||||
*/
|
&& pos.x < MAP_SIZE.x
|
||||||
private Vector2i pathfinding(Vector2i start, Vector2i target) {
|
&& pos.y >= 0
|
||||||
|
&& pos.y < MAP_SIZE.y
|
||||||
|
&& (towers[pos.x] == null || towers[pos.x][pos.y] == null)
|
||||||
|
&& (enemies[pos.x] == null || enemies[pos.x][pos.y] != null);
|
||||||
|
});
|
||||||
|
Vector2i next;
|
||||||
|
try {
|
||||||
|
next = a.next(enemy.pos, WAVE_TARGET);
|
||||||
|
} catch (NoPathException | OutOfBoundsException exceptionO) {
|
||||||
|
throw new RuntimeException("TODO: Implement");
|
||||||
|
}
|
||||||
|
enemy.pos = next;
|
||||||
|
enemies[x][y] = null;
|
||||||
|
enemies[next.x][next.y] = enemy;
|
||||||
|
changedEnemies.add(enemy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return changedEnemies;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void placeTower(Tower tower, int x, int y) throws InvalidPlacementException {
|
public void placeTower(Tower tower, int x, int y) throws InvalidPlacementException {
|
||||||
|
|
|
@ -1,5 +1,100 @@
|
||||||
package de.towerdefence.server.game.pathfinding;
|
package de.towerdefence.server.game.pathfinding;
|
||||||
|
|
||||||
public class AStar {
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.joml.Vector2i;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class AStar {
|
||||||
|
private final WalkableCallback walkable;
|
||||||
|
|
||||||
|
public Vector2i next(Vector2i current, Vector2i target) throws NoPathException, OutOfBoundsException {
|
||||||
|
if (!walkable.call(current) || !walkable.call(target)) {
|
||||||
|
throw new OutOfBoundsException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current.equals(target)) {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<Vector2i, PathNode> remaining = new HashMap<>();
|
||||||
|
Set<Vector2i> evaluated = new HashSet<>();
|
||||||
|
Map<Vector2i, Vector2i> takenPath = new HashMap<>();
|
||||||
|
|
||||||
|
PathNode origin = new PathNode(current, 0, current.gridDistance(target));
|
||||||
|
remaining.put(current, origin);
|
||||||
|
|
||||||
|
while (!remaining.isEmpty()) {
|
||||||
|
PathNode currentNode = getLowestEstimate(remaining);
|
||||||
|
Vector2i currentPos = currentNode.pos;
|
||||||
|
|
||||||
|
if (currentPos.equals(target)) {
|
||||||
|
return reconstructPath(takenPath, current);
|
||||||
|
}
|
||||||
|
|
||||||
|
remaining.remove(currentPos);
|
||||||
|
evaluated.add(currentPos);
|
||||||
|
|
||||||
|
long nextCost = currentNode.cost + 1;
|
||||||
|
for (PathNode neighbor : getNeighbors(currentPos, nextCost, target)) {
|
||||||
|
if (evaluated.contains(neighbor.pos)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!remaining.containsKey(neighbor.pos) || nextCost < remaining.get(neighbor.pos).cost) {
|
||||||
|
takenPath.put(neighbor.pos, currentPos);
|
||||||
|
remaining.put(neighbor.pos, neighbor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NoPathException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector2i reconstructPath(Map<Vector2i, Vector2i> takenPath, Vector2i origin) {
|
||||||
|
Vector2i current = takenPath.keySet().stream().reduce((first, second) -> second).orElse(null);
|
||||||
|
if (current == null)
|
||||||
|
return null;
|
||||||
|
while (takenPath.get(current) != null && !takenPath.get(current).equals(origin)) {
|
||||||
|
current = takenPath.get(current);
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PathNode getLowestEstimate(Map<Vector2i, PathNode> openSet) {
|
||||||
|
PathNode lowestNode = null;
|
||||||
|
for (PathNode node : openSet.values()) {
|
||||||
|
if (lowestNode == null || node.estimate < lowestNode.estimate) {
|
||||||
|
lowestNode = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lowestNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<PathNode> getNeighbors(Vector2i pos, long nextCost, Vector2i target) {
|
||||||
|
List<PathNode> neighbors = new ArrayList<>();
|
||||||
|
|
||||||
|
addNeighbor(neighbors, new Vector2i(pos.x + 1, pos.y), nextCost, target);
|
||||||
|
addNeighbor(neighbors, new Vector2i(pos.x - 1, pos.y), nextCost, target);
|
||||||
|
addNeighbor(neighbors, new Vector2i(pos.x, pos.y + 1), nextCost, target);
|
||||||
|
addNeighbor(neighbors, new Vector2i(pos.x, pos.y - 1), nextCost, target);
|
||||||
|
return neighbors;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addNeighbor(List<PathNode> accumulator, Vector2i pos, long nextCost, Vector2i target) {
|
||||||
|
Vector2i right = new Vector2i(pos.x + 1, pos.y);
|
||||||
|
if (!walkable.call(pos)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
accumulator.add(new PathNode(
|
||||||
|
right,
|
||||||
|
nextCost,
|
||||||
|
right.gridDistance(target) + nextCost));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
package de.towerdefence.server.game.pathfinding;
|
||||||
|
|
||||||
|
public class OutOfBoundsException extends RuntimeException {
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package de.towerdefence.server.game.pathfinding;
|
||||||
|
|
||||||
|
import org.joml.Vector2i;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
final class PathNode {
|
||||||
|
protected final Vector2i pos;
|
||||||
|
protected final long cost; // This is the cost to this Node
|
||||||
|
protected final long estimate; // This is the estimated remaining cost to the Target
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package de.towerdefence.server.game.pathfinding;
|
||||||
|
|
||||||
|
import org.joml.Vector2i;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface WalkableCallback {
|
||||||
|
boolean call(Vector2i pos);
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue