diff --git a/Content.Client/Communications/UI/CommunicationsConsoleBoundUserInterface.cs b/Content.Client/Communications/UI/CommunicationsConsoleBoundUserInterface.cs
index 1a372bdba1f..89dcd788151 100644
--- a/Content.Client/Communications/UI/CommunicationsConsoleBoundUserInterface.cs
+++ b/Content.Client/Communications/UI/CommunicationsConsoleBoundUserInterface.cs
@@ -28,7 +28,6 @@ namespace Content.Client.Communications.UI
_menu.OnBroadcast += BroadcastButtonPressed;
_menu.OnAlertLevel += AlertLevelSelected;
_menu.OnEmergencyLevel += EmergencyShuttleButtonPressed;
- _menu.OnExfiltrationLevel += ExfiltrationShuttleButtonPressed; // DeltaV - Exfiltration shuttle
}
public void AlertLevelSelected(string level)
@@ -48,13 +47,6 @@ namespace Content.Client.Communications.UI
CallShuttle();
}
- // Begin DeltaV - Exfiltration Shuttle
- public void ExfiltrationShuttleButtonPressed()
- {
- SendMessage(new CommunicationsConsoleExfiltrationShuttleMessage(!_menu!.CountdownStarted));
- }
- // End DeltaV - Exfiltration Shuttle
-
public void AnnounceButtonPressed(string message)
{
var maxLength = _cfg.GetCVar(CCVars.ChatMaxAnnouncementLength);
@@ -94,13 +86,6 @@ namespace Content.Client.Communications.UI
_menu.CurrentLevel = commsState.CurrentAlert;
_menu.CountdownEnd = commsState.ExpectedCountdownEnd;
-
- // Begin DeltaV - Exfiltration Shuttle
- _menu.CanExfiltrate = commsState.CanCall;
- _menu.ExfiltrationCountdownEnd = commsState.ExpectedExfiltrationCountdownEnd;
- _menu.ExfiltrationShuttleButton.Disabled = !_menu.CanExfiltrate;
- // End DeltaV - Exfiltration Shuttle
-
_menu.UpdateCountdown();
_menu.UpdateAlertLevels(commsState.AlertLevels, _menu.CurrentLevel);
_menu.AlertLevelButton.Disabled = !_menu.AlertLevelSelectable;
diff --git a/Content.Client/Communications/UI/CommunicationsConsoleMenu.xaml b/Content.Client/Communications/UI/CommunicationsConsoleMenu.xaml
index 9132f3f5029..b74df979cf4 100644
--- a/Content.Client/Communications/UI/CommunicationsConsoleMenu.xaml
+++ b/Content.Client/Communications/UI/CommunicationsConsoleMenu.xaml
@@ -52,19 +52,10 @@
-
-
-
-
-
-
diff --git a/Content.Client/Communications/UI/CommunicationsConsoleMenu.xaml.cs b/Content.Client/Communications/UI/CommunicationsConsoleMenu.xaml.cs
index 977b81c0a19..926b8c65675 100644
--- a/Content.Client/Communications/UI/CommunicationsConsoleMenu.xaml.cs
+++ b/Content.Client/Communications/UI/CommunicationsConsoleMenu.xaml.cs
@@ -29,12 +29,6 @@ namespace Content.Client.Communications.UI
public event Action? OnAnnounce;
public event Action? OnBroadcast;
- // Begin DeltaV - Exfiltration Shuttle
- public bool CanExfiltrate;
- public TimeSpan? ExfiltrationCountdownEnd;
- public event Action? OnExfiltrationLevel;
- // End DeltaV - Exfiltration Shuttle
-
public CommunicationsConsoleMenu()
{
IoCManager.InjectDependencies(this);
@@ -78,18 +72,12 @@ namespace Content.Client.Communications.UI
EmergencyShuttleButton.OnPressed += _ => OnEmergencyLevel?.Invoke();
EmergencyShuttleButton.Disabled = !CanCall;
-
- // Begin DeltaV - Exfiltration Shuttle
- ExfiltrationShuttleButton.OnPressed += _ => OnExfiltrationLevel?.Invoke();
- ExfiltrationShuttleButton.Disabled = !CanExfiltrate;
- // End DeltaV - Exfiltration Shuttle
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
UpdateCountdown();
- UpdateExfiltrationCountdown(); // DeltaV - Exfiltration shuttle
}
// The current alert could make levels unselectable, so we need to ensure that the UI reacts properly.
@@ -129,25 +117,6 @@ namespace Content.Client.Communications.UI
}
}
- // Begin DeltaV - Exfiltration Shuttle
- public void UpdateExfiltrationCountdown()
- {
- if (ExfiltrationCountdownEnd is null)
- {
- ExfiltrationCountdownLabel.SetMessage(string.Empty);
- ExfiltrationShuttleButton.Text = Loc.GetString("comms-console-menu-call-exfiltration");
- return;
- }
-
- var exfiltrationDiff = MathHelper.Max((ExfiltrationCountdownEnd - _timing.CurTime) ?? TimeSpan.Zero, TimeSpan.Zero);
- ExfiltrationShuttleButton.Text = Loc.GetString("comms-console-menu-recall-exfiltration");
-
- var exfiltrationInfoText = Loc.GetString("comms-console-menu-exfiltration-time-remaining",
- ("time", exfiltrationDiff.ToString(@"hh\:mm\:ss", CultureInfo.CurrentCulture)));
- ExfiltrationCountdownLabel.SetMessage(exfiltrationInfoText);
- }
- // End DeltaV - Exfiltration Shuttle
-
public void UpdateCountdown()
{
if (!CountdownStarted)
diff --git a/Content.Client/UserInterface/Controls/ConfirmButton.cs b/Content.Client/UserInterface/Controls/ConfirmButton.cs
index 81f11351a59..000a77a2666 100644
--- a/Content.Client/UserInterface/Controls/ConfirmButton.cs
+++ b/Content.Client/UserInterface/Controls/ConfirmButton.cs
@@ -70,6 +70,11 @@ public sealed class ConfirmButton : Button
[ViewVariables]
public bool IsConfirming = false;
+ // Begin DeltaV
+ [ViewVariables]
+ public bool ForceDisabled = false;
+ // End DeltaV
+
public ConfirmButton()
{
IoCManager.InjectDependencies(this);
@@ -87,7 +92,7 @@ public sealed class ConfirmButton : Button
}
if (Disabled && _gameTiming.CurTime > _nextCooldown)
- Disabled = false;
+ Disabled = ForceDisabled; // DeltaV
}
protected override void DrawModeChanged()
diff --git a/Content.Client/_DV/Communications/DVCommunicationsConsoleBoundUserInterface.cs b/Content.Client/_DV/Communications/DVCommunicationsConsoleBoundUserInterface.cs
new file mode 100644
index 00000000000..b7566e94a6a
--- /dev/null
+++ b/Content.Client/_DV/Communications/DVCommunicationsConsoleBoundUserInterface.cs
@@ -0,0 +1,30 @@
+using Content.Shared._DV.Communications;
+using Robust.Client.UserInterface;
+
+namespace Content.Client._DV.Communications;
+
+public sealed class DVCommunicationsConsoleBoundUserInterface : BoundUserInterface
+{
+ [Dependency] private readonly IEntityManager _entity = default!;
+
+ private DVCommunicationsConsoleMenu? _menu;
+
+ public DVCommunicationsConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
+ {
+ }
+
+ protected override void Open()
+ {
+ base.Open();
+
+ _menu = this.CreateWindow();
+ _menu.OnMessage += SendMessage;
+ if (_entity.TryGetComponent(Owner, out var comp))
+ Update((Owner, comp));
+ }
+
+ public void Update(Entity ent)
+ {
+ _menu?.Update(ent, PlayerManager.LocalEntity!.Value);
+ }
+}
diff --git a/Content.Client/_DV/Communications/DVCommunicationsConsoleMenu.xaml b/Content.Client/_DV/Communications/DVCommunicationsConsoleMenu.xaml
new file mode 100644
index 00000000000..7dfcbed8314
--- /dev/null
+++ b/Content.Client/_DV/Communications/DVCommunicationsConsoleMenu.xaml
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/_DV/Communications/DVCommunicationsConsoleMenu.xaml.cs b/Content.Client/_DV/Communications/DVCommunicationsConsoleMenu.xaml.cs
new file mode 100644
index 00000000000..a1bf3ebce36
--- /dev/null
+++ b/Content.Client/_DV/Communications/DVCommunicationsConsoleMenu.xaml.cs
@@ -0,0 +1,289 @@
+using System.Globalization;
+using System.Numerics;
+using Content.Client.UserInterface.Controls;
+using Content.Shared._DV.Communications;
+using Content.Shared._DV.KeycardAuthenticationDevice;
+using Content.Shared._DV.Screens;
+using Content.Shared.Station;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Timing;
+using Robust.Shared.Utility;
+
+namespace Content.Client._DV.Communications;
+
+[GenerateTypedNameReferences]
+public sealed partial class DVCommunicationsConsoleMenu : FancyWindow
+{
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly IEntityManager _entity = default!;
+
+ private readonly SharedStationSystem _station;
+
+ private readonly BoxContainer[] _screens;
+ private readonly ButtonGroup _group;
+
+ public event Action? OnMessage;
+ private Entity? _console;
+ private Entity? _stationKeycardAuthenticationDevice;
+ private EntityUid? _user;
+
+ private readonly Popup _alertLevelsPopup;
+ private readonly BoxContainer _alertLevels = new()
+ {
+ Orientation = BoxContainer.LayoutOrientation.Vertical,
+ };
+
+ private string _lastConfiguredLine1 = string.Empty;
+ private string _lastConfiguredLine2 = string.Empty;
+
+ public DVCommunicationsConsoleMenu()
+ {
+ RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
+
+ _station = _entity.System();
+
+ _group = new();
+ _screens = new[]
+ {
+ HomeScreen, AnnouncementScreen, ScreenScreen, ShuttlesScreen, AlertLevelScreen,
+ KeycardAuthenticationDeviceScreen,
+ };
+
+ HomeButton.OnPressed += _ => SetScreen(HomeScreen);
+ AnnouncementTab.OnPressed += _ => SetScreen(AnnouncementScreen);
+ ScreenTab.OnPressed += _ => SetScreen(ScreenScreen);
+ ShuttlesTab.OnPressed += _ => SetScreen(ShuttlesScreen);
+ AlertLevelTab.OnPressed += _ => SetScreen(AlertLevelScreen);
+ KeycardAuthenticationDeviceTab.OnPressed += _ => SetScreen(KeycardAuthenticationDeviceScreen);
+
+ foreach (var value in Enum.GetValues())
+ {
+ ScreenContentsButton.AddItem(Loc.GetString($"comms-console-menu-screen-content.{value}"), (int)value);
+ }
+
+ AnnounceButton.OnPressed += OnAnnouncePressed;
+ ScreenUpdateTextButton.OnPressed += OnUpdateScreenText;
+ ScreenContentsButton.OnItemSelected += OnScreenContentsSelected;
+ ScreenAlertBorder.OnToggled += OnScreenAlertBorderToggled;
+
+ ShuttleEmergencyButton.OnPressed += OnEmergencyPressed;
+ ShuttleExfiltrationButton.OnPressed += OnExfiltrationPressed;
+ AlertLevelsDropdown.OnPressed += OnAlertLevelsDropdownPressed;
+
+ CallZeta.OnPressed += OnCallZetaPressed;
+ RequestCodes.OnPressed += OnRequestCodesPressed;
+
+ _alertLevelsPopup = new()
+ {
+ Children = { _alertLevels },
+ };
+ _alertLevelsPopup.OnPopupHide += () => _alertLevelsPopup.Orphan();
+
+ AnnounceInput.Placeholder = new Rope.Leaf(Loc.GetString("comms-console-menu-announcement-placeholder"));
+ }
+
+ private void OnAlertLevelsDropdownPressed(BaseButton.ButtonEventArgs args)
+ {
+ var globalPos = AlertLevelsDropdown.GlobalPosition;
+ globalPos.Y += AlertLevelsDropdown.Size.Y + 1; // Place it below us, with a safety margin.
+ _alertLevels.Measure(Window?.Size ?? Vector2Helpers.Infinity);
+ var (minX, minY) = _alertLevels.DesiredSize;
+ var box = UIBox2.FromDimensions(globalPos, new Vector2(Math.Max(minX, AlertLevelsDropdown.Width), minY));
+ _alertLevelsPopup.Orphan();
+ Root?.ModalRoot.AddChild(_alertLevelsPopup);
+ _alertLevelsPopup.Open(box);
+ }
+
+ protected override void FrameUpdate(FrameEventArgs args)
+ {
+ base.FrameUpdate(args);
+
+ if (_console is not { } console)
+ return;
+
+ AnnounceButton.Disabled = _timing.CurTime <= console.Comp.CanAnnounceAt;
+ AlertLevelsDropdown.Disabled = console.Comp.CanSetAlertAt is null || _timing.CurTime <= console.Comp.CanSetAlertAt;
+ if (AlertLevelsDropdown.Disabled)
+ _alertLevelsPopup.Close();
+
+ if (console.Comp.ExpectedEvacuationArrival is { } evacArrival)
+ {
+ var diff = MathHelper.Max(evacArrival - _timing.CurTime, TimeSpan.Zero);
+ var infoText = Loc.GetString($"comms-console-menu-shuttle-eta",
+ ("time", diff.ToString(@"hh\:mm\:ss", CultureInfo.CurrentCulture)));
+ EmergencyStatus.Text = infoText;
+ }
+ else
+ {
+ EmergencyStatus.Text = Loc.GetString("comms-console-menu-shuttle-not-coming");
+ }
+
+ if (console.Comp.ExpectedExfiltrationArrival is { } exfiltrationArrival)
+ {
+ var diff = MathHelper.Max(exfiltrationArrival - _timing.CurTime, TimeSpan.Zero);
+ var infoText = Loc.GetString($"comms-console-menu-shuttle-eta",
+ ("time", diff.ToString(@"hh\:mm\:ss", CultureInfo.CurrentCulture)));
+ ExfiltrationStatus.Text = infoText;
+ }
+ else
+ {
+ ExfiltrationStatus.Text = Loc.GetString("comms-console-menu-shuttle-not-coming");
+ }
+
+ UpdateKeycardAuthenticationDevice();
+ }
+
+ private void SetScreen(Control screen)
+ {
+ foreach (var other in _screens)
+ {
+ other.Visible = false;
+ }
+
+ screen.Visible = true;
+ HomeButton.Visible = screen != HomeScreen;
+ }
+
+ public void Update(Entity console, EntityUid user)
+ {
+ _console = console;
+ _user = user;
+ if (_station.GetOwningStation(console) is { } station &&
+ _entity.TryGetComponent(station,
+ out var keycardAuthenticationDevice))
+ {
+ _stationKeycardAuthenticationDevice = (station, keycardAuthenticationDevice);
+ }
+
+ if (console.Comp.CanAnnounce && !(console.Comp.CanAlertLevel || console.Comp.CanCallShuttles || console.Comp.CanConfigureScreens || console.Comp.CanKeycardAuthenticationDevice))
+ {
+ SetScreen(AnnouncementScreen);
+ HomeButton.Visible = false;
+ }
+
+ AnnouncementTab.Visible = console.Comp.CanAnnounce;
+ ScreenTab.Visible = console.Comp.CanConfigureScreens;
+ ShuttlesTab.Visible = console.Comp.CanCallShuttles;
+ AlertLevelTab.Visible = console.Comp.CanAlertLevel;
+ KeycardAuthenticationDeviceTab.Visible = console.Comp.CanKeycardAuthenticationDevice;
+
+ ShuttleEmergencyButton.Disabled = !console.Comp.ShuttlesCallable;
+ ShuttleExfiltrationButton.Disabled = !console.Comp.ShuttlesCallable;
+
+ ShuttleEmergencyButton.Text =
+ Loc.GetString($"comms-console-menu-call-emergency.{console.Comp.ExpectedEvacuationArrival is null}");
+ ShuttleExfiltrationButton.Text =
+ Loc.GetString($"comms-console-menu-call-exfiltration.{console.Comp.ExpectedExfiltrationArrival is null}");
+
+ _alertLevels.RemoveAllChildren();
+ foreach (var level in console.Comp.AlertLevels)
+ {
+ if (level.Id == console.Comp.CurrentAlertLevel)
+ {
+ AlertLevel.Text = Loc.GetString("comms-console-menu-current-alert-level",
+ ("name", Loc.GetString(level.AlertLevel)),
+ ("color", level.Color),
+ ("description", Loc.GetString(level.Description)));
+ }
+
+ if (!level.CanSet)
+ continue;
+
+ var btn = new Button
+ {
+ Text = Loc.GetString(level.AlertLevel),
+ ToggleMode = true,
+ Group = _group,
+ Pressed = level.Id == console.Comp.CurrentAlertLevel,
+ };
+ _alertLevels.AddChild(btn);
+ btn.OnPressed += _ =>
+ {
+ _alertLevelsPopup.Close();
+ OnMessage?.Invoke(new DVCommunicationsConsoleAlertLevelMessage(level.Id));
+ };
+ }
+
+ if (console.Comp.LastConfiguredLine1 != _lastConfiguredLine1)
+ ScreenLine1.Text = console.Comp.LastConfiguredLine1;
+
+ if (console.Comp.LastConfiguredLine2 != _lastConfiguredLine2)
+ ScreenLine2.Text = console.Comp.LastConfiguredLine2;
+
+ ScreenContentsButton.SelectId((int)console.Comp.LastConfiguredContent);
+ ScreenAlertBorder.Pressed = console.Comp.LastConfiguredShowBorders;
+
+ _lastConfiguredLine1 = console.Comp.LastConfiguredLine1;
+ _lastConfiguredLine2 = console.Comp.LastConfiguredLine2;
+
+ UpdateKeycardAuthenticationDevice();
+ }
+
+ private void UpdateKeycardAuthenticationDevice()
+ {
+ if (_user is not { } user || _stationKeycardAuthenticationDevice is not { } station)
+ return;
+
+ var canCall = !_entity.HasComponent(user)
+ && station.Comp.AccessibleAfter <= _timing.CurTime;
+
+ CallZeta.ForceDisabled = !(canCall && station.Comp.SwipingFor is null or DVStationKeycardAction.Mayday);
+ CallZeta.Disabled = CallZeta.ForceDisabled;
+ RequestCodes.ForceDisabled = !(canCall && station.Comp.SwipingFor is null or DVStationKeycardAction.Scuttling);
+ RequestCodes.Disabled = RequestCodes.ForceDisabled;
+ }
+
+ private void OnAnnouncePressed(BaseButton.ButtonEventArgs args)
+ {
+ OnMessage?.Invoke(new DVCommunicationsConsoleAnnouncementMessage(Rope.Collapse(AnnounceInput.TextRope)));
+ }
+
+ private void UpdateScreens()
+ {
+ OnMessage?.Invoke(new DVCommunicationsConsoleScreenConfigurationMessage(
+ (DVScreenContent) ScreenContentsButton.SelectedId,
+ ScreenAlertBorder.Pressed,
+ ScreenLine1.Text,
+ ScreenLine2.Text));
+ }
+
+ private void OnUpdateScreenText(BaseButton.ButtonEventArgs buttonEventArgs)
+ {
+ UpdateScreens();
+ }
+
+ private void OnScreenContentsSelected(OptionButton.ItemSelectedEventArgs itemSelectedEventArgs)
+ {
+ ScreenContentsButton.SelectId(itemSelectedEventArgs.Id);
+ UpdateScreens();
+ }
+
+ private void OnScreenAlertBorderToggled(BaseButton.ButtonToggledEventArgs buttonToggledEventArgs)
+ {
+ UpdateScreens();
+ }
+
+ private void OnExfiltrationPressed(BaseButton.ButtonEventArgs args)
+ {
+ OnMessage?.Invoke(new DVCommunicationsConsoleExfiltrationShuttleMessage(_console?.Comp.ExpectedExfiltrationArrival is null));
+ }
+
+ private void OnEmergencyPressed(BaseButton.ButtonEventArgs args)
+ {
+ OnMessage?.Invoke(new DVCommunicationsConsoleEvacuationShuttleMessage(_console?.Comp.ExpectedEvacuationArrival is null));
+ }
+
+ private void OnCallZetaPressed(BaseButton.ButtonEventArgs obj)
+ {
+ OnMessage?.Invoke(new DVCommunicationsConsoleKeycardAuthenticationDeviceMessage(DVStationKeycardAction.Mayday));
+ }
+
+ private void OnRequestCodesPressed(BaseButton.ButtonEventArgs obj)
+ {
+ OnMessage?.Invoke(new DVCommunicationsConsoleKeycardAuthenticationDeviceMessage(DVStationKeycardAction.Scuttling));
+ }
+}
diff --git a/Content.Client/_DV/Communications/DVCommunicationsConsoleSystem.cs b/Content.Client/_DV/Communications/DVCommunicationsConsoleSystem.cs
new file mode 100644
index 00000000000..707b29a4414
--- /dev/null
+++ b/Content.Client/_DV/Communications/DVCommunicationsConsoleSystem.cs
@@ -0,0 +1,26 @@
+using Content.Shared._DV.Communications;
+using Robust.Client.GameObjects;
+
+namespace Content.Client._DV.Communications;
+
+public sealed class DVCommunicationsConsoleSystem : SharedDVCommunicationsConsoleSystem
+{
+ [Dependency] private readonly UserInterfaceSystem _userInterface = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnHandleState);
+ }
+
+ private void OnHandleState(Entity ent, ref AfterAutoHandleStateEvent args)
+ {
+ if (!_userInterface.TryGetOpenUi(ent.Owner,
+ DVCommunicationsConsoleUi.Key,
+ out var bui))
+ return;
+
+ bui.Update(ent);
+ }
+}
diff --git a/Content.Client/_DV/KeycardAuthenticationDevice/DVKeycardAuthenticationDeviceSystem.cs b/Content.Client/_DV/KeycardAuthenticationDevice/DVKeycardAuthenticationDeviceSystem.cs
new file mode 100644
index 00000000000..0b096814c9c
--- /dev/null
+++ b/Content.Client/_DV/KeycardAuthenticationDevice/DVKeycardAuthenticationDeviceSystem.cs
@@ -0,0 +1,5 @@
+using Content.Shared._DV.KeycardAuthenticationDevice;
+
+namespace Content.Client._DV.KeycardAuthenticationDevice;
+
+public sealed class DVStationKeycardAuthenticationDeviceSystem : SharedDVStationKeycardAuthenticationDeviceSystem;
diff --git a/Content.Client/_DV/Screens/DVScreenSystem.cs b/Content.Client/_DV/Screens/DVScreenSystem.cs
new file mode 100644
index 00000000000..55c9a9c254e
--- /dev/null
+++ b/Content.Client/_DV/Screens/DVScreenSystem.cs
@@ -0,0 +1,95 @@
+using System.Globalization;
+using Content.Client.GameTicking.Managers;
+using Content.Client.TextScreen;
+using Content.Shared._DV.Screens;
+using Robust.Shared.Timing;
+
+namespace Content.Client._DV.Screens;
+
+public sealed class DVScreenSystem : DVSharedScreenSystem
+{
+ [Dependency] private readonly DVTextVisualsSystem _textVisuals = default!;
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly ClientGameTicker _ticker = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnScreenState);
+ }
+
+ public override void FrameUpdate(float frameTime)
+ {
+ base.FrameUpdate(frameTime);
+
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var uid, out var screen))
+ {
+ switch (screen.Content)
+ {
+ case DVScreenContent.Text:
+ break;
+ case DVScreenContent.CurrentTime:
+ CurrentTime((uid, screen));
+ break;
+ case DVScreenContent.EstimatedTimeOfArrival:
+ EstimatedTimeOfArrival((uid, screen));
+ break;
+ case DVScreenContent.AlertLevel:
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+ }
+
+ private void OnScreenState(Entity ent, ref AfterAutoHandleStateEvent args)
+ {
+ UpdateVisuals(ent);
+
+ switch (ent.Comp.Content)
+ {
+ case DVScreenContent.Text:
+ Text(ent);
+ break;
+ case DVScreenContent.CurrentTime:
+ CurrentTime(ent);
+ break;
+ case DVScreenContent.EstimatedTimeOfArrival:
+ EstimatedTimeOfArrival(ent);
+ break;
+ case DVScreenContent.AlertLevel:
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ private void Text(Entity ent)
+ {
+ _textVisuals.SetText(ent.Owner, ent.Comp.Line1, ent.Comp.Line2);
+ }
+
+ private void CurrentTime(Entity ent)
+ {
+ var time = (_timing.CurTime - _ticker.RoundStartTimeSpan).Duration();
+ _textVisuals.SetText(ent.Owner, Loc.GetString("status-display-time"), time.ToString("hh\\:mm"));
+ }
+
+ private void EstimatedTimeOfArrival(Entity ent)
+ {
+ if (ent.Comp.TargetTime <= _timing.CurTime)
+ {
+ _textVisuals.SetText(ent.Owner, string.Empty, string.Empty);
+ return;
+ }
+
+ var time = (_timing.CurTime - ent.Comp.TargetTime).Duration();
+ var formatted = time.ToString("mm\\:ss");
+ Log.Debug($"{_timing.CurTime} - {ent.Comp.TargetTime} = {time}");
+ var title = ent.Comp.ScreenIsAtDestination ? Loc.GetString("status-display-etd") : Loc.GetString("status-display-eta");
+
+ _textVisuals.SetText(ent.Owner, title, formatted);
+ }
+}
diff --git a/Content.Client/_DV/Screens/DVTextRenderingOverlay.cs b/Content.Client/_DV/Screens/DVTextRenderingOverlay.cs
new file mode 100644
index 00000000000..90f18b4b031
--- /dev/null
+++ b/Content.Client/_DV/Screens/DVTextRenderingOverlay.cs
@@ -0,0 +1,140 @@
+using System.Linq;
+using System.Numerics;
+using System.Threading;
+using JetBrains.Annotations;
+using Robust.Client.Animations;
+using Robust.Client.GameObjects;
+using Robust.Client.Graphics;
+using Robust.Shared.Enums;
+
+namespace Content.Client._DV.Screens;
+
+[UsedImplicitly]
+public sealed class DVTextRenderingOverlay : Overlay
+{
+ [Dependency] private readonly IClyde _clyde = default!;
+ private readonly SpriteSystem _sprite;
+ private readonly AnimationPlayerSystem _animationPlayer;
+
+ public override OverlaySpace Space => OverlaySpace.ScreenSpaceBelowWorld;
+
+ private readonly Queue<(Entity Entity, Font Font, CancellationToken Cancellation)> _queue = new();
+
+ public const string MarqueeKey = "dv-text-screen-marquee";
+
+ public DVTextRenderingOverlay(SpriteSystem sprite, AnimationPlayerSystem animationPlayer)
+ {
+ IoCManager.InjectDependencies(this);
+ _sprite = sprite;
+ _animationPlayer = animationPlayer;
+
+ ZIndex = -100; // this needs to render before almost everything
+ }
+
+ public CancellationTokenSource QueueRender(
+ Entity ent,
+ Font font)
+ {
+ var source = new CancellationTokenSource();
+ _queue.Enqueue((ent, font, source.Token));
+
+ return source;
+ }
+
+ protected override void Draw(in OverlayDrawArgs args)
+ {
+ var screenHandle = args.ScreenHandle;
+
+ while (_queue.TryDequeue(out var queued))
+ {
+ if (queued.Cancellation.IsCancellationRequested)
+ continue;
+
+ var font = queued.Font;
+ foreach (var row in queued.Entity.Comp.Rows)
+ {
+ if (row.Text == string.Empty)
+ {
+ _sprite.LayerSetTexture(queued.Entity.Owner, row.Layer, null);
+ continue;
+ }
+
+ var dimensions = screenHandle.GetDimensions(queued.Font, row.Text, 1f);
+ var dimensionsInt = new Vector2i((int)MathF.Round(dimensions.X), (int)MathF.Round(dimensions.Y));
+
+ if (row.Texture is null || row.Texture.Size != dimensionsInt)
+ {
+ row.Texture = _clyde.CreateRenderTarget(dimensionsInt,
+ new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8),
+ name: $"dv-text-visuals-{queued.Entity.Owner.Id}");
+
+ _sprite.LayerSetTexture(queued.Entity.Owner, row.Layer, row.Texture.Texture);
+ _sprite.LayerSetOffset(queued.Entity.Owner, row.Layer, row.Offset);
+ }
+
+ args.DrawingHandle.RenderInRenderTarget(row.Texture,
+ () =>
+ {
+ screenHandle.DrawString(font, Vector2.Zero, row.Text);
+ },
+ Color.Transparent);
+ }
+
+ _animationPlayer.Stop(queued.Entity.Owner, MarqueeKey);
+ if (CreateMarqueeAnimation(queued.Entity) is { } animation)
+ {
+ queued.Entity.Comp.Animation = animation;
+ _animationPlayer.Play(queued.Entity.Owner, animation, MarqueeKey);
+ }
+ }
+ }
+
+ private Animation? CreateMarqueeAnimation(Entity ent)
+ {
+ var largestRowWidth = ent.Comp.Rows.Aggregate(0, (i, row) => Math.Max(i, row.Texture?.Size.X ?? 0));
+ var animationTime = ent.Comp.MarqueeRate * largestRowWidth;
+ var marqueeWidth = new Vector2((float)ent.Comp.MarqueeWidth / EyeManager.PixelsPerMeter, 0);
+
+ var animation = new Animation
+ {
+ Length = animationTime,
+ };
+
+ foreach (var row in ent.Comp.Rows)
+ {
+ if (row.Texture is null)
+ continue;
+
+ var rowHalfWidth = new Vector2(row.Texture.Size.X / 2f / EyeManager.PixelsPerMeter, 0f);
+
+ if (row.Texture.Size.X <= ent.Comp.MarqueeWidth)
+ continue;
+
+ animation.AnimationTracks.Add(new AnimationTrackLayerOffset()
+ {
+ Layer = row.Layer,
+ KeyFrames =
+ {
+ new AnimationTrackProperty.KeyFrame(row.Offset + rowHalfWidth + marqueeWidth, 0f),
+ new AnimationTrackProperty.KeyFrame(row.Offset - rowHalfWidth - marqueeWidth, (float)animationTime.TotalSeconds),
+ },
+ });
+ }
+
+ return animation.AnimationTracks.Count > 0 ? animation : null;
+ }
+
+ public sealed class AnimationTrackLayerOffset : AnimationTrackProperty
+ {
+ public required Enum Layer;
+ private readonly SpriteSystem _sprite = IoCManager.Resolve().System();
+
+ protected override void ApplyProperty(object context, object value)
+ {
+ if (value is not Vector2 vector)
+ throw new InvalidOperationException("Value must be a .");
+
+ _sprite.LayerSetOffset((EntityUid) context, Layer, vector);
+ }
+ }
+}
diff --git a/Content.Client/_DV/Screens/DVTextVisualsComponent.cs b/Content.Client/_DV/Screens/DVTextVisualsComponent.cs
new file mode 100644
index 00000000000..21f3a36f32f
--- /dev/null
+++ b/Content.Client/_DV/Screens/DVTextVisualsComponent.cs
@@ -0,0 +1,43 @@
+using System.Numerics;
+using System.Threading;
+using Content.Shared._DV.Screens;
+using Robust.Client.Animations;
+using Robust.Client.Graphics;
+
+namespace Content.Client._DV.Screens;
+
+[RegisterComponent]
+[Access(typeof(DVTextVisualsSystem), typeof(DVTextRenderingOverlay))]
+public sealed partial class DVTextVisualsComponent : Component
+{
+ [DataField(required: true)]
+ public List Rows;
+
+ [DataField]
+ public TimeSpan MarqueeRate = TimeSpan.FromSeconds(0.045f);
+
+ [DataField]
+ public int MarqueeWidth = 24;
+
+ [DataField]
+ public int MarqueePadding = 8;
+
+ public Animation? Animation;
+
+ public CancellationTokenSource? Token;
+}
+
+[DataDefinition]
+public sealed partial class DVTextVisualsRow
+{
+ public IRenderTexture? Texture;
+
+ [DataField]
+ public string Text;
+
+ [DataField]
+ public Vector2 Offset;
+
+ [DataField(required: true)]
+ public Enum Layer = DVTextScreenVisualLayers.Line1;
+}
diff --git a/Content.Client/_DV/Screens/DVTextVisualsSystem.cs b/Content.Client/_DV/Screens/DVTextVisualsSystem.cs
new file mode 100644
index 00000000000..db7726de657
--- /dev/null
+++ b/Content.Client/_DV/Screens/DVTextVisualsSystem.cs
@@ -0,0 +1,73 @@
+using Robust.Client.GameObjects;
+using Robust.Client.Graphics;
+using Robust.Client.ResourceManagement;
+
+namespace Content.Client._DV.Screens;
+
+public sealed class DVTextVisualsSystem : EntitySystem
+{
+ [Dependency] private readonly IOverlayManager _overlay = default!;
+ [Dependency] private readonly IResourceCache _resource = default!;
+ [Dependency] private readonly SpriteSystem _sprite = default!;
+ [Dependency] private readonly AnimationPlayerSystem _animationPlayer = default!;
+
+ private DVTextRenderingOverlay _textRendering = default!;
+
+ private Font _font = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ _textRendering = new(_sprite, _animationPlayer);
+ _overlay.AddOverlay(_textRendering);
+ _font = new VectorFont(_resource.GetResource("/Fonts/_DV/TinyUnicode.ttf"), 12);
+
+ SubscribeLocalEvent(OnComponentInit);
+ SubscribeLocalEvent(OnComponentShutdown);
+
+ SubscribeLocalEvent(OnAnimationComplete);
+ }
+
+ public override void Shutdown()
+ {
+ _overlay.RemoveOverlay(_textRendering);
+ }
+
+ private void OnComponentInit(Entity ent, ref ComponentInit args)
+ {
+ ent.Comp.Token = _textRendering.QueueRender(ent, _font);
+ }
+
+ private void OnComponentShutdown(Entity ent, ref ComponentShutdown args)
+ {
+ foreach (var row in ent.Comp.Rows)
+ {
+ row.Texture?.Dispose();
+ }
+ ent.Comp.Token?.Cancel();
+ }
+
+ private void OnAnimationComplete(Entity ent, ref AnimationCompletedEvent args)
+ {
+ if (args.Key != DVTextRenderingOverlay.MarqueeKey || !args.Finished || ent.Comp.Animation is not { } animation)
+ return;
+
+ _animationPlayer.Play(ent.Owner, animation, DVTextRenderingOverlay.MarqueeKey);
+ }
+
+ public void SetText(Entity ent, params string[] rows)
+ {
+ if (!Resolve(ent, ref ent.Comp))
+ return;
+
+ var count = Math.Min(rows.Length, ent.Comp.Rows.Count);
+ for (var i = 0; i < count; i++)
+ {
+ ent.Comp.Rows[i].Text = rows[i];
+ }
+
+ ent.Comp.Token?.Cancel();
+ ent.Comp.Token = _textRendering.QueueRender((ent, ent.Comp), _font);
+ }
+}
diff --git a/Content.Client/_DV/Stylesheets/Sheetlets/DVLineEditSheetlet.cs b/Content.Client/_DV/Stylesheets/Sheetlets/DVLineEditSheetlet.cs
new file mode 100644
index 00000000000..41d01142891
--- /dev/null
+++ b/Content.Client/_DV/Stylesheets/Sheetlets/DVLineEditSheetlet.cs
@@ -0,0 +1,23 @@
+using Content.Client.Resources;
+using Content.Client.Stylesheets;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
+using static Content.Client.Stylesheets.StylesheetHelpers;
+
+namespace Content.Client._DV.Stylesheets.Sheetlets;
+
+[CommonSheetlet]
+public sealed class DVLineEditSheetlet : Sheetlet
+{
+ public override StyleRule[] GetRules(PalettedStylesheet sheet, object config)
+ {
+ var tinyUnicode = ResCache.GetFont("/Fonts/_DV/TinyUnicode.ttf", size: 24);
+
+ return
+ [
+ E()
+ .Class("comms-console-display")
+ .Font(tinyUnicode),
+ ];
+ }
+}
diff --git a/Content.Server/Communications/CommunicationsConsoleSystem.cs b/Content.Server/Communications/CommunicationsConsoleSystem.cs
index f585033dae9..97054a529c7 100644
--- a/Content.Server/Communications/CommunicationsConsoleSystem.cs
+++ b/Content.Server/Communications/CommunicationsConsoleSystem.cs
@@ -23,7 +23,7 @@ using Robust.Shared.Configuration;
namespace Content.Server.Communications
{
- public sealed partial class CommunicationsConsoleSystem : EntitySystem // DeltaV - Partial Class
+ public sealed class CommunicationsConsoleSystem : EntitySystem
{
[Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!;
[Dependency] private readonly AlertLevelSystem _alertLevelSystem = default!;
@@ -53,8 +53,6 @@ namespace Content.Server.Communications
SubscribeLocalEvent(OnCallShuttleMessage);
SubscribeLocalEvent(OnRecallShuttleMessage);
- InitializeExfiltration(); // DeltaV - Exfiltration shuttle
-
// On console init, set cooldown
SubscribeLocalEvent(OnCommunicationsConsoleMapInit);
}
@@ -138,7 +136,6 @@ namespace Content.Server.Communications
List? levels = null;
string currentLevel = default!;
float currentDelay = 0;
- TimeSpan? exfiltrationTime = null; // DeltaV - exfiltration shuttle
if (stationUid != null)
{
@@ -160,12 +157,6 @@ namespace Content.Server.Communications
currentLevel = alertComp.CurrentLevel;
currentDelay = _alertLevelSystem.GetAlertLevelDelay(stationUid.Value, alertComp);
}
- // Begin DeltaV - exfiltration shuttle
- if (TryComp(stationUid, out var exfiltration))
- {
- exfiltrationTime = exfiltration.ArrivalTime;
- }
- // End DeltaV - exfiltration shuttle
}
_uiSystem.SetUiState(uid, CommunicationsConsoleUiKey.Key, new CommunicationsConsoleInterfaceState(
@@ -174,8 +165,7 @@ namespace Content.Server.Communications
levels,
currentLevel,
currentDelay,
- _roundEndSystem.ExpectedCountdownEnd,
- exfiltrationTime // DeltaV - exfiltration shuttle
+ _roundEndSystem.ExpectedCountdownEnd
));
}
diff --git a/Content.Server/Light/Components/EmergencyLightComponent.cs b/Content.Server/Light/Components/EmergencyLightComponent.cs
index b49a8c3868a..1f9b5e2355c 100644
--- a/Content.Server/Light/Components/EmergencyLightComponent.cs
+++ b/Content.Server/Light/Components/EmergencyLightComponent.cs
@@ -30,6 +30,17 @@ public sealed partial class EmergencyLightComponent : SharedEmergencyLightCompon
[DataField("chargingEfficiency")]
public float ChargingEfficiency = 0.85f;
+ // Begin DeltaV Additions
+ [DataField]
+ public float MaydayRadius = 10f;
+
+ [DataField]
+ public float MaydayFalloff = 0f;
+
+ [DataField]
+ public float MaydayEnergy = 0.7f;
+ // End DeltaV Additions
+
public Dictionary BatteryStateText = new()
{
{ EmergencyLightState.Full, "emergency-light-component-light-state-full" },
diff --git a/Content.Server/_DV/Communications/CommunicationsConsoleSystem.cs b/Content.Server/_DV/Communications/CommunicationsConsoleSystem.cs
deleted file mode 100644
index 7a9cc7ac75a..00000000000
--- a/Content.Server/_DV/Communications/CommunicationsConsoleSystem.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using Content.Server._DV.Station.Components;
-using Content.Server._DV.Station.Systems;
-using Content.Shared._DV.Communications;
-
-namespace Content.Server.Communications;
-
-public sealed partial class CommunicationsConsoleSystem : EntitySystem
-{
- [Dependency] private readonly StationExfiltrationSystem _stationExfiltration = default!;
-
- private void InitializeExfiltration()
- {
- SubscribeLocalEvent(OnExfiltrationMessage);
- }
-
- private void OnExfiltrationMessage(Entity ent, ref CommunicationsConsoleExfiltrationShuttleMessage args)
- {
- if (_stationSystem.GetOwningStation(ent) is not { } station)
- return;
-
- if (!CanUse(args.Actor, ent))
- {
- _popupSystem.PopupEntity(Loc.GetString("comms-console-permission-denied"), ent, args.Actor);
- return;
- }
-
- if (args.Call)
- _stationExfiltration.Call(station);
- else
- _stationExfiltration.Recall(station);
- }
-}
diff --git a/Content.Server/_DV/Communications/DVCommunicationsConsoleSystem.cs b/Content.Server/_DV/Communications/DVCommunicationsConsoleSystem.cs
new file mode 100644
index 00000000000..48e44a56e10
--- /dev/null
+++ b/Content.Server/_DV/Communications/DVCommunicationsConsoleSystem.cs
@@ -0,0 +1,184 @@
+using Content.Server._DV.Station.Components;
+using Content.Server._DV.Station.Systems;
+using Content.Server.AlertLevel;
+using Content.Server.Communications;
+using Content.Server.RoundEnd;
+using Content.Server.Shuttles.Systems;
+using Content.Shared._DV.Communications;
+using Content.Shared.CCVar;
+using Content.Shared.Database;
+using Content.Shared.Station;
+using Robust.Shared.Configuration;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server._DV.Communications;
+
+public sealed class DVCommunicationsConsoleSystem : SharedDVCommunicationsConsoleSystem
+{
+ [Dependency] private readonly SharedStationSystem _station = default!;
+ [Dependency] private readonly EmergencyShuttleSystem _emergencyShuttle = default!;
+ [Dependency] private readonly IPrototypeManager _prototype = default!;
+ [Dependency] private readonly RoundEndSystem _roundEnd = default!;
+ [Dependency] private readonly IConfigurationManager _configuration = default!;
+ [Dependency] private readonly AlertLevelSystem _alertLevel = default!;
+ [Dependency] private readonly StationExfiltrationSystem _stationExfiltration = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnRoundEndChanged);
+ SubscribeLocalEvent(OnExfiltrationChanged);
+ SubscribeLocalEvent(OnAlertLevelChanged);
+ }
+
+ private void OnAlertLevelChanged(AlertLevelChangedEvent ev)
+ {
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var uid, out var comp))
+ {
+ if (_station.GetOwningStation(uid) != ev.Station)
+ continue;
+
+ var alertLevel = Comp(ev.Station);
+ comp.CanSetAlertAt = Timing.CurTime + TimeSpan.FromSeconds(alertLevel.CurrentDelay);
+ comp.CurrentAlertLevel = ev.AlertLevel;
+ if (alertLevel.IsLevelLocked)
+ comp.CanSetAlertAt = null;
+ Dirty(uid, comp);
+ }
+ }
+
+ private void OnExfiltrationChanged(ref StationExfiltrationChangedEvent ev)
+ {
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var uid, out var comp))
+ {
+ if (_station.GetOwningStation(uid) != ev.Station)
+ continue;
+
+ comp.ExpectedExfiltrationArrival = ev.Station.Comp.ArrivalTime;
+ Dirty(uid, comp);
+ }
+ }
+
+ private void OnRoundEndChanged(RoundEndSystemChangedEvent ev)
+ {
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var uid, out var comp))
+ {
+ comp.ExpectedEvacuationArrival = _roundEnd.ExpectedCountdownEnd;
+ comp.ExpectedEvacuationDuration = _roundEnd.ExpectedShuttleLength;
+ comp.ShuttlesCallable = ShuttlesCallable();
+ Dirty(uid, comp);
+ }
+ }
+
+ protected override void OnMapInit(Entity ent, ref MapInitEvent args)
+ {
+ base.OnMapInit(ent, ref args);
+
+ if (_station.GetOwningStation(ent) is not { } station)
+ return;
+
+ if (!TryComp(station, out var alertLevel))
+ return;
+
+ ent.Comp.CurrentAlertLevel = alertLevel.CurrentLevel;
+ var proto = _prototype.Index(alertLevel.AlertLevelPrototype);
+ foreach (var (name, detail) in proto.Levels)
+ {
+ ent.Comp.AlertLevels.Add(new($"alert-level-{name}", $"alert-level-{name}-announcement", name, !detail.DisableSelection, detail.Color));
+ }
+ ent.Comp.CanSetAlertAt = Timing.CurTime + TimeSpan.FromSeconds(alertLevel.CurrentDelay);
+ if (alertLevel.IsLevelLocked)
+ ent.Comp.CanSetAlertAt = null;
+ ent.Comp.ShuttlesCallable = ShuttlesCallable();
+
+ ent.Comp.ExpectedEvacuationArrival = _roundEnd.ExpectedCountdownEnd;
+ ent.Comp.ExpectedEvacuationDuration = _roundEnd.ExpectedShuttleLength;
+ if (TryComp(station, out var exfiltration))
+ ent.Comp.ExpectedExfiltrationArrival = exfiltration.ArrivalTime;
+
+ Dirty(ent);
+ }
+
+ private bool ShuttlesCallable()
+ {
+ // Defer to what the round end system thinks we should be able to do.
+ if (_emergencyShuttle.EmergencyShuttleArrived || !_roundEnd.CanCallOrRecall())
+ return false;
+
+ // Calling shuttle checks
+ if (_roundEnd.ExpectedCountdownEnd is null)
+ return true;
+
+ // Recalling shuttle checks
+ var recallThreshold = _configuration.GetCVar(CCVars.EmergencyRecallTurningPoint);
+
+ // shouldn't really be happening if we got here
+ if (_roundEnd.ShuttleTimeLeft is not { } left
+ || _roundEnd.ExpectedShuttleLength is not { } expected)
+ return false;
+
+ return !(left.TotalSeconds / expected.TotalSeconds < recallThreshold);
+ }
+
+ protected override void OnAlertLevel(Entity ent, ref DVCommunicationsConsoleAlertLevelMessage args)
+ {
+ base.OnAlertLevel(ent, ref args);
+
+ if (!AccessReader.IsAllowed(ent, args.Actor))
+ return;
+
+ if (_station.GetOwningStation(ent) is not { } station)
+ return;
+
+ _alertLevel.SetLevel(station, args.AlertLevel, true, true);
+ AdminLog.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(args.Actor):player} has set the alert level to {args.AlertLevel:level} on {ToPrettyString(station):station} using {ToPrettyString(ent):console}");
+ }
+
+ protected override void OnEvacuationShuttle(Entity ent, ref DVCommunicationsConsoleEvacuationShuttleMessage args)
+ {
+ base.OnEvacuationShuttle(ent, ref args);
+
+ if (!AccessReader.IsAllowed(ent, args.Actor))
+ return;
+
+ if (args.Call)
+ {
+ var ev = new CommunicationConsoleCallShuttleAttemptEvent(ent, default!, args.Actor);
+ RaiseLocalEvent(ref ev);
+
+ _roundEnd.RequestRoundEnd(args.Actor);
+ AdminLog.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(args.Actor):player} has called the evacuation shuttle using {ToPrettyString(ent):console}");
+ }
+ else
+ {
+ _roundEnd.CancelRoundEndCountdown(args.Actor);
+ AdminLog.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(args.Actor):player} has recalled the evacuation shuttle using {ToPrettyString(ent):console}");
+ }
+ }
+
+ protected override void OnExfiltrationShuttle(Entity ent, ref DVCommunicationsConsoleExfiltrationShuttleMessage args)
+ {
+ base.OnExfiltrationShuttle(ent, ref args);
+
+ if (!AccessReader.IsAllowed(ent, args.Actor))
+ return;
+
+ if (_station.GetOwningStation(ent) is not { } station)
+ return;
+
+ if (args.Call)
+ {
+ _stationExfiltration.Call(station);
+ AdminLog.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(args.Actor):player} has called the exfiltration shuttle using {ToPrettyString(ent):console}");
+ }
+ else
+ {
+ _stationExfiltration.Recall(station);
+ AdminLog.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(args.Actor):player} has recalled the exfiltration shuttle using {ToPrettyString(ent):console}");
+ }
+ }
+}
diff --git a/Content.Server/_DV/KeycardAuthenticationDevice/DVStationKeycardAuthenticationDeviceSystem.cs b/Content.Server/_DV/KeycardAuthenticationDevice/DVStationKeycardAuthenticationDeviceSystem.cs
new file mode 100644
index 00000000000..1cbc0e93ff6
--- /dev/null
+++ b/Content.Server/_DV/KeycardAuthenticationDevice/DVStationKeycardAuthenticationDeviceSystem.cs
@@ -0,0 +1,88 @@
+using Content.Server.AlertLevel;
+using Content.Server.Audio.Jukebox;
+using Content.Server.Instruments;
+using Content.Server.Light.Components;
+using Content.Server.Light.EntitySystems;
+using Content.Server.Nuke;
+using Content.Server.RoundEnd;
+using Content.Shared._DV.KeycardAuthenticationDevice;
+using Content.Shared.Audio.Jukebox;
+using Content.Shared.Light.Components;
+using Robust.Server.GameObjects;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server._DV.KeycardAuthenticationDevice;
+
+public sealed class DVStationKeycardAuthenticationDeviceSystem : SharedDVStationKeycardAuthenticationDeviceSystem
+{
+ [Dependency] private readonly NukeCodePaperSystem _nukeCodePaper = default!;
+ [Dependency] private readonly AlertLevelSystem _alertLevel = default!;
+ [Dependency] private readonly RoundEndSystem _roundEnd = default!;
+ [Dependency] private readonly IPrototypeManager _prototype = default!;
+ [Dependency] private readonly PoweredLightSystem _poweredLight = default!;
+ [Dependency] private readonly PointLightSystem _pointLight = default!;
+ [Dependency] private readonly SharedUserInterfaceSystem _userInterface = default!;
+ [Dependency] private readonly JukeboxSystem _jukebox = default!;
+
+ protected override void Mayday(Entity station)
+ {
+ base.Mayday(station);
+
+ _alertLevel.SetLevel(station, "zeta", true, true, true, true);
+ var alertLevel = Comp(station);
+ var level = _prototype.Index(alertLevel.AlertLevelPrototype).Levels[alertLevel.CurrentLevel];
+ _roundEnd.RequestRoundEnd(level.ShuttleTime, null, null, false, cantRecall: true);
+
+ var bulbQuery = GetEntityQuery();
+ var tubeQuery = EntityQueryEnumerator();
+ while (tubeQuery.MoveNext(out var uid, out var light))
+ {
+ if (Station.GetOwningStation(uid) != station.Owner)
+ continue;
+
+ if (_poweredLight.GetBulb(uid, light) is not { } bulb || !bulbQuery.TryComp(bulb, out var bulbComp))
+ continue;
+
+ bulbComp.LightEnergy /= 2;
+ bulbComp.PowerUse /= 2;
+
+ _poweredLight.SetState(uid, light.On, light);
+ }
+
+ var emergencyQuery = EntityQueryEnumerator();
+ while (emergencyQuery.MoveNext(out var uid, out var emergency, out var light))
+ {
+ if (Station.GetOwningStation(uid) != station.Owner)
+ continue;
+
+ _pointLight.SetEnergy(uid, emergency.MaydayEnergy, light);
+ _pointLight.SetRadius(uid, emergency.MaydayRadius, light);
+ _pointLight.SetFalloff(uid, emergency.MaydayFalloff, light);
+ }
+
+ var instrumentQuery = EntityQueryEnumerator();
+ while (instrumentQuery.MoveNext(out var uid, out _))
+ {
+ if (Station.GetOwningStation(uid) != station.Owner)
+ continue;
+
+ _userInterface.CloseUis(uid);
+ }
+
+ var jukeboxQuery = EntityQueryEnumerator();
+ while (jukeboxQuery.MoveNext(out var uid, out var jukebox))
+ {
+ if (Station.GetOwningStation(uid) != station.Owner)
+ continue;
+
+ _jukebox.Stop((uid, jukebox));
+ }
+ }
+
+ protected override void Scuttling(Entity station)
+ {
+ base.Scuttling(station);
+
+ _nukeCodePaper.SendNukeCodes(station);
+ }
+}
diff --git a/Content.Server/_DV/Screens/DVScreenSystem.cs b/Content.Server/_DV/Screens/DVScreenSystem.cs
new file mode 100644
index 00000000000..f4b798bc8fb
--- /dev/null
+++ b/Content.Server/_DV/Screens/DVScreenSystem.cs
@@ -0,0 +1,111 @@
+using Content.Server.AlertLevel;
+using Content.Server.Screens.Components;
+using Content.Shared._DV.Communications;
+using Content.Shared._DV.Screens;
+using Content.Shared.DeviceNetwork.Events;
+using Content.Shared.Station;
+using Robust.Shared.Timing;
+
+namespace Content.Server._DV.Screens;
+
+public sealed class DVScreenSystem : DVSharedScreenSystem
+{
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly SharedStationSystem _station = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnPacket);
+ SubscribeLocalEvent(OnAlertLevelChanged);
+ }
+
+ private void OnPacket(Entity ent, ref DeviceNetworkPacketEvent args)
+ {
+ if (args.Data.TryGetValue(ShuttleTimerMasks.ShuttleMap, out _))
+ OnShuttlePacket(ent, ref args);
+ if (args.Data.TryGetValue(DVScreenPackets.Text, out (string, string)? text))
+ OnTextPacket(ent, text.Value, ref args);
+ if (args.Data.TryGetValue(DVScreenPackets.ShowBorders, out bool? showBorders))
+ OnBordersPacket(ent, showBorders.Value, ref args);
+ if (args.Data.TryGetValue(DVScreenPackets.Content, out DVScreenContent? content))
+ OnContentPacket(ent, content.Value, ref args);
+ }
+
+ private void OnShuttlePacket(Entity ent, ref DeviceNetworkPacketEvent args)
+ {
+ var xform = Transform(ent);
+
+ args.Data.TryGetValue(ShuttleTimerMasks.ShuttleMap, out EntityUid? shuttleMap);
+ args.Data.TryGetValue(ShuttleTimerMasks.SourceMap, out EntityUid? source);
+ args.Data.TryGetValue(ShuttleTimerMasks.DestMap, out EntityUid? dest);
+ args.Data.TryGetValue(ShuttleTimerMasks.Docked, out bool docked);
+ var screenIsAtDestination = docked;
+ string key;
+
+ switch (xform.MapUid)
+ {
+ // sometimes the timer transforms on FTL shuttles have a hyperspace mapuid, so matching by grid works as a fallback.
+ case var local when local == shuttleMap || xform.GridUid == shuttleMap:
+ key = ShuttleTimerMasks.ShuttleTime;
+ break;
+ case var origin when origin == source:
+ key = ShuttleTimerMasks.SourceTime;
+ break;
+ case var remote when remote == dest:
+ key = ShuttleTimerMasks.DestTime;
+ screenIsAtDestination = false;
+ break;
+ default:
+ return;
+ }
+
+ if (!args.Data.TryGetValue(key, out TimeSpan duration))
+ return;
+
+ ent.Comp.ScreenIsAtDestination = screenIsAtDestination;
+ ent.Comp.TargetTime = _timing.CurTime + duration;
+ Dirty(ent);
+ UpdateVisuals(ent);
+ }
+
+ private void OnTextPacket(Entity ent, (string, string) text, ref DeviceNetworkPacketEvent args)
+ {
+ ent.Comp.Line1 = text.Item1;
+ ent.Comp.Line2 = text.Item2;
+
+ Dirty(ent);
+ UpdateVisuals(ent);
+ }
+
+ private void OnBordersPacket(Entity ent, bool showBorders, ref DeviceNetworkPacketEvent args)
+ {
+ ent.Comp.ShowAlertBorder = showBorders;
+
+ Dirty(ent);
+ UpdateVisuals(ent);
+ }
+
+ private void OnContentPacket(Entity ent, DVScreenContent content, ref DeviceNetworkPacketEvent args)
+ {
+ ent.Comp.Content = content;
+
+ Dirty(ent);
+ UpdateVisuals(ent);
+ }
+
+ private void OnAlertLevelChanged(AlertLevelChangedEvent ev)
+ {
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var uid, out var screen))
+ {
+ if (_station.GetOwningStation(uid) != ev.Station)
+ continue;
+
+ screen.AlertLevel = ev.AlertLevel;
+ Dirty(uid, screen);
+ UpdateVisuals((uid, screen));
+ }
+ }
+}
diff --git a/Content.Server/_DV/Station/Systems/StationExfiltrationSystem.cs b/Content.Server/_DV/Station/Systems/StationExfiltrationSystem.cs
index 741dd15466f..204fd3e675d 100644
--- a/Content.Server/_DV/Station/Systems/StationExfiltrationSystem.cs
+++ b/Content.Server/_DV/Station/Systems/StationExfiltrationSystem.cs
@@ -32,7 +32,6 @@ public sealed class StationExfiltrationSystem : EntitySystem
[Dependency] private readonly ShuttleSystem _shuttle = default!;
[Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly NavMapSystem _navMap = default!;
- [Dependency] private readonly CommunicationsConsoleSystem _communicationsConsole = default!;
public override void Initialize()
{
@@ -169,7 +168,8 @@ public sealed class StationExfiltrationSystem : EntitySystem
_chat.DispatchStationAnnouncement(ent, Loc.GetString(ent.Comp.CalledAnnouncement, ("time", ent.Comp.TravelTime.TotalSeconds), ("station", Name(ent))), sender: Loc.GetString(ent.Comp.Sender), colorOverride: Color.Gold);
}
- _communicationsConsole.UpdateCommsConsoleInterface();
+ var evt = new StationExfiltrationChangedEvent((ent, ent.Comp), true);
+ RaiseLocalEvent(ref evt);
}
public void Recall(Entity ent)
@@ -180,6 +180,10 @@ public sealed class StationExfiltrationSystem : EntitySystem
ent.Comp.ArrivalTime = null;
_chat.DispatchStationAnnouncement(ent, Loc.GetString(ent.Comp.RecalledAnnouncement, ("station", Name(ent))), sender: Loc.GetString(ent.Comp.Sender), colorOverride: Color.Gold);
- _communicationsConsole.UpdateCommsConsoleInterface();
+ var evt = new StationExfiltrationChangedEvent((ent, ent.Comp), false);
+ RaiseLocalEvent(ref evt);
}
}
+
+[ByRefEvent]
+public readonly record struct StationExfiltrationChangedEvent(Entity Station, bool Exfiltrating);
diff --git a/Content.Shared/Communications/SharedCommunicationsConsoleComponent.cs b/Content.Shared/Communications/SharedCommunicationsConsoleComponent.cs
index 7e16dde27f8..b129ea4a0fe 100644
--- a/Content.Shared/Communications/SharedCommunicationsConsoleComponent.cs
+++ b/Content.Shared/Communications/SharedCommunicationsConsoleComponent.cs
@@ -18,9 +18,8 @@ namespace Content.Shared.Communications
public List? AlertLevels;
public string CurrentAlert;
public float CurrentAlertDelay;
- public readonly TimeSpan? ExpectedExfiltrationCountdownEnd;
- public CommunicationsConsoleInterfaceState(bool canAnnounce, bool canCall, List? alertLevels, string currentAlert, float currentAlertDelay, TimeSpan? expectedCountdownEnd, TimeSpan? expectedExfiltrationCountdownEnd) // DeltaV - Exfiltration Shuttle
+ public CommunicationsConsoleInterfaceState(bool canAnnounce, bool canCall, List? alertLevels, string currentAlert, float currentAlertDelay, TimeSpan? expectedCountdownEnd)
{
CanAnnounce = canAnnounce;
CanCall = canCall;
@@ -29,7 +28,6 @@ namespace Content.Shared.Communications
AlertLevels = alertLevels;
CurrentAlert = currentAlert;
CurrentAlertDelay = currentAlertDelay;
- ExpectedExfiltrationCountdownEnd = expectedExfiltrationCountdownEnd; // DeltaV - Exfiltration Shuttle
}
}
diff --git a/Content.Shared/_DV/Communications/CommunicationsConsoleExfiltrationShuttleMessage.cs b/Content.Shared/_DV/Communications/CommunicationsConsoleExfiltrationShuttleMessage.cs
deleted file mode 100644
index 8d5dbd3b964..00000000000
--- a/Content.Shared/_DV/Communications/CommunicationsConsoleExfiltrationShuttleMessage.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Robust.Shared.Serialization;
-
-namespace Content.Shared._DV.Communications;
-
-[Serializable, NetSerializable]
-public sealed class CommunicationsConsoleExfiltrationShuttleMessage(bool call) : BoundUserInterfaceMessage
-{
- public readonly bool Call = call;
-}
diff --git a/Content.Shared/_DV/Communications/DVCommunicationsConsoleComponent.cs b/Content.Shared/_DV/Communications/DVCommunicationsConsoleComponent.cs
new file mode 100644
index 00000000000..66fbda5a159
--- /dev/null
+++ b/Content.Shared/_DV/Communications/DVCommunicationsConsoleComponent.cs
@@ -0,0 +1,136 @@
+using Content.Shared._DV.KeycardAuthenticationDevice;
+using Content.Shared._DV.Screens;
+using Robust.Shared.Audio;
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared._DV.Communications;
+
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
+[Access(typeof(SharedDVCommunicationsConsoleSystem))]
+public sealed partial class DVCommunicationsConsoleComponent : Component
+{
+ [DataField]
+ public LocId AnnouncementTitle = "comms-console-announcement-title-station";
+
+ [DataField]
+ public Color AnnouncementColor = Color.Gold;
+
+ [DataField]
+ public SoundSpecifier AnnouncementSound = new SoundPathSpecifier("/Audio/Announcements/announce.ogg");
+
+ [DataField]
+ public bool GlobalAnnouncements;
+
+ [DataField]
+ public bool CanAnnounce = true;
+
+ [DataField]
+ public bool CanAlertLevel = true;
+
+ [DataField]
+ public bool CanCallShuttles = true;
+
+ [DataField]
+ public bool CanConfigureScreens = true;
+
+ [DataField]
+ public bool CanKeycardAuthenticationDevice = true;
+
+ [DataField, AutoNetworkedField]
+ public TimeSpan CanAnnounceAt = TimeSpan.Zero;
+
+ [DataField]
+ public TimeSpan AnnouncementInterval = TimeSpan.FromSeconds(90f);
+
+ [DataField]
+ public TimeSpan InitialAnnouncementDelay = TimeSpan.FromSeconds(30f);
+
+ [DataField, AutoNetworkedField]
+ public string CurrentAlertLevel = string.Empty;
+
+ [DataField, AutoNetworkedField]
+ public List AlertLevels = new();
+
+ [DataField, AutoNetworkedField]
+ public TimeSpan? CanSetAlertAt = TimeSpan.Zero;
+
+ [DataField, AutoNetworkedField]
+ public bool ShuttlesCallable = true;
+
+ [DataField, AutoNetworkedField]
+ public TimeSpan? ExpectedEvacuationArrival;
+
+ [DataField, AutoNetworkedField]
+ public TimeSpan? ExpectedEvacuationDuration;
+
+ [DataField, AutoNetworkedField]
+ public TimeSpan? ExpectedExfiltrationArrival;
+
+ [DataField, AutoNetworkedField]
+ public DVScreenContent LastConfiguredContent = DVScreenContent.Text;
+
+ [DataField, AutoNetworkedField]
+ public bool LastConfiguredShowBorders;
+
+ [DataField, AutoNetworkedField]
+ public string LastConfiguredLine1 = string.Empty;
+
+ [DataField, AutoNetworkedField]
+ public string LastConfiguredLine2 = string.Empty;
+}
+
+[Serializable, NetSerializable]
+public readonly record struct DVCommunicationsConsoleAlertLevel(LocId AlertLevel, LocId Description, string Id, bool CanSet, Color Color);
+
+[Serializable, NetSerializable]
+public sealed class DVCommunicationsConsoleEvacuationShuttleMessage(bool call) : BoundUserInterfaceMessage
+{
+ public readonly bool Call = call;
+}
+
+[Serializable, NetSerializable]
+public sealed class DVCommunicationsConsoleExfiltrationShuttleMessage(bool call) : BoundUserInterfaceMessage
+{
+ public readonly bool Call = call;
+}
+
+[Serializable, NetSerializable]
+public sealed class DVCommunicationsConsoleKeycardAuthenticationDeviceMessage(DVStationKeycardAction action) : BoundUserInterfaceMessage
+{
+ public readonly DVStationKeycardAction Action = action;
+}
+
+[Serializable, NetSerializable]
+public sealed class DVCommunicationsConsoleAnnouncementMessage(string announcement) : BoundUserInterfaceMessage
+{
+ public readonly string Announcement = announcement;
+}
+
+[Serializable, NetSerializable]
+public sealed class DVCommunicationsConsoleAlertLevelMessage(string alertLevel) : BoundUserInterfaceMessage
+{
+ public readonly string AlertLevel = alertLevel;
+}
+
+[Serializable, NetSerializable]
+public sealed class DVCommunicationsConsoleScreenConfigurationMessage(DVScreenContent content, bool showBorder, string line1, string line2) : BoundUserInterfaceMessage
+{
+ public readonly DVScreenContent Content = content;
+ public readonly bool ShowBorder = showBorder;
+ public readonly string Line1 = line1;
+ public readonly string Line2 = line2;
+}
+
+[Serializable, NetSerializable]
+public enum DVCommunicationsConsoleUi : byte
+{
+ Key,
+}
+
+public static class DVScreenPackets
+{
+ public const string Content = "dv-screen-content";
+ public const string ShowBorders = "dv-screen-borders";
+ public const string Text = "dv-screen-text";
+}
diff --git a/Content.Shared/_DV/Communications/DVCommunicationsConsoleSystem.cs b/Content.Shared/_DV/Communications/DVCommunicationsConsoleSystem.cs
new file mode 100644
index 00000000000..cd45a716043
--- /dev/null
+++ b/Content.Shared/_DV/Communications/DVCommunicationsConsoleSystem.cs
@@ -0,0 +1,148 @@
+using Content.Shared._DV.KeycardAuthenticationDevice;
+using Content.Shared._DV.Screens;
+using Content.Shared.Access.Systems;
+using Content.Shared.Administration.Logs;
+using Content.Shared.Chat;
+using Content.Shared.Database;
+using Content.Shared.DeviceNetwork;
+using Content.Shared.DeviceNetwork.Events;
+using Content.Shared.DeviceNetwork.Systems;
+using Content.Shared.IdentityManagement;
+using Robust.Shared.Timing;
+
+namespace Content.Shared._DV.Communications;
+
+public abstract class SharedDVCommunicationsConsoleSystem : EntitySystem
+{
+ [Dependency] private readonly SharedDeviceNetworkSystem _deviceNetwork = default!;
+ [Dependency] protected readonly IGameTiming Timing = default!;
+ [Dependency] private readonly SharedChatSystem _chat = default!;
+ [Dependency] protected readonly AccessReaderSystem AccessReader = default!;
+ [Dependency] protected readonly ISharedAdminLogManager AdminLog = default!;
+ [Dependency] private readonly SharedDVStationKeycardAuthenticationDeviceSystem _stationKeycardAuthenticationDevice = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnMapInit);
+ SubscribeLocalEvent(OnPacketReceive);
+ Subs.BuiEvents(DVCommunicationsConsoleUi.Key,
+ subs =>
+ {
+ subs.Event(OnEvacuationShuttle);
+ subs.Event(OnExfiltrationShuttle);
+ subs.Event(OnConfiguration);
+ subs.Event(OnAnnouncement);
+ subs.Event(OnAlertLevel);
+ subs.Event(OnKeycardAuthenticationDevice);
+ });
+ }
+
+ private void OnPacketReceive(Entity ent, ref DeviceNetworkPacketEvent args)
+ {
+ if (args.Data.TryGetValue(DVScreenPackets.Text, out (string, string)? text))
+ {
+ ent.Comp.LastConfiguredLine1 = text.Value.Item1;
+ ent.Comp.LastConfiguredLine2 = text.Value.Item2;
+ Dirty(ent);
+ }
+ if (args.Data.TryGetValue(DVScreenPackets.ShowBorders, out bool? showBorders))
+ {
+ ent.Comp.LastConfiguredShowBorders = showBorders.Value;
+ Dirty(ent);
+ }
+ if (args.Data.TryGetValue(DVScreenPackets.Content, out DVScreenContent? content))
+ {
+ ent.Comp.LastConfiguredContent = content.Value;
+ Dirty(ent);
+ }
+ }
+
+ protected virtual void OnMapInit(Entity ent, ref MapInitEvent args)
+ {
+ ent.Comp.CanAnnounceAt = Timing.CurTime + ent.Comp.InitialAnnouncementDelay;
+ Dirty(ent);
+ }
+
+ private void OnAnnouncement(Entity ent, ref DVCommunicationsConsoleAnnouncementMessage args)
+ {
+ if (!ent.Comp.CanAnnounce)
+ return;
+
+ if (Timing.CurTime <= ent.Comp.CanAnnounceAt)
+ return;
+
+ if (!AccessReader.IsAllowed(args.Actor, ent))
+ return;
+
+ var identity = new TryGetIdentityShortInfoEvent(ent, args.Actor);
+ RaiseLocalEvent(identity);
+
+ Loc.TryGetString(ent.Comp.AnnouncementTitle, out var title);
+ title ??= ent.Comp.AnnouncementTitle;
+
+ var msg = args.Announcement;
+ msg += "\n" + Loc.GetString("comms-console-announcement-sent-by") + " " + identity.Title;
+
+ if (ent.Comp.GlobalAnnouncements)
+ {
+ _chat.DispatchGlobalAnnouncement(msg, title, announcementSound: ent.Comp.AnnouncementSound, colorOverride: ent.Comp.AnnouncementColor);
+ AdminLog.Add(LogType.Chat, LogImpact.Low, $"{ToPrettyString(args.Actor):player} sent the following global announcement using {ToPrettyString(ent):console}: {msg:message}");
+ }
+ else
+ {
+ _chat.DispatchStationAnnouncement(ent, msg, title, announcementSound: ent.Comp.AnnouncementSound, colorOverride: ent.Comp.AnnouncementColor);
+ AdminLog.Add(LogType.Chat, LogImpact.Low, $"{ToPrettyString(args.Actor):player} sent the following station announcement using {ToPrettyString(ent):console}: {msg:message}");
+ }
+
+ ent.Comp.CanAnnounceAt = Timing.CurTime + ent.Comp.AnnouncementInterval;
+ Dirty(ent);
+ }
+
+ private void OnConfiguration(Entity ent, ref DVCommunicationsConsoleScreenConfigurationMessage args)
+ {
+ if (!ent.Comp.CanConfigureScreens)
+ return;
+
+ if (!AccessReader.IsAllowed(args.Actor, ent))
+ return;
+
+ ent.Comp.LastConfiguredLine1 = args.Line1;
+ ent.Comp.LastConfiguredLine2 = args.Line2;
+ ent.Comp.LastConfiguredShowBorders = args.ShowBorder;
+ ent.Comp.LastConfiguredContent = args.Content;
+ Dirty(ent);
+
+ var payload = new NetworkPayload
+ {
+ [DVScreenPackets.Content] = args.Content,
+ [DVScreenPackets.ShowBorders] = args.ShowBorder,
+ [DVScreenPackets.Text] = (args.Line1, args.Line2),
+ };
+
+ AdminLog.Add(LogType.Chat, LogImpact.Low, $"{ToPrettyString(args.Actor):player} configured the following text using {ToPrettyString(ent):console}: {args.Line1:line1} {args.Line2:line2}");
+ _deviceNetwork.QueuePacket(ent, null, payload);
+ }
+
+ protected virtual void OnExfiltrationShuttle(Entity ent,
+ ref DVCommunicationsConsoleExfiltrationShuttleMessage args)
+ {
+ }
+
+ protected virtual void OnEvacuationShuttle(Entity ent,
+ ref DVCommunicationsConsoleEvacuationShuttleMessage args)
+ {
+ }
+
+ protected virtual void OnAlertLevel(Entity ent,
+ ref DVCommunicationsConsoleAlertLevelMessage args)
+ {
+ }
+
+ private void OnKeycardAuthenticationDevice(Entity ent,
+ ref DVCommunicationsConsoleKeycardAuthenticationDeviceMessage args)
+ {
+ _stationKeycardAuthenticationDevice.TrySwipe(args.Actor, ent, args.Action);
+ }
+}
diff --git a/Content.Shared/_DV/KeycardAuthenticationDevice/DVStationKeycardAuthenticationDeviceAlreadySwipedComponent.cs b/Content.Shared/_DV/KeycardAuthenticationDevice/DVStationKeycardAuthenticationDeviceAlreadySwipedComponent.cs
new file mode 100644
index 00000000000..b524bda946d
--- /dev/null
+++ b/Content.Shared/_DV/KeycardAuthenticationDevice/DVStationKeycardAuthenticationDeviceAlreadySwipedComponent.cs
@@ -0,0 +1,7 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared._DV.KeycardAuthenticationDevice;
+
+[RegisterComponent, NetworkedComponent]
+[Access(typeof(SharedDVStationKeycardAuthenticationDeviceSystem))]
+public sealed partial class DVStationKeycardAuthenticationDeviceAlreadySwipedComponent : Component;
diff --git a/Content.Shared/_DV/KeycardAuthenticationDevice/DVStationKeycardAuthenticationDeviceComponent.cs b/Content.Shared/_DV/KeycardAuthenticationDevice/DVStationKeycardAuthenticationDeviceComponent.cs
new file mode 100644
index 00000000000..0b717cb8286
--- /dev/null
+++ b/Content.Shared/_DV/KeycardAuthenticationDevice/DVStationKeycardAuthenticationDeviceComponent.cs
@@ -0,0 +1,50 @@
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
+
+namespace Content.Shared._DV.KeycardAuthenticationDevice;
+
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause]
+[Access(typeof(SharedDVStationKeycardAuthenticationDeviceSystem))]
+public sealed partial class DVStationKeycardAuthenticationDeviceComponent : Component
+{
+ [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField, AutoPausedField]
+ public TimeSpan AccessibleAfter = TimeSpan.Zero;
+
+ [DataField]
+ public TimeSpan FailureDelay = TimeSpan.FromMinutes(2);
+
+ [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField, AutoPausedField]
+ public TimeSpan? SwipesStartedAt = null;
+
+ [DataField]
+ public TimeSpan SwipeWindow = TimeSpan.FromSeconds(10);
+
+ [DataField(required: true)]
+ public Dictionary ActionThresholds;
+
+ [DataField, AutoNetworkedField]
+ public int Swipes;
+
+ [DataField, AutoNetworkedField]
+ public DVStationKeycardAction? SwipingFor;
+
+ [DataField]
+ public LocId FailureAnnouncementSender = "keycard-authentication-device-sender";
+
+ [DataField]
+ public LocId FailureAnnouncement = "keycard-authentication-device-warning";
+
+ [DataField]
+ public Color FailureAnnouncementColor = Color.FromHex("#e93a9a");
+
+ [DataField]
+ public TimeSpan FailureElectrocutionDuration = TimeSpan.FromSeconds(5);
+}
+
+[Serializable, NetSerializable]
+public enum DVStationKeycardAction
+{
+ Mayday,
+ Scuttling,
+}
diff --git a/Content.Shared/_DV/KeycardAuthenticationDevice/SharedDVStationKeycardAuthenticationDeviceSystem.cs b/Content.Shared/_DV/KeycardAuthenticationDevice/SharedDVStationKeycardAuthenticationDeviceSystem.cs
new file mode 100644
index 00000000000..7f9cd50d81a
--- /dev/null
+++ b/Content.Shared/_DV/KeycardAuthenticationDevice/SharedDVStationKeycardAuthenticationDeviceSystem.cs
@@ -0,0 +1,118 @@
+using Content.Shared.Chat;
+using Content.Shared.Electrocution;
+using Content.Shared.Station;
+using Robust.Shared.Audio;
+using Robust.Shared.Timing;
+
+namespace Content.Shared._DV.KeycardAuthenticationDevice;
+
+public abstract class SharedDVStationKeycardAuthenticationDeviceSystem : EntitySystem
+{
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] protected readonly SharedStationSystem Station = default!;
+ [Dependency] private readonly SharedElectrocutionSystem _electrocution = default!;
+ [Dependency] private readonly SharedChatSystem _chat = default!;
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var uid, out var keycard))
+ {
+ if (keycard.SwipesStartedAt is not { } startedAt || _timing.CurTime <= startedAt + keycard.SwipeWindow)
+ continue;
+
+ StopSwipes((uid, keycard), true);
+ }
+ }
+
+ private void StopSwipes(Entity station, bool failed)
+ {
+ station.Comp.SwipesStartedAt = null;
+ station.Comp.Swipes = 0;
+ station.Comp.SwipingFor = null;
+ if (failed)
+ {
+ station.Comp.AccessibleAfter = _timing.CurTime + station.Comp.FailureDelay;
+ _chat.DispatchStationAnnouncement(station,
+ Loc.GetString(station.Comp.FailureAnnouncement),
+ Loc.GetString(station.Comp.FailureAnnouncementSender),
+ announcementSound: new SoundPathSpecifier("/Audio/_DV/Announcements/attention.ogg"),
+ colorOverride: station.Comp.FailureAnnouncementColor);
+ }
+ Dirty(station);
+
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var uid, out _))
+ {
+ if (failed)
+ {
+ _electrocution.TryDoElectrocution(uid,
+ null,
+ 10,
+ station.Comp.FailureElectrocutionDuration,
+ true,
+ ignoreInsulation: true);
+ }
+ RemCompDeferred(uid);
+ }
+ }
+
+ private void Swipe(Entity station, EntityUid user, DVStationKeycardAction action)
+ {
+ if (!station.Comp.ActionThresholds.TryGetValue(action, out var threshold))
+ return;
+
+ station.Comp.SwipesStartedAt ??= _timing.CurTime;
+ station.Comp.SwipingFor = action;
+ station.Comp.Swipes++;
+ AddComp(user);
+ Dirty(station);
+
+ if (station.Comp.Swipes >= threshold)
+ {
+ StopSwipes(station, false);
+ DoAction(station, action);
+ }
+ }
+
+ private void DoAction(Entity station, DVStationKeycardAction action)
+ {
+ switch (action)
+ {
+ case DVStationKeycardAction.Mayday:
+ Mayday(station);
+ break;
+
+ case DVStationKeycardAction.Scuttling:
+ Scuttling(station);
+ break;
+ }
+ }
+
+ protected virtual void Mayday(Entity station)
+ {
+ }
+
+ protected virtual void Scuttling(Entity station)
+ {
+ }
+
+ public void TrySwipe(EntityUid user, EntityUid console, DVStationKeycardAction action)
+ {
+ if (HasComp(console))
+ return;
+
+ if (Station.GetOwningStation(user) is not { } station || !TryComp(station, out var keycard))
+ return;
+
+ if (keycard.SwipingFor is { } swipingFor && swipingFor != action)
+ return;
+
+ if (_timing.CurTime <= keycard.AccessibleAfter)
+ return;
+
+ Swipe((station, keycard), user, action);
+ }
+}
diff --git a/Content.Shared/_DV/Screens/DVScreenComponent.cs b/Content.Shared/_DV/Screens/DVScreenComponent.cs
new file mode 100644
index 00000000000..b85ca820755
--- /dev/null
+++ b/Content.Shared/_DV/Screens/DVScreenComponent.cs
@@ -0,0 +1,55 @@
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared._DV.Screens;
+
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true), AutoGenerateComponentPause]
+[Access(typeof(DVSharedScreenSystem))]
+public sealed partial class DVScreenComponent : Component
+{
+ [DataField, AutoNetworkedField]
+ public string? AlertLevel; // I don't like this but uhhh the prototype isn't client-accessible
+
+ [DataField, AutoNetworkedField]
+ public bool ShowAlertBorder;
+
+ [DataField, AutoNetworkedField]
+ public DVScreenContent Content = DVScreenContent.Text;
+
+ #region Text Screens
+
+ [DataField, AutoNetworkedField]
+ public string Line1 = string.Empty;
+
+ [DataField, AutoNetworkedField]
+ public string Line2 = string.Empty;
+
+ #endregion
+
+ #region ETA Screens
+
+ [DataField, AutoNetworkedField]
+ public bool ScreenIsAtDestination;
+
+ [DataField, AutoNetworkedField, AutoPausedField]
+ public TimeSpan TargetTime = TimeSpan.Zero;
+
+ #endregion
+}
+
+[Serializable, NetSerializable]
+public enum DVScreenVisuals : byte
+{
+ AlertLevel,
+ ShowAlertBorder,
+ Content,
+}
+
+[Serializable, NetSerializable]
+public enum DVScreenContent : byte
+{
+ Text,
+ CurrentTime,
+ EstimatedTimeOfArrival,
+ AlertLevel,
+}
diff --git a/Content.Shared/_DV/Screens/DVSharedScreenSystem.cs b/Content.Shared/_DV/Screens/DVSharedScreenSystem.cs
new file mode 100644
index 00000000000..0546777a04a
--- /dev/null
+++ b/Content.Shared/_DV/Screens/DVSharedScreenSystem.cs
@@ -0,0 +1,13 @@
+namespace Content.Shared._DV.Screens;
+
+public abstract class DVSharedScreenSystem : EntitySystem
+{
+ [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
+
+ protected void UpdateVisuals(Entity ent)
+ {
+ _appearance.SetData(ent.Owner, DVScreenVisuals.AlertLevel, ent.Comp.AlertLevel ?? string.Empty);
+ _appearance.SetData(ent.Owner, DVScreenVisuals.ShowAlertBorder, ent.Comp.ShowAlertBorder);
+ _appearance.SetData(ent.Owner, DVScreenVisuals.Content, ent.Comp.Content);
+ }
+}
diff --git a/Content.Shared/_DV/Screens/DVTextScreenVisualLayers.cs b/Content.Shared/_DV/Screens/DVTextScreenVisualLayers.cs
new file mode 100644
index 00000000000..86be43fc474
--- /dev/null
+++ b/Content.Shared/_DV/Screens/DVTextScreenVisualLayers.cs
@@ -0,0 +1,10 @@
+using Robust.Shared.Serialization;
+
+namespace Content.Shared._DV.Screens;
+
+[Serializable, NetSerializable]
+public enum DVTextScreenVisualLayers : byte
+{
+ Line1,
+ Line2,
+}
diff --git a/Resources/Audio/_DV/Announcements/Alerts/code_zeta.ogg b/Resources/Audio/_DV/Announcements/Alerts/code_zeta.ogg
new file mode 100644
index 00000000000..12c4cd7fdfe
Binary files /dev/null and b/Resources/Audio/_DV/Announcements/Alerts/code_zeta.ogg differ
diff --git a/Resources/Audio/_Starlight/misc/orange.ogg b/Resources/Audio/_Starlight/misc/orange.ogg
deleted file mode 100644
index 54632638215..00000000000
Binary files a/Resources/Audio/_Starlight/misc/orange.ogg and /dev/null differ
diff --git a/Resources/Fonts/_DV/TinyUnicode.ttf b/Resources/Fonts/_DV/TinyUnicode.ttf
new file mode 100644
index 00000000000..74d0d3e386e
Binary files /dev/null and b/Resources/Fonts/_DV/TinyUnicode.ttf differ
diff --git a/Resources/Locale/en-US/_DV/communications/communications-console-component.ftl b/Resources/Locale/en-US/_DV/communications/communications-console-component.ftl
index 0c91d311730..792822508bd 100644
--- a/Resources/Locale/en-US/_DV/communications/communications-console-component.ftl
+++ b/Resources/Locale/en-US/_DV/communications/communications-console-component.ftl
@@ -1,5 +1,60 @@
comms-console-announcement-title-unauthorized = Unauthorized
-comms-console-menu-call-exfiltration = Call exfiltration shuttle
comms-console-menu-exfiltration-shuttle-button-tooltip = Calls or recalls the exfiltration shuttle.
comms-console-menu-exfiltration-time-remaining = ETA of exfiltration shuttle: {$time}
comms-console-menu-recall-exfiltration = Recall exfiltration shuttle
+
+comms-console-menu-screen-content =
+ .Text = Text
+ .CurrentTime = Current Time
+ .EstimatedTimeOfArrival = ETA / ETD
+ .AlertLevel = Alert Level
+
+comms-console-menu-tab =
+ .Announcement = Make an announcement
+ .Screen = Configure status displays
+ .Shuttles = Manage shuttles
+ .AlertLevel = Set the alert level
+ .KeycardAuthenticationDevice = Keycard Authentication Device
+
+comms-console-menu-line-1 = Line 1:
+comms-console-menu-line-2 = Line 2:
+comms-console-menu-alert-level-border = Alert level border:
+comms-console-menu-alert-level = Alert level
+comms-console-menu-screen-contents = Screen contents:
+comms-console-menu-send-announcement = Send announcement
+comms-console-menu-update-screen-text = Update text
+comms-console-menu-enable-border = Enable alert level border
+comms-console-menu-home = Home
+comms-console-menu-emergency-shuttle = Emergency shuttle
+comms-console-menu-exfiltration-shuttle = Exfiltration shuttle
+comms-console-menu-shuttle-not-coming = This shuttle is not currently requested.
+comms-console-menu-shuttle-eta = This shuttle will arrive in {$time}.
+comms-console-menu-screen-text = Screen text
+comms-console-menu-screen-configuration = Screen options
+
+comms-console-menu-current-alert-level = The current alert level is [color={$color}]{$name}[/color]. {$description}
+comms-console-menu-change-alert-level = Change alert level
+
+comms-console-menu-station-title = Station
+comms-console-menu-transit-title = Transit
+comms-console-menu-emergency-title = Emergency
+
+comms-console-menu-declare-mayday =
+ .title = Declare mayday
+ .desc = Declare the station unfit for life and send out a distress signal to all nearby vessels. Requires two simultaneous authorizations.
+ .button = Declare Code Sigma
+
+comms-console-menu-request-codes =
+ .title = Scuttle the station
+ .desc = Order the destruction of the station to prevent capture, containment breach, or further threats to the sector. Requires three simultaneous authorizations.
+ .button = Request nuclear authorization codes
+
+comms-console-menu-call-emergency =
+ .True = Call emergency shuttle
+ .False = Recall emergency shuttle
+
+comms-console-menu-call-exfiltration =
+ .True = Call exfiltration shuttle
+ .False = Recall exfiltration shuttle
+
+comms-console-menu-announcement-text = Announcement text
diff --git a/Resources/Locale/en-US/_DV/station-events/keycard-authentication-device.ftl b/Resources/Locale/en-US/_DV/station-events/keycard-authentication-device.ftl
new file mode 100644
index 00000000000..0a0044f9d3b
--- /dev/null
+++ b/Resources/Locale/en-US/_DV/station-events/keycard-authentication-device.ftl
@@ -0,0 +1,2 @@
+keycard-authentication-device-sender = Keycard Authentication Device
+keycard-authentication-device-warning = Unauthorized user(s) of the keycard authentication device detected. Administering deterrence measures and initiating cooldown.
diff --git a/Resources/Locale/en-US/_DV/station-events/screens.ftl b/Resources/Locale/en-US/_DV/station-events/screens.ftl
new file mode 100644
index 00000000000..2ce04e66eac
--- /dev/null
+++ b/Resources/Locale/en-US/_DV/station-events/screens.ftl
@@ -0,0 +1,3 @@
+status-display-eta = -ETA-
+status-display-etd = -ETD-
+status-display-time = TIME
diff --git a/Resources/Locale/en-US/_Starlight/alert-levels/alert-levels.ftl b/Resources/Locale/en-US/_Starlight/alert-levels/alert-levels.ftl
deleted file mode 100644
index 90326884aa5..00000000000
--- a/Resources/Locale/en-US/_Starlight/alert-levels/alert-levels.ftl
+++ /dev/null
@@ -1,3 +0,0 @@
-alert-level-orange = Orange
-alert-level-orange-announcement = There is a critical station-wide structural or atmospheric threat and recovery is unlikely. Engineering staff are advised to minimize hazards and secure the Evacuation Dock. Crewmembers are advised to stay away from hazardous areas, and prepare for Evacuation. MAYDAY signals have been activated
-alert-level-orange-instructions = Avoid hazards and prepare for Evacuation.
diff --git a/Resources/Locale/en-US/alert-levels/alert-levels.ftl b/Resources/Locale/en-US/alert-levels/alert-levels.ftl
index 8e5605c6e74..461e538fc0f 100644
--- a/Resources/Locale/en-US/alert-levels/alert-levels.ftl
+++ b/Resources/Locale/en-US/alert-levels/alert-levels.ftl
@@ -45,3 +45,7 @@ alert-level-octarine = Octarine
alert-level-octarine-announcement = A cataclysmic noospheric event threatens to envelop realspace. Station crew are to contain the event if possible, or else evacuate.
alert-level-octarine-instructions = Crewmembers are advised to listen to heads of staff for more information.
+## DeltaV - Zeta
+alert-level-zeta = Zeta
+alert-level-zeta-announcement = The station is unrecoverable, and all important assets and crewmembers are to be evacuated. A distress signal has been sent to all nearby vessels. Security and Engineering staff are advised to minimize hazards and secure the Evacuation Dock. Crewmembers are advised to disembark the station by any means necessary. Godspeed.
+alert-level-zeta-instructions = Avoid hazards and prepare for Evacuation.
diff --git a/Resources/Maps/ovni.yml b/Resources/Maps/ovni.yml
index df7c1421c9e..c9406de2f35 100644
--- a/Resources/Maps/ovni.yml
+++ b/Resources/Maps/ovni.yml
@@ -28978,8 +28978,6 @@ entities:
- type: Transform
pos: 12.5,39.5
parent: 8
- - type: CommunicationsConsole
- announcementCooldownRemaining: -0.0166666
- proto: ComputerCrewMonitoring
entities:
- uid: 678
diff --git a/Resources/Migrations/deltaMigrations.yml b/Resources/Migrations/deltaMigrations.yml
index c7388bea239..5f31aae3715 100644
--- a/Resources/Migrations/deltaMigrations.yml
+++ b/Resources/Migrations/deltaMigrations.yml
@@ -250,4 +250,8 @@ BoxPerformer: BoxPerformerDV
# 2026-07-04
SpawnPointMedicalBorg: SpawnPointBorg
-SpawnPointSecurityBorg: SpawnPointBorg
\ No newline at end of file
+SpawnPointSecurityBorg: SpawnPointBorg
+
+# 2026-06-02
+Screen: StatusDisplay
+ArrivalsShuttleTimer: StatusDisplayShuttle
diff --git a/Resources/Prototypes/AlertLevels/alert_levels.yml b/Resources/Prototypes/AlertLevels/alert_levels.yml
index 8067bec2424..dd9171a4367 100644
--- a/Resources/Prototypes/AlertLevels/alert_levels.yml
+++ b/Resources/Prototypes/AlertLevels/alert_levels.yml
@@ -43,17 +43,6 @@
emergencyLightColor: Red
forceEnableEmergencyLights: true
shuttleTime: 600 #No reduction in time as we don't have swiping for red alert like in /tg/. Shuttle times are intended to create friction, so having a way to brainlessly bypass that would be dumb.
- # Begin Starlight Additions
- orange:
- announcement: alert-level-orange-announcement
- selectable: true
- sound:
- path: /Audio/_Starlight/misc/orange.ogg
- color: "#ff8119"
- emergencyLightColor: "#ff8119"
- forceEnableEmergencyLights: true
- shuttleTime: 480
- # End Starlight Additions
gamma:
announcement: alert-level-gamma-announcement
selectable: false
@@ -90,3 +79,13 @@
emergencyLightColor: CadetBlue
forceEnableEmergencyLights: true
shuttleTime: 600
+ zeta: # DeltaV - zeta
+ announcement: alert-level-zeta-announcement
+ selectable: false
+ disableSelection: true
+ sound:
+ path: /Audio/_DV/Announcements/Alerts/code_zeta.ogg
+ color: "#fe8401"
+ emergencyLightColor: "#fe8401"
+ forceEnableEmergencyLights: true
+ shuttleTime: 480
diff --git a/Resources/Prototypes/Entities/Stations/nanotrasen.yml b/Resources/Prototypes/Entities/Stations/nanotrasen.yml
index 5114fefd991..a524004665f 100644
--- a/Resources/Prototypes/Entities/Stations/nanotrasen.yml
+++ b/Resources/Prototypes/Entities/Stations/nanotrasen.yml
@@ -32,6 +32,7 @@
- BaseStationStockMarket
- BaseStationLavaland
- BaseStationExfiltration
+ - BaseStationKeycardAuthenticationDevice
# End DeltaV - Station additions
categories: [ HideSpawnMenu ]
components:
diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml
index 5aa7b894acd..d439d3f30f6 100644
--- a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml
+++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml
@@ -695,17 +695,22 @@
state: generic_panel_open
- type: AccessReader
access: [[ "Command" ]]
- - type: CommunicationsConsole
- title: comms-console-announcement-title-station
+ # Begin DeltaV Removals - our own thing
+ # - type: CommunicationsConsole
+ # title: comms-console-announcement-title-station
+ # End DeltaV Removals - our own thing
+ - type: DVCommunicationsConsole # DeltaV - new functionality
+ announcementTitle: comms-console-announcement-title-station
- type: DeviceNetwork
deviceNetId: Wireless
transmitFrequencyId: ShuttleTimer
+ receiveFrequencyId: ShuttleTimer # DeltaV - new functionality
- type: ActivatableUI
- key: enum.CommunicationsConsoleUiKey.Key
+ key: enum.DVCommunicationsConsoleUi.Key # DeltaV - new UI
- type: UserInterface
interfaces:
- enum.CommunicationsConsoleUiKey.Key:
- type: CommunicationsConsoleBoundUserInterface
+ enum.DVCommunicationsConsoleUi.Key: # DeltaV - new UI
+ type: DVCommunicationsConsoleBoundUserInterface
enum.WiresUiKey.Key:
type: WiresBoundUserInterface
- type: Computer
@@ -741,13 +746,25 @@
state: generic_panel_open
- type: AccessReader
access: [[ "NuclearOperative" ]]
- - type: CommunicationsConsole
- title: comms-console-announcement-title-unauthorized # DeltaV
- color: "#ff0000"
- canShuttle: false
- global: true #announce to everyone they're about to fuck shit up
- sound: /Audio/Announcements/intercept.ogg # DeltaV
- announceSentBy: false # The title already says who they are.
+ # Begin DeltaV Changes
+ # - type: CommunicationsConsole
+ # title: comms-console-announcement-title-unauthorized # DeltaV
+ # color: "#ff0000"
+ # canShuttle: false
+ # global: true #announce to everyone they're about to fuck shit up
+ # sound: /Audio/Announcements/intercept.ogg # DeltaV
+ # announceSentBy: false # The title already says who they are.
+ - type: DVCommunicationsConsole
+ announcementTitle: comms-console-announcement-title-unauthorized
+ announcementColor: "#ff0000"
+ announcementSound:
+ path: /Audio/Announcements/intercept.ogg
+ canAlertLevel: false
+ canCallShuttles: false
+ canConfigureScreens: false
+ canKeycardAuthenticationDevice: false
+ globalAnnouncements: true
+ # End DeltaV Changes
- type: Computer
board: SyndicateCommsComputerCircuitboard
- type: PointLight
@@ -775,13 +792,23 @@
state: generic_panel_open
- type: AccessReader
access: [[ "Wizard" ]]
- - type: CommunicationsConsole
- title: comms-console-announcement-title-wizard
- color: "#ff00ff"
- canShuttle: false
- global: true #announce to everyone they're about to fuck shit up
- announceSentBy: false
- sound: /Audio/Announcements/announce.ogg # DeltaV - changed from war.oog to announce.ogg
+ # Begin DeltaV Changes
+ # - type: CommunicationsConsole
+ # title: comms-console-announcement-title-wizard
+ # color: "#ff00ff"
+ # canShuttle: false
+ # global: true #announce to everyone they're about to fuck shit up
+ # announceSentBy: false
+ # sound: /Audio/Announcements/announce.ogg # DeltaV - changed from war.oog to announce.ogg
+ - type: DVCommunicationsConsole
+ announcementTitle: comms-console-announcement-title-wizard
+ announcementColor: "#ff00ff"
+ canAlertLevel: false
+ canCallShuttles: false
+ canConfigureScreens: false
+ canKeycardAuthenticationDevice: false
+ globalAnnouncements: true
+ # End DeltaV Changes
- type: Computer
board: WizardCommsComputerCircuitboard
- type: PointLight
@@ -809,11 +836,21 @@
state: generic_panel_open
- type: AccessReader
access: [[ "CentralCommand" ]]
- - type: CommunicationsConsole
- title: comms-console-announcement-title-centcom
- color: "#1d8bad"
- canShuttle: false
- global: true
+ # Begin DeltaV Changes
+ # - type: CommunicationsConsole
+ # title: comms-console-announcement-title-centcom
+ # color: "#1d8bad"
+ # canShuttle: false
+ # global: true
+ - type: DVCommunicationsConsole
+ announcementTitle: comms-console-announcement-title-centcom
+ announcementColor: "#1d8bad"
+ canAlertLevel: false
+ canCallShuttles: false
+ canConfigureScreens: false
+ canKeycardAuthenticationDevice: false
+ globalAnnouncements: true
+ # End DeltaV Changes
- type: Computer
board: CentcommCommsComputerCircuitboard
- type: PointLight
diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/screen.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/screen.yml
index c973201797a..8df73846043 100644
--- a/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/screen.yml
+++ b/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/screen.yml
@@ -1,4 +1,5 @@
- type: entity
+ categories: [ HideSpawnMenu ] # DeltaV - use the other one
parent: BaseWallmountMachine
id: Screen
name: screen
@@ -35,6 +36,7 @@
receiveFrequencyId: ShuttleTimer
- type: entity
+ categories: [ HideSpawnMenu ] # DeltaV - use the other one
id: ArrivalsShuttleTimer
parent: Screen
name: arrivals screen
diff --git a/Resources/Prototypes/_DV/Entities/Stations/base.yml b/Resources/Prototypes/_DV/Entities/Stations/base.yml
index 02515358d5e..52339ea664f 100644
--- a/Resources/Prototypes/_DV/Entities/Stations/base.yml
+++ b/Resources/Prototypes/_DV/Entities/Stations/base.yml
@@ -42,3 +42,12 @@
- type: StationPlanetSpawner
planet: Lavaland
gridPath: /Maps/_DV/Nonstations/lavaland_mining_base.yml
+
+- type: entity
+ abstract: true
+ id: BaseStationKeycardAuthenticationDevice
+ components:
+ - type: DVStationKeycardAuthenticationDevice
+ actionThresholds:
+ Mayday: 2
+ Scuttling: 3
diff --git a/Resources/Prototypes/_DV/Entities/Structures/Wallmounts/status_display.yml b/Resources/Prototypes/_DV/Entities/Structures/Wallmounts/status_display.yml
new file mode 100644
index 00000000000..e8d7bce4425
--- /dev/null
+++ b/Resources/Prototypes/_DV/Entities/Structures/Wallmounts/status_display.yml
@@ -0,0 +1,105 @@
+- type: entity
+ parent: BaseWallmountMachine
+ id: StatusDisplay
+ name: status display
+ description: Displays the current status of the station.
+ components:
+ - type: Rotatable
+ - type: DVTextVisuals
+ rows:
+ - text: ""
+ layer: enum.DVTextScreenVisualLayers.Line1
+ offset: 0.03125,0.15625 # 1/32, 5/32
+ - text: ""
+ layer: enum.DVTextScreenVisualLayers.Line2
+ offset: 0.03125,-0.0625 # 1/32, 0/32
+ - type: DVScreen
+ - type: Sprite
+ drawdepth: WallMountedItems
+ sprite: _DV/Structures/Wallmounts/screen.rsi
+ noRot: true
+ layers:
+ - state: frame
+ - shader: StencilClear
+ state: mask
+ - shader: StencilMask
+ state: mask
+ - map: ["enum.DVTextScreenVisualLayers.Line1"]
+ shader: StencilDraw
+ color: "#22ccff"
+ visible: false
+ - map: ["enum.DVTextScreenVisualLayers.Line2"]
+ shader: StencilDraw
+ color: "#22ccff"
+ visible: false
+ - map: ["alert_status"]
+ visible: false
+ state: status_display_green
+ - map: ["alert_border"]
+ visible: false
+ state: alert_border_green
+ - type: Appearance
+ - type: ApcPowerReceiver
+ - type: ExtensionCableReceiver
+ - type: GenericVisualizer
+ visuals:
+ enum.DVScreenVisuals.ShowAlertBorder:
+ alert_border:
+ True: { visible: true }
+ False: { visible: false }
+ enum.DVScreenVisuals.AlertLevel:
+ alert_border:
+ green: { state: alert_border_green }
+ blue: { state: alert_border_blue }
+ red: { state: alert_border_red }
+ white: { state: alert_border_white }
+ yellow: { state: alert_border_yellow }
+ violet: { state: alert_border_violet }
+ delta: { state: alert_border_delta }
+ epsilon: { state: alert_border_epsilon }
+ gamma: { state: alert_border_gamma }
+ octarine: { state: alert_border_octarine }
+ zeta: { state: alert_border_zeta }
+ alert_status:
+ green: { state: status_display_green }
+ blue: { state: status_display_blue }
+ red: { state: status_display_red }
+ white: { state: status_display_white }
+ yellow: { state: status_display_yellow }
+ violet: { state: status_display_violet }
+ delta: { state: status_display_delta }
+ epsilon: { state: status_display_epsilon }
+ gamma: { state: status_display_gamma }
+ octarine: { state: status_display_octarine }
+ zeta: { state: status_display_zeta }
+ enum.DVScreenVisuals.Content:
+ enum.DVTextScreenVisualLayers.Line1:
+ Text: { visible: true }
+ CurrentTime: { visible: true }
+ EstimatedTimeOfArrival: { visible: true }
+ AlertLevel: { visible: false }
+ enum.DVTextScreenVisualLayers.Line2:
+ Text: { visible: true }
+ CurrentTime: { visible: true }
+ EstimatedTimeOfArrival: { visible: true }
+ AlertLevel: { visible: false }
+ alert_status:
+ Text: { visible: false }
+ CurrentTime: { visible: false }
+ EstimatedTimeOfArrival: { visible: false }
+ AlertLevel: { visible: true }
+ - type: DeviceNetwork
+ deviceNetId: Wireless
+ receiveFrequencyId: ShuttleTimer
+
+- type: entity
+ parent: StatusDisplay
+ id: StatusDisplayShuttle
+ name: arrivals status display
+ description: Displays the current status of the arrivals shuttle.
+ components:
+ - type: DeviceNetwork
+ deviceNetId: Private
+ receiveFrequencyId: ArrivalsShuttleTimer
+ - type: DVScreen
+ content: EstimatedTimeOfArrival
diff --git a/Resources/Prototypes/_DV/shaders.yml b/Resources/Prototypes/_DV/shaders.yml
index 49300544085..55c5c054cc9 100644
--- a/Resources/Prototypes/_DV/shaders.yml
+++ b/Resources/Prototypes/_DV/shaders.yml
@@ -30,3 +30,8 @@
id: MonumentPulse
kind: source
path: "/Textures/_DV/Shaders/monument_pulse.swsl"
+
+- type: shader
+ id: Masked
+ kind: source
+ path: "/Textures/_DV/Shaders/mask.swsl"
diff --git a/Resources/Textures/_DV/Shaders/mask.swsl b/Resources/Textures/_DV/Shaders/mask.swsl
new file mode 100644
index 00000000000..21b2397c216
--- /dev/null
+++ b/Resources/Textures/_DV/Shaders/mask.swsl
@@ -0,0 +1,15 @@
+uniform sampler2D uMask;
+uniform highp vec4 uMaskUV;
+
+varying highp vec2 vMaskUV;
+
+void vertex()
+{
+ vMaskUV = (tCoord2 - uMaskUV.xy) / (uMaskUV.zw - uMaskUV.xy);
+ // vMaskUV = mix(uMaskUV.xy, uMaskUV.zw, tCoord2);
+}
+
+void fragment()
+{
+ COLOR.rgba = zTexture(UV) * texture2D(uMask, vMaskUV);
+}
diff --git a/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_blue.png b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_blue.png
new file mode 100644
index 00000000000..b86f5b9ce16
Binary files /dev/null and b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_blue.png differ
diff --git a/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_delta.png b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_delta.png
new file mode 100644
index 00000000000..72115d88a2d
Binary files /dev/null and b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_delta.png differ
diff --git a/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_epsilon.png b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_epsilon.png
new file mode 100644
index 00000000000..b8c822ab9dc
Binary files /dev/null and b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_epsilon.png differ
diff --git a/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_gamma.png b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_gamma.png
new file mode 100644
index 00000000000..a7e9b97e35b
Binary files /dev/null and b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_gamma.png differ
diff --git a/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_green.png b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_green.png
new file mode 100644
index 00000000000..754c9ed322f
Binary files /dev/null and b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_green.png differ
diff --git a/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_octarine.png b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_octarine.png
new file mode 100644
index 00000000000..1f187ee1cf7
Binary files /dev/null and b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_octarine.png differ
diff --git a/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_red.png b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_red.png
new file mode 100644
index 00000000000..bdd4d7d2b1f
Binary files /dev/null and b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_red.png differ
diff --git a/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_violet.png b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_violet.png
new file mode 100644
index 00000000000..9bda72e10cc
Binary files /dev/null and b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_violet.png differ
diff --git a/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_white.png b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_white.png
new file mode 100644
index 00000000000..66368734bb2
Binary files /dev/null and b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_white.png differ
diff --git a/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_yellow.png b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_yellow.png
new file mode 100644
index 00000000000..18ddbc3572b
Binary files /dev/null and b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_yellow.png differ
diff --git a/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_zeta.png b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_zeta.png
new file mode 100644
index 00000000000..bc34bb609ce
Binary files /dev/null and b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/alert_border_zeta.png differ
diff --git a/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/frame.png b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/frame.png
new file mode 100644
index 00000000000..15e38238af2
Binary files /dev/null and b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/frame.png differ
diff --git a/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/frame_broken.png b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/frame_broken.png
new file mode 100644
index 00000000000..91ed0751011
Binary files /dev/null and b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/frame_broken.png differ
diff --git a/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/mask.png b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/mask.png
new file mode 100644
index 00000000000..0b9a1028c46
Binary files /dev/null and b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/mask.png differ
diff --git a/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/meta.json b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/meta.json
new file mode 100644
index 00000000000..8027bf1e85e
--- /dev/null
+++ b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/meta.json
@@ -0,0 +1,269 @@
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Taken from Baystation12 at https://github.com/Baystation12/Baystation12/blob/926c09fe7fe69d647e530d03dce356737af82a3a/icons/misc/security_state.dmi and https://github.com/Baystation12/Baystation12/blob/926c09fe7fe69d647e530d03dce356737af82a3a/icons/obj/machines/status_display.dmi. white, octarine, zeta, yellow, gamma, epsilon made by sowelipililimute based on prior sprites",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "status_display_green",
+ "delays": [
+ [
+ 1.6,
+ 1.6
+ ]
+ ]
+ },
+ {
+ "name": "status_display_blue",
+ "delays": [
+ [
+ 0.5,
+ 0.5
+ ]
+ ]
+ },
+ {
+ "name": "status_display_red",
+ "delays": [
+ [
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2
+ ]
+ ]
+ },
+ {
+ "name": "status_display_delta",
+ "delays": [
+ [
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2
+ ]
+ ]
+ },
+ {
+ "name": "alert_border_green",
+ "delays": [
+ [
+ 1.6,
+ 1.6
+ ]
+ ]
+ },
+ {
+ "name": "alert_border_blue",
+ "delays": [
+ [
+ 0.5,
+ 0.5
+ ]
+ ]
+ },
+ {
+ "name": "alert_border_delta",
+ "delays": [
+ [
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2
+ ]
+ ]
+ },
+ {
+ "name": "alert_border_red",
+ "delays": [
+ [
+ 0.2,
+ 0.2
+ ]
+ ]
+ },
+ {
+ "name": "alert_border_violet",
+ "delays": [
+ [
+ 0.5,
+ 0.5
+ ]
+ ]
+ },
+ {
+ "name": "frame"
+ },
+ {
+ "name": "frame_broken"
+ },
+ {
+ "name": "mask"
+ },
+ {
+ "name": "radiation",
+ "delays": [
+ [
+ 0.5,
+ 0.5
+ ]
+ ]
+ },
+ {
+ "name": "status_display_violet",
+ "delays": [
+ [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ]
+ ]
+ },
+ {
+ "name": "status_display_white",
+ "delays": [
+ [
+ 0.5,
+ 0.5
+ ]
+ ]
+ },
+ {
+ "name": "alert_border_white",
+ "delays": [
+ [
+ 0.5,
+ 0.5
+ ]
+ ]
+ },
+ {
+ "name": "status_display_octarine",
+ "delays": [
+ [
+ 0.5,
+ 0.5
+ ]
+ ]
+ },
+ {
+ "name": "alert_border_octarine",
+ "delays": [
+ [
+ 0.5,
+ 0.5
+ ]
+ ]
+ },
+ {
+ "name": "status_display_zeta",
+ "delays": [
+ [
+ 0.5,
+ 0.5
+ ]
+ ]
+ },
+ {
+ "name": "alert_border_zeta",
+ "delays": [
+ [
+ 0.5,
+ 0.5
+ ]
+ ]
+ },
+ {
+ "name": "status_display_yellow",
+ "delays": [
+ [
+ 0.5,
+ 0.5
+ ]
+ ]
+ },
+ {
+ "name": "alert_border_yellow",
+ "delays": [
+ [
+ 0.5,
+ 0.5
+ ]
+ ]
+ },
+ {
+ "name": "status_display_gamma",
+ "delays": [
+ [
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2
+ ]
+ ]
+ },
+ {
+ "name": "alert_border_gamma",
+ "delays": [
+ [
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2
+ ]
+ ]
+ },
+ {
+ "name": "status_display_epsilon",
+ "delays": [
+ [
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2
+ ]
+ ]
+ },
+ {
+ "name": "alert_border_epsilon",
+ "delays": [
+ [
+ 0.2,
+ 0.2,
+ 0.2,
+ 0.2
+ ]
+ ]
+ }
+ ]
+}
diff --git a/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/radiation.png b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/radiation.png
new file mode 100644
index 00000000000..1d6dc2f8c78
Binary files /dev/null and b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/radiation.png differ
diff --git a/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_blue.png b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_blue.png
new file mode 100644
index 00000000000..f8c11e90132
Binary files /dev/null and b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_blue.png differ
diff --git a/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_delta.png b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_delta.png
new file mode 100644
index 00000000000..745ae5413a2
Binary files /dev/null and b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_delta.png differ
diff --git a/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_epsilon.png b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_epsilon.png
new file mode 100644
index 00000000000..561336c696b
Binary files /dev/null and b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_epsilon.png differ
diff --git a/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_gamma.png b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_gamma.png
new file mode 100644
index 00000000000..82c1e0d4a19
Binary files /dev/null and b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_gamma.png differ
diff --git a/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_green.png b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_green.png
new file mode 100644
index 00000000000..f0a8ece4aa9
Binary files /dev/null and b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_green.png differ
diff --git a/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_octarine.png b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_octarine.png
new file mode 100644
index 00000000000..0b65d150770
Binary files /dev/null and b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_octarine.png differ
diff --git a/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_red.png b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_red.png
new file mode 100644
index 00000000000..52dfe9f9abe
Binary files /dev/null and b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_red.png differ
diff --git a/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_violet.png b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_violet.png
new file mode 100644
index 00000000000..94cce503c00
Binary files /dev/null and b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_violet.png differ
diff --git a/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_white.png b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_white.png
new file mode 100644
index 00000000000..3b81a52c5ce
Binary files /dev/null and b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_white.png differ
diff --git a/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_yellow.png b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_yellow.png
new file mode 100644
index 00000000000..acc0ea08025
Binary files /dev/null and b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_yellow.png differ
diff --git a/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_zeta.png b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_zeta.png
new file mode 100644
index 00000000000..0ad4cc311e2
Binary files /dev/null and b/Resources/Textures/_DV/Structures/Wallmounts/screen.rsi/status_display_zeta.png differ