Merge remote-tracking branch 'upstream/master' into 20-10-30-admins
This commit is contained in:
124
Content.Shared/Alert/AlertManager.cs
Normal file
124
Content.Shared/Alert/AlertManager.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Shared.Prototypes.Kitchen;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Alert
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to all configured alerts. Ability to encode/decode a given state
|
||||
/// to an int.
|
||||
/// </summary>
|
||||
public class AlertManager
|
||||
{
|
||||
[Dependency]
|
||||
private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
private AlertPrototype[] _orderedAlerts;
|
||||
private Dictionary<AlertType, byte> _typeToIndex;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
// order by type value so we can map between the id and an integer index and use
|
||||
// the index for compact alert change messages
|
||||
_orderedAlerts =
|
||||
_prototypeManager.EnumeratePrototypes<AlertPrototype>()
|
||||
.OrderBy(prototype => prototype.AlertType).ToArray();
|
||||
_typeToIndex = new Dictionary<AlertType, byte>();
|
||||
|
||||
for (var i = 0; i < _orderedAlerts.Length; i++)
|
||||
{
|
||||
if (i > byte.MaxValue)
|
||||
{
|
||||
Logger.ErrorS("alert", "too many alerts for byte encoding ({0})! encoding will need" +
|
||||
" to be changed to use a ushort rather than byte", _typeToIndex.Count);
|
||||
break;
|
||||
}
|
||||
if (!_typeToIndex.TryAdd(_orderedAlerts[i].AlertType, (byte) i))
|
||||
{
|
||||
Logger.ErrorS("alert",
|
||||
"Found alert with duplicate id {0}", _orderedAlerts[i].AlertType);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the alert of the indicated type
|
||||
/// </summary>
|
||||
/// <returns>true if found</returns>
|
||||
public bool TryGet(AlertType alertType, out AlertPrototype alert)
|
||||
{
|
||||
if (_typeToIndex.TryGetValue(alertType, out var idx))
|
||||
{
|
||||
alert = _orderedAlerts[idx];
|
||||
return true;
|
||||
}
|
||||
|
||||
alert = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the alert of the indicated type along with its encoding
|
||||
/// </summary>
|
||||
/// <returns>true if found</returns>
|
||||
public bool TryGetWithEncoded(AlertType alertType, out AlertPrototype alert, out byte encoded)
|
||||
{
|
||||
if (_typeToIndex.TryGetValue(alertType, out var idx))
|
||||
{
|
||||
alert = _orderedAlerts[idx];
|
||||
encoded = (byte) idx;
|
||||
return true;
|
||||
}
|
||||
|
||||
alert = null;
|
||||
encoded = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the compact encoded representation of this alert
|
||||
/// </summary>
|
||||
/// <returns>true if successful</returns>
|
||||
public bool TryEncode(AlertPrototype alert, out byte encoded)
|
||||
{
|
||||
return TryEncode(alert.AlertType, out encoded);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the compact encoded representation of the alert with
|
||||
/// the indicated id
|
||||
/// </summary>
|
||||
/// <returns>true if successful</returns>
|
||||
public bool TryEncode(AlertType alertType, out byte encoded)
|
||||
{
|
||||
if (_typeToIndex.TryGetValue(alertType, out var idx))
|
||||
{
|
||||
encoded = idx;
|
||||
return true;
|
||||
}
|
||||
|
||||
encoded = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the alert from the encoded representation
|
||||
/// </summary>
|
||||
/// <returns>true if successful</returns>
|
||||
public bool TryDecode(byte encodedAlert, out AlertPrototype alert)
|
||||
{
|
||||
if (encodedAlert >= _orderedAlerts.Length)
|
||||
{
|
||||
alert = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
alert = _orderedAlerts[encodedAlert];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
81
Content.Shared/Alert/AlertOrderPrototype.cs
Normal file
81
Content.Shared/Alert/AlertOrderPrototype.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Shared.Alert
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the order of alerts so they show up in a consistent order.
|
||||
/// </summary>
|
||||
[Prototype("alertOrder")]
|
||||
public class AlertOrderPrototype : IPrototype, IComparer<AlertPrototype>
|
||||
{
|
||||
private Dictionary<AlertType, int> _typeToIdx = new Dictionary<AlertType, int>();
|
||||
private Dictionary<AlertCategory, int> _categoryToIdx = new Dictionary<AlertCategory, int>();
|
||||
|
||||
public void LoadFrom(YamlMappingNode mapping)
|
||||
{
|
||||
if (!mapping.TryGetNode("order", out YamlSequenceNode orderMapping)) return;
|
||||
|
||||
int i = 0;
|
||||
foreach (var entryYaml in orderMapping)
|
||||
{
|
||||
var orderEntry = (YamlMappingNode) entryYaml;
|
||||
var serializer = YamlObjectSerializer.NewReader(orderEntry);
|
||||
if (serializer.TryReadDataField("category", out AlertCategory alertCategory))
|
||||
{
|
||||
_categoryToIdx[alertCategory] = i++;
|
||||
}
|
||||
else if (serializer.TryReadDataField("alertType", out AlertType alertType))
|
||||
{
|
||||
_typeToIdx[alertType] = i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int GetOrderIndex(AlertPrototype alert)
|
||||
{
|
||||
if (_typeToIdx.TryGetValue(alert.AlertType, out var idx))
|
||||
{
|
||||
return idx;
|
||||
}
|
||||
if (alert.Category != null &&
|
||||
_categoryToIdx.TryGetValue((AlertCategory) alert.Category, out idx))
|
||||
{
|
||||
return idx;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int Compare(AlertPrototype x, AlertPrototype y)
|
||||
{
|
||||
if ((x == null) && (y == null)) return 0;
|
||||
if (x == null) return 1;
|
||||
if (y == null) return -1;
|
||||
var idx = GetOrderIndex(x);
|
||||
var idy = GetOrderIndex(y);
|
||||
if (idx == -1 && idy == -1)
|
||||
{
|
||||
// break ties by type value
|
||||
return x.AlertType - y.AlertType;
|
||||
}
|
||||
|
||||
if (idx == -1) return 1;
|
||||
if (idy == -1) return -1;
|
||||
var result = idx - idy;
|
||||
// not strictly necessary (we don't care about ones that go at the same index)
|
||||
// but it makes the sort stable
|
||||
if (result == 0)
|
||||
{
|
||||
// break ties by type value
|
||||
return x.AlertType - y.AlertType;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
189
Content.Shared/Alert/AlertPrototype.cs
Normal file
189
Content.Shared/Alert/AlertPrototype.cs
Normal file
@@ -0,0 +1,189 @@
|
||||
using System;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Shared.Alert
|
||||
{
|
||||
/// <summary>
|
||||
/// An alert popup with associated icon, tooltip, and other data.
|
||||
/// </summary>
|
||||
[Prototype("alert")]
|
||||
public class AlertPrototype : IPrototype
|
||||
{
|
||||
/// <summary>
|
||||
/// Type of alert, no 2 alert prototypes should have the same one.
|
||||
/// </summary>
|
||||
public AlertType AlertType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Path to the icon (png) to show in alert bar. If severity levels are supported,
|
||||
/// this should be the path to the icon without the severity number
|
||||
/// (i.e. hot.png if there is hot1.png and hot2.png). Use <see cref="GetIconPath"/>
|
||||
/// to get the correct icon path for a particular severity level.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public string IconPath { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name to show in tooltip window. Accepts formatting.
|
||||
/// </summary>
|
||||
public FormattedMessage Name { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Description to show in tooltip window. Accepts formatting.
|
||||
/// </summary>
|
||||
public FormattedMessage Description { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Category the alert belongs to. Only one alert of a given category
|
||||
/// can be shown at a time. If one is shown while another is already being shown,
|
||||
/// it will be replaced. This can be useful for categories of alerts which should naturally
|
||||
/// replace each other and are mutually exclusive, for example lowpressure / highpressure,
|
||||
/// hot / cold. If left unspecified, the alert will not replace or be replaced by any other alerts.
|
||||
/// </summary>
|
||||
public AlertCategory? Category { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Key which is unique w.r.t category semantics (alerts with same category have equal keys,
|
||||
/// alerts with no category have different keys).
|
||||
/// </summary>
|
||||
public AlertKey AlertKey { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// -1 (no effect) unless MaxSeverity is specified. Defaults to 1. Minimum severity level supported by this state.
|
||||
/// </summary>
|
||||
public short MinSeverity => MaxSeverity == -1 ? (short) -1 : _minSeverity;
|
||||
private short _minSeverity;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum severity level supported by this state. -1 (default) indicates
|
||||
/// no severity levels are supported by the state.
|
||||
/// </summary>
|
||||
public short MaxSeverity { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether this state support severity levels
|
||||
/// </summary>
|
||||
public bool SupportsSeverity => MaxSeverity != -1;
|
||||
|
||||
public void LoadFrom(YamlMappingNode mapping)
|
||||
{
|
||||
var serializer = YamlObjectSerializer.NewReader(mapping);
|
||||
|
||||
serializer.DataField(this, x => x.IconPath, "icon", string.Empty);
|
||||
serializer.DataField(this, x => x.MaxSeverity, "maxSeverity", (short) -1);
|
||||
serializer.DataField(ref _minSeverity, "minSeverity", (short) 1);
|
||||
|
||||
serializer.DataReadFunction("name", string.Empty,
|
||||
s => Name = FormattedMessage.FromMarkup(s));
|
||||
serializer.DataReadFunction("description", string.Empty,
|
||||
s => Description = FormattedMessage.FromMarkup(s));
|
||||
|
||||
serializer.DataField(this, x => x.AlertType, "alertType", AlertType.Error);
|
||||
if (AlertType == AlertType.Error)
|
||||
{
|
||||
Logger.ErrorS("alert", "missing or invalid alertType for alert with name {0}", Name);
|
||||
}
|
||||
|
||||
if (serializer.TryReadDataField("category", out AlertCategory alertCategory))
|
||||
{
|
||||
Category = alertCategory;
|
||||
}
|
||||
AlertKey = new AlertKey(AlertType, Category);
|
||||
}
|
||||
|
||||
/// <param name="severity">severity level, if supported by this alert</param>
|
||||
/// <returns>the icon path to the texture for the provided severity level</returns>
|
||||
public string GetIconPath(short? severity = null)
|
||||
{
|
||||
if (!SupportsSeverity && severity != null)
|
||||
{
|
||||
Logger.WarningS("alert", "attempted to get icon path for severity level for alert {0}, but" +
|
||||
" this alert does not support severity levels", AlertType);
|
||||
}
|
||||
if (!SupportsSeverity) return IconPath;
|
||||
if (severity == null)
|
||||
{
|
||||
Logger.WarningS("alert", "attempted to get icon path without severity level for alert {0}," +
|
||||
" but this alert requires a severity level. Using lowest" +
|
||||
" valid severity level instead...", AlertType);
|
||||
severity = MinSeverity;
|
||||
}
|
||||
|
||||
if (severity < MinSeverity)
|
||||
{
|
||||
Logger.WarningS("alert", "attempted to get icon path with severity level {0} for alert {1}," +
|
||||
" but the minimum severity level for this alert is {2}. Using" +
|
||||
" lowest valid severity level instead...", severity, AlertType, MinSeverity);
|
||||
severity = MinSeverity;
|
||||
}
|
||||
if (severity > MaxSeverity)
|
||||
{
|
||||
Logger.WarningS("alert", "attempted to get icon path with severity level {0} for alert {1}," +
|
||||
" but the max severity level for this alert is {2}. Using" +
|
||||
" highest valid severity level instead...", severity, AlertType, MaxSeverity);
|
||||
severity = MaxSeverity;
|
||||
}
|
||||
|
||||
// split and add the severity number to the path
|
||||
var ext = IconPath.LastIndexOf('.');
|
||||
return IconPath.Substring(0, ext) + severity + IconPath.Substring(ext, IconPath.Length - ext);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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
|
||||
{
|
||||
private readonly AlertType? _alertType;
|
||||
private 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 ID, and you
|
||||
/// compare this to another AlertKey that has both the category and the same ID, it will not consider them equal.
|
||||
public AlertKey(AlertType? alertType, AlertCategory? alertCategory)
|
||||
{
|
||||
// if there is a category, ignore the alerttype.
|
||||
if (alertCategory != null)
|
||||
{
|
||||
_alertCategory = alertCategory;
|
||||
_alertType = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
_alertCategory = null;
|
||||
_alertType = alertType;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Equals(AlertKey other)
|
||||
{
|
||||
return _alertType == other._alertType && _alertCategory == other._alertCategory;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is AlertKey other && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(_alertType, _alertCategory);
|
||||
}
|
||||
|
||||
/// <param name="category">alert category, must not be null</param>
|
||||
/// <returns>An alert key for the provided alert category</returns>
|
||||
public static AlertKey ForCategory(AlertCategory category)
|
||||
{
|
||||
return new AlertKey(null, category);
|
||||
}
|
||||
}
|
||||
}
|
||||
52
Content.Shared/Alert/AlertType.cs
Normal file
52
Content.Shared/Alert/AlertType.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
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,
|
||||
Buckled,
|
||||
Health,
|
||||
Piloting,
|
||||
Hunger,
|
||||
Thirst
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Every kind of alert. Corresponds to alertType field in alert prototypes defined in YML
|
||||
/// </summary>
|
||||
public enum AlertType
|
||||
{
|
||||
Error,
|
||||
LowPressure,
|
||||
HighPressure,
|
||||
Fire,
|
||||
Cold,
|
||||
Hot,
|
||||
Weightless,
|
||||
Stun,
|
||||
Handcuffed,
|
||||
Buckled,
|
||||
HumanCrit,
|
||||
HumanDead,
|
||||
HumanHealth,
|
||||
PilotingShuttle,
|
||||
Overfed,
|
||||
Peckish,
|
||||
Starving,
|
||||
Overhydrated,
|
||||
Thirsty,
|
||||
Parched,
|
||||
Pulled,
|
||||
Pulling,
|
||||
Debug1,
|
||||
Debug2,
|
||||
Debug3,
|
||||
Debug4,
|
||||
Debug5,
|
||||
Debug6
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,6 +7,24 @@ namespace Content.Shared
|
||||
[CVarDefs]
|
||||
public sealed class CCVars : CVars
|
||||
{
|
||||
/*
|
||||
* Status
|
||||
*/
|
||||
|
||||
public static readonly CVarDef<string> StatusMoMMIUrl =
|
||||
CVarDef.Create<string>("status.mommiurl", null);
|
||||
|
||||
public static readonly CVarDef<string> StatusMoMMIPassword =
|
||||
CVarDef.Create<string>("status.mommipassword", null);
|
||||
|
||||
|
||||
/*
|
||||
* Game
|
||||
*/
|
||||
|
||||
public static readonly CVarDef<bool>
|
||||
EventsEnabled = CVarDef.Create("events.enabled", false, CVar.ARCHIVE | CVar.SERVERONLY);
|
||||
|
||||
public static readonly CVarDef<bool>
|
||||
GameLobbyEnabled = CVarDef.Create("game.lobbyenabled", false, CVar.ARCHIVE);
|
||||
|
||||
@@ -34,6 +52,25 @@ namespace Content.Shared
|
||||
public static readonly CVarDef<bool>
|
||||
GamePersistGuests = CVarDef.Create("game.persistguests", true, CVar.ARCHIVE | CVar.SERVERONLY);
|
||||
|
||||
public static readonly CVarDef<int> GameSuspicionMinPlayers =
|
||||
CVarDef.Create("game.suspicion_min_players", 5);
|
||||
|
||||
public static readonly CVarDef<int> GameSuspicionMinTraitors =
|
||||
CVarDef.Create("game.suspicion_min_traitors", 2);
|
||||
|
||||
public static readonly CVarDef<int> GameSuspicionPlayersPerTraitor =
|
||||
CVarDef.Create("game.suspicion_players_per_traitor", 5);
|
||||
|
||||
public static readonly CVarDef<int> GameSuspicionStartingBalance =
|
||||
CVarDef.Create("game.suspicion_starting_balance", 20);
|
||||
|
||||
public static readonly CVarDef<bool> GameDiagonalMovement =
|
||||
CVarDef.Create("game.diagonalmovement", true, CVar.ARCHIVE);
|
||||
|
||||
/*
|
||||
* Console
|
||||
*/
|
||||
|
||||
public static readonly CVarDef<bool>
|
||||
ConsoleLoginLocal = CVarDef.Create("console.loginlocal", true, CVar.ARCHIVE | CVar.SERVERONLY);
|
||||
|
||||
@@ -63,6 +100,44 @@ namespace Content.Shared
|
||||
public static readonly CVarDef<string> DatabasePgPassword =
|
||||
CVarDef.Create("database.pg_password", "", CVar.SERVERONLY);
|
||||
|
||||
|
||||
/*
|
||||
* Outline
|
||||
*/
|
||||
|
||||
public static readonly CVarDef<bool> OutlineEnabled =
|
||||
CVarDef.Create("outline.enabled", true, CVar.CLIENTONLY);
|
||||
|
||||
|
||||
/*
|
||||
* Parallax
|
||||
*/
|
||||
|
||||
public static readonly CVarDef<bool> ParallaxEnabled =
|
||||
CVarDef.Create("parallax.enabled", true);
|
||||
|
||||
public static readonly CVarDef<bool> ParallaxDebug =
|
||||
CVarDef.Create("parallax.debug", true);
|
||||
|
||||
|
||||
/*
|
||||
* AI
|
||||
*/
|
||||
|
||||
public static readonly CVarDef<int> AIMaxUpdates =
|
||||
CVarDef.Create("ai.maxupdates", 64);
|
||||
|
||||
|
||||
/*
|
||||
* Net
|
||||
*/
|
||||
|
||||
public static readonly CVarDef<float> NetAtmosDebugOverlayTickRate =
|
||||
CVarDef.Create("net.atmosdbgoverlaytickrate", 3.0f);
|
||||
|
||||
public static readonly CVarDef<float> NetGasOverlayTickRate =
|
||||
CVarDef.Create("net.gasoverlaytickrate", 3.0f);
|
||||
|
||||
/*
|
||||
* Admin stuff
|
||||
*/
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
using Content.Shared.GameObjects.Components.Body.Mechanism;
|
||||
using Content.Shared.GameObjects.Components.Body.Part;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Body.Behavior
|
||||
{
|
||||
public interface IMechanismBehavior : IComponent
|
||||
public interface IMechanismBehavior : IExposeData
|
||||
{
|
||||
IBody? Body { get; }
|
||||
|
||||
@@ -15,7 +16,20 @@ namespace Content.Shared.GameObjects.Components.Body.Behavior
|
||||
/// Upward reference to the parent <see cref="IMechanism"/> that this
|
||||
/// behavior is attached to.
|
||||
/// </summary>
|
||||
IMechanism? Mechanism { get; }
|
||||
IMechanism Parent { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The entity that owns <see cref="Parent"/>.
|
||||
/// For the entity owning the body that this mechanism may be in,
|
||||
/// see <see cref="IBody.Owner"/>
|
||||
/// </summary>
|
||||
IEntity Owner { get; }
|
||||
|
||||
void Initialize(IMechanism parent);
|
||||
|
||||
void Startup();
|
||||
|
||||
void Update(float frameTime);
|
||||
|
||||
/// <summary>
|
||||
/// Called when the containing <see cref="IBodyPart"/> is attached to a
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
#nullable enable
|
||||
using Content.Shared.GameObjects.Components.Body.Mechanism;
|
||||
using Content.Shared.GameObjects.Components.Body.Part;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Body.Behavior
|
||||
{
|
||||
public abstract class MechanismBehaviorComponent : Component, IMechanismBehavior
|
||||
{
|
||||
public IBody? Body => Part?.Body;
|
||||
|
||||
public IBodyPart? Part => Mechanism?.Part;
|
||||
|
||||
public IMechanism? Mechanism => Owner.GetComponentOrNull<IMechanism>();
|
||||
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
if (Part == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Body == null)
|
||||
{
|
||||
AddedToPart(Part);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddedToPartInBody(Body, Part);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void Update(float frameTime);
|
||||
|
||||
public void AddedToBody(IBody body)
|
||||
{
|
||||
DebugTools.AssertNotNull(Body);
|
||||
DebugTools.AssertNotNull(body);
|
||||
|
||||
OnAddedToBody(body);
|
||||
}
|
||||
|
||||
public void AddedToPart(IBodyPart part)
|
||||
{
|
||||
DebugTools.AssertNotNull(Part);
|
||||
DebugTools.AssertNotNull(part);
|
||||
|
||||
OnAddedToPart(part);
|
||||
}
|
||||
|
||||
public void AddedToPartInBody(IBody body, IBodyPart part)
|
||||
{
|
||||
DebugTools.AssertNotNull(Body);
|
||||
DebugTools.AssertNotNull(body);
|
||||
DebugTools.AssertNotNull(Part);
|
||||
DebugTools.AssertNotNull(part);
|
||||
|
||||
OnAddedToPartInBody(body, part);
|
||||
}
|
||||
|
||||
public void RemovedFromBody(IBody old)
|
||||
{
|
||||
DebugTools.AssertNull(Body);
|
||||
DebugTools.AssertNotNull(old);
|
||||
|
||||
OnRemovedFromBody(old);
|
||||
}
|
||||
|
||||
public void RemovedFromPart(IBodyPart old)
|
||||
{
|
||||
DebugTools.AssertNull(Part);
|
||||
DebugTools.AssertNotNull(old);
|
||||
|
||||
OnRemovedFromPart(old);
|
||||
}
|
||||
|
||||
public void RemovedFromPartInBody(IBody oldBody, IBodyPart oldPart)
|
||||
{
|
||||
DebugTools.AssertNull(Body);
|
||||
DebugTools.AssertNull(Part);
|
||||
DebugTools.AssertNotNull(oldBody);
|
||||
DebugTools.AssertNotNull(oldPart);
|
||||
|
||||
OnRemovedFromPartInBody(oldBody, oldPart);
|
||||
}
|
||||
|
||||
protected virtual void OnAddedToBody(IBody body) { }
|
||||
|
||||
protected virtual void OnAddedToPart(IBodyPart part) { }
|
||||
|
||||
protected virtual void OnAddedToPartInBody(IBody body, IBodyPart part) { }
|
||||
|
||||
protected virtual void OnRemovedFromBody(IBody old) { }
|
||||
|
||||
protected virtual void OnRemovedFromPart(IBodyPart old) { }
|
||||
|
||||
protected virtual void OnRemovedFromPartInBody(IBody oldBody, IBodyPart oldPart) { }
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#nullable enable
|
||||
namespace Content.Shared.GameObjects.Components.Body.Behavior
|
||||
{
|
||||
public abstract class SharedHeartBehaviorComponent : MechanismBehaviorComponent
|
||||
{
|
||||
public override string Name => "Heart";
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
#nullable enable
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Body.Behavior
|
||||
{
|
||||
public abstract class SharedLungBehaviorComponent : MechanismBehaviorComponent
|
||||
{
|
||||
public override string Name => "Lung";
|
||||
|
||||
[ViewVariables] public abstract float Temperature { get; }
|
||||
|
||||
[ViewVariables] public abstract float Volume { get; }
|
||||
|
||||
[ViewVariables] public LungStatus Status { get; set; }
|
||||
|
||||
[ViewVariables] public float CycleDelay { get; set; }
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
serializer.DataField(this, l => l.CycleDelay, "cycleDelay", 2);
|
||||
}
|
||||
|
||||
public abstract void Inhale(float frameTime);
|
||||
|
||||
public abstract void Exhale(float frameTime);
|
||||
|
||||
public abstract void Gasp();
|
||||
}
|
||||
|
||||
public enum LungStatus
|
||||
{
|
||||
None = 0,
|
||||
Inhaling,
|
||||
Exhaling
|
||||
}
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.GameObjects.Components.Body.Networks;
|
||||
using Content.Shared.GameObjects.Components.Chemistry;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Body.Behavior
|
||||
{
|
||||
/// <summary>
|
||||
/// Where reagents go when ingested. Tracks ingested reagents over time, and
|
||||
/// eventually transfers them to <see cref="SharedBloodstreamComponent"/> once digested.
|
||||
/// </summary>
|
||||
public abstract class SharedStomachBehaviorComponent : MechanismBehaviorComponent
|
||||
{
|
||||
public override string Name => "Stomach";
|
||||
|
||||
private float _accumulatedFrameTime;
|
||||
|
||||
/// <summary>
|
||||
/// Updates digestion status of ingested reagents.
|
||||
/// Once reagents surpass _digestionDelay they are moved to the
|
||||
/// bloodstream, where they are then metabolized.
|
||||
/// </summary>
|
||||
/// <param name="frameTime">
|
||||
/// The time since the last update in seconds.
|
||||
/// </param>
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
if (Body == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_accumulatedFrameTime += frameTime;
|
||||
|
||||
// Update at most once per second
|
||||
if (_accumulatedFrameTime < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_accumulatedFrameTime -= 1;
|
||||
|
||||
if (!Body.Owner.TryGetComponent(out SharedSolutionContainerComponent? solution) ||
|
||||
!Body.Owner.TryGetComponent(out SharedBloodstreamComponent? bloodstream))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Add reagents ready for transfer to bloodstream to transferSolution
|
||||
var transferSolution = new Solution();
|
||||
|
||||
// Use ToList here to remove entries while iterating
|
||||
foreach (var delta in _reagentDeltas.ToList())
|
||||
{
|
||||
//Increment lifetime of reagents
|
||||
delta.Increment(frameTime);
|
||||
if (delta.Lifetime > _digestionDelay)
|
||||
{
|
||||
solution.TryRemoveReagent(delta.ReagentId, delta.Quantity);
|
||||
transferSolution.AddReagent(delta.ReagentId, delta.Quantity);
|
||||
_reagentDeltas.Remove(delta);
|
||||
}
|
||||
}
|
||||
|
||||
// Transfer digested reagents to bloodstream
|
||||
bloodstream.TryTransferSolution(transferSolution);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Max volume of internal solution storage
|
||||
/// </summary>
|
||||
public ReagentUnit MaxVolume
|
||||
{
|
||||
get => Owner.TryGetComponent(out SharedSolutionContainerComponent? solution) ? solution.MaxVolume : ReagentUnit.Zero;
|
||||
set
|
||||
{
|
||||
if (Owner.TryGetComponent(out SharedSolutionContainerComponent? solution))
|
||||
{
|
||||
solution.MaxVolume = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initial internal solution storage volume
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
protected ReagentUnit InitialMaxVolume { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Time in seconds between reagents being ingested and them being
|
||||
/// transferred to <see cref="SharedBloodstreamComponent"/>
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
private float _digestionDelay;
|
||||
|
||||
/// <summary>
|
||||
/// Used to track how long each reagent has been in the stomach
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
private readonly List<ReagentDelta> _reagentDeltas = new List<ReagentDelta>();
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
serializer.DataField(this, s => s.InitialMaxVolume, "maxVolume", ReagentUnit.New(100));
|
||||
serializer.DataField(ref _digestionDelay, "digestionDelay", 20);
|
||||
}
|
||||
|
||||
public bool CanTransferSolution(Solution solution)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out SharedSolutionContainerComponent? solutionComponent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: For now no partial transfers. Potentially change by design
|
||||
if (!solutionComponent.CanAddSolution(solution))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryTransferSolution(Solution solution)
|
||||
{
|
||||
if (!CanTransferSolution(solution))
|
||||
return false;
|
||||
|
||||
if (!Owner.TryGetComponent(out SharedSolutionContainerComponent? solutionComponent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add solution to _stomachContents
|
||||
solutionComponent.TryAddSolution(solution, false, true);
|
||||
// Add each reagent to _reagentDeltas. Used to track how long each reagent has been in the stomach
|
||||
foreach (var reagent in solution.Contents)
|
||||
{
|
||||
_reagentDeltas.Add(new ReagentDelta(reagent.ReagentId, reagent.Quantity));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to track quantity changes when ingesting & digesting reagents
|
||||
/// </summary>
|
||||
protected class ReagentDelta
|
||||
{
|
||||
public readonly string ReagentId;
|
||||
public readonly ReagentUnit Quantity;
|
||||
public float Lifetime { get; private set; }
|
||||
|
||||
public ReagentDelta(string reagentId, ReagentUnit quantity)
|
||||
{
|
||||
ReagentId = reagentId;
|
||||
Quantity = quantity;
|
||||
Lifetime = 0.0f;
|
||||
}
|
||||
|
||||
public void Increment(float delta) => Lifetime += delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.GameObjects.Components.Body.Behavior;
|
||||
using Content.Shared.GameObjects.Components.Body.Part;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
|
||||
@@ -10,6 +13,8 @@ namespace Content.Shared.GameObjects.Components.Body.Mechanism
|
||||
|
||||
IBodyPart? Part { get; set; }
|
||||
|
||||
IReadOnlyDictionary<Type, IMechanismBehavior> Behaviors { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Professional description of the <see cref="IMechanism"/>.
|
||||
/// </summary>
|
||||
@@ -55,6 +60,21 @@ namespace Content.Shared.GameObjects.Components.Body.Mechanism
|
||||
/// </summary>
|
||||
BodyPartCompatibility Compatibility { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds a behavior if it does not exist already.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The behavior type to add.</typeparam>
|
||||
/// <returns>
|
||||
/// True if the behavior already existed, false if it had to be created.
|
||||
/// </returns>
|
||||
bool EnsureBehavior<T>(out T behavior) where T : IMechanismBehavior, new();
|
||||
|
||||
bool HasBehavior<T>() where T : IMechanismBehavior;
|
||||
|
||||
bool TryRemoveBehavior<T>() where T : IMechanismBehavior;
|
||||
|
||||
void Update(float frameTime);
|
||||
|
||||
// TODO BODY Turn these into event listeners so they dont need to be exposed
|
||||
/// <summary>
|
||||
/// Called when the containing <see cref="IBodyPart"/> is attached to a
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Shared.GameObjects.Components.Body.Behavior;
|
||||
using Content.Shared.GameObjects.Components.Body.Part;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Body.Mechanism
|
||||
{
|
||||
public static class MechanismExtensions
|
||||
{
|
||||
public static bool HasMechanismBehavior<T>(this IBody body)
|
||||
{
|
||||
return body.Parts.Values.Any(p => p.HasMechanismBehavior<T>());
|
||||
}
|
||||
|
||||
public static bool HasMechanismBehavior<T>(this IBodyPart part)
|
||||
{
|
||||
return part.Mechanisms.Any(m => m.Owner.HasComponent<T>());
|
||||
}
|
||||
|
||||
public static bool HasMechanismBehavior<T>(this IMechanism mechanism)
|
||||
{
|
||||
return mechanism.Owner.HasComponent<T>();
|
||||
}
|
||||
|
||||
public static IEnumerable<IMechanismBehavior> GetMechanismBehaviors(this IBody body)
|
||||
{
|
||||
foreach (var part in body.Parts.Values)
|
||||
foreach (var mechanism in part.Mechanisms)
|
||||
foreach (var behavior in mechanism.Owner.GetAllComponents<IMechanismBehavior>())
|
||||
{
|
||||
yield return behavior;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryGetMechanismBehaviors(this IBody body,
|
||||
[NotNullWhen(true)] out List<IMechanismBehavior>? behaviors)
|
||||
{
|
||||
behaviors = body.GetMechanismBehaviors().ToList();
|
||||
|
||||
if (behaviors.Count == 0)
|
||||
{
|
||||
behaviors = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static IEnumerable<T> GetMechanismBehaviors<T>(this IBody body) where T : class, IMechanismBehavior
|
||||
{
|
||||
foreach (var part in body.Parts.Values)
|
||||
foreach (var mechanism in part.Mechanisms)
|
||||
{
|
||||
if (mechanism.Owner.TryGetComponent(out T? behavior))
|
||||
{
|
||||
yield return behavior;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryGetMechanismBehaviors<T>(this IBody entity, [NotNullWhen(true)] out List<T>? behaviors)
|
||||
where T : class, IMechanismBehavior
|
||||
{
|
||||
behaviors = entity.GetMechanismBehaviors<T>().ToList();
|
||||
|
||||
if (behaviors.Count == 0)
|
||||
{
|
||||
behaviors = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Shared.GameObjects.Components.Body.Behavior;
|
||||
using Content.Shared.GameObjects.Components.Body.Part;
|
||||
using Content.Shared.Interfaces;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -14,11 +18,12 @@ namespace Content.Shared.GameObjects.Components.Body.Mechanism
|
||||
{
|
||||
public override string Name => "Mechanism";
|
||||
|
||||
private IBodyPart? _part;
|
||||
protected readonly Dictionary<int, object> OptionsCache = new Dictionary<int, object>();
|
||||
protected IBody? BodyCache;
|
||||
protected int IdHash;
|
||||
protected IEntity? PerformerCache;
|
||||
private IBodyPart? _part;
|
||||
private readonly Dictionary<Type, IMechanismBehavior> _behaviors = new Dictionary<Type, IMechanismBehavior>();
|
||||
|
||||
public IBody? Body => Part?.Body;
|
||||
|
||||
@@ -61,6 +66,8 @@ namespace Content.Shared.GameObjects.Components.Body.Mechanism
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<Type, IMechanismBehavior> Behaviors => _behaviors;
|
||||
|
||||
public string Description { get; set; } = string.Empty;
|
||||
|
||||
public string ExamineMessage { get; set; } = string.Empty;
|
||||
@@ -98,6 +105,91 @@ namespace Content.Shared.GameObjects.Components.Body.Mechanism
|
||||
serializer.DataField(this, m => m.Size, "size", 1);
|
||||
|
||||
serializer.DataField(this, m => m.Compatibility, "compatibility", BodyPartCompatibility.Universal);
|
||||
|
||||
var moduleManager = IoCManager.Resolve<IModuleManager>();
|
||||
|
||||
if (moduleManager.IsServerModule)
|
||||
{
|
||||
serializer.DataReadWriteFunction(
|
||||
"behaviors",
|
||||
null!,
|
||||
behaviors =>
|
||||
{
|
||||
if (behaviors == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var behavior in behaviors)
|
||||
{
|
||||
var type = behavior.GetType();
|
||||
|
||||
if (!_behaviors.TryAdd(type, behavior))
|
||||
{
|
||||
Logger.Warning($"Duplicate behavior in {nameof(SharedMechanismComponent)} for entity {Owner.Name}: {type}.");
|
||||
continue;
|
||||
}
|
||||
|
||||
IoCManager.InjectDependencies(behavior);
|
||||
}
|
||||
},
|
||||
() => _behaviors.Values.ToList());
|
||||
}
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
foreach (var behavior in _behaviors.Values)
|
||||
{
|
||||
behavior.Initialize(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
foreach (var behavior in _behaviors.Values)
|
||||
{
|
||||
behavior.Startup();
|
||||
}
|
||||
}
|
||||
|
||||
public bool EnsureBehavior<T>(out T behavior) where T : IMechanismBehavior, new()
|
||||
{
|
||||
if (_behaviors.TryGetValue(typeof(T), out var rawBehavior))
|
||||
{
|
||||
behavior = (T) rawBehavior;
|
||||
return true;
|
||||
}
|
||||
|
||||
behavior = new T();
|
||||
IoCManager.InjectDependencies(behavior);
|
||||
_behaviors.Add(typeof(T), behavior);
|
||||
behavior.Initialize(this);
|
||||
behavior.Startup();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool HasBehavior<T>() where T : IMechanismBehavior
|
||||
{
|
||||
return _behaviors.ContainsKey(typeof(T));
|
||||
}
|
||||
|
||||
public bool TryRemoveBehavior<T>() where T : IMechanismBehavior
|
||||
{
|
||||
return _behaviors.Remove(typeof(T));
|
||||
}
|
||||
|
||||
public void Update(float frameTime)
|
||||
{
|
||||
foreach (var behavior in _behaviors.Values)
|
||||
{
|
||||
behavior.Update(frameTime);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddedToBody(IBody body)
|
||||
@@ -105,9 +197,7 @@ namespace Content.Shared.GameObjects.Components.Body.Mechanism
|
||||
DebugTools.AssertNotNull(Body);
|
||||
DebugTools.AssertNotNull(body);
|
||||
|
||||
OnAddedToBody(body);
|
||||
|
||||
foreach (var behavior in Owner.GetAllComponents<IMechanismBehavior>())
|
||||
foreach (var behavior in _behaviors.Values)
|
||||
{
|
||||
behavior.AddedToBody(body);
|
||||
}
|
||||
@@ -119,9 +209,8 @@ namespace Content.Shared.GameObjects.Components.Body.Mechanism
|
||||
DebugTools.AssertNotNull(part);
|
||||
|
||||
Owner.Transform.AttachParent(part.Owner);
|
||||
OnAddedToPart(part);
|
||||
|
||||
foreach (var behavior in Owner.GetAllComponents<IMechanismBehavior>().ToArray())
|
||||
foreach (var behavior in _behaviors.Values)
|
||||
{
|
||||
behavior.AddedToPart(part);
|
||||
}
|
||||
@@ -135,9 +224,8 @@ namespace Content.Shared.GameObjects.Components.Body.Mechanism
|
||||
DebugTools.AssertNotNull(part);
|
||||
|
||||
Owner.Transform.AttachParent(part.Owner);
|
||||
OnAddedToPartInBody(body, part);
|
||||
|
||||
foreach (var behavior in Owner.GetAllComponents<IMechanismBehavior>())
|
||||
foreach (var behavior in _behaviors.Values)
|
||||
{
|
||||
behavior.AddedToPartInBody(body, part);
|
||||
}
|
||||
@@ -148,9 +236,7 @@ namespace Content.Shared.GameObjects.Components.Body.Mechanism
|
||||
DebugTools.AssertNull(Body);
|
||||
DebugTools.AssertNotNull(old);
|
||||
|
||||
OnRemovedFromBody(old);
|
||||
|
||||
foreach (var behavior in Owner.GetAllComponents<IMechanismBehavior>())
|
||||
foreach (var behavior in _behaviors.Values)
|
||||
{
|
||||
behavior.RemovedFromBody(old);
|
||||
}
|
||||
@@ -162,9 +248,8 @@ namespace Content.Shared.GameObjects.Components.Body.Mechanism
|
||||
DebugTools.AssertNotNull(old);
|
||||
|
||||
Owner.Transform.AttachToGridOrMap();
|
||||
OnRemovedFromPart(old);
|
||||
|
||||
foreach (var behavior in Owner.GetAllComponents<IMechanismBehavior>())
|
||||
foreach (var behavior in _behaviors.Values)
|
||||
{
|
||||
behavior.RemovedFromPart(old);
|
||||
}
|
||||
@@ -178,24 +263,11 @@ namespace Content.Shared.GameObjects.Components.Body.Mechanism
|
||||
DebugTools.AssertNotNull(oldPart);
|
||||
|
||||
Owner.Transform.AttachToGridOrMap();
|
||||
OnRemovedFromPartInBody(oldBody, oldPart);
|
||||
|
||||
foreach (var behavior in Owner.GetAllComponents<IMechanismBehavior>())
|
||||
foreach (var behavior in _behaviors.Values)
|
||||
{
|
||||
behavior.RemovedFromPartInBody(oldBody, oldPart);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnAddedToBody(IBody body) { }
|
||||
|
||||
protected virtual void OnAddedToPart(IBodyPart part) { }
|
||||
|
||||
protected virtual void OnAddedToPartInBody(IBody body, IBodyPart part) { }
|
||||
|
||||
protected virtual void OnRemovedFromBody(IBody old) { }
|
||||
|
||||
protected virtual void OnRemovedFromPart(IBodyPart old) { }
|
||||
|
||||
protected virtual void OnRemovedFromPartInBody(IBody oldBody, IBodyPart oldPart) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,279 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Shared.Alert;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Mobs
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles the icons on the right side of the screen.
|
||||
/// Should only be used for player-controlled entities.
|
||||
/// </summary>
|
||||
public abstract class SharedAlertsComponent : Component
|
||||
{
|
||||
private static readonly AlertState[] NO_ALERTS = new AlertState[0];
|
||||
|
||||
[Dependency]
|
||||
protected readonly AlertManager AlertManager = default!;
|
||||
|
||||
public override string Name => "AlertsUI";
|
||||
public override uint? NetID => ContentNetIDs.ALERTS;
|
||||
|
||||
[ViewVariables]
|
||||
private Dictionary<AlertKey, ClickableAlertState> _alerts = new Dictionary<AlertKey, ClickableAlertState>();
|
||||
|
||||
/// <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<AlertState> EnumerateAlertStates()
|
||||
{
|
||||
return _alerts.Values.Select(alertData => alertData.AlertState);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the alert's specified callback if there is one.
|
||||
/// Not intended to be used on clientside.
|
||||
/// </summary>
|
||||
protected void PerformAlertClickCallback(AlertPrototype alert, IEntity owner)
|
||||
{
|
||||
if (_alerts.TryGetValue(alert.AlertKey, out var alertStateCallback))
|
||||
{
|
||||
alertStateCallback.OnClickAlert?.Invoke(new ClickAlertEventArgs(owner, alert));
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.DebugS("alert", "player {0} attempted to invoke" +
|
||||
" alert click for {1} but that alert is not currently" +
|
||||
" showing", owner.Name, alert.AlertType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new array containing all of the current alert states.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected AlertState[] CreateAlertStatesArray()
|
||||
{
|
||||
if (_alerts.Count == 0) return NO_ALERTS;
|
||||
var states = new AlertState[_alerts.Count];
|
||||
// because I don't trust LINQ
|
||||
var idx = 0;
|
||||
foreach (var alertData in _alerts.Values)
|
||||
{
|
||||
states[idx++] = alertData.AlertState;
|
||||
}
|
||||
|
||||
return states;
|
||||
}
|
||||
|
||||
protected bool TryGetAlertState(AlertKey key, out AlertState alertState)
|
||||
{
|
||||
if (_alerts.TryGetValue(key, out var alertData))
|
||||
{
|
||||
alertState = alertData.AlertState;
|
||||
return true;
|
||||
}
|
||||
|
||||
alertState = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replace the current active alerts with the specified alerts. Any
|
||||
/// OnClickAlert callbacks on the active alerts will be erased.
|
||||
/// </summary>
|
||||
protected void SetAlerts(AlertState[] alerts)
|
||||
{
|
||||
var newAlerts = new Dictionary<AlertKey, ClickableAlertState>();
|
||||
foreach (var alertState in alerts)
|
||||
{
|
||||
if (AlertManager.TryDecode(alertState.AlertEncoded, out var alert))
|
||||
{
|
||||
newAlerts[alert.AlertKey] = new ClickableAlertState
|
||||
{
|
||||
AlertState = alertState
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.ErrorS("alert", "unrecognized encoded alert {0}", alertState.AlertEncoded);
|
||||
}
|
||||
}
|
||||
|
||||
_alerts = newAlerts;
|
||||
}
|
||||
|
||||
/// <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="onClickAlert">callback to invoke when ClickAlertMessage is received by the server
|
||||
/// after being clicked by client. Has no effect when specified on the clientside.</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, OnClickAlert onClickAlert = null,
|
||||
ValueTuple<TimeSpan, TimeSpan>? cooldown = null)
|
||||
{
|
||||
if (AlertManager.TryGetWithEncoded(alertType, out var alert, out var encoded))
|
||||
{
|
||||
if (_alerts.TryGetValue(alert.AlertKey, out var alertStateCallback) &&
|
||||
alertStateCallback.AlertState.AlertEncoded == encoded &&
|
||||
alertStateCallback.AlertState.Severity == severity && alertStateCallback.AlertState.Cooldown == cooldown)
|
||||
{
|
||||
alertStateCallback.OnClickAlert = onClickAlert;
|
||||
return;
|
||||
}
|
||||
|
||||
_alerts[alert.AlertKey] = new ClickableAlertState
|
||||
{
|
||||
AlertState = new AlertState
|
||||
{Cooldown = cooldown, AlertEncoded = encoded, Severity = severity},
|
||||
OnClickAlert = onClickAlert
|
||||
};
|
||||
|
||||
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 clearing an alert prior to dirtying the control
|
||||
/// </summary>
|
||||
protected virtual void AfterClearAlert() { }
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class AlertsComponentState : ComponentState
|
||||
{
|
||||
public AlertState[] Alerts;
|
||||
|
||||
public AlertsComponentState(AlertState[] alerts) : base(ContentNetIDs.ALERTS)
|
||||
{
|
||||
Alerts = alerts;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A message that calls the click interaction on a alert
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public class ClickAlertMessage : ComponentMessage
|
||||
{
|
||||
public readonly byte EncodedAlert;
|
||||
|
||||
public ClickAlertMessage(byte encodedAlert)
|
||||
{
|
||||
Directed = true;
|
||||
EncodedAlert = encodedAlert;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public struct AlertState
|
||||
{
|
||||
public byte AlertEncoded;
|
||||
public short? Severity;
|
||||
public ValueTuple<TimeSpan, TimeSpan>? Cooldown;
|
||||
}
|
||||
|
||||
public struct ClickableAlertState
|
||||
{
|
||||
public AlertState AlertState;
|
||||
public OnClickAlert OnClickAlert;
|
||||
}
|
||||
|
||||
public delegate void OnClickAlert(ClickAlertEventArgs args);
|
||||
|
||||
public class ClickAlertEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Player clicking the alert
|
||||
/// </summary>
|
||||
public readonly IEntity Player;
|
||||
/// <summary>
|
||||
/// Alert that was clicked
|
||||
/// </summary>
|
||||
public readonly AlertPrototype Alert;
|
||||
|
||||
public ClickAlertEventArgs(IEntity player, AlertPrototype alert)
|
||||
{
|
||||
Player = player;
|
||||
Alert = alert;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Mobs
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles the icons on the right side of the screen.
|
||||
/// Should only be used for player-controlled entities
|
||||
/// </summary>
|
||||
public abstract class SharedStatusEffectsComponent : Component
|
||||
{
|
||||
public override string Name => "StatusEffectsUI";
|
||||
public override uint? NetID => ContentNetIDs.STATUSEFFECTS;
|
||||
|
||||
public abstract IReadOnlyDictionary<StatusEffect, StatusEffectStatus> Statuses { get; }
|
||||
|
||||
public abstract void ChangeStatusEffectIcon(StatusEffect effect, string icon);
|
||||
|
||||
public abstract void ChangeStatusEffect(StatusEffect effect, string icon, ValueTuple<TimeSpan, TimeSpan>? cooldown);
|
||||
|
||||
public abstract void RemoveStatusEffect(StatusEffect effect);
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class StatusEffectComponentState : ComponentState
|
||||
{
|
||||
public Dictionary<StatusEffect, StatusEffectStatus> StatusEffects;
|
||||
|
||||
public StatusEffectComponentState(Dictionary<StatusEffect, StatusEffectStatus> statusEffects) : base(ContentNetIDs.STATUSEFFECTS)
|
||||
{
|
||||
StatusEffects = statusEffects;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A message that calls the click interaction on a status effect
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public class ClickStatusMessage : ComponentMessage
|
||||
{
|
||||
public readonly StatusEffect Effect;
|
||||
|
||||
public ClickStatusMessage(StatusEffect effect)
|
||||
{
|
||||
Directed = true;
|
||||
Effect = effect;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public struct StatusEffectStatus
|
||||
{
|
||||
public string Icon;
|
||||
public ValueTuple<TimeSpan, TimeSpan>? Cooldown;
|
||||
}
|
||||
|
||||
// Each status effect is assumed to be unique
|
||||
public enum StatusEffect
|
||||
{
|
||||
Health,
|
||||
Hunger,
|
||||
Thirst,
|
||||
Pressure,
|
||||
Fire,
|
||||
Temperature,
|
||||
Stun,
|
||||
Cuffed,
|
||||
Buckled,
|
||||
Piloting,
|
||||
Pulling,
|
||||
Pulled,
|
||||
Weightless
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.GameObjects.Components.Movement;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
@@ -41,7 +42,7 @@ namespace Content.Shared.GameObjects.Components.Mobs
|
||||
protected float KnockdownTimer;
|
||||
protected float SlowdownTimer;
|
||||
|
||||
private string _stunTexture;
|
||||
private string _stunAlertId;
|
||||
|
||||
protected CancellationTokenSource StatusRemoveCancellation = new CancellationTokenSource();
|
||||
|
||||
@@ -117,7 +118,7 @@ namespace Content.Shared.GameObjects.Components.Mobs
|
||||
StunnedTimer = seconds;
|
||||
LastStun = _gameTiming.CurTime;
|
||||
|
||||
SetStatusEffect();
|
||||
SetAlert();
|
||||
OnStun();
|
||||
|
||||
Dirty();
|
||||
@@ -144,7 +145,7 @@ namespace Content.Shared.GameObjects.Components.Mobs
|
||||
KnockdownTimer = seconds;
|
||||
LastStun = _gameTiming.CurTime;
|
||||
|
||||
SetStatusEffect();
|
||||
SetAlert();
|
||||
OnKnockdown();
|
||||
|
||||
Dirty();
|
||||
@@ -186,18 +187,18 @@ namespace Content.Shared.GameObjects.Components.Mobs
|
||||
if (Owner.TryGetComponent(out MovementSpeedModifierComponent movement))
|
||||
movement.RefreshMovementSpeedModifiers();
|
||||
|
||||
SetStatusEffect();
|
||||
SetAlert();
|
||||
Dirty();
|
||||
}
|
||||
|
||||
private void SetStatusEffect()
|
||||
private void SetAlert()
|
||||
{
|
||||
if (!Owner.TryGetComponent(out SharedStatusEffectsComponent status))
|
||||
if (!Owner.TryGetComponent(out SharedAlertsComponent status))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
status.ChangeStatusEffect(StatusEffect.Stun, _stunTexture,
|
||||
status.ShowAlert(AlertType.Stun, cooldown:
|
||||
(StunStart == null || StunEnd == null) ? default : (StunStart.Value, StunEnd.Value));
|
||||
StatusRemoveCancellation.Cancel();
|
||||
StatusRemoveCancellation = new CancellationTokenSource();
|
||||
@@ -212,8 +213,8 @@ namespace Content.Shared.GameObjects.Components.Mobs
|
||||
serializer.DataField(ref _slowdownCap, "slowdownCap", 20f);
|
||||
serializer.DataField(ref _helpInterval, "helpInterval", 1f);
|
||||
serializer.DataField(ref _helpKnockdownRemove, "helpKnockdownRemove", 1f);
|
||||
serializer.DataField(ref _stunTexture, "stunTexture",
|
||||
"/Textures/Objects/Weapons/Melee/stunbaton.rsi/stunbaton_off.png");
|
||||
serializer.DataField(ref _stunAlertId, "stunAlertId",
|
||||
"stun");
|
||||
}
|
||||
|
||||
protected virtual void OnInteractHand() { }
|
||||
@@ -230,7 +231,7 @@ namespace Content.Shared.GameObjects.Components.Mobs
|
||||
|
||||
KnockdownTimer -= _helpKnockdownRemove;
|
||||
|
||||
SetStatusEffect();
|
||||
SetAlert();
|
||||
Dirty();
|
||||
|
||||
return true;
|
||||
|
||||
@@ -139,7 +139,7 @@ namespace Content.Shared.GameObjects.Components.Movement
|
||||
/// Whether or not the player can move diagonally.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public bool DiagonalMovementEnabled => _configurationManager.GetCVar<bool>("game.diagonalmovement");
|
||||
public bool DiagonalMovementEnabled => _configurationManager.GetCVar<bool>(CCVars.GameDiagonalMovement);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnAdd()
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Nutrition
|
||||
{
|
||||
/// <summary>
|
||||
/// Shared class for stomach components
|
||||
/// </summary>
|
||||
public class SharedStomachComponent : Component
|
||||
{
|
||||
public override string Name => "Stomach";
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,15 @@ namespace Content.Shared.GameObjects.Components.PDA
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class PDAEjectPenMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public PDAEjectPenMessage()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class PDAUBoundUserInterfaceState : BoundUserInterfaceState
|
||||
{
|
||||
@@ -40,28 +49,27 @@ namespace Content.Shared.GameObjects.Components.PDA
|
||||
public sealed class PDAUpdateState : PDAUBoundUserInterfaceState
|
||||
{
|
||||
public bool FlashlightEnabled;
|
||||
public bool HasPen;
|
||||
public PDAIdInfoText PDAOwnerInfo;
|
||||
public UplinkAccountData Account;
|
||||
public UplinkListingData[] Listings;
|
||||
|
||||
public PDAUpdateState(bool isFlashlightOn, PDAIdInfoText ownerInfo)
|
||||
public PDAUpdateState(bool isFlashlightOn, bool hasPen, PDAIdInfoText ownerInfo)
|
||||
{
|
||||
FlashlightEnabled = isFlashlightOn;
|
||||
HasPen = hasPen;
|
||||
PDAOwnerInfo = ownerInfo;
|
||||
}
|
||||
|
||||
public PDAUpdateState(bool isFlashlightOn, PDAIdInfoText ownerInfo, UplinkAccountData accountData)
|
||||
public PDAUpdateState(bool isFlashlightOn, bool hasPen, PDAIdInfoText ownerInfo, UplinkAccountData accountData)
|
||||
: this(isFlashlightOn, hasPen, ownerInfo)
|
||||
{
|
||||
FlashlightEnabled = isFlashlightOn;
|
||||
PDAOwnerInfo = ownerInfo;
|
||||
Account = accountData;
|
||||
}
|
||||
|
||||
public PDAUpdateState(bool isFlashlightOn, PDAIdInfoText ownerInfo, UplinkAccountData accountData, UplinkListingData[] listings)
|
||||
public PDAUpdateState(bool isFlashlightOn, bool hasPen, PDAIdInfoText ownerInfo, UplinkAccountData accountData, UplinkListingData[] listings)
|
||||
: this(isFlashlightOn, hasPen, ownerInfo, accountData)
|
||||
{
|
||||
FlashlightEnabled = isFlashlightOn;
|
||||
PDAOwnerInfo = ownerInfo;
|
||||
Account = accountData;
|
||||
Listings = listings;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.Physics;
|
||||
using Content.Shared.Physics.Pull;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.ComponentDependencies;
|
||||
using Robust.Shared.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Physics;
|
||||
@@ -19,7 +22,7 @@ namespace Content.Shared.GameObjects.Components.Pulling
|
||||
public override string Name => "Pullable";
|
||||
public override uint? NetID => ContentNetIDs.PULLABLE;
|
||||
|
||||
[ComponentDependency] private IPhysicsComponent? _physics = default!;
|
||||
[ComponentDependency] private readonly IPhysicsComponent? _physics = default!;
|
||||
|
||||
private IEntity? _puller;
|
||||
|
||||
@@ -36,7 +39,7 @@ namespace Content.Shared.GameObjects.Components.Pulling
|
||||
_puller = value;
|
||||
Dirty();
|
||||
|
||||
if (!Owner.TryGetComponent(out IPhysicsComponent? physics))
|
||||
if (_physics == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -45,7 +48,7 @@ namespace Content.Shared.GameObjects.Components.Pulling
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
if (physics.TryGetController(out controller))
|
||||
if (_physics.TryGetController(out controller))
|
||||
{
|
||||
controller.StopPull();
|
||||
}
|
||||
@@ -53,7 +56,7 @@ namespace Content.Shared.GameObjects.Components.Pulling
|
||||
return;
|
||||
}
|
||||
|
||||
controller = physics.EnsureController<PullController>();
|
||||
controller = _physics.EnsureController<PullController>();
|
||||
controller.StartPull(value);
|
||||
}
|
||||
}
|
||||
@@ -67,12 +70,12 @@ namespace Content.Shared.GameObjects.Components.Pulling
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!puller.TryGetComponent(out IPhysicsComponent? physics))
|
||||
if (_physics == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (physics.Anchored)
|
||||
if (_physics.Anchored)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -145,12 +148,12 @@ namespace Content.Shared.GameObjects.Components.Pulling
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Owner.TryGetComponent(out IPhysicsComponent? physics))
|
||||
if (_physics == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!physics.TryGetController(out PullController controller))
|
||||
if (!_physics.TryGetController(out PullController controller))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -204,29 +207,36 @@ namespace Content.Shared.GameObjects.Components.Pulling
|
||||
|
||||
private void AddPullingStatuses(IEntity puller)
|
||||
{
|
||||
if (Owner.TryGetComponent(out SharedStatusEffectsComponent? pulledStatus))
|
||||
if (Owner.TryGetComponent(out SharedAlertsComponent? pulledStatus))
|
||||
{
|
||||
pulledStatus.ChangeStatusEffectIcon(StatusEffect.Pulled,
|
||||
"/Textures/Interface/StatusEffects/Pull/pulled.png");
|
||||
pulledStatus.ShowAlert(AlertType.Pulled);
|
||||
}
|
||||
|
||||
if (puller.TryGetComponent(out SharedStatusEffectsComponent? ownerStatus))
|
||||
if (puller.TryGetComponent(out SharedAlertsComponent? ownerStatus))
|
||||
{
|
||||
ownerStatus.ChangeStatusEffectIcon(StatusEffect.Pulling,
|
||||
"/Textures/Interface/StatusEffects/Pull/pulling.png");
|
||||
ownerStatus.ShowAlert(AlertType.Pulling, onClickAlert: OnClickAlert);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnClickAlert(ClickAlertEventArgs args)
|
||||
{
|
||||
EntitySystem
|
||||
.Get<SharedPullingSystem>()
|
||||
.GetPulled(args.Player)?
|
||||
.GetComponentOrNull<SharedPullableComponent>()?
|
||||
.TryStopPull();
|
||||
}
|
||||
|
||||
private void RemovePullingStatuses(IEntity puller)
|
||||
{
|
||||
if (Owner.TryGetComponent(out SharedStatusEffectsComponent? pulledStatus))
|
||||
if (Owner.TryGetComponent(out SharedAlertsComponent? pulledStatus))
|
||||
{
|
||||
pulledStatus.RemoveStatusEffect(StatusEffect.Pulled);
|
||||
pulledStatus.ClearAlert(AlertType.Pulled);
|
||||
}
|
||||
|
||||
if (puller.TryGetComponent(out SharedStatusEffectsComponent? ownerStatus))
|
||||
if (puller.TryGetComponent(out SharedAlertsComponent? ownerStatus))
|
||||
{
|
||||
ownerStatus.RemoveStatusEffect(StatusEffect.Pulling);
|
||||
ownerStatus.ClearAlert(AlertType.Pulling);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,21 +13,34 @@ namespace Content.Shared.GameObjects.Components
|
||||
[Serializable, NetSerializable]
|
||||
public class ConfigurationBoundUserInterfaceState : BoundUserInterfaceState
|
||||
{
|
||||
public readonly Dictionary<string, string> Config;
|
||||
|
||||
public Dictionary<string, string> Config { get; }
|
||||
|
||||
public ConfigurationBoundUserInterfaceState(Dictionary<string, string> config)
|
||||
{
|
||||
Config = config;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Message sent to other components on this entity when DeviceNetwork configuration updated.
|
||||
/// </summary>
|
||||
public class ConfigUpdatedComponentMessage : ComponentMessage
|
||||
{
|
||||
public Dictionary<string, string> Config { get; }
|
||||
|
||||
public ConfigUpdatedComponentMessage(Dictionary<string, string> config)
|
||||
{
|
||||
Config = config;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Message data sent from client to server when the device configuration is updated.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public class ConfigurationUpdatedMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public readonly Dictionary<string, string> Config;
|
||||
public Dictionary<string, string> Config { get; }
|
||||
|
||||
public ConfigurationUpdatedMessage(Dictionary<string, string> config)
|
||||
{
|
||||
@@ -38,7 +51,7 @@ namespace Content.Shared.GameObjects.Components
|
||||
[Serializable, NetSerializable]
|
||||
public class ValidationUpdateMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public readonly string ValidationString;
|
||||
public string ValidationString { get; }
|
||||
|
||||
public ValidationUpdateMessage(string validationString)
|
||||
{
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
public const uint RESEARCH_CONSOLE = 1023;
|
||||
public const uint WIRES = 1024;
|
||||
public const uint COMBATMODE = 1025;
|
||||
public const uint STATUSEFFECTS = 1026;
|
||||
public const uint ALERTS = 1026;
|
||||
public const uint OVERLAYEFFECTS = 1027;
|
||||
public const uint STOMACH = 1028;
|
||||
public const uint ITEMCOOLDOWN = 1029;
|
||||
|
||||
21
Content.Shared/GameObjects/EntitySystems/MechanismSystem.cs
Normal file
21
Content.Shared/GameObjects/EntitySystems/MechanismSystem.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using Content.Shared.GameObjects.Components.Body.Behavior;
|
||||
using Content.Shared.GameObjects.Components.Body.Mechanism;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
|
||||
namespace Content.Shared.GameObjects.EntitySystems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class MechanismSystem : EntitySystem
|
||||
{
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
foreach (var mechanism in ComponentManager.EntityQuery<IMechanism>())
|
||||
{
|
||||
mechanism.Update(frameTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,8 +41,6 @@ namespace Content.Shared.GameObjects.EntitySystems
|
||||
.Bind(EngineKeyFunctions.MoveDown, moveDownCmdHandler)
|
||||
.Bind(EngineKeyFunctions.Walk, new WalkInputCmdHandler())
|
||||
.Register<SharedMoverSystem>();
|
||||
|
||||
_configurationManager.RegisterCVar("game.diagonalmovement", true, CVar.ARCHIVE);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
Reference in New Issue
Block a user