Remove pathfinding graph node directions (#1223)
Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
This commit is contained in:
@@ -0,0 +1,65 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Content.Server.GameObjects.EntitySystems.AI.Pathfinding;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using Robust.Shared.GameObjects.Systems;
|
||||||
|
using Robust.Shared.Interfaces.Map;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
|
||||||
|
namespace Content.IntegrationTests.Tests.Pathfinding
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
[TestOf(typeof(PathfindingChunk))]
|
||||||
|
public class PathfindingChunkTest : ContentIntegrationTest
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public async Task Test()
|
||||||
|
{
|
||||||
|
var server = StartServerDummyTicker();
|
||||||
|
|
||||||
|
server.Assert(() =>
|
||||||
|
{
|
||||||
|
var pathfindingSystem = EntitySystem.Get<PathfindingSystem>();
|
||||||
|
var mapMan = IoCManager.Resolve<IMapManager>();
|
||||||
|
|
||||||
|
// Setup
|
||||||
|
var mapId = mapMan.CreateMap(new MapId(1));
|
||||||
|
var gridId = new GridId(2);
|
||||||
|
mapMan.CreateGrid(mapId, gridId);
|
||||||
|
var chunkTile = mapMan.GetGrid(gridId).GetTileRef(new MapIndices(0, 0));
|
||||||
|
var chunk = pathfindingSystem.GetChunk(chunkTile);
|
||||||
|
Assert.That(chunk.Nodes.Length == PathfindingChunk.ChunkSize * PathfindingChunk.ChunkSize);
|
||||||
|
|
||||||
|
// Neighbors
|
||||||
|
var chunkNeighbors = chunk.GetNeighbors().ToList();
|
||||||
|
Assert.That(chunkNeighbors.Count == 0);
|
||||||
|
var neighborChunkTile = mapMan.GetGrid(gridId).GetTileRef(new MapIndices(PathfindingChunk.ChunkSize, PathfindingChunk.ChunkSize));
|
||||||
|
var neighborChunk = pathfindingSystem.GetChunk(neighborChunkTile);
|
||||||
|
chunkNeighbors = chunk.GetNeighbors().ToList();
|
||||||
|
Assert.That(chunkNeighbors.Count == 1);
|
||||||
|
|
||||||
|
// Directions
|
||||||
|
Assert.That(PathfindingHelpers.RelativeDirection(neighborChunk, chunk) == Direction.NorthEast);
|
||||||
|
Assert.That(PathfindingHelpers.RelativeDirection(chunk.Nodes[0, 1], chunk.Nodes[0, 0]) == Direction.North);
|
||||||
|
|
||||||
|
// Nodes
|
||||||
|
var node = chunk.Nodes[1, 1];
|
||||||
|
var nodeNeighbors = node.GetNeighbors().ToList();
|
||||||
|
Assert.That(nodeNeighbors.Count == 8);
|
||||||
|
|
||||||
|
// Bottom-left corner with no chunk neighbor
|
||||||
|
node = chunk.Nodes[0, 0];
|
||||||
|
nodeNeighbors = node.GetNeighbors().ToList();
|
||||||
|
Assert.That(nodeNeighbors.Count == 3);
|
||||||
|
|
||||||
|
// Given we have 1 NE neighbor then NE corner should have 4 neighbors due to the 1 extra from the neighbor chunk
|
||||||
|
node = chunk.Nodes[PathfindingChunk.ChunkSize - 1, PathfindingChunk.ChunkSize - 1];
|
||||||
|
nodeNeighbors = node.GetNeighbors().ToList();
|
||||||
|
Assert.That(nodeNeighbors.Count == 4);
|
||||||
|
});
|
||||||
|
await server.WaitIdleAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -80,7 +80,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders
|
|||||||
|
|
||||||
closedTiles.Add(currentNode);
|
closedTiles.Add(currentNode);
|
||||||
|
|
||||||
foreach (var (direction, nextNode) in currentNode.Neighbors)
|
foreach (var nextNode in currentNode.GetNeighbors())
|
||||||
{
|
{
|
||||||
if (closedTiles.Contains(nextNode))
|
if (closedTiles.Contains(nextNode))
|
||||||
{
|
{
|
||||||
@@ -89,6 +89,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders
|
|||||||
|
|
||||||
// If tile is untraversable it'll be null
|
// If tile is untraversable it'll be null
|
||||||
var tileCost = PathfindingHelpers.GetTileCost(_pathfindingArgs, currentNode, nextNode);
|
var tileCost = PathfindingHelpers.GetTileCost(_pathfindingArgs, currentNode, nextNode);
|
||||||
|
var direction = PathfindingHelpers.RelativeDirection(nextNode, currentNode);
|
||||||
|
|
||||||
if (tileCost == null || !PathfindingHelpers.DirectionTraversable(_pathfindingArgs.CollisionMask, _pathfindingArgs.Access, currentNode, direction))
|
if (tileCost == null || !PathfindingHelpers.DirectionTraversable(_pathfindingArgs.CollisionMask, _pathfindingArgs.Access, currentNode, direction))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Server.GameObjects.EntitySystems.JobQueues;
|
using Content.Server.GameObjects.EntitySystems.JobQueues;
|
||||||
@@ -14,6 +15,8 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders
|
|||||||
{
|
{
|
||||||
public class JpsPathfindingJob : Job<Queue<TileRef>>
|
public class JpsPathfindingJob : Job<Queue<TileRef>>
|
||||||
{
|
{
|
||||||
|
// Some of this is probably fugly due to other structural changes in pathfinding so it could do with optimisation
|
||||||
|
// Realistically it's probably not getting used given it doesn't support tile costs which can be very useful
|
||||||
public static event Action<SharedAiDebug.JpsRouteDebug> DebugRoute;
|
public static event Action<SharedAiDebug.JpsRouteDebug> DebugRoute;
|
||||||
|
|
||||||
private PathfindingNode _startNode;
|
private PathfindingNode _startNode;
|
||||||
@@ -78,8 +81,9 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var (direction, _) in currentNode.Neighbors)
|
foreach (var node in currentNode.GetNeighbors())
|
||||||
{
|
{
|
||||||
|
var direction = PathfindingHelpers.RelativeDirection(node, currentNode);
|
||||||
var jumpNode = GetJumpPoint(currentNode, direction, _endNode);
|
var jumpNode = GetJumpPoint(currentNode, direction, _endNode);
|
||||||
|
|
||||||
if (jumpNode != null && !closedTiles.Contains(jumpNode))
|
if (jumpNode != null && !closedTiles.Contains(jumpNode))
|
||||||
@@ -156,7 +160,15 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders
|
|||||||
while (count < 1000)
|
while (count < 1000)
|
||||||
{
|
{
|
||||||
count++;
|
count++;
|
||||||
var nextNode = currentNode.GetNeighbor(direction);
|
PathfindingNode nextNode = null;
|
||||||
|
foreach (var node in currentNode.GetNeighbors())
|
||||||
|
{
|
||||||
|
if (PathfindingHelpers.RelativeDirection(node, currentNode) == direction)
|
||||||
|
{
|
||||||
|
nextNode = node;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We'll do opposite DirectionTraversable just because of how the method's setup
|
// We'll do opposite DirectionTraversable just because of how the method's setup
|
||||||
// Nodes should be 2-way anyway.
|
// Nodes should be 2-way anyway.
|
||||||
@@ -270,43 +282,99 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders
|
|||||||
private bool IsDiagonalJumpPoint(Direction direction, PathfindingNode currentNode)
|
private bool IsDiagonalJumpPoint(Direction direction, PathfindingNode currentNode)
|
||||||
{
|
{
|
||||||
// If we're going diagonally need to check all cardinals.
|
// If we're going diagonally need to check all cardinals.
|
||||||
// I just just using casts int casts and offset to make it smaller but brain no workyand it wasn't working.
|
// I tried just casting direction ints and offsets to make it smaller but brain no worky.
|
||||||
// From NorthEast we check (Closed / Open) S - SE, W - NW
|
// From NorthEast we check (Closed / Open) S - SE, W - NW
|
||||||
|
|
||||||
PathfindingNode openNeighborOne;
|
PathfindingNode openNeighborOne = null;
|
||||||
PathfindingNode closedNeighborOne;
|
PathfindingNode closedNeighborOne = null;
|
||||||
PathfindingNode openNeighborTwo;
|
PathfindingNode openNeighborTwo = null;
|
||||||
PathfindingNode closedNeighborTwo;
|
PathfindingNode closedNeighborTwo = null;
|
||||||
|
|
||||||
switch (direction)
|
switch (direction)
|
||||||
{
|
{
|
||||||
case Direction.NorthEast:
|
case Direction.NorthEast:
|
||||||
openNeighborOne = currentNode.GetNeighbor(Direction.SouthEast);
|
foreach (var neighbor in currentNode.GetNeighbors())
|
||||||
closedNeighborOne = currentNode.GetNeighbor(Direction.South);
|
{
|
||||||
|
var neighborDirection = PathfindingHelpers.RelativeDirection(neighbor, currentNode);
|
||||||
openNeighborTwo = currentNode.GetNeighbor(Direction.NorthWest);
|
switch (neighborDirection)
|
||||||
closedNeighborTwo = currentNode.GetNeighbor(Direction.West);
|
{
|
||||||
|
case Direction.SouthEast:
|
||||||
|
openNeighborOne = neighbor;
|
||||||
|
break;
|
||||||
|
case Direction.South:
|
||||||
|
closedNeighborOne = neighbor;
|
||||||
|
break;
|
||||||
|
case Direction.NorthWest:
|
||||||
|
openNeighborTwo = neighbor;
|
||||||
|
break;
|
||||||
|
case Direction.West:
|
||||||
|
closedNeighborTwo = neighbor;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Direction.SouthEast:
|
case Direction.SouthEast:
|
||||||
openNeighborOne = currentNode.GetNeighbor(Direction.NorthEast);
|
foreach (var neighbor in currentNode.GetNeighbors())
|
||||||
closedNeighborOne = currentNode.GetNeighbor(Direction.North);
|
{
|
||||||
|
var neighborDirection = PathfindingHelpers.RelativeDirection(neighbor, currentNode);
|
||||||
openNeighborTwo = currentNode.GetNeighbor(Direction.SouthWest);
|
switch (neighborDirection)
|
||||||
closedNeighborTwo = currentNode.GetNeighbor(Direction.West);
|
{
|
||||||
|
case Direction.NorthEast:
|
||||||
|
openNeighborOne = neighbor;
|
||||||
|
break;
|
||||||
|
case Direction.North:
|
||||||
|
closedNeighborOne = neighbor;
|
||||||
|
break;
|
||||||
|
case Direction.SouthWest:
|
||||||
|
openNeighborTwo = neighbor;
|
||||||
|
break;
|
||||||
|
case Direction.West:
|
||||||
|
closedNeighborTwo = neighbor;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Direction.SouthWest:
|
case Direction.SouthWest:
|
||||||
openNeighborOne = currentNode.GetNeighbor(Direction.NorthWest);
|
foreach (var neighbor in currentNode.GetNeighbors())
|
||||||
closedNeighborOne = currentNode.GetNeighbor(Direction.North);
|
{
|
||||||
|
var neighborDirection = PathfindingHelpers.RelativeDirection(neighbor, currentNode);
|
||||||
openNeighborTwo = currentNode.GetNeighbor(Direction.SouthEast);
|
switch (neighborDirection)
|
||||||
closedNeighborTwo = currentNode.GetNeighbor(Direction.East);
|
{
|
||||||
|
case Direction.NorthWest:
|
||||||
|
openNeighborOne = neighbor;
|
||||||
|
break;
|
||||||
|
case Direction.North:
|
||||||
|
closedNeighborOne = neighbor;
|
||||||
|
break;
|
||||||
|
case Direction.SouthEast:
|
||||||
|
openNeighborTwo = neighbor;
|
||||||
|
break;
|
||||||
|
case Direction.East:
|
||||||
|
closedNeighborTwo = neighbor;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Direction.NorthWest:
|
case Direction.NorthWest:
|
||||||
openNeighborOne = currentNode.GetNeighbor(Direction.SouthWest);
|
foreach (var neighbor in currentNode.GetNeighbors())
|
||||||
closedNeighborOne = currentNode.GetNeighbor(Direction.South);
|
{
|
||||||
|
var neighborDirection = PathfindingHelpers.RelativeDirection(neighbor, currentNode);
|
||||||
openNeighborTwo = currentNode.GetNeighbor(Direction.NorthEast);
|
switch (neighborDirection)
|
||||||
closedNeighborTwo = currentNode.GetNeighbor(Direction.East);
|
{
|
||||||
|
case Direction.SouthWest:
|
||||||
|
openNeighborOne = neighbor;
|
||||||
|
break;
|
||||||
|
case Direction.South:
|
||||||
|
closedNeighborOne = neighbor;
|
||||||
|
break;
|
||||||
|
case Direction.NorthEast:
|
||||||
|
openNeighborTwo = neighbor;
|
||||||
|
break;
|
||||||
|
case Direction.East:
|
||||||
|
closedNeighborTwo = neighbor;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException();
|
throw new ArgumentOutOfRangeException();
|
||||||
@@ -332,40 +400,96 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private bool IsCardinalJumpPoint(Direction direction, PathfindingNode currentNode)
|
private bool IsCardinalJumpPoint(Direction direction, PathfindingNode currentNode)
|
||||||
{
|
{
|
||||||
PathfindingNode openNeighborOne;
|
PathfindingNode openNeighborOne = null;
|
||||||
PathfindingNode closedNeighborOne;
|
PathfindingNode closedNeighborOne = null;
|
||||||
PathfindingNode openNeighborTwo;
|
PathfindingNode openNeighborTwo = null;
|
||||||
PathfindingNode closedNeighborTwo;
|
PathfindingNode closedNeighborTwo = null;
|
||||||
|
|
||||||
switch (direction)
|
switch (direction)
|
||||||
{
|
{
|
||||||
case Direction.North:
|
case Direction.North:
|
||||||
openNeighborOne = currentNode.GetNeighbor(Direction.NorthEast);
|
foreach (var neighbor in currentNode.GetNeighbors())
|
||||||
closedNeighborOne = currentNode.GetNeighbor(Direction.East);
|
{
|
||||||
|
var neighborDirection = PathfindingHelpers.RelativeDirection(neighbor, currentNode);
|
||||||
openNeighborTwo = currentNode.GetNeighbor(Direction.NorthWest);
|
switch (neighborDirection)
|
||||||
closedNeighborTwo = currentNode.GetNeighbor(Direction.West);
|
{
|
||||||
|
case Direction.NorthEast:
|
||||||
|
openNeighborOne = neighbor;
|
||||||
|
break;
|
||||||
|
case Direction.East:
|
||||||
|
closedNeighborOne = neighbor;
|
||||||
|
break;
|
||||||
|
case Direction.NorthWest:
|
||||||
|
openNeighborTwo = neighbor;
|
||||||
|
break;
|
||||||
|
case Direction.West:
|
||||||
|
closedNeighborTwo = neighbor;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Direction.East:
|
case Direction.East:
|
||||||
openNeighborOne = currentNode.GetNeighbor(Direction.NorthEast);
|
foreach (var neighbor in currentNode.GetNeighbors())
|
||||||
closedNeighborOne = currentNode.GetNeighbor(Direction.North);
|
{
|
||||||
|
var neighborDirection = PathfindingHelpers.RelativeDirection(neighbor, currentNode);
|
||||||
openNeighborTwo = currentNode.GetNeighbor(Direction.SouthEast);
|
switch (neighborDirection)
|
||||||
closedNeighborTwo = currentNode.GetNeighbor(Direction.South);
|
{
|
||||||
|
case Direction.NorthEast:
|
||||||
|
openNeighborOne = neighbor;
|
||||||
|
break;
|
||||||
|
case Direction.North:
|
||||||
|
closedNeighborOne = neighbor;
|
||||||
|
break;
|
||||||
|
case Direction.SouthEast:
|
||||||
|
openNeighborTwo = neighbor;
|
||||||
|
break;
|
||||||
|
case Direction.South:
|
||||||
|
closedNeighborTwo = neighbor;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Direction.South:
|
case Direction.South:
|
||||||
openNeighborOne = currentNode.GetNeighbor(Direction.SouthEast);
|
foreach (var neighbor in currentNode.GetNeighbors())
|
||||||
closedNeighborOne = currentNode.GetNeighbor(Direction.East);
|
{
|
||||||
|
var neighborDirection = PathfindingHelpers.RelativeDirection(neighbor, currentNode);
|
||||||
openNeighborTwo = currentNode.GetNeighbor(Direction.SouthWest);
|
switch (neighborDirection)
|
||||||
closedNeighborTwo = currentNode.GetNeighbor(Direction.West);
|
{
|
||||||
|
case Direction.SouthEast:
|
||||||
|
openNeighborOne = neighbor;
|
||||||
|
break;
|
||||||
|
case Direction.East:
|
||||||
|
closedNeighborOne = neighbor;
|
||||||
|
break;
|
||||||
|
case Direction.SouthWest:
|
||||||
|
openNeighborTwo = neighbor;
|
||||||
|
break;
|
||||||
|
case Direction.West:
|
||||||
|
closedNeighborTwo = neighbor;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Direction.West:
|
case Direction.West:
|
||||||
openNeighborOne = currentNode.GetNeighbor(Direction.NorthWest);
|
foreach (var neighbor in currentNode.GetNeighbors())
|
||||||
closedNeighborOne = currentNode.GetNeighbor(Direction.North);
|
{
|
||||||
|
var neighborDirection = PathfindingHelpers.RelativeDirection(neighbor, currentNode);
|
||||||
openNeighborTwo = currentNode.GetNeighbor(Direction.SouthWest);
|
switch (neighborDirection)
|
||||||
closedNeighborTwo = currentNode.GetNeighbor(Direction.South);
|
{
|
||||||
|
case Direction.NorthWest:
|
||||||
|
openNeighborOne = neighbor;
|
||||||
|
break;
|
||||||
|
case Direction.North:
|
||||||
|
closedNeighborOne = neighbor;
|
||||||
|
break;
|
||||||
|
case Direction.SouthWest:
|
||||||
|
openNeighborTwo = neighbor;
|
||||||
|
break;
|
||||||
|
case Direction.South:
|
||||||
|
closedNeighborTwo = neighbor;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException();
|
throw new ArgumentOutOfRangeException();
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using Content.Server.GameObjects.EntitySystems.Pathfinding;
|
using Content.Server.GameObjects.EntitySystems.Pathfinding;
|
||||||
|
using Robust.Shared.GameObjects.Systems;
|
||||||
using Robust.Shared.Interfaces.Map;
|
using Robust.Shared.Interfaces.Map;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
@@ -19,7 +22,6 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
|
|||||||
public static int ChunkSize => 16;
|
public static int ChunkSize => 16;
|
||||||
public PathfindingNode[,] Nodes => _nodes;
|
public PathfindingNode[,] Nodes => _nodes;
|
||||||
private PathfindingNode[,] _nodes = new PathfindingNode[ChunkSize,ChunkSize];
|
private PathfindingNode[,] _nodes = new PathfindingNode[ChunkSize,ChunkSize];
|
||||||
public Dictionary<Direction, PathfindingChunk> Neighbors { get; } = new Dictionary<Direction, PathfindingChunk>(8);
|
|
||||||
|
|
||||||
public PathfindingChunk(GridId gridId, MapIndices indices)
|
public PathfindingChunk(GridId gridId, MapIndices indices)
|
||||||
{
|
{
|
||||||
@@ -38,244 +40,31 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
|
|||||||
CreateNode(tileRef);
|
CreateNode(tileRef);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RefreshNodeNeighbors();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public IEnumerable<PathfindingChunk> GetNeighbors()
|
||||||
/// Updates all internal nodes with references to every other internal node
|
|
||||||
/// </summary>
|
|
||||||
private void RefreshNodeNeighbors()
|
|
||||||
{
|
{
|
||||||
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];
|
if (x == 0 && y == 0) continue;
|
||||||
// West
|
var (neighborX, neighborY) = (_indices.X + ChunkSize * x, _indices.Y + ChunkSize * y);
|
||||||
if (x != 0)
|
if (chunkGrid.TryGetValue(new MapIndices(neighborX, neighborY), out var neighbor))
|
||||||
{
|
{
|
||||||
if (y != ChunkSize - 1)
|
yield return neighbor;
|
||||||
{
|
|
||||||
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]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public bool InBounds(MapIndices mapIndices)
|
||||||
/// This will work both ways
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="chunk"></param>
|
|
||||||
/// <exception cref="InvalidOperationException"></exception>
|
|
||||||
public void AddNeighbor(PathfindingChunk chunk)
|
|
||||||
{
|
{
|
||||||
if (chunk == this) return;
|
if (mapIndices.X < _indices.X || mapIndices.Y < _indices.Y) return false;
|
||||||
if (Neighbors.ContainsValue(chunk))
|
if (mapIndices.X >= _indices.X + ChunkSize || mapIndices.Y >= _indices.Y + ChunkSize) return false;
|
||||||
{
|
|
||||||
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;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,6 +82,73 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
|
|||||||
return false;
|
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)
|
public PathfindingNode GetNode(TileRef tile)
|
||||||
{
|
{
|
||||||
var chunkX = tile.X - _indices.X;
|
var chunkX = tile.X - _indices.X;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Content.Server.GameObjects.Components.Access;
|
|
||||||
using Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders;
|
using Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders;
|
||||||
using Content.Server.GameObjects.EntitySystems.Pathfinding;
|
using Content.Server.GameObjects.EntitySystems.Pathfinding;
|
||||||
using Robust.Shared.Interfaces.GameObjects;
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
@@ -21,7 +20,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
|
|||||||
{
|
{
|
||||||
// TODO: Should make this account for proximities,
|
// TODO: Should make this account for proximities,
|
||||||
// probably some kind of breadth-first search to find a valid one
|
// probably some kind of breadth-first search to find a valid one
|
||||||
foreach (var (_, node) in endNode.Neighbors)
|
foreach (var node in endNode.GetNeighbors())
|
||||||
{
|
{
|
||||||
if (Traversable(pathfindingArgs.CollisionMask, pathfindingArgs.Access, node))
|
if (Traversable(pathfindingArgs.CollisionMask, pathfindingArgs.Access, node))
|
||||||
{
|
{
|
||||||
@@ -43,10 +42,40 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
|
|||||||
// Given there's different collision layers stored for each node in the graph it's probably not worth it to cache this
|
// Given there's different collision layers stored for each node in the graph it's probably not worth it to cache this
|
||||||
// Also this will help with corner-cutting
|
// Also this will help with corner-cutting
|
||||||
|
|
||||||
currentNode.Neighbors.TryGetValue(Direction.North, out var northNeighbor);
|
PathfindingNode northNeighbor = null;
|
||||||
currentNode.Neighbors.TryGetValue(Direction.South, out var southNeighbor);
|
PathfindingNode southNeighbor = null;
|
||||||
currentNode.Neighbors.TryGetValue(Direction.East, out var eastNeighbor);
|
PathfindingNode eastNeighbor = null;
|
||||||
currentNode.Neighbors.TryGetValue(Direction.West, out var westNeighbor);
|
PathfindingNode westNeighbor = null;
|
||||||
|
foreach (var neighbor in currentNode.GetNeighbors())
|
||||||
|
{
|
||||||
|
if (neighbor.TileRef.X == currentNode.TileRef.X &&
|
||||||
|
neighbor.TileRef.Y == currentNode.TileRef.Y + 1)
|
||||||
|
{
|
||||||
|
northNeighbor = neighbor;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (neighbor.TileRef.X == currentNode.TileRef.X + 1 &&
|
||||||
|
neighbor.TileRef.Y == currentNode.TileRef.Y)
|
||||||
|
{
|
||||||
|
eastNeighbor = neighbor;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (neighbor.TileRef.X == currentNode.TileRef.X &&
|
||||||
|
neighbor.TileRef.Y == currentNode.TileRef.Y - 1)
|
||||||
|
{
|
||||||
|
southNeighbor = neighbor;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (neighbor.TileRef.X == currentNode.TileRef.X - 1 &&
|
||||||
|
neighbor.TileRef.Y == currentNode.TileRef.Y)
|
||||||
|
{
|
||||||
|
westNeighbor = neighbor;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (direction)
|
switch (direction)
|
||||||
{
|
{
|
||||||
@@ -255,5 +284,66 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
|
|||||||
|
|
||||||
return cost;
|
return cost;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Direction RelativeDirection(PathfindingChunk endChunk, PathfindingChunk startChunk)
|
||||||
|
{
|
||||||
|
var xDiff = (endChunk.Indices.X - startChunk.Indices.X) / PathfindingChunk.ChunkSize;
|
||||||
|
var yDiff = (endChunk.Indices.Y - startChunk.Indices.Y) / PathfindingChunk.ChunkSize;
|
||||||
|
|
||||||
|
return RelativeDirection(xDiff, yDiff);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Direction RelativeDirection(PathfindingNode endNode, PathfindingNode startNode)
|
||||||
|
{
|
||||||
|
var xDiff = endNode.TileRef.X - startNode.TileRef.X;
|
||||||
|
var yDiff = endNode.TileRef.Y - startNode.TileRef.Y;
|
||||||
|
|
||||||
|
return RelativeDirection(xDiff, yDiff);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Direction RelativeDirection(int x, int y)
|
||||||
|
{
|
||||||
|
switch (x)
|
||||||
|
{
|
||||||
|
case -1:
|
||||||
|
switch (y)
|
||||||
|
{
|
||||||
|
case -1:
|
||||||
|
return Direction.SouthWest;
|
||||||
|
case 0:
|
||||||
|
return Direction.West;
|
||||||
|
case 1:
|
||||||
|
return Direction.NorthWest;
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
case 0:
|
||||||
|
switch (y)
|
||||||
|
{
|
||||||
|
case -1:
|
||||||
|
return Direction.South;
|
||||||
|
case 0:
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
case 1:
|
||||||
|
return Direction.North;
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
switch (y)
|
||||||
|
{
|
||||||
|
case -1:
|
||||||
|
return Direction.SouthEast;
|
||||||
|
case 0:
|
||||||
|
return Direction.East;
|
||||||
|
case 1:
|
||||||
|
return Direction.NorthEast;
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Content.Server.GameObjects.Components.Access;
|
using Content.Server.GameObjects.Components.Access;
|
||||||
using Content.Server.GameObjects.Components.Doors;
|
using Content.Server.GameObjects.Components.Doors;
|
||||||
using Content.Server.GameObjects.EntitySystems.AI.Pathfinding;
|
using Content.Server.GameObjects.EntitySystems.AI.Pathfinding;
|
||||||
@@ -9,6 +10,7 @@ using Robust.Shared.GameObjects.Components;
|
|||||||
using Robust.Shared.Interfaces.GameObjects;
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Maths;
|
using Robust.Shared.Maths;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.EntitySystems.Pathfinding
|
namespace Content.Server.GameObjects.EntitySystems.Pathfinding
|
||||||
{
|
{
|
||||||
@@ -17,9 +19,6 @@ namespace Content.Server.GameObjects.EntitySystems.Pathfinding
|
|||||||
public PathfindingChunk ParentChunk => _parentChunk;
|
public PathfindingChunk ParentChunk => _parentChunk;
|
||||||
private readonly PathfindingChunk _parentChunk;
|
private readonly PathfindingChunk _parentChunk;
|
||||||
|
|
||||||
public Dictionary<Direction, PathfindingNode> Neighbors => _neighbors;
|
|
||||||
private Dictionary<Direction, PathfindingNode> _neighbors = new Dictionary<Direction, PathfindingNode>();
|
|
||||||
|
|
||||||
public TileRef TileRef { get; private set; }
|
public TileRef TileRef { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -45,72 +44,48 @@ namespace Content.Server.GameObjects.EntitySystems.Pathfinding
|
|||||||
GenerateMask();
|
GenerateMask();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddNeighbor(Direction direction, PathfindingNode node)
|
/// <summary>
|
||||||
|
/// Return our neighboring nodes (even across chunks)
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public IEnumerable<PathfindingNode> GetNeighbors()
|
||||||
{
|
{
|
||||||
_neighbors.Add(direction, node);
|
List<PathfindingChunk> neighborChunks = null;
|
||||||
}
|
if (ParentChunk.OnEdge(this))
|
||||||
|
|
||||||
public void AddNeighbor(PathfindingNode node)
|
|
||||||
{
|
|
||||||
if (node.TileRef.GridIndex != TileRef.GridIndex)
|
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException();
|
neighborChunks = ParentChunk.RelevantChunks(this).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
Direction direction;
|
for (var x = -1; x <= 1; x++)
|
||||||
if (node.TileRef.X < TileRef.X)
|
|
||||||
{
|
{
|
||||||
if (node.TileRef.Y > TileRef.Y)
|
for (var y = -1; y <= 1; y++)
|
||||||
{
|
{
|
||||||
direction = Direction.NorthWest;
|
if (x == 0 && y == 0) continue;
|
||||||
} else if (node.TileRef.Y < TileRef.Y)
|
var indices = new MapIndices(TileRef.X + x, TileRef.Y + y);
|
||||||
{
|
if (ParentChunk.InBounds(indices))
|
||||||
direction = Direction.SouthWest;
|
{
|
||||||
}
|
var (relativeX, relativeY) = (indices.X - ParentChunk.Indices.X,
|
||||||
else
|
indices.Y - ParentChunk.Indices.Y);
|
||||||
{
|
yield return ParentChunk.Nodes[relativeX, relativeY];
|
||||||
direction = Direction.West;
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DebugTools.AssertNotNull(neighborChunks);
|
||||||
|
// Get the relevant chunk and then get the node on it
|
||||||
|
foreach (var neighbor in neighborChunks)
|
||||||
|
{
|
||||||
|
// A lot of edge transitions are going to have a single neighboring chunk
|
||||||
|
// (given > 1 only affects corners)
|
||||||
|
// So we can just check the count to see if it's inbound
|
||||||
|
if (neighborChunks.Count > 0 && !neighbor.InBounds(indices)) continue;
|
||||||
|
var (relativeX, relativeY) = (indices.X - neighbor.Indices.X,
|
||||||
|
indices.Y - neighbor.Indices.Y);
|
||||||
|
yield return neighbor.Nodes[relativeX, relativeY];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (node.TileRef.X > TileRef.X)
|
|
||||||
{
|
|
||||||
if (node.TileRef.Y > TileRef.Y)
|
|
||||||
{
|
|
||||||
direction = Direction.NorthEast;
|
|
||||||
} else if (node.TileRef.Y < TileRef.Y)
|
|
||||||
{
|
|
||||||
direction = Direction.SouthEast;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
direction = Direction.East;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (node.TileRef.Y > TileRef.Y)
|
|
||||||
{
|
|
||||||
direction = Direction.North;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
direction = Direction.South;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_neighbors.ContainsKey(direction))
|
|
||||||
{
|
|
||||||
// Should we verify that they align?
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_neighbors.Add(direction, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
public PathfindingNode GetNeighbor(Direction direction)
|
|
||||||
{
|
|
||||||
_neighbors.TryGetValue(direction, out var node);
|
|
||||||
return node;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateTile(TileRef newTile)
|
public void UpdateTile(TileRef newTile)
|
||||||
|
|||||||
@@ -167,32 +167,11 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
|
|||||||
{
|
{
|
||||||
var newChunk = new PathfindingChunk(gridId, indices);
|
var newChunk = new PathfindingChunk(gridId, indices);
|
||||||
newChunk.Initialize();
|
newChunk.Initialize();
|
||||||
if (_graph.TryGetValue(gridId, out var chunks))
|
if (!_graph.ContainsKey(gridId))
|
||||||
{
|
|
||||||
for (var x = -1; x < 2; x++)
|
|
||||||
{
|
|
||||||
for (var y = -1; y < 2; y++)
|
|
||||||
{
|
|
||||||
if (x == 0 && y == 0) continue;
|
|
||||||
|
|
||||||
var neighborIndices = new MapIndices(
|
|
||||||
indices.X + x * PathfindingChunk.ChunkSize,
|
|
||||||
indices.Y + y * PathfindingChunk.ChunkSize);
|
|
||||||
|
|
||||||
if (chunks.TryGetValue(neighborIndices, out var neighborChunk))
|
|
||||||
{
|
|
||||||
neighborChunk.AddNeighbor(newChunk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
_graph.Add(gridId, new Dictionary<MapIndices, PathfindingChunk>());
|
_graph.Add(gridId, new Dictionary<MapIndices, PathfindingChunk>());
|
||||||
}
|
}
|
||||||
|
|
||||||
_graph[gridId].Add(indices, newChunk);
|
_graph[gridId].Add(indices, newChunk);
|
||||||
|
|
||||||
return newChunk;
|
return newChunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user