Remove pathfinding graph node directions (#1223)

Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
This commit is contained in:
metalgearsloth
2020-06-29 01:43:06 +10:00
committed by GitHub
parent 24831bf8a0
commit 29f1730d71
7 changed files with 462 additions and 372 deletions

View File

@@ -1,6 +1,9 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Content.Server.GameObjects.EntitySystems.Pathfinding;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
using Robust.Shared.Map;
@@ -19,7 +22,6 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
public static int ChunkSize => 16;
public PathfindingNode[,] Nodes => _nodes;
private PathfindingNode[,] _nodes = new PathfindingNode[ChunkSize,ChunkSize];
public Dictionary<Direction, PathfindingChunk> Neighbors { get; } = new Dictionary<Direction, PathfindingChunk>(8);
public PathfindingChunk(GridId gridId, MapIndices indices)
{
@@ -38,244 +40,31 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
CreateNode(tileRef);
}
}
RefreshNodeNeighbors();
}
/// <summary>
/// Updates all internal nodes with references to every other internal node
/// </summary>
private void RefreshNodeNeighbors()
public IEnumerable<PathfindingChunk> GetNeighbors()
{
for (var x = 0; x < ChunkSize; x++)
var pathfindingSystem = EntitySystem.Get<PathfindingSystem>();
var chunkGrid = pathfindingSystem.Graph[GridId];
for (var x = -1; x <= 1; x++)
{
for (var y = 0; y < ChunkSize; y++)
for (var y = -1; y <= 1; y++)
{
var node = _nodes[x, y];
// West
if (x != 0)
if (x == 0 && y == 0) continue;
var (neighborX, neighborY) = (_indices.X + ChunkSize * x, _indices.Y + ChunkSize * y);
if (chunkGrid.TryGetValue(new MapIndices(neighborX, neighborY), out var neighbor))
{
if (y != ChunkSize - 1)
{
node.AddNeighbor(Direction.NorthWest, _nodes[x - 1, y + 1]);
}
node.AddNeighbor(Direction.West, _nodes[x - 1, y]);
if (y != 0)
{
node.AddNeighbor(Direction.SouthWest, _nodes[x - 1, y - 1]);
}
}
// Same column
if (y != ChunkSize - 1)
{
node.AddNeighbor(Direction.North, _nodes[x, y + 1]);
}
if (y != 0)
{
node.AddNeighbor(Direction.South, _nodes[x, y - 1]);
}
// East
if (x != ChunkSize - 1)
{
if (y != ChunkSize - 1)
{
node.AddNeighbor(Direction.NorthEast, _nodes[x + 1, y + 1]);
}
node.AddNeighbor(Direction.East, _nodes[x + 1, y]);
if (y != 0)
{
node.AddNeighbor(Direction.SouthEast, _nodes[x + 1, y - 1]);
}
yield return neighbor;
}
}
}
}
/// <summary>
/// This will work both ways
/// </summary>
/// <param name="chunk"></param>
/// <exception cref="InvalidOperationException"></exception>
public void AddNeighbor(PathfindingChunk chunk)
public bool InBounds(MapIndices mapIndices)
{
if (chunk == this) return;
if (Neighbors.ContainsValue(chunk))
{
return;
}
Direction direction;
if (chunk.Indices.X < _indices.X)
{
if (chunk.Indices.Y > _indices.Y)
{
direction = Direction.NorthWest;
} else if (chunk.Indices.Y < _indices.Y)
{
direction = Direction.SouthWest;
}
else
{
direction = Direction.West;
}
}
else if (chunk.Indices.X > _indices.X)
{
if (chunk.Indices.Y > _indices.Y)
{
direction = Direction.NorthEast;
} else if (chunk.Indices.Y < _indices.Y)
{
direction = Direction.SouthEast;
}
else
{
direction = Direction.East;
}
}
else
{
if (chunk.Indices.Y > _indices.Y)
{
direction = Direction.North;
} else if (chunk.Indices.Y < _indices.Y)
{
direction = Direction.South;
}
else
{
throw new InvalidOperationException();
}
}
Neighbors.TryAdd(direction, chunk);
foreach (var node in GetBorderNodes(direction))
{
foreach (var counter in chunk.GetCounterpartNodes(direction))
{
var xDiff = node.TileRef.X - counter.TileRef.X;
var yDiff = node.TileRef.Y - counter.TileRef.Y;
if (Math.Abs(xDiff) <= 1 && Math.Abs(yDiff) <= 1)
{
node.AddNeighbor(counter);
counter.AddNeighbor(node);
}
}
}
chunk.Neighbors.TryAdd(OppositeDirection(direction), this);
if (Neighbors.Count > 8)
{
throw new InvalidOperationException();
}
}
private Direction OppositeDirection(Direction direction)
{
return (Direction) (((int) direction + 4) % 8);
}
// TODO I was too tired to think of an easier system. Could probably just google an array wraparound
private IEnumerable<PathfindingNode> GetCounterpartNodes(Direction direction)
{
switch (direction)
{
case Direction.West:
for (var i = 0; i < ChunkSize; i++)
{
yield return _nodes[ChunkSize - 1, i];
}
break;
case Direction.SouthWest:
yield return _nodes[ChunkSize - 1, ChunkSize - 1];
break;
case Direction.South:
for (var i = 0; i < ChunkSize; i++)
{
yield return _nodes[i, ChunkSize - 1];
}
break;
case Direction.SouthEast:
yield return _nodes[0, ChunkSize - 1];
break;
case Direction.East:
for (var i = 0; i < ChunkSize; i++)
{
yield return _nodes[0, i];
}
break;
case Direction.NorthEast:
yield return _nodes[0, 0];
break;
case Direction.North:
for (var i = 0; i < ChunkSize; i++)
{
yield return _nodes[i, 0];
}
break;
case Direction.NorthWest:
yield return _nodes[ChunkSize - 1, 0];
break;
default:
throw new ArgumentOutOfRangeException(nameof(direction), direction, null);
}
}
public IEnumerable<PathfindingNode> GetBorderNodes(Direction direction)
{
switch (direction)
{
case Direction.East:
for (var i = 0; i < ChunkSize; i++)
{
yield return _nodes[ChunkSize - 1, i];
}
break;
case Direction.NorthEast:
yield return _nodes[ChunkSize - 1, ChunkSize - 1];
break;
case Direction.North:
for (var i = 0; i < ChunkSize; i++)
{
yield return _nodes[i, ChunkSize - 1];
}
break;
case Direction.NorthWest:
yield return _nodes[0, ChunkSize - 1];
break;
case Direction.West:
for (var i = 0; i < ChunkSize; i++)
{
yield return _nodes[0, i];
}
break;
case Direction.SouthWest:
yield return _nodes[0, 0];
break;
case Direction.South:
for (var i = 0; i < ChunkSize; i++)
{
yield return _nodes[i, 0];
}
break;
case Direction.SouthEast:
yield return _nodes[ChunkSize - 1, 0];
break;
default:
throw new ArgumentOutOfRangeException(nameof(direction), direction, null);
}
}
public bool InBounds(TileRef tile)
{
if (tile.X < _indices.X || tile.Y < _indices.Y) return false;
if (tile.X >= _indices.X + ChunkSize || tile.Y >= _indices.Y + ChunkSize) return false;
if (mapIndices.X < _indices.X || mapIndices.Y < _indices.Y) return false;
if (mapIndices.X >= _indices.X + ChunkSize || mapIndices.Y >= _indices.Y + ChunkSize) return false;
return true;
}
@@ -293,6 +82,73 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
return false;
}
/// <summary>
/// Gets our neighbors that are relevant for the node to retrieve its own neighbors
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
public IEnumerable<PathfindingChunk> RelevantChunks(PathfindingNode node)
{
var relevantDirections = GetEdges(node).ToList();
foreach (var chunk in GetNeighbors())
{
var chunkDirection = PathfindingHelpers.RelativeDirection(chunk, this);
if (relevantDirections.Contains(chunkDirection))
{
yield return chunk;
}
}
}
private IEnumerable<Direction> GetEdges(PathfindingNode node)
{
// West Edge
if (node.TileRef.X == _indices.X)
{
yield return Direction.West;
if (node.TileRef.Y == _indices.Y)
{
yield return Direction.SouthWest;
yield return Direction.South;
} else if (node.TileRef.Y == _indices.Y + ChunkSize - 1)
{
yield return Direction.NorthWest;
yield return Direction.North;
}
yield break;
}
// East edge
if (node.TileRef.X == _indices.X + ChunkSize - 1)
{
yield return Direction.East;
if (node.TileRef.Y == _indices.Y)
{
yield return Direction.SouthEast;
yield return Direction.South;
} else if (node.TileRef.Y == _indices.Y + ChunkSize - 1)
{
yield return Direction.NorthEast;
yield return Direction.North;
}
yield break;
}
// South edge
if (node.TileRef.Y == _indices.Y)
{
yield return Direction.South;
// Given we already checked south-west and south-east above shouldn't need any more
}
// North edge
if (node.TileRef.Y == _indices.Y + ChunkSize - 1)
{
yield return Direction.North;
}
}
public PathfindingNode GetNode(TileRef tile)
{
var chunkX = tile.X - _indices.X;