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;
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);
+ }
+
}
}
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!;
/// <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;
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;
}
// 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));
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);
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);
}
}