From c7181999b246d586ea40def9cb5ffb3c8167896c Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 19 Jun 2023 17:21:21 -0400 Subject: [PATCH] Pathfinding improvements --- core/src/mindustry/ai/ControlPathfinder.java | 31 ++++++++++++++++--- core/src/mindustry/ai/Pathfinder.java | 9 ++++-- .../mindustry/entities/EntityCollisions.java | 3 +- core/src/mindustry/world/Tile.java | 5 +++ 4 files changed, 39 insertions(+), 9 deletions(-) diff --git a/core/src/mindustry/ai/ControlPathfinder.java b/core/src/mindustry/ai/ControlPathfinder.java index 31eafee6d2..d2e74f9825 100644 --- a/core/src/mindustry/ai/ControlPathfinder.java +++ b/core/src/mindustry/ai/ControlPathfinder.java @@ -7,6 +7,7 @@ import arc.math.*; import arc.math.geom.*; import arc.struct.*; import arc.util.*; +import mindustry.content.*; import mindustry.core.*; import mindustry.game.EventType.*; import mindustry.game.*; @@ -51,7 +52,7 @@ public class ControlPathfinder{ costLegs = (team, tile) -> PathTile.legSolid(tile) ? impassable : 1 + (PathTile.deep(tile) ? 6000 : 0) + - (PathTile.solid(tile) ? 2 : 0), + (PathTile.nearLegSolid(tile) ? 3 : 0), costNaval = (team, tile) -> //impassable same-team neutral block, or non-liquid @@ -232,11 +233,11 @@ public class ControlPathfinder{ req.curId = pathId; //check for the unit getting stuck every N seconds - if((req.stuckTimer += Time.delta) >= 60f * 2.5f){ + if((req.stuckTimer += Time.delta) >= 60f * 1.5f){ req.stuckTimer = 0f; //force recalculate if(req.lastPos.within(unit, 1.5f)){ - req.lastWorldUpdate = -1; + req.forceRecalculate(); } req.lastPos.set(unit); } @@ -280,6 +281,18 @@ public class ControlPathfinder{ Tile tile = tile(items[req.rayPathIndex]); out.set(tile); + if(req.rayPathIndex > 0){ + float angleToNext = tile(items[req.rayPathIndex - 1]).angleTo(tile); + float angleToDest = unit.angleTo(tile); + //force recalculate when the unit moves backwards + if(Angles.angleDist(angleToNext, angleToDest) > 80f && !unit.within(tile, 1f)){ + if(showDebug){ + Fx.placeBlock.at(unit, 1f); + } + req.forceRecalculate(); + } + } + if(unit.within(tile, range)){ req.pathIndex = req.rayPathIndex = Math.max(req.pathIndex, req.rayPathIndex + 1); } @@ -482,6 +495,7 @@ public class ControlPathfinder{ volatile PathCost cost; volatile int team; volatile int lastWorldUpdate; + volatile boolean forcedRecalc; final Vec2 lastPos = new Vec2(); float stuckTimer = 0f; @@ -506,6 +520,7 @@ public class ControlPathfinder{ long lastUpdateId; long lastTime; + long forceRecalcTime; volatile int lastId, curId; @@ -513,6 +528,13 @@ public class ControlPathfinder{ this.thread = thread; } + public void forceRecalculate(){ + //keep it at 3 times/sec + if(Time.timeSinceMillis(forceRecalcTime) < 1000 / 3) return; + forcedRecalc = true; + forceRecalcTime = Time.millis(); + } + void update(long maxUpdateNs){ if(curId != lastId){ clear(true); @@ -520,9 +542,10 @@ public class ControlPathfinder{ lastId = curId; //re-do everything when world updates, but keep the old path around - if(Time.timeSinceMillis(lastTime) > 1000 * 3 && (worldUpdateId != lastWorldUpdate || !destination.epsilonEquals(lastDestination, 2f))){ + if(forcedRecalc || (Time.timeSinceMillis(lastTime) > 1000 * 3 && (worldUpdateId != lastWorldUpdate || !destination.epsilonEquals(lastDestination, 2f)))){ lastTime = Time.millis(); lastWorldUpdate = worldUpdateId; + forcedRecalc = false; clear(false); } diff --git a/core/src/mindustry/ai/Pathfinder.java b/core/src/mindustry/ai/Pathfinder.java index e5f862e14f..8aba767dda 100644 --- a/core/src/mindustry/ai/Pathfinder.java +++ b/core/src/mindustry/ai/Pathfinder.java @@ -6,7 +6,6 @@ import arc.math.geom.*; import arc.struct.*; import arc.util.*; import mindustry.annotations.Annotations.*; -import mindustry.content.*; import mindustry.core.*; import mindustry.game.EventType.*; import mindustry.game.*; @@ -153,7 +152,7 @@ public class Pathfinder implements Runnable{ /** Packs a tile into its internal representation. */ public int packTile(Tile tile){ - boolean nearLiquid = false, nearSolid = false, nearGround = false, solid = tile.solid(), allDeep = tile.floor().isDeep(); + boolean nearLiquid = false, nearSolid = false, nearLegSolid = false, nearGround = false, solid = tile.solid(), allDeep = tile.floor().isDeep(); for(int i = 0; i < 4; i++){ Tile other = tile.nearby(i); @@ -165,6 +164,7 @@ public class Pathfinder implements Runnable{ if(osolid && !other.block().teamPassable) nearSolid = true; if(!floor.isLiquid) nearGround = true; if(!floor.isDeep()) allDeep = false; + if(other.legSolid()) nearLegSolid = true; //other tile is now near solid if(solid && !tile.block().teamPassable){ @@ -180,10 +180,11 @@ public class Pathfinder implements Runnable{ tid == 0 && tile.build != null && state.rules.coreCapture ? 255 : tid, //use teamid = 255 when core capture is enabled to mark out derelict structures solid, tile.floor().isLiquid, - tile.staticDarkness() >= 2 || (tile.floor().solid && tile.block() == Blocks.air), + tile.legSolid(), nearLiquid, nearGround, nearSolid, + nearLegSolid, tile.floor().isDeep(), tile.floor().damageTaken > 0.00001f, allDeep, @@ -564,6 +565,8 @@ public class Pathfinder implements Runnable{ boolean nearGround; //whether this block is near a solid object boolean nearSolid; + //whether this block is near a block that is solid for legged units + boolean nearLegSolid; //whether this block is deep / drownable boolean deep; //whether the floor damages diff --git a/core/src/mindustry/entities/EntityCollisions.java b/core/src/mindustry/entities/EntityCollisions.java index 7343b7ff59..c3746c40cc 100644 --- a/core/src/mindustry/entities/EntityCollisions.java +++ b/core/src/mindustry/entities/EntityCollisions.java @@ -5,7 +5,6 @@ import arc.math.*; import arc.math.geom.*; import arc.struct.*; import arc.util.*; -import mindustry.content.*; import mindustry.gen.*; import mindustry.world.*; @@ -129,7 +128,7 @@ public class EntityCollisions{ public static boolean legsSolid(int x, int y){ Tile tile = world.tile(x, y); - return tile == null || tile.staticDarkness() >= 2 || (tile.floor().solid && tile.block() == Blocks.air); + return tile == null || tile.legSolid(); } public static boolean waterSolid(int x, int y){ diff --git a/core/src/mindustry/world/Tile.java b/core/src/mindustry/world/Tile.java index 1b459a7d9b..9c6cef6489 100644 --- a/core/src/mindustry/world/Tile.java +++ b/core/src/mindustry/world/Tile.java @@ -547,6 +547,11 @@ public class Tile implements Position, QuadTreeObject, Displayable{ return block.solid && block.fillsTile && !block.synthetic() ? data : 0; } + /** @return whether this tile is solid for legged units */ + public boolean legSolid(){ + return staticDarkness() >= 2 || (floor.solid && block == Blocks.air); + } + /** @return true if these tiles are right next to each other. */ public boolean adjacentTo(Tile tile){ return relativeTo(tile) != -1;