Pathfinder rework (#11452)
This commit is contained in:
23
Content.Shared/NPC/Events/PathBreadcrumbsMessage.cs
Normal file
23
Content.Shared/NPC/Events/PathBreadcrumbsMessage.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.NPC;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class PathBreadcrumbsMessage : EntityEventArgs
|
||||
{
|
||||
public Dictionary<EntityUid, Dictionary<Vector2i, List<PathfindingBreadcrumb>>> Breadcrumbs = new();
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class PathBreadcrumbsRefreshMessage : EntityEventArgs
|
||||
{
|
||||
public EntityUid GridUid;
|
||||
public Vector2i Origin;
|
||||
public List<PathfindingBreadcrumb> Data = new();
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class PathPolysMessage : EntityEventArgs
|
||||
{
|
||||
public Dictionary<EntityUid, Dictionary<Vector2i, Dictionary<Vector2i, List<DebugPathPoly>>>> Polys = new();
|
||||
}
|
||||
15
Content.Shared/NPC/Events/PathPolysRefreshMessage.cs
Normal file
15
Content.Shared/NPC/Events/PathPolysRefreshMessage.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.NPC;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class PathPolysRefreshMessage : EntityEventArgs
|
||||
{
|
||||
public EntityUid GridUid;
|
||||
public Vector2i Origin;
|
||||
|
||||
/// <summary>
|
||||
/// Multi-dimension arrays aren't supported so
|
||||
/// </summary>
|
||||
public Dictionary<Vector2i, List<DebugPathPoly>> Polys = new();
|
||||
}
|
||||
19
Content.Shared/NPC/Events/PathRouteMessage.cs
Normal file
19
Content.Shared/NPC/Events/PathRouteMessage.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.NPC;
|
||||
|
||||
/// <summary>
|
||||
/// Debug message containing a pathfinding route.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class PathRouteMessage : EntityEventArgs
|
||||
{
|
||||
public List<DebugPathPoly> Path;
|
||||
public Dictionary<DebugPathPoly, float> Costs;
|
||||
|
||||
public PathRouteMessage(List<DebugPathPoly> path, Dictionary<DebugPathPoly, float> costs)
|
||||
{
|
||||
Path = path;
|
||||
Costs = costs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.NPC;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class RequestPathfindingDebugMessage : EntityEventArgs
|
||||
{
|
||||
public PathfindingDebugMode Mode;
|
||||
}
|
||||
32
Content.Shared/NPC/PathPoly.cs
Normal file
32
Content.Shared/NPC/PathPoly.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.NPC;
|
||||
|
||||
/*
|
||||
* I bikeshedded a lot on how to do this and I'm still not entirely happy.
|
||||
* The main thing is you need a weak ref to the poly because it may be invalidated due to graph updates.
|
||||
* I had a struct version but you still need to store the neighbors somewhere, maybe on the chunk itself?
|
||||
* Future dev work required.
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// A path poly to be used for networked debug purposes.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class DebugPathPoly
|
||||
{
|
||||
public EntityUid GraphUid;
|
||||
public Vector2i ChunkOrigin;
|
||||
public byte TileIndex;
|
||||
|
||||
public Box2 Box;
|
||||
public PathfindingData Data;
|
||||
public List<EntityCoordinates> Neighbors = default!;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class DebugPathPolyNeighbor
|
||||
{
|
||||
public EntityCoordinates Coordinates;
|
||||
}
|
||||
23
Content.Shared/NPC/PathfindingBoundary.cs
Normal file
23
Content.Shared/NPC/PathfindingBoundary.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.NPC;
|
||||
|
||||
/// <summary>
|
||||
/// Boundary around a navigation region.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public struct PathfindingBoundary
|
||||
{
|
||||
public List<PathfindingBreadcrumb> Breadcrumbs;
|
||||
|
||||
/// <summary>
|
||||
/// Is it a closed loop or is it a special-case chain (e.g. thindows).
|
||||
/// </summary>
|
||||
public bool Closed;
|
||||
|
||||
public PathfindingBoundary(bool closed, List<PathfindingBreadcrumb> crumbs)
|
||||
{
|
||||
Closed = closed;
|
||||
Breadcrumbs = crumbs;
|
||||
}
|
||||
}
|
||||
118
Content.Shared/NPC/PathfindingBreadcrumb.cs
Normal file
118
Content.Shared/NPC/PathfindingBreadcrumb.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.NPC;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public struct PathfindingBreadcrumb : IEquatable<PathfindingBreadcrumb>
|
||||
{
|
||||
/// <summary>
|
||||
/// The X and Y index in the point grid.
|
||||
/// The actual coordinates require using <see cref="SharedPathfindingSystem.ChunkSize"/> and <see cref="SharedPathfindingSystem.SubStep"/>
|
||||
/// </summary>
|
||||
public Vector2i Coordinates;
|
||||
|
||||
public PathfindingData Data;
|
||||
|
||||
public static readonly PathfindingBreadcrumb Invalid = new()
|
||||
{
|
||||
Data = new PathfindingData(PathfindingBreadcrumbFlag.None, -1, -1, 0f),
|
||||
};
|
||||
|
||||
public PathfindingBreadcrumb(Vector2i coordinates, int layer, int mask, float damage, PathfindingBreadcrumbFlag flags = PathfindingBreadcrumbFlag.None)
|
||||
{
|
||||
Coordinates = coordinates;
|
||||
Data = new PathfindingData(flags, layer, mask, damage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is this crumb equal for pathfinding region purposes.
|
||||
/// </summary>
|
||||
public bool Equivalent(PathfindingBreadcrumb other)
|
||||
{
|
||||
return Data.Equals(other.Data);
|
||||
}
|
||||
|
||||
public bool Equals(PathfindingBreadcrumb other)
|
||||
{
|
||||
return Coordinates.Equals(other.Coordinates) && Data.Equals(other.Data);
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is PathfindingBreadcrumb other && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(Coordinates, Data);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The data relevant for pathfinding.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public struct PathfindingData : IEquatable<PathfindingData>
|
||||
{
|
||||
public PathfindingBreadcrumbFlag Flags;
|
||||
public int CollisionLayer;
|
||||
public int CollisionMask;
|
||||
public float Damage;
|
||||
|
||||
public bool IsFreeSpace => (Flags == PathfindingBreadcrumbFlag.None && Damage.Equals(0f));
|
||||
|
||||
public PathfindingData(PathfindingBreadcrumbFlag flag, int layer, int mask, float damage)
|
||||
{
|
||||
Flags = flag;
|
||||
CollisionLayer = layer;
|
||||
CollisionMask = mask;
|
||||
Damage = damage;
|
||||
}
|
||||
|
||||
public bool IsEquivalent(PathfindingData other)
|
||||
{
|
||||
return CollisionLayer.Equals(other.CollisionLayer) &&
|
||||
CollisionMask.Equals(other.CollisionMask) &&
|
||||
Flags.Equals(other.Flags);
|
||||
}
|
||||
|
||||
public bool Equals(PathfindingData other)
|
||||
{
|
||||
return CollisionLayer.Equals(other.CollisionLayer) &&
|
||||
CollisionMask.Equals(other.CollisionMask) &&
|
||||
Flags.Equals(other.Flags) &&
|
||||
Damage.Equals(other.Damage);
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is PathfindingData other && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine((int) Flags, CollisionLayer, CollisionMask);
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum PathfindingBreadcrumbFlag : ushort
|
||||
{
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Has this poly been replaced and is it no longer valid.
|
||||
/// </summary>
|
||||
Invalid = 1 << 0,
|
||||
Space = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// Is there a door that is potentially pryable
|
||||
/// </summary>
|
||||
Door = 1 << 2,
|
||||
|
||||
/// <summary>
|
||||
/// Is there access required
|
||||
/// </summary>
|
||||
Access = 1 << 3,
|
||||
}
|
||||
46
Content.Shared/NPC/PathfindingDebugMode.cs
Normal file
46
Content.Shared/NPC/PathfindingDebugMode.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
namespace Content.Shared.NPC;
|
||||
|
||||
[Flags]
|
||||
public enum PathfindingDebugMode : ushort
|
||||
{
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Show the individual pathfinding breadcrumbs.
|
||||
/// </summary>
|
||||
Breadcrumbs = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// Show the pathfinding chunk edges.
|
||||
/// </summary>
|
||||
Chunks = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// Shows the stats nearest crumb to the mouse cursor.
|
||||
/// </summary>
|
||||
Crumb = 1 << 2,
|
||||
|
||||
/// <summary>
|
||||
/// Shows all of the pathfinding polys.
|
||||
/// </summary>
|
||||
Polys = 1 << 6,
|
||||
|
||||
/// <summary>
|
||||
/// Shows the edges between pathfinding polys.
|
||||
/// </summary>
|
||||
PolyNeighbors = 1 << 7,
|
||||
|
||||
/// <summary>
|
||||
/// Shows the nearest poly to the mouse cursor.
|
||||
/// </summary>
|
||||
Poly = 1 << 8,
|
||||
|
||||
/// <summary>
|
||||
/// Gets a path from the current attached entity to the mouse cursor.
|
||||
/// </summary>
|
||||
Path = 1 << 9,
|
||||
|
||||
Routes = 1 << 10,
|
||||
|
||||
RouteCosts = 1 << 11,
|
||||
}
|
||||
22
Content.Shared/NPC/SharedPathfindingSystem.cs
Normal file
22
Content.Shared/NPC/SharedPathfindingSystem.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
namespace Content.Shared.NPC;
|
||||
|
||||
public abstract class SharedPathfindingSystem : EntitySystem
|
||||
{
|
||||
/// <summary>
|
||||
/// This is equivalent to agent radii for navmeshes. In our case it's preferable that things are cleanly
|
||||
/// divisible per tile so we'll make sure it works as a discrete number.
|
||||
/// </summary>
|
||||
public const byte SubStep = 4;
|
||||
|
||||
public const byte ChunkSize = 8;
|
||||
|
||||
/// <summary>
|
||||
/// We won't do points on edges so we'll offset them slightly.
|
||||
/// </summary>
|
||||
protected const float StepOffset = 1f / SubStep / 2f;
|
||||
|
||||
public Vector2 GetCoordinate(Vector2i chunk, Vector2i index)
|
||||
{
|
||||
return new Vector2(index.X, index.Y) / SubStep+ (chunk) * ChunkSize + StepOffset;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user