Atmospheric network monitor (#32294)
* Updated to latest master version * Added gas pipe analyzer * Completed prototype * Playing with UI display * Refinement of the main UI * Renamed gas pipe analyzer to gas pipe sensor * Added focus network highlighting and map icons for gas pipe sensors * Added construction graph for gas pipe sensor * Improved efficiency of atmos pipe and focus pipe network data storage * Added gas pipe sensor variants * Fixed gas pipe sensor nav map icon not highlighting on focus * Rendered pipe lines now get merged together * Set up appearance handling for the gas pipe sensor, but setting the layers is bugged * Gas pipe sensor lights turn off when the device is unpowered * Renamed console * The gas pipe sensor is now a pipe. Redistributed components between it and its assembly * AtmosMonitors can now optionally monitor their internal pipe network instead of the surrounding atmosphere * Massive code clean up * Added delta states to handle pipe net updates, fixed entity deletion handling * Nav map blip data has been replaced with prototypes * Nav map blip fixes * Nav map colors are now set by the console component * Made the nav map more responsive to changes in focus * Updated nav map icons * Reverted unnecessary namespace changes * Code tidy up * Updated sprites and construction graph for gas pipe sensor * Updated localization files * Misc bug fixes * Added missing comment * Fixed issue with the circuit board for the monitor * Embellished the background of the console network entries * Updated console to account for PR #32273 * Removed gas pipe sensor * Fixing merge conflict * Update * Addressing reviews part 1 * Addressing review part 2 * Addressing reviews part 3 * Removed unnecessary references * Side panel values will be grayed out if there is no gas present in the pipe network * Declaring colors at the start of some files * Added a colored stripe to the side of the atmos network entries * Fixed an issue with pipe sensor blip coloration * Fixed delay that occurs when toggling gas sensors on/off
@@ -31,19 +31,6 @@ public sealed partial class AtmosAlarmEntryContainer : BoxContainer
|
||||
[AtmosAlarmType.Danger] = "atmos-alerts-window-danger-state",
|
||||
};
|
||||
|
||||
private Dictionary<Gas, string> _gasShorthands = new Dictionary<Gas, string>()
|
||||
{
|
||||
[Gas.Ammonia] = "NH₃",
|
||||
[Gas.CarbonDioxide] = "CO₂",
|
||||
[Gas.Frezon] = "F",
|
||||
[Gas.Nitrogen] = "N₂",
|
||||
[Gas.NitrousOxide] = "N₂O",
|
||||
[Gas.Oxygen] = "O₂",
|
||||
[Gas.Plasma] = "P",
|
||||
[Gas.Tritium] = "T",
|
||||
[Gas.WaterVapor] = "H₂O",
|
||||
};
|
||||
|
||||
public AtmosAlarmEntryContainer(NetEntity uid, EntityCoordinates? coordinates)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
@@ -162,12 +149,11 @@ public sealed partial class AtmosAlarmEntryContainer : BoxContainer
|
||||
foreach ((var gas, (var mol, var percent, var alert)) in keyValuePairs)
|
||||
{
|
||||
FixedPoint2 gasPercent = percent * 100f;
|
||||
|
||||
var gasShorthand = _gasShorthands.GetValueOrDefault(gas, "X");
|
||||
var gasAbbreviation = Atmospherics.GasAbbreviations.GetValueOrDefault(gas, Loc.GetString("gas-unknown-abbreviation"));
|
||||
|
||||
var gasLabel = new Label()
|
||||
{
|
||||
Text = Loc.GetString("atmos-alerts-window-other-gases-value", ("shorthand", gasShorthand), ("value", gasPercent)),
|
||||
Text = Loc.GetString("atmos-alerts-window-other-gases-value", ("shorthand", gasAbbreviation), ("value", gasPercent)),
|
||||
FontOverride = normalFont,
|
||||
FontColorOverride = GetAlarmStateColor(alert),
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
using Content.Shared.Atmos.Components;
|
||||
|
||||
namespace Content.Client.Atmos.Consoles;
|
||||
|
||||
public sealed class AtmosMonitoringConsoleBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[ViewVariables]
|
||||
private AtmosMonitoringConsoleWindow? _menu;
|
||||
|
||||
public AtmosMonitoringConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { }
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_menu = new AtmosMonitoringConsoleWindow(this, Owner);
|
||||
_menu.OpenCentered();
|
||||
_menu.OnClose += Close;
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (state is not AtmosMonitoringConsoleBoundInterfaceState castState)
|
||||
return;
|
||||
|
||||
EntMan.TryGetComponent<TransformComponent>(Owner, out var xform);
|
||||
_menu?.UpdateUI(xform?.Coordinates, castState.AtmosNetworks);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
_menu?.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
using Content.Client.Pinpointer.UI;
|
||||
using Content.Shared.Atmos.Components;
|
||||
using Content.Shared.Pinpointer;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Map.Components;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Content.Client.Atmos.Consoles;
|
||||
|
||||
public sealed partial class AtmosMonitoringConsoleNavMapControl : NavMapControl
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
|
||||
public bool ShowPipeNetwork = true;
|
||||
public int? FocusNetId = null;
|
||||
|
||||
private const int ChunkSize = 4;
|
||||
|
||||
private readonly Color _basePipeNetColor = Color.LightGray;
|
||||
private readonly Color _unfocusedPipeNetColor = Color.DimGray;
|
||||
|
||||
private List<AtmosMonitoringConsoleLine> _atmosPipeNetwork = new();
|
||||
private Dictionary<Color, Color> _sRGBLookUp = new Dictionary<Color, Color>();
|
||||
|
||||
// Look up tables for merging continuous lines. Indexed by line color
|
||||
private Dictionary<Color, Dictionary<Vector2i, Vector2i>> _horizLines = new();
|
||||
private Dictionary<Color, Dictionary<Vector2i, Vector2i>> _horizLinesReversed = new();
|
||||
private Dictionary<Color, Dictionary<Vector2i, Vector2i>> _vertLines = new();
|
||||
private Dictionary<Color, Dictionary<Vector2i, Vector2i>> _vertLinesReversed = new();
|
||||
|
||||
public AtmosMonitoringConsoleNavMapControl() : base()
|
||||
{
|
||||
PostWallDrawingAction += DrawAllPipeNetworks;
|
||||
}
|
||||
|
||||
protected override void UpdateNavMap()
|
||||
{
|
||||
base.UpdateNavMap();
|
||||
|
||||
if (!_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(Owner, out var console))
|
||||
return;
|
||||
|
||||
if (!_entManager.TryGetComponent<MapGridComponent>(MapUid, out var grid))
|
||||
return;
|
||||
|
||||
_atmosPipeNetwork = GetDecodedAtmosPipeChunks(console.AtmosPipeChunks, grid);
|
||||
}
|
||||
|
||||
private void DrawAllPipeNetworks(DrawingHandleScreen handle)
|
||||
{
|
||||
if (!ShowPipeNetwork)
|
||||
return;
|
||||
|
||||
// Draw networks
|
||||
if (_atmosPipeNetwork != null && _atmosPipeNetwork.Any())
|
||||
DrawPipeNetwork(handle, _atmosPipeNetwork);
|
||||
}
|
||||
|
||||
private void DrawPipeNetwork(DrawingHandleScreen handle, List<AtmosMonitoringConsoleLine> atmosPipeNetwork)
|
||||
{
|
||||
var offset = GetOffset();
|
||||
offset = offset with { Y = -offset.Y };
|
||||
|
||||
if (WorldRange / WorldMaxRange > 0.5f)
|
||||
{
|
||||
var pipeNetworks = new Dictionary<Color, ValueList<Vector2>>();
|
||||
|
||||
foreach (var chunkedLine in atmosPipeNetwork)
|
||||
{
|
||||
var start = ScalePosition(chunkedLine.Origin - offset);
|
||||
var end = ScalePosition(chunkedLine.Terminus - offset);
|
||||
|
||||
if (!pipeNetworks.TryGetValue(chunkedLine.Color, out var subNetwork))
|
||||
subNetwork = new ValueList<Vector2>();
|
||||
|
||||
subNetwork.Add(start);
|
||||
subNetwork.Add(end);
|
||||
|
||||
pipeNetworks[chunkedLine.Color] = subNetwork;
|
||||
}
|
||||
|
||||
foreach ((var color, var subNetwork) in pipeNetworks)
|
||||
{
|
||||
if (subNetwork.Count > 0)
|
||||
handle.DrawPrimitives(DrawPrimitiveTopology.LineList, subNetwork.Span, color);
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
var pipeVertexUVs = new Dictionary<Color, ValueList<Vector2>>();
|
||||
|
||||
foreach (var chunkedLine in atmosPipeNetwork)
|
||||
{
|
||||
var leftTop = ScalePosition(new Vector2
|
||||
(Math.Min(chunkedLine.Origin.X, chunkedLine.Terminus.X) - 0.1f,
|
||||
Math.Min(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) - 0.1f)
|
||||
- offset);
|
||||
|
||||
var rightTop = ScalePosition(new Vector2
|
||||
(Math.Max(chunkedLine.Origin.X, chunkedLine.Terminus.X) + 0.1f,
|
||||
Math.Min(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) - 0.1f)
|
||||
- offset);
|
||||
|
||||
var leftBottom = ScalePosition(new Vector2
|
||||
(Math.Min(chunkedLine.Origin.X, chunkedLine.Terminus.X) - 0.1f,
|
||||
Math.Max(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) + 0.1f)
|
||||
- offset);
|
||||
|
||||
var rightBottom = ScalePosition(new Vector2
|
||||
(Math.Max(chunkedLine.Origin.X, chunkedLine.Terminus.X) + 0.1f,
|
||||
Math.Max(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) + 0.1f)
|
||||
- offset);
|
||||
|
||||
if (!pipeVertexUVs.TryGetValue(chunkedLine.Color, out var pipeVertexUV))
|
||||
pipeVertexUV = new ValueList<Vector2>();
|
||||
|
||||
pipeVertexUV.Add(leftBottom);
|
||||
pipeVertexUV.Add(leftTop);
|
||||
pipeVertexUV.Add(rightBottom);
|
||||
pipeVertexUV.Add(leftTop);
|
||||
pipeVertexUV.Add(rightBottom);
|
||||
pipeVertexUV.Add(rightTop);
|
||||
|
||||
pipeVertexUVs[chunkedLine.Color] = pipeVertexUV;
|
||||
}
|
||||
|
||||
foreach ((var color, var pipeVertexUV) in pipeVertexUVs)
|
||||
{
|
||||
if (pipeVertexUV.Count > 0)
|
||||
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleList, pipeVertexUV.Span, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<AtmosMonitoringConsoleLine> GetDecodedAtmosPipeChunks(Dictionary<Vector2i, AtmosPipeChunk>? chunks, MapGridComponent? grid)
|
||||
{
|
||||
var decodedOutput = new List<AtmosMonitoringConsoleLine>();
|
||||
|
||||
if (chunks == null || grid == null)
|
||||
return decodedOutput;
|
||||
|
||||
// Clear stale look up table values
|
||||
_horizLines.Clear();
|
||||
_horizLinesReversed.Clear();
|
||||
_vertLines.Clear();
|
||||
_vertLinesReversed.Clear();
|
||||
|
||||
// Generate masks
|
||||
var northMask = (ulong)1 << 0;
|
||||
var southMask = (ulong)1 << 1;
|
||||
var westMask = (ulong)1 << 2;
|
||||
var eastMask = (ulong)1 << 3;
|
||||
|
||||
foreach ((var chunkOrigin, var chunk) in chunks)
|
||||
{
|
||||
var list = new List<AtmosMonitoringConsoleLine>();
|
||||
|
||||
foreach (var ((netId, hexColor), atmosPipeData) in chunk.AtmosPipeData)
|
||||
{
|
||||
// Determine the correct coloration for the pipe
|
||||
var color = Color.FromHex(hexColor) * _basePipeNetColor;
|
||||
|
||||
if (FocusNetId != null && FocusNetId != netId)
|
||||
color *= _unfocusedPipeNetColor;
|
||||
|
||||
// Get the associated line look up tables
|
||||
if (!_horizLines.TryGetValue(color, out var horizLines))
|
||||
{
|
||||
horizLines = new();
|
||||
_horizLines[color] = horizLines;
|
||||
}
|
||||
|
||||
if (!_horizLinesReversed.TryGetValue(color, out var horizLinesReversed))
|
||||
{
|
||||
horizLinesReversed = new();
|
||||
_horizLinesReversed[color] = horizLinesReversed;
|
||||
}
|
||||
|
||||
if (!_vertLines.TryGetValue(color, out var vertLines))
|
||||
{
|
||||
vertLines = new();
|
||||
_vertLines[color] = vertLines;
|
||||
}
|
||||
|
||||
if (!_vertLinesReversed.TryGetValue(color, out var vertLinesReversed))
|
||||
{
|
||||
vertLinesReversed = new();
|
||||
_vertLinesReversed[color] = vertLinesReversed;
|
||||
}
|
||||
|
||||
// Loop over the chunk
|
||||
for (var tileIdx = 0; tileIdx < ChunkSize * ChunkSize; tileIdx++)
|
||||
{
|
||||
if (atmosPipeData == 0)
|
||||
continue;
|
||||
|
||||
var mask = (ulong)SharedNavMapSystem.AllDirMask << tileIdx * SharedNavMapSystem.Directions;
|
||||
|
||||
if ((atmosPipeData & mask) == 0)
|
||||
continue;
|
||||
|
||||
var relativeTile = GetTileFromIndex(tileIdx);
|
||||
var tile = (chunk.Origin * ChunkSize + relativeTile) * grid.TileSize;
|
||||
tile = tile with { Y = -tile.Y };
|
||||
|
||||
// Calculate the draw point offsets
|
||||
var vertLineOrigin = (atmosPipeData & northMask << tileIdx * SharedNavMapSystem.Directions) > 0 ?
|
||||
new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 1f) : new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 0.5f);
|
||||
|
||||
var vertLineTerminus = (atmosPipeData & southMask << tileIdx * SharedNavMapSystem.Directions) > 0 ?
|
||||
new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 0f) : new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 0.5f);
|
||||
|
||||
var horizLineOrigin = (atmosPipeData & eastMask << tileIdx * SharedNavMapSystem.Directions) > 0 ?
|
||||
new Vector2(grid.TileSize * 1f, -grid.TileSize * 0.5f) : new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 0.5f);
|
||||
|
||||
var horizLineTerminus = (atmosPipeData & westMask << tileIdx * SharedNavMapSystem.Directions) > 0 ?
|
||||
new Vector2(grid.TileSize * 0f, -grid.TileSize * 0.5f) : new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 0.5f);
|
||||
|
||||
// Since we can have pipe lines that have a length of a half tile,
|
||||
// double the vectors and convert to vector2i so we can merge them
|
||||
AddOrUpdateNavMapLine(ConvertVector2ToVector2i(tile + horizLineOrigin, 2), ConvertVector2ToVector2i(tile + horizLineTerminus, 2), horizLines, horizLinesReversed);
|
||||
AddOrUpdateNavMapLine(ConvertVector2ToVector2i(tile + vertLineOrigin, 2), ConvertVector2ToVector2i(tile + vertLineTerminus, 2), vertLines, vertLinesReversed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scale the vector2is back down and convert to vector2
|
||||
foreach (var (color, horizLines) in _horizLines)
|
||||
{
|
||||
// Get the corresponding sRBG color
|
||||
var sRGB = GetsRGBColor(color);
|
||||
|
||||
foreach (var (origin, terminal) in horizLines)
|
||||
decodedOutput.Add(new AtmosMonitoringConsoleLine
|
||||
(ConvertVector2iToVector2(origin, 0.5f), ConvertVector2iToVector2(terminal, 0.5f), sRGB));
|
||||
}
|
||||
|
||||
foreach (var (color, vertLines) in _vertLines)
|
||||
{
|
||||
// Get the corresponding sRBG color
|
||||
var sRGB = GetsRGBColor(color);
|
||||
|
||||
foreach (var (origin, terminal) in vertLines)
|
||||
decodedOutput.Add(new AtmosMonitoringConsoleLine
|
||||
(ConvertVector2iToVector2(origin, 0.5f), ConvertVector2iToVector2(terminal, 0.5f), sRGB));
|
||||
}
|
||||
|
||||
return decodedOutput;
|
||||
}
|
||||
|
||||
private Vector2 ConvertVector2iToVector2(Vector2i vector, float scale = 1f)
|
||||
{
|
||||
return new Vector2(vector.X * scale, vector.Y * scale);
|
||||
}
|
||||
|
||||
private Vector2i ConvertVector2ToVector2i(Vector2 vector, float scale = 1f)
|
||||
{
|
||||
return new Vector2i((int)MathF.Round(vector.X * scale), (int)MathF.Round(vector.Y * scale));
|
||||
}
|
||||
|
||||
private Vector2i GetTileFromIndex(int index)
|
||||
{
|
||||
var x = index / ChunkSize;
|
||||
var y = index % ChunkSize;
|
||||
return new Vector2i(x, y);
|
||||
}
|
||||
|
||||
private Color GetsRGBColor(Color color)
|
||||
{
|
||||
if (!_sRGBLookUp.TryGetValue(color, out var sRGB))
|
||||
{
|
||||
sRGB = Color.ToSrgb(color);
|
||||
_sRGBLookUp[color] = sRGB;
|
||||
}
|
||||
|
||||
return sRGB;
|
||||
}
|
||||
}
|
||||
|
||||
public struct AtmosMonitoringConsoleLine
|
||||
{
|
||||
public readonly Vector2 Origin;
|
||||
public readonly Vector2 Terminus;
|
||||
public readonly Color Color;
|
||||
|
||||
public AtmosMonitoringConsoleLine(Vector2 origin, Vector2 terminus, Color color)
|
||||
{
|
||||
Origin = origin;
|
||||
Terminus = terminus;
|
||||
Color = color;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
using Content.Shared.Atmos.Components;
|
||||
using Content.Shared.Atmos.Consoles;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client.Atmos.Consoles;
|
||||
|
||||
public sealed class AtmosMonitoringConsoleSystem : SharedAtmosMonitoringConsoleSystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<AtmosMonitoringConsoleComponent, ComponentHandleState>(OnHandleState);
|
||||
}
|
||||
|
||||
private void OnHandleState(EntityUid uid, AtmosMonitoringConsoleComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
Dictionary<Vector2i, Dictionary<(int, string), ulong>> modifiedChunks;
|
||||
Dictionary<NetEntity, AtmosDeviceNavMapData> atmosDevices;
|
||||
|
||||
switch (args.Current)
|
||||
{
|
||||
case AtmosMonitoringConsoleDeltaState delta:
|
||||
{
|
||||
modifiedChunks = delta.ModifiedChunks;
|
||||
atmosDevices = delta.AtmosDevices;
|
||||
|
||||
foreach (var index in component.AtmosPipeChunks.Keys)
|
||||
{
|
||||
if (!delta.AllChunks!.Contains(index))
|
||||
component.AtmosPipeChunks.Remove(index);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case AtmosMonitoringConsoleState state:
|
||||
{
|
||||
modifiedChunks = state.Chunks;
|
||||
atmosDevices = state.AtmosDevices;
|
||||
|
||||
foreach (var index in component.AtmosPipeChunks.Keys)
|
||||
{
|
||||
if (!state.Chunks.ContainsKey(index))
|
||||
component.AtmosPipeChunks.Remove(index);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (origin, chunk) in modifiedChunks)
|
||||
{
|
||||
var newChunk = new AtmosPipeChunk(origin);
|
||||
newChunk.AtmosPipeData = new Dictionary<(int, string), ulong>(chunk);
|
||||
|
||||
component.AtmosPipeChunks[origin] = newChunk;
|
||||
}
|
||||
|
||||
component.AtmosDevices.Clear();
|
||||
|
||||
foreach (var (nuid, atmosDevice) in atmosDevices)
|
||||
{
|
||||
component.AtmosDevices[nuid] = atmosDevice;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:ui="clr-namespace:Content.Client.Atmos.Consoles"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||
Title="{Loc 'atmos-monitoring-window-title'}"
|
||||
Resizable="False"
|
||||
SetSize="1120 750"
|
||||
MinSize="1120 750">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<!-- Main display -->
|
||||
<BoxContainer Orientation="Horizontal" VerticalExpand="True" HorizontalExpand="True">
|
||||
<!-- Nav map -->
|
||||
<BoxContainer Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True">
|
||||
<ui:AtmosMonitoringConsoleNavMapControl Name="NavMap" Margin="5 5" VerticalExpand="True" HorizontalExpand="True">
|
||||
|
||||
<!-- System warning -->
|
||||
<PanelContainer Name="SystemWarningPanel"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Top"
|
||||
HorizontalExpand="True"
|
||||
Margin="0 48 0 0"
|
||||
Visible="False">
|
||||
<RichTextLabel Name="SystemWarningLabel" Margin="12 8 12 8"/>
|
||||
</PanelContainer>
|
||||
|
||||
</ui:AtmosMonitoringConsoleNavMapControl>
|
||||
|
||||
<!-- Nav map legend -->
|
||||
<BoxContainer Orientation="Horizontal" Margin="0 10 0 10">
|
||||
<TextureRect Stretch="KeepAspectCentered"
|
||||
TexturePath="/Textures/Interface/NavMap/beveled_square.png"
|
||||
Modulate="#a9a9a9"
|
||||
SetSize="16 16"
|
||||
Margin="20 0 5 0"/>
|
||||
<Label Text="{Loc 'atmos-monitoring-window-label-gas-opening'}"/>
|
||||
<TextureRect Stretch="KeepAspectCentered"
|
||||
TexturePath="/Textures/Interface/NavMap/beveled_circle.png"
|
||||
SetSize="16 16"
|
||||
Modulate="#a9a9a9"
|
||||
Margin="20 0 5 0"/>
|
||||
<Label Text="{Loc 'atmos-monitoring-window-label-gas-scrubber'}"/>
|
||||
<TextureRect Stretch="KeepAspectCentered"
|
||||
TexturePath="/Textures/Interface/NavMap/beveled_arrow_east.png"
|
||||
SetSize="16 16"
|
||||
Modulate="#a9a9a9"
|
||||
Margin="20 0 5 0"/>
|
||||
<Label Text="{Loc 'atmos-monitoring-window-label-gas-flow-regulator'}"/>
|
||||
<TextureRect Stretch="KeepAspectCentered"
|
||||
TexturePath="/Textures/Interface/NavMap/beveled_hexagon.png"
|
||||
SetSize="16 16"
|
||||
Modulate="#a9a9a9"
|
||||
Margin="20 0 5 0"/>
|
||||
<Label Text="{Loc 'atmos-monitoring-window-label-thermoregulator'}"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
||||
<!-- Atmosphere status -->
|
||||
<BoxContainer Orientation="Vertical" VerticalExpand="True" SetWidth="440" Margin="0 0 10 10">
|
||||
|
||||
<!-- Station name -->
|
||||
<controls:StripeBack>
|
||||
<PanelContainer>
|
||||
<RichTextLabel Name="StationName" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0 5 0 3"/>
|
||||
</PanelContainer>
|
||||
</controls:StripeBack>
|
||||
|
||||
<!-- Alarm status (entries added by C# code) -->
|
||||
<TabContainer Name="MasterTabContainer" VerticalExpand="True" HorizontalExpand="True" Margin="0 10 0 0">
|
||||
<ScrollContainer HorizontalExpand="True" Margin="8, 8, 8, 8">
|
||||
<BoxContainer Name="AtmosNetworksTable" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True" Margin="0 0 0 10"/>
|
||||
</ScrollContainer>
|
||||
</TabContainer>
|
||||
|
||||
<!-- Overlay toggles -->
|
||||
<BoxContainer Orientation="Vertical" Margin="0 10 0 0">
|
||||
<Label Text="{Loc 'atmos-monitoring-window-toggle-overlays'}" Margin="0 0 0 5"/>
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<CheckBox Name="ShowPipeNetwork" Text="{Loc 'atmos-monitoring-window-show-pipe-network'}" Pressed="True" HorizontalExpand="True"/>
|
||||
<CheckBox Name="ShowGasPipeSensors" Text="{Loc 'atmos-monitoring-window-show-gas-pipe-sensors'}" Pressed="False" HorizontalExpand="True"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
||||
</BoxContainer>
|
||||
|
||||
</BoxContainer>
|
||||
|
||||
<!-- Footer -->
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<PanelContainer StyleClasses="LowDivider" />
|
||||
<BoxContainer Orientation="Horizontal" Margin="10 2 5 0" VerticalAlignment="Bottom">
|
||||
<Label Text="{Loc 'atmos-monitoring-window-flavor-left'}" StyleClasses="WindowFooterText" />
|
||||
<Label Text="{Loc 'atmos-monitoring-window-flavor-right'}" StyleClasses="WindowFooterText"
|
||||
HorizontalAlignment="Right" HorizontalExpand="True" Margin="0 0 5 0" />
|
||||
<TextureRect StyleClasses="NTLogoDark" Stretch="KeepAspectCentered"
|
||||
VerticalAlignment="Center" HorizontalAlignment="Right" SetSize="19 19"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</controls:FancyWindow>
|
||||
@@ -0,0 +1,455 @@
|
||||
using Content.Client.Pinpointer.UI;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Atmos.Components;
|
||||
using Content.Shared.Prototypes;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Client.Atmos.Consoles;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AtmosMonitoringConsoleWindow : FancyWindow
|
||||
{
|
||||
private readonly IEntityManager _entManager;
|
||||
private readonly IPrototypeManager _protoManager;
|
||||
private readonly SpriteSystem _spriteSystem;
|
||||
|
||||
private EntityUid? _owner;
|
||||
private NetEntity? _focusEntity;
|
||||
private int? _focusNetId;
|
||||
|
||||
private bool _autoScrollActive = false;
|
||||
|
||||
private readonly Color _unfocusedDeviceColor = Color.DimGray;
|
||||
private ProtoId<NavMapBlipPrototype> _navMapConsoleProtoId = "NavMapConsole";
|
||||
private ProtoId<NavMapBlipPrototype> _gasPipeSensorProtoId = "GasPipeSensor";
|
||||
|
||||
public AtmosMonitoringConsoleWindow(AtmosMonitoringConsoleBoundUserInterface userInterface, EntityUid? owner)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
_entManager = IoCManager.Resolve<IEntityManager>();
|
||||
_protoManager = IoCManager.Resolve<IPrototypeManager>();
|
||||
_spriteSystem = _entManager.System<SpriteSystem>();
|
||||
|
||||
// Pass the owner to nav map
|
||||
_owner = owner;
|
||||
NavMap.Owner = _owner;
|
||||
|
||||
// Set nav map grid uid
|
||||
var stationName = Loc.GetString("atmos-monitoring-window-unknown-location");
|
||||
EntityCoordinates? consoleCoords = null;
|
||||
|
||||
if (_entManager.TryGetComponent<TransformComponent>(owner, out var xform))
|
||||
{
|
||||
consoleCoords = xform.Coordinates;
|
||||
NavMap.MapUid = xform.GridUid;
|
||||
|
||||
// Assign station name
|
||||
if (_entManager.TryGetComponent<MetaDataComponent>(xform.GridUid, out var stationMetaData))
|
||||
stationName = stationMetaData.EntityName;
|
||||
|
||||
var msg = new FormattedMessage();
|
||||
msg.TryAddMarkup(Loc.GetString("atmos-monitoring-window-station-name", ("stationName", stationName)), out _);
|
||||
|
||||
StationName.SetMessage(msg);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
StationName.SetMessage(stationName);
|
||||
NavMap.Visible = false;
|
||||
}
|
||||
|
||||
// Set trackable entity selected action
|
||||
NavMap.TrackedEntitySelectedAction += SetTrackedEntityFromNavMap;
|
||||
|
||||
// Update nav map
|
||||
NavMap.ForceNavMapUpdate();
|
||||
|
||||
// Set tab container headers
|
||||
MasterTabContainer.SetTabTitle(0, Loc.GetString("atmos-monitoring-window-tab-networks"));
|
||||
|
||||
// Set UI toggles
|
||||
ShowPipeNetwork.OnToggled += _ => OnShowPipeNetworkToggled();
|
||||
ShowGasPipeSensors.OnToggled += _ => OnShowGasPipeSensors();
|
||||
|
||||
// Set nav map colors
|
||||
if (!_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(_owner, out var console))
|
||||
return;
|
||||
|
||||
NavMap.TileColor = console.NavMapTileColor;
|
||||
NavMap.WallColor = console.NavMapWallColor;
|
||||
|
||||
// Initalize
|
||||
UpdateUI(consoleCoords, Array.Empty<AtmosMonitoringConsoleEntry>());
|
||||
}
|
||||
|
||||
#region Toggle handling
|
||||
|
||||
private void OnShowPipeNetworkToggled()
|
||||
{
|
||||
if (_owner == null)
|
||||
return;
|
||||
|
||||
if (!_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(_owner.Value, out var console))
|
||||
return;
|
||||
|
||||
NavMap.ShowPipeNetwork = ShowPipeNetwork.Pressed;
|
||||
|
||||
foreach (var (netEnt, device) in console.AtmosDevices)
|
||||
{
|
||||
if (device.NavMapBlip == _gasPipeSensorProtoId)
|
||||
continue;
|
||||
|
||||
if (ShowPipeNetwork.Pressed)
|
||||
AddTrackedEntityToNavMap(device);
|
||||
|
||||
else
|
||||
NavMap.TrackedEntities.Remove(netEnt);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnShowGasPipeSensors()
|
||||
{
|
||||
if (_owner == null)
|
||||
return;
|
||||
|
||||
if (!_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(_owner.Value, out var console))
|
||||
return;
|
||||
|
||||
foreach (var (netEnt, device) in console.AtmosDevices)
|
||||
{
|
||||
if (device.NavMapBlip != _gasPipeSensorProtoId)
|
||||
continue;
|
||||
|
||||
if (ShowGasPipeSensors.Pressed)
|
||||
AddTrackedEntityToNavMap(device, true);
|
||||
|
||||
else
|
||||
NavMap.TrackedEntities.Remove(netEnt);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void UpdateUI
|
||||
(EntityCoordinates? consoleCoords,
|
||||
AtmosMonitoringConsoleEntry[] atmosNetworks)
|
||||
{
|
||||
if (_owner == null)
|
||||
return;
|
||||
|
||||
if (!_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(_owner.Value, out var console))
|
||||
return;
|
||||
|
||||
// Reset nav map values
|
||||
NavMap.TrackedCoordinates.Clear();
|
||||
NavMap.TrackedEntities.Clear();
|
||||
|
||||
if (_focusEntity != null && !console.AtmosDevices.Any(x => x.Key == _focusEntity))
|
||||
ClearFocus();
|
||||
|
||||
// Add tracked entities to the nav map
|
||||
UpdateNavMapBlips();
|
||||
|
||||
// Show the monitor location
|
||||
var consoleNetEnt = _entManager.GetNetEntity(_owner);
|
||||
|
||||
if (consoleCoords != null && consoleNetEnt != null)
|
||||
{
|
||||
var proto = _protoManager.Index(_navMapConsoleProtoId);
|
||||
|
||||
if (proto.TexturePaths != null && proto.TexturePaths.Length != 0)
|
||||
{
|
||||
var texture = _spriteSystem.Frame0(new SpriteSpecifier.Texture(proto.TexturePaths[0]));
|
||||
var blip = new NavMapBlip(consoleCoords.Value, texture, proto.Color, proto.Blinks, proto.Selectable);
|
||||
NavMap.TrackedEntities[consoleNetEnt.Value] = blip;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the nav map
|
||||
NavMap.ForceNavMapUpdate();
|
||||
|
||||
// Clear excess children from the tables
|
||||
while (AtmosNetworksTable.ChildCount > atmosNetworks.Length)
|
||||
AtmosNetworksTable.RemoveChild(AtmosNetworksTable.GetChild(AtmosNetworksTable.ChildCount - 1));
|
||||
|
||||
// Update all entries in each table
|
||||
for (int index = 0; index < atmosNetworks.Length; index++)
|
||||
{
|
||||
var entry = atmosNetworks.ElementAt(index);
|
||||
UpdateUIEntry(entry, index, AtmosNetworksTable, console);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateNavMapBlips()
|
||||
{
|
||||
if (_owner == null || !_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(_owner.Value, out var console))
|
||||
return;
|
||||
|
||||
if (NavMap.Visible)
|
||||
{
|
||||
foreach (var (netEnt, device) in console.AtmosDevices)
|
||||
{
|
||||
// Update the focus network ID, incase it has changed
|
||||
if (_focusEntity == netEnt)
|
||||
{
|
||||
_focusNetId = device.NetId;
|
||||
NavMap.FocusNetId = _focusNetId;
|
||||
}
|
||||
|
||||
var isSensor = device.NavMapBlip == _gasPipeSensorProtoId;
|
||||
|
||||
// Skip network devices if the toggled is off
|
||||
if (!ShowPipeNetwork.Pressed && !isSensor)
|
||||
continue;
|
||||
|
||||
// Skip gas pipe sensors if the toggle is off
|
||||
if (!ShowGasPipeSensors.Pressed && isSensor)
|
||||
continue;
|
||||
|
||||
AddTrackedEntityToNavMap(device, isSensor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddTrackedEntityToNavMap(AtmosDeviceNavMapData metaData, bool isSensor = false)
|
||||
{
|
||||
var proto = _protoManager.Index(metaData.NavMapBlip);
|
||||
|
||||
if (proto.TexturePaths == null || proto.TexturePaths.Length == 0)
|
||||
return;
|
||||
|
||||
var idx = Math.Clamp((int)metaData.Direction / 2, 0, proto.TexturePaths.Length - 1);
|
||||
var texture = proto.TexturePaths.Length > 0 ? proto.TexturePaths[idx] : proto.TexturePaths[0];
|
||||
var color = isSensor ? proto.Color : proto.Color * metaData.PipeColor;
|
||||
|
||||
if (_focusNetId != null && metaData.NetId != _focusNetId)
|
||||
color *= _unfocusedDeviceColor;
|
||||
|
||||
var blinks = proto.Blinks || _focusEntity == metaData.NetEntity;
|
||||
var coords = _entManager.GetCoordinates(metaData.NetCoordinates);
|
||||
var blip = new NavMapBlip(coords, _spriteSystem.Frame0(new SpriteSpecifier.Texture(texture)), color, blinks, proto.Selectable, proto.Scale);
|
||||
NavMap.TrackedEntities[metaData.NetEntity] = blip;
|
||||
}
|
||||
|
||||
private void UpdateUIEntry(AtmosMonitoringConsoleEntry data, int index, Control table, AtmosMonitoringConsoleComponent console)
|
||||
{
|
||||
// Make new UI entry if required
|
||||
if (index >= table.ChildCount)
|
||||
{
|
||||
var newEntryContainer = new AtmosMonitoringEntryContainer(data);
|
||||
|
||||
// On click
|
||||
newEntryContainer.FocusButton.OnButtonUp += args =>
|
||||
{
|
||||
if (_focusEntity == newEntryContainer.Data.NetEntity)
|
||||
{
|
||||
ClearFocus();
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
SetFocus(newEntryContainer.Data.NetEntity, newEntryContainer.Data.NetId);
|
||||
|
||||
var coords = _entManager.GetCoordinates(newEntryContainer.Data.Coordinates);
|
||||
NavMap.CenterToCoordinates(coords);
|
||||
}
|
||||
|
||||
// Update affected UI elements across all tables
|
||||
UpdateConsoleTable(console, AtmosNetworksTable, _focusEntity);
|
||||
};
|
||||
|
||||
// Add the entry to the current table
|
||||
table.AddChild(newEntryContainer);
|
||||
}
|
||||
|
||||
// Update values and UI elements
|
||||
var tableChild = table.GetChild(index);
|
||||
|
||||
if (tableChild is not AtmosMonitoringEntryContainer)
|
||||
{
|
||||
table.RemoveChild(tableChild);
|
||||
UpdateUIEntry(data, index, table, console);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var entryContainer = (AtmosMonitoringEntryContainer)tableChild;
|
||||
entryContainer.UpdateEntry(data, data.NetEntity == _focusEntity);
|
||||
}
|
||||
|
||||
private void UpdateConsoleTable(AtmosMonitoringConsoleComponent console, Control table, NetEntity? currTrackedEntity)
|
||||
{
|
||||
foreach (var tableChild in table.Children)
|
||||
{
|
||||
if (tableChild is not AtmosAlarmEntryContainer)
|
||||
continue;
|
||||
|
||||
var entryContainer = (AtmosAlarmEntryContainer)tableChild;
|
||||
|
||||
if (entryContainer.NetEntity != currTrackedEntity)
|
||||
entryContainer.RemoveAsFocus();
|
||||
|
||||
else if (entryContainer.NetEntity == currTrackedEntity)
|
||||
entryContainer.SetAsFocus();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetTrackedEntityFromNavMap(NetEntity? focusEntity)
|
||||
{
|
||||
if (focusEntity == null)
|
||||
return;
|
||||
|
||||
if (!_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(_owner, out var console))
|
||||
return;
|
||||
|
||||
foreach (var (netEnt, device) in console.AtmosDevices)
|
||||
{
|
||||
if (netEnt != focusEntity)
|
||||
continue;
|
||||
|
||||
if (device.NavMapBlip != _gasPipeSensorProtoId)
|
||||
return;
|
||||
|
||||
// Set new focus
|
||||
SetFocus(focusEntity.Value, device.NetId);
|
||||
|
||||
// Get the scroll position of the selected entity on the selected button the UI
|
||||
ActivateAutoScrollToFocus();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
AutoScrollToFocus();
|
||||
}
|
||||
|
||||
private void ActivateAutoScrollToFocus()
|
||||
{
|
||||
_autoScrollActive = true;
|
||||
}
|
||||
|
||||
private void AutoScrollToFocus()
|
||||
{
|
||||
if (!_autoScrollActive)
|
||||
return;
|
||||
|
||||
var scroll = AtmosNetworksTable.Parent as ScrollContainer;
|
||||
if (scroll == null)
|
||||
return;
|
||||
|
||||
if (!TryGetVerticalScrollbar(scroll, out var vScrollbar))
|
||||
return;
|
||||
|
||||
if (!TryGetNextScrollPosition(out float? nextScrollPosition))
|
||||
return;
|
||||
|
||||
vScrollbar.ValueTarget = nextScrollPosition.Value;
|
||||
|
||||
if (MathHelper.CloseToPercent(vScrollbar.Value, vScrollbar.ValueTarget))
|
||||
_autoScrollActive = false;
|
||||
}
|
||||
|
||||
private bool TryGetVerticalScrollbar(ScrollContainer scroll, [NotNullWhen(true)] out VScrollBar? vScrollBar)
|
||||
{
|
||||
vScrollBar = null;
|
||||
|
||||
foreach (var control in scroll.Children)
|
||||
{
|
||||
if (control is not VScrollBar)
|
||||
continue;
|
||||
|
||||
vScrollBar = (VScrollBar)control;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryGetNextScrollPosition([NotNullWhen(true)] out float? nextScrollPosition)
|
||||
{
|
||||
nextScrollPosition = null;
|
||||
|
||||
var scroll = AtmosNetworksTable.Parent as ScrollContainer;
|
||||
if (scroll == null)
|
||||
return false;
|
||||
|
||||
var container = scroll.Children.ElementAt(0) as BoxContainer;
|
||||
if (container == null || container.Children.Count() == 0)
|
||||
return false;
|
||||
|
||||
// Exit if the heights of the children haven't been initialized yet
|
||||
if (!container.Children.Any(x => x.Height > 0))
|
||||
return false;
|
||||
|
||||
nextScrollPosition = 0;
|
||||
|
||||
foreach (var control in container.Children)
|
||||
{
|
||||
if (control is not AtmosMonitoringEntryContainer)
|
||||
continue;
|
||||
|
||||
var entry = (AtmosMonitoringEntryContainer)control;
|
||||
|
||||
if (entry.Data.NetEntity == _focusEntity)
|
||||
return true;
|
||||
|
||||
nextScrollPosition += control.Height;
|
||||
}
|
||||
|
||||
// Failed to find control
|
||||
nextScrollPosition = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SetFocus(NetEntity focusEntity, int focusNetId)
|
||||
{
|
||||
_focusEntity = focusEntity;
|
||||
_focusNetId = focusNetId;
|
||||
NavMap.FocusNetId = focusNetId;
|
||||
|
||||
OnFocusChanged();
|
||||
}
|
||||
|
||||
private void ClearFocus()
|
||||
{
|
||||
_focusEntity = null;
|
||||
_focusNetId = null;
|
||||
NavMap.FocusNetId = null;
|
||||
|
||||
OnFocusChanged();
|
||||
}
|
||||
|
||||
private void OnFocusChanged()
|
||||
{
|
||||
UpdateNavMapBlips();
|
||||
NavMap.ForceNavMapUpdate();
|
||||
|
||||
if (!_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(_owner, out var console))
|
||||
return;
|
||||
|
||||
for (int index = 0; index < AtmosNetworksTable.ChildCount; index++)
|
||||
{
|
||||
var entry = (AtmosMonitoringEntryContainer)AtmosNetworksTable.GetChild(index);
|
||||
|
||||
if (entry == null)
|
||||
continue;
|
||||
|
||||
UpdateUIEntry(entry.Data, index, AtmosNetworksTable, console);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<BoxContainer xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:s="clr-namespace:Content.Client.Stylesheets"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Orientation="Vertical" HorizontalExpand ="True" Margin="0 0 0 3">
|
||||
|
||||
<!-- Network selection button -->
|
||||
<Button Name="FocusButton" HorizontalExpand="True" VerticalExpand="True" Margin="0 0 6 8" StyleClasses="OpenLeft" Access="Public">
|
||||
<BoxContainer HorizontalExpand="True" VerticalExpand="True" Orientation="Vertical">
|
||||
<BoxContainer HorizontalExpand="True" VerticalExpand="True" Orientation="Horizontal" SetHeight="32">
|
||||
<PanelContainer Name="NetworkColorStripe" HorizontalAlignment="Left" SetWidth="8" VerticalExpand="True" Margin="-8 -2 0 0">
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#d7d7d7"/>
|
||||
</PanelContainer.PanelOverride>
|
||||
</PanelContainer>
|
||||
<Label Name="NetworkNameLabel" Text="???" HorizontalExpand="True" HorizontalAlignment="Center"/>
|
||||
</BoxContainer>
|
||||
|
||||
<!-- Panel that appears on selecting the device -->
|
||||
|
||||
<PanelContainer HorizontalExpand="True" Margin="-8 0 -14 -4" Access="Public">
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#25252a"/>
|
||||
</PanelContainer.PanelOverride>
|
||||
<BoxContainer Name="MainDataContainer" HorizontalExpand="True" VerticalExpand="True" Orientation="Vertical">
|
||||
<Control>
|
||||
<BoxContainer HorizontalExpand="True" VerticalExpand="True" Orientation="Vertical">
|
||||
<BoxContainer HorizontalExpand="True" Orientation="Horizontal">
|
||||
<Label Name="TemperatureHeaderLabel" Text="{Loc 'atmos-alerts-window-temperature-label'}" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#a9a9a9" Margin="0 2 0 0" SetHeight="24"></Label>
|
||||
<Label Name="PressureHeaderLabel" Text="{Loc 'atmos-alerts-window-pressure-label'}" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#a9a9a9" Margin="0 2 0 0" SetHeight="24"></Label>
|
||||
<Label Name="TotalMolHeaderLabel" Text="{Loc 'atmos-alerts-window-total-mol-label'}" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#a9a9a9" Margin="0 2 0 0" SetHeight="24"></Label>
|
||||
</BoxContainer>
|
||||
<PanelContainer HorizontalExpand="True">
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#202023"/>
|
||||
</PanelContainer.PanelOverride>
|
||||
<BoxContainer HorizontalExpand="True" Orientation="Horizontal">
|
||||
<Label Name="TemperatureLabel" Text="???" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#a9a9a9" Margin="0 2 0 0" SetHeight="24"></Label>
|
||||
<Label Name="PressureLabel" Text="???" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#a9a9a9" Margin="0 2 0 0" SetHeight="24"></Label>
|
||||
<Label Name="TotalMolLabel" Text="???" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#a9a9a9" Margin="0 2 0 0" SetHeight="24"></Label>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
<BoxContainer HorizontalExpand="True" Orientation="Horizontal" Margin="8 0">
|
||||
<TextureRect Name="ArrowTexture" VerticalAlignment="Center" SetSize="12 12" Stretch="KeepAspectCentered" Margin="3 0" TexturePath="/Textures/Interface/Nano/triangle_right.png"></TextureRect>
|
||||
<Label Name="GasesHeaderLabel" Text="{Loc 'atmos-monitoring-window-label-gases'}" HorizontalAlignment="Left" HorizontalExpand="True" FontColorOverride="#a9a9a9" Margin="4 0 0 0" SetHeight="24"></Label>
|
||||
</BoxContainer>
|
||||
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
|
||||
<!-- Atmosphere status -->
|
||||
<Control Name="FocusContainer" ReservesSpace="False" Visible="False">
|
||||
<!-- Main container for displaying atmospheric data -->
|
||||
<BoxContainer HorizontalExpand="True" VerticalExpand="True" Orientation="Vertical">
|
||||
<PanelContainer HorizontalExpand="True">
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#202023"/>
|
||||
</PanelContainer.PanelOverride>
|
||||
|
||||
<!-- Gas entries added via C# code -->
|
||||
<GridContainer Name="GasGridContainer" HorizontalExpand="True" Columns = "4"></GridContainer>
|
||||
</PanelContainer>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
</BoxContainer>
|
||||
|
||||
<!-- If the alarm is inactive, this is label is displayed instead -->
|
||||
<Label Name="NoDataLabel" Text="{Loc 'atmos-alerts-window-no-data-available'}" HorizontalAlignment="Center" Margin="0 15" FontColorOverride="#a9a9a9" ReservesSpace="False" Visible="False"></Label>
|
||||
|
||||
</PanelContainer>
|
||||
</BoxContainer>
|
||||
</Button>
|
||||
</BoxContainer>
|
||||
@@ -0,0 +1,166 @@
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Components;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Temperature;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Client.Atmos.Consoles;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AtmosMonitoringEntryContainer : BoxContainer
|
||||
{
|
||||
public AtmosMonitoringConsoleEntry Data;
|
||||
|
||||
private readonly IEntityManager _entManager;
|
||||
private readonly IResourceCache _cache;
|
||||
|
||||
public AtmosMonitoringEntryContainer(AtmosMonitoringConsoleEntry data)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
_entManager = IoCManager.Resolve<IEntityManager>();
|
||||
_cache = IoCManager.Resolve<IResourceCache>();
|
||||
|
||||
Data = data;
|
||||
|
||||
// Modulate colored stripe
|
||||
NetworkColorStripe.Modulate = data.Color;
|
||||
|
||||
// Load fonts
|
||||
var headerFont = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Bold.ttf"), 11);
|
||||
var normalFont = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSansDisplay/NotoSansDisplay-Regular.ttf"), 11);
|
||||
|
||||
// Set fonts
|
||||
TemperatureHeaderLabel.FontOverride = headerFont;
|
||||
PressureHeaderLabel.FontOverride = headerFont;
|
||||
TotalMolHeaderLabel.FontOverride = headerFont;
|
||||
GasesHeaderLabel.FontOverride = headerFont;
|
||||
|
||||
TemperatureLabel.FontOverride = normalFont;
|
||||
PressureLabel.FontOverride = normalFont;
|
||||
TotalMolLabel.FontOverride = normalFont;
|
||||
|
||||
NoDataLabel.FontOverride = headerFont;
|
||||
}
|
||||
|
||||
public void UpdateEntry(AtmosMonitoringConsoleEntry updatedData, bool isFocus)
|
||||
{
|
||||
// Load fonts
|
||||
var normalFont = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSansDisplay/NotoSansDisplay-Regular.ttf"), 11);
|
||||
|
||||
// Update name and values
|
||||
if (!string.IsNullOrEmpty(updatedData.Address))
|
||||
NetworkNameLabel.Text = Loc.GetString("atmos-alerts-window-alarm-label", ("name", updatedData.EntityName), ("address", updatedData.Address));
|
||||
|
||||
else
|
||||
NetworkNameLabel.Text = Loc.GetString(updatedData.EntityName);
|
||||
|
||||
Data = updatedData;
|
||||
|
||||
// Modulate colored stripe
|
||||
NetworkColorStripe.Modulate = Data.Color;
|
||||
|
||||
// Focus updates
|
||||
if (isFocus)
|
||||
SetAsFocus();
|
||||
else
|
||||
RemoveAsFocus();
|
||||
|
||||
// Check if powered
|
||||
if (!updatedData.IsPowered)
|
||||
{
|
||||
MainDataContainer.Visible = false;
|
||||
NoDataLabel.Visible = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Set container visibility
|
||||
MainDataContainer.Visible = true;
|
||||
NoDataLabel.Visible = false;
|
||||
|
||||
// Update temperature
|
||||
var isNotVacuum = updatedData.TotalMolData > 1e-6f;
|
||||
var tempK = (FixedPoint2)updatedData.TemperatureData;
|
||||
var tempC = (FixedPoint2)TemperatureHelpers.KelvinToCelsius(tempK.Float());
|
||||
|
||||
TemperatureLabel.Text = isNotVacuum ?
|
||||
Loc.GetString("atmos-alerts-window-temperature-value", ("valueInC", tempC), ("valueInK", tempK)) :
|
||||
Loc.GetString("atmos-alerts-window-invalid-value");
|
||||
|
||||
TemperatureLabel.FontColorOverride = isNotVacuum ? Color.DarkGray : StyleNano.DisabledFore;
|
||||
|
||||
// Update pressure
|
||||
PressureLabel.Text = Loc.GetString("atmos-alerts-window-pressure-value", ("value", (FixedPoint2)updatedData.PressureData));
|
||||
PressureLabel.FontColorOverride = isNotVacuum ? Color.DarkGray : StyleNano.DisabledFore;
|
||||
|
||||
// Update total mol
|
||||
TotalMolLabel.Text = Loc.GetString("atmos-alerts-window-total-mol-value", ("value", (FixedPoint2)updatedData.TotalMolData));
|
||||
TotalMolLabel.FontColorOverride = isNotVacuum ? Color.DarkGray : StyleNano.DisabledFore;
|
||||
|
||||
// Update other present gases
|
||||
GasGridContainer.RemoveAllChildren();
|
||||
|
||||
if (updatedData.GasData.Count() == 0)
|
||||
{
|
||||
// No gases
|
||||
var gasLabel = new Label()
|
||||
{
|
||||
Text = Loc.GetString("atmos-alerts-window-other-gases-value-nil"),
|
||||
FontOverride = normalFont,
|
||||
FontColorOverride = StyleNano.DisabledFore,
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
HorizontalExpand = true,
|
||||
Margin = new Thickness(0, 2, 0, 0),
|
||||
SetHeight = 24f,
|
||||
};
|
||||
|
||||
GasGridContainer.AddChild(gasLabel);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// Add an entry for each gas
|
||||
foreach (var (gas, percent) in updatedData.GasData)
|
||||
{
|
||||
var gasPercent = (FixedPoint2)0f;
|
||||
gasPercent = percent * 100f;
|
||||
|
||||
var gasAbbreviation = Atmospherics.GasAbbreviations.GetValueOrDefault(gas, Loc.GetString("gas-unknown-abbreviation"));
|
||||
|
||||
var gasLabel = new Label()
|
||||
{
|
||||
Text = Loc.GetString("atmos-alerts-window-other-gases-value", ("shorthand", gasAbbreviation), ("value", gasPercent)),
|
||||
FontOverride = normalFont,
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
HorizontalExpand = true,
|
||||
Margin = new Thickness(0, 2, 0, 0),
|
||||
SetHeight = 24f,
|
||||
};
|
||||
|
||||
GasGridContainer.AddChild(gasLabel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetAsFocus()
|
||||
{
|
||||
FocusButton.AddStyleClass(StyleNano.StyleClassButtonColorGreen);
|
||||
ArrowTexture.TexturePath = "/Textures/Interface/Nano/inverted_triangle.svg.png";
|
||||
FocusContainer.Visible = true;
|
||||
}
|
||||
|
||||
public void RemoveAsFocus()
|
||||
{
|
||||
FocusButton.RemoveStyleClass(StyleNano.StyleClassButtonColorGreen);
|
||||
ArrowTexture.TexturePath = "/Textures/Interface/Nano/triangle_right.png";
|
||||
FocusContainer.Visible = false;
|
||||
}
|
||||
}
|
||||
@@ -385,26 +385,6 @@ public partial class NavMapControl : MapGridControl
|
||||
if (PostWallDrawingAction != null)
|
||||
PostWallDrawingAction.Invoke(handle);
|
||||
|
||||
// Beacons
|
||||
if (_beacons.Pressed)
|
||||
{
|
||||
var rectBuffer = new Vector2(5f, 3f);
|
||||
|
||||
// Calculate font size for current zoom level
|
||||
var fontSize = (int) Math.Round(1 / WorldRange * DefaultDisplayedRange * UIScale * _targetFontsize, 0);
|
||||
var font = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Bold.ttf"), fontSize);
|
||||
|
||||
foreach (var beacon in _navMap.Beacons.Values)
|
||||
{
|
||||
var position = beacon.Position - offset;
|
||||
position = ScalePosition(position with { Y = -position.Y });
|
||||
|
||||
var textDimensions = handle.GetDimensions(font, beacon.Text, 1f);
|
||||
handle.DrawRect(new UIBox2(position - textDimensions / 2 - rectBuffer, position + textDimensions / 2 + rectBuffer), BackgroundColor);
|
||||
handle.DrawString(font, position - textDimensions / 2, beacon.Text, beacon.Color);
|
||||
}
|
||||
}
|
||||
|
||||
var curTime = Timing.RealTime;
|
||||
var blinkFrequency = 1f / 1f;
|
||||
var lit = curTime.TotalSeconds % blinkFrequency > blinkFrequency / 2f;
|
||||
@@ -443,11 +423,31 @@ public partial class NavMapControl : MapGridControl
|
||||
position = ScalePosition(new Vector2(position.X, -position.Y));
|
||||
|
||||
var scalingCoefficient = MinmapScaleModifier * float.Sqrt(MinimapScale);
|
||||
var positionOffset = new Vector2(scalingCoefficient * blip.Texture.Width, scalingCoefficient * blip.Texture.Height);
|
||||
var positionOffset = new Vector2(scalingCoefficient * blip.Scale * blip.Texture.Width, scalingCoefficient * blip.Scale * blip.Texture.Height);
|
||||
|
||||
handle.DrawTextureRect(blip.Texture, new UIBox2(position - positionOffset, position + positionOffset), blip.Color);
|
||||
}
|
||||
}
|
||||
|
||||
// Beacons
|
||||
if (_beacons.Pressed)
|
||||
{
|
||||
var rectBuffer = new Vector2(5f, 3f);
|
||||
|
||||
// Calculate font size for current zoom level
|
||||
var fontSize = (int)Math.Round(1 / WorldRange * DefaultDisplayedRange * UIScale * _targetFontsize, 0);
|
||||
var font = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Bold.ttf"), fontSize);
|
||||
|
||||
foreach (var beacon in _navMap.Beacons.Values)
|
||||
{
|
||||
var position = beacon.Position - offset;
|
||||
position = ScalePosition(position with { Y = -position.Y });
|
||||
|
||||
var textDimensions = handle.GetDimensions(font, beacon.Text, 1f);
|
||||
handle.DrawRect(new UIBox2(position - textDimensions / 2 - rectBuffer, position + textDimensions / 2 + rectBuffer), BackgroundColor);
|
||||
handle.DrawString(font, position - textDimensions / 2, beacon.Text, beacon.Color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
@@ -689,6 +689,9 @@ public partial class NavMapControl : MapGridControl
|
||||
Vector2i foundTermius;
|
||||
Vector2i foundOrigin;
|
||||
|
||||
if (origin == terminus)
|
||||
return;
|
||||
|
||||
// Does our new line end at the beginning of an existing line?
|
||||
if (lookup.Remove(terminus, out foundTermius))
|
||||
{
|
||||
@@ -739,13 +742,15 @@ public struct NavMapBlip
|
||||
public Color Color;
|
||||
public bool Blinks;
|
||||
public bool Selectable;
|
||||
public float Scale;
|
||||
|
||||
public NavMapBlip(EntityCoordinates coordinates, Texture texture, Color color, bool blinks, bool selectable = true)
|
||||
public NavMapBlip(EntityCoordinates coordinates, Texture texture, Color color, bool blinks, bool selectable = true, float scale = 1f)
|
||||
{
|
||||
Coordinates = coordinates;
|
||||
Texture = texture;
|
||||
Color = color;
|
||||
Blinks = blinks;
|
||||
Selectable = selectable;
|
||||
Scale = scale;
|
||||
}
|
||||
}
|
||||
|
||||
542
Content.Server/Atmos/Consoles/AtmosMonitoringConsoleSystem.cs
Normal file
@@ -0,0 +1,542 @@
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Atmos.Piping.Components;
|
||||
using Content.Server.DeviceNetwork.Components;
|
||||
using Content.Server.NodeContainer;
|
||||
using Content.Server.NodeContainer.EntitySystems;
|
||||
using Content.Server.NodeContainer.NodeGroups;
|
||||
using Content.Server.NodeContainer.Nodes;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Components;
|
||||
using Content.Shared.Atmos.Consoles;
|
||||
using Content.Shared.Labels.Components;
|
||||
using Content.Shared.Pinpointer;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Timing;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Server.Atmos.Consoles;
|
||||
|
||||
public sealed class AtmosMonitoringConsoleSystem : SharedAtmosMonitoringConsoleSystem
|
||||
{
|
||||
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
|
||||
[Dependency] private readonly SharedMapSystem _sharedMapSystem = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
// Private variables
|
||||
// Note: this data does not need to be saved
|
||||
private Dictionary<EntityUid, Dictionary<Vector2i, AtmosPipeChunk>> _gridAtmosPipeChunks = new();
|
||||
private float _updateTimer = 1.0f;
|
||||
|
||||
// Constants
|
||||
private const float UpdateTime = 1.0f;
|
||||
private const int ChunkSize = 4;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
// Console events
|
||||
SubscribeLocalEvent<AtmosMonitoringConsoleComponent, ComponentInit>(OnConsoleInit);
|
||||
SubscribeLocalEvent<AtmosMonitoringConsoleComponent, AnchorStateChangedEvent>(OnConsoleAnchorChanged);
|
||||
SubscribeLocalEvent<AtmosMonitoringConsoleComponent, EntParentChangedMessage>(OnConsoleParentChanged);
|
||||
|
||||
// Tracked device events
|
||||
SubscribeLocalEvent<AtmosMonitoringConsoleDeviceComponent, NodeGroupsRebuilt>(OnEntityNodeGroupsRebuilt);
|
||||
SubscribeLocalEvent<AtmosMonitoringConsoleDeviceComponent, AtmosPipeColorChangedEvent>(OnEntityPipeColorChanged);
|
||||
SubscribeLocalEvent<AtmosMonitoringConsoleDeviceComponent, EntityTerminatingEvent>(OnEntityShutdown);
|
||||
|
||||
// Grid events
|
||||
SubscribeLocalEvent<GridSplitEvent>(OnGridSplit);
|
||||
}
|
||||
|
||||
#region Event handling
|
||||
|
||||
private void OnConsoleInit(EntityUid uid, AtmosMonitoringConsoleComponent component, ComponentInit args)
|
||||
{
|
||||
InitializeAtmosMonitoringConsole(uid, component);
|
||||
}
|
||||
|
||||
private void OnConsoleAnchorChanged(EntityUid uid, AtmosMonitoringConsoleComponent component, AnchorStateChangedEvent args)
|
||||
{
|
||||
InitializeAtmosMonitoringConsole(uid, component);
|
||||
}
|
||||
|
||||
private void OnConsoleParentChanged(EntityUid uid, AtmosMonitoringConsoleComponent component, EntParentChangedMessage args)
|
||||
{
|
||||
component.ForceFullUpdate = true;
|
||||
InitializeAtmosMonitoringConsole(uid, component);
|
||||
}
|
||||
|
||||
private void OnEntityNodeGroupsRebuilt(EntityUid uid, AtmosMonitoringConsoleDeviceComponent component, NodeGroupsRebuilt args)
|
||||
{
|
||||
InitializeAtmosMonitoringDevice(uid, component);
|
||||
}
|
||||
|
||||
private void OnEntityPipeColorChanged(EntityUid uid, AtmosMonitoringConsoleDeviceComponent component, AtmosPipeColorChangedEvent args)
|
||||
{
|
||||
InitializeAtmosMonitoringDevice(uid, component);
|
||||
}
|
||||
|
||||
private void OnEntityShutdown(EntityUid uid, AtmosMonitoringConsoleDeviceComponent component, EntityTerminatingEvent args)
|
||||
{
|
||||
ShutDownAtmosMonitoringEntity(uid, component);
|
||||
}
|
||||
|
||||
private void OnGridSplit(ref GridSplitEvent args)
|
||||
{
|
||||
// Collect grids
|
||||
var allGrids = args.NewGrids.ToList();
|
||||
|
||||
if (!allGrids.Contains(args.Grid))
|
||||
allGrids.Add(args.Grid);
|
||||
|
||||
// Rebuild the pipe networks on the affected grids
|
||||
foreach (var ent in allGrids)
|
||||
{
|
||||
if (!TryComp<MapGridComponent>(ent, out var grid))
|
||||
continue;
|
||||
|
||||
RebuildAtmosPipeGrid(ent, grid);
|
||||
}
|
||||
|
||||
// Update atmos monitoring consoles that stand upon an updated grid
|
||||
var query = AllEntityQuery<AtmosMonitoringConsoleComponent, TransformComponent>();
|
||||
while (query.MoveNext(out var ent, out var entConsole, out var entXform))
|
||||
{
|
||||
if (entXform.GridUid == null)
|
||||
continue;
|
||||
|
||||
if (!allGrids.Contains(entXform.GridUid.Value))
|
||||
continue;
|
||||
|
||||
InitializeAtmosMonitoringConsole(ent, entConsole);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region UI updates
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
_updateTimer += frameTime;
|
||||
|
||||
if (_updateTimer >= UpdateTime)
|
||||
{
|
||||
_updateTimer -= UpdateTime;
|
||||
|
||||
var query = AllEntityQuery<AtmosMonitoringConsoleComponent, TransformComponent>();
|
||||
while (query.MoveNext(out var ent, out var entConsole, out var entXform))
|
||||
{
|
||||
if (entXform?.GridUid == null)
|
||||
continue;
|
||||
|
||||
UpdateUIState(ent, entConsole, entXform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateUIState
|
||||
(EntityUid uid,
|
||||
AtmosMonitoringConsoleComponent component,
|
||||
TransformComponent xform)
|
||||
{
|
||||
if (!_userInterfaceSystem.IsUiOpen(uid, AtmosMonitoringConsoleUiKey.Key))
|
||||
return;
|
||||
|
||||
var gridUid = xform.GridUid!.Value;
|
||||
|
||||
if (!TryComp<MapGridComponent>(gridUid, out var mapGrid))
|
||||
return;
|
||||
|
||||
if (!TryComp<GridAtmosphereComponent>(gridUid, out var atmosphere))
|
||||
return;
|
||||
|
||||
// The grid must have a NavMapComponent to visualize the map in the UI
|
||||
EnsureComp<NavMapComponent>(gridUid);
|
||||
|
||||
// Gathering data to be send to the client
|
||||
var atmosNetworks = new List<AtmosMonitoringConsoleEntry>();
|
||||
var query = AllEntityQuery<GasPipeSensorComponent, TransformComponent>();
|
||||
|
||||
while (query.MoveNext(out var ent, out var entSensor, out var entXform))
|
||||
{
|
||||
if (entXform?.GridUid != xform.GridUid)
|
||||
continue;
|
||||
|
||||
if (!entXform.Anchored)
|
||||
continue;
|
||||
|
||||
var entry = CreateAtmosMonitoringConsoleEntry(ent, entXform);
|
||||
|
||||
if (entry != null)
|
||||
atmosNetworks.Add(entry.Value);
|
||||
}
|
||||
|
||||
// Set the UI state
|
||||
_userInterfaceSystem.SetUiState(uid, AtmosMonitoringConsoleUiKey.Key,
|
||||
new AtmosMonitoringConsoleBoundInterfaceState(atmosNetworks.ToArray()));
|
||||
}
|
||||
|
||||
private AtmosMonitoringConsoleEntry? CreateAtmosMonitoringConsoleEntry(EntityUid uid, TransformComponent xform)
|
||||
{
|
||||
AtmosMonitoringConsoleEntry? entry = null;
|
||||
|
||||
var netEnt = GetNetEntity(uid);
|
||||
var name = MetaData(uid).EntityName;
|
||||
var address = string.Empty;
|
||||
|
||||
if (xform.GridUid == null)
|
||||
return null;
|
||||
|
||||
if (!TryGettingFirstPipeNode(uid, out var pipeNode, out var netId) ||
|
||||
pipeNode == null ||
|
||||
netId == null)
|
||||
return null;
|
||||
|
||||
var pipeColor = TryComp<AtmosPipeColorComponent>(uid, out var colorComponent) ? colorComponent.Color : Color.White;
|
||||
|
||||
// Name the entity based on its label, if available
|
||||
if (TryComp<LabelComponent>(uid, out var label) && label.CurrentLabel != null)
|
||||
name = label.CurrentLabel;
|
||||
|
||||
// Otherwise use its base name and network address
|
||||
else if (TryComp<DeviceNetworkComponent>(uid, out var deviceNet))
|
||||
address = deviceNet.Address;
|
||||
|
||||
// Entry for unpowered devices
|
||||
if (TryComp<ApcPowerReceiverComponent>(uid, out var apcPowerReceiver) && !apcPowerReceiver.Powered)
|
||||
{
|
||||
entry = new AtmosMonitoringConsoleEntry(netEnt, GetNetCoordinates(xform.Coordinates), netId.Value, name, address)
|
||||
{
|
||||
IsPowered = false,
|
||||
Color = pipeColor
|
||||
};
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
// Entry for powered devices
|
||||
var gasData = new Dictionary<Gas, float>();
|
||||
var isAirPresent = pipeNode.Air.TotalMoles > 0;
|
||||
|
||||
if (isAirPresent)
|
||||
{
|
||||
foreach (var gas in Enum.GetValues<Gas>())
|
||||
{
|
||||
if (pipeNode.Air[(int)gas] > 0)
|
||||
gasData.Add(gas, pipeNode.Air[(int)gas] / pipeNode.Air.TotalMoles);
|
||||
}
|
||||
}
|
||||
|
||||
entry = new AtmosMonitoringConsoleEntry(netEnt, GetNetCoordinates(xform.Coordinates), netId.Value, name, address)
|
||||
{
|
||||
TemperatureData = isAirPresent ? pipeNode.Air.Temperature : 0f,
|
||||
PressureData = pipeNode.Air.Pressure,
|
||||
TotalMolData = pipeNode.Air.TotalMoles,
|
||||
GasData = gasData,
|
||||
Color = pipeColor
|
||||
};
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
private Dictionary<NetEntity, AtmosDeviceNavMapData> GetAllAtmosDeviceNavMapData(EntityUid gridUid)
|
||||
{
|
||||
var atmosDeviceNavMapData = new Dictionary<NetEntity, AtmosDeviceNavMapData>();
|
||||
|
||||
var query = AllEntityQuery<AtmosMonitoringConsoleDeviceComponent, TransformComponent>();
|
||||
while (query.MoveNext(out var ent, out var entComponent, out var entXform))
|
||||
{
|
||||
if (TryGetAtmosDeviceNavMapData(ent, entComponent, entXform, gridUid, out var data))
|
||||
atmosDeviceNavMapData.Add(data.Value.NetEntity, data.Value);
|
||||
}
|
||||
|
||||
return atmosDeviceNavMapData;
|
||||
}
|
||||
|
||||
private bool TryGetAtmosDeviceNavMapData
|
||||
(EntityUid uid,
|
||||
AtmosMonitoringConsoleDeviceComponent component,
|
||||
TransformComponent xform,
|
||||
EntityUid gridUid,
|
||||
[NotNullWhen(true)] out AtmosDeviceNavMapData? device)
|
||||
{
|
||||
device = null;
|
||||
|
||||
if (component.NavMapBlip == null)
|
||||
return false;
|
||||
|
||||
if (xform.GridUid != gridUid)
|
||||
return false;
|
||||
|
||||
if (!xform.Anchored)
|
||||
return false;
|
||||
|
||||
var direction = xform.LocalRotation.GetCardinalDir();
|
||||
|
||||
if (!TryGettingFirstPipeNode(uid, out var _, out var netId))
|
||||
netId = -1;
|
||||
|
||||
var color = Color.White;
|
||||
|
||||
if (TryComp<AtmosPipeColorComponent>(uid, out var atmosPipeColor))
|
||||
color = atmosPipeColor.Color;
|
||||
|
||||
device = new AtmosDeviceNavMapData(GetNetEntity(uid), GetNetCoordinates(xform.Coordinates), netId.Value, component.NavMapBlip.Value, direction, color);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Pipe net functions
|
||||
|
||||
private void RebuildAtmosPipeGrid(EntityUid gridUid, MapGridComponent grid)
|
||||
{
|
||||
var allChunks = new Dictionary<Vector2i, AtmosPipeChunk>();
|
||||
|
||||
// Adds all atmos pipes to the nav map via bit mask chunks
|
||||
var queryPipes = AllEntityQuery<AtmosPipeColorComponent, NodeContainerComponent, TransformComponent>();
|
||||
while (queryPipes.MoveNext(out var ent, out var entAtmosPipeColor, out var entNodeContainer, out var entXform))
|
||||
{
|
||||
if (entXform.GridUid != gridUid)
|
||||
continue;
|
||||
|
||||
if (!entXform.Anchored)
|
||||
continue;
|
||||
|
||||
var tile = _sharedMapSystem.GetTileRef(gridUid, grid, entXform.Coordinates);
|
||||
var chunkOrigin = SharedMapSystem.GetChunkIndices(tile.GridIndices, ChunkSize);
|
||||
var relative = SharedMapSystem.GetChunkRelative(tile.GridIndices, ChunkSize);
|
||||
|
||||
if (!allChunks.TryGetValue(chunkOrigin, out var chunk))
|
||||
{
|
||||
chunk = new AtmosPipeChunk(chunkOrigin);
|
||||
allChunks[chunkOrigin] = chunk;
|
||||
}
|
||||
|
||||
UpdateAtmosPipeChunk(ent, entNodeContainer, entAtmosPipeColor, GetTileIndex(relative), ref chunk);
|
||||
}
|
||||
|
||||
// Add or update the chunks on the associated grid
|
||||
_gridAtmosPipeChunks[gridUid] = allChunks;
|
||||
|
||||
// Update the consoles that are on the same grid
|
||||
var queryConsoles = AllEntityQuery<AtmosMonitoringConsoleComponent, TransformComponent>();
|
||||
while (queryConsoles.MoveNext(out var ent, out var entConsole, out var entXform))
|
||||
{
|
||||
if (gridUid != entXform.GridUid)
|
||||
continue;
|
||||
|
||||
entConsole.AtmosPipeChunks = allChunks;
|
||||
Dirty(ent, entConsole);
|
||||
}
|
||||
}
|
||||
|
||||
private void RebuildSingleTileOfPipeNetwork(EntityUid gridUid, MapGridComponent grid, EntityCoordinates coords)
|
||||
{
|
||||
if (!_gridAtmosPipeChunks.TryGetValue(gridUid, out var allChunks))
|
||||
allChunks = new Dictionary<Vector2i, AtmosPipeChunk>();
|
||||
|
||||
var tile = _sharedMapSystem.GetTileRef(gridUid, grid, coords);
|
||||
var chunkOrigin = SharedMapSystem.GetChunkIndices(tile.GridIndices, ChunkSize);
|
||||
var relative = SharedMapSystem.GetChunkRelative(tile.GridIndices, ChunkSize);
|
||||
var tileIdx = GetTileIndex(relative);
|
||||
|
||||
if (!allChunks.TryGetValue(chunkOrigin, out var chunk))
|
||||
chunk = new AtmosPipeChunk(chunkOrigin);
|
||||
|
||||
// Remove all stale values for the tile
|
||||
foreach (var (index, atmosPipeData) in chunk.AtmosPipeData)
|
||||
{
|
||||
var mask = (ulong)SharedNavMapSystem.AllDirMask << tileIdx * SharedNavMapSystem.Directions;
|
||||
chunk.AtmosPipeData[index] = atmosPipeData & ~mask;
|
||||
}
|
||||
|
||||
// Rebuild the tile's pipe data
|
||||
foreach (var ent in _sharedMapSystem.GetAnchoredEntities(gridUid, grid, coords))
|
||||
{
|
||||
if (!TryComp<AtmosPipeColorComponent>(ent, out var entAtmosPipeColor))
|
||||
continue;
|
||||
|
||||
if (!TryComp<NodeContainerComponent>(ent, out var entNodeContainer))
|
||||
continue;
|
||||
|
||||
UpdateAtmosPipeChunk(ent, entNodeContainer, entAtmosPipeColor, tileIdx, ref chunk);
|
||||
}
|
||||
|
||||
// Add or update the chunk on the associated grid
|
||||
// Only the modified chunk will be sent to the client
|
||||
chunk.LastUpdate = _gameTiming.CurTick;
|
||||
allChunks[chunkOrigin] = chunk;
|
||||
_gridAtmosPipeChunks[gridUid] = allChunks;
|
||||
|
||||
// Update the components of the monitoring consoles that are attached to the same grid
|
||||
var query = AllEntityQuery<AtmosMonitoringConsoleComponent, TransformComponent>();
|
||||
|
||||
while (query.MoveNext(out var ent, out var entConsole, out var entXform))
|
||||
{
|
||||
if (gridUid != entXform.GridUid)
|
||||
continue;
|
||||
|
||||
entConsole.AtmosPipeChunks = allChunks;
|
||||
Dirty(ent, entConsole);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateAtmosPipeChunk(EntityUid uid, NodeContainerComponent nodeContainer, AtmosPipeColorComponent pipeColor, int tileIdx, ref AtmosPipeChunk chunk)
|
||||
{
|
||||
// Entities that are actively being deleted are not to be drawn
|
||||
if (MetaData(uid).EntityLifeStage >= EntityLifeStage.Terminating)
|
||||
return;
|
||||
|
||||
foreach ((var id, var node) in nodeContainer.Nodes)
|
||||
{
|
||||
if (node is not PipeNode)
|
||||
continue;
|
||||
|
||||
var pipeNode = (PipeNode)node;
|
||||
var netId = GetPipeNodeNetId(pipeNode);
|
||||
var pipeDirection = pipeNode.CurrentPipeDirection;
|
||||
|
||||
chunk.AtmosPipeData.TryGetValue((netId, pipeColor.Color.ToHex()), out var atmosPipeData);
|
||||
atmosPipeData |= (ulong)pipeDirection << tileIdx * SharedNavMapSystem.Directions;
|
||||
chunk.AtmosPipeData[(netId, pipeColor.Color.ToHex())] = atmosPipeData;
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryGettingFirstPipeNode(EntityUid uid, [NotNullWhen(true)] out PipeNode? pipeNode, [NotNullWhen(true)] out int? netId)
|
||||
{
|
||||
pipeNode = null;
|
||||
netId = null;
|
||||
|
||||
if (!TryComp<NodeContainerComponent>(uid, out var nodeContainer))
|
||||
return false;
|
||||
|
||||
foreach (var node in nodeContainer.Nodes.Values)
|
||||
{
|
||||
if (node is PipeNode)
|
||||
{
|
||||
pipeNode = (PipeNode)node;
|
||||
netId = GetPipeNodeNetId(pipeNode);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private int GetPipeNodeNetId(PipeNode pipeNode)
|
||||
{
|
||||
if (pipeNode.NodeGroup is BaseNodeGroup)
|
||||
{
|
||||
var nodeGroup = (BaseNodeGroup)pipeNode.NodeGroup;
|
||||
|
||||
return nodeGroup.NetId;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Initialization functions
|
||||
|
||||
private void InitializeAtmosMonitoringConsole(EntityUid uid, AtmosMonitoringConsoleComponent component)
|
||||
{
|
||||
var xform = Transform(uid);
|
||||
|
||||
if (xform.GridUid == null)
|
||||
return;
|
||||
|
||||
var grid = xform.GridUid.Value;
|
||||
|
||||
if (!TryComp<MapGridComponent>(grid, out var map))
|
||||
return;
|
||||
|
||||
component.AtmosDevices = GetAllAtmosDeviceNavMapData(grid);
|
||||
|
||||
if (!_gridAtmosPipeChunks.TryGetValue(grid, out var chunks))
|
||||
{
|
||||
RebuildAtmosPipeGrid(grid, map);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
component.AtmosPipeChunks = chunks;
|
||||
Dirty(uid, component);
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeAtmosMonitoringDevice(EntityUid uid, AtmosMonitoringConsoleDeviceComponent component)
|
||||
{
|
||||
// Rebuild tile
|
||||
var xform = Transform(uid);
|
||||
var gridUid = xform.GridUid;
|
||||
|
||||
if (gridUid != null && TryComp<MapGridComponent>(gridUid, out var grid))
|
||||
RebuildSingleTileOfPipeNetwork(gridUid.Value, grid, xform.Coordinates);
|
||||
|
||||
// Update blips on affected consoles
|
||||
if (component.NavMapBlip == null)
|
||||
return;
|
||||
|
||||
var netEntity = EntityManager.GetNetEntity(uid);
|
||||
var query = AllEntityQuery<AtmosMonitoringConsoleComponent, TransformComponent>();
|
||||
|
||||
while (query.MoveNext(out var ent, out var entConsole, out var entXform))
|
||||
{
|
||||
var isDirty = entConsole.AtmosDevices.Remove(netEntity);
|
||||
|
||||
if (gridUid != null &&
|
||||
gridUid == entXform.GridUid &&
|
||||
xform.Anchored &&
|
||||
TryGetAtmosDeviceNavMapData(uid, component, xform, gridUid.Value, out var data))
|
||||
{
|
||||
entConsole.AtmosDevices.Add(netEntity, data.Value);
|
||||
isDirty = true;
|
||||
}
|
||||
|
||||
if (isDirty)
|
||||
Dirty(ent, entConsole);
|
||||
}
|
||||
}
|
||||
|
||||
private void ShutDownAtmosMonitoringEntity(EntityUid uid, AtmosMonitoringConsoleDeviceComponent component)
|
||||
{
|
||||
// Rebuild tile
|
||||
var xform = Transform(uid);
|
||||
var gridUid = xform.GridUid;
|
||||
|
||||
if (gridUid != null && TryComp<MapGridComponent>(gridUid, out var grid))
|
||||
RebuildSingleTileOfPipeNetwork(gridUid.Value, grid, xform.Coordinates);
|
||||
|
||||
// Update blips on affected consoles
|
||||
if (component.NavMapBlip == null)
|
||||
return;
|
||||
|
||||
var netEntity = EntityManager.GetNetEntity(uid);
|
||||
var query = AllEntityQuery<AtmosMonitoringConsoleComponent>();
|
||||
|
||||
while (query.MoveNext(out var ent, out var entConsole))
|
||||
{
|
||||
if (entConsole.AtmosDevices.Remove(netEntity))
|
||||
Dirty(ent, entConsole);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private int GetTileIndex(Vector2i relativeTile)
|
||||
{
|
||||
return relativeTile.X * ChunkSize + relativeTile.Y;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,24 @@
|
||||
using Content.Server.Atmos.Piping.EntitySystems;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Content.Server.Atmos.Piping.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed partial class AtmosPipeColorComponent : Component
|
||||
{
|
||||
[DataField("color")]
|
||||
public Color Color { get; set; } = Color.White;
|
||||
namespace Content.Server.Atmos.Piping.Components;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), UsedImplicitly]
|
||||
public Color ColorVV
|
||||
{
|
||||
get => Color;
|
||||
set => IoCManager.Resolve<IEntityManager>().System<AtmosPipeColorSystem>().SetColor(Owner, this, value);
|
||||
}
|
||||
[RegisterComponent]
|
||||
public sealed partial class AtmosPipeColorComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public Color Color { get; set; } = Color.White;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), UsedImplicitly]
|
||||
public Color ColorVV
|
||||
{
|
||||
get => Color;
|
||||
set => IoCManager.Resolve<IEntityManager>().System<AtmosPipeColorSystem>().SetColor(Owner, this, value);
|
||||
}
|
||||
}
|
||||
|
||||
[ByRefEvent]
|
||||
public record struct AtmosPipeColorChangedEvent(Color color)
|
||||
{
|
||||
public Color Color = color;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,9 @@ namespace Content.Server.Atmos.Piping.EntitySystems
|
||||
return;
|
||||
|
||||
_appearance.SetData(uid, PipeColorVisuals.Color, color, appearance);
|
||||
|
||||
var ev = new AtmosPipeColorChangedEvent(color);
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,6 +145,22 @@ namespace Content.Shared.Atmos
|
||||
/// </summary>
|
||||
public const float SpaceHeatCapacity = 7000f;
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary of chemical abbreviations for <see cref="Gas"/>
|
||||
/// </summary>
|
||||
public static Dictionary<Gas, string> GasAbbreviations = new Dictionary<Gas, string>()
|
||||
{
|
||||
[Gas.Ammonia] = Loc.GetString("gas-ammonia-abbreviation"),
|
||||
[Gas.CarbonDioxide] = Loc.GetString("gas-carbon-dioxide-abbreviation"),
|
||||
[Gas.Frezon] = Loc.GetString("gas-frezon-abbreviation"),
|
||||
[Gas.Nitrogen] = Loc.GetString("gas-nitrogen-abbreviation"),
|
||||
[Gas.NitrousOxide] = Loc.GetString("gas-nitrous-oxide-abbreviation"),
|
||||
[Gas.Oxygen] = Loc.GetString("gas-oxygen-abbreviation"),
|
||||
[Gas.Plasma] = Loc.GetString("gas-plasma-abbreviation"),
|
||||
[Gas.Tritium] = Loc.GetString("gas-tritium-abbreviation"),
|
||||
[Gas.WaterVapor] = Loc.GetString("gas-water-vapor-abbreviation"),
|
||||
};
|
||||
|
||||
#region Excited Groups
|
||||
|
||||
/// <summary>
|
||||
|
||||
10
Content.Shared/Atmos/Components/GasPipeSensorComponent.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Atmos.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Entities with component will be queried against for their
|
||||
/// atmos monitoring data on atmos monitoring consoles
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class GasPipeSensorComponent : Component;
|
||||
@@ -0,0 +1,235 @@
|
||||
using Content.Shared.Atmos.Consoles;
|
||||
using Content.Shared.Pinpointer;
|
||||
using Content.Shared.Prototypes;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared.Atmos.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Entities capable of opening the atmos monitoring console UI
|
||||
/// require this component to function correctly
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
[Access(typeof(SharedAtmosMonitoringConsoleSystem))]
|
||||
public sealed partial class AtmosMonitoringConsoleComponent : Component
|
||||
{
|
||||
/*
|
||||
* Don't need DataFields as this can be reconstructed
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// A dictionary of the all the nav map chunks that contain anchored atmos pipes
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public Dictionary<Vector2i, AtmosPipeChunk> AtmosPipeChunks = new();
|
||||
|
||||
/// <summary>
|
||||
/// A list of all the atmos devices that will be used to populate the nav map
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public Dictionary<NetEntity, AtmosDeviceNavMapData> AtmosDevices = new();
|
||||
|
||||
/// <summary>
|
||||
/// Color of the floor tiles on the nav map screen
|
||||
/// </summary>
|
||||
[DataField, ViewVariables]
|
||||
public Color NavMapTileColor;
|
||||
|
||||
/// <summary>
|
||||
/// Color of the wall lines on the nav map screen
|
||||
/// </summary>
|
||||
[DataField, ViewVariables]
|
||||
public Color NavMapWallColor;
|
||||
|
||||
/// <summary>
|
||||
/// The next time this component is dirtied, it will force the full state
|
||||
/// to be sent to the client, instead of just the delta state
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public bool ForceFullUpdate = false;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public struct AtmosPipeChunk(Vector2i origin)
|
||||
{
|
||||
/// <summary>
|
||||
/// Chunk position
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public readonly Vector2i Origin = origin;
|
||||
|
||||
/// <summary>
|
||||
/// Bitmask look up for atmos pipes, 1 for occupied and 0 for empty.
|
||||
/// Indexed by the color hexcode of the pipe
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public Dictionary<(int, string), ulong> AtmosPipeData = new();
|
||||
|
||||
/// <summary>
|
||||
/// The last game tick that the chunk was updated
|
||||
/// </summary>
|
||||
[NonSerialized]
|
||||
public GameTick LastUpdate;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public struct AtmosDeviceNavMapData
|
||||
{
|
||||
/// <summary>
|
||||
/// The entity in question
|
||||
/// </summary>
|
||||
public NetEntity NetEntity;
|
||||
|
||||
/// <summary>
|
||||
/// Location of the entity
|
||||
/// </summary>
|
||||
public NetCoordinates NetCoordinates;
|
||||
|
||||
/// <summary>
|
||||
/// The associated pipe network ID
|
||||
/// </summary>
|
||||
public int NetId = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Prototype ID for the nav map blip
|
||||
/// </summary>
|
||||
public ProtoId<NavMapBlipPrototype> NavMapBlip;
|
||||
|
||||
/// <summary>
|
||||
/// Direction of the entity
|
||||
/// </summary>
|
||||
public Direction Direction;
|
||||
|
||||
/// <summary>
|
||||
/// Color of the attached pipe
|
||||
/// </summary>
|
||||
public Color PipeColor;
|
||||
|
||||
/// <summary>
|
||||
/// Populate the atmos monitoring console nav map with a single entity
|
||||
/// </summary>
|
||||
public AtmosDeviceNavMapData(NetEntity netEntity, NetCoordinates netCoordinates, int netId, ProtoId<NavMapBlipPrototype> navMapBlip, Direction direction, Color pipeColor)
|
||||
{
|
||||
NetEntity = netEntity;
|
||||
NetCoordinates = netCoordinates;
|
||||
NetId = netId;
|
||||
NavMapBlip = navMapBlip;
|
||||
Direction = direction;
|
||||
PipeColor = pipeColor;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class AtmosMonitoringConsoleBoundInterfaceState : BoundUserInterfaceState
|
||||
{
|
||||
/// <summary>
|
||||
/// A list of all entries to populate the UI with
|
||||
/// </summary>
|
||||
public AtmosMonitoringConsoleEntry[] AtmosNetworks;
|
||||
|
||||
/// <summary>
|
||||
/// Sends data from the server to the client to populate the atmos monitoring console UI
|
||||
/// </summary>
|
||||
public AtmosMonitoringConsoleBoundInterfaceState(AtmosMonitoringConsoleEntry[] atmosNetworks)
|
||||
{
|
||||
AtmosNetworks = atmosNetworks;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public struct AtmosMonitoringConsoleEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// The entity in question
|
||||
/// </summary>
|
||||
public NetEntity NetEntity;
|
||||
|
||||
/// <summary>
|
||||
/// Location of the entity
|
||||
/// </summary>
|
||||
public NetCoordinates Coordinates;
|
||||
|
||||
/// <summary>
|
||||
/// The associated pipe network ID
|
||||
/// </summary>
|
||||
public int NetId = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Localised device name
|
||||
/// </summary>
|
||||
public string EntityName;
|
||||
|
||||
/// <summary>
|
||||
/// Device network address
|
||||
/// </summary>
|
||||
public string Address;
|
||||
|
||||
/// <summary>
|
||||
/// Temperature (K)
|
||||
/// </summary>
|
||||
public float TemperatureData;
|
||||
|
||||
/// <summary>
|
||||
/// Pressure (kPA)
|
||||
/// </summary>
|
||||
public float PressureData;
|
||||
|
||||
/// <summary>
|
||||
/// Total number of mols of gas
|
||||
/// </summary>
|
||||
public float TotalMolData;
|
||||
|
||||
/// <summary>
|
||||
/// Mol and percentage for all detected gases
|
||||
/// </summary>
|
||||
public Dictionary<Gas, float> GasData = new();
|
||||
|
||||
/// <summary>
|
||||
/// The color to be associated with the pipe network
|
||||
/// </summary>
|
||||
public Color Color;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the entity is powered
|
||||
/// </summary>
|
||||
public bool IsPowered = true;
|
||||
|
||||
/// <summary>
|
||||
/// Used to populate the atmos monitoring console UI with data from a single air alarm
|
||||
/// </summary>
|
||||
public AtmosMonitoringConsoleEntry
|
||||
(NetEntity entity,
|
||||
NetCoordinates coordinates,
|
||||
int netId,
|
||||
string entityName,
|
||||
string address)
|
||||
{
|
||||
NetEntity = entity;
|
||||
Coordinates = coordinates;
|
||||
NetId = netId;
|
||||
EntityName = entityName;
|
||||
Address = address;
|
||||
}
|
||||
}
|
||||
|
||||
public enum AtmosPipeChunkDataFacing : byte
|
||||
{
|
||||
// Values represent bit shift offsets when retrieving data in the tile array.
|
||||
North = 0,
|
||||
South = SharedNavMapSystem.ArraySize,
|
||||
East = SharedNavMapSystem.ArraySize * 2,
|
||||
West = SharedNavMapSystem.ArraySize * 3,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UI key associated with the atmos monitoring console
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public enum AtmosMonitoringConsoleUiKey
|
||||
{
|
||||
Key
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Content.Shared.Prototypes;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Atmos.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Entities with this component appear on the
|
||||
/// nav maps of atmos monitoring consoles
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class AtmosMonitoringConsoleDeviceComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Prototype ID for the blip used to represent this
|
||||
/// entity on the atmos monitoring console nav map.
|
||||
/// If null, no blip is drawn (i.e., null for pipes)
|
||||
/// </summary>
|
||||
[DataField, ViewVariables]
|
||||
public ProtoId<NavMapBlipPrototype>? NavMapBlip = null;
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
using Content.Shared.Atmos.Components;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Atmos.Consoles;
|
||||
|
||||
public abstract class SharedAtmosMonitoringConsoleSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<AtmosMonitoringConsoleComponent, ComponentGetState>(OnGetState);
|
||||
}
|
||||
|
||||
private void OnGetState(EntityUid uid, AtmosMonitoringConsoleComponent component, ref ComponentGetState args)
|
||||
{
|
||||
Dictionary<Vector2i, Dictionary<(int, string), ulong>> chunks;
|
||||
|
||||
// Should this be a full component state or a delta-state?
|
||||
if (args.FromTick <= component.CreationTick || component.ForceFullUpdate)
|
||||
{
|
||||
component.ForceFullUpdate = false;
|
||||
|
||||
// Full state
|
||||
chunks = new(component.AtmosPipeChunks.Count);
|
||||
|
||||
foreach (var (origin, chunk) in component.AtmosPipeChunks)
|
||||
{
|
||||
chunks.Add(origin, chunk.AtmosPipeData);
|
||||
}
|
||||
|
||||
args.State = new AtmosMonitoringConsoleState(chunks, component.AtmosDevices);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
chunks = new();
|
||||
|
||||
foreach (var (origin, chunk) in component.AtmosPipeChunks)
|
||||
{
|
||||
if (chunk.LastUpdate < args.FromTick)
|
||||
continue;
|
||||
|
||||
chunks.Add(origin, chunk.AtmosPipeData);
|
||||
}
|
||||
|
||||
args.State = new AtmosMonitoringConsoleDeltaState(chunks, component.AtmosDevices, new(component.AtmosPipeChunks.Keys));
|
||||
}
|
||||
|
||||
#region: System messages
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
protected sealed class AtmosMonitoringConsoleState(
|
||||
Dictionary<Vector2i, Dictionary<(int, string), ulong>> chunks,
|
||||
Dictionary<NetEntity, AtmosDeviceNavMapData> atmosDevices)
|
||||
: ComponentState
|
||||
{
|
||||
public Dictionary<Vector2i, Dictionary<(int, string), ulong>> Chunks = chunks;
|
||||
public Dictionary<NetEntity, AtmosDeviceNavMapData> AtmosDevices = atmosDevices;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
protected sealed class AtmosMonitoringConsoleDeltaState(
|
||||
Dictionary<Vector2i, Dictionary<(int, string), ulong>> modifiedChunks,
|
||||
Dictionary<NetEntity, AtmosDeviceNavMapData> atmosDevices,
|
||||
HashSet<Vector2i> allChunks)
|
||||
: ComponentState, IComponentDeltaState<AtmosMonitoringConsoleState>
|
||||
{
|
||||
public Dictionary<Vector2i, Dictionary<(int, string), ulong>> ModifiedChunks = modifiedChunks;
|
||||
public Dictionary<NetEntity, AtmosDeviceNavMapData> AtmosDevices = atmosDevices;
|
||||
public HashSet<Vector2i> AllChunks = allChunks;
|
||||
|
||||
public void ApplyToFullState(AtmosMonitoringConsoleState state)
|
||||
{
|
||||
foreach (var key in state.Chunks.Keys)
|
||||
{
|
||||
if (!AllChunks!.Contains(key))
|
||||
state.Chunks.Remove(key);
|
||||
}
|
||||
|
||||
foreach (var (index, data) in ModifiedChunks)
|
||||
{
|
||||
state.Chunks[index] = new Dictionary<(int, string), ulong>(data);
|
||||
}
|
||||
|
||||
state.AtmosDevices.Clear();
|
||||
foreach (var (nuid, atmosDevice) in AtmosDevices)
|
||||
{
|
||||
state.AtmosDevices.Add(nuid, atmosDevice);
|
||||
}
|
||||
}
|
||||
|
||||
public AtmosMonitoringConsoleState CreateNewFullState(AtmosMonitoringConsoleState state)
|
||||
{
|
||||
var chunks = new Dictionary<Vector2i, Dictionary<(int, string), ulong>>(state.Chunks.Count);
|
||||
|
||||
foreach (var (index, data) in state.Chunks)
|
||||
{
|
||||
if (!AllChunks!.Contains(index))
|
||||
continue;
|
||||
|
||||
if (ModifiedChunks.ContainsKey(index))
|
||||
chunks[index] = new Dictionary<(int, string), ulong>(ModifiedChunks[index]);
|
||||
|
||||
else
|
||||
chunks[index] = new Dictionary<(int, string), ulong>(state.Chunks[index]);
|
||||
}
|
||||
|
||||
return new AtmosMonitoringConsoleState(chunks, new(AtmosDevices));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
42
Content.Shared/Prototypes/NavMapBlipPrototype.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Prototypes;
|
||||
|
||||
[Prototype("navMapBlip")]
|
||||
public sealed partial class NavMapBlipPrototype : IPrototype
|
||||
{
|
||||
[ViewVariables]
|
||||
[IdDataField]
|
||||
public string ID { get; private set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Sets whether the associated entity can be selected when the blip is clicked
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool Selectable = false;
|
||||
|
||||
/// <summary>
|
||||
/// Sets whether the blips is always blinking
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool Blinks = false;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the color of the blip
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Color Color { get; private set; } = Color.LightGray;
|
||||
|
||||
/// <summary>
|
||||
/// Texture paths associated with the blip
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public ResPath[]? TexturePaths { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the UI scaling of the blip
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float Scale { get; private set; } = 1f;
|
||||
}
|
||||
@@ -10,6 +10,9 @@ atmos-alerts-window-tab-fire-alarms = Fire alarms
|
||||
atmos-alerts-window-alarm-label = {CAPITALIZE($name)} ({$address})
|
||||
atmos-alerts-window-temperature-label = Temperature
|
||||
atmos-alerts-window-temperature-value = {$valueInC} °C ({$valueInK} K)
|
||||
atmos-alerts-window-invalid-value = N/A
|
||||
atmos-alerts-window-total-mol-label = Total moles
|
||||
atmos-alerts-window-total-mol-value = {$value} mol
|
||||
atmos-alerts-window-pressure-label = Pressure
|
||||
atmos-alerts-window-pressure-value = {$value} kPa
|
||||
atmos-alerts-window-oxygenation-label = Oxygenation
|
||||
|
||||
10
Resources/Locale/en-US/atmos/gases.ftl
Normal file
@@ -0,0 +1,10 @@
|
||||
gas-ammonia-abbreviation = NH₃
|
||||
gas-carbon-dioxide-abbreviation = CO₂
|
||||
gas-frezon-abbreviation = F
|
||||
gas-nitrogen-abbreviation = N₂
|
||||
gas-nitrous-oxide-abbreviation = N₂O
|
||||
gas-oxygen-abbreviation = O₂
|
||||
gas-plasma-abbreviation = P
|
||||
gas-tritium-abbreviation = T
|
||||
gas-water-vapor-abbreviation = H₂O
|
||||
gas-unknown-abbreviation = X
|
||||
@@ -0,0 +1,14 @@
|
||||
atmos-monitoring-window-title = Atmospheric Network Monitor
|
||||
atmos-monitoring-window-station-name = [color=white][font size=14]{$stationName}[/font][/color]
|
||||
atmos-monitoring-window-unknown-location = Unknown location
|
||||
atmos-monitoring-window-label-gas-opening = Network opening
|
||||
atmos-monitoring-window-label-gas-scrubber = Air scrubber
|
||||
atmos-monitoring-window-label-gas-flow-regulator = Flow regulator
|
||||
atmos-monitoring-window-label-thermoregulator = Thermoregulator
|
||||
atmos-monitoring-window-tab-networks = Atmospheric networks
|
||||
atmos-monitoring-window-toggle-overlays = Toggle map overlays
|
||||
atmos-monitoring-window-show-pipe-network = Pipe network
|
||||
atmos-monitoring-window-show-gas-pipe-sensors = Gas pipe sensors
|
||||
atmos-monitoring-window-label-gases = Present gases
|
||||
atmos-monitoring-window-flavor-left = Contact an atmospheric technician for assistance
|
||||
atmos-monitoring-window-flavor-right = v1.1
|
||||
@@ -26,6 +26,15 @@
|
||||
components:
|
||||
- type: ComputerBoard
|
||||
prototype: ComputerAlert
|
||||
|
||||
- type: entity
|
||||
parent: BaseComputerCircuitboard
|
||||
id: AtmosMonitoringComputerCircuitboard
|
||||
name: atmospheric network monitor board
|
||||
description: A computer printed circuit board for an atmospheric network monitor.
|
||||
components:
|
||||
- type: ComputerBoard
|
||||
prototype: ComputerAtmosMonitoring
|
||||
|
||||
- type: entity
|
||||
parent: BaseComputerCircuitboard
|
||||
|
||||
@@ -49,6 +49,39 @@
|
||||
enum.WiresUiKey.Key:
|
||||
type: WiresBoundUserInterface
|
||||
|
||||
- type: entity
|
||||
parent: BaseComputerAiAccess
|
||||
id: ComputerAtmosMonitoring
|
||||
name: atmospheric network monitor
|
||||
description: Used to monitor the station's atmospheric networks.
|
||||
components:
|
||||
- type: Computer
|
||||
board: AtmosMonitoringComputerCircuitboard
|
||||
- type: Sprite
|
||||
layers:
|
||||
- map: ["computerLayerBody"]
|
||||
state: computer
|
||||
- map: ["computerLayerKeyboard"]
|
||||
state: generic_keyboard
|
||||
- map: ["computerLayerScreen"]
|
||||
state: tank
|
||||
- map: ["computerLayerKeys"]
|
||||
state: atmos_key
|
||||
- map: [ "enum.WiresVisualLayers.MaintenancePanel" ]
|
||||
state: generic_panel_open
|
||||
- type: AtmosMonitoringConsole
|
||||
navMapTileColor: "#1a1a1a"
|
||||
navMapWallColor: "#404040"
|
||||
- type: ActivatableUI
|
||||
singleUser: true
|
||||
key: enum.AtmosMonitoringConsoleUiKey.Key
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
enum.AtmosMonitoringConsoleUiKey.Key:
|
||||
type: AtmosMonitoringConsoleBoundUserInterface
|
||||
enum.WiresUiKey.Key:
|
||||
type: WiresBoundUserInterface
|
||||
|
||||
- type: entity
|
||||
parent: BaseComputer
|
||||
id: ComputerEmergencyShuttle
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
# All consoles
|
||||
- type: navMapBlip
|
||||
id: NavMapConsole
|
||||
blinks: true
|
||||
color: Cyan
|
||||
texturePaths:
|
||||
- "/Textures/Interface/NavMap/beveled_circle.png"
|
||||
|
||||
# Atmos monitoring console
|
||||
- type: navMapBlip
|
||||
id: GasPipeSensor
|
||||
selectable: true
|
||||
color: "#ffcd00"
|
||||
texturePaths:
|
||||
- "/Textures/Interface/NavMap/beveled_star.png"
|
||||
|
||||
- type: navMapBlip
|
||||
id: GasVentOpening
|
||||
scale: 0.6667
|
||||
color: LightGray
|
||||
texturePaths:
|
||||
- "/Textures/Interface/NavMap/beveled_square.png"
|
||||
|
||||
- type: navMapBlip
|
||||
id: GasVentScrubber
|
||||
scale: 0.6667
|
||||
color: LightGray
|
||||
texturePaths:
|
||||
- "/Textures/Interface/NavMap/beveled_circle.png"
|
||||
|
||||
- type: navMapBlip
|
||||
id: GasFlowRegulator
|
||||
scale: 0.75
|
||||
color: LightGray
|
||||
texturePaths:
|
||||
- "/Textures/Interface/NavMap/beveled_arrow_south.png"
|
||||
- "/Textures/Interface/NavMap/beveled_arrow_east.png"
|
||||
- "/Textures/Interface/NavMap/beveled_arrow_north.png"
|
||||
- "/Textures/Interface/NavMap/beveled_arrow_west.png"
|
||||
|
||||
- type: navMapBlip
|
||||
id: GasValve
|
||||
scale: 0.6667
|
||||
color: LightGray
|
||||
texturePaths:
|
||||
- "/Textures/Interface/NavMap/beveled_diamond_north_south.png"
|
||||
- "/Textures/Interface/NavMap/beveled_diamond_east_west.png"
|
||||
- "/Textures/Interface/NavMap/beveled_diamond_north_south.png"
|
||||
- "/Textures/Interface/NavMap/beveled_diamond_east_west.png"
|
||||
|
||||
- type: navMapBlip
|
||||
id: Thermoregulator
|
||||
scale: 0.6667
|
||||
color: LightGray
|
||||
texturePaths:
|
||||
- "/Textures/Interface/NavMap/beveled_hexagon.png"
|
||||
@@ -79,6 +79,8 @@
|
||||
!type:PortablePipeNode
|
||||
nodeGroupID: Pipe
|
||||
pipeDirection: South
|
||||
- type: AtmosMonitoringConsoleDevice
|
||||
navMapBlip: Thermoregulator
|
||||
- type: ItemSlots
|
||||
slots:
|
||||
beakerSlot:
|
||||
|
||||
@@ -73,7 +73,9 @@
|
||||
range: 5
|
||||
sound:
|
||||
path: /Audio/Ambience/Objects/gas_pump.ogg
|
||||
|
||||
- type: AtmosMonitoringConsoleDevice
|
||||
navMapBlip: GasFlowRegulator
|
||||
|
||||
- type: entity
|
||||
parent: GasBinaryBase
|
||||
id: GasVolumePump
|
||||
@@ -130,7 +132,9 @@
|
||||
examinableAddress: true
|
||||
prefix: device-address-prefix-volume-pump
|
||||
- type: WiredNetworkConnection
|
||||
|
||||
- type: AtmosMonitoringConsoleDevice
|
||||
navMapBlip: GasFlowRegulator
|
||||
|
||||
- type: entity
|
||||
parent: GasBinaryBase
|
||||
id: GasPassiveGate
|
||||
@@ -159,7 +163,9 @@
|
||||
range: 5
|
||||
sound:
|
||||
path: /Audio/Ambience/Objects/gas_hiss.ogg
|
||||
|
||||
- type: AtmosMonitoringConsoleDevice
|
||||
navMapBlip: GasValve
|
||||
|
||||
- type: entity
|
||||
parent: GasBinaryBase
|
||||
id: GasValve
|
||||
@@ -207,7 +213,9 @@
|
||||
range: 5
|
||||
sound:
|
||||
path: /Audio/Ambience/Objects/gas_hiss.ogg
|
||||
|
||||
- type: AtmosMonitoringConsoleDevice
|
||||
navMapBlip: GasValve
|
||||
|
||||
- type: entity
|
||||
parent: GasBinaryBase
|
||||
id: SignalControlledValve
|
||||
@@ -266,7 +274,9 @@
|
||||
range: 5
|
||||
sound:
|
||||
path: /Audio/Ambience/Objects/gas_hiss.ogg
|
||||
|
||||
- type: AtmosMonitoringConsoleDevice
|
||||
navMapBlip: GasValve
|
||||
|
||||
- type: entity
|
||||
parent: GasBinaryBase
|
||||
id: GasPort
|
||||
@@ -295,7 +305,9 @@
|
||||
- type: Construction
|
||||
graph: GasBinary
|
||||
node: port
|
||||
|
||||
- type: AtmosMonitoringConsoleDevice
|
||||
navMapBlip: GasVentOpening
|
||||
|
||||
- type: entity
|
||||
parent: GasVentPump
|
||||
id: GasDualPortVentPump
|
||||
@@ -351,7 +363,9 @@
|
||||
pipeDirection: South
|
||||
- type: AmbientSound
|
||||
enabled: true
|
||||
|
||||
- type: AtmosMonitoringConsoleDevice
|
||||
navMapBlip: GasVentOpening
|
||||
|
||||
- type: entity
|
||||
parent: [ BaseMachine, ConstructibleMachine ]
|
||||
id: GasRecycler
|
||||
@@ -413,7 +427,9 @@
|
||||
acts: ["Destruction"]
|
||||
- type: Machine
|
||||
board: GasRecyclerMachineCircuitboard
|
||||
|
||||
- type: AtmosMonitoringConsoleDevice
|
||||
navMapBlip: GasValve
|
||||
|
||||
- type: entity
|
||||
parent: GasBinaryBase
|
||||
id: HeatExchanger
|
||||
@@ -453,3 +469,5 @@
|
||||
- type: Construction
|
||||
graph: GasBinary
|
||||
node: radiator
|
||||
- type: AtmosMonitoringConsoleDevice
|
||||
navMapBlip: Thermoregulator
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
True: { state: lights }
|
||||
- type: AtmosMonitor
|
||||
monitorsPipeNet: true
|
||||
- type: GasPipeSensor
|
||||
- type: AtmosMonitoringConsoleDevice
|
||||
navMapBlip: GasPipeSensor
|
||||
- type: ApcPowerReceiver
|
||||
- type: ExtensionCableReceiver
|
||||
- type: Construction
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
- type: PipeRestrictOverlap
|
||||
- type: AtmosUnsafeUnanchor
|
||||
- type: AtmosPipeColor
|
||||
- type: AtmosMonitoringConsoleDevice
|
||||
- type: Tag
|
||||
tags:
|
||||
- Pipe
|
||||
|
||||
@@ -70,7 +70,9 @@
|
||||
range: 5
|
||||
sound:
|
||||
path: /Audio/Ambience/Objects/gas_hiss.ogg
|
||||
|
||||
- type: AtmosMonitoringConsoleDevice
|
||||
navMapBlip: GasFlowRegulator
|
||||
|
||||
- type: entity
|
||||
parent: GasFilter
|
||||
id: GasFilterFlipped
|
||||
@@ -158,7 +160,9 @@
|
||||
range: 5
|
||||
sound:
|
||||
path: /Audio/Ambience/Objects/gas_hiss.ogg
|
||||
|
||||
- type: AtmosMonitoringConsoleDevice
|
||||
navMapBlip: GasFlowRegulator
|
||||
|
||||
- type: entity
|
||||
parent: GasMixer
|
||||
id: GasMixerFlipped
|
||||
@@ -257,3 +261,5 @@
|
||||
- type: Construction
|
||||
graph: GasTrinary
|
||||
node: pneumaticvalve
|
||||
- type: AtmosMonitoringConsoleDevice
|
||||
navMapBlip: GasFlowRegulator
|
||||
@@ -68,7 +68,9 @@
|
||||
sound:
|
||||
path: /Audio/Ambience/Objects/gas_vent.ogg
|
||||
- type: Weldable
|
||||
|
||||
- type: AtmosMonitoringConsoleDevice
|
||||
navMapBlip: GasVentOpening
|
||||
|
||||
- type: entity
|
||||
parent: GasUnaryBase
|
||||
id: GasPassiveVent
|
||||
@@ -92,7 +94,9 @@
|
||||
- type: Construction
|
||||
graph: GasUnary
|
||||
node: passivevent
|
||||
|
||||
- type: AtmosMonitoringConsoleDevice
|
||||
navMapBlip: GasVentOpening
|
||||
|
||||
- type: entity
|
||||
parent: [GasUnaryBase, AirSensorBase]
|
||||
id: GasVentScrubber
|
||||
@@ -141,7 +145,9 @@
|
||||
sound:
|
||||
path: /Audio/Ambience/Objects/gas_vent.ogg
|
||||
- type: Weldable
|
||||
|
||||
- type: AtmosMonitoringConsoleDevice
|
||||
navMapBlip: GasVentScrubber
|
||||
|
||||
- type: entity
|
||||
parent: GasUnaryBase
|
||||
id: GasOutletInjector
|
||||
@@ -180,7 +186,9 @@
|
||||
visibleLayers:
|
||||
- enum.SubfloorLayers.FirstLayer
|
||||
- enum.LightLayers.Unshaded
|
||||
|
||||
- type: AtmosMonitoringConsoleDevice
|
||||
navMapBlip: GasVentOpening
|
||||
|
||||
- type: entity
|
||||
parent: [ BaseMachinePowered, ConstructibleMachine ]
|
||||
id: BaseGasThermoMachine
|
||||
@@ -224,7 +232,9 @@
|
||||
examinableAddress: true
|
||||
- type: WiredNetworkConnection
|
||||
- type: PowerSwitch
|
||||
|
||||
- type: AtmosMonitoringConsoleDevice
|
||||
navMapBlip: Thermoregulator
|
||||
|
||||
- type: entity
|
||||
parent: BaseGasThermoMachine
|
||||
id: GasThermoMachineFreezer
|
||||
@@ -429,3 +439,5 @@
|
||||
- type: ExaminableSolution
|
||||
solution: tank
|
||||
- type: PowerSwitch
|
||||
- type: AtmosMonitoringConsoleDevice
|
||||
navMapBlip: Thermoregulator
|
||||
|
||||
59
Resources/Textures/Interface/NavMap/attributions.yml
Normal file
@@ -0,0 +1,59 @@
|
||||
- files: ["beveled_arrow_east.png"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
copyright: "Created by chromiumboy"
|
||||
source: "https://github.com/chromiumboy"
|
||||
|
||||
- files: ["beveled_arrow_north.png"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
copyright: "Created by chromiumboy"
|
||||
source: "https://github.com/chromiumboy"
|
||||
|
||||
- files: ["beveled_arrow_south.png"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
copyright: "Created by chromiumboy"
|
||||
source: "https://github.com/chromiumboy"
|
||||
|
||||
- files: ["beveled_arrow_west.png"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
copyright: "Created by chromiumboy"
|
||||
source: "https://github.com/chromiumboy"
|
||||
|
||||
- files: ["beveled_circle.png"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
copyright: "Created by chromiumboy"
|
||||
source: "https://github.com/chromiumboy"
|
||||
|
||||
- files: ["beveled_diamond.png"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
copyright: "Created by chromiumboy"
|
||||
source: "https://github.com/chromiumboy"
|
||||
|
||||
- files: ["beveled_diamond_east_west.png"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
copyright: "Created by chromiumboy"
|
||||
source: "https://github.com/chromiumboy"
|
||||
|
||||
- files: ["beveled_diamond_north_south.png"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
copyright: "Created by chromiumboy"
|
||||
source: "https://github.com/chromiumboy"
|
||||
|
||||
- files: ["beveled_hexagon.png"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
copyright: "Created by chromiumboy"
|
||||
source: "https://github.com/chromiumboy"
|
||||
|
||||
- files: ["beveled_square.png"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
copyright: "Created by chromiumboy"
|
||||
source: "https://github.com/chromiumboy"
|
||||
|
||||
- files: ["beveled_star.png"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
copyright: "Created by chromiumboy"
|
||||
source: "https://github.com/chromiumboy"
|
||||
|
||||
- files: ["beveled_triangle.png"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
copyright: "Created by chromiumboy"
|
||||
source: "https://github.com/chromiumboy"
|
||||
BIN
Resources/Textures/Interface/NavMap/beveled_arrow_east.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,2 @@
|
||||
sample:
|
||||
filter: true
|
||||
BIN
Resources/Textures/Interface/NavMap/beveled_arrow_north.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,2 @@
|
||||
sample:
|
||||
filter: true
|
||||
BIN
Resources/Textures/Interface/NavMap/beveled_arrow_south.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,2 @@
|
||||
sample:
|
||||
filter: true
|
||||
BIN
Resources/Textures/Interface/NavMap/beveled_arrow_west.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,2 @@
|
||||
sample:
|
||||
filter: true
|
||||
BIN
Resources/Textures/Interface/NavMap/beveled_diamond.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,2 @@
|
||||
sample:
|
||||
filter: true
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
@@ -0,0 +1,2 @@
|
||||
sample:
|
||||
filter: true
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,2 @@
|
||||
sample:
|
||||
filter: true
|
||||
BIN
Resources/Textures/Interface/NavMap/beveled_star.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
2
Resources/Textures/Interface/NavMap/beveled_star.png.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
sample:
|
||||
filter: true
|
||||