Atmospheric alerts computer (#25938)
* Atmospheric alerts computer * Moved components, restricted access to them * Minor tweaks * The screen will now turn off when the computer is not powered * Bug fix * Adjusted label * Updated to latest master version
This commit is contained in:
81
Content.Client/Atmos/Consoles/AtmosAlarmEntryContainer.xaml
Normal file
81
Content.Client/Atmos/Consoles/AtmosAlarmEntryContainer.xaml
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
<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">
|
||||||
|
|
||||||
|
<!-- Device selection button -->
|
||||||
|
<Button Name="FocusButton" HorizontalExpand="True" SetHeight="32" Margin="12 0 0 0" StyleClasses="OpenBoth" Access="Public">
|
||||||
|
<BoxContainer HorizontalExpand="True" VerticalExpand="True" Orientation="Horizontal">
|
||||||
|
|
||||||
|
<!-- Alarm state -->
|
||||||
|
<TextureRect Stretch="Keep" HorizontalAlignment="Left" Margin="-20 -2 0 0" ModulateSelfOverride="#25252a" TexturePath="/Textures/Interface/AtmosMonitoring/status_bg.png">
|
||||||
|
<BoxContainer VerticalExpand="True" 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="AlarmStateLabel" HorizontalExpand="True" HorizontalAlignment="Center" FontColorOverride="#5A5A5A" Text="{Loc 'atmos-alerts-window-invalid-state'}"></Label>
|
||||||
|
</BoxContainer>
|
||||||
|
</TextureRect>
|
||||||
|
|
||||||
|
<!-- Alarm name -->
|
||||||
|
<Label Name="AlarmNameLabel" Text="???" HorizontalExpand="True" HorizontalAlignment="Center" Margin="5 0"></Label>
|
||||||
|
</BoxContainer>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<!-- Panel that appears on selecting the device -->
|
||||||
|
<PanelContainer Name="FocusContainer" HorizontalExpand="True" Margin="1 -1 1 0" ReservesSpace="False" Visible="False" Access="Public">
|
||||||
|
<PanelContainer.PanelOverride>
|
||||||
|
<gfx:StyleBoxFlat BackgroundColor="#25252a"/>
|
||||||
|
</PanelContainer.PanelOverride>
|
||||||
|
<BoxContainer HorizontalExpand="True" VerticalExpand="True" Orientation="Vertical">
|
||||||
|
|
||||||
|
<!-- Atmosphere status -->
|
||||||
|
<Control>
|
||||||
|
|
||||||
|
<!-- Main container for displaying atmospheric data -->
|
||||||
|
<BoxContainer Name="MainDataContainer" HorizontalExpand="True" VerticalExpand="True" Orientation="Vertical" ReservesSpace="False" Visible="False">
|
||||||
|
<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="OxygenationHeaderLabel" Text="{Loc 'atmos-alerts-window-oxygenation-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="#5A5A5A" Margin="0 2 0 0" SetHeight="24"></Label>
|
||||||
|
<Label Name="PressureLabel" Text="???" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#5A5A5A" Margin="0 2 0 0" SetHeight="24"></Label>
|
||||||
|
<Label Name="OxygenationLabel" Text="???" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#5A5A5A" Margin="0 2 0 0" SetHeight="24"></Label>
|
||||||
|
</BoxContainer>
|
||||||
|
</PanelContainer>
|
||||||
|
<BoxContainer HorizontalExpand="True" Orientation="Horizontal">
|
||||||
|
<Label Name="GasesHeaderLabel" Text="{Loc 'atmos-alerts-window-other-gases-label'}" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#a9a9a9" Margin="0 4 0 0" SetHeight="24"></Label>
|
||||||
|
</BoxContainer>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<!-- If the alarm is inactive, this is label is diplayed instead -->
|
||||||
|
<Label Name="NoDataLabel" Text="{Loc 'atmos-alerts-window-no-data-available'}" HorizontalAlignment="Center" Margin="0 15" FontColorOverride="#a9a9a9" ReservesSpace="False" Visible="False"></Label>
|
||||||
|
|
||||||
|
<!-- Silencing progress bar -->
|
||||||
|
<controls:StripeBack Name="SilenceAlarmProgressBar" ReservesSpace="False" Visible="False" Access="Public">
|
||||||
|
<PanelContainer>
|
||||||
|
<Label Text="{Loc 'atmos-alerts-window-alerts-being-silenced'}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5 5 5 5"/>
|
||||||
|
</PanelContainer>
|
||||||
|
</controls:StripeBack>
|
||||||
|
</Control>
|
||||||
|
|
||||||
|
<!-- Check box for silencing this alarm -->
|
||||||
|
<CheckBox Name="SilenceCheckBox" Text="{Loc 'atmos-alerts-window-silence-alerts'}" HorizontalAlignment="Left" Margin="5 5 5 5" Access="Public"></CheckBox>
|
||||||
|
</BoxContainer>
|
||||||
|
</PanelContainer>
|
||||||
|
|
||||||
|
</BoxContainer>
|
||||||
210
Content.Client/Atmos/Consoles/AtmosAlarmEntryContainer.xaml.cs
Normal file
210
Content.Client/Atmos/Consoles/AtmosAlarmEntryContainer.xaml.cs
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
using Content.Client.Stylesheets;
|
||||||
|
using Content.Shared.Atmos;
|
||||||
|
using Content.Shared.Atmos.Components;
|
||||||
|
using Content.Shared.Atmos.Monitor;
|
||||||
|
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 Robust.Shared.Map;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Content.Client.Atmos.Consoles;
|
||||||
|
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class AtmosAlarmEntryContainer : BoxContainer
|
||||||
|
{
|
||||||
|
public NetEntity NetEntity;
|
||||||
|
public EntityCoordinates? Coordinates;
|
||||||
|
|
||||||
|
private IResourceCache _cache;
|
||||||
|
|
||||||
|
private Dictionary<AtmosAlarmType, string> _alarmStrings = new Dictionary<AtmosAlarmType, string>()
|
||||||
|
{
|
||||||
|
[AtmosAlarmType.Invalid] = "atmos-alerts-window-invalid-state",
|
||||||
|
[AtmosAlarmType.Normal] = "atmos-alerts-window-normal-state",
|
||||||
|
[AtmosAlarmType.Warning] = "atmos-alerts-window-warning-state",
|
||||||
|
[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);
|
||||||
|
|
||||||
|
_cache = IoCManager.Resolve<IResourceCache>();
|
||||||
|
|
||||||
|
NetEntity = uid;
|
||||||
|
Coordinates = coordinates;
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
var smallFont = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10);
|
||||||
|
|
||||||
|
// Set fonts
|
||||||
|
TemperatureHeaderLabel.FontOverride = headerFont;
|
||||||
|
PressureHeaderLabel.FontOverride = headerFont;
|
||||||
|
OxygenationHeaderLabel.FontOverride = headerFont;
|
||||||
|
GasesHeaderLabel.FontOverride = headerFont;
|
||||||
|
|
||||||
|
TemperatureLabel.FontOverride = normalFont;
|
||||||
|
PressureLabel.FontOverride = normalFont;
|
||||||
|
OxygenationLabel.FontOverride = normalFont;
|
||||||
|
|
||||||
|
NoDataLabel.FontOverride = headerFont;
|
||||||
|
|
||||||
|
SilenceCheckBox.Label.FontOverride = smallFont;
|
||||||
|
SilenceCheckBox.Label.FontColorOverride = Color.DarkGray;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateEntry(AtmosAlertsComputerEntry entry, bool isFocus, AtmosAlertsFocusDeviceData? focusData = null)
|
||||||
|
{
|
||||||
|
// Load fonts
|
||||||
|
var normalFont = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSansDisplay/NotoSansDisplay-Regular.ttf"), 11);
|
||||||
|
|
||||||
|
// Update alarm state
|
||||||
|
if (!_alarmStrings.TryGetValue(entry.AlarmState, out var alarmString))
|
||||||
|
alarmString = "atmos-alerts-window-invalid-state";
|
||||||
|
|
||||||
|
AlarmStateLabel.Text = Loc.GetString(alarmString);
|
||||||
|
AlarmStateLabel.FontColorOverride = GetAlarmStateColor(entry.AlarmState);
|
||||||
|
|
||||||
|
// Update alarm name
|
||||||
|
AlarmNameLabel.Text = Loc.GetString("atmos-alerts-window-alarm-label", ("name", entry.EntityName), ("address", entry.Address));
|
||||||
|
|
||||||
|
// Focus updates
|
||||||
|
FocusContainer.Visible = isFocus;
|
||||||
|
|
||||||
|
if (isFocus)
|
||||||
|
SetAsFocus();
|
||||||
|
else
|
||||||
|
RemoveAsFocus();
|
||||||
|
|
||||||
|
if (isFocus && entry.Group == AtmosAlertsComputerGroup.AirAlarm)
|
||||||
|
{
|
||||||
|
MainDataContainer.Visible = (entry.AlarmState != AtmosAlarmType.Invalid);
|
||||||
|
NoDataLabel.Visible = (entry.AlarmState == AtmosAlarmType.Invalid);
|
||||||
|
|
||||||
|
if (focusData != null)
|
||||||
|
{
|
||||||
|
// Update temperature
|
||||||
|
var tempK = (FixedPoint2) focusData.Value.TemperatureData.Item1;
|
||||||
|
var tempC = (FixedPoint2) TemperatureHelpers.KelvinToCelsius(tempK.Float());
|
||||||
|
|
||||||
|
TemperatureLabel.Text = Loc.GetString("atmos-alerts-window-temperature-value", ("valueInC", tempC), ("valueInK", tempK));
|
||||||
|
TemperatureLabel.FontColorOverride = GetAlarmStateColor(focusData.Value.TemperatureData.Item2);
|
||||||
|
|
||||||
|
// Update pressure
|
||||||
|
PressureLabel.Text = Loc.GetString("atmos-alerts-window-pressure-value", ("value", (FixedPoint2) focusData.Value.PressureData.Item1));
|
||||||
|
PressureLabel.FontColorOverride = GetAlarmStateColor(focusData.Value.PressureData.Item2);
|
||||||
|
|
||||||
|
// Update oxygenation
|
||||||
|
var oxygenPercent = (FixedPoint2) 0f;
|
||||||
|
var oxygenAlert = AtmosAlarmType.Invalid;
|
||||||
|
|
||||||
|
if (focusData.Value.GasData.TryGetValue(Gas.Oxygen, out var oxygenData))
|
||||||
|
{
|
||||||
|
oxygenPercent = oxygenData.Item2 * 100f;
|
||||||
|
oxygenAlert = oxygenData.Item3;
|
||||||
|
}
|
||||||
|
|
||||||
|
OxygenationLabel.Text = Loc.GetString("atmos-alerts-window-oxygenation-value", ("value", oxygenPercent));
|
||||||
|
OxygenationLabel.FontColorOverride = GetAlarmStateColor(oxygenAlert);
|
||||||
|
|
||||||
|
// Update other present gases
|
||||||
|
GasGridContainer.RemoveAllChildren();
|
||||||
|
|
||||||
|
var gasData = focusData.Value.GasData.Where(g => g.Key != Gas.Oxygen);
|
||||||
|
|
||||||
|
if (gasData.Count() == 0)
|
||||||
|
{
|
||||||
|
// No other 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, (var mol, var percent, var alert)) in gasData)
|
||||||
|
{
|
||||||
|
var gasPercent = (FixedPoint2) 0f;
|
||||||
|
gasPercent = percent * 100f;
|
||||||
|
|
||||||
|
if (!_gasShorthands.TryGetValue(gas, out var gasShorthand))
|
||||||
|
gasShorthand = "X";
|
||||||
|
|
||||||
|
var gasLabel = new Label()
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("atmos-alerts-window-other-gases-value", ("shorthand", gasShorthand), ("value", gasPercent)),
|
||||||
|
FontOverride = normalFont,
|
||||||
|
FontColorOverride = GetAlarmStateColor(alert),
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveAsFocus()
|
||||||
|
{
|
||||||
|
FocusButton.RemoveStyleClass(StyleNano.StyleClassButtonColorGreen);
|
||||||
|
ArrowTexture.TexturePath = "/Textures/Interface/Nano/triangle_right.png";
|
||||||
|
FocusContainer.Visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color GetAlarmStateColor(AtmosAlarmType alarmType)
|
||||||
|
{
|
||||||
|
switch (alarmType)
|
||||||
|
{
|
||||||
|
case AtmosAlarmType.Normal:
|
||||||
|
return StyleNano.GoodGreenFore;
|
||||||
|
case AtmosAlarmType.Warning:
|
||||||
|
return StyleNano.ConcerningOrangeFore;
|
||||||
|
case AtmosAlarmType.Danger:
|
||||||
|
return StyleNano.DangerousRedFore;
|
||||||
|
}
|
||||||
|
|
||||||
|
return StyleNano.DisabledFore;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
using Content.Shared.Atmos.Components;
|
||||||
|
|
||||||
|
namespace Content.Client.Atmos.Consoles;
|
||||||
|
|
||||||
|
public sealed class AtmosAlertsComputerBoundUserInterface : BoundUserInterface
|
||||||
|
{
|
||||||
|
[ViewVariables]
|
||||||
|
private AtmosAlertsComputerWindow? _menu;
|
||||||
|
|
||||||
|
public AtmosAlertsComputerBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { }
|
||||||
|
|
||||||
|
protected override void Open()
|
||||||
|
{
|
||||||
|
_menu = new AtmosAlertsComputerWindow(this, Owner);
|
||||||
|
_menu.OpenCentered();
|
||||||
|
_menu.OnClose += Close;
|
||||||
|
|
||||||
|
EntMan.TryGetComponent<TransformComponent>(Owner, out var xform);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateState(BoundUserInterfaceState state)
|
||||||
|
{
|
||||||
|
base.UpdateState(state);
|
||||||
|
|
||||||
|
var castState = (AtmosAlertsComputerBoundInterfaceState) state;
|
||||||
|
|
||||||
|
if (castState == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
EntMan.TryGetComponent<TransformComponent>(Owner, out var xform);
|
||||||
|
_menu?.UpdateUI(xform?.Coordinates, castState.AirAlarms, castState.FireAlarms, castState.FocusData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendFocusChangeMessage(NetEntity? netEntity)
|
||||||
|
{
|
||||||
|
SendMessage(new AtmosAlertsComputerFocusChangeMessage(netEntity));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendDeviceSilencedMessage(NetEntity netEntity, bool silenceDevice)
|
||||||
|
{
|
||||||
|
SendMessage(new AtmosAlertsComputerDeviceSilencedMessage(netEntity, silenceDevice));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
base.Dispose(disposing);
|
||||||
|
if (!disposing)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_menu?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
108
Content.Client/Atmos/Consoles/AtmosAlertsComputerWindow.xaml
Normal file
108
Content.Client/Atmos/Consoles/AtmosAlertsComputerWindow.xaml
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||||
|
xmlns:ui="clr-namespace:Content.Client.Pinpointer.UI"
|
||||||
|
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||||
|
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||||
|
Title="{Loc 'atmos-alerts-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:NavMapControl 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:NavMapControl>
|
||||||
|
|
||||||
|
<!-- Nav map legend -->
|
||||||
|
<BoxContainer Orientation="Horizontal" Margin="0 10 0 10">
|
||||||
|
<Label Text="{Loc 'atmos-alerts-window-label-alert-types'}"
|
||||||
|
Margin="20 0 5 0"/>
|
||||||
|
<TextureRect Stretch="KeepAspectCentered"
|
||||||
|
TexturePath="/Textures/Interface/NavMap/beveled_circle.png"
|
||||||
|
Modulate="#5A5A5A"
|
||||||
|
SetSize="16 16"
|
||||||
|
Margin="20 0 5 0"/>
|
||||||
|
<Label Text="{Loc 'atmos-alerts-window-invalid-state'}"/>
|
||||||
|
<TextureRect Stretch="KeepAspectCentered"
|
||||||
|
TexturePath="/Textures/Interface/NavMap/beveled_circle.png"
|
||||||
|
Modulate="#32cd32"
|
||||||
|
SetSize="16 16"
|
||||||
|
Margin="20 0 5 0"/>
|
||||||
|
<Label Text="{Loc 'atmos-alerts-window-normal-state'}"/>
|
||||||
|
<TextureRect Stretch="KeepAspectCentered"
|
||||||
|
TexturePath="/Textures/Interface/NavMap/beveled_triangle.png"
|
||||||
|
SetSize="16 16"
|
||||||
|
Modulate="#ffb648"
|
||||||
|
Margin="20 0 5 0"/>
|
||||||
|
<Label Text="{Loc 'atmos-alerts-window-warning-state'}"/>
|
||||||
|
<TextureRect Stretch="KeepAspectCentered"
|
||||||
|
TexturePath="/Textures/Interface/NavMap/beveled_square.png"
|
||||||
|
SetSize="16 16"
|
||||||
|
Modulate="#ff4343"
|
||||||
|
Margin="20 0 5 0"/>
|
||||||
|
<Label Text="{Loc 'atmos-alerts-window-danger-state'}"/>
|
||||||
|
</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="AlertsTable" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True" Margin="0 0 0 10"/>
|
||||||
|
</ScrollContainer>
|
||||||
|
<ScrollContainer HorizontalExpand="True" Margin="8, 8, 8, 8">
|
||||||
|
<BoxContainer Name="AirAlarmsTable" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True" Margin="0 0 0 10"/>
|
||||||
|
</ScrollContainer>
|
||||||
|
<ScrollContainer HorizontalExpand="True" Margin="8, 8, 8, 8">
|
||||||
|
<BoxContainer Name="FireAlarmsTable" 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-alerts-window-toggle-overlays'}" Margin="0 0 0 5"/>
|
||||||
|
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||||
|
<CheckBox Name="ShowInactiveAlarms" Text="{Loc 'atmos-alerts-window-invalid-state'}" Pressed="False" HorizontalExpand="True"/>
|
||||||
|
<CheckBox Name="ShowNormalAlarms" Text="{Loc 'atmos-alerts-window-normal-state'}" Pressed="False" HorizontalExpand="True"/>
|
||||||
|
<CheckBox Name="ShowWarningAlarms" Text="{Loc 'atmos-alerts-window-warning-state'}" Pressed="True" HorizontalExpand="True"/>
|
||||||
|
<CheckBox Name="ShowDangerAlarms" Text="{Loc 'atmos-alerts-window-danger-state'}" Pressed="True" 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-alerts-window-flavor-left'}" StyleClasses="WindowFooterText" />
|
||||||
|
<Label Text="{Loc 'atmos-alerts-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>
|
||||||
556
Content.Client/Atmos/Consoles/AtmosAlertsComputerWindow.xaml.cs
Normal file
556
Content.Client/Atmos/Consoles/AtmosAlertsComputerWindow.xaml.cs
Normal file
@@ -0,0 +1,556 @@
|
|||||||
|
using Content.Client.Message;
|
||||||
|
using Content.Client.Pinpointer.UI;
|
||||||
|
using Content.Client.Stylesheets;
|
||||||
|
using Content.Client.UserInterface.Controls;
|
||||||
|
using Content.Shared.Atmos.Components;
|
||||||
|
using Content.Shared.Atmos.Monitor;
|
||||||
|
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.Timing;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Content.Client.Atmos.Consoles;
|
||||||
|
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class AtmosAlertsComputerWindow : FancyWindow
|
||||||
|
{
|
||||||
|
private readonly IEntityManager _entManager;
|
||||||
|
private readonly SpriteSystem _spriteSystem;
|
||||||
|
|
||||||
|
private EntityUid? _owner;
|
||||||
|
private NetEntity? _trackedEntity;
|
||||||
|
|
||||||
|
private AtmosAlertsComputerEntry[]? _airAlarms = null;
|
||||||
|
private AtmosAlertsComputerEntry[]? _fireAlarms = null;
|
||||||
|
private IEnumerable<AtmosAlertsComputerEntry>? _activeAlarms = null;
|
||||||
|
private Dictionary<NetEntity, float> _deviceSilencingProgress = new();
|
||||||
|
|
||||||
|
public event Action<NetEntity?>? SendFocusChangeMessageAction;
|
||||||
|
public event Action<NetEntity, bool>? SendDeviceSilencedMessageAction;
|
||||||
|
|
||||||
|
private bool _autoScrollActive = false;
|
||||||
|
private bool _autoScrollAwaitsUpdate = false;
|
||||||
|
|
||||||
|
private const float SilencingDuration = 2.5f;
|
||||||
|
|
||||||
|
public AtmosAlertsComputerWindow(AtmosAlertsComputerBoundUserInterface userInterface, EntityUid? owner)
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
_entManager = IoCManager.Resolve<IEntityManager>();
|
||||||
|
_spriteSystem = _entManager.System<SpriteSystem>();
|
||||||
|
|
||||||
|
// Pass the owner to nav map
|
||||||
|
_owner = owner;
|
||||||
|
NavMap.Owner = _owner;
|
||||||
|
|
||||||
|
// Set nav map colors
|
||||||
|
NavMap.WallColor = new Color(64, 64, 64);
|
||||||
|
NavMap.TileColor = Color.DimGray * NavMap.WallColor;
|
||||||
|
|
||||||
|
// Set nav map grid uid
|
||||||
|
var stationName = Loc.GetString("atmos-alerts-window-unknown-location");
|
||||||
|
|
||||||
|
if (_entManager.TryGetComponent<TransformComponent>(owner, out var xform))
|
||||||
|
{
|
||||||
|
NavMap.MapUid = xform.GridUid;
|
||||||
|
|
||||||
|
// Assign station name
|
||||||
|
if (_entManager.TryGetComponent<MetaDataComponent>(xform.GridUid, out var stationMetaData))
|
||||||
|
stationName = stationMetaData.EntityName;
|
||||||
|
|
||||||
|
var msg = new FormattedMessage();
|
||||||
|
msg.AddMarkup(Loc.GetString("atmos-alerts-window-station-name", ("stationName", stationName)));
|
||||||
|
|
||||||
|
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-alerts-window-tab-no-alerts"));
|
||||||
|
MasterTabContainer.SetTabTitle(1, Loc.GetString("atmos-alerts-window-tab-air-alarms"));
|
||||||
|
MasterTabContainer.SetTabTitle(2, Loc.GetString("atmos-alerts-window-tab-fire-alarms"));
|
||||||
|
|
||||||
|
// Set UI toggles
|
||||||
|
ShowInactiveAlarms.OnToggled += _ => OnShowAlarmsToggled(ShowInactiveAlarms, AtmosAlarmType.Invalid);
|
||||||
|
ShowNormalAlarms.OnToggled += _ => OnShowAlarmsToggled(ShowNormalAlarms, AtmosAlarmType.Normal);
|
||||||
|
ShowWarningAlarms.OnToggled += _ => OnShowAlarmsToggled(ShowWarningAlarms, AtmosAlarmType.Warning);
|
||||||
|
ShowDangerAlarms.OnToggled += _ => OnShowAlarmsToggled(ShowDangerAlarms, AtmosAlarmType.Danger);
|
||||||
|
|
||||||
|
// Set atmos monitoring message action
|
||||||
|
SendFocusChangeMessageAction += userInterface.SendFocusChangeMessage;
|
||||||
|
SendDeviceSilencedMessageAction += userInterface.SendDeviceSilencedMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Toggle handling
|
||||||
|
|
||||||
|
private void OnShowAlarmsToggled(CheckBox toggle, AtmosAlarmType toggledAlarmState)
|
||||||
|
{
|
||||||
|
if (_owner == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!_entManager.TryGetComponent<AtmosAlertsComputerComponent>(_owner.Value, out var console))
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var device in console.AtmosDevices)
|
||||||
|
{
|
||||||
|
var alarmState = GetAlarmState(device.NetEntity, device.Group);
|
||||||
|
|
||||||
|
if (toggledAlarmState != alarmState)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (toggle.Pressed)
|
||||||
|
AddTrackedEntityToNavMap(device, alarmState);
|
||||||
|
|
||||||
|
else
|
||||||
|
NavMap.TrackedEntities.Remove(device.NetEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSilenceAlertsToggled(NetEntity netEntity, bool toggleState)
|
||||||
|
{
|
||||||
|
if (!_entManager.TryGetComponent<AtmosAlertsComputerComponent>(_owner, out var console))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (toggleState)
|
||||||
|
_deviceSilencingProgress[netEntity] = SilencingDuration;
|
||||||
|
|
||||||
|
else
|
||||||
|
_deviceSilencingProgress.Remove(netEntity);
|
||||||
|
|
||||||
|
foreach (AtmosAlarmEntryContainer entryContainer in AlertsTable.Children)
|
||||||
|
{
|
||||||
|
if (entryContainer.NetEntity == netEntity)
|
||||||
|
entryContainer.SilenceAlarmProgressBar.Visible = toggleState;
|
||||||
|
}
|
||||||
|
|
||||||
|
SendDeviceSilencedMessageAction?.Invoke(netEntity, toggleState);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public void UpdateUI(EntityCoordinates? consoleCoords, AtmosAlertsComputerEntry[] airAlarms, AtmosAlertsComputerEntry[] fireAlarms, AtmosAlertsFocusDeviceData? focusData)
|
||||||
|
{
|
||||||
|
if (_owner == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!_entManager.TryGetComponent<AtmosAlertsComputerComponent>(_owner.Value, out var console))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_trackedEntity != focusData?.NetEntity)
|
||||||
|
{
|
||||||
|
SendFocusChangeMessageAction?.Invoke(_trackedEntity);
|
||||||
|
focusData = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retain alarm data for use inbetween updates
|
||||||
|
_airAlarms = airAlarms;
|
||||||
|
_fireAlarms = fireAlarms;
|
||||||
|
|
||||||
|
var allAlarms = airAlarms.Concat(fireAlarms);
|
||||||
|
var silenced = console.SilencedDevices;
|
||||||
|
|
||||||
|
_activeAlarms = allAlarms.Where(x => x.AlarmState > AtmosAlarmType.Normal &&
|
||||||
|
(!silenced.Contains(x.NetEntity) || _deviceSilencingProgress.ContainsKey(x.NetEntity)));
|
||||||
|
|
||||||
|
// Reset nav map data
|
||||||
|
NavMap.TrackedCoordinates.Clear();
|
||||||
|
NavMap.TrackedEntities.Clear();
|
||||||
|
|
||||||
|
// Add tracked entities to the nav map
|
||||||
|
foreach (var device in console.AtmosDevices)
|
||||||
|
{
|
||||||
|
if (!NavMap.Visible)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var alarmState = GetAlarmState(device.NetEntity, device.Group);
|
||||||
|
|
||||||
|
if (_trackedEntity != device.NetEntity)
|
||||||
|
{
|
||||||
|
// Skip air alarms if the appropriate overlay is off
|
||||||
|
if (!ShowInactiveAlarms.Pressed && alarmState == AtmosAlarmType.Invalid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!ShowNormalAlarms.Pressed && alarmState == AtmosAlarmType.Normal)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!ShowWarningAlarms.Pressed && alarmState == AtmosAlarmType.Warning)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!ShowDangerAlarms.Pressed && alarmState == AtmosAlarmType.Danger)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
AddTrackedEntityToNavMap(device, alarmState);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show the monitor location
|
||||||
|
var consoleUid = _entManager.GetNetEntity(_owner);
|
||||||
|
|
||||||
|
if (consoleCoords != null && consoleUid != null)
|
||||||
|
{
|
||||||
|
var texture = _spriteSystem.Frame0(new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_circle.png")));
|
||||||
|
var blip = new NavMapBlip(consoleCoords.Value, texture, Color.Cyan, true, false);
|
||||||
|
NavMap.TrackedEntities[consoleUid.Value] = blip;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the nav map
|
||||||
|
NavMap.ForceNavMapUpdate();
|
||||||
|
|
||||||
|
// Clear excess children from the tables
|
||||||
|
var activeAlarmCount = _activeAlarms.Count();
|
||||||
|
|
||||||
|
while (AlertsTable.ChildCount > activeAlarmCount)
|
||||||
|
AlertsTable.RemoveChild(AlertsTable.GetChild(AlertsTable.ChildCount - 1));
|
||||||
|
|
||||||
|
while (AirAlarmsTable.ChildCount > airAlarms.Length)
|
||||||
|
AirAlarmsTable.RemoveChild(AirAlarmsTable.GetChild(AirAlarmsTable.ChildCount - 1));
|
||||||
|
|
||||||
|
while (FireAlarmsTable.ChildCount > fireAlarms.Length)
|
||||||
|
FireAlarmsTable.RemoveChild(FireAlarmsTable.GetChild(FireAlarmsTable.ChildCount - 1));
|
||||||
|
|
||||||
|
// Update all entries in each table
|
||||||
|
for (int index = 0; index < _activeAlarms.Count(); index++)
|
||||||
|
{
|
||||||
|
var entry = _activeAlarms.ElementAt(index);
|
||||||
|
UpdateUIEntry(entry, index, AlertsTable, console, focusData);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int index = 0; index < airAlarms.Count(); index++)
|
||||||
|
{
|
||||||
|
var entry = airAlarms.ElementAt(index);
|
||||||
|
UpdateUIEntry(entry, index, AirAlarmsTable, console, focusData);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int index = 0; index < fireAlarms.Count(); index++)
|
||||||
|
{
|
||||||
|
var entry = fireAlarms.ElementAt(index);
|
||||||
|
UpdateUIEntry(entry, index, FireAlarmsTable, console, focusData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no alerts are active, display a message
|
||||||
|
if (MasterTabContainer.CurrentTab == 0 && activeAlarmCount == 0)
|
||||||
|
{
|
||||||
|
var label = new RichTextLabel()
|
||||||
|
{
|
||||||
|
HorizontalExpand = true,
|
||||||
|
VerticalExpand = true,
|
||||||
|
HorizontalAlignment = HAlignment.Center,
|
||||||
|
VerticalAlignment = VAlignment.Center,
|
||||||
|
};
|
||||||
|
|
||||||
|
label.SetMarkup(Loc.GetString("atmos-alerts-window-no-active-alerts", ("color", StyleNano.GoodGreenFore.ToHexNoAlpha())));
|
||||||
|
|
||||||
|
AlertsTable.AddChild(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the alerts tab with the number of active alerts
|
||||||
|
if (activeAlarmCount == 0)
|
||||||
|
MasterTabContainer.SetTabTitle(0, Loc.GetString("atmos-alerts-window-tab-no-alerts"));
|
||||||
|
|
||||||
|
else
|
||||||
|
MasterTabContainer.SetTabTitle(0, Loc.GetString("atmos-alerts-window-tab-alerts", ("value", activeAlarmCount)));
|
||||||
|
|
||||||
|
// Auto-scroll re-enable
|
||||||
|
if (_autoScrollAwaitsUpdate)
|
||||||
|
{
|
||||||
|
_autoScrollActive = true;
|
||||||
|
_autoScrollAwaitsUpdate = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddTrackedEntityToNavMap(AtmosAlertsDeviceNavMapData metaData, AtmosAlarmType alarmState)
|
||||||
|
{
|
||||||
|
var data = GetBlipTexture(alarmState);
|
||||||
|
|
||||||
|
if (data == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var texture = data.Value.Item1;
|
||||||
|
var color = data.Value.Item2;
|
||||||
|
var coords = _entManager.GetCoordinates(metaData.NetCoordinates);
|
||||||
|
|
||||||
|
if (_trackedEntity != null && _trackedEntity != metaData.NetEntity)
|
||||||
|
color *= Color.DimGray;
|
||||||
|
|
||||||
|
var selectable = true;
|
||||||
|
var blip = new NavMapBlip(coords, _spriteSystem.Frame0(texture), color, _trackedEntity == metaData.NetEntity, selectable);
|
||||||
|
|
||||||
|
NavMap.TrackedEntities[metaData.NetEntity] = blip;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateUIEntry(AtmosAlertsComputerEntry entry, int index, Control table, AtmosAlertsComputerComponent console, AtmosAlertsFocusDeviceData? focusData = null)
|
||||||
|
{
|
||||||
|
// Make new UI entry if required
|
||||||
|
if (index >= table.ChildCount)
|
||||||
|
{
|
||||||
|
var newEntryContainer = new AtmosAlarmEntryContainer(entry.NetEntity, _entManager.GetCoordinates(entry.Coordinates));
|
||||||
|
|
||||||
|
// On click
|
||||||
|
newEntryContainer.FocusButton.OnButtonUp += args =>
|
||||||
|
{
|
||||||
|
var prevTrackedEntity = _trackedEntity;
|
||||||
|
|
||||||
|
if (_trackedEntity == entry.NetEntity)
|
||||||
|
{
|
||||||
|
_trackedEntity = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_trackedEntity = newEntryContainer.NetEntity;
|
||||||
|
NavMap.CenterToCoordinates(_entManager.GetCoordinates(entry.Coordinates));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send message to console that the focus has changed
|
||||||
|
SendFocusChangeMessageAction?.Invoke(_trackedEntity);
|
||||||
|
|
||||||
|
// Update affected UI elements across all tables
|
||||||
|
UpdateConsoleTable(console, AlertsTable, _trackedEntity, prevTrackedEntity);
|
||||||
|
UpdateConsoleTable(console, AirAlarmsTable, _trackedEntity, prevTrackedEntity);
|
||||||
|
UpdateConsoleTable(console, FireAlarmsTable, _trackedEntity, prevTrackedEntity);
|
||||||
|
};
|
||||||
|
|
||||||
|
// On toggling the silence check box
|
||||||
|
newEntryContainer.SilenceCheckBox.OnToggled += _ => OnSilenceAlertsToggled(entry.NetEntity, newEntryContainer.SilenceCheckBox.Pressed);
|
||||||
|
|
||||||
|
// Add the entry to the current table
|
||||||
|
table.AddChild(newEntryContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update values and UI elements
|
||||||
|
var tableChild = table.GetChild(index);
|
||||||
|
|
||||||
|
if (tableChild is not AtmosAlarmEntryContainer)
|
||||||
|
{
|
||||||
|
table.RemoveChild(tableChild);
|
||||||
|
UpdateUIEntry(entry, index, table, console, focusData);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var entryContainer = tableChild as AtmosAlarmEntryContainer;
|
||||||
|
var silenced = console.SilencedDevices;
|
||||||
|
|
||||||
|
if (entryContainer == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
entryContainer.UpdateEntry(entry, entry.NetEntity == _trackedEntity, focusData);
|
||||||
|
entryContainer.SilenceCheckBox.Pressed = (silenced.Contains(entry.NetEntity) || _deviceSilencingProgress.ContainsKey(entry.NetEntity));
|
||||||
|
entryContainer.SilenceAlarmProgressBar.Visible = (table == AlertsTable && _deviceSilencingProgress.ContainsKey(entry.NetEntity));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateConsoleTable(AtmosAlertsComputerComponent console, Control table, NetEntity? currTrackedEntity, NetEntity? prevTrackedEntity)
|
||||||
|
{
|
||||||
|
foreach (var child in table.Children)
|
||||||
|
{
|
||||||
|
if (child is not AtmosAlarmEntryContainer)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var castAlert = (AtmosAlarmEntryContainer) child;
|
||||||
|
|
||||||
|
if (castAlert.NetEntity == prevTrackedEntity)
|
||||||
|
castAlert.RemoveAsFocus();
|
||||||
|
|
||||||
|
else if (castAlert.NetEntity == currTrackedEntity)
|
||||||
|
castAlert.SetAsFocus();
|
||||||
|
|
||||||
|
if (castAlert?.Coordinates == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var device = console.AtmosDevices.FirstOrNull(x => x.NetEntity == castAlert.NetEntity);
|
||||||
|
|
||||||
|
if (device == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var alarmState = GetAlarmState(device.Value.NetEntity, device.Value.Group);
|
||||||
|
|
||||||
|
if (currTrackedEntity != device.Value.NetEntity &&
|
||||||
|
!ShowInactiveAlarms.Pressed &&
|
||||||
|
alarmState <= AtmosAlarmType.Normal)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
AddTrackedEntityToNavMap(device.Value, alarmState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetTrackedEntityFromNavMap(NetEntity? netEntity)
|
||||||
|
{
|
||||||
|
if (netEntity == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!_entManager.TryGetComponent<AtmosAlertsComputerComponent>(_owner, out var console))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_trackedEntity = netEntity;
|
||||||
|
|
||||||
|
if (netEntity != null)
|
||||||
|
{
|
||||||
|
// Tab switching
|
||||||
|
if (MasterTabContainer.CurrentTab != 0 || _activeAlarms?.Any(x => x.NetEntity == netEntity) == false)
|
||||||
|
{
|
||||||
|
var device = console.AtmosDevices.FirstOrNull(x => x.NetEntity == netEntity);
|
||||||
|
|
||||||
|
switch (device?.Group)
|
||||||
|
{
|
||||||
|
case AtmosAlertsComputerGroup.AirAlarm:
|
||||||
|
MasterTabContainer.CurrentTab = 1; break;
|
||||||
|
case AtmosAlertsComputerGroup.FireAlarm:
|
||||||
|
MasterTabContainer.CurrentTab = 2; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the scroll position of the selected entity on the selected button the UI
|
||||||
|
ActivateAutoScrollToFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send message to console that the focus has changed
|
||||||
|
SendFocusChangeMessageAction?.Invoke(_trackedEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void FrameUpdate(FrameEventArgs args)
|
||||||
|
{
|
||||||
|
AutoScrollToFocus();
|
||||||
|
|
||||||
|
// Device silencing update
|
||||||
|
foreach ((var device, var remainingTime) in _deviceSilencingProgress)
|
||||||
|
{
|
||||||
|
var t = remainingTime - args.DeltaSeconds;
|
||||||
|
|
||||||
|
if (t <= 0)
|
||||||
|
_deviceSilencingProgress.Remove(device);
|
||||||
|
|
||||||
|
else
|
||||||
|
_deviceSilencingProgress[device] = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ActivateAutoScrollToFocus()
|
||||||
|
{
|
||||||
|
_autoScrollActive = false;
|
||||||
|
_autoScrollAwaitsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AutoScrollToFocus()
|
||||||
|
{
|
||||||
|
if (!_autoScrollActive)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var scroll = MasterTabContainer.Children.ElementAt(MasterTabContainer.CurrentTab) 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 child in scroll.Children)
|
||||||
|
{
|
||||||
|
if (child is not VScrollBar)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var castChild = child as VScrollBar;
|
||||||
|
|
||||||
|
if (castChild != null)
|
||||||
|
{
|
||||||
|
vScrollBar = castChild;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryGetNextScrollPosition([NotNullWhen(true)] out float? nextScrollPosition)
|
||||||
|
{
|
||||||
|
nextScrollPosition = null;
|
||||||
|
|
||||||
|
var scroll = MasterTabContainer.Children.ElementAt(MasterTabContainer.CurrentTab) 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 == null || control is not AtmosAlarmEntryContainer)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (((AtmosAlarmEntryContainer) control).NetEntity == _trackedEntity)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
nextScrollPosition += control.Height;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Failed to find control
|
||||||
|
nextScrollPosition = null;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AtmosAlarmType GetAlarmState(NetEntity netEntity, AtmosAlertsComputerGroup group)
|
||||||
|
{
|
||||||
|
var alarms = (group == AtmosAlertsComputerGroup.AirAlarm) ? _airAlarms : _fireAlarms;
|
||||||
|
var alarmState = alarms?.FirstOrNull(x => x.NetEntity == netEntity)?.AlarmState;
|
||||||
|
|
||||||
|
if (alarmState == null)
|
||||||
|
return AtmosAlarmType.Invalid;
|
||||||
|
|
||||||
|
return alarmState.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private (SpriteSpecifier.Texture, Color)? GetBlipTexture(AtmosAlarmType alarmState)
|
||||||
|
{
|
||||||
|
(SpriteSpecifier.Texture, Color)? output = null;
|
||||||
|
|
||||||
|
switch (alarmState)
|
||||||
|
{
|
||||||
|
case AtmosAlarmType.Invalid:
|
||||||
|
output = (new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_circle.png")), StyleNano.DisabledFore); break;
|
||||||
|
case AtmosAlarmType.Normal:
|
||||||
|
output = (new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_circle.png")), Color.LimeGreen); break;
|
||||||
|
case AtmosAlarmType.Warning:
|
||||||
|
output = (new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_triangle.png")), new Color(255, 182, 72)); break;
|
||||||
|
case AtmosAlarmType.Danger:
|
||||||
|
output = (new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_square.png")), new Color(255, 67, 67)); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
348
Content.Server/Atmos/Consoles/AtmosAlertsComputerSystem.cs
Normal file
348
Content.Server/Atmos/Consoles/AtmosAlertsComputerSystem.cs
Normal file
@@ -0,0 +1,348 @@
|
|||||||
|
using Content.Server.Atmos.Monitor.Components;
|
||||||
|
using Content.Server.DeviceNetwork.Components;
|
||||||
|
using Content.Server.Power.Components;
|
||||||
|
using Content.Shared.Atmos;
|
||||||
|
using Content.Shared.Atmos.Components;
|
||||||
|
using Content.Shared.Atmos.Consoles;
|
||||||
|
using Content.Shared.Atmos.Monitor;
|
||||||
|
using Content.Shared.Atmos.Monitor.Components;
|
||||||
|
using Content.Shared.Pinpointer;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
|
using Robust.Shared.Map.Components;
|
||||||
|
using Robust.Shared.Player;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Content.Server.Atmos.Monitor.Systems;
|
||||||
|
|
||||||
|
public sealed class AtmosAlertsComputerSystem : SharedAtmosAlertsComputerSystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
|
||||||
|
[Dependency] private readonly AirAlarmSystem _airAlarmSystem = default!;
|
||||||
|
[Dependency] private readonly AtmosDeviceNetworkSystem _atmosDevNet = default!;
|
||||||
|
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||||
|
|
||||||
|
private const float UpdateTime = 1.0f;
|
||||||
|
|
||||||
|
// Note: this data does not need to be saved
|
||||||
|
private float _updateTimer = 1.0f;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
// Console events
|
||||||
|
SubscribeLocalEvent<AtmosAlertsComputerComponent, ComponentInit>(OnConsoleInit);
|
||||||
|
SubscribeLocalEvent<AtmosAlertsComputerComponent, EntParentChangedMessage>(OnConsoleParentChanged);
|
||||||
|
SubscribeLocalEvent<AtmosAlertsComputerComponent, AtmosAlertsComputerFocusChangeMessage>(OnFocusChangedMessage);
|
||||||
|
|
||||||
|
// Grid events
|
||||||
|
SubscribeLocalEvent<GridSplitEvent>(OnGridSplit);
|
||||||
|
SubscribeLocalEvent<AtmosAlertsDeviceComponent, AnchorStateChangedEvent>(OnDeviceAnchorChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Event handling
|
||||||
|
|
||||||
|
private void OnConsoleInit(EntityUid uid, AtmosAlertsComputerComponent component, ComponentInit args)
|
||||||
|
{
|
||||||
|
InitalizeConsole(uid, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnConsoleParentChanged(EntityUid uid, AtmosAlertsComputerComponent component, EntParentChangedMessage args)
|
||||||
|
{
|
||||||
|
InitalizeConsole(uid, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnFocusChangedMessage(EntityUid uid, AtmosAlertsComputerComponent component, AtmosAlertsComputerFocusChangeMessage args)
|
||||||
|
{
|
||||||
|
component.FocusDevice = args.FocusDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGridSplit(ref GridSplitEvent args)
|
||||||
|
{
|
||||||
|
// Collect grids
|
||||||
|
var allGrids = args.NewGrids.ToList();
|
||||||
|
|
||||||
|
if (!allGrids.Contains(args.Grid))
|
||||||
|
allGrids.Add(args.Grid);
|
||||||
|
|
||||||
|
// Update atmos monitoring consoles that stand upon an updated grid
|
||||||
|
var query = AllEntityQuery<AtmosAlertsComputerComponent, 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;
|
||||||
|
|
||||||
|
InitalizeConsole(ent, entConsole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDeviceAnchorChanged(EntityUid uid, AtmosAlertsDeviceComponent component, AnchorStateChangedEvent args)
|
||||||
|
{
|
||||||
|
var xform = Transform(uid);
|
||||||
|
var gridUid = xform.GridUid;
|
||||||
|
|
||||||
|
if (gridUid == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!TryGetAtmosDeviceNavMapData(uid, component, xform, gridUid.Value, out var data))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var netEntity = EntityManager.GetNetEntity(uid);
|
||||||
|
|
||||||
|
var query = AllEntityQuery<AtmosAlertsComputerComponent, TransformComponent>();
|
||||||
|
while (query.MoveNext(out var ent, out var entConsole, out var entXform))
|
||||||
|
{
|
||||||
|
if (gridUid != entXform.GridUid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (args.Anchored)
|
||||||
|
entConsole.AtmosDevices.Add(data.Value);
|
||||||
|
|
||||||
|
else if (!args.Anchored)
|
||||||
|
entConsole.AtmosDevices.RemoveWhere(x => x.NetEntity == netEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
base.Update(frameTime);
|
||||||
|
|
||||||
|
_updateTimer += frameTime;
|
||||||
|
|
||||||
|
if (_updateTimer >= UpdateTime)
|
||||||
|
{
|
||||||
|
_updateTimer -= UpdateTime;
|
||||||
|
|
||||||
|
// Keep a list of UI entries for each gridUid, in case multiple consoles stand on the same grid
|
||||||
|
var airAlarmEntriesForEachGrid = new Dictionary<EntityUid, AtmosAlertsComputerEntry[]>();
|
||||||
|
var fireAlarmEntriesForEachGrid = new Dictionary<EntityUid, AtmosAlertsComputerEntry[]>();
|
||||||
|
|
||||||
|
var query = AllEntityQuery<AtmosAlertsComputerComponent, TransformComponent>();
|
||||||
|
while (query.MoveNext(out var ent, out var entConsole, out var entXform))
|
||||||
|
{
|
||||||
|
if (entXform?.GridUid == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Make a list of alarm state data for all the air and fire alarms on the grid
|
||||||
|
if (!airAlarmEntriesForEachGrid.TryGetValue(entXform.GridUid.Value, out var airAlarmEntries))
|
||||||
|
{
|
||||||
|
airAlarmEntries = GetAlarmStateData(entXform.GridUid.Value, AtmosAlertsComputerGroup.AirAlarm).ToArray();
|
||||||
|
airAlarmEntriesForEachGrid[entXform.GridUid.Value] = airAlarmEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fireAlarmEntriesForEachGrid.TryGetValue(entXform.GridUid.Value, out var fireAlarmEntries))
|
||||||
|
{
|
||||||
|
fireAlarmEntries = GetAlarmStateData(entXform.GridUid.Value, AtmosAlertsComputerGroup.FireAlarm).ToArray();
|
||||||
|
fireAlarmEntriesForEachGrid[entXform.GridUid.Value] = fireAlarmEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the highest level of alert for the console (based on non-silenced alarms)
|
||||||
|
var highestAlert = AtmosAlarmType.Invalid;
|
||||||
|
|
||||||
|
foreach (var entry in airAlarmEntries)
|
||||||
|
{
|
||||||
|
if (entry.AlarmState > highestAlert && !entConsole.SilencedDevices.Contains(entry.NetEntity))
|
||||||
|
highestAlert = entry.AlarmState;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var entry in fireAlarmEntries)
|
||||||
|
{
|
||||||
|
if (entry.AlarmState > highestAlert && !entConsole.SilencedDevices.Contains(entry.NetEntity))
|
||||||
|
highestAlert = entry.AlarmState;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the appearance of the console based on the highest recorded level of alert
|
||||||
|
if (TryComp<AppearanceComponent>(ent, out var entAppearance))
|
||||||
|
_appearance.SetData(ent, AtmosAlertsComputerVisuals.ComputerLayerScreen, (int) highestAlert, entAppearance);
|
||||||
|
|
||||||
|
// If the console UI is open, send UI data to each subscribed session
|
||||||
|
UpdateUIState(ent, airAlarmEntries, fireAlarmEntries, entConsole, entXform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateUIState
|
||||||
|
(EntityUid uid,
|
||||||
|
AtmosAlertsComputerEntry[] airAlarmStateData,
|
||||||
|
AtmosAlertsComputerEntry[] fireAlarmStateData,
|
||||||
|
AtmosAlertsComputerComponent component,
|
||||||
|
TransformComponent xform)
|
||||||
|
{
|
||||||
|
if (!_userInterfaceSystem.IsUiOpen(uid, AtmosAlertsComputerUiKey.Key))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var gridUid = xform.GridUid!.Value;
|
||||||
|
|
||||||
|
if (!HasComp<MapGridComponent>(gridUid))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// The grid must have a NavMapComponent to visualize the map in the UI
|
||||||
|
EnsureComp<NavMapComponent>(gridUid);
|
||||||
|
|
||||||
|
// Gathering remaining data to be send to the client
|
||||||
|
var focusAlarmData = GetFocusAlarmData(uid, GetEntity(component.FocusDevice), gridUid);
|
||||||
|
|
||||||
|
// Set the UI state
|
||||||
|
_userInterfaceSystem.SetUiState(uid, AtmosAlertsComputerUiKey.Key,
|
||||||
|
new AtmosAlertsComputerBoundInterfaceState(airAlarmStateData, fireAlarmStateData, focusAlarmData));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<AtmosAlertsComputerEntry> GetAlarmStateData(EntityUid gridUid, AtmosAlertsComputerGroup group)
|
||||||
|
{
|
||||||
|
var alarmStateData = new List<AtmosAlertsComputerEntry>();
|
||||||
|
|
||||||
|
var queryAlarms = AllEntityQuery<AtmosAlertsDeviceComponent, AtmosAlarmableComponent, DeviceNetworkComponent, TransformComponent>();
|
||||||
|
while (queryAlarms.MoveNext(out var ent, out var entDevice, out var entAtmosAlarmable, out var entDeviceNetwork, out var entXform))
|
||||||
|
{
|
||||||
|
if (entXform.GridUid != gridUid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!entXform.Anchored)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (entDevice.Group != group)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// If emagged, change the alarm type to normal
|
||||||
|
var alarmState = (entAtmosAlarmable.LastAlarmState == AtmosAlarmType.Emagged) ? AtmosAlarmType.Normal : entAtmosAlarmable.LastAlarmState;
|
||||||
|
|
||||||
|
// Unpowered alarms can't sound
|
||||||
|
if (TryComp<ApcPowerReceiverComponent>(ent, out var entAPCPower) && !entAPCPower.Powered)
|
||||||
|
alarmState = AtmosAlarmType.Invalid;
|
||||||
|
|
||||||
|
var entry = new AtmosAlertsComputerEntry
|
||||||
|
(GetNetEntity(ent),
|
||||||
|
GetNetCoordinates(entXform.Coordinates),
|
||||||
|
entDevice.Group,
|
||||||
|
alarmState,
|
||||||
|
MetaData(ent).EntityName,
|
||||||
|
entDeviceNetwork.Address);
|
||||||
|
|
||||||
|
alarmStateData.Add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return alarmStateData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AtmosAlertsFocusDeviceData? GetFocusAlarmData(EntityUid uid, EntityUid? focusDevice, EntityUid gridUid)
|
||||||
|
{
|
||||||
|
if (focusDevice == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var focusDeviceXform = Transform(focusDevice.Value);
|
||||||
|
|
||||||
|
if (!focusDeviceXform.Anchored ||
|
||||||
|
focusDeviceXform.GridUid != gridUid ||
|
||||||
|
!TryComp<AirAlarmComponent>(focusDevice.Value, out var focusDeviceAirAlarm))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force update the sensors attached to the alarm
|
||||||
|
if (!_userInterfaceSystem.IsUiOpen(focusDevice.Value, SharedAirAlarmInterfaceKey.Key))
|
||||||
|
{
|
||||||
|
_atmosDevNet.Register(focusDevice.Value, null);
|
||||||
|
_atmosDevNet.Sync(focusDevice.Value, null);
|
||||||
|
|
||||||
|
foreach ((var address, var _) in focusDeviceAirAlarm.SensorData)
|
||||||
|
_atmosDevNet.Register(uid, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the sensor data
|
||||||
|
var temperatureData = (_airAlarmSystem.CalculateTemperatureAverage(focusDeviceAirAlarm), AtmosAlarmType.Normal);
|
||||||
|
var pressureData = (_airAlarmSystem.CalculatePressureAverage(focusDeviceAirAlarm), AtmosAlarmType.Normal);
|
||||||
|
var gasData = new Dictionary<Gas, (float, float, AtmosAlarmType)>();
|
||||||
|
|
||||||
|
foreach ((var address, var sensorData) in focusDeviceAirAlarm.SensorData)
|
||||||
|
{
|
||||||
|
if (sensorData.TemperatureThreshold.CheckThreshold(sensorData.Temperature, out var temperatureState) &&
|
||||||
|
(int) temperatureState > (int) temperatureData.Item2)
|
||||||
|
{
|
||||||
|
temperatureData = (temperatureData.Item1, temperatureState);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sensorData.PressureThreshold.CheckThreshold(sensorData.Pressure, out var pressureState) &&
|
||||||
|
(int) pressureState > (int) pressureData.Item2)
|
||||||
|
{
|
||||||
|
pressureData = (pressureData.Item1, pressureState);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (focusDeviceAirAlarm.SensorData.Sum(g => g.Value.TotalMoles) > 1e-8)
|
||||||
|
{
|
||||||
|
foreach ((var gas, var threshold) in sensorData.GasThresholds)
|
||||||
|
{
|
||||||
|
if (!gasData.ContainsKey(gas))
|
||||||
|
{
|
||||||
|
float mol = _airAlarmSystem.CalculateGasMolarConcentrationAverage(focusDeviceAirAlarm, gas, out var percentage);
|
||||||
|
|
||||||
|
if (mol < 1e-8)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
gasData[gas] = (mol, percentage, AtmosAlarmType.Normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (threshold.CheckThreshold(gasData[gas].Item2, out var gasState) &&
|
||||||
|
(int) gasState > (int) gasData[gas].Item3)
|
||||||
|
{
|
||||||
|
gasData[gas] = (gasData[gas].Item1, gasData[gas].Item2, gasState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AtmosAlertsFocusDeviceData(GetNetEntity(focusDevice.Value), temperatureData, pressureData, gasData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashSet<AtmosAlertsDeviceNavMapData> GetAllAtmosDeviceNavMapData(EntityUid gridUid)
|
||||||
|
{
|
||||||
|
var atmosDeviceNavMapData = new HashSet<AtmosAlertsDeviceNavMapData>();
|
||||||
|
|
||||||
|
var query = AllEntityQuery<AtmosAlertsDeviceComponent, 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return atmosDeviceNavMapData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryGetAtmosDeviceNavMapData
|
||||||
|
(EntityUid uid,
|
||||||
|
AtmosAlertsDeviceComponent component,
|
||||||
|
TransformComponent xform,
|
||||||
|
EntityUid gridUid,
|
||||||
|
[NotNullWhen(true)] out AtmosAlertsDeviceNavMapData? output)
|
||||||
|
{
|
||||||
|
output = null;
|
||||||
|
|
||||||
|
if (xform.GridUid != gridUid)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!xform.Anchored)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
output = new AtmosAlertsDeviceNavMapData(GetNetEntity(uid), GetNetCoordinates(xform.Coordinates), component.Group);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitalizeConsole(EntityUid uid, AtmosAlertsComputerComponent component)
|
||||||
|
{
|
||||||
|
var xform = Transform(uid);
|
||||||
|
|
||||||
|
if (xform.GridUid == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var grid = xform.GridUid.Value;
|
||||||
|
component.AtmosDevices = GetAllAtmosDeviceNavMapData(grid);
|
||||||
|
|
||||||
|
Dirty(uid, component);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
using System.Linq;
|
|
||||||
using Content.Server.Atmos.Monitor.Components;
|
using Content.Server.Atmos.Monitor.Components;
|
||||||
using Content.Server.Atmos.Piping.Components;
|
using Content.Server.Atmos.Piping.Components;
|
||||||
using Content.Server.DeviceLinking.Systems;
|
using Content.Server.DeviceLinking.Systems;
|
||||||
@@ -22,6 +21,7 @@ using Content.Shared.Power;
|
|||||||
using Content.Shared.Wires;
|
using Content.Shared.Wires;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Content.Server.Atmos.Monitor.Systems;
|
namespace Content.Server.Atmos.Monitor.Systems;
|
||||||
|
|
||||||
@@ -582,6 +582,20 @@ public sealed class AirAlarmSystem : EntitySystem
|
|||||||
? alarm.SensorData.Values.Select(v => v.Temperature).Average()
|
? alarm.SensorData.Values.Select(v => v.Temperature).Average()
|
||||||
: 0f;
|
: 0f;
|
||||||
}
|
}
|
||||||
|
public float CalculateGasMolarConcentrationAverage(AirAlarmComponent alarm, Gas gas, out float percentage)
|
||||||
|
{
|
||||||
|
percentage = 0f;
|
||||||
|
|
||||||
|
var data = alarm.SensorData.Values.SelectMany(v => v.Gases.Where(g => g.Key == gas));
|
||||||
|
|
||||||
|
if (data.Count() == 0)
|
||||||
|
return 0f;
|
||||||
|
|
||||||
|
var averageMol = data.Select(kvp => kvp.Value).Average();
|
||||||
|
percentage = data.Select(kvp => kvp.Value).Sum() / alarm.SensorData.Values.Select(v => v.TotalMoles).Sum();
|
||||||
|
|
||||||
|
return averageMol;
|
||||||
|
}
|
||||||
|
|
||||||
public void UpdateUI(EntityUid uid, AirAlarmComponent? alarm = null, DeviceNetworkComponent? devNet = null, AtmosAlarmableComponent? alarmable = null)
|
public void UpdateUI(EntityUid uid, AirAlarmComponent? alarm = null, DeviceNetworkComponent? devNet = null, AtmosAlarmableComponent? alarmable = null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,235 @@
|
|||||||
|
using Content.Shared.Atmos.Consoles;
|
||||||
|
using Content.Shared.Atmos.Monitor;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Atmos.Components;
|
||||||
|
|
||||||
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
|
[Access(typeof(SharedAtmosAlertsComputerSystem))]
|
||||||
|
public sealed partial class AtmosAlertsComputerComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The current entity of interest (selected via the console UI)
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public NetEntity? FocusDevice;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A list of all the atmos devices that will be used to populate the nav map
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables, AutoNetworkedField]
|
||||||
|
public HashSet<AtmosAlertsDeviceNavMapData> AtmosDevices = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A list of all the air alarms that have had their alerts silenced on this particular console
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables, AutoNetworkedField]
|
||||||
|
public HashSet<NetEntity> SilencedDevices = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public struct AtmosAlertsDeviceNavMapData
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The entity in question
|
||||||
|
/// </summary>
|
||||||
|
public NetEntity NetEntity;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Location of the entity
|
||||||
|
/// </summary>
|
||||||
|
public NetCoordinates NetCoordinates;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to determine what map icons to use
|
||||||
|
/// </summary>
|
||||||
|
public AtmosAlertsComputerGroup Group;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Populate the atmos monitoring console nav map with a single entity
|
||||||
|
/// </summary>
|
||||||
|
public AtmosAlertsDeviceNavMapData(NetEntity netEntity, NetCoordinates netCoordinates, AtmosAlertsComputerGroup group)
|
||||||
|
{
|
||||||
|
NetEntity = netEntity;
|
||||||
|
NetCoordinates = netCoordinates;
|
||||||
|
Group = group;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public struct AtmosAlertsFocusDeviceData
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Focus entity
|
||||||
|
/// </summary>
|
||||||
|
public NetEntity NetEntity;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Temperature (K) and related alert state
|
||||||
|
/// </summary>
|
||||||
|
public (float, AtmosAlarmType) TemperatureData;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pressure (kPA) and related alert state
|
||||||
|
/// </summary>
|
||||||
|
public (float, AtmosAlarmType) PressureData;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Moles, percentage, and related alert state, for all detected gases
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<Gas, (float, float, AtmosAlarmType)> GasData;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Populates the atmos monitoring console focus entry with atmospheric data
|
||||||
|
/// </summary>
|
||||||
|
public AtmosAlertsFocusDeviceData
|
||||||
|
(NetEntity netEntity,
|
||||||
|
(float, AtmosAlarmType) temperatureData,
|
||||||
|
(float, AtmosAlarmType) pressureData,
|
||||||
|
Dictionary<Gas, (float, float, AtmosAlarmType)> gasData)
|
||||||
|
{
|
||||||
|
NetEntity = netEntity;
|
||||||
|
TemperatureData = temperatureData;
|
||||||
|
PressureData = pressureData;
|
||||||
|
GasData = gasData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class AtmosAlertsComputerBoundInterfaceState : BoundUserInterfaceState
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A list of all air alarms
|
||||||
|
/// </summary>
|
||||||
|
public AtmosAlertsComputerEntry[] AirAlarms;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A list of all fire alarms
|
||||||
|
/// </summary>
|
||||||
|
public AtmosAlertsComputerEntry[] FireAlarms;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Data for the UI focus (if applicable)
|
||||||
|
/// </summary>
|
||||||
|
public AtmosAlertsFocusDeviceData? FocusData;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends data from the server to the client to populate the atmos monitoring console UI
|
||||||
|
/// </summary>
|
||||||
|
public AtmosAlertsComputerBoundInterfaceState(AtmosAlertsComputerEntry[] airAlarms, AtmosAlertsComputerEntry[] fireAlarms, AtmosAlertsFocusDeviceData? focusData)
|
||||||
|
{
|
||||||
|
AirAlarms = airAlarms;
|
||||||
|
FireAlarms = fireAlarms;
|
||||||
|
FocusData = focusData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public struct AtmosAlertsComputerEntry
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The entity in question
|
||||||
|
/// </summary>
|
||||||
|
public NetEntity NetEntity;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Location of the entity
|
||||||
|
/// </summary>
|
||||||
|
public NetCoordinates Coordinates;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The type of entity
|
||||||
|
/// </summary>
|
||||||
|
public AtmosAlertsComputerGroup Group;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Current alarm state
|
||||||
|
/// </summary>
|
||||||
|
public AtmosAlarmType AlarmState;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Localised device name
|
||||||
|
/// </summary>
|
||||||
|
public string EntityName;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Device network address
|
||||||
|
/// </summary>
|
||||||
|
public string Address;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to populate the atmos monitoring console UI with data from a single air alarm
|
||||||
|
/// </summary>
|
||||||
|
public AtmosAlertsComputerEntry
|
||||||
|
(NetEntity entity,
|
||||||
|
NetCoordinates coordinates,
|
||||||
|
AtmosAlertsComputerGroup group,
|
||||||
|
AtmosAlarmType alarmState,
|
||||||
|
string entityName,
|
||||||
|
string address)
|
||||||
|
{
|
||||||
|
NetEntity = entity;
|
||||||
|
Coordinates = coordinates;
|
||||||
|
Group = group;
|
||||||
|
AlarmState = alarmState;
|
||||||
|
EntityName = entityName;
|
||||||
|
Address = address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class AtmosAlertsComputerFocusChangeMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public NetEntity? FocusDevice;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to inform the server that the specified focus for the atmos monitoring console has been changed by the client
|
||||||
|
/// </summary>
|
||||||
|
public AtmosAlertsComputerFocusChangeMessage(NetEntity? focusDevice)
|
||||||
|
{
|
||||||
|
FocusDevice = focusDevice;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class AtmosAlertsComputerDeviceSilencedMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public NetEntity AtmosDevice;
|
||||||
|
public bool SilenceDevice = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to inform the server that the client has silenced alerts from the specified device to this atmos monitoring console
|
||||||
|
/// </summary>
|
||||||
|
public AtmosAlertsComputerDeviceSilencedMessage(NetEntity atmosDevice, bool silenceDevice = true)
|
||||||
|
{
|
||||||
|
AtmosDevice = atmosDevice;
|
||||||
|
SilenceDevice = silenceDevice;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// List of all the different atmos device groups
|
||||||
|
/// </summary>
|
||||||
|
public enum AtmosAlertsComputerGroup
|
||||||
|
{
|
||||||
|
Invalid,
|
||||||
|
AirAlarm,
|
||||||
|
FireAlarm,
|
||||||
|
}
|
||||||
|
|
||||||
|
[NetSerializable, Serializable]
|
||||||
|
public enum AtmosAlertsComputerVisuals
|
||||||
|
{
|
||||||
|
ComputerLayerScreen,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// UI key associated with the atmos monitoring console
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum AtmosAlertsComputerUiKey
|
||||||
|
{
|
||||||
|
Key
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Atmos.Components;
|
||||||
|
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
[Access([])]
|
||||||
|
public sealed partial class AtmosAlertsDeviceComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The group that the entity belongs to
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables]
|
||||||
|
public AtmosAlertsComputerGroup Group;
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using Content.Shared.Atmos.Components;
|
||||||
|
|
||||||
|
namespace Content.Shared.Atmos.Consoles;
|
||||||
|
|
||||||
|
public abstract partial class SharedAtmosAlertsComputerSystem : EntitySystem
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<AtmosAlertsComputerComponent, AtmosAlertsComputerDeviceSilencedMessage>(OnDeviceSilencedMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDeviceSilencedMessage(EntityUid uid, AtmosAlertsComputerComponent component, AtmosAlertsComputerDeviceSilencedMessage args)
|
||||||
|
{
|
||||||
|
if (args.SilenceDevice)
|
||||||
|
component.SilencedDevices.Add(args.AtmosDevice);
|
||||||
|
|
||||||
|
else
|
||||||
|
component.SilencedDevices.Remove(args.AtmosDevice);
|
||||||
|
|
||||||
|
Dirty(uid, component);
|
||||||
|
}
|
||||||
|
}
|
||||||
35
Resources/Locale/en-US/atmos/atmos-alerts-console.ftl
Normal file
35
Resources/Locale/en-US/atmos/atmos-alerts-console.ftl
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
atmos-alerts-window-title = Atmospheric Alerts Computer
|
||||||
|
atmos-alerts-window-station-name = [color=white][font size=14]{$stationName}[/font][/color]
|
||||||
|
atmos-alerts-window-unknown-location = Unknown location
|
||||||
|
|
||||||
|
atmos-alerts-window-tab-no-alerts = Alerts
|
||||||
|
atmos-alerts-window-tab-alerts = Alerts ({$value})
|
||||||
|
atmos-alerts-window-tab-air-alarms = Air alarms
|
||||||
|
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-pressure-label = Pressure
|
||||||
|
atmos-alerts-window-pressure-value = {$value} kPa
|
||||||
|
atmos-alerts-window-oxygenation-label = Oxygenation
|
||||||
|
atmos-alerts-window-oxygenation-value = {$value}%
|
||||||
|
atmos-alerts-window-other-gases-label = Other present gases
|
||||||
|
atmos-alerts-window-other-gases-value = {$shorthand} ({$value}%)
|
||||||
|
atmos-alerts-window-other-gases-value-nil = None
|
||||||
|
atmos-alerts-window-silence-alerts = Silence alerts from this alarm
|
||||||
|
|
||||||
|
atmos-alerts-window-label-alert-types = Alert levels:
|
||||||
|
atmos-alerts-window-normal-state = Normal
|
||||||
|
atmos-alerts-window-warning-state = Warning
|
||||||
|
atmos-alerts-window-danger-state = Danger!
|
||||||
|
atmos-alerts-window-invalid-state = Inactive
|
||||||
|
|
||||||
|
atmos-alerts-window-no-active-alerts = [font size=16][color=white]No active alerts -[/color] [color={$color}]situation normal[/color][/font]
|
||||||
|
atmos-alerts-window-no-data-available = No data available
|
||||||
|
atmos-alerts-window-alerts-being-silenced = Silencing alerts...
|
||||||
|
|
||||||
|
atmos-alerts-window-toggle-overlays = Toggle alarm display
|
||||||
|
|
||||||
|
atmos-alerts-window-flavor-left = Contact an atmospheric technician for assistance
|
||||||
|
atmos-alerts-window-flavor-right = v1.8
|
||||||
@@ -21,8 +21,8 @@
|
|||||||
- type: entity
|
- type: entity
|
||||||
parent: BaseComputerCircuitboard
|
parent: BaseComputerCircuitboard
|
||||||
id: AlertsComputerCircuitboard
|
id: AlertsComputerCircuitboard
|
||||||
name: alerts computer board
|
name: atmospheric alerts computer board
|
||||||
description: A computer printed circuit board for an alerts computer.
|
description: A computer printed circuit board for an atmospheric alerts computer.
|
||||||
components:
|
components:
|
||||||
- type: ComputerBoard
|
- type: ComputerBoard
|
||||||
prototype: ComputerAlert
|
prototype: ComputerAlert
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
- type: entity
|
- type: entity
|
||||||
parent: BaseComputer
|
parent: BaseComputer
|
||||||
id: ComputerAlert
|
id: ComputerAlert
|
||||||
name: alerts computer
|
name: atmospheric alerts computer
|
||||||
description: Used to access the station's automated alert system.
|
description: Used to access the station's atmospheric automated alert system.
|
||||||
components:
|
components:
|
||||||
- type: Computer
|
- type: Computer
|
||||||
board: AlertsComputerCircuitboard
|
board: AlertsComputerCircuitboard
|
||||||
@@ -13,9 +13,33 @@
|
|||||||
- map: ["computerLayerKeyboard"]
|
- map: ["computerLayerKeyboard"]
|
||||||
state: generic_keyboard
|
state: generic_keyboard
|
||||||
- map: ["computerLayerScreen"]
|
- map: ["computerLayerScreen"]
|
||||||
state: alert-2
|
state: alert-0
|
||||||
- map: ["computerLayerKeys"]
|
- map: ["computerLayerKeys"]
|
||||||
state: atmos_key
|
state: atmos_key
|
||||||
|
- type: GenericVisualizer
|
||||||
|
visuals:
|
||||||
|
enum.ComputerVisuals.Powered:
|
||||||
|
computerLayerScreen:
|
||||||
|
True: { visible: true, shader: unshaded }
|
||||||
|
False: { visible: false }
|
||||||
|
computerLayerKeys:
|
||||||
|
True: { visible: true, shader: unshaded }
|
||||||
|
False: { visible: true, shader: shaded }
|
||||||
|
enum.AtmosAlertsComputerVisuals.ComputerLayerScreen:
|
||||||
|
computerLayerScreen:
|
||||||
|
0: { state: alert-0 }
|
||||||
|
1: { state: alert-0 }
|
||||||
|
2: { state: alert-1 }
|
||||||
|
3: { state: alert-2 }
|
||||||
|
4: { state: alert-2 }
|
||||||
|
- type: AtmosAlertsComputer
|
||||||
|
- type: ActivatableUI
|
||||||
|
singleUser: true
|
||||||
|
key: enum.AtmosAlertsComputerUiKey.Key
|
||||||
|
- type: UserInterface
|
||||||
|
interfaces:
|
||||||
|
enum.AtmosAlertsComputerUiKey.Key:
|
||||||
|
type: AtmosAlertsComputerBoundUserInterface
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: BaseComputer
|
parent: BaseComputer
|
||||||
|
|||||||
@@ -53,6 +53,8 @@
|
|||||||
- AirAlarm
|
- AirAlarm
|
||||||
- type: AtmosDevice
|
- type: AtmosDevice
|
||||||
- type: AirAlarm
|
- type: AirAlarm
|
||||||
|
- type: AtmosAlertsDevice
|
||||||
|
group: AirAlarm
|
||||||
- type: Clickable
|
- type: Clickable
|
||||||
- type: InteractionOutline
|
- type: InteractionOutline
|
||||||
- type: UserInterface
|
- type: UserInterface
|
||||||
|
|||||||
@@ -42,6 +42,8 @@
|
|||||||
- type: Clickable
|
- type: Clickable
|
||||||
- type: InteractionOutline
|
- type: InteractionOutline
|
||||||
- type: FireAlarm
|
- type: FireAlarm
|
||||||
|
- type: AtmosAlertsDevice
|
||||||
|
group: FireAlarm
|
||||||
- type: ContainerFill
|
- type: ContainerFill
|
||||||
containers:
|
containers:
|
||||||
board: [ FireAlarmElectronics ]
|
board: [ FireAlarmElectronics ]
|
||||||
|
|||||||
BIN
Resources/Textures/Interface/AtmosMonitoring/status_bg.png
Normal file
BIN
Resources/Textures/Interface/AtmosMonitoring/status_bg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 157 B |
Reference in New Issue
Block a user