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.Humanoid; 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 they only have the ability to announce, there's no point in having a home button if (console.Comp.CanAnnounce && !(console.Comp.CanAlertLevel || console.Comp.CanCallShuttles || console.Comp.CanConfigureScreens || console.Comp.CanKeycardAuthenticationDevice)) { SetScreen(AnnouncementScreen); HomeButton.Visible = false; } // Station StationHeaderLabel.Visible = console.Comp.CanAnnounce || console.Comp.CanConfigureScreens || console.Comp.CanAlertLevel; AnnouncementTab.Visible = console.Comp.CanAnnounce; ScreenTab.Visible = console.Comp.CanConfigureScreens; AlertLevelTab.Visible = console.Comp.CanAlertLevel; // Shuttles TransitHeaderLabel.Visible = console.Comp.CanCallShuttles; // Should be only hidden if all buttons under are also hidden. ShuttlesTab.Visible = console.Comp.CanCallShuttles; // Emergency EmergencyHeaderLabel.Visible = console.Comp.CanKeycardAuthenticationDevice; // Should be only hidden if all buttons under are also hidden. 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) && _entity.HasComponent(user) // Can't be AI or a borg && 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; if (CallZeta.IsConfirming || RequestCodes.IsConfirming) return; // Kinda messy but it works if (station.Comp.SwipesStartedAt is { } firstSwipeTime) { var countdown = firstSwipeTime + station.Comp.SwipeWindow; var timeRemaining = countdown.Subtract(_timing.CurTime); if (timeRemaining > TimeSpan.Zero && station.Comp.SwipingFor is DVStationKeycardAction.Mayday) CallZeta.Text = $"{timeRemaining:ss\\.fff}"; if (timeRemaining > TimeSpan.Zero && station.Comp.SwipingFor is DVStationKeycardAction.Scuttling) RequestCodes.Text = $"{timeRemaining:ss\\.fff}"; } else { CallZeta.Text = Loc.GetString("comms-console-menu-declare-mayday.button"); RequestCodes.Text = Loc.GetString("comms-console-menu-request-codes.button"); } } 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)); } }