Pathfinder progress
This commit is contained in:
@@ -23,6 +23,38 @@ public class Pathfinder implements Runnable{
|
|||||||
private static final int impassable = -1;
|
private static final int impassable = -1;
|
||||||
private static final int fieldTimeout = 1000 * 60 * 2;
|
private static final int fieldTimeout = 1000 * 60 * 2;
|
||||||
|
|
||||||
|
public static final int
|
||||||
|
fieldCore = 0,
|
||||||
|
fieldRally = 1;
|
||||||
|
|
||||||
|
public static final Seq<Prov<Flowfield>> fieldTypes = Seq.with(
|
||||||
|
EnemyCoreField::new,
|
||||||
|
RallyField::new
|
||||||
|
);
|
||||||
|
|
||||||
|
public static final int
|
||||||
|
costGround = 0,
|
||||||
|
costLegs = 1,
|
||||||
|
costWater = 2;
|
||||||
|
|
||||||
|
public static final Seq<PathCost> costTypes = Seq.with(
|
||||||
|
(team, tile) -> (PathTile.team(tile) == team.id || PathTile.team(tile) == 0) && PathTile.solid(tile) ? impassable : 1 +
|
||||||
|
PathTile.health(tile) * 5 +
|
||||||
|
(PathTile.nearSolid(tile) ? 2 : 0) +
|
||||||
|
(PathTile.nearLiquid(tile) ? 6 : 0) +
|
||||||
|
(PathTile.deep(tile) ? 70 : 0),
|
||||||
|
|
||||||
|
(team, tile) -> PathTile.legSolid(tile) ? impassable : 1 +
|
||||||
|
(PathTile.solid(tile) ? 5 : 0),
|
||||||
|
|
||||||
|
(team, tile) -> PathTile.solid(tile) || !PathTile.liquid(tile) ? impassable : 2 + //TODO cannot go through blocks
|
||||||
|
(PathTile.nearGround(tile) || PathTile.nearSolid(tile) ? 14 : 0) +
|
||||||
|
(PathTile.deep(tile) ? -1 : 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
//maps team, cost, type to flow field
|
||||||
|
private Flowfield[][][] cache;
|
||||||
|
|
||||||
/** tile data, see PathTileStruct */
|
/** tile data, see PathTileStruct */
|
||||||
private int[][] tiles;
|
private int[][] tiles;
|
||||||
/** unordered array of path data for iteration only. DO NOT iterate or access this in the main thread. */
|
/** unordered array of path data for iteration only. DO NOT iterate or access this in the main thread. */
|
||||||
@@ -34,6 +66,8 @@ public class Pathfinder implements Runnable{
|
|||||||
private IntSeq tmpArray = new IntSeq();
|
private IntSeq tmpArray = new IntSeq();
|
||||||
|
|
||||||
public Pathfinder(){
|
public Pathfinder(){
|
||||||
|
clearCache();
|
||||||
|
|
||||||
Events.on(WorldLoadEvent.class, event -> {
|
Events.on(WorldLoadEvent.class, event -> {
|
||||||
stop();
|
stop();
|
||||||
|
|
||||||
@@ -41,13 +75,14 @@ public class Pathfinder implements Runnable{
|
|||||||
tiles = new int[world.width()][world.height()];
|
tiles = new int[world.width()][world.height()];
|
||||||
threadList = new Seq<>();
|
threadList = new Seq<>();
|
||||||
mainList = new Seq<>();
|
mainList = new Seq<>();
|
||||||
|
clearCache();
|
||||||
|
|
||||||
for(Tile tile : world.tiles){
|
for(Tile tile : world.tiles){
|
||||||
tiles[tile.x][tile.y] = packTile(tile);
|
tiles[tile.x][tile.y] = packTile(tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
//special preset which may help speed things up; this is optional
|
//special preset which may help speed things up; this is optional
|
||||||
//preloadPath(state.rules.waveTeam, FlagTarget.enemyCores);
|
preloadPath(getField(state.rules.waveTeam, costGround, fieldCore));
|
||||||
|
|
||||||
start();
|
start();
|
||||||
});
|
});
|
||||||
@@ -57,8 +92,8 @@ public class Pathfinder implements Runnable{
|
|||||||
Events.on(BuildinghangeEvent.class, event -> updateTile(event.tile));
|
Events.on(BuildinghangeEvent.class, event -> updateTile(event.tile));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Flowfield getField(Team team, PathCost cost){
|
private void clearCache(){
|
||||||
|
cache = new Flowfield[256][5][5];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Packs a tile into its internal representation. */
|
/** Packs a tile into its internal representation. */
|
||||||
@@ -189,34 +224,26 @@ public class Pathfinder implements Runnable{
|
|||||||
// return getTargetTile(tile, team, getTarget(target));
|
// return getTargetTile(tile, team, getTarget(target));
|
||||||
// }
|
// }
|
||||||
|
|
||||||
public @Nullable Tile getTargetTile(Tile tile, Prov<Flowfield> fieldtype){
|
public Flowfield getField(Team team, int costType, int fieldType){
|
||||||
if(true){ //TODO cache this
|
if(cache[team.id][costType][fieldType] == null){
|
||||||
Flowfield field = fieldtype.get();
|
Flowfield field = fieldTypes.get(fieldType).get();
|
||||||
IntSeq out = new IntSeq();
|
field.team = team;
|
||||||
field.getPositions(out);
|
field.cost = costTypes.get(costType);
|
||||||
createPath(field, out);
|
field.targets.clear();
|
||||||
}
|
field.getPositions(field.targets);
|
||||||
|
|
||||||
if(false){ //TODO if field exists
|
cache[team.id][costType][fieldType] = field;
|
||||||
//TODO fetch it from the cache
|
queue.post(() -> registerPath(field));
|
||||||
//return getTargetTile(tile, path);
|
|
||||||
}
|
}
|
||||||
|
return cache[team.id][costType][fieldType];
|
||||||
return tile;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets next tile to travel to. Main thread only. */
|
/** Gets next tile to travel to. Main thread only. */
|
||||||
public @Nullable Tile getTargetTile(Tile tile, Flowfield path){
|
public @Nullable Tile getTargetTile(Tile tile, Flowfield path){
|
||||||
if(tile == null) return null;
|
if(tile == null) return null;
|
||||||
|
|
||||||
if(path == null){
|
//uninitialized flowfields are not applicable
|
||||||
//if this combination is not found, create it on request
|
if(!path.initialized){
|
||||||
//TODO do above task
|
|
||||||
//if(fieldMapUsed[team.id].add(target)){
|
|
||||||
//grab targets since this is run on main thread
|
|
||||||
// IntSeq targets = target.getPositions(team, new IntSeq());
|
|
||||||
// queue.post(() -> createPath(team, target, targets));
|
|
||||||
//}
|
|
||||||
return tile;
|
return tile;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,9 +333,10 @@ public class Pathfinder implements Runnable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void preloadPath(Flowfield path){
|
private void preloadPath(Flowfield path){
|
||||||
IntSeq out = new IntSeq();
|
path.targets.clear();
|
||||||
path.getPositions(out);
|
path.getPositions(path.targets);
|
||||||
updateFrontier(createPath(path, out), -1);
|
registerPath(path);
|
||||||
|
updateFrontier(path, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -316,7 +344,7 @@ public class Pathfinder implements Runnable{
|
|||||||
* Created a new flowfield that aims to get to a certain target for a certain team.
|
* Created a new flowfield that aims to get to a certain target for a certain team.
|
||||||
* Pathfinding thread only.
|
* Pathfinding thread only.
|
||||||
*/
|
*/
|
||||||
private Flowfield createPath(Flowfield path, IntSeq targets){
|
private void registerPath(Flowfield path){
|
||||||
path.lastUpdateTime = Time.millis();
|
path.lastUpdateTime = Time.millis();
|
||||||
path.setup(tiles.length, tiles[0].length);
|
path.setup(tiles.length, tiles[0].length);
|
||||||
|
|
||||||
@@ -331,12 +359,6 @@ public class Pathfinder implements Runnable{
|
|||||||
//}
|
//}
|
||||||
});
|
});
|
||||||
|
|
||||||
//grab targets from passed array
|
|
||||||
synchronized(path.targets){
|
|
||||||
path.targets.clear();
|
|
||||||
path.targets.addAll(targets);
|
|
||||||
}
|
|
||||||
|
|
||||||
//fill with impassables by default
|
//fill with impassables by default
|
||||||
for(int x = 0; x < world.width(); x++){
|
for(int x = 0; x < world.width(); x++){
|
||||||
for(int y = 0; y < world.height(); y++){
|
for(int y = 0; y < world.height(); y++){
|
||||||
@@ -350,8 +372,6 @@ public class Pathfinder implements Runnable{
|
|||||||
path.weights[Point2.x(pos)][Point2.y(pos)] = 0;
|
path.weights[Point2.x(pos)][Point2.y(pos)] = 0;
|
||||||
path.frontier.addFirst(pos);
|
path.frontier.addFirst(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
return path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Update the frontier for a path. Pathfinding thread only. */
|
/** Update the frontier for a path. Pathfinding thread only. */
|
||||||
@@ -386,21 +406,6 @@ public class Pathfinder implements Runnable{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final PathCost
|
|
||||||
|
|
||||||
groundCost = (team, tile) -> (PathTile.team(tile) == team.id || PathTile.team(tile) == 0) && PathTile.solid(tile) ? impassable : 1 +
|
|
||||||
PathTile.health(tile) * 5 +
|
|
||||||
(PathTile.nearSolid(tile) ? 2 : 0) +
|
|
||||||
(PathTile.nearLiquid(tile) ? 6 : 0) +
|
|
||||||
(PathTile.deep(tile) ? 70 : 0),
|
|
||||||
|
|
||||||
legsCost = (team, tile) -> PathTile.legSolid(tile) ? impassable : 1 +
|
|
||||||
(PathTile.solid(tile) ? 5 : 0),
|
|
||||||
|
|
||||||
waterCost = (team, tile) -> PathTile.solid(tile) || !PathTile.liquid(tile) ? impassable : 2 + //TODO cannot go through blocks
|
|
||||||
(PathTile.nearGround(tile) || PathTile.nearSolid(tile) ? 14 : 0) +
|
|
||||||
(PathTile.deep(tile) ? -1 : 0);
|
|
||||||
|
|
||||||
public static class EnemyCoreField extends Flowfield{
|
public static class EnemyCoreField extends Flowfield{
|
||||||
@Override
|
@Override
|
||||||
protected void getPositions(IntSeq out){
|
protected void getPositions(IntSeq out){
|
||||||
@@ -451,7 +456,7 @@ public class Pathfinder implements Runnable{
|
|||||||
/** Team this path is for. Set before using. */
|
/** Team this path is for. Set before using. */
|
||||||
protected Team team = Team.derelict;
|
protected Team team = Team.derelict;
|
||||||
/** Function for calculating path cost. Set before using. */
|
/** Function for calculating path cost. Set before using. */
|
||||||
protected PathCost cost = groundCost;
|
protected PathCost cost = costTypes.get(costGround);
|
||||||
|
|
||||||
/** costs of getting to a specific tile */
|
/** costs of getting to a specific tile */
|
||||||
int[][] weights;
|
int[][] weights;
|
||||||
|
|||||||
@@ -47,7 +47,8 @@ public class FormationAI extends AIController implements FormationMember{
|
|||||||
Vec2 realtarget = vec.set(target);
|
Vec2 realtarget = vec.set(target);
|
||||||
|
|
||||||
if(unit.isGrounded() && Vars.world.raycast(unit.tileX(), unit.tileY(), leader.tileX(), leader.tileY(), Vars.world::solid)){
|
if(unit.isGrounded() && Vars.world.raycast(unit.tileX(), unit.tileY(), leader.tileX(), leader.tileY(), Vars.world::solid)){
|
||||||
realtarget.set(Vars.pathfinder.getTargetTile(unit.tileOn(), unit.team, leader));
|
//TODO pathfind
|
||||||
|
//realtarget.set(Vars.pathfinder.getTargetTile(unit.tileOn(), unit.team, leader));
|
||||||
}
|
}
|
||||||
|
|
||||||
unit.moveAt(realtarget.sub(unit).limit(unit.type().speed));
|
unit.moveAt(realtarget.sub(unit).limit(unit.type().speed));
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
package mindustry.ai.types;
|
package mindustry.ai.types;
|
||||||
|
|
||||||
import mindustry.ai.Pathfinder.*;
|
import mindustry.ai.*;
|
||||||
import mindustry.entities.*;
|
import mindustry.entities.*;
|
||||||
import mindustry.entities.units.*;
|
import mindustry.entities.units.*;
|
||||||
import mindustry.game.*;
|
|
||||||
import mindustry.gen.*;
|
import mindustry.gen.*;
|
||||||
import mindustry.world.*;
|
import mindustry.world.*;
|
||||||
|
|
||||||
@@ -24,7 +23,7 @@ public class GroundAI extends AIController{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!unit.within(core, unit.range() * 0.5f)){
|
if(!unit.within(core, unit.range() * 0.5f)){
|
||||||
moveToCore(FlagTarget.enemyCores);
|
moveTo(Pathfinder.fieldCore);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,41 +51,18 @@ public class GroundAI extends AIController{
|
|||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void moveToCore(FlagTarget path){
|
protected void moveTo(int pathType){
|
||||||
|
int costType =
|
||||||
|
unit instanceof Legsc ? Pathfinder.costLegs :
|
||||||
|
unit instanceof WaterMovec ? Pathfinder.costWater :
|
||||||
|
Pathfinder.costGround;
|
||||||
|
|
||||||
Tile tile = unit.tileOn();
|
Tile tile = unit.tileOn();
|
||||||
if(tile == null) return;
|
if(tile == null) return;
|
||||||
Tile targetTile = pathfinder.getTargetTile(tile, unit.team(), path);
|
Tile targetTile = pathfinder.getTargetTile(tile, pathfinder.getField(unit.team, costType, pathType));
|
||||||
|
|
||||||
if(tile == targetTile) return;
|
if(tile == targetTile) return;
|
||||||
|
|
||||||
unit.moveAt(vec.trns(unit.angleTo(targetTile), unit.type().speed));
|
unit.moveAt(vec.trns(unit.angleTo(targetTile), unit.type().speed));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void moveAwayFromCore(){
|
|
||||||
Team enemy = null;
|
|
||||||
for(Team team : unit.team().enemies()){
|
|
||||||
if(team.active()){
|
|
||||||
enemy = team;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(enemy == null){
|
|
||||||
for(Team team : unit.team().enemies()){
|
|
||||||
enemy = team;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(enemy == null) return;
|
|
||||||
|
|
||||||
Tile tile = unit.tileOn();
|
|
||||||
if(tile == null) return;
|
|
||||||
Tile targetTile = pathfinder.getTargetTile(tile, enemy, FlagTarget.enemyCores);
|
|
||||||
Building core = unit.closestCore();
|
|
||||||
|
|
||||||
if(tile == targetTile || core == null || unit.within(core, 120f)) return;
|
|
||||||
|
|
||||||
unit.moveAt(vec.trns(unit.angleTo(targetTile), unit.type().speed));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package mindustry.ai.types;
|
package mindustry.ai.types;
|
||||||
|
|
||||||
import mindustry.*;
|
import mindustry.*;
|
||||||
import mindustry.ai.Pathfinder.*;
|
import mindustry.ai.*;
|
||||||
import mindustry.entities.*;
|
import mindustry.entities.*;
|
||||||
import mindustry.gen.*;
|
import mindustry.gen.*;
|
||||||
import mindustry.world.*;
|
import mindustry.world.*;
|
||||||
@@ -59,7 +59,7 @@ public class SuicideAI extends GroundAI{
|
|||||||
|
|
||||||
}else{
|
}else{
|
||||||
if(core != null){
|
if(core != null){
|
||||||
moveToCore(FlagTarget.enemyCores);
|
moveTo(Pathfinder.fieldCore);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(unit.moving()) unit.lookAt(unit.vel().angle());
|
if(unit.moving()) unit.lookAt(unit.vel().angle());
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ public class SectorDamage{
|
|||||||
if(core != null && !frontier.isEmpty()){
|
if(core != null && !frontier.isEmpty()){
|
||||||
for(Tile spawner : frontier){
|
for(Tile spawner : frontier){
|
||||||
//find path from spawn to core
|
//find path from spawn to core
|
||||||
Seq<Tile> path = Astar.pathfind(spawner, core.tile, t -> t.cost, t -> !(t.block().isStatic() && t.solid()));
|
//TODO this is broken
|
||||||
|
Seq<Tile> path = Astar.pathfind(spawner, core.tile, SectorDamage::cost, t -> !(t.block().isStatic() && t.solid()));
|
||||||
int amount = (int)(path.size * fraction);
|
int amount = (int)(path.size * fraction);
|
||||||
for(int i = 0; i < amount; i++){
|
for(int i = 0; i < amount; i++){
|
||||||
Tile t = path.get(i);
|
Tile t = path.get(i);
|
||||||
@@ -104,8 +105,12 @@ public class SectorDamage{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static float cost(Tile tile){
|
||||||
|
return 1f +
|
||||||
|
(tile.block().isStatic() && tile.solid() ? 200f : 0f) +
|
||||||
|
(tile.build != null ? tile.build.health / 40f : 0f) +
|
||||||
|
(tile.floor().isLiquid ? 10f : 0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,63 +39,6 @@ public abstract class BasicGenerator implements WorldGenerator{
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//for visual testing only
|
|
||||||
public void cliffs2(){
|
|
||||||
for(Tile tile : tiles){
|
|
||||||
tile.setBlock(Blocks.air);
|
|
||||||
tile.cost = tile.floor().isLiquid ? 0 : (byte)(noise(tile.x, tile.y, 4, 0.5f, 90f, 1) * 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(Tile tile : tiles){
|
|
||||||
if(tile.floor().isLiquid) continue;
|
|
||||||
|
|
||||||
int rotation = 0;
|
|
||||||
for(int i = 0; i < 8; i++){
|
|
||||||
Tile other = tiles.get(tile.x + Geometry.d8[i].x, tile.y + Geometry.d8[i].y);
|
|
||||||
if(other != null && other.cost < tile.cost){ //down slope
|
|
||||||
rotation |= (1 << i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tile.data = (byte)rotation;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(Tile tile : tiles){
|
|
||||||
if(tile.data != 0){
|
|
||||||
int rotation = tile.data;
|
|
||||||
tile.setBlock(Blocks.cliff);
|
|
||||||
tile.setOverlay(Blocks.air);
|
|
||||||
tile.data = (byte)rotation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void cliffs(){
|
|
||||||
for(Tile tile : tiles){
|
|
||||||
if(!tile.block().isStatic()) continue;
|
|
||||||
|
|
||||||
int rotation = 0;
|
|
||||||
for(int i = 0; i < 8; i++){
|
|
||||||
Tile other = tiles.get(tile.x + Geometry.d8[i].x, tile.y + Geometry.d8[i].y);
|
|
||||||
if(other != null && !other.block().isStatic()){
|
|
||||||
rotation |= (1 << i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(rotation != 0){
|
|
||||||
tile.setBlock(Blocks.cliff);
|
|
||||||
}
|
|
||||||
|
|
||||||
tile.data = (byte)rotation;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(Tile tile : tiles){
|
|
||||||
if(tile.block() != Blocks.cliff && tile.block().isStatic()){
|
|
||||||
tile.setBlock(Blocks.air);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void median(int radius){
|
public void median(int radius){
|
||||||
median(radius, 0.5);
|
median(radius, 0.5);
|
||||||
}
|
}
|
||||||
@@ -336,13 +279,13 @@ public abstract class BasicGenerator implements WorldGenerator{
|
|||||||
while(!arr.isEmpty()){
|
while(!arr.isEmpty()){
|
||||||
int i = arr.pop();
|
int i = arr.pop();
|
||||||
int x = Point2.x(i), y = Point2.y(i);
|
int x = Point2.x(i), y = Point2.y(i);
|
||||||
tiles.getn(x, y).cost = 2;
|
tiles.getn(x, y).data = 2;
|
||||||
for(Point2 point : Geometry.d4){
|
for(Point2 point : Geometry.d4){
|
||||||
int newx = x + point.x, newy = y + point.y;
|
int newx = x + point.x, newy = y + point.y;
|
||||||
if(tiles.in(newx, newy)){
|
if(tiles.in(newx, newy)){
|
||||||
Tile child = tiles.getn(newx, newy);
|
Tile child = tiles.getn(newx, newy);
|
||||||
if(child.block() == Blocks.air && child.cost != 2){
|
if(child.block() == Blocks.air && child.data != 2){
|
||||||
child.cost = 2;
|
child.data = 2;
|
||||||
arr.add(child.pos());
|
arr.add(child.pos());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -350,7 +293,7 @@ public abstract class BasicGenerator implements WorldGenerator{
|
|||||||
}
|
}
|
||||||
|
|
||||||
for(Tile tile : tiles){
|
for(Tile tile : tiles){
|
||||||
if(tile.cost != 2 && tile.block() == Blocks.air){
|
if(tile.data != 2 && tile.block() == Blocks.air){
|
||||||
tile.setBlock(tile.floor().wall);
|
tile.setBlock(tile.floor().wall);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
org.gradle.daemon=true
|
org.gradle.daemon=true
|
||||||
org.gradle.jvmargs=-Xms256m -Xmx1024m
|
org.gradle.jvmargs=-Xms256m -Xmx1024m
|
||||||
archash=0fafa5b7159c2a6c11f03ebd378a47736101b46e
|
archash=a72f08a6812107fdf4462b7c6955efc19a67715b
|
||||||
|
|||||||
Reference in New Issue
Block a user