]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Alerts for breathing plasma/tritium (#24484)
authorErrant <35878406+Errant-4@users.noreply.github.com>
Sat, 2 Mar 2024 14:07:05 +0000 (15:07 +0100)
committerGitHub <noreply@github.com>
Sat, 2 Mar 2024 14:07:05 +0000 (01:07 +1100)
* Alert autoremove v0

* Code cleanup and timing

* comment

* Tritium, code compression

* not resolving manually

* reduced lookups, new comp

* fix-fix yes

* use RemCompDeferred, handle OnUnpaused

* missed a todo

* entitysystem resolve

* remove unnecessary component updates

* remove AlertState from comp, move EntityUnpausedEvent actions to AlertStateComponent's Timespan

* Code cleanup

* comments

* combines AutoRemove input into Clear

* minor logic adjustment that does not really change anything but is less ambiguous

Content.Client/UserInterface/Systems/Alerts/Widgets/AlertsUI.xaml.cs
Content.Server/Chemistry/ReagentEffects/AdjustAlert.cs
Content.Shared/Alert/AlertAutoRemoveComponent.cs [new file with mode: 0644]
Content.Shared/Alert/AlertState.cs
Content.Shared/Alert/AlertsSystem.cs
Resources/Prototypes/Reagents/gases.yml

index 52dcc7e51df7e670833661bf207c22147c8baafa..189de5040796942c6ed288043d6f264f7559590b 100644 (file)
@@ -92,7 +92,8 @@ public sealed partial class AlertsUI : UIWidget
             {
                 // key is the same, simply update the existing control severity / cooldown
                 existingAlertControl.SetSeverity(alertState.Severity);
-                existingAlertControl.Cooldown = alertState.Cooldown;
+                if (alertState.ShowCooldown)
+                    existingAlertControl.Cooldown = alertState.Cooldown;
             }
             else
             {
@@ -133,9 +134,13 @@ public sealed partial class AlertsUI : UIWidget
 
     private AlertControl CreateAlertControl(AlertPrototype alert, AlertState alertState)
     {
+        (TimeSpan, TimeSpan)? cooldown = null;
+        if (alertState.ShowCooldown)
+            cooldown = alertState.Cooldown;
+
         var alertControl = new AlertControl(alert, alertState.Severity)
         {
-            Cooldown = alertState.Cooldown
+            Cooldown = cooldown
         };
         alertControl.OnPressed += AlertControlPressed;
         return alertControl;
index cf3d71405bf18da7eefbf72def6d7c7beeb8b02c..8d475570ad015dd94d460c87d3fe85336062cad5 100644 (file)
@@ -7,15 +7,27 @@ namespace Content.Server.Chemistry.ReagentEffects;
 
 public sealed partial class AdjustAlert : ReagentEffect
 {
+    /// <summary>
+    /// The specific Alert that will be adjusted
+    /// </summary>
     [DataField("alertType", required: true)]
     public AlertType Type;
 
+    /// <summary>
+    /// If true, the alert is removed after Time seconds. If Time was not specified the alert is removed immediately.
+    /// </summary>
     [DataField]
     public bool Clear;
 
+    /// <summary>
+    /// Visually display cooldown progress over the alert icon.
+    /// </summary>
     [DataField]
-    public bool Cooldown;
+    public bool ShowCooldown;
 
+    /// <summary>
+    /// The length of the cooldown or delay before removing the alert (in seconds).
+    /// </summary>
     [DataField]
     public float Time;
 
@@ -24,23 +36,24 @@ public sealed partial class AdjustAlert : ReagentEffect
 
     public override void Effect(ReagentEffectArgs args)
     {
-        var alertSys = EntitySystem.Get<AlertsSystem>();
-        if (args.EntityManager.HasComponent<AlertsComponent>(args.SolutionEntity))
+        var alertSys = args.EntityManager.EntitySysManager.GetEntitySystem<AlertsSystem>();
+        if (!args.EntityManager.HasComponent<AlertsComponent>(args.SolutionEntity))
+            return;
+
+        if (Clear && Time <= 0)
         {
-            if (Clear)
-            {
                 alertSys.ClearAlert(args.SolutionEntity, Type);
-            }
-            else
-            {
-                (TimeSpan, TimeSpan)? cooldown = null;
-                if (Cooldown)
-                {
-                    var timing = IoCManager.Resolve<IGameTiming>();
-                    cooldown = (timing.CurTime, timing.CurTime + TimeSpan.FromSeconds(Time));
-                }
-                alertSys.ShowAlert(args.SolutionEntity, Type, cooldown: cooldown);
-            }
         }
+        else
+        {
+            var timing = IoCManager.Resolve<IGameTiming>();
+            (TimeSpan, TimeSpan)? cooldown = null;
+
+            if ((ShowCooldown || Clear) && Time > 0)
+                cooldown = (timing.CurTime, timing.CurTime + TimeSpan.FromSeconds(Time));
+
+            alertSys.ShowAlert(args.SolutionEntity, Type, cooldown: cooldown, autoRemove: Clear, showCooldown: ShowCooldown);
+        }
+
     }
 }
diff --git a/Content.Shared/Alert/AlertAutoRemoveComponent.cs b/Content.Shared/Alert/AlertAutoRemoveComponent.cs
new file mode 100644 (file)
index 0000000..44e2dc9
--- /dev/null
@@ -0,0 +1,19 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Alert;
+
+/// <summary>
+///     Copy of the entity's alerts that are flagged for autoRemove, so that not all of the alerts need to be checked constantly
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class AlertAutoRemoveComponent : Component
+{
+    /// <summary>
+    ///     List of alerts that have to be checked on every tick for automatic removal at a specific time
+    /// </summary>
+    [AutoNetworkedField]
+    [DataField]
+    public List<AlertKey> AlertKeys = new();
+
+    public override bool SendOnlyToOwner => true;
+}
index b7244d3d4e00e494481076a4432d8306b32dc819..effd9522036a214713dd117fb0c0807a184923da 100644 (file)
@@ -7,5 +7,7 @@ public struct AlertState
 {
     public short? Severity;
     public (TimeSpan, TimeSpan)? Cooldown;
+    public bool AutoRemove;
+    public bool ShowCooldown;
     public AlertType Type;
-}
\ No newline at end of file
+}
index 424a4670ba9eb3a46bd923cd190cde989262cf5a..d8737a717aae81bd06eefcea15d1d752820c93fd 100644 (file)
@@ -2,12 +2,14 @@ using System.Collections.Frozen;
 using System.Diagnostics.CodeAnalysis;
 using Robust.Shared.Player;
 using Robust.Shared.Prototypes;
+using Robust.Shared.Timing;
 
 namespace Content.Shared.Alert;
 
 public abstract class AlertsSystem : EntitySystem
 {
     [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+    [Dependency] private readonly IGameTiming _timing = default!;
 
     private FrozenDictionary<AlertType, AlertPrototype> _typeToAlert = default!;
 
@@ -74,7 +76,9 @@ public abstract class AlertsSystem : EntitySystem
     /// <param name="severity">severity, if supported by the alert</param>
     /// <param name="cooldown">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)</param>
-    public void ShowAlert(EntityUid euid, AlertType alertType, short? severity = null, (TimeSpan, TimeSpan)? cooldown = null)
+    /// <param name="autoRemove">if true, the alert will be removed at the end of the cooldown</param>
+    /// <param name="showCooldown">if true, the cooldown will be visibly shown over the alert icon</param>
+    public void ShowAlert(EntityUid euid, AlertType alertType, short? severity = null, (TimeSpan, TimeSpan)? cooldown = null, bool autoRemove = false, bool showCooldown = true )
     {
         if (!TryComp(euid, out AlertsComponent? alertsComponent))
             return;
@@ -86,7 +90,9 @@ public abstract class AlertsSystem : EntitySystem
             if (alertsComponent.Alerts.TryGetValue(alert.AlertKey, out var alertStateCallback) &&
                 alertStateCallback.Type == alertType &&
                 alertStateCallback.Severity == severity &&
-                alertStateCallback.Cooldown == cooldown)
+                alertStateCallback.Cooldown == cooldown &&
+                alertStateCallback.AutoRemove == autoRemove &&
+                alertStateCallback.ShowCooldown == showCooldown)
             {
                 return;
             }
@@ -94,8 +100,17 @@ public abstract class AlertsSystem : EntitySystem
             // 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 };
+            var state = new AlertState
+                { Cooldown = cooldown, Severity = severity, Type = alertType, AutoRemove = autoRemove, ShowCooldown = showCooldown};
+            alertsComponent.Alerts[alert.AlertKey] = state;
+
+            // Keeping a list of AutoRemove alerts, so Update() doesn't need to check every alert
+            if (autoRemove)
+            {
+                var autoComp = EnsureComp<AlertAutoRemoveComponent>(euid);
+                if (!autoComp.AlertKeys.Contains(alert.AlertKey))
+                    autoComp.AlertKeys.Add(alert.AlertKey);
+            }
 
             AfterShowAlert((euid, alertsComponent));
 
@@ -171,11 +186,81 @@ public abstract class AlertsSystem : EntitySystem
         SubscribeLocalEvent<AlertsComponent, ComponentShutdown>(HandleComponentShutdown);
         SubscribeLocalEvent<AlertsComponent, PlayerAttachedEvent>(OnPlayerAttached);
 
+        SubscribeLocalEvent<AlertAutoRemoveComponent, EntityUnpausedEvent>(OnAutoRemoveUnPaused);
+
         SubscribeNetworkEvent<ClickAlertEvent>(HandleClickAlert);
         SubscribeLocalEvent<PrototypesReloadedEventArgs>(HandlePrototypesReloaded);
         LoadPrototypes();
     }
 
+    private void OnAutoRemoveUnPaused(EntityUid uid, AlertAutoRemoveComponent comp, EntityUnpausedEvent args)
+    {
+        if (!TryComp<AlertsComponent>(uid, out var alertComp))
+        {
+            return;
+        }
+
+        var dirty = false;
+
+        foreach (var alert in alertComp.Alerts)
+        {
+            if (alert.Value.Cooldown is null)
+                continue;
+
+            var cooldown = (alert.Value.Cooldown.Value.Item1, alert.Value.Cooldown.Value.Item2 + args.PausedTime);
+
+            var state = new AlertState
+            {
+                Severity = alert.Value.Severity,
+                Cooldown = cooldown,
+                ShowCooldown = alert.Value.ShowCooldown,
+                AutoRemove = alert.Value.AutoRemove,
+                Type = alert.Value.Type
+            };
+            alertComp.Alerts[alert.Key] = state;
+            dirty = true;
+        }
+
+        if (dirty)
+            Dirty(uid, comp);
+    }
+
+    public override void Update(float frameTime)
+    {
+        base.Update(frameTime);
+
+        var query = EntityQueryEnumerator<AlertAutoRemoveComponent>();
+        while (query.MoveNext(out var uid, out var autoComp))
+        {
+            var dirtyComp = false;
+            if (autoComp.AlertKeys.Count <= 0 || !TryComp<AlertsComponent>(uid, out var alertComp))
+            {
+                RemCompDeferred(uid, autoComp);
+                continue;
+            }
+
+            var removeList = new List<AlertKey>();
+            foreach (var alertKey in autoComp.AlertKeys)
+            {
+                alertComp.Alerts.TryGetValue(alertKey, out var alertState);
+
+                if (alertState.Cooldown is null || alertState.Cooldown.Value.Item2 >= _timing.CurTime)
+                    continue;
+                removeList.Add(alertKey);
+                alertComp.Alerts.Remove(alertKey);
+                dirtyComp = true;
+            }
+
+            foreach (var alertKey in removeList)
+            {
+                autoComp.AlertKeys.Remove(alertKey);
+            }
+
+            if (dirtyComp)
+                Dirty(uid, alertComp);
+        }
+    }
+
     protected virtual void HandleComponentShutdown(EntityUid uid, AlertsComponent component, ComponentShutdown args)
     {
         RaiseLocalEvent(uid, new AlertSyncEvent(uid), true);
@@ -200,7 +285,7 @@ public abstract class AlertsSystem : EntitySystem
             if (!dict.TryAdd(alert.AlertType, alert))
             {
                 Log.Error("Found alert with duplicate alertType {0} - all alerts must have" +
-                          " a unique alerttype, this one will be skipped", alert.AlertType);
+                          " a unique alertType, this one will be skipped", alert.AlertType);
             }
         }
 
index 2566076be7f08e98efb82825a65ada15fb105f71..9cb73fffb85881e4460cbe4f5c4a07302292013b 100644 (file)
           types:
             Poison:
               1
-      # Cant be added until I add metabolism effects on reagent removal
-      #- !type:AdjustAlert
-      #  alertType: Toxins
+      # We need a metabolism effect on reagent removal
+      - !type:AdjustAlert
+        alertType: Toxins
+        conditions:
+          - !type:ReagentThreshold
+            min: 1.5
+        clear: True
+        time: 5
   reactiveEffects:
     Flammable:
       methods: [ Touch ]
           types:
             Radiation:
               1
-      # Cant be added until I add metabolism effects on reagent removal
-      #- !type:AdjustAlert
-      #  alertType: Toxins
+      # We need a metabolism effect on reagent removal
+      - !type:AdjustAlert
+        alertType: Toxins
+        conditions:
+          - !type:ReagentThreshold
+            min: 1.5
+        clear: True
+        time: 5
 
 - type: reagent
   id: CarbonDioxide
           type: Plant
           shouldHave: false
         factor: -4
-      # Cant be added until I add metabolism effects on reagent removal
+      # We need a metabolism effect on reagent removal
       #- !type:AdjustAlert
       #  alertType: CarbonDioxide