VGRoid support (#27659)

* Dungeon spawn support for grid spawns

* Recursive dungeons working

* Mask approach working

* zack

* More work

* Fix recursive dungeons

* Heap of work

* weh

* the cud

* rar

* Job

* weh

* weh

* weh

* Master merges

* orch

* weh

* vgroid most of the work

* Tweaks

* Tweaks

* weh

* do do do do do do

* Basic layout

* Ore spawning working

* Big breaking changes

* Mob gen working

* weh

* Finalising

* emo

* More finalising

* reverty

* Reduce distance
This commit is contained in:
metalgearsloth
2024-07-03 22:23:11 +10:00
committed by GitHub
parent 1faa1b5df6
commit a2f99cc69e
103 changed files with 4928 additions and 2627 deletions

View File

@@ -0,0 +1,123 @@
namespace Content.Server.NPC.Pathfinding;
public sealed partial class PathfindingSystem
{
/*
* Handle BFS searches from Start->End. Doesn't consider NPC pathfinding.
*/
/// <summary>
/// Pathfinding args for a 1-many path.
/// </summary>
public record struct BreadthPathArgs()
{
public Vector2i Start;
public List<Vector2i> Ends;
public bool Diagonals = false;
public Func<Vector2i, float>? TileCost;
public int Limit = 10000;
}
/// <summary>
/// Gets a BFS path from start to any end. Can also supply an optional tile-cost for tiles.
/// </summary>
public SimplePathResult GetBreadthPath(BreadthPathArgs args)
{
var cameFrom = new Dictionary<Vector2i, Vector2i>();
var costSoFar = new Dictionary<Vector2i, float>();
var frontier = new PriorityQueue<Vector2i, float>();
costSoFar[args.Start] = 0f;
frontier.Enqueue(args.Start, 0f);
var count = 0;
while (frontier.TryDequeue(out var node, out _) && count < args.Limit)
{
count++;
if (args.Ends.Contains(node))
{
// Found target
var path = ReconstructPath(node, cameFrom);
return new SimplePathResult()
{
CameFrom = cameFrom,
Path = path,
};
}
var gCost = costSoFar[node];
if (args.Diagonals)
{
for (var x = -1; x <= 1; x++)
{
for (var y = -1; y <= 1; y++)
{
var neighbor = node + new Vector2i(x, y);
var neighborCost = OctileDistance(node, neighbor) * args.TileCost?.Invoke(neighbor) ?? 1f;
if (neighborCost.Equals(0f))
{
continue;
}
// f = g + h
// gScore is distance to the start node
// hScore is distance to the end node
var gScore = gCost + neighborCost;
// Slower to get here so just ignore it.
if (costSoFar.TryGetValue(neighbor, out var nextValue) && gScore >= nextValue)
{
continue;
}
cameFrom[neighbor] = node;
costSoFar[neighbor] = gScore;
// pFactor is tie-breaker where the fscore is otherwise equal.
// See http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html#breaking-ties
// There's other ways to do it but future consideration
// The closer the fScore is to the actual distance then the better the pathfinder will be
// (i.e. somewhere between 1 and infinite)
// Can use hierarchical pathfinder or whatever to improve the heuristic but this is fine for now.
frontier.Enqueue(neighbor, gScore);
}
}
}
else
{
for (var x = -1; x <= 1; x++)
{
for (var y = -1; y <= 1; y++)
{
if (x != 0 && y != 0)
continue;
var neighbor = node + new Vector2i(x, y);
var neighborCost = ManhattanDistance(node, neighbor) * args.TileCost?.Invoke(neighbor) ?? 1f;
if (neighborCost.Equals(0f))
continue;
var gScore = gCost + neighborCost;
if (costSoFar.TryGetValue(neighbor, out var nextValue) && gScore >= nextValue)
continue;
cameFrom[neighbor] = node;
costSoFar[neighbor] = gScore;
frontier.Enqueue(neighbor, gScore);
}
}
}
}
return SimplePathResult.NoPath;
}
}