AI Reachable system (#1342)
Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
This commit is contained in:
@@ -7,9 +7,11 @@ using Robust.Client.Graphics.Overlays;
|
||||
using Robust.Client.Graphics.Shaders;
|
||||
using Robust.Client.Interfaces.Graphics.ClientEye;
|
||||
using Robust.Client.Interfaces.Graphics.Overlays;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.Random;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
@@ -30,6 +32,16 @@ namespace Content.Client.GameObjects.EntitySystems.AI
|
||||
SubscribeNetworkEvent<SharedAiDebug.AStarRouteMessage>(HandleAStarRouteMessage);
|
||||
SubscribeNetworkEvent<SharedAiDebug.JpsRouteMessage>(HandleJpsRouteMessage);
|
||||
SubscribeNetworkEvent<SharedAiDebug.PathfindingGraphMessage>(HandleGraphMessage);
|
||||
SubscribeNetworkEvent<SharedAiDebug.ReachableChunkRegionsDebugMessage>(HandleRegionsMessage);
|
||||
SubscribeNetworkEvent<SharedAiDebug.ReachableCacheDebugMessage>(HandleCachedRegionsMessage);
|
||||
// I'm lazy
|
||||
EnableOverlay();
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
DisableOverlay();
|
||||
}
|
||||
|
||||
private void HandleAStarRouteMessage(SharedAiDebug.AStarRouteMessage message)
|
||||
@@ -62,10 +74,20 @@ namespace Content.Client.GameObjects.EntitySystems.AI
|
||||
|
||||
private void HandleGraphMessage(SharedAiDebug.PathfindingGraphMessage message)
|
||||
{
|
||||
if ((_modes & PathfindingDebugMode.Graph) != 0)
|
||||
{
|
||||
_overlay.UpdateGraph(message.Graph);
|
||||
}
|
||||
EnableOverlay();
|
||||
_overlay.UpdateGraph(message.Graph);
|
||||
}
|
||||
|
||||
private void HandleRegionsMessage(SharedAiDebug.ReachableChunkRegionsDebugMessage message)
|
||||
{
|
||||
EnableOverlay();
|
||||
_overlay.UpdateRegions(message.GridId, message.Regions);
|
||||
}
|
||||
|
||||
private void HandleCachedRegionsMessage(SharedAiDebug.ReachableCacheDebugMessage message)
|
||||
{
|
||||
EnableOverlay();
|
||||
_overlay.UpdateCachedRegions(message.GridId, message.Regions, message.Cached);
|
||||
}
|
||||
|
||||
private void EnableOverlay()
|
||||
@@ -114,6 +136,9 @@ namespace Content.Client.GameObjects.EntitySystems.AI
|
||||
var systemMessage = new SharedAiDebug.RequestPathfindingGraphMessage();
|
||||
EntityManager.EntityNetManager.SendSystemNetworkMessage(systemMessage);
|
||||
}
|
||||
|
||||
// TODO: Request region graph, although the client system messages didn't seem to be going through anymore
|
||||
// So need further investigation.
|
||||
}
|
||||
|
||||
private void DisableMode(PathfindingDebugMode mode)
|
||||
@@ -144,6 +169,9 @@ namespace Content.Client.GameObjects.EntitySystems.AI
|
||||
|
||||
internal sealed class DebugPathfindingOverlay : Overlay
|
||||
{
|
||||
private IEyeManager _eyeManager;
|
||||
private IPlayerManager _playerManager;
|
||||
|
||||
// TODO: Add a box like the debug one and show the most recent path stuff
|
||||
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
|
||||
|
||||
@@ -152,7 +180,21 @@ namespace Content.Client.GameObjects.EntitySystems.AI
|
||||
// Graph debugging
|
||||
public readonly Dictionary<int, List<Vector2>> Graph = new Dictionary<int, List<Vector2>>();
|
||||
private readonly Dictionary<int, Color> _graphColors = new Dictionary<int, Color>();
|
||||
|
||||
|
||||
// Cached regions
|
||||
public readonly Dictionary<GridId, Dictionary<int, List<Vector2>>> CachedRegions =
|
||||
new Dictionary<GridId, Dictionary<int, List<Vector2>>>();
|
||||
|
||||
private readonly Dictionary<GridId, Dictionary<int, Color>> _cachedRegionColors =
|
||||
new Dictionary<GridId, Dictionary<int, Color>>();
|
||||
|
||||
// Regions
|
||||
public readonly Dictionary<GridId, Dictionary<int, Dictionary<int, List<Vector2>>>> Regions =
|
||||
new Dictionary<GridId, Dictionary<int, Dictionary<int, List<Vector2>>>>();
|
||||
|
||||
private readonly Dictionary<GridId, Dictionary<int, Dictionary<int, Color>>> _regionColors =
|
||||
new Dictionary<GridId, Dictionary<int, Dictionary<int, Color>>>();
|
||||
|
||||
// Route debugging
|
||||
// As each pathfinder is very different you'll likely want to draw them completely different
|
||||
public readonly List<SharedAiDebug.AStarRouteMessage> AStarRoutes = new List<SharedAiDebug.AStarRouteMessage>();
|
||||
@@ -161,8 +203,11 @@ namespace Content.Client.GameObjects.EntitySystems.AI
|
||||
public DebugPathfindingOverlay() : base(nameof(DebugPathfindingOverlay))
|
||||
{
|
||||
Shader = IoCManager.Resolve<IPrototypeManager>().Index<ShaderPrototype>("unshaded").Instance();
|
||||
_eyeManager = IoCManager.Resolve<IEyeManager>();
|
||||
_playerManager = IoCManager.Resolve<IPlayerManager>();
|
||||
}
|
||||
|
||||
#region Graph
|
||||
public void UpdateGraph(Dictionary<int, List<Vector2>> graph)
|
||||
{
|
||||
Graph.Clear();
|
||||
@@ -176,19 +221,15 @@ namespace Content.Client.GameObjects.EntitySystems.AI
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawGraph(DrawingHandleScreen screenHandle)
|
||||
private void DrawGraph(DrawingHandleScreen screenHandle, Box2 viewport)
|
||||
{
|
||||
var eyeManager = IoCManager.Resolve<IEyeManager>();
|
||||
var viewport = IoCManager.Resolve<IEyeManager>().GetWorldViewport();
|
||||
|
||||
foreach (var (chunk, nodes) in Graph)
|
||||
{
|
||||
foreach (var tile in nodes)
|
||||
{
|
||||
if (!viewport.Contains(tile)) continue;
|
||||
|
||||
var screenTile = eyeManager.WorldToScreen(tile);
|
||||
|
||||
var screenTile = _eyeManager.WorldToScreen(tile);
|
||||
var box = new UIBox2(
|
||||
screenTile.X - 15.0f,
|
||||
screenTile.Y - 15.0f,
|
||||
@@ -199,21 +240,130 @@ namespace Content.Client.GameObjects.EntitySystems.AI
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region pathfinder
|
||||
|
||||
private void DrawAStarRoutes(DrawingHandleScreen screenHandle)
|
||||
#region Regions
|
||||
//Server side debugger should increment every region
|
||||
public void UpdateCachedRegions(GridId gridId, Dictionary<int, List<Vector2>> messageRegions, bool cached)
|
||||
{
|
||||
var eyeManager = IoCManager.Resolve<IEyeManager>();
|
||||
var viewport = eyeManager.GetWorldViewport();
|
||||
if (!CachedRegions.ContainsKey(gridId))
|
||||
{
|
||||
CachedRegions.Add(gridId, new Dictionary<int, List<Vector2>>());
|
||||
_cachedRegionColors.Add(gridId, new Dictionary<int, Color>());
|
||||
}
|
||||
|
||||
foreach (var (region, nodes) in messageRegions)
|
||||
{
|
||||
CachedRegions[gridId][region] = nodes;
|
||||
if (cached)
|
||||
{
|
||||
_cachedRegionColors[gridId][region] = Color.Blue.WithAlpha(0.3f);
|
||||
}
|
||||
else
|
||||
{
|
||||
_cachedRegionColors[gridId][region] = Color.Green.WithAlpha(0.3f);
|
||||
}
|
||||
|
||||
Timer.Spawn(3000, () =>
|
||||
{
|
||||
if (CachedRegions[gridId].ContainsKey(region))
|
||||
{
|
||||
CachedRegions[gridId].Remove(region);
|
||||
_cachedRegionColors[gridId].Remove(region);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawCachedRegions(DrawingHandleScreen screenHandle, Box2 viewport)
|
||||
{
|
||||
var attachedEntity = _playerManager.LocalPlayer?.ControlledEntity;
|
||||
if (attachedEntity == null || !CachedRegions.TryGetValue(attachedEntity.Transform.GridID, out var entityRegions))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (region, nodes) in entityRegions)
|
||||
{
|
||||
foreach (var tile in nodes)
|
||||
{
|
||||
if (!viewport.Contains(tile)) continue;
|
||||
|
||||
var screenTile = _eyeManager.WorldToScreen(tile);
|
||||
var box = new UIBox2(
|
||||
screenTile.X - 15.0f,
|
||||
screenTile.Y - 15.0f,
|
||||
screenTile.X + 15.0f,
|
||||
screenTile.Y + 15.0f);
|
||||
|
||||
screenHandle.DrawRect(box, _cachedRegionColors[attachedEntity.Transform.GridID][region]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateRegions(GridId gridId, Dictionary<int, Dictionary<int, List<Vector2>>> messageRegions)
|
||||
{
|
||||
if (!Regions.ContainsKey(gridId))
|
||||
{
|
||||
Regions.Add(gridId, new Dictionary<int, Dictionary<int, List<Vector2>>>());
|
||||
_regionColors.Add(gridId, new Dictionary<int, Dictionary<int, Color>>());
|
||||
}
|
||||
|
||||
var robustRandom = IoCManager.Resolve<IRobustRandom>();
|
||||
foreach (var (chunk, regions) in messageRegions)
|
||||
{
|
||||
Regions[gridId][chunk] = new Dictionary<int, List<Vector2>>();
|
||||
_regionColors[gridId][chunk] = new Dictionary<int, Color>();
|
||||
|
||||
foreach (var (region, nodes) in regions)
|
||||
{
|
||||
Regions[gridId][chunk].Add(region, nodes);
|
||||
_regionColors[gridId][chunk][region] = new Color(robustRandom.NextFloat(), robustRandom.NextFloat(),
|
||||
robustRandom.NextFloat(), 0.3f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawRegions(DrawingHandleScreen screenHandle, Box2 viewport)
|
||||
{
|
||||
var attachedEntity = _playerManager.LocalPlayer?.ControlledEntity;
|
||||
if (attachedEntity == null || !Regions.TryGetValue(attachedEntity.Transform.GridID, out var entityRegions))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (chunk, regions) in entityRegions)
|
||||
{
|
||||
foreach (var (region, nodes) in regions)
|
||||
{
|
||||
foreach (var tile in nodes)
|
||||
{
|
||||
if (!viewport.Contains(tile)) continue;
|
||||
|
||||
var screenTile = _eyeManager.WorldToScreen(tile);
|
||||
var box = new UIBox2(
|
||||
screenTile.X - 15.0f,
|
||||
screenTile.Y - 15.0f,
|
||||
screenTile.X + 15.0f,
|
||||
screenTile.Y + 15.0f);
|
||||
|
||||
screenHandle.DrawRect(box, _regionColors[attachedEntity.Transform.GridID][chunk][region]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Pathfinder
|
||||
private void DrawAStarRoutes(DrawingHandleScreen screenHandle, Box2 viewport)
|
||||
{
|
||||
foreach (var route in AStarRoutes)
|
||||
{
|
||||
// Draw box on each tile of route
|
||||
foreach (var position in route.Route)
|
||||
{
|
||||
if (!viewport.Contains(position)) continue;
|
||||
var screenTile = eyeManager.WorldToScreen(position);
|
||||
var screenTile = _eyeManager.WorldToScreen(position);
|
||||
// worldHandle.DrawLine(position, nextWorld.Value, Color.Blue);
|
||||
var box = new UIBox2(
|
||||
screenTile.X - 15.0f,
|
||||
@@ -225,11 +375,8 @@ namespace Content.Client.GameObjects.EntitySystems.AI
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawAStarNodes(DrawingHandleScreen screenHandle)
|
||||
private void DrawAStarNodes(DrawingHandleScreen screenHandle, Box2 viewport)
|
||||
{
|
||||
var eyeManager = IoCManager.Resolve<IEyeManager>();
|
||||
var viewport = eyeManager.GetWorldViewport();
|
||||
|
||||
foreach (var route in AStarRoutes)
|
||||
{
|
||||
var highestgScore = route.GScores.Values.Max();
|
||||
@@ -242,8 +389,7 @@ namespace Content.Client.GameObjects.EntitySystems.AI
|
||||
continue;
|
||||
}
|
||||
|
||||
var screenTile = eyeManager.WorldToScreen(tile);
|
||||
|
||||
var screenTile = _eyeManager.WorldToScreen(tile);
|
||||
var box = new UIBox2(
|
||||
screenTile.X - 15.0f,
|
||||
screenTile.Y - 15.0f,
|
||||
@@ -259,18 +405,15 @@ namespace Content.Client.GameObjects.EntitySystems.AI
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawJpsRoutes(DrawingHandleScreen screenHandle)
|
||||
private void DrawJpsRoutes(DrawingHandleScreen screenHandle, Box2 viewport)
|
||||
{
|
||||
var eyeManager = IoCManager.Resolve<IEyeManager>();
|
||||
var viewport = eyeManager.GetWorldViewport();
|
||||
|
||||
foreach (var route in JpsRoutes)
|
||||
{
|
||||
// Draw box on each tile of route
|
||||
foreach (var position in route.Route)
|
||||
{
|
||||
if (!viewport.Contains(position)) continue;
|
||||
var screenTile = eyeManager.WorldToScreen(position);
|
||||
var screenTile = _eyeManager.WorldToScreen(position);
|
||||
// worldHandle.DrawLine(position, nextWorld.Value, Color.Blue);
|
||||
var box = new UIBox2(
|
||||
screenTile.X - 15.0f,
|
||||
@@ -282,11 +425,8 @@ namespace Content.Client.GameObjects.EntitySystems.AI
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawJpsNodes(DrawingHandleScreen screenHandle)
|
||||
private void DrawJpsNodes(DrawingHandleScreen screenHandle, Box2 viewport)
|
||||
{
|
||||
var eyeManager = IoCManager.Resolve<IEyeManager>();
|
||||
var viewport = eyeManager.GetWorldViewport();
|
||||
|
||||
foreach (var route in JpsRoutes)
|
||||
{
|
||||
foreach (var tile in route.JumpNodes)
|
||||
@@ -297,8 +437,7 @@ namespace Content.Client.GameObjects.EntitySystems.AI
|
||||
continue;
|
||||
}
|
||||
|
||||
var screenTile = eyeManager.WorldToScreen(tile);
|
||||
|
||||
var screenTile = _eyeManager.WorldToScreen(tile);
|
||||
var box = new UIBox2(
|
||||
screenTile.X - 15.0f,
|
||||
screenTile.Y - 15.0f,
|
||||
@@ -324,22 +463,33 @@ namespace Content.Client.GameObjects.EntitySystems.AI
|
||||
}
|
||||
|
||||
var screenHandle = (DrawingHandleScreen) handle;
|
||||
var viewport = _eyeManager.GetWorldViewport();
|
||||
|
||||
if ((Modes & PathfindingDebugMode.Route) != 0)
|
||||
{
|
||||
DrawAStarRoutes(screenHandle);
|
||||
DrawJpsRoutes(screenHandle);
|
||||
DrawAStarRoutes(screenHandle, viewport);
|
||||
DrawJpsRoutes(screenHandle, viewport);
|
||||
}
|
||||
|
||||
if ((Modes & PathfindingDebugMode.Nodes) != 0)
|
||||
{
|
||||
DrawAStarNodes(screenHandle);
|
||||
DrawJpsNodes(screenHandle);
|
||||
DrawAStarNodes(screenHandle, viewport);
|
||||
DrawJpsNodes(screenHandle, viewport);
|
||||
}
|
||||
|
||||
if ((Modes & PathfindingDebugMode.Graph) != 0)
|
||||
{
|
||||
DrawGraph(screenHandle);
|
||||
DrawGraph(screenHandle, viewport);
|
||||
}
|
||||
|
||||
if ((Modes & PathfindingDebugMode.CachedRegions) != 0)
|
||||
{
|
||||
DrawCachedRegions(screenHandle, viewport);
|
||||
}
|
||||
|
||||
if ((Modes & PathfindingDebugMode.Regions) != 0)
|
||||
{
|
||||
DrawRegions(screenHandle, viewport);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -350,6 +500,8 @@ namespace Content.Client.GameObjects.EntitySystems.AI
|
||||
Route = 1 << 0,
|
||||
Graph = 1 << 1,
|
||||
Nodes = 1 << 2,
|
||||
CachedRegions = 1 << 3,
|
||||
Regions = 1 << 4,
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user