diff --git a/core/src/mindustry/ai/Pathfinder.java b/core/src/mindustry/ai/Pathfinder.java index dac1a3fde0..160bf09d35 100644 --- a/core/src/mindustry/ai/Pathfinder.java +++ b/core/src/mindustry/ai/Pathfinder.java @@ -23,6 +23,38 @@ public class Pathfinder implements Runnable{ private static final int impassable = -1; private static final int fieldTimeout = 1000 * 60 * 2; + public static final int + fieldCore = 0, + fieldRally = 1; + + public static final Seq> fieldTypes = Seq.with( + EnemyCoreField::new, + RallyField::new + ); + + public static final int + costGround = 0, + costLegs = 1, + costWater = 2; + + public static final Seq 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 */ private int[][] tiles; /** 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(); public Pathfinder(){ + clearCache(); + Events.on(WorldLoadEvent.class, event -> { stop(); @@ -41,13 +75,14 @@ public class Pathfinder implements Runnable{ tiles = new int[world.width()][world.height()]; threadList = new Seq<>(); mainList = new Seq<>(); + clearCache(); for(Tile tile : world.tiles){ tiles[tile.x][tile.y] = packTile(tile); } //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(); }); @@ -57,8 +92,8 @@ public class Pathfinder implements Runnable{ 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. */ @@ -189,34 +224,26 @@ public class Pathfinder implements Runnable{ // return getTargetTile(tile, team, getTarget(target)); // } - public @Nullable Tile getTargetTile(Tile tile, Prov fieldtype){ - if(true){ //TODO cache this - Flowfield field = fieldtype.get(); - IntSeq out = new IntSeq(); - field.getPositions(out); - createPath(field, out); - } + public Flowfield getField(Team team, int costType, int fieldType){ + if(cache[team.id][costType][fieldType] == null){ + Flowfield field = fieldTypes.get(fieldType).get(); + field.team = team; + field.cost = costTypes.get(costType); + field.targets.clear(); + field.getPositions(field.targets); - if(false){ //TODO if field exists - //TODO fetch it from the cache - //return getTargetTile(tile, path); + cache[team.id][costType][fieldType] = field; + queue.post(() -> registerPath(field)); } - - return tile; + return cache[team.id][costType][fieldType]; } /** Gets next tile to travel to. Main thread only. */ public @Nullable Tile getTargetTile(Tile tile, Flowfield path){ if(tile == null) return null; - if(path == null){ - //if this combination is not found, create it on request - //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)); - //} + //uninitialized flowfields are not applicable + if(!path.initialized){ return tile; } @@ -306,9 +333,10 @@ public class Pathfinder implements Runnable{ } private void preloadPath(Flowfield path){ - IntSeq out = new IntSeq(); - path.getPositions(out); - updateFrontier(createPath(path, out), -1); + path.targets.clear(); + path.getPositions(path.targets); + 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. * Pathfinding thread only. */ - private Flowfield createPath(Flowfield path, IntSeq targets){ + private void registerPath(Flowfield path){ path.lastUpdateTime = Time.millis(); 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 for(int x = 0; x < world.width(); x++){ 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.frontier.addFirst(pos); } - - return path; } /** 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{ @Override protected void getPositions(IntSeq out){ @@ -451,7 +456,7 @@ public class Pathfinder implements Runnable{ /** Team this path is for. Set before using. */ protected Team team = Team.derelict; /** 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 */ int[][] weights; diff --git a/core/src/mindustry/ai/types/FormationAI.java b/core/src/mindustry/ai/types/FormationAI.java index 9ef064cfe1..0a5bdbb25b 100644 --- a/core/src/mindustry/ai/types/FormationAI.java +++ b/core/src/mindustry/ai/types/FormationAI.java @@ -47,7 +47,8 @@ public class FormationAI extends AIController implements FormationMember{ Vec2 realtarget = vec.set(target); 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)); diff --git a/core/src/mindustry/ai/types/GroundAI.java b/core/src/mindustry/ai/types/GroundAI.java index ac2c110c12..bf33c99004 100644 --- a/core/src/mindustry/ai/types/GroundAI.java +++ b/core/src/mindustry/ai/types/GroundAI.java @@ -1,9 +1,8 @@ package mindustry.ai.types; -import mindustry.ai.Pathfinder.*; +import mindustry.ai.*; import mindustry.entities.*; import mindustry.entities.units.*; -import mindustry.game.*; import mindustry.gen.*; import mindustry.world.*; @@ -24,7 +23,7 @@ public class GroundAI extends AIController{ } 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(); 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; 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)); - } } diff --git a/core/src/mindustry/ai/types/SuicideAI.java b/core/src/mindustry/ai/types/SuicideAI.java index 354b90f8b3..57908dd584 100644 --- a/core/src/mindustry/ai/types/SuicideAI.java +++ b/core/src/mindustry/ai/types/SuicideAI.java @@ -1,7 +1,7 @@ package mindustry.ai.types; import mindustry.*; -import mindustry.ai.Pathfinder.*; +import mindustry.ai.*; import mindustry.entities.*; import mindustry.gen.*; import mindustry.world.*; @@ -59,7 +59,7 @@ public class SuicideAI extends GroundAI{ }else{ if(core != null){ - moveToCore(FlagTarget.enemyCores); + moveTo(Pathfinder.fieldCore); } if(unit.moving()) unit.lookAt(unit.vel().angle()); diff --git a/core/src/mindustry/maps/SectorDamage.java b/core/src/mindustry/maps/SectorDamage.java index da946ea686..5f4e267d74 100644 --- a/core/src/mindustry/maps/SectorDamage.java +++ b/core/src/mindustry/maps/SectorDamage.java @@ -35,7 +35,8 @@ public class SectorDamage{ if(core != null && !frontier.isEmpty()){ for(Tile spawner : frontier){ //find path from spawn to core - Seq path = Astar.pathfind(spawner, core.tile, t -> t.cost, t -> !(t.block().isStatic() && t.solid())); + //TODO this is broken + Seq path = Astar.pathfind(spawner, core.tile, SectorDamage::cost, t -> !(t.block().isStatic() && t.solid())); int amount = (int)(path.size * fraction); for(int i = 0; i < amount; 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); } } diff --git a/core/src/mindustry/maps/generators/BasicGenerator.java b/core/src/mindustry/maps/generators/BasicGenerator.java index d7db4d12ff..8c1076e692 100644 --- a/core/src/mindustry/maps/generators/BasicGenerator.java +++ b/core/src/mindustry/maps/generators/BasicGenerator.java @@ -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){ median(radius, 0.5); } @@ -336,13 +279,13 @@ public abstract class BasicGenerator implements WorldGenerator{ while(!arr.isEmpty()){ int i = arr.pop(); 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){ int newx = x + point.x, newy = y + point.y; if(tiles.in(newx, newy)){ Tile child = tiles.getn(newx, newy); - if(child.block() == Blocks.air && child.cost != 2){ - child.cost = 2; + if(child.block() == Blocks.air && child.data != 2){ + child.data = 2; arr.add(child.pos()); } } @@ -350,7 +293,7 @@ public abstract class BasicGenerator implements WorldGenerator{ } 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); } } diff --git a/gradle.properties b/gradle.properties index 30169cb6ce..ee83875d3c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=true org.gradle.jvmargs=-Xms256m -Xmx1024m -archash=0fafa5b7159c2a6c11f03ebd378a47736101b46e +archash=a72f08a6812107fdf4462b7c6955efc19a67715b