diff --git a/Content.Client/Command/CommunicationsConsoleMenu.cs b/Content.Client/Command/CommunicationsConsoleMenu.cs index aede8c6e69..aa1f24cf6e 100644 --- a/Content.Client/Command/CommunicationsConsoleMenu.cs +++ b/Content.Client/Command/CommunicationsConsoleMenu.cs @@ -16,7 +16,7 @@ namespace Content.Client.Command private CommunicationsConsoleBoundUserInterface Owner { get; set; } private readonly CancellationTokenSource _timerCancelTokenSource = new(); - private readonly Button _emergencyShuttleButton; + public readonly Button EmergencyShuttleButton; private readonly RichTextLabel _countdownLabel; public CommunicationsConsoleMenu(CommunicationsConsoleBoundUserInterface owner) @@ -27,13 +27,14 @@ namespace Content.Client.Command Owner = owner; _countdownLabel = new RichTextLabel(){CustomMinimumSize = new Vector2(0, 200)}; - _emergencyShuttleButton = new Button(); - _emergencyShuttleButton.OnPressed += (e) => Owner.EmergencyShuttleButtonPressed(); + EmergencyShuttleButton = new Button(); + EmergencyShuttleButton.OnPressed += (_) => Owner.EmergencyShuttleButtonPressed(); + EmergencyShuttleButton.Disabled = !owner.CanCall; var vbox = new VBoxContainer() {SizeFlagsHorizontal = SizeFlags.FillExpand, SizeFlagsVertical = SizeFlags.FillExpand}; vbox.AddChild(_countdownLabel); - vbox.AddChild(_emergencyShuttleButton); + vbox.AddChild(EmergencyShuttleButton); var hbox = new HBoxContainer() {SizeFlagsHorizontal = SizeFlags.FillExpand, SizeFlagsVertical = SizeFlags.FillExpand}; hbox.AddChild(new Control(){CustomMinimumSize = new Vector2(100,0), SizeFlagsHorizontal = SizeFlags.FillExpand}); @@ -51,11 +52,11 @@ namespace Content.Client.Command if (!Owner.CountdownStarted) { _countdownLabel.SetMessage(""); - _emergencyShuttleButton.Text = Loc.GetString("Call emergency shuttle"); + EmergencyShuttleButton.Text = Loc.GetString("Call emergency shuttle"); return; } - _emergencyShuttleButton.Text = Loc.GetString("Recall emergency shuttle"); + EmergencyShuttleButton.Text = Loc.GetString("Recall emergency shuttle"); _countdownLabel.SetMessage($"Time remaining\n{Owner.Countdown.ToString()}s"); } diff --git a/Content.Client/GameObjects/Components/Command/CommunicationsConsoleBoundUserInterface.cs b/Content.Client/GameObjects/Components/Command/CommunicationsConsoleBoundUserInterface.cs index 09aba9276c..70320c19c1 100644 --- a/Content.Client/GameObjects/Components/Command/CommunicationsConsoleBoundUserInterface.cs +++ b/Content.Client/GameObjects/Components/Command/CommunicationsConsoleBoundUserInterface.cs @@ -1,4 +1,5 @@ -using System; +#nullable enable +using System; using Content.Client.Command; using Content.Shared.GameObjects.Components.Command; using Robust.Client.GameObjects; @@ -13,7 +14,9 @@ namespace Content.Client.GameObjects.Components.Command { [Dependency] private readonly IGameTiming _gameTiming = default!; - [ViewVariables] private CommunicationsConsoleMenu _menu; + [ViewVariables] private CommunicationsConsoleMenu? _menu; + + public bool CanCall { get; private set; } public bool CountdownStarted { get; private set; } @@ -30,15 +33,13 @@ namespace Content.Client.GameObjects.Components.Command base.Open(); _menu = new CommunicationsConsoleMenu(this); - _menu.OnClose += Close; - _menu.OpenCentered(); } public void EmergencyShuttleButtonPressed() { - if(CountdownStarted) + if (CountdownStarted) RecallShuttle(); else CallShuttle(); @@ -61,10 +62,15 @@ namespace Content.Client.GameObjects.Components.Command if (state is not CommunicationsConsoleInterfaceState commsState) return; + CanCall = commsState.CanCall; _expectedCountdownTime = commsState.ExpectedCountdownEnd; CountdownStarted = commsState.CountdownStarted; - _menu?.UpdateCountdown(); + if (_menu != null) + { + _menu.UpdateCountdown(); + _menu.EmergencyShuttleButton.Disabled = !CanCall; + } } protected override void Dispose(bool disposing) diff --git a/Content.Server/GameObjects/Components/Command/CommunicationsConsoleComponent.cs b/Content.Server/GameObjects/Components/Command/CommunicationsConsoleComponent.cs index 0bf27eb1fd..8561077130 100644 --- a/Content.Server/GameObjects/Components/Command/CommunicationsConsoleComponent.cs +++ b/Content.Server/GameObjects/Components/Command/CommunicationsConsoleComponent.cs @@ -7,7 +7,6 @@ using Content.Shared.Interfaces.GameObjects.Components; using Robust.Server.GameObjects; using Robust.Server.Player; using Robust.Shared.GameObjects; -using Robust.Shared.IoC; using Robust.Shared.ViewVariables; namespace Content.Server.GameObjects.Components.Command @@ -16,11 +15,9 @@ namespace Content.Server.GameObjects.Components.Command [ComponentReference(typeof(IActivate))] public class CommunicationsConsoleComponent : SharedCommunicationsConsoleComponent, IActivate { - [Dependency] private readonly IEntitySystemManager _entitySystemManager = default!; - private bool Powered => !Owner.TryGetComponent(out PowerReceiverComponent? receiver) || receiver.Powered; - private RoundEndSystem RoundEndSystem => _entitySystemManager.GetEntitySystem(); + private RoundEndSystem RoundEndSystem => EntitySystem.Get(); [ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(CommunicationsConsoleUiKey.Key); @@ -36,12 +33,24 @@ namespace Content.Server.GameObjects.Components.Command RoundEndSystem.OnRoundEndCountdownStarted += UpdateBoundInterface; RoundEndSystem.OnRoundEndCountdownCancelled += UpdateBoundInterface; RoundEndSystem.OnRoundEndCountdownFinished += UpdateBoundInterface; + RoundEndSystem.OnCallCooldownEnded += UpdateBoundInterface; + } + + protected override void Startup() + { + base.Startup(); + + UpdateBoundInterface(); } private void UpdateBoundInterface() { if (!Deleted) - UserInterface?.SetState(new CommunicationsConsoleInterfaceState(RoundEndSystem.ExpectedCountdownEnd)); + { + var system = RoundEndSystem; + + UserInterface?.SetState(new CommunicationsConsoleInterfaceState(system.CanCall(), system.ExpectedCountdownEnd)); + } } public override void OnRemove() diff --git a/Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs b/Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs index 78ee2f25f6..840db1f010 100644 --- a/Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/RoundEndSystem.cs @@ -21,10 +21,15 @@ namespace Content.Server.GameObjects.EntitySystems public const float RestartRoundTime = 20f; private CancellationTokenSource _roundEndCancellationTokenSource = new(); + private CancellationTokenSource _callCooldownEndedTokenSource = new(); public bool IsRoundEndCountdownStarted { get; private set; } public TimeSpan RoundEndCountdownTime { get; set; } = TimeSpan.FromMinutes(4); public TimeSpan? ExpectedCountdownEnd = null; + public TimeSpan LastCallTime { get; private set; } + + public TimeSpan CallCooldown { get; } = TimeSpan.FromSeconds(30); + public delegate void RoundEndCountdownStarted(); public event RoundEndCountdownStarted OnRoundEndCountdownStarted; @@ -34,12 +39,31 @@ namespace Content.Server.GameObjects.EntitySystems public delegate void RoundEndCountdownFinished(); public event RoundEndCountdownFinished OnRoundEndCountdownFinished; + public delegate void CallCooldownEnded(); + public event CallCooldownEnded OnCallCooldownEnded; + void IResettingEntitySystem.Reset() { IsRoundEndCountdownStarted = false; _roundEndCancellationTokenSource.Cancel(); _roundEndCancellationTokenSource = new CancellationTokenSource(); + _callCooldownEndedTokenSource.Cancel(); + _callCooldownEndedTokenSource = new CancellationTokenSource(); ExpectedCountdownEnd = null; + LastCallTime = default; + } + + public bool CanCall() + { + return _gameTiming.CurTime >= LastCallTime + CallCooldown; + } + + private void ActivateCooldown() + { + _callCooldownEndedTokenSource.Cancel(); + _callCooldownEndedTokenSource = new CancellationTokenSource(); + LastCallTime = _gameTiming.CurTime; + Timer.Spawn(CallCooldown, () => OnCallCooldownEnded?.Invoke(), _callCooldownEndedTokenSource.Token); } public void RequestRoundEnd() @@ -47,6 +71,11 @@ namespace Content.Server.GameObjects.EntitySystems if (IsRoundEndCountdownStarted) return; + if (!CanCall()) + { + return; + } + IsRoundEndCountdownStarted = true; _chatManager.DispatchStationAnnouncement(Loc.GetString("An emergency shuttle has been sent. ETA: {0} minutes.", RoundEndCountdownTime.Minutes), Loc.GetString("Station")); @@ -55,6 +84,9 @@ namespace Content.Server.GameObjects.EntitySystems ExpectedCountdownEnd = _gameTiming.CurTime + RoundEndCountdownTime; Timer.Spawn(RoundEndCountdownTime, EndRound, _roundEndCancellationTokenSource.Token); + + ActivateCooldown(); + OnRoundEndCountdownStarted?.Invoke(); } @@ -63,6 +95,11 @@ namespace Content.Server.GameObjects.EntitySystems if (!IsRoundEndCountdownStarted) return; + if (!CanCall()) + { + return; + } + IsRoundEndCountdownStarted = false; _chatManager.DispatchStationAnnouncement(Loc.GetString("The emergency shuttle has been recalled."), Loc.GetString("Station")); @@ -74,6 +111,8 @@ namespace Content.Server.GameObjects.EntitySystems ExpectedCountdownEnd = null; + ActivateCooldown(); + OnRoundEndCountdownCancelled?.Invoke(); } diff --git a/Content.Shared/GameObjects/Components/Command/SharedCommunicationsConsoleComponent.cs b/Content.Shared/GameObjects/Components/Command/SharedCommunicationsConsoleComponent.cs index 61f9f741e8..a8d267a02b 100644 --- a/Content.Shared/GameObjects/Components/Command/SharedCommunicationsConsoleComponent.cs +++ b/Content.Shared/GameObjects/Components/Command/SharedCommunicationsConsoleComponent.cs @@ -7,37 +7,31 @@ namespace Content.Shared.GameObjects.Components.Command public class SharedCommunicationsConsoleComponent : Component { public override string Name => "CommunicationsConsole"; - } [Serializable, NetSerializable] public class CommunicationsConsoleInterfaceState : BoundUserInterfaceState { + public readonly bool CanCall; public readonly TimeSpan? ExpectedCountdownEnd; public readonly bool CountdownStarted; - public CommunicationsConsoleInterfaceState(TimeSpan? expectedCountdownEnd = null) + public CommunicationsConsoleInterfaceState(bool canCall, TimeSpan? expectedCountdownEnd = null) { + CanCall = canCall; ExpectedCountdownEnd = expectedCountdownEnd; CountdownStarted = expectedCountdownEnd != null; - } } [Serializable, NetSerializable] public class CommunicationsConsoleCallEmergencyShuttleMessage : BoundUserInterfaceMessage { - public CommunicationsConsoleCallEmergencyShuttleMessage() - { - } } [Serializable, NetSerializable] public class CommunicationsConsoleRecallEmergencyShuttleMessage : BoundUserInterfaceMessage { - public CommunicationsConsoleRecallEmergencyShuttleMessage() - { - } } [Serializable, NetSerializable]