NodeVisualizationOverlay.cs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. using System.Numerics;
  2. using System.Text;
  3. using Content.Client.Resources;
  4. using Robust.Client.Graphics;
  5. using Robust.Client.Input;
  6. using Robust.Client.ResourceManagement;
  7. using Robust.Shared.Enums;
  8. using Robust.Shared.Map;
  9. using Robust.Shared.Map.Components;
  10. using Robust.Shared.Timing;
  11. using Robust.Shared.Utility;
  12. using static Content.Shared.NodeContainer.NodeVis;
  13. namespace Content.Client.NodeContainer
  14. {
  15. public sealed class NodeVisualizationOverlay : Overlay
  16. {
  17. private readonly NodeGroupSystem _system;
  18. private readonly EntityLookupSystem _lookup;
  19. private readonly IMapManager _mapManager;
  20. private readonly IInputManager _inputManager;
  21. private readonly IEntityManager _entityManager;
  22. private readonly SharedTransformSystem _transformSystem;
  23. private readonly Dictionary<(int, int), NodeRenderData> _nodeIndex = new();
  24. private readonly Dictionary<EntityUid, Dictionary<Vector2i, List<(GroupData, NodeDatum)>>> _gridIndex = new ();
  25. private List<Entity<MapGridComponent>> _grids = new();
  26. private readonly Font _font;
  27. private Vector2 _mouseWorldPos = default;
  28. private (int group, int node)? _hovered;
  29. private float _time;
  30. public override OverlaySpace Space => OverlaySpace.ScreenSpace | OverlaySpace.WorldSpace;
  31. public NodeVisualizationOverlay(
  32. NodeGroupSystem system,
  33. EntityLookupSystem lookup,
  34. IMapManager mapManager,
  35. IInputManager inputManager,
  36. IResourceCache cache,
  37. IEntityManager entityManager)
  38. {
  39. _system = system;
  40. _lookup = lookup;
  41. _mapManager = mapManager;
  42. _inputManager = inputManager;
  43. _entityManager = entityManager;
  44. _transformSystem = _entityManager.System<SharedTransformSystem>();
  45. _font = cache.GetFont("/Fonts/NotoSans/NotoSans-Regular.ttf", 12);
  46. }
  47. protected override void Draw(in OverlayDrawArgs args)
  48. {
  49. if ((args.Space & OverlaySpace.WorldSpace) != 0)
  50. {
  51. DrawWorld(args);
  52. }
  53. else if ((args.Space & OverlaySpace.ScreenSpace) != 0)
  54. {
  55. DrawScreen(args);
  56. }
  57. }
  58. private void DrawScreen(in OverlayDrawArgs args)
  59. {
  60. var mousePos = _inputManager.MouseScreenPosition.Position;
  61. _mouseWorldPos = args
  62. .ViewportControl!
  63. .PixelToMap(mousePos)
  64. .Position;
  65. if (_hovered == null)
  66. return;
  67. var (groupId, nodeId) = _hovered.Value;
  68. var group = _system.Groups[groupId];
  69. var node = _system.NodeLookup[(groupId, nodeId)];
  70. var xform = _entityManager.GetComponent<TransformComponent>(_entityManager.GetEntity(node.Entity));
  71. if (!_entityManager.TryGetComponent<MapGridComponent>(xform.GridUid, out var grid))
  72. return;
  73. var gridTile = grid.TileIndicesFor(xform.Coordinates);
  74. var sb = new StringBuilder();
  75. sb.Append($"entity: {node.Entity}\n");
  76. sb.Append($"group id: {group.GroupId}\n");
  77. sb.Append($"node: {node.Name}\n");
  78. sb.Append($"type: {node.Type}\n");
  79. sb.Append($"grid pos: {gridTile}\n");
  80. sb.Append(group.DebugData);
  81. args.ScreenHandle.DrawString(_font, mousePos + new Vector2(20, -20), sb.ToString());
  82. }
  83. private void DrawWorld(in OverlayDrawArgs overlayDrawArgs)
  84. {
  85. const float nodeSize = 8f / 32;
  86. const float nodeOffset = 6f / 32;
  87. var handle = overlayDrawArgs.WorldHandle;
  88. var map = overlayDrawArgs.Viewport.Eye?.Position.MapId ?? default;
  89. if (map == MapId.Nullspace)
  90. return;
  91. _hovered = default;
  92. var cursorBox = Box2.CenteredAround(_mouseWorldPos, new Vector2(nodeSize, nodeSize));
  93. // Group visible nodes by grid tiles.
  94. var worldAABB = overlayDrawArgs.WorldAABB;
  95. var xformQuery = _entityManager.GetEntityQuery<TransformComponent>();
  96. _grids.Clear();
  97. _mapManager.FindGridsIntersecting(map, worldAABB, ref _grids);
  98. foreach (var grid in _grids)
  99. {
  100. foreach (var entity in _lookup.GetEntitiesIntersecting(grid, worldAABB))
  101. {
  102. if (!_system.Entities.TryGetValue(entity, out var nodeData))
  103. continue;
  104. var gridDict = _gridIndex.GetOrNew(grid);
  105. var coords = xformQuery.GetComponent(entity).Coordinates;
  106. // TODO: This probably shouldn't be capable of returning NaN...
  107. if (float.IsNaN(coords.Position.X) || float.IsNaN(coords.Position.Y))
  108. continue;
  109. var tile = gridDict.GetOrNew(grid.Comp.TileIndicesFor(coords));
  110. foreach (var (group, nodeDatum) in nodeData)
  111. {
  112. if (!_system.Filtered.Contains(group.GroupId))
  113. {
  114. tile.Add((group, nodeDatum));
  115. }
  116. }
  117. }
  118. }
  119. foreach (var (gridId, gridDict) in _gridIndex)
  120. {
  121. var grid = _entityManager.GetComponent<MapGridComponent>(gridId);
  122. var (_, _, worldMatrix, invMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(gridId);
  123. var lCursorBox = invMatrix.TransformBox(cursorBox);
  124. foreach (var (pos, list) in gridDict)
  125. {
  126. var centerPos = (Vector2) pos + grid.TileSizeHalfVector;
  127. list.Sort(NodeDisplayComparer.Instance);
  128. var offset = -(list.Count - 1) * nodeOffset / 2;
  129. foreach (var (group, node) in list)
  130. {
  131. var nodePos = centerPos + new Vector2(offset, offset);
  132. if (lCursorBox.Contains(nodePos))
  133. _hovered = (group.NetId, node.NetId);
  134. _nodeIndex[(group.NetId, node.NetId)] = new NodeRenderData(group, node, nodePos);
  135. offset += nodeOffset;
  136. }
  137. }
  138. handle.SetTransform(worldMatrix);
  139. foreach (var nodeRenderData in _nodeIndex.Values)
  140. {
  141. var pos = nodeRenderData.NodePos;
  142. var bounds = Box2.CenteredAround(pos, new Vector2(nodeSize, nodeSize));
  143. var groupData = nodeRenderData.GroupData;
  144. var color = groupData.Color;
  145. if (!_hovered.HasValue)
  146. color.A = 0.5f;
  147. else if (_hovered.Value.group != groupData.NetId)
  148. color.A = 0.2f;
  149. else
  150. color.A = 0.75f + MathF.Sin(_time * 4) * 0.25f;
  151. handle.DrawRect(bounds, color);
  152. foreach (var reachable in nodeRenderData.NodeDatum.Reachable)
  153. {
  154. if (_nodeIndex.TryGetValue((groupData.NetId, reachable), out var reachDat))
  155. {
  156. handle.DrawLine(pos, reachDat.NodePos, color);
  157. }
  158. }
  159. }
  160. _nodeIndex.Clear();
  161. }
  162. handle.SetTransform(Matrix3x2.Identity);
  163. _gridIndex.Clear();
  164. }
  165. protected override void FrameUpdate(FrameEventArgs args)
  166. {
  167. base.FrameUpdate(args);
  168. _time += args.DeltaSeconds;
  169. }
  170. private sealed class NodeDisplayComparer : IComparer<(GroupData, NodeDatum)>
  171. {
  172. public static readonly NodeDisplayComparer Instance = new();
  173. public int Compare((GroupData, NodeDatum) x, (GroupData, NodeDatum) y)
  174. {
  175. var (groupX, nodeX) = x;
  176. var (groupY, nodeY) = y;
  177. var cmp = groupX.NetId.CompareTo(groupY.NetId);
  178. if (cmp != 0)
  179. return cmp;
  180. return nodeX.NetId.CompareTo(nodeY.NetId);
  181. }
  182. }
  183. private sealed class NodeRenderData
  184. {
  185. public GroupData GroupData;
  186. public NodeDatum NodeDatum;
  187. public Vector2 NodePos;
  188. public NodeRenderData(GroupData groupData, NodeDatum nodeDatum, Vector2 nodePos)
  189. {
  190. GroupData = groupData;
  191. NodeDatum = nodeDatum;
  192. NodePos = nodePos;
  193. }
  194. }
  195. }
  196. }