diff --git a/Content.Client/Icarus/IcarusTerminalBoundUserInterface.cs b/Content.Client/Icarus/IcarusTerminalBoundUserInterface.cs
new file mode 100644
index 0000000000..a18afef2eb
--- /dev/null
+++ b/Content.Client/Icarus/IcarusTerminalBoundUserInterface.cs
@@ -0,0 +1,58 @@
+using Content.Shared.Icarus;
+using JetBrains.Annotations;
+using Robust.Client.GameObjects;
+
+namespace Content.Client.Icarus;
+
+[UsedImplicitly]
+public sealed class IcarusTerminalBoundUserInterface : BoundUserInterface
+{
+ private IcarusTerminalWindow? _window;
+
+ public IcarusTerminalBoundUserInterface([NotNull] ClientUserInterfaceComponent owner, [NotNull] object uiKey) : base(owner, uiKey)
+ {
+ }
+
+ protected override void Open()
+ {
+ base.Open();
+ _window = new IcarusTerminalWindow();
+ _window.OnClose += Close;
+ _window.OpenCentered();
+
+ _window.FireButtonPressed += OnFireButtonPressed;
+ }
+
+ private void OnFireButtonPressed()
+ {
+ if (_window == null)
+ return;
+
+ SendMessage(new IcarusTerminalFireMessage());
+ }
+
+ protected override void UpdateState(BoundUserInterfaceState state)
+ {
+ base.UpdateState(state);
+
+ if (_window == null)
+ return;
+
+ if (state is not IcarusTerminalUiState cast)
+ return;
+
+ _window.UpdateState(cast);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+ if (!disposing)
+ return;
+
+ if (_window != null)
+ _window.OnClose -= Close;
+
+ _window?.Dispose();
+ }
+}
diff --git a/Content.Client/Icarus/IcarusTerminalWindow.xaml b/Content.Client/Icarus/IcarusTerminalWindow.xaml
new file mode 100644
index 0000000000..6787d48f42
--- /dev/null
+++ b/Content.Client/Icarus/IcarusTerminalWindow.xaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
diff --git a/Content.Client/Icarus/IcarusTerminalWindow.xaml.cs b/Content.Client/Icarus/IcarusTerminalWindow.xaml.cs
new file mode 100644
index 0000000000..1024d67496
--- /dev/null
+++ b/Content.Client/Icarus/IcarusTerminalWindow.xaml.cs
@@ -0,0 +1,32 @@
+using Content.Shared.Icarus;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.CustomControls;
+using Robust.Client.UserInterface.XAML;
+
+namespace Content.Client.Icarus;
+
+[GenerateTypedNameReferences]
+public sealed partial class IcarusTerminalWindow : DefaultWindow
+{
+ public event Action? FireButtonPressed;
+
+ public IcarusTerminalWindow()
+ {
+ RobustXamlLoader.Load(this);
+
+ FireButton.OnPressed += _ => FireButtonPressed?.Invoke();
+ }
+
+ public void UpdateState(IcarusTerminalUiState state)
+ {
+ switch (state.Status)
+ {
+ case IcarusTerminalStatus.FIRE_READY:
+ FireButton.Disabled = false;
+ break;
+ default:
+ FireButton.Disabled = true;
+ break;
+ }
+ }
+}
diff --git a/Content.Server/Icarus/IcarusTerminalSystem.cs b/Content.Server/Icarus/IcarusTerminalSystem.cs
index b247272dc5..bf8d7dfc98 100644
--- a/Content.Server/Icarus/IcarusTerminalSystem.cs
+++ b/Content.Server/Icarus/IcarusTerminalSystem.cs
@@ -6,6 +6,7 @@ using Content.Server.Station.Systems;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.Icarus;
using Content.Shared.Verbs;
+using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Map;
using Robust.Shared.Player;
@@ -26,6 +27,7 @@ public sealed class IcarusTerminalSystem : EntitySystem
[Dependency] private readonly GameTicker _gameTicker = default!;
[Dependency] private readonly StationSystem _stationSystem = default!;
[Dependency] private readonly IcarusBeamSystem _icarusSystem = default!;
+ [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
public override void Update(float frameTime)
{
@@ -52,17 +54,22 @@ public sealed class IcarusTerminalSystem : EntitySystem
SubscribeLocalEvent(OnInit);
SubscribeLocalEvent(OnItemSlotChanged);
SubscribeLocalEvent>(AddFireVerb);
+
+ // UI events
+ SubscribeLocalEvent(OnFireButtonPressed);
}
private void OnInit(EntityUid uid, IcarusTerminalComponent component, ComponentInit args)
{
component.RemainingTime = component.Timer;
UpdateStatus(component);
+ UpdateUserInterface(component);
}
private void OnItemSlotChanged(EntityUid uid, IcarusTerminalComponent component, ref ItemSlotChangedEvent args)
{
UpdateStatus(component);
+ UpdateUserInterface(component);
}
private void AddFireVerb(EntityUid uid, IcarusTerminalComponent component, GetVerbsEvent args)
@@ -75,6 +82,11 @@ public sealed class IcarusTerminalSystem : EntitySystem
args.Verbs.Add(verb);
}
+ private void OnFireButtonPressed(EntityUid uid, IcarusTerminalComponent component, IcarusTerminalFireMessage args)
+ {
+ Fire(component);
+ }
+
private void Fire(IcarusTerminalComponent component)
{
if (component.Status == IcarusTerminalStatus.FIRE_PREPARING)
@@ -83,8 +95,11 @@ public sealed class IcarusTerminalSystem : EntitySystem
component.RemainingTime = component.Timer;
component.Status = IcarusTerminalStatus.FIRE_PREPARING;
- _chatSystem.DispatchStationAnnouncement(component.Owner, Loc.GetString("goldeneye-icarus-announcement"),
- Loc.GetString("goldeneye-announce-sender"), false, Color.Red);
+ _chatSystem.DispatchStationAnnouncement(component.Owner,
+ Loc.GetString("goldeneye-icarus-announcement", ("seconds", component.Timer)),
+ Loc.GetString("goldeneye-announce-sender"),
+ false,
+ Color.Red);
SoundSystem.Play(component.AlertSound.GetSound(), Filter.Broadcast());
}
@@ -107,6 +122,15 @@ public sealed class IcarusTerminalSystem : EntitySystem
}
}
+ private void UpdateUserInterface(IcarusTerminalComponent component)
+ {
+ _userInterfaceSystem.TrySetUiState(component.Owner, IcarusTerminalUiKey.Key, new IcarusTerminalUiState(
+ component.Status,
+ (int) component.RemainingTime,
+ (int) component.CooldownTime)
+ );
+ }
+
private bool IsAccessGranted(EntityUid uid)
{
return Comp(uid).Slots.Values.All(v => v.HasItem);
@@ -143,6 +167,8 @@ public sealed class IcarusTerminalSystem : EntitySystem
component.Status = IcarusTerminalStatus.AWAIT_DISKS;
UpdateStatus(component);
}
+
+ UpdateUserInterface(component);
}
private void TickTimer(IcarusTerminalComponent component, float frameTime)
@@ -153,6 +179,8 @@ public sealed class IcarusTerminalSystem : EntitySystem
component.RemainingTime = 0;
ActivateBeam(component);
}
+
+ UpdateUserInterface(component);
}
private void ActivateBeam(IcarusTerminalComponent component)
diff --git a/Content.Shared/Icarus/SharedIcarus.cs b/Content.Shared/Icarus/SharedIcarus.cs
index 728598fc9f..9b906ee11c 100644
--- a/Content.Shared/Icarus/SharedIcarus.cs
+++ b/Content.Shared/Icarus/SharedIcarus.cs
@@ -1,4 +1,13 @@
-namespace Content.Shared.Icarus;
+using Content.Shared.Nuke;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Icarus;
+
+[Serializable, NetSerializable]
+public enum IcarusTerminalUiKey
+{
+ Key,
+}
public enum IcarusTerminalStatus : byte
{
@@ -7,3 +16,23 @@ public enum IcarusTerminalStatus : byte
FIRE_PREPARING,
COOLDOWN
}
+
+[Serializable, NetSerializable]
+public sealed class IcarusTerminalUiState : BoundUserInterfaceState
+{
+ public IcarusTerminalStatus Status { get; }
+ public int RemainingTime { get; }
+ public int CooldownTime { get; }
+
+ public IcarusTerminalUiState(IcarusTerminalStatus status, int remainingTime, int cooldownTime)
+ {
+ Status = status;
+ RemainingTime = remainingTime;
+ CooldownTime = cooldownTime;
+ }
+}
+
+[Serializable, NetSerializable]
+public sealed class IcarusTerminalFireMessage : BoundUserInterfaceMessage
+{
+}
diff --git a/Resources/Locale/en-US/corvax/assault-operatives/icarus-terminal-component.ftl b/Resources/Locale/en-US/corvax/assault-operatives/icarus-terminal-component.ftl
new file mode 100644
index 0000000000..71a2f46e66
--- /dev/null
+++ b/Resources/Locale/en-US/corvax/assault-operatives/icarus-terminal-component.ftl
@@ -0,0 +1,14 @@
+# Announce
+goldeneye-announce-sender = Icarus Defence Network
+goldeneye-authorized-announcement = UNAUTHORISED KEYCARD UPLOAD DETECTED. ALL KEYCARDS UPLOADED!
+goldeneye-icarus-announcement = /// ICARUS DEFENCE NETWORK BREACHED ///
+ Unauthorised Icarus Defence Network access detected.
+ ICARUS online.
+ Targeting system override detected...
+ New target: /NTSS14/
+ ICARUS firing protocols activated.
+ ETA to fire: { $seconds } seconds.
+
+# UI
+icarus-ui-window-title = Терминал Icarus
+icarus-ui-fire = Огонь
diff --git a/Resources/Locale/ru-RU/corvax/assault-operatives/icarus-terminal-component.ftl b/Resources/Locale/ru-RU/corvax/assault-operatives/icarus-terminal-component.ftl
index 5bd74e96b7..8067c8d0a5 100644
--- a/Resources/Locale/ru-RU/corvax/assault-operatives/icarus-terminal-component.ftl
+++ b/Resources/Locale/ru-RU/corvax/assault-operatives/icarus-terminal-component.ftl
@@ -1,10 +1,18 @@
-goldeneye-announce-sender = Icarus Defence Network
-goldeneye-authorized-announcement = UNAUTHORISED KEYCARD UPLOAD DETECTED. ALL KEYCARDS UPLOADED!
-goldeneye-icarus-announcement = /// GOLDENEYE DEFENCE NETWORK BREACHED ///
- Unauthorised GoldenEye Defence Network access detected.
- ICARUS online.
- Targeting system override detected...
- New target: /NTSS14/
- ICARUS firing protocols activated.
- ETA to fire: 20 seconds.
-goldeneye-ui-fire = Fire
+# Announce
+goldeneye-announce-sender = Оборонная сеть "Икарус"
+goldeneye-authorized-announcement = ОБНАРУЖЕНА НЕСАНКЦИОНИРОВАННАЯ ЗАГРУЗКА КЛЮЧ-КАРТ. ВСЕ КЛЮЧИ ЗАГРУЖЕНЫ!
+goldeneye-icarus-announcement = /// ВЗЛОМ ЗАЩИЩЕННОЙ СЕТИ "ИКАРУС" ///
+ Обнаружен несанкционированный доступ к оборонной сеть "Икарус"
+ ИКАРУС онлайн.
+ Обнаружено переопределение системы нацеливания...
+ Новая цель: /NTSS14/
+ Активированы протоколы стрельбы ИКАРУС.
+ РАСЧЕТНОЕ ВРЕМЯ выстера: { $seconds } { $seconds ->
+ [one] секунду
+ [few] секунды
+ *[other] секунд
+ }.
+
+# UI
+icarus-ui-window-title = Icarus Terminal
+icarus-ui-fire = Fire
diff --git a/Resources/Prototypes/Corvax/AssaultOperatives/terminal.yml b/Resources/Prototypes/Corvax/AssaultOperatives/terminal.yml
index 135469b077..af88af0d01 100644
--- a/Resources/Prototypes/Corvax/AssaultOperatives/terminal.yml
+++ b/Resources/Prototypes/Corvax/AssaultOperatives/terminal.yml
@@ -42,3 +42,9 @@
components:
- IcarusKey
- type: IcarusTerminal
+ - type: UserInterface
+ interfaces:
+ - key: enum.IcarusTerminalUiKey.Key
+ type: IcarusTerminalBoundUserInterface
+ - type: ActivatableUI
+ key: enum.IcarusTerminalUiKey.Key