| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 |
- using System.Globalization;
- using System.Linq;
- using System.Numerics;
- using Content.Client.Atmos.EntitySystems;
- using Content.Client.Resources;
- using Content.Shared.Atmos;
- using Robust.Client.Graphics;
- using Robust.Client.Input;
- using Robust.Client.ResourceManagement;
- using Robust.Client.UserInterface;
- using Robust.Client.UserInterface.CustomControls;
- using Robust.Shared.Enums;
- using Robust.Shared.Map;
- using Robust.Shared.Map.Components;
- using AtmosDebugOverlayData = Content.Shared.Atmos.EntitySystems.SharedAtmosDebugOverlaySystem.AtmosDebugOverlayData;
- using DebugMessage = Content.Shared.Atmos.EntitySystems.SharedAtmosDebugOverlaySystem.AtmosDebugOverlayMessage;
- namespace Content.Client.Atmos.Overlays;
- public sealed class AtmosDebugOverlay : Overlay
- {
- [Dependency] private readonly IEntityManager _entManager = default!;
- [Dependency] private readonly IMapManager _mapManager = default!;
- [Dependency] private readonly IInputManager _input = default!;
- [Dependency] private readonly IUserInterfaceManager _ui = default!;
- [Dependency] private readonly IResourceCache _cache = default!;
- private readonly SharedTransformSystem _transform;
- private readonly AtmosDebugOverlaySystem _system;
- private readonly SharedMapSystem _map;
- private readonly Font _font;
- private List<(Entity<MapGridComponent>, DebugMessage)> _grids = new();
- public override OverlaySpace Space => OverlaySpace.WorldSpace | OverlaySpace.ScreenSpace;
- internal AtmosDebugOverlay(AtmosDebugOverlaySystem system)
- {
- IoCManager.InjectDependencies(this);
- _system = system;
- _transform = _entManager.System<SharedTransformSystem>();
- _map = _entManager.System<SharedMapSystem>();
- _font = _cache.GetFont("/Fonts/NotoSans/NotoSans-Regular.ttf", 12);
- }
- protected override void Draw(in OverlayDrawArgs args)
- {
- if (args.Space == OverlaySpace.ScreenSpace)
- {
- DrawTooltip(args);
- return;
- }
- var handle = args.WorldHandle;
- GetGrids(args.MapId, args.WorldBounds);
- // IF YOU ARE ABOUT TO INTRODUCE CHUNKING OR SOME OTHER OPTIMIZATION INTO THIS CODE:
- // -- THINK! --
- // 1. "Is this going to make a critical atmos debugging tool harder to debug itself?"
- // 2. "Is this going to do anything that could cause the atmos debugging tool to use resources, server-side or client-side, when nobody's using it?"
- // 3. "Is this going to make it harder for atmos programmers to add data that may not be chunk-friendly into the atmos debugger?"
- // Nanotrasen needs YOU! to avoid premature optimization in critical debugging tools - 20kdc
- foreach (var (grid, msg) in _grids)
- {
- handle.SetTransform(_transform.GetWorldMatrix(grid));
- DrawData(msg, handle);
- }
- handle.SetTransform(Matrix3x2.Identity);
- }
- private void DrawData(DebugMessage msg,
- DrawingHandleWorld handle)
- {
- foreach (var data in msg.OverlayData)
- {
- if (data != null)
- DrawGridTile(data.Value, handle);
- }
- }
- private void DrawGridTile(AtmosDebugOverlayData data,
- DrawingHandleWorld handle)
- {
- DrawFill(data, handle);
- DrawBlocked(data, handle);
- }
- private void DrawFill(AtmosDebugOverlayData data, DrawingHandleWorld handle)
- {
- var tile = data.Indices;
- var fill = GetFillData(data);
- var interp = (fill - _system.CfgBase) / _system.CfgScale;
- Color res;
- if (_system.CfgCBM)
- {
- // Greyscale interpolation
- res = Color.InterpolateBetween(Color.Black, Color.White, interp);
- }
- else
- {
- // Red-Green-Blue interpolation
- res = interp < 0.5f
- ? Color.InterpolateBetween(Color.Red, Color.LimeGreen, interp * 2)
- : Color.InterpolateBetween(Color.LimeGreen, Color.Blue, (interp - 0.5f) * 2);
- }
- res = res.WithAlpha(0.75f);
- handle.DrawRect(Box2.FromDimensions(new Vector2(tile.X, tile.Y), new Vector2(1, 1)), res);
- }
- private float GetFillData(AtmosDebugOverlayData data)
- {
- if (data.Moles == null)
- return 0;
- switch (_system.CfgMode)
- {
- case AtmosDebugOverlayMode.TotalMoles:
- var total = 0f;
- foreach (var f in data.Moles)
- {
- total += f;
- }
- return total;
- case AtmosDebugOverlayMode.GasMoles:
- return data.Moles[_system.CfgSpecificGas];
- default:
- return data.Temperature;
- }
- }
- private void DrawBlocked(AtmosDebugOverlayData data, DrawingHandleWorld handle)
- {
- var tile = data.Indices;
- var tileCentre = tile + 0.5f * Vector2.One;
- CheckAndShowBlockDir(data, handle, AtmosDirection.North, tileCentre);
- CheckAndShowBlockDir(data, handle, AtmosDirection.South, tileCentre);
- CheckAndShowBlockDir(data, handle, AtmosDirection.East, tileCentre);
- CheckAndShowBlockDir(data, handle, AtmosDirection.West, tileCentre);
- // -- Pressure Direction --
- if (data.PressureDirection != AtmosDirection.Invalid)
- {
- DrawPressureDirection(handle, data.PressureDirection, tileCentre, Color.Blue);
- }
- else if (data.LastPressureDirection != AtmosDirection.Invalid)
- {
- DrawPressureDirection(handle, data.LastPressureDirection, tileCentre, Color.LightGray);
- }
- // -- Excited Groups --
- if (data.InExcitedGroup is {} grp)
- {
- var basisA = tile;
- var basisB = tile + new Vector2(1.0f, 1.0f);
- var basisC = tile + new Vector2(0.0f, 1.0f);
- var basisD = tile + new Vector2(1.0f, 0.0f);
- var color = Color.White // Use first three nibbles for an unique color... Good enough?
- .WithRed(grp & 0x000F)
- .WithGreen((grp & 0x00F0) >> 4)
- .WithBlue((grp & 0x0F00) >> 8);
- handle.DrawLine(basisA, basisB, color);
- handle.DrawLine(basisC, basisD, color);
- }
- if (data.IsSpace)
- handle.DrawCircle(tileCentre, 0.15f, Color.Yellow);
- if (data.MapAtmosphere)
- handle.DrawCircle(tileCentre, 0.1f, Color.Orange);
- if (data.NoGrid)
- handle.DrawCircle(tileCentre, 0.05f, Color.Black);
- }
- private void CheckAndShowBlockDir(
- AtmosDebugOverlayData data,
- DrawingHandleWorld handle,
- AtmosDirection dir,
- Vector2 tileCentre)
- {
- if (!data.BlockDirection.HasFlag(dir))
- return;
- // Account for South being 0.
- var atmosAngle = dir.ToAngle() - Angle.FromDegrees(90);
- var atmosAngleOfs = atmosAngle.ToVec() * 0.45f;
- var atmosAngleOfsR90 = new Vector2(atmosAngleOfs.Y, -atmosAngleOfs.X);
- var basisA = tileCentre + atmosAngleOfs - atmosAngleOfsR90;
- var basisB = tileCentre + atmosAngleOfs + atmosAngleOfsR90;
- handle.DrawLine(basisA, basisB, Color.Azure);
- }
- private void DrawPressureDirection(
- DrawingHandleWorld handle,
- AtmosDirection d,
- Vector2 center,
- Color color)
- {
- // Account for South being 0.
- var atmosAngle = d.ToAngle() - Angle.FromDegrees(90);
- var atmosAngleOfs = atmosAngle.ToVec() * 0.4f;
- handle.DrawLine(center, center + atmosAngleOfs, color);
- }
- private void DrawTooltip(in OverlayDrawArgs args)
- {
- var handle = args.ScreenHandle;
- var mousePos = _input.MouseScreenPosition;
- if (!mousePos.IsValid)
- return;
- if (_ui.MouseGetControl(mousePos) is not IViewportControl viewport)
- return;
- var coords= viewport.PixelToMap(mousePos.Position);
- var box = Box2.CenteredAround(coords.Position, 3 * Vector2.One);
- GetGrids(coords.MapId, new Box2Rotated(box));
- foreach (var (grid, msg) in _grids)
- {
- var index = _map.WorldToTile(grid, grid, coords.Position);
- foreach (var data in msg.OverlayData)
- {
- if (data?.Indices == index)
- {
- DrawTooltip(handle, mousePos.Position, data.Value);
- return;
- }
- }
- }
- }
- private void DrawTooltip(DrawingHandleScreen handle, Vector2 pos, AtmosDebugOverlayData data)
- {
- var lineHeight = _font.GetLineHeight(1f);
- var offset = new Vector2(0, lineHeight);
- var moles = data.Moles == null
- ? "No Air"
- : data.Moles.Sum().ToString(CultureInfo.InvariantCulture);
- handle.DrawString(_font, pos, $"Moles: {moles}");
- pos += offset;
- handle.DrawString(_font, pos, $"Temp: {data.Temperature}");
- pos += offset;
- handle.DrawString(_font, pos, $"Excited: {data.InExcitedGroup?.ToString() ?? "None"}");
- pos += offset;
- handle.DrawString(_font, pos, $"Space: {data.IsSpace}");
- pos += offset;
- handle.DrawString(_font, pos, $"Map: {data.MapAtmosphere}");
- pos += offset;
- handle.DrawString(_font, pos, $"NoGrid: {data.NoGrid}");
- pos += offset;
- handle.DrawString(_font, pos, $"Immutable: {data.Immutable}");
- }
- private void GetGrids(MapId mapId, Box2Rotated box)
- {
- _grids.Clear();
- _mapManager.FindGridsIntersecting(
- mapId,
- box,
- ref _grids,
- (EntityUid uid,
- MapGridComponent grid,
- ref List<(Entity<MapGridComponent>, DebugMessage)> state) =>
- {
- if (_system.TileData.TryGetValue(uid, out var data))
- state.Add(((uid, grid), data));
- return true;
- });
- }
- }
|