Pathfinding cleanup
This commit is contained in:
@@ -23,36 +23,33 @@ public class ControlPathfinder{
|
||||
private static final int updateInterval = 1000 / updateFPS;
|
||||
private static final int wallImpassableCap = 100_000;
|
||||
|
||||
public static final PathCost
|
||||
|
||||
costGround = (team, tile) ->
|
||||
//deep is impassable
|
||||
PathTile.allDeep(tile) ? impassable :
|
||||
//impassable same-team or neutral block
|
||||
PathTile.solid(tile) && (PathTile.team(tile) == team || PathTile.team(tile) == 0) ? impassable :
|
||||
//impassable synthetic enemy block
|
||||
((PathTile.team(tile) != team && PathTile.team(tile) != 0) && PathTile.solid(tile) ? wallImpassableCap : 0) +
|
||||
1 +
|
||||
(PathTile.nearSolid(tile) ? 6 : 0) +
|
||||
(PathTile.nearLiquid(tile) ? 8 : 0) +
|
||||
(PathTile.deep(tile) ? 6000 : 0) +
|
||||
(PathTile.damages(tile) ? 50 : 0),
|
||||
|
||||
costLegs = (team, tile) ->
|
||||
PathTile.legSolid(tile) ? impassable : 1 +
|
||||
(PathTile.deep(tile) ? 6000 : 0) +
|
||||
(PathTile.nearSolid(tile) || PathTile.solid(tile) ? 3 : 0),
|
||||
|
||||
costNaval = (team, tile) ->
|
||||
(PathTile.solid(tile) || !PathTile.liquid(tile) ? impassable : 1) +
|
||||
(PathTile.nearGround(tile) || PathTile.nearSolid(tile) ? 2 : 0) +
|
||||
(PathTile.deep(tile) ? 0 : 1);
|
||||
|
||||
public static boolean showDebug = false;
|
||||
|
||||
public static final Seq<PathCost> costTypes = Seq.with(
|
||||
//ground
|
||||
(team, tile) ->
|
||||
//deep is impassable
|
||||
PathTile.allDeep(tile) ? impassable :
|
||||
//impassable same-team or neutral block
|
||||
PathTile.solid(tile) && (PathTile.team(tile) == team || PathTile.team(tile) == 0) ? impassable :
|
||||
//impassable synthetic enemy block
|
||||
((PathTile.team(tile) != team && PathTile.team(tile) != 0) && PathTile.solid(tile) ? wallImpassableCap : 0) +
|
||||
1 +
|
||||
(PathTile.nearSolid(tile) ? 6 : 0) +
|
||||
(PathTile.nearLiquid(tile) ? 8 : 0) +
|
||||
(PathTile.deep(tile) ? 6000 : 0) +
|
||||
(PathTile.damages(tile) ? 50 : 0),
|
||||
|
||||
//legs
|
||||
(team, tile) ->
|
||||
PathTile.legSolid(tile) ? impassable : 1 +
|
||||
(PathTile.deep(tile) ? 6000 : 0) +
|
||||
(PathTile.nearSolid(tile) || PathTile.solid(tile) ? 3 : 0),
|
||||
|
||||
//water
|
||||
(team, tile) ->
|
||||
(PathTile.solid(tile) || !PathTile.liquid(tile) ? impassable : 1) +
|
||||
(PathTile.nearGround(tile) || PathTile.nearSolid(tile) ? 2 : 0) +
|
||||
(PathTile.deep(tile) ? 0 : 1)
|
||||
);
|
||||
|
||||
//static access probably faster than object access
|
||||
static int wwidth, wheight;
|
||||
//increments each tile change
|
||||
@@ -153,16 +150,16 @@ public class ControlPathfinder{
|
||||
//uninitialized
|
||||
if(threads == null) return false;
|
||||
|
||||
int pathType = unit.pathType();
|
||||
PathCost costType = unit.type.pathCost;
|
||||
|
||||
//if the destination can be trivially reached in a straight line, do that.
|
||||
if((!requests.containsKey(unit) || requests.get(unit).curId != pathId) && !raycast(pathType, unit.tileX(), unit.tileY(), World.toTile(destination.x), World.toTile(destination.y))){
|
||||
if((!requests.containsKey(unit) || requests.get(unit).curId != pathId) && !raycast(costType, unit.tileX(), unit.tileY(), World.toTile(destination.x), World.toTile(destination.y))){
|
||||
out.set(destination);
|
||||
return true;
|
||||
}
|
||||
|
||||
//destination is impassable, can't go there.
|
||||
if(solid(pathType, world.packArray(World.toTile(destination.x), World.toTile(destination.y)))){
|
||||
if(solid(costType, world.packArray(World.toTile(destination.x), World.toTile(destination.y)))){
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -172,7 +169,7 @@ public class ControlPathfinder{
|
||||
|
||||
var req = new PathRequest(thread);
|
||||
req.unit = unit;
|
||||
req.pathType = pathType;
|
||||
req.cost = costType;
|
||||
req.destination.set(destination);
|
||||
req.curId = pathId;
|
||||
req.team = unit.team.id;
|
||||
@@ -224,7 +221,7 @@ public class ControlPathfinder{
|
||||
Tile tile = tile(items[i]);
|
||||
float dst = unit.dst2(tile);
|
||||
//TODO maybe put this on a timer since raycasts can be expensive?
|
||||
if(dst < minDst && !permissiveRaycast(pathType, tileX, tileY, tile.x, tile.y)){
|
||||
if(dst < minDst && !permissiveRaycast(costType, tileX, tileY, tile.x, tile.y)){
|
||||
req.pathIndex = Math.max(dst <= range * range ? i + 1 : i, req.pathIndex);
|
||||
minDst = Math.min(dst, minDst);
|
||||
}
|
||||
@@ -237,7 +234,7 @@ public class ControlPathfinder{
|
||||
if((req.raycastTimer += Time.delta) >= 50f){
|
||||
for(int i = len - 1; i > req.pathIndex; i--){
|
||||
int val = items[i];
|
||||
if(!raycast(pathType, tileX, tileY, val % wwidth, val / wwidth)){
|
||||
if(!raycast(costType, tileX, tileY, val % wwidth, val / wwidth)){
|
||||
req.rayPathIndex = i;
|
||||
break;
|
||||
}
|
||||
@@ -288,14 +285,14 @@ public class ControlPathfinder{
|
||||
requests.clear();
|
||||
}
|
||||
|
||||
private static boolean raycast(int type, int x1, int y1, int x2, int y2){
|
||||
private static boolean raycast(PathCost cost, int x1, int y1, int x2, int y2){
|
||||
int ww = world.width(), wh = world.height();
|
||||
int x = x1, dx = Math.abs(x2 - x), sx = x < x2 ? 1 : -1;
|
||||
int y = y1, dy = Math.abs(y2 - y), sy = y < y2 ? 1 : -1;
|
||||
int e2, err = dx - dy;
|
||||
|
||||
while(x >= 0 && y >= 0 && x < ww && y < wh){
|
||||
if(avoid(type, x + y * wwidth)) return true;
|
||||
if(avoid(cost, x + y * wwidth)) return true;
|
||||
if(x == x2 && y == y2) return false;
|
||||
|
||||
e2 = 2 * err;
|
||||
@@ -314,7 +311,7 @@ public class ControlPathfinder{
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean permissiveRaycast(int type, int x1, int y1, int x2, int y2){
|
||||
private static boolean permissiveRaycast(PathCost type, int x1, int y1, int x2, int y2){
|
||||
int ww = world.width(), wh = world.height();
|
||||
int x = x1, dx = Math.abs(x2 - x), sx = x < x2 ? 1 : -1;
|
||||
int y = y1, dy = Math.abs(y2 - y), sy = y < y2 ? 1 : -1;
|
||||
@@ -337,8 +334,8 @@ public class ControlPathfinder{
|
||||
return true;
|
||||
}
|
||||
|
||||
static boolean cast(int pathType, int from, int to){
|
||||
return raycast(pathType, from % wwidth, from / wwidth, to % wwidth, to / wwidth);
|
||||
static boolean cast(PathCost cost, int from, int to){
|
||||
return raycast(cost, from % wwidth, from / wwidth, to % wwidth, to / wwidth);
|
||||
}
|
||||
|
||||
private Tile tile(int pos){
|
||||
@@ -351,25 +348,25 @@ public class ControlPathfinder{
|
||||
return Math.abs(x - x2) + Math.abs(y - y2);
|
||||
}
|
||||
|
||||
private static int tcost(int team, int type, int tilePos){
|
||||
return costTypes.items[type].getCost(team, pathfinder.tiles[tilePos]);
|
||||
private static int tcost(int team, PathCost cost, int tilePos){
|
||||
return cost.getCost(team, pathfinder.tiles[tilePos]);
|
||||
}
|
||||
|
||||
private static int cost(int type, int tilePos){
|
||||
return costTypes.items[type].getCost(-1, pathfinder.tiles[tilePos]);
|
||||
private static int cost(PathCost cost, int tilePos){
|
||||
return cost.getCost(-1, pathfinder.tiles[tilePos]);
|
||||
}
|
||||
|
||||
private static boolean avoid(int type, int tilePos){
|
||||
private static boolean avoid(PathCost type, int tilePos){
|
||||
int cost = cost(type, tilePos);
|
||||
return cost == impassable || cost >= 2;
|
||||
}
|
||||
|
||||
private static boolean solid(int type, int tilePos){
|
||||
private static boolean solid(PathCost type, int tilePos){
|
||||
int cost = cost(type, tilePos);
|
||||
return cost == impassable || cost >= 6000;
|
||||
}
|
||||
|
||||
private static float tileCost(int type, int a, int b){
|
||||
private static float tileCost(PathCost type, int a, int b){
|
||||
//currently flat cost
|
||||
return cost(type, b);
|
||||
}
|
||||
@@ -423,7 +420,7 @@ public class ControlPathfinder{
|
||||
volatile boolean done = false;
|
||||
volatile boolean foundEnd = false;
|
||||
volatile Unit unit;
|
||||
volatile int pathType;
|
||||
volatile PathCost cost;
|
||||
volatile int team;
|
||||
volatile int lastWorldUpdate;
|
||||
|
||||
@@ -492,9 +489,9 @@ public class ControlPathfinder{
|
||||
if(newx >= wwidth || newy >= wheight || newx < 0 || newy < 0) continue;
|
||||
|
||||
//in fallback mode, enemy walls are passable
|
||||
if(tcost(team, pathType, next) == impassable) continue;
|
||||
if(tcost(team, cost, next) == impassable) continue;
|
||||
|
||||
float add = tileCost(pathType, current, next);
|
||||
float add = tileCost(cost, current, next);
|
||||
float currentCost = costs.get(current);
|
||||
|
||||
//the cost can include an impassable enemy wall, so cap the cost if so and add the base cost instead
|
||||
@@ -552,7 +549,7 @@ public class ControlPathfinder{
|
||||
int output = 1, input = 2;
|
||||
|
||||
while(input < len){
|
||||
if(cast(pathType, result.get(output - 1), result.get(input))){
|
||||
if(cast(cost, result.get(output - 1), result.get(input))){
|
||||
result.swap(output, input - 1);
|
||||
output++;
|
||||
}
|
||||
|
||||
@@ -2,13 +2,16 @@ package mindustry.ctype;
|
||||
|
||||
import arc.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.graphics.g2d.TextureAtlas.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.TechTree.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.graphics.MultiPacker.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.meta.*;
|
||||
@@ -91,6 +94,20 @@ public abstract class UnlockableContent extends MappableContent{
|
||||
|
||||
}
|
||||
|
||||
protected void makeOutline(PageType page, MultiPacker packer, TextureRegion region, boolean makeNew, Color outlineColor, int outlineRadius){
|
||||
if(region instanceof AtlasRegion at && region.found()){
|
||||
String name = at.name;
|
||||
if(!makeNew || !packer.has(name + "-outline")){
|
||||
PixmapRegion base = Core.atlas.getPixmap(region);
|
||||
var result = Pixmaps.outline(base, outlineColor, outlineRadius);
|
||||
if(Core.settings.getBool("linear", true)){
|
||||
Pixmaps.bleed(result);
|
||||
}
|
||||
packer.add(page, name + (makeNew ? "-outline" : ""), result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @return items needed to research this content */
|
||||
public ItemStack[] researchRequirements(){
|
||||
return ItemStack.empty;
|
||||
|
||||
@@ -10,6 +10,8 @@ import mindustry.ctype.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.graphics.MultiPacker.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
public class StatusEffect extends UnlockableContent{
|
||||
@@ -47,6 +49,8 @@ public class StatusEffect extends UnlockableContent{
|
||||
public Effect effect = Fx.none;
|
||||
/** Affinity & opposite values for stat displays. */
|
||||
public ObjectSet<StatusEffect> affinities = new ObjectSet<>(), opposites = new ObjectSet<>();
|
||||
/** Set to false to disable outline generation. */
|
||||
public boolean outline = true;
|
||||
/** Transition handler map. */
|
||||
protected ObjectMap<StatusEffect, TransitionHandler> transitions = new ObjectMap<>();
|
||||
/** Called on init. */
|
||||
@@ -181,6 +185,15 @@ public class StatusEffect extends UnlockableContent{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createIcons(MultiPacker packer){
|
||||
super.createIcons(packer);
|
||||
|
||||
if(outline){
|
||||
makeOutline(PageType.ui, packer, uiIcon, true, Pal.gray, 3);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return localizedName;
|
||||
|
||||
@@ -13,6 +13,8 @@ import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.ai.*;
|
||||
import mindustry.ai.Pathfinder.*;
|
||||
import mindustry.ai.types.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
@@ -180,6 +182,8 @@ public class UnitType extends UnlockableContent{
|
||||
public boolean forceMultiTarget = false;
|
||||
public boolean hidden = false;
|
||||
public boolean internal = false;
|
||||
/** Function used for calculating cost of moving with ControlPathfinder. Does not affect "normal" flow field pathfinding. */
|
||||
public @Nullable PathCost pathCost;
|
||||
/** A sample of the unit that this type creates. Do not modify! */
|
||||
public @Nullable Unit sample;
|
||||
|
||||
@@ -417,6 +421,13 @@ public class UnitType extends UnlockableContent{
|
||||
}
|
||||
}
|
||||
|
||||
if(pathCost == null){
|
||||
pathCost =
|
||||
example instanceof WaterMovec ? ControlPathfinder.costNaval :
|
||||
allowLegStep ? ControlPathfinder.costLegs :
|
||||
ControlPathfinder.costGround;
|
||||
}
|
||||
|
||||
if(flying){
|
||||
envEnabled |= Env.space;
|
||||
}
|
||||
@@ -589,20 +600,6 @@ public class UnitType extends UnlockableContent{
|
||||
clipSize = Math.max(region.width * 2f, clipSize);
|
||||
}
|
||||
|
||||
private void makeOutline(MultiPacker packer, TextureRegion region, boolean makeNew){
|
||||
if(region instanceof AtlasRegion at && region.found()){
|
||||
String name = at.name;
|
||||
if(!makeNew || !packer.has(name + "-outline")){
|
||||
PixmapRegion base = Core.atlas.getPixmap(region);
|
||||
var result = Pixmaps.outline(base, outlineColor, outlineRadius);
|
||||
if(Core.settings.getBool("linear", true)){
|
||||
Pixmaps.bleed(result);
|
||||
}
|
||||
packer.add(PageType.main, name + (makeNew ? "-outline" : ""), result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void getRegionsToOutline(Seq<TextureRegion> out){
|
||||
for(Weapon weapon : weapons){
|
||||
for(var part : weapon.parts){
|
||||
@@ -640,11 +637,11 @@ public class UnitType extends UnlockableContent{
|
||||
if(outlines){
|
||||
|
||||
//outlines only created when forced at the moment
|
||||
makeOutline(packer, region, alwaysCreateOutline);
|
||||
makeOutline(PageType.main, packer, region, alwaysCreateOutline, outlineColor, outlineRadius);
|
||||
|
||||
for(Weapon weapon : weapons){
|
||||
if(!weapon.name.isEmpty()){
|
||||
makeOutline(packer, weapon.region, true);
|
||||
makeOutline(PageType.main, packer, weapon.region, true, outlineColor, outlineRadius);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user