diff --git a/Content.Client/Alerts/ClientAlertsComponent.cs b/Content.Client/Alerts/ClientAlertsComponent.cs
deleted file mode 100644
index 385135589c..0000000000
--- a/Content.Client/Alerts/ClientAlertsComponent.cs
+++ /dev/null
@@ -1,226 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-using Content.Client.Alerts.UI;
-using Content.Shared.Alert;
-using Robust.Client.GameObjects;
-using Robust.Client.Player;
-using Robust.Client.UserInterface;
-using Robust.Shared.GameObjects;
-using Robust.Shared.Input;
-using Robust.Shared.IoC;
-using Robust.Shared.Log;
-using Robust.Shared.Prototypes;
-using Robust.Shared.ViewVariables;
-using static Robust.Client.UserInterface.Controls.BaseButton;
-
-namespace Content.Client.Alerts
-{
- ///
- [RegisterComponent]
- [ComponentReference(typeof(SharedAlertsComponent))]
- public sealed class ClientAlertsComponent : SharedAlertsComponent
- {
- [Dependency] private readonly IPlayerManager _playerManager = default!;
-
- private AlertsUI? _ui;
- private AlertOrderPrototype? _alertOrder;
-
- [ViewVariables]
- private readonly Dictionary _alertControls
- = new();
-
- ///
- /// Allows calculating if we need to act due to this component being controlled by the current mob
- ///
- [ViewVariables]
- private bool CurrentlyControlled => _playerManager.LocalPlayer != null && _playerManager.LocalPlayer.ControlledEntity == Owner;
-
- protected override void Shutdown()
- {
- base.Shutdown();
- PlayerDetached();
- }
-
- public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
- {
- base.HandleComponentState(curState, nextState);
-
- if (curState is not AlertsComponentState)
- {
- return;
- }
-
- UpdateAlertsControls();
- }
-
- public void PlayerAttached()
- {
- if (!CurrentlyControlled || _ui != null)
- {
- return;
- }
-
- _alertOrder = IoCManager.Resolve().EnumeratePrototypes().FirstOrDefault();
- if (_alertOrder == null)
- {
- Logger.ErrorS("alert", "no alertOrder prototype found, alerts will be in random order");
- }
-
- _ui = new AlertsUI();
- IoCManager.Resolve().StateRoot.AddChild(_ui);
-
- UpdateAlertsControls();
- }
-
- public void PlayerDetached()
- {
- foreach (var alertControl in _alertControls.Values)
- {
- alertControl.OnPressed -= AlertControlOnPressed;
- }
-
- if (_ui != null)
- {
- IoCManager.Resolve().StateRoot.RemoveChild(_ui);
- _ui = null;
- }
- _alertControls.Clear();
- }
-
- ///
- /// Updates the displayed alerts based on current state of Alerts, performing
- /// a diff to ensure we only change what's changed (this avoids active tooltips disappearing any
- /// time state changes)
- ///
- private void UpdateAlertsControls()
- {
- if (!CurrentlyControlled || _ui == null)
- {
- return;
- }
-
- // remove any controls with keys no longer present
- var toRemove = new List();
- foreach (var existingKey in _alertControls.Keys)
- {
- if (!IsShowingAlert(existingKey))
- {
- toRemove.Add(existingKey);
- }
- }
- foreach (var alertKeyToRemove in toRemove)
- {
- _alertControls.Remove(alertKeyToRemove, out var control);
- if (control == null) return;
- _ui.AlertContainer.Children.Remove(control);
- }
-
- // now we know that alertControls contains alerts that should still exist but
- // may need to updated,
- // also there may be some new alerts we need to show.
- // further, we need to ensure they are ordered w.r.t their configured order
- foreach (var (alertKey, alertState) in EnumerateAlertStates())
- {
- if (!alertKey.AlertType.HasValue)
- {
- Logger.WarningS("alert", "found alertkey without alerttype," +
- " alert keys should never be stored without an alerttype set: {0}", alertKey);
- continue;
- }
- var alertType = alertKey.AlertType.Value;
- if (!AlertManager.TryGet(alertType, out var newAlert))
- {
- Logger.ErrorS("alert", "Unrecognized alertType {0}", alertType);
- continue;
- }
-
- if (_alertControls.TryGetValue(newAlert.AlertKey, out var existingAlertControl) &&
- existingAlertControl.Alert.AlertType == newAlert.AlertType)
- {
- // key is the same, simply update the existing control severity / cooldown
- existingAlertControl.SetSeverity(alertState.Severity);
- existingAlertControl.Cooldown = alertState.Cooldown;
- }
- else
- {
- if (existingAlertControl != null)
- {
- _ui.AlertContainer.Children.Remove(existingAlertControl);
- }
-
- // this is a new alert + alert key or just a different alert with the same
- // key, create the control and add it in the appropriate order
- var newAlertControl = CreateAlertControl(newAlert, alertState);
- if (_alertOrder != null)
- {
- var added = false;
- foreach (var alertControl in _ui.AlertContainer.Children)
- {
- if (_alertOrder.Compare(newAlert, ((AlertControl) alertControl).Alert) < 0)
- {
- var idx = alertControl.GetPositionInParent();
- _ui.AlertContainer.Children.Add(newAlertControl);
- newAlertControl.SetPositionInParent(idx);
- added = true;
- break;
- }
- }
-
- if (!added)
- {
- _ui.AlertContainer.Children.Add(newAlertControl);
- }
- }
- else
- {
- _ui.AlertContainer.Children.Add(newAlertControl);
- }
-
- _alertControls[newAlert.AlertKey] = newAlertControl;
- }
- }
- }
-
- private AlertControl CreateAlertControl(AlertPrototype alert, AlertState alertState)
- {
- var alertControl = new AlertControl(alert, alertState.Severity)
- {
- Cooldown = alertState.Cooldown
- };
- alertControl.OnPressed += AlertControlOnPressed;
- return alertControl;
- }
-
- private void AlertControlOnPressed(ButtonEventArgs args)
- {
- if (args.Button is not AlertControl control)
- {
- return;
- }
-
- AlertPressed(args, control);
- }
-
- private void AlertPressed(ButtonEventArgs args, AlertControl alert)
- {
- if (args.Event.Function != EngineKeyFunctions.UIClick)
- {
- return;
- }
-
-#pragma warning disable 618
- SendNetworkMessage(new ClickAlertMessage(alert.Alert.AlertType));
-#pragma warning restore 618
- }
-
- protected override void AfterShowAlert()
- {
- UpdateAlertsControls();
- }
-
- protected override void AfterClearAlert()
- {
- UpdateAlertsControls();
- }
- }
-}
diff --git a/Content.Client/Alerts/ClientAlertsSystem.cs b/Content.Client/Alerts/ClientAlertsSystem.cs
index 19d266fbfe..0c189963e3 100644
--- a/Content.Client/Alerts/ClientAlertsSystem.cs
+++ b/Content.Client/Alerts/ClientAlertsSystem.cs
@@ -1,16 +1,115 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Content.Shared.Alert;
+using JetBrains.Annotations;
using Robust.Client.GameObjects;
+using Robust.Client.Player;
using Robust.Shared.GameObjects;
+using Robust.Shared.GameStates;
+using Robust.Shared.IoC;
+using Robust.Shared.Log;
+using Robust.Shared.Prototypes;
-namespace Content.Client.Alerts
+namespace Content.Client.Alerts;
+
+[UsedImplicitly]
+internal class ClientAlertsSystem : AlertsSystem
{
- internal class ClientAlertsSystem : EntitySystem
- {
- public override void Initialize()
- {
- base.Initialize();
+ public AlertOrderPrototype? AlertOrder { get; set; }
- SubscribeLocalEvent((_, component, _) => component.PlayerAttached());
- SubscribeLocalEvent((_, component, _) => component.PlayerDetached());
+ [Dependency] private readonly IPlayerManager _playerManager = default!;
+ [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+
+ public event EventHandler? ClearAlerts;
+ public event EventHandler>? SyncAlerts;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent((_, component, _) => PlayerAttached(component));
+ SubscribeLocalEvent((_, _, _) => PlayerDetached());
+
+ SubscribeLocalEvent(ClientAlertsHandleState);
+ }
+
+ protected override void LoadPrototypes()
+ {
+ base.LoadPrototypes();
+
+ AlertOrder = _prototypeManager.EnumeratePrototypes().FirstOrDefault();
+ if (AlertOrder == null)
+ Logger.ErrorS("alert", "no alertOrder prototype found, alerts will be in random order");
+ }
+
+ public IReadOnlyDictionary? ActiveAlerts
+ {
+ get
+ {
+ var ent = _playerManager.LocalPlayer?.ControlledEntity;
+ return ent is not null
+ ? GetActiveAlerts(ent.Value)
+ : null;
}
}
+
+ protected override void AfterShowAlert(AlertsComponent alertsComponent)
+ {
+ if (!CurControlled(alertsComponent.Owner, _playerManager))
+ return;
+
+ SyncAlerts?.Invoke(this, alertsComponent.Alerts);
+ }
+
+ protected override void AfterClearAlert(AlertsComponent alertsComponent)
+ {
+ if (!CurControlled(alertsComponent.Owner, _playerManager))
+ return;
+
+ SyncAlerts?.Invoke(this, alertsComponent.Alerts);
+ }
+
+ private void ClientAlertsHandleState(EntityUid uid, AlertsComponent component, ref ComponentHandleState args)
+ {
+ var componentAlerts = (args.Current as AlertsComponentState)?.Alerts;
+ if (componentAlerts == null) return;
+
+ //TODO: Do we really want to send alerts for non-attached entity?
+ component.Alerts = componentAlerts;
+ if (!CurControlled(component.Owner, _playerManager)) return;
+
+ SyncAlerts?.Invoke(this, componentAlerts);
+ }
+
+ private void PlayerAttached(AlertsComponent clientAlertsComponent)
+ {
+ if (!CurControlled(clientAlertsComponent.Owner, _playerManager)) return;
+ SyncAlerts?.Invoke(this, clientAlertsComponent.Alerts);
+ }
+
+ protected override void HandleComponentShutdown(EntityUid uid)
+ {
+ base.HandleComponentShutdown(uid);
+
+ PlayerDetached();
+ }
+
+ private void PlayerDetached()
+ {
+ ClearAlerts?.Invoke(this, EventArgs.Empty);
+ }
+
+ public void AlertClicked(AlertType alertType)
+ {
+ RaiseNetworkEvent(new ClickAlertEvent(alertType));
+ }
+
+ ///
+ /// Allows calculating if we need to act due to this component being controlled by the current mob
+ ///
+ private static bool CurControlled(EntityUid entity, IPlayerManager playerManager)
+ {
+ return playerManager.LocalPlayer != null && playerManager.LocalPlayer.ControlledEntity == entity;
+ }
}
diff --git a/Content.Client/Alerts/UI/AlertsUI.xaml.cs b/Content.Client/Alerts/UI/AlertsUI.xaml.cs
index 0bbbe9d08c..42d31cd463 100644
--- a/Content.Client/Alerts/UI/AlertsUI.xaml.cs
+++ b/Content.Client/Alerts/UI/AlertsUI.xaml.cs
@@ -1,79 +1,344 @@
-using Content.Client.Chat.Managers;
+using System;
+using System.Collections.Generic;
+using Content.Client.Chat.Managers;
using Content.Client.Chat.UI;
+using Content.Shared.Alert;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Input;
using Robust.Shared.IoC;
+using Robust.Shared.Log;
-namespace Content.Client.Alerts.UI
+namespace Content.Client.Alerts.UI;
+
+public class AlertsFramePresenter : IDisposable
{
- ///
- /// The status effects display on the right side of the screen.
- ///
- [GenerateTypedNameReferences]
- public sealed partial class AlertsUI : Control
+ [Dependency] private readonly IEntitySystemManager _systemManager = default!;
+ [Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
+ [Dependency] private readonly IChatManager _chatManager = default!;
+
+ private IAlertsFrameView _alertsFrame;
+ private ClientAlertsSystem? _alertsSystem;
+
+ public AlertsFramePresenter()
{
- [Dependency] private readonly IChatManager _chatManager = default!;
+ // This is a lot easier than a factory
+ IoCManager.InjectDependencies(this);
- public const float ChatSeparation = 38f;
+ _alertsFrame = new AlertsUI(_chatManager);
+ _userInterfaceManager.StateRoot.AddChild((AlertsUI) _alertsFrame);
- public AlertsUI()
+ // This is required so that if we load after the system is initialized, we can bind to it immediately
+ if (_systemManager.TryGetEntitySystem(out var alertsSystem))
+ SystemBindingChanged(alertsSystem);
+
+ _systemManager.SystemLoaded += OnSystemLoaded;
+ _systemManager.SystemUnloaded += OnSystemUnloaded;
+
+ _alertsFrame.AlertPressed += OnAlertPressed;
+
+ // initially populate the frame if system is available
+ var alerts = alertsSystem?.ActiveAlerts;
+ if (alerts != null)
{
- IoCManager.InjectDependencies(this);
- RobustXamlLoader.Load(this);
-
- LayoutContainer.SetGrowHorizontal(this, LayoutContainer.GrowDirection.Begin);
- LayoutContainer.SetGrowVertical(this, LayoutContainer.GrowDirection.End);
- LayoutContainer.SetAnchorTop(this, 0f);
- LayoutContainer.SetAnchorRight(this, 1f);
- LayoutContainer.SetAnchorBottom(this, 1f);
- LayoutContainer.SetMarginBottom(this, -180);
- LayoutContainer.SetMarginTop(this, 250);
- LayoutContainer.SetMarginRight(this, -10);
+ SystemOnSyncAlerts(alertsSystem, alerts);
}
+ }
- protected override void EnteredTree()
+ ///
+ public void Dispose()
+ {
+ _userInterfaceManager.StateRoot.RemoveChild((AlertsUI) _alertsFrame);
+ _alertsFrame.Dispose();
+ _alertsFrame = null!;
+
+ SystemBindingChanged(null);
+ _systemManager.SystemLoaded -= OnSystemLoaded;
+ _systemManager.SystemUnloaded -= OnSystemUnloaded;
+ }
+
+ private void OnAlertPressed(object? sender, AlertType e)
+ {
+ _alertsSystem?.AlertClicked(e);
+ }
+
+ private void SystemOnClearAlerts(object? sender, EventArgs e)
+ {
+ _alertsFrame.ClearAllControls();
+ }
+
+ private void SystemOnSyncAlerts(object? sender, IReadOnlyDictionary e)
+ {
+ if (sender is ClientAlertsSystem system)
+ _alertsFrame.SyncControls(system, system.AlertOrder, e);
+ }
+
+ //TODO: This system binding boilerplate seems to be duplicated between every presenter
+ // prob want to pull it out into a generic object with callbacks for Onbind/OnUnbind
+ #region System Binding
+
+ private void OnSystemLoaded(object? sender, SystemChangedArgs args)
+ {
+ if (args.System is ClientAlertsSystem system) SystemBindingChanged(system);
+ }
+
+ private void OnSystemUnloaded(object? sender, SystemChangedArgs args)
+ {
+ if (args.System is ClientAlertsSystem) SystemBindingChanged(null);
+ }
+
+ private void SystemBindingChanged(ClientAlertsSystem? newSystem)
+ {
+ if (newSystem is null)
{
- base.EnteredTree();
- _chatManager.OnChatBoxResized += OnChatResized;
- OnChatResized(new ChatResizedEventArgs(HudChatBox.InitialChatBottom));
+ if (_alertsSystem is null)
+ return;
+
+ UnbindFromSystem();
}
-
- protected override void ExitedTree()
+ else
{
- base.ExitedTree();
- _chatManager.OnChatBoxResized -= OnChatResized;
- }
-
- private void OnChatResized(ChatResizedEventArgs chatResizedEventArgs)
- {
- // resize us to fit just below the chatbox
- if (_chatManager.CurrentChatBox != null)
+ if (_alertsSystem is null)
{
- LayoutContainer.SetMarginTop(this, chatResizedEventArgs.NewBottom + ChatSeparation);
+ BindToSystem(newSystem);
+ return;
+ }
+
+ UnbindFromSystem();
+ BindToSystem(newSystem);
+ }
+ }
+
+ private void BindToSystem(ClientAlertsSystem system)
+ {
+ _alertsSystem = system;
+ system.SyncAlerts += SystemOnSyncAlerts;
+ system.ClearAlerts += SystemOnClearAlerts;
+ }
+
+ private void UnbindFromSystem()
+ {
+ var system = _alertsSystem;
+
+ if (system is null)
+ throw new InvalidOperationException();
+
+ system.SyncAlerts -= SystemOnSyncAlerts;
+ system.ClearAlerts -= SystemOnClearAlerts;
+ }
+
+ #endregion
+}
+
+///
+/// This is the frame of vertical set of alerts that show up on the HUD.
+///
+public interface IAlertsFrameView : IDisposable
+{
+ event EventHandler? AlertPressed;
+
+ void SyncControls(AlertsSystem alertsSystem, AlertOrderPrototype? alertOrderPrototype,
+ IReadOnlyDictionary alertStates);
+ void ClearAllControls();
+}
+
+///
+/// The status effects display on the right side of the screen.
+///
+[GenerateTypedNameReferences]
+public sealed partial class AlertsUI : Control, IAlertsFrameView
+{
+ // also known as Control.Children?
+ private readonly Dictionary _alertControls = new();
+
+ public AlertsUI(IChatManager chatManager)
+ {
+ _chatManager = chatManager;
+ RobustXamlLoader.Load(this);
+
+ LayoutContainer.SetGrowHorizontal(this, LayoutContainer.GrowDirection.Begin);
+ LayoutContainer.SetGrowVertical(this, LayoutContainer.GrowDirection.End);
+ LayoutContainer.SetAnchorTop(this, 0f);
+ LayoutContainer.SetAnchorRight(this, 1f);
+ LayoutContainer.SetAnchorBottom(this, 1f);
+ LayoutContainer.SetMarginBottom(this, -180);
+ LayoutContainer.SetMarginTop(this, 250);
+ LayoutContainer.SetMarginRight(this, -10);
+ }
+
+ public void SyncControls(AlertsSystem alertsSystem, AlertOrderPrototype? alertOrderPrototype,
+ IReadOnlyDictionary alertStates)
+ {
+ // remove any controls with keys no longer present
+ if (SyncRemoveControls(alertStates)) return;
+
+ // now we know that alertControls contains alerts that should still exist but
+ // may need to updated,
+ // also there may be some new alerts we need to show.
+ // further, we need to ensure they are ordered w.r.t their configured order
+ SyncUpdateControls(alertsSystem, alertOrderPrototype, alertStates);
+ }
+
+ public void ClearAllControls()
+ {
+ foreach (var alertControl in _alertControls.Values)
+ {
+ alertControl.OnPressed -= AlertControlPressed;
+ alertControl.Dispose();
+ }
+
+ _alertControls.Clear();
+ }
+
+ public event EventHandler? AlertPressed;
+
+ //TODO: This control caring about it's layout relative to other controls in the tree is terrible
+ // the presenters or gamescreen should be dealing with this
+ // probably want to tackle this after chatbox gets MVP'd
+ #region Spaghetti
+
+ public const float ChatSeparation = 38f;
+ private readonly IChatManager _chatManager;
+
+ protected override void EnteredTree()
+ {
+ base.EnteredTree();
+ _chatManager.OnChatBoxResized += OnChatResized;
+ OnChatResized(new ChatResizedEventArgs(HudChatBox.InitialChatBottom));
+ }
+
+ protected override void ExitedTree()
+ {
+ base.ExitedTree();
+ _chatManager.OnChatBoxResized -= OnChatResized;
+ }
+
+ private void OnChatResized(ChatResizedEventArgs chatResizedEventArgs)
+ {
+ // resize us to fit just below the chat box
+ if (_chatManager.CurrentChatBox != null)
+ LayoutContainer.SetMarginTop(this, chatResizedEventArgs.NewBottom + ChatSeparation);
+ else
+ LayoutContainer.SetMarginTop(this, 250);
+ }
+
+ #endregion
+
+ // This makes no sense but I'm leaving it in place in case I break anything by removing it.
+ protected override void Resized()
+ {
+ // TODO: Can rework this once https://github.com/space-wizards/RobustToolbox/issues/1392 is done,
+ // this is here because there isn't currently a good way to allow the grid to adjust its height based
+ // on constraints, otherwise we would use anchors to lay it out
+ base.Resized();
+ AlertContainer.MaxGridHeight = Height;
+ }
+
+ protected override void UIScaleChanged()
+ {
+ AlertContainer.MaxGridHeight = Height;
+ base.UIScaleChanged();
+ }
+
+ private bool SyncRemoveControls(IReadOnlyDictionary alertStates)
+ {
+ var toRemove = new List();
+ foreach (var existingKey in _alertControls.Keys)
+ {
+ if (!alertStates.ContainsKey(existingKey)) toRemove.Add(existingKey);
+ }
+
+ foreach (var alertKeyToRemove in toRemove)
+ {
+ _alertControls.Remove(alertKeyToRemove, out var control);
+ if (control == null) return true;
+ AlertContainer.Children.Remove(control);
+ }
+
+ return false;
+ }
+
+ private void SyncUpdateControls(AlertsSystem alertsSystem, AlertOrderPrototype? alertOrderPrototype,
+ IReadOnlyDictionary alertStates)
+ {
+ foreach (var (alertKey, alertState) in alertStates)
+ {
+ if (!alertKey.AlertType.HasValue)
+ {
+ Logger.WarningS("alert", "found alertkey without alerttype," +
+ " alert keys should never be stored without an alerttype set: {0}", alertKey);
+ continue;
+ }
+
+ var alertType = alertKey.AlertType.Value;
+ if (!alertsSystem.TryGet(alertType, out var newAlert))
+ {
+ Logger.ErrorS("alert", "Unrecognized alertType {0}", alertType);
+ continue;
+ }
+
+ if (_alertControls.TryGetValue(newAlert.AlertKey, out var existingAlertControl) &&
+ existingAlertControl.Alert.AlertType == newAlert.AlertType)
+ {
+ // key is the same, simply update the existing control severity / cooldown
+ existingAlertControl.SetSeverity(alertState.Severity);
+ existingAlertControl.Cooldown = alertState.Cooldown;
}
else
{
- LayoutContainer.SetMarginTop(this, 250);
+ if (existingAlertControl != null) AlertContainer.Children.Remove(existingAlertControl);
+
+ // this is a new alert + alert key or just a different alert with the same
+ // key, create the control and add it in the appropriate order
+ var newAlertControl = CreateAlertControl(newAlert, alertState);
+
+ //TODO: Can the presenter sort the states before giving it to us?
+ if (alertOrderPrototype != null)
+ {
+ var added = false;
+ foreach (var alertControl in AlertContainer.Children)
+ {
+ if (alertOrderPrototype.Compare(newAlert, ((AlertControl) alertControl).Alert) >= 0)
+ continue;
+
+ var idx = alertControl.GetPositionInParent();
+ AlertContainer.Children.Add(newAlertControl);
+ newAlertControl.SetPositionInParent(idx);
+ added = true;
+ break;
+ }
+
+ if (!added) AlertContainer.Children.Add(newAlertControl);
+ }
+ else
+ AlertContainer.Children.Add(newAlertControl);
+
+ _alertControls[newAlert.AlertKey] = newAlertControl;
}
}
+ }
- // This makes no sense but I'm leaving it in place in case I break anything by removing it.
-
- protected override void Resized()
+ private AlertControl CreateAlertControl(AlertPrototype alert, AlertState alertState)
+ {
+ var alertControl = new AlertControl(alert, alertState.Severity)
{
- // TODO: Can rework this once https://github.com/space-wizards/RobustToolbox/issues/1392 is done,
- // this is here because there isn't currently a good way to allow the grid to adjust its height based
- // on constraints, otherwise we would use anchors to lay it out
- base.Resized();
- AlertContainer.MaxGridHeight = Height;
- }
+ Cooldown = alertState.Cooldown
+ };
+ alertControl.OnPressed += AlertControlPressed;
+ return alertControl;
+ }
- protected override void UIScaleChanged()
- {
- AlertContainer.MaxGridHeight = Height;
- base.UIScaleChanged();
- }
+ private void AlertControlPressed(BaseButton.ButtonEventArgs args)
+ {
+ if (args.Button is not AlertControl control)
+ return;
+
+ if (args.Event.Function != EngineKeyFunctions.UIClick)
+ return;
+
+ AlertPressed?.Invoke(this, control.Alert.AlertType);
}
}
diff --git a/Content.Client/Entry/EntryPoint.cs b/Content.Client/Entry/EntryPoint.cs
index fee72f5096..6d27ad6eff 100644
--- a/Content.Client/Entry/EntryPoint.cs
+++ b/Content.Client/Entry/EntryPoint.cs
@@ -190,7 +190,6 @@ namespace Content.Client.Entry
IoCManager.Resolve().Initialize();
IoCManager.Resolve().Initialize();
IoCManager.Resolve().Initialize();
- IoCManager.Resolve().Initialize();
IoCManager.Resolve().Initialize();
IoCManager.Resolve().Initialize();
IoCManager.Resolve().Initialize();
diff --git a/Content.Client/IoC/ClientContentIoC.cs b/Content.Client/IoC/ClientContentIoC.cs
index 961d9dd588..d0d0e0da9a 100644
--- a/Content.Client/IoC/ClientContentIoC.cs
+++ b/Content.Client/IoC/ClientContentIoC.cs
@@ -19,7 +19,6 @@ using Content.Client.Viewport;
using Content.Client.Voting;
using Content.Shared.Actions;
using Content.Shared.Administration;
-using Content.Shared.Alert;
using Content.Shared.Module;
using Robust.Shared.IoC;
@@ -41,7 +40,6 @@ namespace Content.Client.IoC
IoCManager.Register();
IoCManager.Register();
IoCManager.Register();
- IoCManager.Register();
IoCManager.Register();
IoCManager.Register();
IoCManager.Register();
diff --git a/Content.Client/Viewport/GameScreen.cs b/Content.Client/Viewport/GameScreen.cs
index c58a7de94c..ae6737aaf7 100644
--- a/Content.Client/Viewport/GameScreen.cs
+++ b/Content.Client/Viewport/GameScreen.cs
@@ -1,4 +1,4 @@
-using Content.Client.Administration.Managers;
+using Content.Client.Alerts.UI;
using Content.Client.Chat;
using Content.Client.Chat.Managers;
using Content.Client.Chat.UI;
@@ -38,6 +38,7 @@ namespace Content.Client.Viewport
[ViewVariables] private ChatBox? _gameChat;
private ConstructionMenuPresenter? _constructionMenu;
+ private AlertsFramePresenter? _alertsFramePresenter;
private FpsCounter _fpsCounter = default!;
@@ -107,6 +108,10 @@ namespace Content.Client.Viewport
///
private void SetupPresenters()
{
+ // HUD
+ _alertsFramePresenter = new AlertsFramePresenter();
+
+ // Windows
_constructionMenu = new ConstructionMenuPresenter(_gameHud);
}
@@ -115,7 +120,11 @@ namespace Content.Client.Viewport
///
private void DisposePresenters()
{
+ // Windows
_constructionMenu?.Dispose();
+
+ // HUD
+ _alertsFramePresenter?.Dispose();
}
internal static void FocusChat(ChatBox chat)
diff --git a/Content.IntegrationTests/Tests/GameObjects/Components/Mobs/AlertsComponentTests.cs b/Content.IntegrationTests/Tests/GameObjects/Components/Mobs/AlertsComponentTests.cs
index e454fb179b..96c0d67c9d 100644
--- a/Content.IntegrationTests/Tests/GameObjects/Components/Mobs/AlertsComponentTests.cs
+++ b/Content.IntegrationTests/Tests/GameObjects/Components/Mobs/AlertsComponentTests.cs
@@ -1,20 +1,17 @@
using System.Linq;
using System.Threading.Tasks;
-using Content.Client.Alerts;
using Content.Client.Alerts.UI;
-using Content.Server.Alert;
using Content.Shared.Alert;
using NUnit.Framework;
using Robust.Client.UserInterface;
-using Robust.Server.Player;
using Robust.Shared.GameObjects;
+using Robust.Server.Player;
using Robust.Shared.IoC;
namespace Content.IntegrationTests.Tests.GameObjects.Components.Mobs
{
[TestFixture]
- [TestOf(typeof(ClientAlertsComponent))]
- [TestOf(typeof(ServerAlertsComponent))]
+ [TestOf(typeof(AlertsComponent))]
public class AlertsComponentTests : ContentIntegrationTest
{
[Test]
@@ -26,17 +23,18 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.Mobs
await client.WaitIdleAsync();
var serverPlayerManager = server.ResolveDependency();
+ var alertsSystem = server.ResolveDependency().GetEntitySystem();
await server.WaitAssertion(() =>
{
var playerEnt = serverPlayerManager.Sessions.Single().AttachedEntity.GetValueOrDefault();
Assert.That(playerEnt != default);
- var alertsComponent = IoCManager.Resolve().GetComponent(playerEnt);
+ var alertsComponent = IoCManager.Resolve().GetComponent(playerEnt);
Assert.NotNull(alertsComponent);
// show 2 alerts
- alertsComponent.ShowAlert(AlertType.Debug1);
- alertsComponent.ShowAlert(AlertType.Debug2);
+ alertsSystem.ShowAlert(alertsComponent.Owner, AlertType.Debug1, null, null);
+ alertsSystem.ShowAlert(alertsComponent.Owner, AlertType.Debug2, null, null);
});
await server.WaitRunTicks(5);
@@ -51,7 +49,7 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.Mobs
Assert.NotNull(local);
var controlled = local.ControlledEntity;
Assert.NotNull(controlled);
- var alertsComponent = IoCManager.Resolve().GetComponent(controlled.Value);
+ var alertsComponent = IoCManager.Resolve().GetComponent(controlled.Value);
Assert.NotNull(alertsComponent);
// find the alertsui
@@ -71,10 +69,10 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.Mobs
{
var playerEnt = serverPlayerManager.Sessions.Single().AttachedEntity.GetValueOrDefault();
Assert.That(playerEnt, Is.Not.EqualTo(default));
- var alertsComponent = IoCManager.Resolve().GetComponent(playerEnt);
+ var alertsComponent = IoCManager.Resolve().GetComponent(playerEnt);
Assert.NotNull(alertsComponent);
- alertsComponent.ClearAlert(AlertType.Debug1);
+ alertsSystem.ClearAlert(alertsComponent.Owner, AlertType.Debug1);
});
await server.WaitRunTicks(5);
await client.WaitRunTicks(5);
@@ -86,7 +84,7 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.Mobs
Assert.NotNull(local);
var controlled = local.ControlledEntity;
Assert.NotNull(controlled);
- var alertsComponent = IoCManager.Resolve().GetComponent(controlled.Value);
+ var alertsComponent = IoCManager.Resolve().GetComponent(controlled.Value);
Assert.NotNull(alertsComponent);
// find the alertsui
diff --git a/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs b/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs
index 862c30d6fe..1e5b8b6335 100644
--- a/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs
+++ b/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs
@@ -1,4 +1,4 @@
-using System.Threading.Tasks;
+using System.Threading.Tasks;
using Content.Server.Gravity;
using Content.Server.Gravity.EntitySystems;
using Content.Shared.Alert;
@@ -42,9 +42,9 @@ namespace Content.IntegrationTests.Tests.Gravity
var mapManager = server.ResolveDependency();
var entityManager = server.ResolveDependency();
+ var alertsSystem = server.ResolveDependency().GetEntitySystem();
EntityUid human = default;
- SharedAlertsComponent alerts = null;
await server.WaitAssertion(() =>
{
@@ -52,7 +52,7 @@ namespace Content.IntegrationTests.Tests.Gravity
var coordinates = grid.ToCoordinates();
human = entityManager.SpawnEntity("HumanDummy", coordinates);
- Assert.True(entityManager.TryGetComponent(human, out alerts));
+ Assert.True(entityManager.TryGetComponent(human, out AlertsComponent alerts));
});
// Let WeightlessSystem and GravitySystem tick
@@ -61,7 +61,7 @@ namespace Content.IntegrationTests.Tests.Gravity
await server.WaitAssertion(() =>
{
// No gravity without a gravity generator
- Assert.True(alerts.IsShowingAlert(AlertType.Weightless));
+ Assert.True(alertsSystem.IsShowingAlert(human, AlertType.Weightless));
entityManager.SpawnEntity("GravityGeneratorDummy", entityManager.GetComponent(human).Coordinates);
});
@@ -71,7 +71,7 @@ namespace Content.IntegrationTests.Tests.Gravity
await server.WaitAssertion(() =>
{
- Assert.False(alerts.IsShowingAlert(AlertType.Weightless));
+ Assert.False(alertsSystem.IsShowingAlert(human, AlertType.Weightless));
// TODO: Re-add gravity generator breaking when Vera is done with construction stuff.
/*
diff --git a/Content.Server/Alert/Click/RemoveCuffs.cs b/Content.Server/Alert/Click/RemoveCuffs.cs
index af55ca2617..ffc7f2ff7f 100644
--- a/Content.Server/Alert/Click/RemoveCuffs.cs
+++ b/Content.Server/Alert/Click/RemoveCuffs.cs
@@ -14,11 +14,11 @@ namespace Content.Server.Alert.Click
[DataDefinition]
public class RemoveCuffs : IAlertClick
{
- public void AlertClicked(ClickAlertEventArgs args)
+ public void AlertClicked(EntityUid player)
{
- if (IoCManager.Resolve().TryGetComponent(args.Player, out CuffableComponent? cuffableComponent))
+ if (IoCManager.Resolve().TryGetComponent(player, out CuffableComponent? cuffableComponent))
{
- cuffableComponent.TryUncuff(args.Player);
+ cuffableComponent.TryUncuff(player);
}
}
}
diff --git a/Content.Server/Alert/Click/ResistFire.cs b/Content.Server/Alert/Click/ResistFire.cs
index 8a980b1037..1b5660623a 100644
--- a/Content.Server/Alert/Click/ResistFire.cs
+++ b/Content.Server/Alert/Click/ResistFire.cs
@@ -1,4 +1,4 @@
-using Content.Server.Atmos.Components;
+using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems;
using Content.Shared.Alert;
using JetBrains.Annotations;
@@ -15,11 +15,11 @@ namespace Content.Server.Alert.Click
[DataDefinition]
public class ResistFire : IAlertClick
{
- public void AlertClicked(ClickAlertEventArgs args)
+ public void AlertClicked(EntityUid player)
{
- if (IoCManager.Resolve().TryGetComponent(args.Player, out FlammableComponent? flammable))
+ if (IoCManager.Resolve().TryGetComponent(player, out FlammableComponent? flammable))
{
- EntitySystem.Get().Resist(args.Player, flammable);
+ EntitySystem.Get().Resist(player, flammable);
}
}
}
diff --git a/Content.Server/Alert/Click/StopBeingPulled.cs b/Content.Server/Alert/Click/StopBeingPulled.cs
index 2d76c98347..4046b324bd 100644
--- a/Content.Server/Alert/Click/StopBeingPulled.cs
+++ b/Content.Server/Alert/Click/StopBeingPulled.cs
@@ -16,12 +16,12 @@ namespace Content.Server.Alert.Click
[DataDefinition]
public class StopBeingPulled : IAlertClick
{
- public void AlertClicked(ClickAlertEventArgs args)
+ public void AlertClicked(EntityUid player)
{
- if (!EntitySystem.Get().CanInteract(args.Player))
+ if (!EntitySystem.Get().CanInteract(player))
return;
- if (IoCManager.Resolve().TryGetComponent(args.Player, out var playerPullable))
+ if (IoCManager.Resolve().TryGetComponent(player, out var playerPullable))
{
EntitySystem.Get().TryStopPull(playerPullable);
}
diff --git a/Content.Server/Alert/Click/StopPiloting.cs b/Content.Server/Alert/Click/StopPiloting.cs
index 9b1ccabed7..bb624a2951 100644
--- a/Content.Server/Alert/Click/StopPiloting.cs
+++ b/Content.Server/Alert/Click/StopPiloting.cs
@@ -1,4 +1,4 @@
-using Content.Server.Shuttles;
+using Content.Server.Shuttles;
using Content.Server.Shuttles.EntitySystems;
using Content.Shared.Alert;
using Content.Shared.Shuttles;
@@ -17,9 +17,9 @@ namespace Content.Server.Alert.Click
[DataDefinition]
public class StopPiloting : IAlertClick
{
- public void AlertClicked(ClickAlertEventArgs args)
+ public void AlertClicked(EntityUid player)
{
- if (IoCManager.Resolve().TryGetComponent(args.Player, out PilotComponent? pilotComponent) &&
+ if (IoCManager.Resolve().TryGetComponent(player, out PilotComponent? pilotComponent) &&
pilotComponent.Console != null)
{
EntitySystem.Get().RemovePilot(pilotComponent);
diff --git a/Content.Server/Alert/Click/StopPulling.cs b/Content.Server/Alert/Click/StopPulling.cs
index 30d3d11cce..f0381097a6 100644
--- a/Content.Server/Alert/Click/StopPulling.cs
+++ b/Content.Server/Alert/Click/StopPulling.cs
@@ -15,10 +15,10 @@ namespace Content.Server.Alert.Click
[DataDefinition]
public class StopPulling : IAlertClick
{
- public void AlertClicked(ClickAlertEventArgs args)
+ public void AlertClicked(EntityUid player)
{
var ps = EntitySystem.Get();
- var playerTarget = ps.GetPulled(args.Player);
+ var playerTarget = ps.GetPulled(player);
if (playerTarget != default && IoCManager.Resolve().TryGetComponent(playerTarget, out SharedPullableComponent playerPullable))
{
ps.TryStopPull(playerPullable);
diff --git a/Content.Server/Alert/Click/Unbuckle.cs b/Content.Server/Alert/Click/Unbuckle.cs
index 9515320efe..cc848aa02b 100644
--- a/Content.Server/Alert/Click/Unbuckle.cs
+++ b/Content.Server/Alert/Click/Unbuckle.cs
@@ -1,4 +1,4 @@
-using Content.Server.Buckle.Components;
+using Content.Server.Buckle.Components;
using Content.Shared.Alert;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
@@ -14,11 +14,11 @@ namespace Content.Server.Alert.Click
[DataDefinition]
public class Unbuckle : IAlertClick
{
- public void AlertClicked(ClickAlertEventArgs args)
+ public void AlertClicked(EntityUid player)
{
- if (IoCManager.Resolve().TryGetComponent(args.Player, out BuckleComponent? buckle))
+ if (IoCManager.Resolve().TryGetComponent(player, out BuckleComponent? buckle))
{
- buckle.TryUnbuckle(args.Player);
+ buckle.TryUnbuckle(player);
}
}
}
diff --git a/Content.Server/Alert/Commands/ClearAlert.cs b/Content.Server/Alert/Commands/ClearAlert.cs
index 3348d3bf3b..87a031405d 100644
--- a/Content.Server/Alert/Commands/ClearAlert.cs
+++ b/Content.Server/Alert/Commands/ClearAlert.cs
@@ -34,21 +34,21 @@ namespace Content.Server.Alert.Commands
if (!CommandUtils.TryGetAttachedEntityByUsernameOrId(shell, target, player, out attachedEntity)) return;
}
- if (!IoCManager.Resolve().TryGetComponent(attachedEntity, out ServerAlertsComponent? alertsComponent))
+ if (!IoCManager.Resolve().TryGetComponent(attachedEntity, out AlertsComponent? alertsComponent))
{
shell.WriteLine("user has no alerts component");
return;
}
var alertType = args[0];
- var alertMgr = IoCManager.Resolve();
- if (!alertMgr.TryGet(Enum.Parse(alertType), out var alert))
+ var alertsSystem = EntitySystem.Get();
+ if (!alertsSystem.TryGet(Enum.Parse(alertType), out var alert))
{
shell.WriteLine("unrecognized alertType " + alertType);
return;
}
- alertsComponent.ClearAlert(alert.AlertType);
+ alertsSystem.ClearAlert(attachedEntity, alert.AlertType);
}
}
}
diff --git a/Content.Server/Alert/Commands/ShowAlert.cs b/Content.Server/Alert/Commands/ShowAlert.cs
index 04769bc3d2..c676eeb94c 100644
--- a/Content.Server/Alert/Commands/ShowAlert.cs
+++ b/Content.Server/Alert/Commands/ShowAlert.cs
@@ -34,7 +34,7 @@ namespace Content.Server.Alert.Commands
if (!CommandUtils.TryGetAttachedEntityByUsernameOrId(shell, target, player, out attachedEntity)) return;
}
- if (!IoCManager.Resolve().TryGetComponent(attachedEntity, out ServerAlertsComponent? alertsComponent))
+ if (!IoCManager.Resolve().TryGetComponent(attachedEntity, out AlertsComponent? alertsComponent))
{
shell.WriteLine("user has no alerts component");
return;
@@ -42,8 +42,8 @@ namespace Content.Server.Alert.Commands
var alertType = args[0];
var severity = args[1];
- var alertMgr = IoCManager.Resolve();
- if (!alertMgr.TryGet(Enum.Parse(alertType), out var alert))
+ var alertsSystem = EntitySystem.Get();
+ if (!alertsSystem.TryGet(Enum.Parse(alertType), out var alert))
{
shell.WriteLine("unrecognized alertType " + alertType);
return;
@@ -53,7 +53,9 @@ namespace Content.Server.Alert.Commands
shell.WriteLine("invalid severity " + sevint);
return;
}
- alertsComponent.ShowAlert(alert.AlertType, sevint == -1 ? null : sevint);
+
+ short? severity1 = sevint == -1 ? null : sevint;
+ alertsSystem.ShowAlert(attachedEntity, alert.AlertType, severity1, null);
}
}
}
diff --git a/Content.Server/Alert/ServerAlertsComponent.cs b/Content.Server/Alert/ServerAlertsComponent.cs
deleted file mode 100644
index 954838a1ba..0000000000
--- a/Content.Server/Alert/ServerAlertsComponent.cs
+++ /dev/null
@@ -1,86 +0,0 @@
-using System;
-using Content.Server.Gravity.EntitySystems;
-using Content.Shared.Alert;
-using Robust.Shared.GameObjects;
-using Robust.Shared.IoC;
-using Robust.Shared.Log;
-using Robust.Shared.Network;
-using Robust.Shared.Players;
-
-namespace Content.Server.Alert
-{
- [RegisterComponent]
- [ComponentReference(typeof(SharedAlertsComponent))]
- public sealed class ServerAlertsComponent : SharedAlertsComponent
- {
-
- protected override void Startup()
- {
- base.Startup();
-
- if (EntitySystem.TryGet(out var weightlessSystem))
- {
- weightlessSystem.AddAlert(this);
- }
- else
- {
- Logger.WarningS("alert", "weightlesssystem not found");
- }
- }
-
- protected override void OnRemove()
- {
- if (EntitySystem.TryGet(out var weightlessSystem))
- {
- weightlessSystem.RemoveAlert(this);
- }
- else
- {
- Logger.WarningS("alert", $"{nameof(WeightlessSystem)} not found");
- }
-
- base.OnRemove();
- }
-
- [Obsolete("Component Messages are deprecated, use Entity Events instead.")]
- public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, ICommonSession? session = null)
- {
- base.HandleNetworkMessage(message, netChannel, session);
-
- if (session == null)
- {
- throw new ArgumentNullException(nameof(session));
- }
-
- switch (message)
- {
- case ClickAlertMessage msg:
- {
- var player = session.AttachedEntity.GetValueOrDefault();
-
- if (player != Owner)
- {
- break;
- }
-
- if (!IsShowingAlert(msg.Type))
- {
- Logger.DebugS("alert", "user {0} attempted to" +
- " click alert {1} which is not currently showing for them",
- IoCManager.Resolve().GetComponent(player).EntityName, msg.Type);
- break;
- }
-
- if (!AlertManager.TryGet(msg.Type, out var alert))
- {
- Logger.WarningS("alert", "unrecognized encoded alert {0}", msg.Type);
- break;
- }
-
- alert.OnClick?.AlertClicked(new ClickAlertEventArgs(player, alert));
- break;
- }
- }
- }
- }
-}
diff --git a/Content.Server/Alert/ServerAlertsSystem.cs b/Content.Server/Alert/ServerAlertsSystem.cs
new file mode 100644
index 0000000000..0398dd4b17
--- /dev/null
+++ b/Content.Server/Alert/ServerAlertsSystem.cs
@@ -0,0 +1,7 @@
+using Content.Shared.Alert;
+
+namespace Content.Server.Alert;
+
+// The only reason this exists is because the DI system requires the shared AlertsSystem
+// to be abstract.
+internal class ServerAlertsSystem : AlertsSystem { }
diff --git a/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs b/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs
index ebd18caf5c..a5195f8c06 100644
--- a/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs
+++ b/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs
@@ -1,6 +1,5 @@
using System;
using Content.Server.Administration.Logs;
-using Content.Server.Alert;
using Content.Server.Atmos.Components;
using Content.Shared.Alert;
using Content.Shared.Atmos;
@@ -16,11 +15,11 @@ namespace Content.Server.Atmos.EntitySystems
{
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
+ [Dependency] private readonly AlertsSystem _alertsSystem = default!;
[Dependency] private readonly AdminLogSystem _logSystem = default!;
private const float UpdateTimer = 1f;
-
- private float _timer = 0f;
+ private float _timer;
public override void Initialize()
{
@@ -72,7 +71,7 @@ namespace Content.Server.Atmos.EntitySystems
_timer -= UpdateTimer;
- foreach (var (barotrauma, damageable, transform) in EntityManager.EntityQuery(false))
+ foreach (var (barotrauma, damageable, transform) in EntityManager.EntityQuery())
{
var totalDamage = FixedPoint2.Zero;
foreach (var (barotraumaDamageType, _) in barotrauma.Damage.DamageDict)
@@ -84,28 +83,24 @@ namespace Content.Server.Atmos.EntitySystems
if (totalDamage >= barotrauma.MaxDamage)
continue;
- var uid = barotrauma.Owner;
-
- var status = EntityManager.GetComponentOrNull(barotrauma.Owner);
-
var pressure = 1f;
if (_atmosphereSystem.GetTileMixture(transform.Coordinates) is { } mixture)
{
- pressure = MathF.Max(mixture.Pressure, 1f);;
+ pressure = MathF.Max(mixture.Pressure, 1f);
}
switch (pressure)
{
// Low pressure.
case <= Atmospherics.WarningLowPressure:
- pressure = GetFeltLowPressure(uid, pressure);
+ pressure = GetFeltLowPressure(barotrauma.Owner, pressure);
if (pressure > Atmospherics.WarningLowPressure)
goto default;
// Deal damage and ignore resistances. Resistance to pressure damage should be done via pressure protection gear.
- _damageableSystem.TryChangeDamage(uid, barotrauma.Damage * Atmospherics.LowPressureDamage, true, false);
+ _damageableSystem.TryChangeDamage(barotrauma.Owner, barotrauma.Damage * Atmospherics.LowPressureDamage, true, false);
if (!barotrauma.TakingDamage)
{
@@ -113,20 +108,18 @@ namespace Content.Server.Atmos.EntitySystems
_logSystem.Add(LogType.Barotrauma, $"{ToPrettyString(barotrauma.Owner):entity} started taking low pressure damage");
}
- if (status == null) break;
-
if (pressure <= Atmospherics.HazardLowPressure)
{
- status.ShowAlert(AlertType.LowPressure, 2);
+ _alertsSystem.ShowAlert(barotrauma.Owner, AlertType.LowPressure, 2);
break;
}
- status.ShowAlert(AlertType.LowPressure, 1);
+ _alertsSystem.ShowAlert(barotrauma.Owner, AlertType.LowPressure, 1);
break;
// High pressure.
case >= Atmospherics.WarningHighPressure:
- pressure = GetFeltHighPressure(uid, pressure);
+ pressure = GetFeltHighPressure(barotrauma.Owner, pressure);
if(pressure < Atmospherics.WarningHighPressure)
goto default;
@@ -134,7 +127,7 @@ namespace Content.Server.Atmos.EntitySystems
var damageScale = MathF.Min((pressure / Atmospherics.HazardHighPressure) * Atmospherics.PressureDamageCoefficient, Atmospherics.MaxHighPressureDamage);
// Deal damage and ignore resistances. Resistance to pressure damage should be done via pressure protection gear.
- _damageableSystem.TryChangeDamage(uid, barotrauma.Damage * damageScale, true, false);
+ _damageableSystem.TryChangeDamage(barotrauma.Owner, barotrauma.Damage * damageScale, true, false);
if (!barotrauma.TakingDamage)
{
@@ -142,15 +135,13 @@ namespace Content.Server.Atmos.EntitySystems
_logSystem.Add(LogType.Barotrauma, $"{ToPrettyString(barotrauma.Owner):entity} started taking high pressure damage");
}
- if (status == null) break;
-
if (pressure >= Atmospherics.HazardHighPressure)
{
- status.ShowAlert(AlertType.HighPressure, 2);
+ _alertsSystem.ShowAlert(barotrauma.Owner, AlertType.HighPressure, 2);
break;
}
- status.ShowAlert(AlertType.HighPressure, 1);
+ _alertsSystem.ShowAlert(barotrauma.Owner, AlertType.HighPressure, 1);
break;
// Normal pressure.
@@ -160,7 +151,7 @@ namespace Content.Server.Atmos.EntitySystems
barotrauma.TakingDamage = false;
_logSystem.Add(LogType.Barotrauma, $"{ToPrettyString(barotrauma.Owner):entity} stopped taking pressure damage");
}
- status?.ClearAlertCategory(AlertCategory.Pressure);
+ _alertsSystem.ClearAlertCategory(barotrauma.Owner, AlertCategory.Pressure);
break;
}
}
diff --git a/Content.Server/Atmos/EntitySystems/FlammableSystem.cs b/Content.Server/Atmos/EntitySystems/FlammableSystem.cs
index 111bedc2e1..6d546d3eb5 100644
--- a/Content.Server/Atmos/EntitySystems/FlammableSystem.cs
+++ b/Content.Server/Atmos/EntitySystems/FlammableSystem.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using Content.Server.Administration.Logs;
-using Content.Server.Alert;
using Content.Server.Atmos.Components;
using Content.Server.Stunnable;
using Content.Server.Temperature.Systems;
@@ -28,6 +27,7 @@ namespace Content.Server.Atmos.EntitySystems
[Dependency] private readonly StunSystem _stunSystem = default!;
[Dependency] private readonly TemperatureSystem _temperatureSystem = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
+ [Dependency] private readonly AlertsSystem _alertsSystem = default!;
[Dependency] private readonly AdminLogSystem _logSystem = default!;
private const float MinimumFireStacks = -10f;
@@ -167,10 +167,9 @@ namespace Content.Server.Atmos.EntitySystems
}
public void Resist(EntityUid uid,
- FlammableComponent? flammable = null,
- ServerAlertsComponent? alerts = null)
+ FlammableComponent? flammable = null)
{
- if (!Resolve(uid, ref flammable, ref alerts))
+ if (!Resolve(uid, ref flammable))
return;
if (!flammable.OnFire || !_actionBlockerSystem.CanInteract(flammable.Owner) || flammable.Resisting)
@@ -179,7 +178,7 @@ namespace Content.Server.Atmos.EntitySystems
flammable.Resisting = true;
flammable.Owner.PopupMessage(Loc.GetString("flammable-component-resist-message"));
- _stunSystem.TryParalyze(uid, TimeSpan.FromSeconds(2f), true, alerts: alerts);
+ _stunSystem.TryParalyze(uid, TimeSpan.FromSeconds(2f), true);
// TODO FLAMMABLE: Make this not use TimerComponent...
flammable.Owner.SpawnTimer(2000, () =>
@@ -224,15 +223,13 @@ namespace Content.Server.Atmos.EntitySystems
flammable.FireStacks = MathF.Min(0, flammable.FireStacks + 1);
}
- EntityManager.TryGetComponent(flammable.Owner, out ServerAlertsComponent? status);
-
if (!flammable.OnFire)
{
- status?.ClearAlert(AlertType.Fire);
+ _alertsSystem.ClearAlert(uid, AlertType.Fire);
continue;
}
- status?.ShowAlert(AlertType.Fire);
+ _alertsSystem.ShowAlert(uid, AlertType.Fire, null, null);
if (flammable.FireStacks > 0)
{
diff --git a/Content.Server/Body/Systems/RespiratorSystem.cs b/Content.Server/Body/Systems/RespiratorSystem.cs
index 8bb39d3197..323a019a05 100644
--- a/Content.Server/Body/Systems/RespiratorSystem.cs
+++ b/Content.Server/Body/Systems/RespiratorSystem.cs
@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Content.Server.Administration.Logs;
-using Content.Server.Alert;
using Content.Server.Atmos;
using Content.Server.Body.Components;
using Content.Shared.Alert;
@@ -24,6 +23,7 @@ namespace Content.Server.Body.Systems
[Dependency] private readonly AdminLogSystem _logSys = default!;
[Dependency] private readonly BodySystem _bodySystem = default!;
[Dependency] private readonly LungSystem _lungSystem = default!;
+ [Dependency] private readonly AlertsSystem _alertsSystem = default!;
public override void Update(float frameTime)
{
@@ -199,10 +199,7 @@ namespace Content.Server.Body.Systems
respirator.Suffocating = true;
- if (EntityManager.TryGetComponent(uid, out ServerAlertsComponent? alertsComponent))
- {
- alertsComponent.ShowAlert(AlertType.LowOxygen);
- }
+ _alertsSystem.ShowAlert(uid, AlertType.LowOxygen);
_damageableSys.TryChangeDamage(uid, respirator.Damage, true, false);
}
@@ -214,10 +211,7 @@ namespace Content.Server.Body.Systems
respirator.Suffocating = false;
- if (EntityManager.TryGetComponent(uid, out ServerAlertsComponent? alertsComponent))
- {
- alertsComponent.ClearAlert(AlertType.LowOxygen);
- }
+ _alertsSystem.ClearAlert(uid, AlertType.LowOxygen);
_damageableSys.TryChangeDamage(uid, respirator.DamageRecovery, true);
}
diff --git a/Content.Server/Buckle/Components/BuckleComponent.cs b/Content.Server/Buckle/Components/BuckleComponent.cs
index 9da4b1eea4..d0542dfe14 100644
--- a/Content.Server/Buckle/Components/BuckleComponent.cs
+++ b/Content.Server/Buckle/Components/BuckleComponent.cs
@@ -1,6 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
-using Content.Server.Alert;
using Content.Server.Hands.Components;
using Content.Server.Pulling;
using Content.Shared.ActionBlocker;
@@ -36,7 +35,6 @@ namespace Content.Server.Buckle.Components
[Dependency] private readonly IGameTiming _gameTiming = default!;
[ComponentDependency] public readonly AppearanceComponent? Appearance = null;
- [ComponentDependency] private readonly ServerAlertsComponent? _serverAlerts = null;
[ComponentDependency] private readonly MobStateComponent? _mobState = null;
[DataField("size")]
@@ -94,18 +92,14 @@ namespace Content.Server.Buckle.Components
///
private void UpdateBuckleStatus()
{
- if (_serverAlerts == null)
- {
- return;
- }
-
if (Buckled)
{
- _serverAlerts.ShowAlert(BuckledTo?.BuckledAlertType ?? AlertType.Buckled);
+ AlertType alertType = BuckledTo?.BuckledAlertType ?? AlertType.Buckled;
+ EntitySystem.Get().ShowAlert(Owner, alertType);
}
else
{
- _serverAlerts.ClearAlertCategory(AlertCategory.Buckled);
+ EntitySystem.Get().ClearAlertCategory(Owner, AlertCategory.Buckled);
}
}
diff --git a/Content.Server/Clothing/MagbootsSystem.cs b/Content.Server/Clothing/MagbootsSystem.cs
index 8b4ad12210..9486e18e33 100644
--- a/Content.Server/Clothing/MagbootsSystem.cs
+++ b/Content.Server/Clothing/MagbootsSystem.cs
@@ -7,12 +7,15 @@ using Content.Shared.Movement.EntitySystems;
using Content.Shared.Slippery;
using Content.Shared.Verbs;
using Robust.Shared.GameObjects;
+using Robust.Shared.IoC;
using Robust.Shared.Localization;
namespace Content.Server.Clothing
{
public sealed class MagbootsSystem : EntitySystem
{
+ [Dependency] private readonly AlertsSystem _alertsSystem = default!;
+
public override void Initialize()
{
base.Initialize();
@@ -35,16 +38,13 @@ namespace Content.Server.Clothing
movedByPressure.Enabled = state;
}
- if (TryComp(parent, out ServerAlertsComponent? alerts))
+ if (state)
{
- if (state)
- {
- alerts.ShowAlert(AlertType.Magboots);
- }
- else
- {
- alerts.ClearAlert(AlertType.Magboots);
- }
+ _alertsSystem.ShowAlert(parent, AlertType.Magboots);
+ }
+ else
+ {
+ _alertsSystem.ClearAlert(parent, AlertType.Magboots);
}
}
diff --git a/Content.Server/Cuffs/Components/CuffableComponent.cs b/Content.Server/Cuffs/Components/CuffableComponent.cs
index 024c68c8ef..740985ae73 100644
--- a/Content.Server/Cuffs/Components/CuffableComponent.cs
+++ b/Content.Server/Cuffs/Components/CuffableComponent.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using Content.Server.Alert;
using Content.Server.DoAfter;
using Content.Server.Hands.Components;
using Content.Shared.Alert;
@@ -158,16 +157,13 @@ namespace Content.Server.Cuffs.Components
///
private void UpdateAlert()
{
- if (_entMan.TryGetComponent(Owner, out ServerAlertsComponent? status))
+ if (CanStillInteract)
{
- if (CanStillInteract)
- {
- status.ClearAlert(AlertType.Handcuffed);
- }
- else
- {
- status.ShowAlert(AlertType.Handcuffed);
- }
+ EntitySystem.Get().ClearAlert(Owner, AlertType.Handcuffed);
+ }
+ else
+ {
+ EntitySystem.Get().ShowAlert(Owner, AlertType.Handcuffed);
}
}
diff --git a/Content.Server/Electrocution/ElectrocutionSystem.cs b/Content.Server/Electrocution/ElectrocutionSystem.cs
index ae1d6d3f26..dc30f97931 100644
--- a/Content.Server/Electrocution/ElectrocutionSystem.cs
+++ b/Content.Server/Electrocution/ElectrocutionSystem.cs
@@ -9,7 +9,6 @@ using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Server.Power.NodeGroups;
using Content.Server.Window;
-using Content.Shared.Alert;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Content.Shared.Database;
@@ -109,8 +108,10 @@ namespace Content.Server.Electrocution
var actual = _damageableSystem.TryChangeDamage(finished.Electrocuting, damage);
if (actual != null)
+ {
_logSystem.Add(LogType.Electrocution,
$"{ToPrettyString(finished.Owner):entity} received {actual.Total:damage} powered electrocution damage");
+ }
}
EntityManager.DeleteEntity(uid);
@@ -232,10 +233,10 @@ namespace Content.Server.Electrocution
Node? TryNode(string? id)
{
- if (id != null && nodeContainer.TryGetNode(id, out var node)
- && node.NodeGroup is IBasePowerNet { NetworkNode: { LastAvailableSupplySum: >0 } })
+ if (id != null && nodeContainer.TryGetNode(id, out var tryNode)
+ && tryNode.NodeGroup is IBasePowerNet { NetworkNode: { LastAvailableSupplySum: >0 } })
{
- return node;
+ return tryNode;
}
return null;
@@ -245,11 +246,10 @@ namespace Content.Server.Electrocution
/// Whether the entity was stunned by the shock.
public bool TryDoElectrocution(
EntityUid uid, EntityUid? sourceUid, int shockDamage, TimeSpan time, bool refresh, float siemensCoefficient = 1f,
- StatusEffectsComponent? statusEffects = null,
- SharedAlertsComponent? alerts = null)
+ StatusEffectsComponent? statusEffects = null)
{
if (!DoCommonElectrocutionAttempt(uid, sourceUid, ref siemensCoefficient)
- || !DoCommonElectrocution(uid, sourceUid, shockDamage, time, refresh, siemensCoefficient, statusEffects, alerts))
+ || !DoCommonElectrocution(uid, sourceUid, shockDamage, time, refresh, siemensCoefficient, statusEffects))
return false;
RaiseLocalEvent(uid, new ElectrocutedEvent(uid, sourceUid, siemensCoefficient));
@@ -266,7 +266,6 @@ namespace Content.Server.Electrocution
bool refresh,
float siemensCoefficient = 1f,
StatusEffectsComponent? statusEffects = null,
- SharedAlertsComponent? alerts = null,
TransformComponent? sourceTransform = null)
{
if (!DoCommonElectrocutionAttempt(uid, sourceUid, ref siemensCoefficient))
@@ -274,9 +273,9 @@ namespace Content.Server.Electrocution
// Coefficient needs to be higher than this to do a powered electrocution!
if(siemensCoefficient <= 0.5f)
- return DoCommonElectrocution(uid, sourceUid, shockDamage, time, refresh, siemensCoefficient, statusEffects, alerts);
+ return DoCommonElectrocution(uid, sourceUid, shockDamage, time, refresh, siemensCoefficient, statusEffects);
- if (!DoCommonElectrocution(uid, sourceUid, null, time, refresh, siemensCoefficient, statusEffects, alerts))
+ if (!DoCommonElectrocution(uid, sourceUid, null, time, refresh, siemensCoefficient, statusEffects))
return false;
if (!Resolve(sourceUid, ref sourceTransform)) // This shouldn't really happen, but just in case...
@@ -318,8 +317,7 @@ namespace Content.Server.Electrocution
private bool DoCommonElectrocution(EntityUid uid, EntityUid? sourceUid,
int? shockDamage, TimeSpan time, bool refresh, float siemensCoefficient = 1f,
- StatusEffectsComponent? statusEffects = null,
- SharedAlertsComponent? alerts = null)
+ StatusEffectsComponent? statusEffects = null)
{
if (siemensCoefficient <= 0)
return false;
@@ -332,21 +330,18 @@ namespace Content.Server.Electrocution
return false;
}
- // Optional component.
- Resolve(uid, ref alerts, false);
-
if (!Resolve(uid, ref statusEffects, false) ||
!_statusEffectsSystem.CanApplyEffect(uid, StatusEffectKey, statusEffects))
return false;
if (!_statusEffectsSystem.TryAddStatusEffect(uid, StatusEffectKey, time, refresh,
- statusEffects, alerts))
+ statusEffects))
return false;
var shouldStun = siemensCoefficient > 0.5f;
if (shouldStun)
- _stunSystem.TryParalyze(uid, time * ParalyzeTimeMultiplier, refresh, statusEffects, alerts);
+ _stunSystem.TryParalyze(uid, time * ParalyzeTimeMultiplier, refresh, statusEffects);
// TODO: Sparks here.
@@ -356,13 +351,15 @@ namespace Content.Server.Electrocution
new DamageSpecifier(_prototypeManager.Index(DamageType), dmg));
if (actual != null)
+ {
_logSystem.Add(LogType.Electrocution,
$"{ToPrettyString(statusEffects.Owner):entity} received {actual.Total:damage} powered electrocution damage");
+ }
}
- _stutteringSystem.DoStutter(uid, time * StutteringTimeMultiplier, refresh, statusEffects, alerts);
+ _stutteringSystem.DoStutter(uid, time * StutteringTimeMultiplier, refresh, statusEffects);
_jitteringSystem.DoJitter(uid, time * JitterTimeMultiplier, refresh, JitterAmplitude, JitterFrequency, true,
- statusEffects, alerts);
+ statusEffects);
_popupSystem.PopupEntity(Loc.GetString("electrocuted-component-mob-shocked-popup-player"), uid,
Filter.Entities(uid).Unpredicted());
diff --git a/Content.Server/Entry/EntryPoint.cs b/Content.Server/Entry/EntryPoint.cs
index 9233ce32c8..656ec0b4f5 100644
--- a/Content.Server/Entry/EntryPoint.cs
+++ b/Content.Server/Entry/EntryPoint.cs
@@ -89,7 +89,6 @@ namespace Content.Server.Entry
IoCManager.Resolve().Initialize();
IoCManager.Resolve().Initialize();
- IoCManager.Resolve().Initialize();
IoCManager.Resolve().Initialize();
IoCManager.Resolve().Initialize();
IoCManager.Resolve().Initialize();
diff --git a/Content.Server/Gravity/EntitySystems/WeightlessSystem.cs b/Content.Server/Gravity/EntitySystems/WeightlessSystem.cs
index 6708340cbf..2fcbe2508f 100644
--- a/Content.Server/Gravity/EntitySystems/WeightlessSystem.cs
+++ b/Content.Server/Gravity/EntitySystems/WeightlessSystem.cs
@@ -1,5 +1,4 @@
-using System.Collections.Generic;
-using Content.Server.Alert;
+using System.Collections.Generic;
using Content.Shared.Alert;
using Content.Shared.GameTicking;
using Content.Shared.Gravity;
@@ -15,8 +14,9 @@ namespace Content.Server.Gravity.EntitySystems
public class WeightlessSystem : EntitySystem
{
[Dependency] private readonly IMapManager _mapManager = default!;
+ [Dependency] private readonly AlertsSystem _alertsSystem = default!;
- private readonly Dictionary> _alerts = new();
+ private readonly Dictionary> _alerts = new();
public override void Initialize()
{
@@ -25,6 +25,7 @@ namespace Content.Server.Gravity.EntitySystems
SubscribeLocalEvent(Reset);
SubscribeLocalEvent(GravityChanged);
SubscribeLocalEvent(EntParentChanged);
+ SubscribeLocalEvent(HandleAlertSyncEvent);
}
public void Reset(RoundRestartCleanupEvent ev)
@@ -32,7 +33,7 @@ namespace Content.Server.Gravity.EntitySystems
_alerts.Clear();
}
- public void AddAlert(ServerAlertsComponent status)
+ public void AddAlert(AlertsComponent status)
{
var gridId = EntityManager.GetComponent(status.Owner).GridID;
var alerts = _alerts.GetOrNew(gridId);
@@ -43,16 +44,16 @@ namespace Content.Server.Gravity.EntitySystems
{
if (EntityManager.GetComponent(grid.GridEntityId).Enabled)
{
- RemoveWeightless(status);
+ RemoveWeightless(status.Owner);
}
else
{
- AddWeightless(status);
+ AddWeightless(status.Owner);
}
}
}
- public void RemoveAlert(ServerAlertsComponent status)
+ public void RemoveAlert(AlertsComponent status)
{
var grid = EntityManager.GetComponent(status.Owner).GridID;
if (!_alerts.TryGetValue(grid, out var statuses))
@@ -74,31 +75,31 @@ namespace Content.Server.Gravity.EntitySystems
{
foreach (var status in statuses)
{
- RemoveWeightless(status);
+ RemoveWeightless(status.Owner);
}
}
else
{
foreach (var status in statuses)
{
- AddWeightless(status);
+ AddWeightless(status.Owner);
}
}
}
- private void AddWeightless(ServerAlertsComponent status)
+ private void AddWeightless(EntityUid euid)
{
- status.ShowAlert(AlertType.Weightless);
+ _alertsSystem.ShowAlert(euid, AlertType.Weightless);
}
- private void RemoveWeightless(ServerAlertsComponent status)
+ private void RemoveWeightless(EntityUid euid)
{
- status.ClearAlert(AlertType.Weightless);
+ _alertsSystem.ClearAlert(euid, AlertType.Weightless);
}
private void EntParentChanged(ref EntParentChangedMessage ev)
{
- if (!EntityManager.TryGetComponent(ev.Entity, out ServerAlertsComponent? status))
+ if (!EntityManager.TryGetComponent(ev.Entity, out AlertsComponent? status))
{
return;
}
@@ -119,5 +120,18 @@ namespace Content.Server.Gravity.EntitySystems
newStatuses.Add(status);
}
+
+ private void HandleAlertSyncEvent(EntityUid uid, AlertsComponent component, AlertSyncEvent args)
+ {
+ switch (component.LifeStage)
+ {
+ case ComponentLifeStage.Starting:
+ AddAlert(component);
+ break;
+ case ComponentLifeStage.Removing:
+ RemoveAlert(component);
+ break;
+ }
+ }
}
}
diff --git a/Content.Server/IoC/ServerContentIoC.cs b/Content.Server/IoC/ServerContentIoC.cs
index bef312b4bb..3a549ebd26 100644
--- a/Content.Server/IoC/ServerContentIoC.cs
+++ b/Content.Server/IoC/ServerContentIoC.cs
@@ -7,10 +7,7 @@ using Content.Server.AI.WorldState;
using Content.Server.Chat.Managers;
using Content.Server.Connection;
using Content.Server.Database;
-using Content.Server.DeviceNetwork;
using Content.Server.EUI;
-using Content.Server.Holiday;
-using Content.Server.Holiday.Interfaces;
using Content.Server.Info;
using Content.Server.Maps;
using Content.Server.Module;
@@ -20,11 +17,9 @@ using Content.Server.Objectives;
using Content.Server.Objectives.Interfaces;
using Content.Server.Preferences.Managers;
using Content.Server.Sandbox;
-using Content.Server.Speech;
using Content.Server.Voting.Managers;
using Content.Shared.Actions;
using Content.Shared.Administration;
-using Content.Shared.Alert;
using Content.Shared.Kitchen;
using Content.Shared.Module;
using Robust.Shared.IoC;
@@ -43,7 +38,6 @@ namespace Content.Server.IoC
IoCManager.Register();
IoCManager.Register();
IoCManager.Register();
- IoCManager.Register();
IoCManager.Register();
IoCManager.Register();
IoCManager.Register();
diff --git a/Content.Server/MobState/States/DeadMobState.cs b/Content.Server/MobState/States/DeadMobState.cs
index 81d05e66ad..0a7c42fbf3 100644
--- a/Content.Server/MobState/States/DeadMobState.cs
+++ b/Content.Server/MobState/States/DeadMobState.cs
@@ -1,4 +1,4 @@
-using Content.Server.Alert;
+using System;
using Content.Server.Stunnable;
using Content.Server.Stunnable.Components;
using Content.Shared.Alert;
@@ -17,10 +17,7 @@ namespace Content.Server.MobState.States
{
base.EnterState(uid, entityManager);
- if (entityManager.TryGetComponent(uid, out ServerAlertsComponent? status))
- {
- status.ShowAlert(AlertType.HumanDead);
- }
+ EntitySystem.Get().ShowAlert(uid, AlertType.HumanDead);
if (entityManager.TryGetComponent(uid, out StatusEffectsComponent? stun))
{
diff --git a/Content.Server/MobState/States/NormalMobState.cs b/Content.Server/MobState/States/NormalMobState.cs
index f86b29b151..a5999f0c72 100644
--- a/Content.Server/MobState/States/NormalMobState.cs
+++ b/Content.Server/MobState/States/NormalMobState.cs
@@ -1,4 +1,4 @@
-using Content.Server.Alert;
+using System;
using Content.Shared.Alert;
using Content.Shared.Damage;
using Content.Shared.FixedPoint;
@@ -19,11 +19,6 @@ namespace Content.Server.MobState.States
return;
}
- if (!entityManager.TryGetComponent(entity, out ServerAlertsComponent? alerts))
- {
- return;
- }
-
if (!entityManager.TryGetComponent(entity, out MobStateComponent? stateComponent))
{
return;
@@ -36,7 +31,7 @@ namespace Content.Server.MobState.States
modifier = (short) (damageable.TotalDamage / (earliestThreshold / 7f));
}
- alerts.ShowAlert(AlertType.HumanHealth, modifier);
+ EntitySystem.Get().ShowAlert(entity, AlertType.HumanHealth, modifier);
}
}
}
diff --git a/Content.Server/Nutrition/Components/HungerComponent.cs b/Content.Server/Nutrition/Components/HungerComponent.cs
index f89ba4ad59..ffe32a3732 100644
--- a/Content.Server/Nutrition/Components/HungerComponent.cs
+++ b/Content.Server/Nutrition/Components/HungerComponent.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using Content.Server.Administration.Logs;
-using Content.Server.Alert;
using Content.Shared.Administration.Logs;
using Content.Shared.Alert;
using Content.Shared.Damage;
@@ -95,15 +94,13 @@ namespace Content.Server.Nutrition.Components
}
// Update UI
- _entMan.TryGetComponent(Owner, out ServerAlertsComponent? alertsComponent);
-
if (HungerThresholdAlertTypes.TryGetValue(_currentHungerThreshold, out var alertId))
{
- alertsComponent?.ShowAlert(alertId);
+ EntitySystem.Get().ShowAlert(Owner, alertId);
}
else
{
- alertsComponent?.ClearAlertCategory(AlertCategory.Hunger);
+ EntitySystem.Get().ClearAlertCategory(Owner, AlertCategory.Hunger);
}
switch (_currentHungerThreshold)
diff --git a/Content.Server/Nutrition/Components/ThirstComponent.cs b/Content.Server/Nutrition/Components/ThirstComponent.cs
index 502500db74..fd5bb4ab8a 100644
--- a/Content.Server/Nutrition/Components/ThirstComponent.cs
+++ b/Content.Server/Nutrition/Components/ThirstComponent.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using Content.Server.Administration.Logs;
-using Content.Server.Alert;
using Content.Shared.Administration.Logs;
using Content.Shared.Alert;
using Content.Shared.Damage;
@@ -94,15 +93,13 @@ namespace Content.Server.Nutrition.Components
}
// Update UI
- _entMan.TryGetComponent(Owner, out ServerAlertsComponent? alertsComponent);
-
if (ThirstThresholdAlertTypes.TryGetValue(_currentThirstThreshold, out var alertId))
{
- alertsComponent?.ShowAlert(alertId);
+ EntitySystem.Get().ShowAlert(Owner, alertId);
}
else
{
- alertsComponent?.ClearAlertCategory(AlertCategory.Thirst);
+ EntitySystem.Get().ClearAlertCategory(Owner, AlertCategory.Thirst);
}
switch (_currentThirstThreshold)
diff --git a/Content.Server/Shuttles/EntitySystems/ShuttleConsoleSystem.cs b/Content.Server/Shuttles/EntitySystems/ShuttleConsoleSystem.cs
index 5e879cdbc7..348b9825d7 100644
--- a/Content.Server/Shuttles/EntitySystems/ShuttleConsoleSystem.cs
+++ b/Content.Server/Shuttles/EntitySystems/ShuttleConsoleSystem.cs
@@ -1,4 +1,4 @@
-using Content.Server.Alert;
+using System;
using Content.Server.Power.Components;
using Content.Server.Shuttles.Components;
using Content.Shared.ActionBlocker;
@@ -18,6 +18,7 @@ namespace Content.Server.Shuttles.EntitySystems
internal sealed class ShuttleConsoleSystem : SharedShuttleConsoleSystem
{
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
+ [Dependency] private readonly AlertsSystem _alertsSystem = default!;
public override void Initialize()
{
@@ -141,10 +142,7 @@ namespace Content.Server.Shuttles.EntitySystems
component.SubscribedPilots.Add(pilotComponent);
- if (EntityManager.TryGetComponent(entity, out ServerAlertsComponent? alertsComponent))
- {
- alertsComponent.ShowAlert(AlertType.PilotingShuttle);
- }
+ _alertsSystem.ShowAlert(entity, AlertType.PilotingShuttle);
entity.PopupMessage(Loc.GetString("shuttle-pilot-start"));
pilotComponent.Console = component;
@@ -163,10 +161,7 @@ namespace Content.Server.Shuttles.EntitySystems
if (!helmsman.SubscribedPilots.Remove(pilotComponent)) return;
- if (EntityManager.TryGetComponent(pilotComponent.Owner, out ServerAlertsComponent? alertsComponent))
- {
- alertsComponent.ClearAlert(AlertType.PilotingShuttle);
- }
+ _alertsSystem.ClearAlert(pilotComponent.Owner, AlertType.PilotingShuttle);
pilotComponent.Owner.PopupMessage(Loc.GetString("shuttle-pilot-end"));
diff --git a/Content.Server/Speech/EntitySystems/StutteringSystem.cs b/Content.Server/Speech/EntitySystems/StutteringSystem.cs
index a6efe37e98..b860b8cf7f 100644
--- a/Content.Server/Speech/EntitySystems/StutteringSystem.cs
+++ b/Content.Server/Speech/EntitySystems/StutteringSystem.cs
@@ -1,9 +1,7 @@
using System;
using System.Text;
using System.Text.RegularExpressions;
-using Content.Server.Alert;
using Content.Server.Speech.Components;
-using Content.Shared.Alert;
using Content.Shared.Speech.EntitySystems;
using Content.Shared.StatusEffect;
using Robust.Shared.GameObjects;
@@ -28,12 +26,12 @@ namespace Content.Server.Speech.EntitySystems
SubscribeLocalEvent(OnAccent);
}
- public override void DoStutter(EntityUid uid, TimeSpan time, bool refresh, StatusEffectsComponent? status = null, SharedAlertsComponent? alerts = null)
+ public override void DoStutter(EntityUid uid, TimeSpan time, bool refresh, StatusEffectsComponent? status = null)
{
if (!Resolve(uid, ref status, false))
return;
- _statusEffectsSystem.TryAddStatusEffect(uid, StutterKey, time, refresh, status, alerts);
+ _statusEffectsSystem.TryAddStatusEffect(uid, StutterKey, time, refresh, status);
}
private void OnAccent(EntityUid uid, StutteringAccentComponent component, AccentGetEvent args)
diff --git a/Content.Server/Stunnable/StunOnCollideSystem.cs b/Content.Server/Stunnable/StunOnCollideSystem.cs
index e88fae7ac0..9143c9b09b 100644
--- a/Content.Server/Stunnable/StunOnCollideSystem.cs
+++ b/Content.Server/Stunnable/StunOnCollideSystem.cs
@@ -1,5 +1,4 @@
using System;
-using Content.Server.Alert;
using Content.Server.Stunnable.Components;
using Content.Shared.Standing;
using Content.Shared.StatusEffect;
@@ -27,20 +26,19 @@ namespace Content.Server.Stunnable
if (EntityManager.TryGetComponent(otherUid, out var status))
{
- ServerAlertsComponent? alerts = null;
StandingStateComponent? standingState = null;
AppearanceComponent? appearance = null;
// Let the actual methods log errors for these.
- Resolve(otherUid, ref alerts, ref standingState, ref appearance, false);
+ Resolve(otherUid, ref standingState, ref appearance, false);
- _stunSystem.TryStun(otherUid, TimeSpan.FromSeconds(component.StunAmount), true, status, alerts);
+ _stunSystem.TryStun(otherUid, TimeSpan.FromSeconds(component.StunAmount), true, status);
_stunSystem.TryKnockdown(otherUid, TimeSpan.FromSeconds(component.KnockdownAmount), true,
- status, alerts);
+ status);
_stunSystem.TrySlowdown(otherUid, TimeSpan.FromSeconds(component.SlowdownAmount), true,
- component.WalkSpeedMultiplier, component.RunSpeedMultiplier, status, alerts);
+ component.WalkSpeedMultiplier, component.RunSpeedMultiplier, status);
}
}
}
diff --git a/Content.Server/Temperature/Systems/TemperatureSystem.cs b/Content.Server/Temperature/Systems/TemperatureSystem.cs
index b15f32c305..eee2476d7e 100644
--- a/Content.Server/Temperature/Systems/TemperatureSystem.cs
+++ b/Content.Server/Temperature/Systems/TemperatureSystem.cs
@@ -2,15 +2,12 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Content.Server.Administration.Logs;
-using Content.Server.Alert;
using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Temperature.Components;
-using Content.Shared.Administration.Logs;
using Content.Shared.Alert;
using Content.Shared.Damage;
using Content.Shared.Database;
-using Content.Shared.FixedPoint;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
@@ -20,6 +17,7 @@ namespace Content.Server.Temperature.Systems
{
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
+ [Dependency] private readonly AlertsSystem _alertsSystem = default!;
[Dependency] private readonly AdminLogSystem _logSystem = default!;
///
@@ -31,13 +29,13 @@ namespace Content.Server.Temperature.Systems
public float UpdateInterval = 1.0f;
- private float _accumulatedFrametime = 0.0f;
+ private float _accumulatedFrametime;
public override void Initialize()
{
SubscribeLocalEvent(EnqueueDamage);
SubscribeLocalEvent(OnAtmosExposedUpdate);
- SubscribeLocalEvent(ServerAlert);
+ SubscribeLocalEvent(ServerAlert);
SubscribeLocalEvent(OnTemperatureChangeAttempt);
}
@@ -103,43 +101,43 @@ namespace Content.Server.Temperature.Systems
ChangeHeat(uid, heat * temperature.AtmosTemperatureTransferEfficiency, temperature: temperature );
}
- private void ServerAlert(EntityUid uid, ServerAlertsComponent status, OnTemperatureChangeEvent args)
+ private void ServerAlert(EntityUid uid, AlertsComponent status, OnTemperatureChangeEvent args)
{
switch (args.CurrentTemperature)
{
// Cold strong.
case <= 260:
- status.ShowAlert(AlertType.Cold, 3);
+ _alertsSystem.ShowAlert(uid, AlertType.Cold, 3);
break;
// Cold mild.
case <= 280 and > 260:
- status.ShowAlert(AlertType.Cold, 2);
+ _alertsSystem.ShowAlert(uid, AlertType.Cold, 2);
break;
// Cold weak.
case <= 292 and > 280:
- status.ShowAlert(AlertType.Cold, 1);
+ _alertsSystem.ShowAlert(uid, AlertType.Cold, 1);
break;
// Safe.
case <= 327 and > 292:
- status.ClearAlertCategory(AlertCategory.Temperature);
+ _alertsSystem.ClearAlertCategory(uid, AlertCategory.Temperature);
break;
// Heat weak.
case <= 335 and > 327:
- status.ShowAlert(AlertType.Hot, 1);
+ _alertsSystem.ShowAlert(uid, AlertType.Hot, 1);
break;
// Heat mild.
case <= 360 and > 335:
- status.ShowAlert(AlertType.Hot, 2);
+ _alertsSystem.ShowAlert(uid, AlertType.Hot, 2);
break;
// Heat strong.
case > 360:
- status.ShowAlert(AlertType.Hot, 3);
+ _alertsSystem.ShowAlert(uid, AlertType.Hot, 3);
break;
}
}
@@ -151,7 +149,7 @@ namespace Content.Server.Temperature.Systems
private void ChangeDamage(EntityUid uid, TemperatureComponent temperature)
{
- if (!EntityManager.TryGetComponent(uid, out var damage))
+ if (!EntityManager.HasComponent(uid))
return;
// See this link for where the scaling func comes from:
diff --git a/Content.Shared/Alert/AlertCategory.cs b/Content.Shared/Alert/AlertCategory.cs
new file mode 100644
index 0000000000..8c7d65ef76
--- /dev/null
+++ b/Content.Shared/Alert/AlertCategory.cs
@@ -0,0 +1,16 @@
+namespace Content.Shared.Alert;
+
+///
+/// Every category of alert. Corresponds to category field in alert prototypes defined in YML
+///
+public enum AlertCategory
+{
+ Pressure,
+ Temperature,
+ Breathing,
+ Buckled,
+ Health,
+ Piloting,
+ Hunger,
+ Thirst
+}
\ No newline at end of file
diff --git a/Content.Shared/Alert/AlertKey.cs b/Content.Shared/Alert/AlertKey.cs
new file mode 100644
index 0000000000..f649bf7b6d
--- /dev/null
+++ b/Content.Shared/Alert/AlertKey.cs
@@ -0,0 +1,62 @@
+using System;
+using Robust.Shared.Serialization;
+using Robust.Shared.Serialization.Manager;
+
+namespace Content.Shared.Alert;
+
+///
+/// Key for an alert which is unique (for equality and hashcode purposes) w.r.t category semantics.
+/// I.e., entirely defined by the category, if a category was specified, otherwise
+/// falls back to the id.
+///
+[Serializable, NetSerializable]
+public struct AlertKey : ISerializationHooks, IPopulateDefaultValues
+{
+ public AlertType? AlertType { get; private set; }
+ public readonly AlertCategory? AlertCategory;
+
+ /// NOTE: if the alert has a category you must pass the category for this to work
+ /// properly as a key. I.e. if the alert has a category and you pass only the alert type, and you
+ /// compare this to another AlertKey that has both the category and the same alert type, it will not consider them equal.
+ public AlertKey(AlertType? alertType, AlertCategory? alertCategory)
+ {
+ AlertCategory = alertCategory;
+ AlertType = alertType;
+ }
+
+ public bool Equals(AlertKey other)
+ {
+ // compare only on alert category if we have one
+ if (AlertCategory.HasValue)
+ {
+ return other.AlertCategory == AlertCategory;
+ }
+
+ return AlertType == other.AlertType && AlertCategory == other.AlertCategory;
+ }
+
+ public override bool Equals(object? obj)
+ {
+ return obj is AlertKey other && Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ // use only alert category if we have one
+ if (AlertCategory.HasValue) return AlertCategory.GetHashCode();
+ return AlertType.GetHashCode();
+ }
+
+ public void PopulateDefaultValues()
+ {
+ AlertType = Alert.AlertType.Error;
+ }
+
+ /// alert category, must not be null
+ /// An alert key for the provided alert category. This must only be used for
+ /// queries and never storage, as it is lacking an alert type.
+ public static AlertKey ForCategory(AlertCategory category)
+ {
+ return new(null, category);
+ }
+}
\ No newline at end of file
diff --git a/Content.Shared/Alert/AlertManager.cs b/Content.Shared/Alert/AlertManager.cs
deleted file mode 100644
index 4fa11c9482..0000000000
--- a/Content.Shared/Alert/AlertManager.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using Robust.Shared.IoC;
-using Robust.Shared.Log;
-using Robust.Shared.Prototypes;
-
-namespace Content.Shared.Alert
-{
- ///
- /// Provides access to all configured alerts by alert type.
- ///
- public class AlertManager
- {
- [Dependency]
- private readonly IPrototypeManager _prototypeManager = default!;
-
- private readonly Dictionary _typeToAlert = new();
-
- public void Initialize()
- {
- foreach (var alert in _prototypeManager.EnumeratePrototypes())
- {
- if (!_typeToAlert.TryAdd(alert.AlertType, alert))
- {
- Logger.ErrorS("alert",
- "Found alert with duplicate alertType {0} - all alerts must have" +
- " a unique alerttype, this one will be skipped", alert.AlertType);
- }
- }
- }
-
- ///
- /// Tries to get the alert of the indicated type
- ///
- /// true if found
- public bool TryGet(AlertType alertType, [NotNullWhen(true)] out AlertPrototype? alert)
- {
- return _typeToAlert.TryGetValue(alertType, out alert);
- }
- }
-}
diff --git a/Content.Shared/Alert/AlertPrototype.cs b/Content.Shared/Alert/AlertPrototype.cs
index 79eafd637d..4bbb1de323 100644
--- a/Content.Shared/Alert/AlertPrototype.cs
+++ b/Content.Shared/Alert/AlertPrototype.cs
@@ -3,7 +3,6 @@ using System.Globalization;
using Robust.Shared.Log;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
-using Robust.Shared.Serialization.Manager;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
@@ -142,61 +141,4 @@ namespace Content.Shared.Alert
}
}
}
-
- ///
- /// Key for an alert which is unique (for equality and hashcode purposes) w.r.t category semantics.
- /// I.e., entirely defined by the category, if a category was specified, otherwise
- /// falls back to the id.
- ///
- [Serializable, NetSerializable]
- public struct AlertKey : ISerializationHooks, IPopulateDefaultValues
- {
- public AlertType? AlertType { get; private set; }
- public readonly AlertCategory? AlertCategory;
-
- /// NOTE: if the alert has a category you must pass the category for this to work
- /// properly as a key. I.e. if the alert has a category and you pass only the alert type, and you
- /// compare this to another AlertKey that has both the category and the same alert type, it will not consider them equal.
- public AlertKey(AlertType? alertType, AlertCategory? alertCategory)
- {
- AlertCategory = alertCategory;
- AlertType = alertType;
- }
-
- public bool Equals(AlertKey other)
- {
- // compare only on alert category if we have one
- if (AlertCategory.HasValue)
- {
- return other.AlertCategory == AlertCategory;
- }
-
- return AlertType == other.AlertType && AlertCategory == other.AlertCategory;
- }
-
- public override bool Equals(object? obj)
- {
- return obj is AlertKey other && Equals(other);
- }
-
- public override int GetHashCode()
- {
- // use only alert category if we have one
- if (AlertCategory.HasValue) return AlertCategory.GetHashCode();
- return AlertType.GetHashCode();
- }
-
- public void PopulateDefaultValues()
- {
- AlertType = Alert.AlertType.Error;
- }
-
- /// alert category, must not be null
- /// An alert key for the provided alert category. This must only be used for
- /// queries and never storage, as it is lacking an alert type.
- public static AlertKey ForCategory(AlertCategory category)
- {
- return new(null, category);
- }
- }
}
diff --git a/Content.Shared/Alert/AlertState.cs b/Content.Shared/Alert/AlertState.cs
new file mode 100644
index 0000000000..21cf322fb6
--- /dev/null
+++ b/Content.Shared/Alert/AlertState.cs
@@ -0,0 +1,12 @@
+using System;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Alert;
+
+[Serializable, NetSerializable]
+public struct AlertState
+{
+ public short? Severity;
+ public (TimeSpan, TimeSpan)? Cooldown;
+ public AlertType Type;
+}
\ No newline at end of file
diff --git a/Content.Shared/Alert/AlertSyncEvent.cs b/Content.Shared/Alert/AlertSyncEvent.cs
new file mode 100644
index 0000000000..2c605c36e9
--- /dev/null
+++ b/Content.Shared/Alert/AlertSyncEvent.cs
@@ -0,0 +1,16 @@
+using Robust.Shared.GameObjects;
+
+namespace Content.Shared.Alert;
+
+///
+/// Raised when the AlertSystem needs alert sources to recalculate their alert states and set them.
+///
+public class AlertSyncEvent : EntityEventArgs
+{
+ public EntityUid Euid { get; }
+
+ public AlertSyncEvent(EntityUid euid)
+ {
+ Euid = euid;
+ }
+}
diff --git a/Content.Shared/Alert/AlertType.cs b/Content.Shared/Alert/AlertType.cs
index a4962a1d36..3c87650419 100644
--- a/Content.Shared/Alert/AlertType.cs
+++ b/Content.Shared/Alert/AlertType.cs
@@ -1,20 +1,5 @@
namespace Content.Shared.Alert
{
- ///
- /// Every category of alert. Corresponds to category field in alert prototypes defined in YML
- ///
- public enum AlertCategory
- {
- Pressure,
- Temperature,
- Breathing,
- Buckled,
- Health,
- Piloting,
- Hunger,
- Thirst
- }
-
///
/// Every kind of alert. Corresponds to alertType field in alert prototypes defined in YML
/// NOTE: Using byte for a compact encoding when sending this in messages, can upgrade
diff --git a/Content.Shared/Alert/AlertsComponent.cs b/Content.Shared/Alert/AlertsComponent.cs
new file mode 100644
index 0000000000..a1d16de665
--- /dev/null
+++ b/Content.Shared/Alert/AlertsComponent.cs
@@ -0,0 +1,18 @@
+using System.Collections.Generic;
+using Robust.Shared.GameObjects;
+using Robust.Shared.GameStates;
+using Robust.Shared.ViewVariables;
+
+namespace Content.Shared.Alert;
+
+///
+/// Handles the icons on the right side of the screen.
+/// Should only be used for player-controlled entities.
+///
+[RegisterComponent]
+[NetworkedComponent]
+[ComponentProtoName("Alerts")]
+public class AlertsComponent : Component
+{
+ [ViewVariables] public Dictionary Alerts = new();
+}
diff --git a/Content.Shared/Alert/AlertsComponentState.cs b/Content.Shared/Alert/AlertsComponentState.cs
new file mode 100644
index 0000000000..f61bf3b8c6
--- /dev/null
+++ b/Content.Shared/Alert/AlertsComponentState.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Alert;
+
+[Serializable, NetSerializable]
+public class AlertsComponentState : ComponentState
+{
+ public Dictionary Alerts;
+
+ public AlertsComponentState(Dictionary alerts)
+ {
+ Alerts = alerts;
+ }
+}
\ No newline at end of file
diff --git a/Content.Shared/Alert/AlertsSystem.cs b/Content.Shared/Alert/AlertsSystem.cs
new file mode 100644
index 0000000000..8baea2b664
--- /dev/null
+++ b/Content.Shared/Alert/AlertsSystem.cs
@@ -0,0 +1,237 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using Robust.Shared.GameObjects;
+using Robust.Shared.GameStates;
+using Robust.Shared.IoC;
+using Robust.Shared.Log;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.Alert;
+
+public abstract class AlertsSystem : EntitySystem
+{
+ [Dependency]
+ private readonly IPrototypeManager _prototypeManager = default!;
+
+ private readonly Dictionary _typeToAlert = new();
+
+ public IReadOnlyDictionary? GetActiveAlerts(EntityUid euid)
+ {
+ return EntityManager.TryGetComponent(euid, out AlertsComponent comp)
+ ? comp.Alerts
+ : null;
+ }
+
+ public bool IsShowingAlert(EntityUid euid, AlertType alertType)
+ {
+ if (!EntityManager.TryGetComponent(euid, out AlertsComponent alertsComponent))
+ return false;
+
+ if (TryGet(alertType, out var alert))
+ {
+ return alertsComponent.Alerts.ContainsKey(alert.AlertKey);
+ }
+
+ Logger.DebugS("alert", "unknown alert type {0}", alertType);
+ return false;
+ }
+
+ /// true iff an alert of the indicated alert category is currently showing
+ public bool IsShowingAlertCategory(EntityUid euid, AlertCategory alertCategory)
+ {
+ return EntityManager.TryGetComponent(euid, out AlertsComponent alertsComponent)
+ && alertsComponent.Alerts.ContainsKey(AlertKey.ForCategory(alertCategory));
+ }
+
+ public bool TryGetAlertState(EntityUid euid, AlertKey key, out AlertState alertState)
+ {
+ if (EntityManager.TryGetComponent(euid, out AlertsComponent alertsComponent))
+ return alertsComponent.Alerts.TryGetValue(key, out alertState);
+
+ alertState = default;
+ return false;
+
+ }
+
+ ///
+ /// Shows the alert. If the alert or another alert of the same category is already showing,
+ /// it will be updated / replaced with the specified values.
+ ///
+ ///
+ /// type of the alert to set
+ /// severity, if supported by the alert
+ /// cooldown start and end, if null there will be no cooldown (and it will
+ /// be erased if there is currently a cooldown for the alert)
+ public void ShowAlert(EntityUid euid, AlertType alertType, short? severity = null, (TimeSpan, TimeSpan)? cooldown = null)
+ {
+ if (!EntityManager.TryGetComponent(euid, out AlertsComponent alertsComponent))
+ return;
+
+ if (TryGet(alertType, out var alert))
+ {
+ // Check whether the alert category we want to show is already being displayed, with the same type,
+ // severity, and cooldown.
+ if (alertsComponent.Alerts.TryGetValue(alert.AlertKey, out var alertStateCallback) &&
+ alertStateCallback.Type == alertType &&
+ alertStateCallback.Severity == severity &&
+ alertStateCallback.Cooldown == cooldown)
+ {
+ return;
+ }
+
+ // In the case we're changing the alert type but not the category, we need to remove it first.
+ alertsComponent.Alerts.Remove(alert.AlertKey);
+
+ alertsComponent.Alerts[alert.AlertKey] = new AlertState
+ { Cooldown = cooldown, Severity = severity, Type = alertType };
+
+ AfterShowAlert(alertsComponent);
+
+ alertsComponent.Dirty();
+ }
+ else
+ {
+ Logger.ErrorS("alert", "Unable to show alert {0}, please ensure this alertType has" +
+ " a corresponding YML alert prototype",
+ alertType);
+ }
+ }
+
+ ///
+ /// Clear the alert with the given category, if one is currently showing.
+ ///
+ public void ClearAlertCategory(EntityUid euid, AlertCategory category)
+ {
+ if(!EntityManager.TryGetComponent(euid, out AlertsComponent alertsComponent))
+ return;
+
+ var key = AlertKey.ForCategory(category);
+ if (!alertsComponent.Alerts.Remove(key))
+ {
+ return;
+ }
+
+ AfterClearAlert(alertsComponent);
+
+ alertsComponent.Dirty();
+ }
+
+ ///
+ /// Clear the alert of the given type if it is currently showing.
+ ///
+ public void ClearAlert(EntityUid euid, AlertType alertType)
+ {
+ if (!EntityManager.TryGetComponent(euid, out AlertsComponent alertsComponent))
+ return;
+
+ if (TryGet(alertType, out var alert))
+ {
+ if (!alertsComponent.Alerts.Remove(alert.AlertKey))
+ {
+ return;
+ }
+
+ AfterClearAlert(alertsComponent);
+
+ alertsComponent.Dirty();
+ }
+ else
+ {
+ Logger.ErrorS("alert", "unable to clear alert, unknown alertType {0}", alertType);
+ }
+ }
+
+ ///
+ /// Invoked after showing an alert prior to dirtying the component
+ ///
+ ///
+ protected virtual void AfterShowAlert(AlertsComponent alertsComponent) { }
+
+ ///
+ /// Invoked after clearing an alert prior to dirtying the component
+ ///
+ ///
+ protected virtual void AfterClearAlert(AlertsComponent alertsComponent) { }
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent((uid, _, _) => RaiseLocalEvent(uid, new AlertSyncEvent(uid)));
+ SubscribeLocalEvent((uid, _, _) => HandleComponentShutdown(uid));
+
+ SubscribeLocalEvent(ClientAlertsGetState);
+ SubscribeNetworkEvent(HandleClickAlert);
+
+ LoadPrototypes();
+ _prototypeManager.PrototypesReloaded += HandlePrototypesReloaded;
+ }
+
+ protected virtual void HandleComponentShutdown(EntityUid uid)
+ {
+ RaiseLocalEvent(uid, new AlertSyncEvent(uid));
+ }
+
+ public override void Shutdown()
+ {
+ _prototypeManager.PrototypesReloaded -= HandlePrototypesReloaded;
+
+ base.Shutdown();
+ }
+
+ private void HandlePrototypesReloaded(PrototypesReloadedEventArgs obj)
+ {
+ LoadPrototypes();
+ }
+
+ protected virtual void LoadPrototypes()
+ {
+ _typeToAlert.Clear();
+ foreach (var alert in _prototypeManager.EnumeratePrototypes())
+ {
+ if (!_typeToAlert.TryAdd(alert.AlertType, alert))
+ {
+ Logger.ErrorS("alert",
+ "Found alert with duplicate alertType {0} - all alerts must have" +
+ " a unique alerttype, this one will be skipped", alert.AlertType);
+ }
+ }
+ }
+
+ ///
+ /// Tries to get the alert of the indicated type
+ ///
+ /// true if found
+ public bool TryGet(AlertType alertType, [NotNullWhen(true)] out AlertPrototype? alert)
+ {
+ return _typeToAlert.TryGetValue(alertType, out alert);
+ }
+
+ private void HandleClickAlert(ClickAlertEvent msg, EntitySessionEventArgs args)
+ {
+ var player = args.SenderSession.AttachedEntity;
+ if (player is null || !EntityManager.TryGetComponent(player, out var alertComp)) return;
+
+ if (!IsShowingAlert(player.Value, msg.Type))
+ {
+ Logger.DebugS("alert", "user {0} attempted to" +
+ " click alert {1} which is not currently showing for them",
+ EntityManager.GetComponent(player.Value).EntityName, msg.Type);
+ return;
+ }
+
+ if (!TryGet(msg.Type, out var alert))
+ {
+ Logger.WarningS("alert", "unrecognized encoded alert {0}", msg.Type);
+ return;
+ }
+
+ alert.OnClick?.AlertClicked(player.Value);
+ }
+
+ private static void ClientAlertsGetState(EntityUid uid, AlertsComponent component, ref ComponentGetState args)
+ {
+ args.State = new AlertsComponentState(component.Alerts);
+ }
+}
diff --git a/Content.Shared/Alert/ClickAlertEvent.cs b/Content.Shared/Alert/ClickAlertEvent.cs
new file mode 100644
index 0000000000..b7a5a8e305
--- /dev/null
+++ b/Content.Shared/Alert/ClickAlertEvent.cs
@@ -0,0 +1,19 @@
+using System;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Alert;
+
+///
+/// A message that calls the click interaction on a alert
+///
+[Serializable, NetSerializable]
+public class ClickAlertEvent : EntityEventArgs
+{
+ public readonly AlertType Type;
+
+ public ClickAlertEvent(AlertType alertType)
+ {
+ Type = alertType;
+ }
+}
\ No newline at end of file
diff --git a/Content.Shared/Alert/IAlertClick.cs b/Content.Shared/Alert/IAlertClick.cs
index e953b6f525..a6287a22c3 100644
--- a/Content.Shared/Alert/IAlertClick.cs
+++ b/Content.Shared/Alert/IAlertClick.cs
@@ -1,5 +1,4 @@
-using System;
-using Robust.Shared.GameObjects;
+using Robust.Shared.GameObjects;
namespace Content.Shared.Alert
{
@@ -11,25 +10,7 @@ namespace Content.Shared.Alert
///
/// Invoked on server side when user clicks an alert.
///
- ///
- void AlertClicked(ClickAlertEventArgs args);
- }
-
- public class ClickAlertEventArgs : EventArgs
- {
- ///
- /// Player clicking the alert
- ///
- public readonly EntityUid Player;
- ///
- /// Alert that was clicked
- ///
- public readonly AlertPrototype Alert;
-
- public ClickAlertEventArgs(EntityUid player, AlertPrototype alert)
- {
- Player = player;
- Alert = alert;
- }
+ ///
+ void AlertClicked(EntityUid player);
}
}
diff --git a/Content.Shared/Alert/SharedAlertsComponent.cs b/Content.Shared/Alert/SharedAlertsComponent.cs
deleted file mode 100644
index db7859a482..0000000000
--- a/Content.Shared/Alert/SharedAlertsComponent.cs
+++ /dev/null
@@ -1,204 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Robust.Shared.GameObjects;
-using Robust.Shared.GameStates;
-using Robust.Shared.IoC;
-using Robust.Shared.Log;
-using Robust.Shared.Players;
-using Robust.Shared.Serialization;
-using Robust.Shared.ViewVariables;
-
-namespace Content.Shared.Alert
-{
- ///
- /// Handles the icons on the right side of the screen.
- /// Should only be used for player-controlled entities.
- ///
- [NetworkedComponent()]
- public abstract class SharedAlertsComponent : Component
- {
- [Dependency]
- protected readonly AlertManager AlertManager = default!;
-
- public override string Name => "Alerts";
-
- [ViewVariables] private Dictionary _alerts = new();
-
- public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
- {
- base.HandleComponentState(curState, nextState);
-
- if (curState is not AlertsComponentState state)
- {
- return;
- }
-
- _alerts = state.Alerts;
- }
-
- public override ComponentState GetComponentState()
- {
- return new AlertsComponentState(_alerts);
- }
-
- /// true iff an alert of the indicated alert category is currently showing
- public bool IsShowingAlertCategory(AlertCategory alertCategory)
- {
- return IsShowingAlert(AlertKey.ForCategory(alertCategory));
- }
-
- /// true iff an alert of the indicated id is currently showing
- public bool IsShowingAlert(AlertType alertType)
- {
- if (AlertManager.TryGet(alertType, out var alert))
- {
- return IsShowingAlert(alert.AlertKey);
- }
- Logger.DebugS("alert", "unknown alert type {0}", alertType);
- return false;
-
- }
-
- /// true iff an alert of the indicated key is currently showing
- protected bool IsShowingAlert(AlertKey alertKey)
- {
- return _alerts.ContainsKey(alertKey);
- }
-
- protected IEnumerable> EnumerateAlertStates()
- {
- return _alerts;
- }
-
- protected bool TryGetAlertState(AlertKey key, out AlertState alertState)
- {
- return _alerts.TryGetValue(key, out alertState);
- }
-
- ///
- /// Shows the alert. If the alert or another alert of the same category is already showing,
- /// it will be updated / replaced with the specified values.
- ///
- /// type of the alert to set
- /// severity, if supported by the alert
- /// cooldown start and end, if null there will be no cooldown (and it will
- /// be erased if there is currently a cooldown for the alert)
- public void ShowAlert(AlertType alertType, short? severity = null, (TimeSpan, TimeSpan)? cooldown = null)
- {
- if (AlertManager.TryGet(alertType, out var alert))
- {
- // Check whether the alert category we want to show is already being displayed, with the same type,
- // severity, and cooldown.
- if (_alerts.TryGetValue(alert.AlertKey, out var alertStateCallback) &&
- alertStateCallback.Type == alertType &&
- alertStateCallback.Severity == severity &&
- alertStateCallback.Cooldown == cooldown)
- {
- return;
- }
-
- // In the case we're changing the alert type but not the category, we need to remove it first.
- _alerts.Remove(alert.AlertKey);
-
- _alerts[alert.AlertKey] = new AlertState
- {Cooldown = cooldown, Severity = severity, Type=alertType};
-
- AfterShowAlert();
-
- Dirty();
-
- }
- else
- {
- Logger.ErrorS("alert", "Unable to show alert {0}, please ensure this alertType has" +
- " a corresponding YML alert prototype",
- alertType);
- }
- }
-
- ///
- /// Clear the alert with the given category, if one is currently showing.
- ///
- public void ClearAlertCategory(AlertCategory category)
- {
- var key = AlertKey.ForCategory(category);
- if (!_alerts.Remove(key))
- {
- return;
- }
-
- AfterClearAlert();
-
- Dirty();
- }
-
- ///
- /// Clear the alert of the given type if it is currently showing.
- ///
- public void ClearAlert(AlertType alertType)
- {
- if (AlertManager.TryGet(alertType, out var alert))
- {
- if (!_alerts.Remove(alert.AlertKey))
- {
- return;
- }
-
- AfterClearAlert();
-
- Dirty();
- }
- else
- {
- Logger.ErrorS("alert", "unable to clear alert, unknown alertType {0}", alertType);
- }
-
- }
-
- ///
- /// Invoked after showing an alert prior to dirtying the component
- ///
- protected virtual void AfterShowAlert() { }
-
- ///
- /// Invoked after clearing an alert prior to dirtying the component
- ///
- protected virtual void AfterClearAlert() { }
- }
-
- [Serializable, NetSerializable]
- public class AlertsComponentState : ComponentState
- {
- public Dictionary Alerts;
-
- public AlertsComponentState(Dictionary alerts)
- {
- Alerts = alerts;
- }
- }
-
- ///
- /// A message that calls the click interaction on a alert
- ///
- [Serializable, NetSerializable]
-#pragma warning disable 618
- public class ClickAlertMessage : ComponentMessage
-#pragma warning restore 618
- {
- public readonly AlertType Type;
-
- public ClickAlertMessage(AlertType alertType)
- {
- Directed = true;
- Type = alertType;
- }
- }
-
- [Serializable, NetSerializable]
- public struct AlertState
- {
- public short? Severity;
- public (TimeSpan, TimeSpan)? Cooldown;
- public AlertType Type;
- }
-}
diff --git a/Content.Shared/Jittering/SharedJitteringSystem.cs b/Content.Shared/Jittering/SharedJitteringSystem.cs
index 7a5757d2c9..7bd02f28f4 100644
--- a/Content.Shared/Jittering/SharedJitteringSystem.cs
+++ b/Content.Shared/Jittering/SharedJitteringSystem.cs
@@ -1,6 +1,4 @@
using System;
-using System.Collections.Generic;
-using Content.Shared.Alert;
using Content.Shared.StatusEffect;
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
@@ -58,10 +56,8 @@ namespace Content.Shared.Jittering
/// Frequency for jittering. See and .
/// Whether to change any existing jitter value even if they're greater than the ones we're setting.
/// The status effects component to modify.
- /// The alerts component.
public void DoJitter(EntityUid uid, TimeSpan time, bool refresh, float amplitude = 10f, float frequency = 4f, bool forceValueChange = false,
- StatusEffectsComponent? status = null,
- SharedAlertsComponent? alerts = null)
+ StatusEffectsComponent? status = null)
{
if (!Resolve(uid, ref status, false))
return;
@@ -69,7 +65,7 @@ namespace Content.Shared.Jittering
amplitude = Math.Clamp(amplitude, MinAmplitude, MaxAmplitude);
frequency = Math.Clamp(frequency, MinFrequency, MaxFrequency);
- if (StatusEffects.TryAddStatusEffect(uid, "Jitter", time, refresh, status, alerts))
+ if (StatusEffects.TryAddStatusEffect(uid, "Jitter", time, refresh, status))
{
var jittering = EntityManager.GetComponent(uid);
diff --git a/Content.Shared/MobState/Components/MobStateComponent.cs b/Content.Shared/MobState/Components/MobStateComponent.cs
index 90f9dfeaed..82756b497c 100644
--- a/Content.Shared/MobState/Components/MobStateComponent.cs
+++ b/Content.Shared/MobState/Components/MobStateComponent.cs
@@ -10,7 +10,6 @@ using Content.Shared.MobState.State;
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.IoC;
-using Robust.Shared.Players;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
@@ -69,10 +68,7 @@ namespace Content.Shared.MobState.Components
protected override void OnRemove()
{
- if (_entMan.TryGetComponent(Owner, out SharedAlertsComponent? status))
- {
- status.ClearAlert(AlertType.HumanHealth);
- }
+ EntitySystem.Get().ClearAlert(Owner, AlertType.HumanHealth);
base.OnRemove();
}
diff --git a/Content.Shared/MobState/State/SharedCriticalMobState.cs b/Content.Shared/MobState/State/SharedCriticalMobState.cs
index 4da171f79a..c0a4e03609 100644
--- a/Content.Shared/MobState/State/SharedCriticalMobState.cs
+++ b/Content.Shared/MobState/State/SharedCriticalMobState.cs
@@ -1,4 +1,4 @@
-using Content.Shared.Alert;
+using Content.Shared.Alert;
using Content.Shared.Standing;
using Robust.Shared.GameObjects;
@@ -15,10 +15,7 @@ namespace Content.Shared.MobState.State
{
base.EnterState(uid, entityManager);
- if (entityManager.TryGetComponent(uid, out SharedAlertsComponent? status))
- {
- status.ShowAlert(AlertType.HumanCrit); // TODO: combine humancrit-0 and humancrit-1 into a gif and display it
- }
+ EntitySystem.Get().ShowAlert(uid, AlertType.HumanCrit); // TODO: combine humancrit-0 and humancrit-1 into a gif and display it
EntitySystem.Get().Down(uid);
diff --git a/Content.Shared/Pulling/Systems/SharedPullerSystem.cs b/Content.Shared/Pulling/Systems/SharedPullerSystem.cs
index d69fca7773..10ff273900 100644
--- a/Content.Shared/Pulling/Systems/SharedPullerSystem.cs
+++ b/Content.Shared/Pulling/Systems/SharedPullerSystem.cs
@@ -14,6 +14,7 @@ namespace Content.Shared.Pulling.Systems
{
[Dependency] private readonly SharedPullingSystem _pullSystem = default!;
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifierSystem = default!;
+ [Dependency] private readonly AlertsSystem _alertsSystem = default!;
public override void Initialize()
{
@@ -47,8 +48,7 @@ namespace Content.Shared.Pulling.Systems
if (args.Puller.Owner != uid)
return;
- if (EntityManager.TryGetComponent(component.Owner, out SharedAlertsComponent? alerts))
- alerts.ShowAlert(AlertType.Pulling);
+ _alertsSystem.ShowAlert(component.Owner, AlertType.Pulling);
RefreshMovementSpeed(component);
}
@@ -61,8 +61,8 @@ namespace Content.Shared.Pulling.Systems
if (args.Puller.Owner != uid)
return;
- if (EntityManager.TryGetComponent(component.Owner, out SharedAlertsComponent? alerts))
- alerts.ClearAlert(AlertType.Pulling);
+ var euid = component.Owner;
+ _alertsSystem.ClearAlert(euid, AlertType.Pulling);
RefreshMovementSpeed(component);
}
diff --git a/Content.Shared/Pulling/Systems/SharedPullingSystem.cs b/Content.Shared/Pulling/Systems/SharedPullingSystem.cs
index 5223200464..ca98198be9 100644
--- a/Content.Shared/Pulling/Systems/SharedPullingSystem.cs
+++ b/Content.Shared/Pulling/Systems/SharedPullingSystem.cs
@@ -25,6 +25,7 @@ namespace Content.Shared.Pulling
public abstract partial class SharedPullingSystem : EntitySystem
{
[Dependency] private readonly SharedPullingStateManagementSystem _pullSm = default!;
+ [Dependency] private readonly AlertsSystem _alertsSystem = default!;
///
/// A mapping of pullers to the entity that they are pulling.
@@ -105,9 +106,8 @@ namespace Content.Shared.Pulling
{
if (args.Pulled.Owner != uid)
return;
-
- if (EntityManager.TryGetComponent(component.Owner, out SharedAlertsComponent? alerts))
- alerts.ShowAlert(AlertType.Pulled);
+
+ _alertsSystem.ShowAlert(component.Owner, AlertType.Pulled);
}
private void PullableHandlePullStopped(EntityUid uid, SharedPullableComponent component, PullStoppedMessage args)
@@ -115,8 +115,7 @@ namespace Content.Shared.Pulling
if (args.Pulled.Owner != uid)
return;
- if (EntityManager.TryGetComponent(component.Owner, out SharedAlertsComponent? alerts))
- alerts.ClearAlert(AlertType.Pulled);
+ _alertsSystem.ClearAlert(component.Owner, AlertType.Pulled);
}
public override void Update(float frameTime)
diff --git a/Content.Shared/Speech/EntitySystems/SharedStutteringSystem.cs b/Content.Shared/Speech/EntitySystems/SharedStutteringSystem.cs
index a5b63bab40..5d00175e98 100644
--- a/Content.Shared/Speech/EntitySystems/SharedStutteringSystem.cs
+++ b/Content.Shared/Speech/EntitySystems/SharedStutteringSystem.cs
@@ -1,5 +1,4 @@
using System;
-using Content.Shared.Alert;
using Content.Shared.StatusEffect;
using Robust.Shared.GameObjects;
@@ -8,7 +7,7 @@ namespace Content.Shared.Speech.EntitySystems
public abstract class SharedStutteringSystem : EntitySystem
{
// For code in shared... I imagine we ain't getting accent prediction anytime soon so let's not bother.
- public virtual void DoStutter(EntityUid uid, TimeSpan time, bool refresh, StatusEffectsComponent? status = null, SharedAlertsComponent? alerts = null)
+ public virtual void DoStutter(EntityUid uid, TimeSpan time, bool refresh, StatusEffectsComponent? status = null)
{
}
}
diff --git a/Content.Shared/StatusEffect/StatusEffectsSystem.cs b/Content.Shared/StatusEffect/StatusEffectsSystem.cs
index db296706c1..b0982f91c8 100644
--- a/Content.Shared/StatusEffect/StatusEffectsSystem.cs
+++ b/Content.Shared/StatusEffect/StatusEffectsSystem.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Diagnostics.CodeAnalysis;
using Content.Shared.Alert;
using Robust.Shared.GameObjects;
@@ -15,6 +15,7 @@ namespace Content.Shared.StatusEffect
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IComponentFactory _componentFactory = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
+ [Dependency] private readonly AlertsSystem _alertsSystem = default!;
public override void Initialize()
{
@@ -80,20 +81,16 @@ namespace Content.Shared.StatusEffect
/// How long the effect should last for.
/// The status effect cooldown should be refreshed (true) or accumulated (false).
/// The status effects component to change, if you already have it.
- /// The alerts component to modify, if the status effect has an alert.
/// False if the effect could not be added or the component already exists, true otherwise.
/// The component type to add and remove from the entity.
public bool TryAddStatusEffect(EntityUid uid, string key, TimeSpan time, bool refresh,
- StatusEffectsComponent? status=null,
- SharedAlertsComponent? alerts=null)
+ StatusEffectsComponent? status = null)
where T: Component, new()
{
if (!Resolve(uid, ref status, false))
return false;
- Resolve(uid, ref alerts, false);
-
- if (TryAddStatusEffect(uid, key, time, refresh, status, alerts))
+ if (TryAddStatusEffect(uid, key, time, refresh, status))
{
// If they already have the comp, we just won't bother updating anything.
if (!EntityManager.HasComponent(uid))
@@ -108,15 +105,12 @@ namespace Content.Shared.StatusEffect
}
public bool TryAddStatusEffect(EntityUid uid, string key, TimeSpan time, bool refresh, string component,
- StatusEffectsComponent? status = null,
- SharedAlertsComponent? alerts = null)
+ StatusEffectsComponent? status = null)
{
if (!Resolve(uid, ref status, false))
return false;
- Resolve(uid, ref alerts, false);
-
- if (TryAddStatusEffect(uid, key, time, refresh, status, alerts))
+ if (TryAddStatusEffect(uid, key, time, refresh, status))
{
// If they already have the comp, we just won't bother updating anything.
if (!EntityManager.HasComponent(uid, _componentFactory.GetRegistration(component).Type))
@@ -142,26 +136,22 @@ namespace Content.Shared.StatusEffect
/// How long the effect should last for.
/// The status effect cooldown should be refreshed (true) or accumulated (false).
/// The status effects component to change, if you already have it.
- /// The alerts component to modify, if the status effect has an alert.
/// False if the effect could not be added, or if the effect already existed.
///
/// This obviously does not add any actual 'effects' on its own. Use the generic overload,
/// which takes in a component type, if you want to automatically add and remove a component.
- ///
+ ///
/// If the effect already exists, it will simply replace the cooldown with the new one given.
/// If you want special 'effect merging' behavior, do it your own damn self!
///
public bool TryAddStatusEffect(EntityUid uid, string key, TimeSpan time, bool refresh,
- StatusEffectsComponent? status=null,
- SharedAlertsComponent? alerts=null)
+ StatusEffectsComponent? status=null)
{
if (!Resolve(uid, ref status, false))
return false;
if (!CanApplyEffect(uid, key, status))
return false;
- Resolve(uid, ref alerts, false);
-
// we already checked if it has the index in CanApplyEffect so a straight index and not tryindex here
// is fine
var proto = _prototypeManager.Index(key);
@@ -191,9 +181,10 @@ namespace Content.Shared.StatusEffect
status.ActiveEffects.Add(key, new StatusEffectState(cooldown, refresh, null));
}
- if (proto.Alert != null && alerts != null)
+ if (proto.Alert != null)
{
- alerts.ShowAlert(proto.Alert.Value, cooldown: GetAlertCooldown(uid, proto.Alert.Value, status));
+ var cooldown1 = GetAlertCooldown(uid, proto.Alert.Value, status);
+ _alertsSystem.ShowAlert(uid, proto.Alert.Value, null, cooldown1);
}
status.Dirty();
@@ -233,15 +224,13 @@ namespace Content.Shared.StatusEffect
/// The entity to remove an effect from.
/// The effect ID to remove.
/// The status effects component to change, if you already have it.
- /// The alerts component to modify, if the status effect has an alert.
/// False if the effect could not be removed, true otherwise.
///
/// Obviously this doesn't automatically clear any effects a status effect might have.
/// That's up to the removed component to handle itself when it's removed.
///
public bool TryRemoveStatusEffect(EntityUid uid, string key,
- StatusEffectsComponent? status=null,
- SharedAlertsComponent? alerts=null)
+ StatusEffectsComponent? status=null)
{
if (!Resolve(uid, ref status, false))
return false;
@@ -250,8 +239,6 @@ namespace Content.Shared.StatusEffect
if (!_prototypeManager.TryIndex(key, out var proto))
return false;
- Resolve(uid, ref alerts, false);
-
var state = status.ActiveEffects[key];
// There are cases where a status effect component might be server-only, so TryGetRegistration...
@@ -267,9 +254,9 @@ namespace Content.Shared.StatusEffect
EntityManager.RemoveComponent(uid, type);
}
- if (proto.Alert != null && alerts != null)
+ if (proto.Alert != null)
{
- alerts.ClearAlert(proto.Alert.Value);
+ _alertsSystem.ClearAlert(uid, proto.Alert.Value);
}
status.ActiveEffects.Remove(key);
@@ -284,21 +271,17 @@ namespace Content.Shared.StatusEffect
///
/// The entity to remove effects from.
/// The status effects component to change, if you already have it.
- /// The alerts component to modify, if the status effect has an alert.
/// False if any status effects failed to be removed, true if they all did.
public bool TryRemoveAllStatusEffects(EntityUid uid,
- StatusEffectsComponent? status = null,
- SharedAlertsComponent? alerts = null)
+ StatusEffectsComponent? status = null)
{
if (!Resolve(uid, ref status, false))
return false;
- Resolve(uid, ref alerts, false);
-
bool failed = false;
foreach (var effect in status.ActiveEffects)
{
- if(!TryRemoveStatusEffect(uid, effect.Key, status, alerts))
+ if(!TryRemoveStatusEffect(uid, effect.Key, status))
failed = true;
}
@@ -350,14 +333,11 @@ namespace Content.Shared.StatusEffect
/// The amount of time to add.
/// The status effect component, should you already have it.
public bool TryAddTime(EntityUid uid, string key, TimeSpan time,
- StatusEffectsComponent? status=null,
- SharedAlertsComponent? alert=null)
+ StatusEffectsComponent? status=null)
{
if (!Resolve(uid, ref status, false))
return false;
- Resolve(uid, ref alert, false);
-
if (!HasStatusEffect(uid, key, status))
return false;
@@ -366,11 +346,10 @@ namespace Content.Shared.StatusEffect
status.ActiveEffects[key].Cooldown = timer;
if (_prototypeManager.TryIndex(key, out var proto)
- && alert != null
&& proto.Alert != null)
{
- alert.ShowAlert(proto.Alert.Value, cooldown: GetAlertCooldown(uid, proto.Alert.Value, status));
-
+ (TimeSpan, TimeSpan)? cooldown = GetAlertCooldown(uid, proto.Alert.Value, status);
+ _alertsSystem.ShowAlert(uid, proto.Alert.Value, null, cooldown);
}
return true;
@@ -384,14 +363,11 @@ namespace Content.Shared.StatusEffect
/// The amount of time to add.
/// The status effect component, should you already have it.
public bool TryRemoveTime(EntityUid uid, string key, TimeSpan time,
- StatusEffectsComponent? status=null,
- SharedAlertsComponent? alert=null)
+ StatusEffectsComponent? status=null)
{
if (!Resolve(uid, ref status, false))
return false;
- Resolve(uid, ref alert, false);
-
if (!HasStatusEffect(uid, key, status))
return false;
@@ -405,11 +381,10 @@ namespace Content.Shared.StatusEffect
status.ActiveEffects[key].Cooldown = timer;
if (_prototypeManager.TryIndex(key, out var proto)
- && alert != null
&& proto.Alert != null)
{
- alert.ShowAlert(proto.Alert.Value, cooldown: GetAlertCooldown(uid, proto.Alert.Value, status));
-
+ (TimeSpan, TimeSpan)? cooldown = GetAlertCooldown(uid, proto.Alert.Value, status);
+ _alertsSystem.ShowAlert(uid, proto.Alert.Value, null, cooldown);
}
return true;
diff --git a/Content.Shared/Stunnable/SharedStunSystem.cs b/Content.Shared/Stunnable/SharedStunSystem.cs
index c69e5c8245..aac907f929 100644
--- a/Content.Shared/Stunnable/SharedStunSystem.cs
+++ b/Content.Shared/Stunnable/SharedStunSystem.cs
@@ -1,5 +1,4 @@
using System;
-using Content.Shared.Alert;
using Content.Shared.Audio;
using Content.Shared.DragDrop;
using Content.Shared.Interaction;
@@ -119,8 +118,7 @@ namespace Content.Shared.Stunnable
/// Stuns the entity, disallowing it from doing many interactions temporarily.
///
public bool TryStun(EntityUid uid, TimeSpan time, bool refresh,
- StatusEffectsComponent? status = null,
- SharedAlertsComponent? alerts = null)
+ StatusEffectsComponent? status = null)
{
if (time <= TimeSpan.Zero)
return false;
@@ -128,17 +126,14 @@ namespace Content.Shared.Stunnable
if (!Resolve(uid, ref status, false))
return false;
- Resolve(uid, ref alerts, false);
-
- return _statusEffectSystem.TryAddStatusEffect(uid, "Stun", time, refresh, alerts: alerts);
+ return _statusEffectSystem.TryAddStatusEffect(uid, "Stun", time, refresh);
}
///
/// Knocks down the entity, making it fall to the ground.
///
public bool TryKnockdown(EntityUid uid, TimeSpan time, bool refresh,
- StatusEffectsComponent? status = null,
- SharedAlertsComponent? alerts = null)
+ StatusEffectsComponent? status = null)
{
if (time <= TimeSpan.Zero)
return false;
@@ -146,25 +141,19 @@ namespace Content.Shared.Stunnable
if (!Resolve(uid, ref status, false))
return false;
- Resolve(uid, ref alerts, false);
-
- return _statusEffectSystem.TryAddStatusEffect(uid, "KnockedDown", time, refresh, alerts: alerts);
+ return _statusEffectSystem.TryAddStatusEffect(uid, "KnockedDown", time, refresh);
}
///
/// Applies knockdown and stun to the entity temporarily.
///
public bool TryParalyze(EntityUid uid, TimeSpan time, bool refresh,
- StatusEffectsComponent? status = null,
- SharedAlertsComponent? alerts = null)
+ StatusEffectsComponent? status = null)
{
if (!Resolve(uid, ref status))
return false;
- // Optional component.
- Resolve(uid, ref alerts, false);
-
- return TryKnockdown(uid, time, refresh, status, alerts) && TryStun(uid, time, refresh, status, alerts);
+ return TryKnockdown(uid, time, refresh, status) && TryStun(uid, time, refresh, status);
}
///
@@ -172,19 +161,15 @@ namespace Content.Shared.Stunnable
///
public bool TrySlowdown(EntityUid uid, TimeSpan time, bool refresh,
float walkSpeedMultiplier = 1f, float runSpeedMultiplier = 1f,
- StatusEffectsComponent? status = null,
- SharedAlertsComponent? alerts = null)
+ StatusEffectsComponent? status = null)
{
if (!Resolve(uid, ref status))
return false;
- // "Optional" component.
- Resolve(uid, ref alerts, false);
-
if (time <= TimeSpan.Zero)
return false;
- if (_statusEffectSystem.TryAddStatusEffect(uid, "SlowedDown", time, refresh, status, alerts))
+ if (_statusEffectSystem.TryAddStatusEffect(uid, "SlowedDown", time, refresh, status))
{
var slowed = EntityManager.GetComponent(uid);
// Doesn't make much sense to have the "TrySlowdown" method speed up entities now does it?
diff --git a/Content.Tests/Shared/Alert/AlertManagerTests.cs b/Content.Tests/Shared/Alert/AlertManagerTests.cs
index c56e12c588..ec6108f1b0 100644
--- a/Content.Tests/Shared/Alert/AlertManagerTests.cs
+++ b/Content.Tests/Shared/Alert/AlertManagerTests.cs
@@ -1,14 +1,17 @@
using System.IO;
using Content.Shared.Alert;
using NUnit.Framework;
+using NUnit.Framework.Interfaces;
+using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
+using Robust.Shared.Reflection;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Utility;
namespace Content.Tests.Shared.Alert
{
- [TestFixture, TestOf(typeof(AlertManager))]
+ [TestFixture, TestOf(typeof(AlertsSystem))]
public class AlertManagerTests : ContentUnitTest
{
const string PROTOTYPES = @"
@@ -24,23 +27,26 @@ namespace Content.Tests.Shared.Alert
";
[Test]
+ [Ignore("There is no way to load extra Systems in a unit test, fixing RobustUnitTest is out of scope.")]
public void TestAlertManager()
{
IoCManager.Resolve().Initialize();
+
+ var reflection = IoCManager.Resolve();
+ reflection.LoadAssemblies();
+
var prototypeManager = IoCManager.Resolve();
prototypeManager.Initialize();
prototypeManager.LoadFromStream(new StringReader(PROTOTYPES));
- var alertManager = IoCManager.Resolve();
- alertManager.Initialize();
- Assert.That(alertManager.TryGet(AlertType.LowPressure, out var lowPressure));
+ Assert.That(EntitySystem.Get().TryGet(AlertType.LowPressure, out var lowPressure));
Assert.That(lowPressure.Icon, Is.EqualTo(new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/Alerts/Pressure/lowpressure.png"))));
- Assert.That(alertManager.TryGet(AlertType.HighPressure, out var highPressure));
+ Assert.That(EntitySystem.Get().TryGet(AlertType.HighPressure, out var highPressure));
Assert.That(highPressure.Icon, Is.EqualTo(new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/Alerts/Pressure/highpressure.png"))));
- Assert.That(alertManager.TryGet(AlertType.LowPressure, out lowPressure));
+ Assert.That(EntitySystem.Get().TryGet(AlertType.LowPressure, out lowPressure));
Assert.That(lowPressure.Icon, Is.EqualTo(new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/Alerts/Pressure/lowpressure.png"))));
- Assert.That(alertManager.TryGet(AlertType.HighPressure, out highPressure));
+ Assert.That(EntitySystem.Get().TryGet(AlertType.HighPressure, out highPressure));
Assert.That(highPressure.Icon, Is.EqualTo(new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/Alerts/Pressure/highpressure.png"))));
}
}
diff --git a/Content.Tests/Server/GameObjects/Components/Mobs/ServerAlertsComponentTests.cs b/Content.Tests/Shared/Alert/ServerAlertsComponentTests.cs
similarity index 66%
rename from Content.Tests/Server/GameObjects/Components/Mobs/ServerAlertsComponentTests.cs
rename to Content.Tests/Shared/Alert/ServerAlertsComponentTests.cs
index e6016b1476..87c57ab57d 100644
--- a/Content.Tests/Server/GameObjects/Components/Mobs/ServerAlertsComponentTests.cs
+++ b/Content.Tests/Shared/Alert/ServerAlertsComponentTests.cs
@@ -1,4 +1,5 @@
-using System.IO;
+using System;
+using System.IO;
using Content.Server.Alert;
using Content.Shared.Alert;
using NUnit.Framework;
@@ -7,10 +8,10 @@ using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager;
-namespace Content.Tests.Server.GameObjects.Components.Mobs
+namespace Content.Tests.Shared.Alert
{
[TestFixture]
- [TestOf(typeof(ServerAlertsComponent))]
+ [TestOf(typeof(AlertsComponent))]
public class ServerAlertsComponentTests : ContentUnitTest
{
const string PROTOTYPES = @"
@@ -28,6 +29,7 @@ namespace Content.Tests.Server.GameObjects.Components.Mobs
";
[Test]
+ [Ignore("There is no way to load extra Systems in a unit test, fixing RobustUnitTest is out of scope.")]
public void ShowAlerts()
{
// this is kind of unnecessary because there's integration test coverage of Alert components
@@ -38,31 +40,31 @@ namespace Content.Tests.Server.GameObjects.Components.Mobs
var prototypeManager = IoCManager.Resolve();
prototypeManager.Initialize();
var factory = IoCManager.Resolve();
- factory.RegisterClass();
+ factory.RegisterClass();
prototypeManager.LoadFromStream(new StringReader(PROTOTYPES));
prototypeManager.Resync();
- var alertManager = IoCManager.Resolve();
- alertManager.Initialize();
+ var entSys = IoCManager.Resolve();
+ entSys.LoadExtraSystemType();
- var alertsComponent = new ServerAlertsComponent();
+ var alertsComponent = new AlertsComponent();
alertsComponent = IoCManager.InjectDependencies(alertsComponent);
- Assert.That(alertManager.TryGet(AlertType.LowPressure, out var lowpressure));
- Assert.That(alertManager.TryGet(AlertType.HighPressure, out var highpressure));
+ Assert.That(EntitySystem.Get().TryGet(AlertType.LowPressure, out var lowpressure));
+ Assert.That(EntitySystem.Get().TryGet(AlertType.HighPressure, out var highpressure));
- alertsComponent.ShowAlert(AlertType.LowPressure);
+ EntitySystem.Get().ShowAlert(alertsComponent.Owner, AlertType.LowPressure, null, null);
var alertState = alertsComponent.GetComponentState() as AlertsComponentState;
Assert.NotNull(alertState);
Assert.That(alertState.Alerts.Count, Is.EqualTo(1));
Assert.That(alertState.Alerts.ContainsKey(lowpressure.AlertKey));
- alertsComponent.ShowAlert(AlertType.HighPressure);
+ EntitySystem.Get().ShowAlert(alertsComponent.Owner, AlertType.HighPressure, null, null);
alertState = alertsComponent.GetComponentState() as AlertsComponentState;
Assert.That(alertState.Alerts.Count, Is.EqualTo(1));
Assert.That(alertState.Alerts.ContainsKey(highpressure.AlertKey));
- alertsComponent.ClearAlertCategory(AlertCategory.Pressure);
+ EntitySystem.Get().ClearAlertCategory(alertsComponent.Owner, AlertCategory.Pressure);
alertState = alertsComponent.GetComponentState() as AlertsComponentState;
Assert.That(alertState.Alerts.Count, Is.EqualTo(0));
}