ECSatize AlertsSystem (#5559)
This commit is contained in:
16
Content.Shared/Alert/AlertCategory.cs
Normal file
16
Content.Shared/Alert/AlertCategory.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace Content.Shared.Alert;
|
||||
|
||||
/// <summary>
|
||||
/// Every category of alert. Corresponds to category field in alert prototypes defined in YML
|
||||
/// </summary>
|
||||
public enum AlertCategory
|
||||
{
|
||||
Pressure,
|
||||
Temperature,
|
||||
Breathing,
|
||||
Buckled,
|
||||
Health,
|
||||
Piloting,
|
||||
Hunger,
|
||||
Thirst
|
||||
}
|
||||
62
Content.Shared/Alert/AlertKey.cs
Normal file
62
Content.Shared/Alert/AlertKey.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
|
||||
namespace Content.Shared.Alert;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <param name="category">alert category, must not be null</param>
|
||||
/// <returns>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.</returns>
|
||||
public static AlertKey ForCategory(AlertCategory category)
|
||||
{
|
||||
return new(null, category);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to all configured alerts by alert type.
|
||||
/// </summary>
|
||||
public class AlertManager
|
||||
{
|
||||
[Dependency]
|
||||
private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
private readonly Dictionary<AlertType, AlertPrototype> _typeToAlert = new();
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
foreach (var alert in _prototypeManager.EnumeratePrototypes<AlertPrototype>())
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the alert of the indicated type
|
||||
/// </summary>
|
||||
/// <returns>true if found</returns>
|
||||
public bool TryGet(AlertType alertType, [NotNullWhen(true)] out AlertPrototype? alert)
|
||||
{
|
||||
return _typeToAlert.TryGetValue(alertType, out alert);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <param name="category">alert category, must not be null</param>
|
||||
/// <returns>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.</returns>
|
||||
public static AlertKey ForCategory(AlertCategory category)
|
||||
{
|
||||
return new(null, category);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
12
Content.Shared/Alert/AlertState.cs
Normal file
12
Content.Shared/Alert/AlertState.cs
Normal file
@@ -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;
|
||||
}
|
||||
16
Content.Shared/Alert/AlertSyncEvent.cs
Normal file
16
Content.Shared/Alert/AlertSyncEvent.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Shared.Alert;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the AlertSystem needs alert sources to recalculate their alert states and set them.
|
||||
/// </summary>
|
||||
public class AlertSyncEvent : EntityEventArgs
|
||||
{
|
||||
public EntityUid Euid { get; }
|
||||
|
||||
public AlertSyncEvent(EntityUid euid)
|
||||
{
|
||||
Euid = euid;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,5 @@
|
||||
namespace Content.Shared.Alert
|
||||
{
|
||||
/// <summary>
|
||||
/// Every category of alert. Corresponds to category field in alert prototypes defined in YML
|
||||
/// </summary>
|
||||
public enum AlertCategory
|
||||
{
|
||||
Pressure,
|
||||
Temperature,
|
||||
Breathing,
|
||||
Buckled,
|
||||
Health,
|
||||
Piloting,
|
||||
Hunger,
|
||||
Thirst
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
|
||||
18
Content.Shared/Alert/AlertsComponent.cs
Normal file
18
Content.Shared/Alert/AlertsComponent.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.Alert;
|
||||
|
||||
/// <summary>
|
||||
/// Handles the icons on the right side of the screen.
|
||||
/// Should only be used for player-controlled entities.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[NetworkedComponent]
|
||||
[ComponentProtoName("Alerts")]
|
||||
public class AlertsComponent : Component
|
||||
{
|
||||
[ViewVariables] public Dictionary<AlertKey, AlertState> Alerts = new();
|
||||
}
|
||||
17
Content.Shared/Alert/AlertsComponentState.cs
Normal file
17
Content.Shared/Alert/AlertsComponentState.cs
Normal file
@@ -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<AlertKey, AlertState> Alerts;
|
||||
|
||||
public AlertsComponentState(Dictionary<AlertKey, AlertState> alerts)
|
||||
{
|
||||
Alerts = alerts;
|
||||
}
|
||||
}
|
||||
237
Content.Shared/Alert/AlertsSystem.cs
Normal file
237
Content.Shared/Alert/AlertsSystem.cs
Normal file
@@ -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<AlertType, AlertPrototype> _typeToAlert = new();
|
||||
|
||||
public IReadOnlyDictionary<AlertKey, AlertState>? 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;
|
||||
}
|
||||
|
||||
/// <returns>true iff an alert of the indicated alert category is currently showing</returns>
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="euid"></param>
|
||||
/// <param name="alertType">type of the alert to set</param>
|
||||
/// <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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear the alert with the given category, if one is currently showing.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear the alert of the given type if it is currently showing.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked after showing an alert prior to dirtying the component
|
||||
/// </summary>
|
||||
/// <param name="alertsComponent"></param>
|
||||
protected virtual void AfterShowAlert(AlertsComponent alertsComponent) { }
|
||||
|
||||
/// <summary>
|
||||
/// Invoked after clearing an alert prior to dirtying the component
|
||||
/// </summary>
|
||||
/// <param name="alertsComponent"></param>
|
||||
protected virtual void AfterClearAlert(AlertsComponent alertsComponent) { }
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<AlertsComponent, ComponentStartup>((uid, _, _) => RaiseLocalEvent(uid, new AlertSyncEvent(uid)));
|
||||
SubscribeLocalEvent<AlertsComponent, ComponentShutdown>((uid, _, _) => HandleComponentShutdown(uid));
|
||||
|
||||
SubscribeLocalEvent<AlertsComponent, ComponentGetState>(ClientAlertsGetState);
|
||||
SubscribeNetworkEvent<ClickAlertEvent>(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<AlertPrototype>())
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the alert of the indicated type
|
||||
/// </summary>
|
||||
/// <returns>true if found</returns>
|
||||
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<AlertsComponent>(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<MetaDataComponent>(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);
|
||||
}
|
||||
}
|
||||
19
Content.Shared/Alert/ClickAlertEvent.cs
Normal file
19
Content.Shared/Alert/ClickAlertEvent.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Alert;
|
||||
|
||||
/// <summary>
|
||||
/// A message that calls the click interaction on a alert
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public class ClickAlertEvent : EntityEventArgs
|
||||
{
|
||||
public readonly AlertType Type;
|
||||
|
||||
public ClickAlertEvent(AlertType alertType)
|
||||
{
|
||||
Type = alertType;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
/// <summary>
|
||||
/// Invoked on server side when user clicks an alert.
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
void AlertClicked(ClickAlertEventArgs args);
|
||||
}
|
||||
|
||||
public class ClickAlertEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Player clicking the alert
|
||||
/// </summary>
|
||||
public readonly EntityUid Player;
|
||||
/// <summary>
|
||||
/// Alert that was clicked
|
||||
/// </summary>
|
||||
public readonly AlertPrototype Alert;
|
||||
|
||||
public ClickAlertEventArgs(EntityUid player, AlertPrototype alert)
|
||||
{
|
||||
Player = player;
|
||||
Alert = alert;
|
||||
}
|
||||
/// <param name="player"></param>
|
||||
void AlertClicked(EntityUid player);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles the icons on the right side of the screen.
|
||||
/// Should only be used for player-controlled entities.
|
||||
/// </summary>
|
||||
[NetworkedComponent()]
|
||||
public abstract class SharedAlertsComponent : Component
|
||||
{
|
||||
[Dependency]
|
||||
protected readonly AlertManager AlertManager = default!;
|
||||
|
||||
public override string Name => "Alerts";
|
||||
|
||||
[ViewVariables] private Dictionary<AlertKey, AlertState> _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);
|
||||
}
|
||||
|
||||
/// <returns>true iff an alert of the indicated alert category is currently showing</returns>
|
||||
public bool IsShowingAlertCategory(AlertCategory alertCategory)
|
||||
{
|
||||
return IsShowingAlert(AlertKey.ForCategory(alertCategory));
|
||||
}
|
||||
|
||||
/// <returns>true iff an alert of the indicated id is currently showing</returns>
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
/// <returns>true iff an alert of the indicated key is currently showing</returns>
|
||||
protected bool IsShowingAlert(AlertKey alertKey)
|
||||
{
|
||||
return _alerts.ContainsKey(alertKey);
|
||||
}
|
||||
|
||||
protected IEnumerable<KeyValuePair<AlertKey, AlertState>> EnumerateAlertStates()
|
||||
{
|
||||
return _alerts;
|
||||
}
|
||||
|
||||
protected bool TryGetAlertState(AlertKey key, out AlertState alertState)
|
||||
{
|
||||
return _alerts.TryGetValue(key, out alertState);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="alertType">type of the alert to set</param>
|
||||
/// <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(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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear the alert with the given category, if one is currently showing.
|
||||
/// </summary>
|
||||
public void ClearAlertCategory(AlertCategory category)
|
||||
{
|
||||
var key = AlertKey.ForCategory(category);
|
||||
if (!_alerts.Remove(key))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AfterClearAlert();
|
||||
|
||||
Dirty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear the alert of the given type if it is currently showing.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked after showing an alert prior to dirtying the component
|
||||
/// </summary>
|
||||
protected virtual void AfterShowAlert() { }
|
||||
|
||||
/// <summary>
|
||||
/// Invoked after clearing an alert prior to dirtying the component
|
||||
/// </summary>
|
||||
protected virtual void AfterClearAlert() { }
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class AlertsComponentState : ComponentState
|
||||
{
|
||||
public Dictionary<AlertKey, AlertState> Alerts;
|
||||
|
||||
public AlertsComponentState(Dictionary<AlertKey, AlertState> alerts)
|
||||
{
|
||||
Alerts = alerts;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A message that calls the click interaction on a alert
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
/// <param name="frequency">Frequency for jittering. See <see cref="MaxFrequency"/> and <see cref="MinFrequency"/>.</param>
|
||||
/// <param name="forceValueChange">Whether to change any existing jitter value even if they're greater than the ones we're setting.</param>
|
||||
/// <param name="status">The status effects component to modify.</param>
|
||||
/// <param name="alerts">The alerts component.</param>
|
||||
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<JitteringComponent>(uid, "Jitter", time, refresh, status, alerts))
|
||||
if (StatusEffects.TryAddStatusEffect<JitteringComponent>(uid, "Jitter", time, refresh, status))
|
||||
{
|
||||
var jittering = EntityManager.GetComponent<JitteringComponent>(uid);
|
||||
|
||||
|
||||
@@ -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<AlertsSystem>().ClearAlert(Owner, AlertType.HumanHealth);
|
||||
|
||||
base.OnRemove();
|
||||
}
|
||||
|
||||
@@ -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<AlertsSystem>().ShowAlert(uid, AlertType.HumanCrit); // TODO: combine humancrit-0 and humancrit-1 into a gif and display it
|
||||
|
||||
EntitySystem.Get<StandingStateSystem>().Down(uid);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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!;
|
||||
|
||||
/// <summary>
|
||||
/// 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)
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
/// <param name="time">How long the effect should last for.</param>
|
||||
/// <param name="refresh">The status effect cooldown should be refreshed (true) or accumulated (false).</param>
|
||||
/// <param name="status">The status effects component to change, if you already have it.</param>
|
||||
/// <param name="alerts">The alerts component to modify, if the status effect has an alert.</param>
|
||||
/// <returns>False if the effect could not be added or the component already exists, true otherwise.</returns>
|
||||
/// <typeparam name="T">The component type to add and remove from the entity.</typeparam>
|
||||
public bool TryAddStatusEffect<T>(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<T>(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
|
||||
/// <param name="time">How long the effect should last for.</param>
|
||||
/// <param name="refresh">The status effect cooldown should be refreshed (true) or accumulated (false).</param>
|
||||
/// <param name="status">The status effects component to change, if you already have it.</param>
|
||||
/// <param name="alerts">The alerts component to modify, if the status effect has an alert.</param>
|
||||
/// <returns>False if the effect could not be added, or if the effect already existed.</returns>
|
||||
/// <remarks>
|
||||
/// 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!
|
||||
/// </remarks>
|
||||
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<StatusEffectPrototype>(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
|
||||
/// <param name="uid">The entity to remove an effect from.</param>
|
||||
/// <param name="key">The effect ID to remove.</param>
|
||||
/// <param name="status">The status effects component to change, if you already have it.</param>
|
||||
/// <param name="alerts">The alerts component to modify, if the status effect has an alert.</param>
|
||||
/// <returns>False if the effect could not be removed, true otherwise.</returns>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
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<StatusEffectPrototype>(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
|
||||
/// </summary>
|
||||
/// <param name="uid">The entity to remove effects from.</param>
|
||||
/// <param name="status">The status effects component to change, if you already have it.</param>
|
||||
/// <param name="alerts">The alerts component to modify, if the status effect has an alert.</param>
|
||||
/// <returns>False if any status effects failed to be removed, true if they all did.</returns>
|
||||
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
|
||||
/// <param name="time">The amount of time to add.</param>
|
||||
/// <param name="status">The status effect component, should you already have it.</param>
|
||||
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<StatusEffectPrototype>(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
|
||||
/// <param name="time">The amount of time to add.</param>
|
||||
/// <param name="status">The status effect component, should you already have it.</param>
|
||||
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<StatusEffectPrototype>(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;
|
||||
|
||||
@@ -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.
|
||||
/// </summary>
|
||||
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<StunnedComponent>(uid, "Stun", time, refresh, alerts: alerts);
|
||||
return _statusEffectSystem.TryAddStatusEffect<StunnedComponent>(uid, "Stun", time, refresh);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Knocks down the entity, making it fall to the ground.
|
||||
/// </summary>
|
||||
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<KnockedDownComponent>(uid, "KnockedDown", time, refresh, alerts: alerts);
|
||||
return _statusEffectSystem.TryAddStatusEffect<KnockedDownComponent>(uid, "KnockedDown", time, refresh);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies knockdown and stun to the entity temporarily.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -172,19 +161,15 @@ namespace Content.Shared.Stunnable
|
||||
/// </summary>
|
||||
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<SlowedDownComponent>(uid, "SlowedDown", time, refresh, status, alerts))
|
||||
if (_statusEffectSystem.TryAddStatusEffect<SlowedDownComponent>(uid, "SlowedDown", time, refresh, status))
|
||||
{
|
||||
var slowed = EntityManager.GetComponent<SlowedDownComponent>(uid);
|
||||
// Doesn't make much sense to have the "TrySlowdown" method speed up entities now does it?
|
||||
|
||||
Reference in New Issue
Block a user