suffering

This commit is contained in:
Anuken 2023-11-04 17:18:53 -04:00
parent 88285c79cc
commit 70538e2985
2 changed files with 181 additions and 9 deletions

View File

@ -313,8 +313,8 @@ public class Vars implements Loadable{
spawner = new WaveSpawner();
indexer = new BlockIndexer();
pathfinder = new Pathfinder();
hpath = new HierarchyPathFinder();
controlPath = new ControlPathfinder();
hpath = new HierarchyPathFinder();
fogControl = new FogControl();
bases = new BaseRegistry();
logicVars = new GlobalVars();

View File

@ -6,13 +6,17 @@ import arc.graphics.g2d.*;
import arc.math.*;
import arc.math.geom.*;
import arc.struct.*;
import mindustry.game.*;
import arc.util.*;
import mindustry.content.*;
import mindustry.game.EventType.*;
import mindustry.game.*;
import mindustry.graphics.*;
import static mindustry.Vars.*;
import static mindustry.ai.Pathfinder.*;
//https://webdocs.cs.ualberta.ca/~mmueller/ps/hpastar.pdf
//https://www.gameaipro.com/GameAIPro/GameAIPro_Chapter23_Crowd_Pathfinding_and_Steering_Using_Flow_Field_Tiles.pdf
public class HierarchyPathFinder{
static final boolean debug = true;
@ -80,7 +84,7 @@ public class HierarchyPathFinder{
Draw.color(Color.green);
Lines.rect(cx * clusterSize * tilesize - tilesize/2f, cy * clusterSize * tilesize - tilesize/2f, clusterSize * tilesize, clusterSize * tilesize);
Draw.color(Color.blue);
Draw.color(Color.red);
for(int d = 0; d < 4; d++){
IntSeq portals = cluster.portals[d];
@ -102,6 +106,16 @@ public class HierarchyPathFinder{
}
}
}
Draw.color(Color.magenta);
for(var con : cluster.cons){
float
x1 = Point2.x(con.posFrom) * tilesize, y1 = Point2.y(con.posFrom) * tilesize,
x2 = Point2.x(con.posTo) * tilesize, y2 = Point2.y(con.posTo) * tilesize,
mx = (cx * clusterSize + clusterSize/2f) * tilesize, my = (cy * clusterSize + clusterSize/2f) * tilesize;
//Lines.curve(x1, y1, mx, my, mx, my, x2, y2, 20);
Lines.line(x1, y1, x2, y2);
}
}
}
}
@ -124,6 +138,8 @@ public class HierarchyPathFinder{
cluster.innerEdges.clear();
}
//TODO: other cluster inner edges should be recomputed if changed.
//TODO look it up based on number.
PathCost cost = ControlPathfinder.costGround;
@ -139,14 +155,12 @@ public class HierarchyPathFinder{
if(other == null){
//create new portals at direction
portals = cluster.portals[direction] = new IntSeq();
portals = cluster.portals[direction] = new IntSeq(4);
}else{
//share portals with the other cluster
portals = cluster.portals[direction] = other.portals[(direction + 2) % 4];
}
//Point2 adder = Geometry.d4[(direction + 1) % 4];
int addX = moveDirs[direction * 2], addY = moveDirs[direction * 2 + 1];
int
baseX = cx * clusterSize + offsets[direction * 2] * (clusterSize - 1),
@ -185,6 +199,157 @@ public class HierarchyPathFinder{
portals.add(Point2.pack(previous, lastPortal));
}
}
connectInnerEdges(cx, cy, team, cost, cluster);
}
static PathfindQueue frontier = new PathfindQueue();
//node index -> total cost
static IntFloatMap costs = new IntFloatMap();
static IntSet usedEdges = new IntSet();
void connectInnerEdges(int cx, int cy, int team, PathCost cost, Cluster cluster){
int minX = cx * clusterSize, minY = cy * clusterSize, maxX = Math.min(minX + clusterSize - 1, wwidth - 1), maxY = Math.min(minY + clusterSize - 1, wheight - 1);
usedEdges.clear();
cluster.cons.clear();
//TODO: how the hell to identify a vertex?
//cluster (i16) | direction (i2) | index (i14)
for(int direction = 0; direction < 4; direction++){
var portals = cluster.portals[direction];
if(portals == null) continue;
int addX = moveDirs[direction * 2], addY = moveDirs[direction * 2 + 1];
for(int i = 0; i < portals.size; i++){
usedEdges.add(Point2.pack(direction, i));
int
portal = portals.items[i],
from = Point2.x(portal), to = Point2.y(portal),
average = (from + to) / 2,
x = (addX * average + cx * clusterSize + offsets[direction * 2] * (clusterSize - 1)),
y = (addY * average + cy * clusterSize + offsets[direction * 2 + 1] * (clusterSize - 1));
for(int otherDir = 0; otherDir < 4; otherDir++){
var otherPortals = cluster.portals[otherDir];
for(int j = 0; j < otherPortals.size; j++){
//TODO redundant calculations?
if(!usedEdges.contains(Point2.pack(otherDir, j))){
int
other = otherPortals.items[j],
otherFrom = Point2.x(other), otherTo = Point2.y(other),
otherAverage = (otherFrom + otherTo) / 2,
ox = cx * clusterSize + offsets[otherDir * 2] * (clusterSize - 1),
oy = cy * clusterSize + offsets[otherDir * 2 + 1] * (clusterSize - 1),
otherX = (moveDirs[otherDir * 2] * otherAverage + ox),
otherY = (moveDirs[otherDir * 2 + 1] * otherAverage + oy);
//HOW
if(Point2.pack(x, y) == Point2.pack(otherX, otherY)){
if(true) continue;
Log.infoList("self ", direction, " ", i, " | ", otherDir, " ", j);
System.exit(1);
}
float connectionCost = astar(
team, cost,
minX, minY, maxX, maxY,
x + y * wwidth,
otherX + otherY * wwidth,
(moveDirs[otherDir * 2] * otherFrom + ox),
(moveDirs[otherDir * 2 + 1] * otherFrom + oy),
(moveDirs[otherDir * 2] * otherTo + ox),
(moveDirs[otherDir * 2 + 1] * otherTo + oy)
);
if(connectionCost != -1f){
cluster.cons.add(new Con(Point2.pack(x, y), Point2.pack(otherX, otherY), connectionCost));
Fx.debugLine.at(x* tilesize, y * tilesize, 0f, Color.purple,
new Vec2[]{new Vec2(x, y).scl(tilesize), new Vec2(otherX, otherY).scl(tilesize)});
}
}
}
}
}
}
}
//distance heuristic: manhattan
private static float heuristic(int a, int b){
int x = a % wwidth, x2 = b % wwidth, y = a / wwidth, y2 = b / wwidth;
return Math.abs(x - x2) + Math.abs(y - y2);
}
private static int tcost(int team, PathCost cost, int tilePos){
return cost.getCost(team, pathfinder.tiles[tilePos]);
}
private static float tileCost(int team, PathCost type, int a, int b){
//currently flat cost
return cost(team, type, b);
}
/** @return -1 if no path was found */
float astar(int team, PathCost cost, int minX, int minY, int maxX, int maxY, int startPos, int goalPos, int goalX1, int goalY1, int goalX2, int goalY2){
frontier.clear();
costs.clear();
costs.put(startPos, 0);
frontier.add(startPos, 0);
if(debug && false){
Fx.debugLine.at(Point2.x(startPos) * tilesize, Point2.y(startPos) * tilesize, 0f, Color.purple,
new Vec2[]{new Vec2(Point2.x(startPos), Point2.y(startPos)).scl(tilesize), new Vec2(Point2.x(goalPos), Point2.y(goalPos)).scl(tilesize)});
}
while(frontier.size > 0){
int current = frontier.poll();
int cx = current % wwidth, cy = current / wwidth;
//found the goal (it's in the portal rectangle)
//TODO portal rectangle approach does not work.
if((cx >= goalX1 && cy >= goalY1 && cx <= goalX2 && cy <= goalY2) || current == goalPos){
return costs.get(current);
}
for(Point2 point : Geometry.d4){
int newx = cx + point.x, newy = cy + point.y;
int next = newx + wwidth * newy;
if(newx > maxX || newy > maxY || newx < minX || newy < minY) continue;
//TODO fallback mode for enemy walls or whatever
if(tcost(team, cost, next) == impassable) continue;
float add = tileCost(team, cost, current, next);
float currentCost = costs.get(current);
if(add < 0) continue;
float newCost = currentCost + add;
//a cost of 0 means "not set"
if(!costs.containsKey(next) || newCost < costs.get(next)){
costs.put(next, newCost);
float priority = newCost + heuristic(next, goalPos);
frontier.add(next, priority);
}
}
}
return -1f;
}
Cluster cluster(int pathCost, int cx, int cy){
@ -213,11 +378,18 @@ public class HierarchyPathFinder{
static class Cluster{
IntSeq[] portals = new IntSeq[4];
IntSeq innerEdges = new IntSeq();
Seq<Con> cons = new Seq<>();
}
Cluster(){
//TODO for debugging only
static class Con{
int posFrom, posTo;
float cost;
public Con(int posFrom, int posTo, float cost){
this.posFrom = posFrom;
this.posTo = posTo;
this.cost = cost;
}
}
}