diff --git a/Content.Client/Actions/ActionEvents.cs b/Content.Client/Actions/ActionEvents.cs
index 36739b0dbe..2fdf25c976 100644
--- a/Content.Client/Actions/ActionEvents.cs
+++ b/Content.Client/Actions/ActionEvents.cs
@@ -1,11 +1,9 @@
-using Content.Shared.Actions.ActionTypes;
-
namespace Content.Client.Actions;
///
-/// This event is raised when a user clicks on an empty action slot. Enables other systems to fill this slow.
+/// This event is raised when a user clicks on an empty action slot. Enables other systems to fill this slot.
///
public sealed class FillActionSlotEvent : EntityEventArgs
{
- public ActionType? Action;
+ public EntityUid? Action;
}
diff --git a/Content.Client/Actions/ActionsSystem.cs b/Content.Client/Actions/ActionsSystem.cs
index ba007f35aa..55ced8632c 100644
--- a/Content.Client/Actions/ActionsSystem.cs
+++ b/Content.Client/Actions/ActionsSystem.cs
@@ -1,10 +1,10 @@
using System.IO;
using System.Linq;
using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Client.Player;
+using Robust.Shared.Containers;
using Robust.Shared.ContentPack;
using Robust.Shared.GameStates;
using Robust.Shared.Input.Binding;
@@ -12,6 +12,7 @@ using Robust.Shared.Serialization.Manager;
using Robust.Shared.Serialization.Markdown;
using Robust.Shared.Serialization.Markdown.Mapping;
using Robust.Shared.Serialization.Markdown.Sequence;
+using Robust.Shared.Serialization.Markdown.Value;
using Robust.Shared.Utility;
using YamlDotNet.RepresentationModel;
@@ -20,14 +21,15 @@ namespace Content.Client.Actions
[UsedImplicitly]
public sealed class ActionsSystem : SharedActionsSystem
{
- public delegate void OnActionReplaced(ActionType existing, ActionType action);
+ public delegate void OnActionReplaced(EntityUid actionId);
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IResourceManager _resources = default!;
[Dependency] private readonly ISerializationManager _serialization = default!;
+ [Dependency] private readonly MetaDataSystem _metaData = default!;
- public event Action? ActionAdded;
- public event Action? ActionRemoved;
+ public event Action? ActionAdded;
+ public event Action? ActionRemoved;
public event OnActionReplaced? ActionReplaced;
public event Action? ActionsUpdated;
public event Action? LinkActions;
@@ -35,7 +37,11 @@ namespace Content.Client.Actions
public event Action? ClearAssignments;
public event Action>? AssignSlot;
- public ActionsComponent? PlayerActions { get; private set; }
+ ///
+ /// Queue of entities with that needs to be updated after
+ /// handling a state.
+ ///
+ private readonly Queue _actionHoldersQueue = new();
public override void Initialize()
{
@@ -45,110 +51,90 @@ namespace Content.Client.Actions
SubscribeLocalEvent(HandleComponentState);
}
- public override void Dirty(ActionType action)
+ public override void Dirty(EntityUid? actionId)
{
- if (_playerManager.LocalPlayer?.ControlledEntity != action.AttachedEntity)
+ var action = GetActionData(actionId);
+ if (_playerManager.LocalPlayer?.ControlledEntity != action?.AttachedEntity)
return;
- base.Dirty(action);
+ base.Dirty(actionId);
ActionsUpdated?.Invoke();
}
private void HandleComponentState(EntityUid uid, ActionsComponent component, ref ComponentHandleState args)
{
- if (args.Current is not ActionsComponentState state)
+ if (args.Current is not ActionsComponentState)
return;
- state.SortedActions ??= new SortedSet(state.Actions);
- var serverActions = state.SortedActions;
- var removed = new List();
-
- foreach (var act in component.Actions.ToList())
- {
- if (act.ClientExclusive)
- continue;
-
- if (!serverActions.TryGetValue(act, out var serverAct))
- {
- component.Actions.Remove(act);
- if (act.AutoRemove)
- removed.Add(act);
-
- continue;
- }
-
- act.CopyFrom(serverAct);
- }
-
- var added = new List();
-
- // Anything that remains is a new action
- foreach (var newAct in serverActions)
- {
- if (component.Actions.Contains(newAct))
- continue;
-
- // We create a new action, not just sorting a reference to the state's action.
- var action = (ActionType) newAct.Clone();
- component.Actions.Add(action);
- added.Add(action);
- }
-
- if (_playerManager.LocalPlayer?.ControlledEntity != uid)
- return;
-
- foreach (var action in removed)
- {
- ActionRemoved?.Invoke(action);
- }
-
- foreach (var action in added)
- {
- ActionAdded?.Invoke(action);
- }
-
- ActionsUpdated?.Invoke();
+ _actionHoldersQueue.Enqueue(uid);
}
- protected override void AddActionInternal(ActionsComponent comp, ActionType action)
+ protected override void AddActionInternal(EntityUid actionId, IContainer container)
{
// Sometimes the client receives actions from the server, before predicting that newly added components will add
// their own shared actions. Just in case those systems ever decided to directly access action properties (e.g.,
// action.Toggled), we will remove duplicates:
- if (comp.Actions.TryGetValue(action, out var existing))
+ if (container.Contains(actionId))
{
- comp.Actions.Remove(existing);
- ActionReplaced?.Invoke(existing, action);
+ ActionReplaced?.Invoke(actionId);
+ }
+ else
+ {
+ container.Insert(actionId);
+ }
+ }
+
+ public override void AddAction(EntityUid holderId, EntityUid actionId, EntityUid? provider, ActionsComponent? holder = null, BaseActionComponent? action = null, bool dirty = true, IContainer? actionContainer = null)
+ {
+ if (!Resolve(holderId, ref holder, false))
+ return;
+
+ action ??= GetActionData(actionId);
+ if (action == null)
+ {
+ Log.Warning($"No {nameof(BaseActionComponent)} found on entity {actionId}");
+ return;
}
- comp.Actions.Add(action);
+ dirty &= !action.ClientExclusive;
+ base.AddAction(holderId, actionId, provider, holder, action, dirty, actionContainer);
+
+ if (holderId == _playerManager.LocalPlayer?.ControlledEntity)
+ ActionAdded?.Invoke(actionId);
}
- public override void AddAction(EntityUid uid, ActionType action, EntityUid? provider, ActionsComponent? comp = null, bool dirty = true)
+ public override void RemoveAction(EntityUid holderId, EntityUid? actionId, ActionsComponent? comp = null, BaseActionComponent? action = null, bool dirty = true, ContainerManagerComponent? actionContainer = null)
{
- if (!Resolve(uid, ref comp, false))
+ if (GameTiming.ApplyingState)
return;
- dirty &= !action.ClientExclusive;
- base.AddAction(uid, action, provider, comp, dirty);
+ if (!Resolve(holderId, ref comp, false))
+ return;
- if (uid == _playerManager.LocalPlayer?.ControlledEntity)
- ActionAdded?.Invoke(action);
+ if (actionId == null)
+ return;
+
+ action ??= GetActionData(actionId);
+
+ if (action is { ClientExclusive: false })
+ return;
+
+ dirty &= !action?.ClientExclusive ?? true;
+ base.RemoveAction(holderId, actionId, comp, action, dirty, actionContainer);
+
+ if (_playerManager.LocalPlayer?.ControlledEntity != holderId)
+ return;
+
+ if (action == null || action.AutoRemove)
+ ActionRemoved?.Invoke(actionId.Value);
}
- public override void RemoveAction(EntityUid uid, ActionType action, ActionsComponent? comp = null, bool dirty = true)
+ public IEnumerable<(EntityUid Id, BaseActionComponent Comp)> GetClientActions()
{
- if (GameTiming.ApplyingState && !action.ClientExclusive)
- return;
+ if (_playerManager.LocalPlayer?.ControlledEntity is not { } user)
+ return Enumerable.Empty<(EntityUid, BaseActionComponent)>();
- if (!Resolve(uid, ref comp, false))
- return;
-
- dirty &= !action.ClientExclusive;
- base.RemoveAction(uid, action, comp, dirty);
-
- if (action.AutoRemove && uid == _playerManager.LocalPlayer?.ControlledEntity)
- ActionRemoved?.Invoke(action);
+ return GetActions(user);
}
private void OnPlayerAttached(EntityUid uid, ActionsComponent component, PlayerAttachedEvent args)
@@ -163,20 +149,18 @@ namespace Content.Client.Actions
public void UnlinkAllActions()
{
- PlayerActions = null;
UnlinkActions?.Invoke();
}
public void LinkAllActions(ActionsComponent? actions = null)
{
- var player = _playerManager.LocalPlayer?.ControlledEntity;
- if (player == null || !Resolve(player.Value, ref actions, false))
+ if (_playerManager.LocalPlayer?.ControlledEntity is not { } user ||
+ !Resolve(user, ref actions, false))
{
return;
}
LinkActions?.Invoke(actions);
- PlayerActions = actions;
}
public override void Shutdown()
@@ -185,29 +169,30 @@ namespace Content.Client.Actions
CommandBinds.Unregister();
}
- public void TriggerAction(ActionType? action)
+ public void TriggerAction(EntityUid actionId, BaseActionComponent action)
{
- if (PlayerActions == null || action == null || _playerManager.LocalPlayer?.ControlledEntity is not { Valid: true } user)
+ if (_playerManager.LocalPlayer?.ControlledEntity is not { } user ||
+ !TryComp(user, out ActionsComponent? actions))
+ {
return;
+ }
if (action.Provider != null && Deleted(action.Provider))
return;
- if (action is not InstantAction instantAction)
- {
+ if (action is not InstantActionComponent instantAction)
return;
- }
if (action.ClientExclusive)
{
if (instantAction.Event != null)
instantAction.Event.Performer = user;
- PerformAction(user, PlayerActions, instantAction, instantAction.Event, GameTiming.CurTime);
+ PerformAction(user, actions, actionId, instantAction, instantAction.Event, GameTiming.CurTime);
}
else
{
- var request = new RequestPerformActionEvent(instantAction);
+ var request = new RequestPerformActionEvent(actionId);
EntityManager.RaisePredictiveEvent(request);
}
}
@@ -239,7 +224,7 @@ namespace Content.Client.Actions
///
public void LoadActionAssignments(string path, bool userData)
{
- if (PlayerActions == null)
+ if (_playerManager.LocalPlayer?.ControlledEntity is not { } user)
return;
var file = new ResPath(path).ToRootedPath();
@@ -265,17 +250,13 @@ namespace Content.Client.Actions
if (!map.TryGet("action", out var actionNode))
continue;
- var action = _serialization.Read(actionNode, notNullableOverride: true);
+ var action = _serialization.Read(actionNode, notNullableOverride: true);
+ var actionId = Spawn(null);
+ AddComp(actionId, action);
+ AddAction(user, actionId, null);
- if (PlayerActions.Actions.TryGetValue(action, out var existingAction))
- {
- existingAction.CopyFrom(action);
- action = existingAction;
- }
- else
- {
- PlayerActions.Actions.Add(action);
- }
+ if (map.TryGet("name", out var nameNode))
+ _metaData.SetEntityName(actionId, nameNode.Value);
if (!map.TryGet("assignments", out var assignmentNode))
continue;
@@ -284,7 +265,7 @@ namespace Content.Client.Actions
foreach (var index in nodeAssignments)
{
- var assignment = new SlotAssignment(index.Hotbar, index.Slot, action);
+ var assignment = new SlotAssignment(index.Hotbar, index.Slot, actionId);
assignments.Add(assignment);
}
}
@@ -292,6 +273,73 @@ namespace Content.Client.Actions
AssignSlot?.Invoke(assignments);
}
- public record struct SlotAssignment(byte Hotbar, byte Slot, ActionType Action);
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+
+ if (_actionHoldersQueue.Count == 0)
+ return;
+
+ var removed = new List();
+ var added = new List();
+ var query = GetEntityQuery();
+ var queue = new Queue(_actionHoldersQueue);
+ _actionHoldersQueue.Clear();
+
+ while (queue.TryDequeue(out var holderId))
+ {
+ if (!TryGetContainer(holderId, out var container) || container.ExpectedEntities.Count > 0)
+ {
+ _actionHoldersQueue.Enqueue(holderId);
+ continue;
+ }
+
+ if (!query.TryGetComponent(holderId, out var holder))
+ continue;
+
+ removed.Clear();
+ added.Clear();
+
+ foreach (var (act, data) in holder.OldClientActions.ToList())
+ {
+ if (data.ClientExclusive)
+ continue;
+
+ if (!container.Contains(act))
+ {
+ holder.OldClientActions.Remove(act);
+ if (data.AutoRemove)
+ removed.Add(act);
+ }
+ }
+
+ // Anything that remains is a new action
+ foreach (var newAct in container.ContainedEntities)
+ {
+ if (!holder.OldClientActions.ContainsKey(newAct))
+ added.Add(newAct);
+
+ if (TryGetActionData(newAct, out var serverData))
+ holder.OldClientActions[newAct] = new ActionMetaData(serverData.ClientExclusive, serverData.AutoRemove);
+ }
+
+ if (_playerManager.LocalPlayer?.ControlledEntity != holderId)
+ return;
+
+ foreach (var action in removed)
+ {
+ ActionRemoved?.Invoke(action);
+ }
+
+ foreach (var action in added)
+ {
+ ActionAdded?.Invoke(action);
+ }
+
+ ActionsUpdated?.Invoke();
+ }
+ }
+
+ public record struct SlotAssignment(byte Hotbar, byte Slot, EntityUid ActionId);
}
}
diff --git a/Content.Client/Actions/UI/ActionAlertTooltip.cs b/Content.Client/Actions/UI/ActionAlertTooltip.cs
index 319c682299..f48350d772 100644
--- a/Content.Client/Actions/UI/ActionAlertTooltip.cs
+++ b/Content.Client/Actions/UI/ActionAlertTooltip.cs
@@ -1,9 +1,5 @@
-using System;
-using Content.Client.Stylesheets;
-using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
+using Content.Client.Stylesheets;
using Robust.Client.UserInterface.Controls;
-using Robust.Shared.IoC;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using static Robust.Client.UserInterface.Controls.BoxContainer;
diff --git a/Content.Client/Decals/DecalPlacementSystem.cs b/Content.Client/Decals/DecalPlacementSystem.cs
index cf5f89ffd8..03248f4355 100644
--- a/Content.Client/Decals/DecalPlacementSystem.cs
+++ b/Content.Client/Decals/DecalPlacementSystem.cs
@@ -2,7 +2,6 @@ using System.Numerics;
using Content.Client.Actions;
using Content.Client.Decals.Overlays;
using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Decals;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
@@ -10,7 +9,6 @@ using Robust.Client.Input;
using Robust.Shared.Input;
using Robust.Shared.Input.Binding;
using Robust.Shared.Prototypes;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Client.Decals;
@@ -22,6 +20,7 @@ public sealed class DecalPlacementSystem : EntitySystem
[Dependency] private readonly IOverlayManager _overlay = default!;
[Dependency] private readonly IPrototypeManager _protoMan = default!;
[Dependency] private readonly InputSystem _inputSystem = default!;
+ [Dependency] private readonly MetaDataSystem _metaData = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
@@ -153,9 +152,10 @@ public sealed class DecalPlacementSystem : EntitySystem
Cleanable = _cleanable,
};
- ev.Action = new WorldTargetAction()
+ var actionId = Spawn(null);
+ AddComp(actionId, new WorldTargetActionComponent
{
- DisplayName = $"{_decalId} ({_decalColor.ToHex()}, {(int) _decalAngle.Degrees})", // non-unique actions may be considered duplicates when saving/loading.
+ // non-unique actions may be considered duplicates when saving/loading.
Icon = decalProto.Sprite,
Repeat = true,
ClientExclusive = true,
@@ -164,7 +164,11 @@ public sealed class DecalPlacementSystem : EntitySystem
Range = -1,
Event = actionEvent,
IconColor = _decalColor,
- };
+ });
+
+ _metaData.SetEntityName(actionId, $"{_decalId} ({_decalColor.ToHex()}, {(int) _decalAngle.Degrees})");
+
+ ev.Action = actionId;
}
public override void Shutdown()
@@ -194,24 +198,3 @@ public sealed class DecalPlacementSystem : EntitySystem
_inputSystem.SetEntityContextActive();
}
}
-
-public sealed partial class PlaceDecalActionEvent : WorldTargetActionEvent
-{
- [DataField("decalId", customTypeSerializer:typeof(PrototypeIdSerializer), required:true)]
- public string DecalId = string.Empty;
-
- [DataField("color")]
- public Color Color;
-
- [DataField("rotation")]
- public double Rotation;
-
- [DataField("snap")]
- public bool Snap;
-
- [DataField("zIndex")]
- public int ZIndex;
-
- [DataField("cleanable")]
- public bool Cleanable;
-}
diff --git a/Content.Client/Ghost/GhostSystem.cs b/Content.Client/Ghost/GhostSystem.cs
index 8207e0eac9..944d4e0de0 100644
--- a/Content.Client/Ghost/GhostSystem.cs
+++ b/Content.Client/Ghost/GhostSystem.cs
@@ -2,12 +2,10 @@ using Content.Client.Movement.Systems;
using Content.Shared.Actions;
using Content.Shared.Ghost;
using Content.Shared.Popups;
-using JetBrains.Annotations;
using Robust.Client.Console;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Player;
-using Robust.Shared.GameStates;
namespace Content.Client.Ghost
{
@@ -81,9 +79,9 @@ namespace Content.Client.Ghost
sprite.Visible = GhostVisibility;
}
- _actions.AddAction(uid, component.ToggleLightingAction, null);
- _actions.AddAction(uid, component.ToggleFoVAction, null);
- _actions.AddAction(uid, component.ToggleGhostsAction, null);
+ _actions.AddAction(uid, ref component.ToggleLightingActionEntity, component.ToggleGhostsAction);
+ _actions.AddAction(uid, ref component.ToggleFoVActionEntity, component.ToggleFoVAction);
+ _actions.AddAction(uid, ref component.ToggleGhostsActionEntity, component.ToggleGhostsAction);
}
private void OnToggleLighting(EntityUid uid, GhostComponent component, ToggleLightingActionEvent args)
@@ -118,9 +116,9 @@ namespace Content.Client.Ghost
private void OnGhostRemove(EntityUid uid, GhostComponent component, ComponentRemove args)
{
- _actions.RemoveAction(uid, component.ToggleLightingAction);
- _actions.RemoveAction(uid, component.ToggleFoVAction);
- _actions.RemoveAction(uid, component.ToggleGhostsAction);
+ _actions.RemoveAction(uid, component.ToggleLightingActionEntity);
+ _actions.RemoveAction(uid, component.ToggleFoVActionEntity);
+ _actions.RemoveAction(uid, component.ToggleGhostsActionEntity);
if (uid != _playerManager.LocalPlayer?.ControlledEntity)
return;
diff --git a/Content.Client/Mapping/MappingSystem.cs b/Content.Client/Mapping/MappingSystem.cs
index 2d6db2bb46..4456be36a6 100644
--- a/Content.Client/Mapping/MappingSystem.cs
+++ b/Content.Client/Mapping/MappingSystem.cs
@@ -1,10 +1,11 @@
using Content.Client.Actions;
using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
+using Content.Shared.Mapping;
using Content.Shared.Maps;
using Robust.Client.Placement;
using Robust.Shared.Map;
using Robust.Shared.Utility;
+using static Robust.Shared.Utility.SpriteSpecifier;
namespace Content.Client.Mapping;
@@ -13,16 +14,17 @@ public sealed partial class MappingSystem : EntitySystem
[Dependency] private readonly IPlacementManager _placementMan = default!;
[Dependency] private readonly ITileDefinitionManager _tileMan = default!;
[Dependency] private readonly ActionsSystem _actionsSystem = default!;
+ [Dependency] private readonly MetaDataSystem _metaData = default!;
///
/// The icon to use for space tiles.
///
- private readonly SpriteSpecifier _spaceIcon = new SpriteSpecifier.Texture(new ("Tiles/cropped_parallax.png"));
+ private readonly SpriteSpecifier _spaceIcon = new Texture(new ("Tiles/cropped_parallax.png"));
///
/// The icon to use for entity-eraser.
///
- private readonly SpriteSpecifier _deleteIcon = new SpriteSpecifier.Texture(new ("Interface/VerbIcons/delete.svg.192dpi.png"));
+ private readonly SpriteSpecifier _deleteIcon = new Texture(new ("Interface/VerbIcons/delete.svg.192dpi.png"));
public string DefaultMappingActions = "/mapping_actions.yml";
@@ -73,6 +75,9 @@ public sealed partial class MappingSystem : EntitySystem
else
return;
+ InstantActionComponent action;
+ string name;
+
if (tileDef != null)
{
if (tileDef is not ContentTileDefinition contentTileDef)
@@ -80,45 +85,51 @@ public sealed partial class MappingSystem : EntitySystem
var tileIcon = contentTileDef.IsSpace
? _spaceIcon
- : new SpriteSpecifier.Texture(contentTileDef.Sprite!.Value);
+ : new Texture(contentTileDef.Sprite!.Value);
- ev.Action = new InstantAction()
+ action = new InstantActionComponent
{
ClientExclusive = true,
CheckCanInteract = false,
Event = actionEvent,
- DisplayName = Loc.GetString(tileDef.Name),
Icon = tileIcon
};
- return;
+ name = Loc.GetString(tileDef.Name);
}
-
- if (actionEvent.Eraser)
+ else if (actionEvent.Eraser)
{
- ev.Action = new InstantAction()
+ action = new InstantActionComponent
{
ClientExclusive = true,
CheckCanInteract = false,
Event = actionEvent,
- DisplayName = "action-name-mapping-erase",
Icon = _deleteIcon,
};
- return;
+ name = Loc.GetString("action-name-mapping-erase");
+ }
+ else
+ {
+ if (string.IsNullOrWhiteSpace(actionEvent.EntityType))
+ return;
+
+ action = new InstantActionComponent
+ {
+ ClientExclusive = true,
+ CheckCanInteract = false,
+ Event = actionEvent,
+ Icon = new EntityPrototype(actionEvent.EntityType),
+ };
+
+ name = actionEvent.EntityType;
}
- if (string.IsNullOrWhiteSpace(actionEvent.EntityType))
- return;
+ var actionId = Spawn(null);
+ AddComp(actionId, action);
+ _metaData.SetEntityName(actionId, name);
- ev.Action = new InstantAction()
- {
- ClientExclusive = true,
- CheckCanInteract = false,
- Event = actionEvent,
- DisplayName = actionEvent.EntityType,
- Icon = new SpriteSpecifier.EntityPrototype(actionEvent.EntityType),
- };
+ ev.Action = actionId;
}
private void OnStartPlacementAction(StartPlacementActionEvent args)
@@ -140,18 +151,3 @@ public sealed partial class MappingSystem : EntitySystem
_placementMan.ToggleEraser();
}
}
-
-public sealed partial class StartPlacementActionEvent : InstantActionEvent
-{
- [DataField("entityType")]
- public string? EntityType;
-
- [DataField("tileId")]
- public string? TileId;
-
- [DataField("placementOption")]
- public string? PlacementOption;
-
- [DataField("eraser")]
- public bool Eraser;
-}
diff --git a/Content.Client/NetworkConfigurator/Systems/NetworkConfiguratorSystem.cs b/Content.Client/NetworkConfigurator/Systems/NetworkConfiguratorSystem.cs
index d56296cae9..48b378af04 100644
--- a/Content.Client/NetworkConfigurator/Systems/NetworkConfiguratorSystem.cs
+++ b/Content.Client/NetworkConfigurator/Systems/NetworkConfiguratorSystem.cs
@@ -3,7 +3,6 @@ using Content.Client.Actions;
using Content.Client.Items;
using Content.Client.Message;
using Content.Client.Stylesheets;
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.DeviceNetwork.Components;
using Content.Shared.DeviceNetwork.Systems;
using Content.Shared.Input;
@@ -22,11 +21,11 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
{
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IOverlayManager _overlay = default!;
- [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly ActionsSystem _actions = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
- private const string Action = "ClearNetworkLinkOverlays";
+ [ValidatePrototypeId]
+ private const string Action = "ActionClearNetworkLinkOverlays";
public override void Initialize()
{
@@ -71,7 +70,7 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
if (!EntityQuery().Any())
{
_overlay.RemoveOverlay();
- _actions.RemoveAction(_playerManager.LocalPlayer.ControlledEntity.Value, _prototypeManager.Index(Action));
+ _actions.RemoveAction(_playerManager.LocalPlayer.ControlledEntity.Value, Action);
}
@@ -81,7 +80,7 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
if (!_overlay.HasOverlay())
{
_overlay.AddOverlay(new NetworkConfiguratorLinkOverlay());
- _actions.AddAction(_playerManager.LocalPlayer.ControlledEntity.Value, new InstantAction(_prototypeManager.Index(Action)), null);
+ _actions.AddAction(_playerManager.LocalPlayer.ControlledEntity.Value, Spawn(Action), null);
}
EnsureComp(component.ActiveDeviceList.Value);
@@ -103,7 +102,7 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
if (_playerManager.LocalPlayer?.ControlledEntity != null)
{
- _actions.RemoveAction(_playerManager.LocalPlayer.ControlledEntity.Value, _prototypeManager.Index(Action));
+ _actions.RemoveAction(_playerManager.LocalPlayer.ControlledEntity.Value, Action);
}
}
diff --git a/Content.Client/Store/Ui/StoreMenu.xaml.cs b/Content.Client/Store/Ui/StoreMenu.xaml.cs
index a3f246badb..c59c3a1062 100644
--- a/Content.Client/Store/Ui/StoreMenu.xaml.cs
+++ b/Content.Client/Store/Ui/StoreMenu.xaml.cs
@@ -1,15 +1,15 @@
+using System.Linq;
+using Content.Client.Actions;
using Content.Client.Message;
+using Content.Shared.FixedPoint;
using Content.Shared.Store;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
+using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
-using Robust.Client.Graphics;
-using Content.Shared.Actions.ActionTypes;
-using System.Linq;
-using Content.Shared.FixedPoint;
namespace Content.Client.Store.Ui;
@@ -128,9 +128,12 @@ public sealed partial class StoreMenu : DefaultWindow
}
else if (listing.ProductAction != null)
{
- var action = _prototypeManager.Index(listing.ProductAction);
- if (action.Icon != null)
+ var actionId = _entityManager.Spawn(listing.ProductAction);
+ if (_entityManager.System().TryGetActionData(actionId, out var action) &&
+ action.Icon != null)
+ {
texture = spriteSys.Frame0(action.Icon);
+ }
}
var newListing = new StoreListingControl(listingName, listingDesc, GetListingPriceString(listing), canBuy, texture);
diff --git a/Content.Client/UserInterface/Systems/Actions/ActionUIController.cs b/Content.Client/UserInterface/Systems/Actions/ActionUIController.cs
index ea91e95951..7e288070b0 100644
--- a/Content.Client/UserInterface/Systems/Actions/ActionUIController.cs
+++ b/Content.Client/UserInterface/Systems/Actions/ActionUIController.cs
@@ -13,7 +13,6 @@ using Content.Client.UserInterface.Systems.Actions.Widgets;
using Content.Client.UserInterface.Systems.Actions.Windows;
using Content.Client.UserInterface.Systems.Gameplay;
using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Input;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
@@ -65,7 +64,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged
/// Action slot we are currently selecting a target for.
///
- public TargetedAction? SelectingTargetFor { get; private set; }
+ public EntityUid? SelectingTargetFor { get; private set; }
public ActionUIController()
{
@@ -175,7 +174,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged
private bool TargetingOnUse(in PointerInputCmdArgs args)
{
- if (!_timing.IsFirstTimePredicted || _actionsSystem == null || SelectingTargetFor is not { } action)
+ if (!_timing.IsFirstTimePredicted || _actionsSystem == null || SelectingTargetFor is not { } actionId)
return false;
if (_playerManager.LocalPlayer?.ControlledEntity is not { } user)
@@ -184,6 +183,12 @@ public sealed class ActionUIController : UIController, IOnStateChanged action.Enabled,
Filters.Item => action.Provider != null && action.Provider != _playerManager.LocalPlayer?.ControlledEntity,
Filters.Innate => action.Provider == null || action.Provider == _playerManager.LocalPlayer?.ControlledEntity,
- Filters.Instant => action is InstantAction,
- Filters.Targeted => action is TargetedAction,
+ Filters.Instant => action is InstantActionComponent,
+ Filters.Targeted => action is BaseTargetActionComponent,
_ => throw new ArgumentOutOfRangeException(nameof(filter), filter, null)
};
}
@@ -518,7 +533,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged actions)
+ private void PopulateActions(IEnumerable<(EntityUid Id, BaseActionComponent Comp)> actions)
{
if (_window == null)
return;
@@ -529,7 +544,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged? actions = (component ?? _actionsSystem?.PlayerActions)?.Actions;
- actions ??= Array.Empty();
+ var actions = _actionsSystem.GetClientActions();
if (filters.Count == 0 && string.IsNullOrWhiteSpace(search))
{
@@ -557,45 +570,46 @@ public sealed class ActionUIController : UIController, IOnStateChanged
{
- if (filters.Count > 0 && filters.Any(filter => !MatchesFilter(action, filter)))
+ if (filters.Count > 0 && filters.Any(filter => !MatchesFilter(action.Comp, filter)))
return false;
- if (action.Keywords.Any(keyword => search.Contains(keyword, StringComparison.OrdinalIgnoreCase)))
+ if (action.Comp.Keywords.Any(keyword => search.Contains(keyword, StringComparison.OrdinalIgnoreCase)))
return true;
- if (action.DisplayName.Contains(search, StringComparison.OrdinalIgnoreCase))
+ var name = EntityManager.GetComponent(action.Id).EntityName;
+ if (name.Contains(search, StringComparison.OrdinalIgnoreCase))
return true;
- if (action.Provider == null || action.Provider == _playerManager.LocalPlayer?.ControlledEntity)
+ if (action.Comp.Provider == null || action.Comp.Provider == _playerManager.LocalPlayer?.ControlledEntity)
return false;
- var name = EntityManager.GetComponent(action.Provider.Value).EntityName;
- return name.Contains(search, StringComparison.OrdinalIgnoreCase);
+ var providerName = EntityManager.GetComponent(action.Comp.Provider.Value).EntityName;
+ return providerName.Contains(search, StringComparison.OrdinalIgnoreCase);
});
PopulateActions(actions);
}
- private void SetAction(ActionButton button, ActionType? type)
+ private void SetAction(ActionButton button, EntityUid? actionId)
{
int position;
- if (type == null)
+ if (actionId == null)
{
button.ClearData();
if (_container?.TryGetButtonIndex(button, out position) ?? false)
{
- CurrentPage[position] = type;
+ CurrentPage[position] = actionId;
}
return;
}
- if (button.TryReplaceWith(type) &&
+ if (button.TryReplaceWith(actionId.Value) &&
_container != null &&
_container.TryGetButtonIndex(button, out position))
{
- CurrentPage[position] = type;
+ CurrentPage[position] = actionId;
}
}
@@ -603,7 +617,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged actionType.AutoPopulate).ToList();
+ if (_actionsSystem == null)
+ return;
+
+ var actions = _actionsSystem.GetClientActions().Where(action => action.Comp.AutoPopulate).ToList();
var offset = 0;
var totalPages = _pages.Count;
@@ -892,7 +912,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged
- private void ToggleTargeting(TargetedAction action)
+ private void ToggleTargeting(EntityUid actionId, BaseTargetActionComponent action)
{
- if (SelectingTargetFor == action)
+ if (SelectingTargetFor == actionId)
{
StopTargeting();
return;
}
- StartTargeting(action);
+ StartTargeting(actionId, action);
}
///
/// Puts us in targeting mode, where we need to pick either a target point or entity
///
- private void StartTargeting(TargetedAction action)
+ private void StartTargeting(EntityUid actionId, BaseTargetActionComponent action)
{
// If we were targeting something else we should stop
StopTargeting();
- SelectingTargetFor = action;
+ SelectingTargetFor = actionId;
// override "held-item" overlay
if (action.TargetingIndicator && _overlays.TryGetOverlay(out var handOverlay))
@@ -955,7 +975,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged? predicate = null;
@@ -992,20 +1012,20 @@ public sealed class ActionUIController : UIController, IOnStateChanged _data[index];
set => _data[index] = value;
}
- public static implicit operator ActionType?[](ActionPage p)
+ public static implicit operator EntityUid?[](ActionPage p)
{
return p._data.ToArray();
}
diff --git a/Content.Client/UserInterface/Systems/Actions/Controls/ActionButton.cs b/Content.Client/UserInterface/Systems/Actions/Controls/ActionButton.cs
index 1b2dcdb89c..5a95194b1c 100644
--- a/Content.Client/UserInterface/Systems/Actions/Controls/ActionButton.cs
+++ b/Content.Client/UserInterface/Systems/Actions/Controls/ActionButton.cs
@@ -1,9 +1,9 @@
using System.Numerics;
+using Content.Client.Actions;
using Content.Client.Actions.UI;
using Content.Client.Cooldown;
using Content.Client.Stylesheets;
using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
@@ -19,11 +19,14 @@ namespace Content.Client.UserInterface.Systems.Actions.Controls;
public sealed class ActionButton : Control
{
+ private IEntityManager? _entities;
+
private ActionUIController Controller => UserInterfaceManager.GetUIController();
+ private IEntityManager Entities => _entities ??= IoCManager.Resolve();
+ private ActionsSystem Actions => Entities.System();
private bool _beingHovered;
private bool _depressed;
private bool _toggled;
- private bool _spriteViewDirty;
public BoundKeyFunction? KeyBind
{
@@ -48,7 +51,7 @@ public sealed class ActionButton : Control
private readonly SpriteView _smallItemSpriteView;
private readonly SpriteView _bigItemSpriteView;
- public ActionType? Action { get; private set; }
+ public EntityUid? ActionId { get; private set; }
public bool Locked { get; set; }
public event Action? ActionPressed;
@@ -176,11 +179,11 @@ public sealed class ActionButton : Control
private Control? SupplyTooltip(Control sender)
{
- if (Action == null)
+ if (!Entities.TryGetComponent(ActionId, out MetaDataComponent? metadata))
return null;
- var name = FormattedMessage.FromMarkupPermissive(Loc.GetString(Action.DisplayName));
- var decr = FormattedMessage.FromMarkupPermissive(Loc.GetString(Action.Description));
+ var name = FormattedMessage.FromMarkupPermissive(Loc.GetString(metadata.EntityName));
+ var decr = FormattedMessage.FromMarkupPermissive(Loc.GetString(metadata.EntityDescription));
return new ActionAlertTooltip(name, decr);
}
@@ -192,18 +195,9 @@ public sealed class ActionButton : Control
private void UpdateItemIcon()
{
- var entityManager = IoCManager.Resolve();
- if (Action?.EntityIcon != null && !entityManager.EntityExists(Action.EntityIcon))
- {
- // This is almost certainly because a player received/processed their own actions component state before
- // being send the entity in their inventory that enabled this action.
-
- // Defer updating icons to the next FrameUpdate().
- _spriteViewDirty = true;
- return;
- }
-
- if (Action?.EntityIcon is not { } entity || !entityManager.HasComponent(entity))
+ if (!Actions.TryGetActionData(ActionId, out var action) ||
+ action is not { EntityIcon: { } entity } ||
+ !Entities.HasComponent(entity))
{
_bigItemSpriteView.Visible = false;
_bigItemSpriteView.SetEntity(null);
@@ -212,7 +206,7 @@ public sealed class ActionButton : Control
}
else
{
- switch (Action.ItemIconStyle)
+ switch (action.ItemIconStyle)
{
case ItemActionIconStyle.BigItem:
_bigItemSpriteView.Visible = true;
@@ -238,17 +232,17 @@ public sealed class ActionButton : Control
private void SetActionIcon(Texture? texture)
{
- if (texture == null || Action == null)
+ if (!Actions.TryGetActionData(ActionId, out var action) || texture == null)
{
_bigActionIcon.Texture = null;
_bigActionIcon.Visible = false;
_smallActionIcon.Texture = null;
_smallActionIcon.Visible = false;
}
- else if (Action.EntityIcon != null && Action.ItemIconStyle == ItemActionIconStyle.BigItem)
+ else if (action.EntityIcon != null && action.ItemIconStyle == ItemActionIconStyle.BigItem)
{
_smallActionIcon.Texture = texture;
- _smallActionIcon.Modulate = Action.IconColor;
+ _smallActionIcon.Modulate = action.IconColor;
_smallActionIcon.Visible = true;
_bigActionIcon.Texture = null;
_bigActionIcon.Visible = false;
@@ -256,7 +250,7 @@ public sealed class ActionButton : Control
else
{
_bigActionIcon.Texture = texture;
- _bigActionIcon.Modulate = Action.IconColor;
+ _bigActionIcon.Modulate = action.IconColor;
_bigActionIcon.Visible = true;
_smallActionIcon.Texture = null;
_smallActionIcon.Visible = false;
@@ -267,39 +261,39 @@ public sealed class ActionButton : Control
{
UpdateItemIcon();
- if (Action == null)
+ if (!Actions.TryGetActionData(ActionId, out var action))
{
SetActionIcon(null);
return;
}
- if ((Controller.SelectingTargetFor == Action || Action.Toggled) && Action.IconOn != null)
- SetActionIcon(Action.IconOn.Frame0());
+ if ((Controller.SelectingTargetFor == ActionId || action.Toggled) && action.IconOn != null)
+ SetActionIcon(action.IconOn.Frame0());
else
- SetActionIcon(Action.Icon?.Frame0());
+ SetActionIcon(action.Icon?.Frame0());
}
- public bool TryReplaceWith(ActionType action)
+ public bool TryReplaceWith(EntityUid actionId)
{
if (Locked)
{
return false;
}
- UpdateData(action);
+ UpdateData(actionId);
return true;
}
- public void UpdateData(ActionType action)
+ public void UpdateData(EntityUid actionId)
{
- Action = action;
+ ActionId = actionId;
Label.Visible = true;
UpdateIcons();
}
public void ClearData()
{
- Action = null;
+ ActionId = null;
Cooldown.Visible = false;
Cooldown.Progress = 1;
Label.Visible = false;
@@ -310,20 +304,19 @@ public sealed class ActionButton : Control
{
base.FrameUpdate(args);
- if (_spriteViewDirty)
+ if (!Actions.TryGetActionData(ActionId, out var action))
{
- _spriteViewDirty = false;
- UpdateIcons();
+ return;
}
- if (Action?.Cooldown != null)
+ if (action.Cooldown != null)
{
- Cooldown.FromTime(Action.Cooldown.Value.Start, Action.Cooldown.Value.End);
+ Cooldown.FromTime(action.Cooldown.Value.Start, action.Cooldown.Value.End);
}
- if (Action != null && _toggled != Action.Toggled)
+ if (ActionId != null && _toggled != action.Toggled)
{
- _toggled = Action.Toggled;
+ _toggled = action.Toggled;
}
}
@@ -350,7 +343,7 @@ public sealed class ActionButton : Control
public void Depress(GUIBoundKeyEventArgs args, bool depress)
{
// action can still be toggled if it's allowed to stay selected
- if (Action is not {Enabled: true})
+ if (!Actions.TryGetActionData(ActionId, out var action) || action is not { Enabled: true })
return;
if (_depressed && !depress)
@@ -368,14 +361,14 @@ public sealed class ActionButton : Control
HighlightRect.Visible = _beingHovered;
// always show the normal empty button style if no action in this slot
- if (Action == null)
+ if (!Actions.TryGetActionData(ActionId, out var action))
{
SetOnlyStylePseudoClass(ContainerButton.StylePseudoClassNormal);
return;
}
// show a hover only if the action is usable or another action is being dragged on top of this
- if (_beingHovered && (Controller.IsDragging || Action.Enabled))
+ if (_beingHovered && (Controller.IsDragging || action.Enabled))
{
SetOnlyStylePseudoClass(ContainerButton.StylePseudoClassHover);
}
@@ -390,16 +383,16 @@ public sealed class ActionButton : Control
}
// if it's toggled on, always show the toggled on style (currently same as depressed style)
- if (Action.Toggled || Controller.SelectingTargetFor == Action)
+ if (action.Toggled || Controller.SelectingTargetFor == ActionId)
{
// when there's a toggle sprite, we're showing that sprite instead of highlighting this slot
- SetOnlyStylePseudoClass(Action.IconOn != null
+ SetOnlyStylePseudoClass(action.IconOn != null
? ContainerButton.StylePseudoClassNormal
: ContainerButton.StylePseudoClassPressed);
return;
}
- if (!Action.Enabled)
+ if (!action.Enabled)
{
SetOnlyStylePseudoClass(ContainerButton.StylePseudoClassDisabled);
return;
diff --git a/Content.Client/UserInterface/Systems/Actions/Controls/ActionButtonContainer.cs b/Content.Client/UserInterface/Systems/Actions/Controls/ActionButtonContainer.cs
index 5dfa53f5ab..bbda27f0a0 100644
--- a/Content.Client/UserInterface/Systems/Actions/Controls/ActionButtonContainer.cs
+++ b/Content.Client/UserInterface/Systems/Actions/Controls/ActionButtonContainer.cs
@@ -1,4 +1,3 @@
-using Content.Shared.Actions.ActionTypes;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
@@ -30,7 +29,7 @@ public class ActionButtonContainer : GridContainer
}
}
- public void SetActionData(params ActionType?[] actionTypes)
+ public void SetActionData(params EntityUid?[] actionTypes)
{
ClearActionData();
@@ -40,7 +39,7 @@ public class ActionButtonContainer : GridContainer
if (action == null)
continue;
- ((ActionButton) GetChild(i)).UpdateData(action);
+ ((ActionButton) GetChild(i)).UpdateData(action.Value);
}
}
diff --git a/Content.IntegrationTests/Tests/Actions/ActionsAddedTest.cs b/Content.IntegrationTests/Tests/Actions/ActionsAddedTest.cs
index 0f4250c508..c61fa8974f 100644
--- a/Content.IntegrationTests/Tests/Actions/ActionsAddedTest.cs
+++ b/Content.IntegrationTests/Tests/Actions/ActionsAddedTest.cs
@@ -1,6 +1,5 @@
using System.Linq;
using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.CombatMode;
using Robust.Server.Player;
using Robust.Shared.GameObjects;
@@ -25,6 +24,8 @@ public sealed class ActionsAddedTest
var sEntMan = server.ResolveDependency();
var cEntMan = client.ResolveDependency();
var session = server.ResolveDependency().ServerSessions.Single();
+ var sActionSystem = server.System();
+ var cActionSystem = client.System();
// Dummy ticker is disabled - client should be in control of a normal mob.
Assert.NotNull(session.AttachedEntity);
@@ -43,21 +44,24 @@ public sealed class ActionsAddedTest
// This action should have a non-null event both on the server & client.
var evType = typeof(ToggleCombatActionEvent);
- var sActions = sComp!.Actions.Where(
- x => x is InstantAction act && act.Event?.GetType() == evType).ToArray();
- var cActions = cComp!.Actions.Where(
- x => x is InstantAction act && act.Event?.GetType() == evType).ToArray();
+ var sActions = sActionSystem.GetActions(ent).Where(
+ x => x.Comp is InstantActionComponent act && act.Event?.GetType() == evType).ToArray();
+ var cActions = cActionSystem.GetActions(ent).Where(
+ x => x.Comp is InstantActionComponent act && act.Event?.GetType() == evType).ToArray();
Assert.That(sActions.Length, Is.EqualTo(1));
Assert.That(cActions.Length, Is.EqualTo(1));
- var sAct = (InstantAction) sActions[0];
- var cAct = (InstantAction) cActions[0];
+ var sAct = sActions[0].Comp;
+ var cAct = cActions[0].Comp;
+
+ Assert.NotNull(sAct);
+ Assert.NotNull(cAct);
// Finally, these two actions are not the same object
// required, because integration tests do not respect the [NonSerialized] attribute and will simply events by reference.
Assert.That(ReferenceEquals(sAct, cAct), Is.False);
- Assert.That(ReferenceEquals(sAct.Event, cAct.Event), Is.False);
+ Assert.That(ReferenceEquals(sAct.BaseEvent, cAct.BaseEvent), Is.False);
await pair.CleanReturnAsync();
}
diff --git a/Content.Server/Abilities/Mime/MimePowersComponent.cs b/Content.Server/Abilities/Mime/MimePowersComponent.cs
index 8d2205cdb6..35393093b7 100644
--- a/Content.Server/Abilities/Mime/MimePowersComponent.cs
+++ b/Content.Server/Abilities/Mime/MimePowersComponent.cs
@@ -1,8 +1,6 @@
-using Content.Shared.Actions.ActionTypes;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-using Robust.Shared.Utility;
namespace Content.Server.Abilities.Mime
{
@@ -22,18 +20,12 @@ namespace Content.Server.Abilities.Mime
/// The wall prototype to use.
///
[DataField("wallPrototype", customTypeSerializer: typeof(PrototypeIdSerializer))]
- public string WallPrototype = "WallInvisible";
+ public string WallPrototype = "ActionMimeInvisibleWall";
- [DataField("invisibleWallAction")]
- public InstantAction InvisibleWallAction = new()
- {
- UseDelay = TimeSpan.FromSeconds(30),
- Icon = new SpriteSpecifier.Texture(new("Structures/Walls/solid.rsi/full.png")),
- DisplayName = "mime-invisible-wall",
- Description = "mime-invisible-wall-desc",
- Priority = -1,
- Event = new InvisibleWallActionEvent(),
- };
+ [DataField("invisibleWallAction", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string? InvisibleWallAction;
+
+ [DataField("invisibleWallActionEntity")] public EntityUid? InvisibleWallActionEntity;
// The vow zone lies below
public bool VowBroken = false;
diff --git a/Content.Server/Abilities/Mime/MimePowersSystem.cs b/Content.Server/Abilities/Mime/MimePowersSystem.cs
index 1e32bc09c5..5a47fb1d8d 100644
--- a/Content.Server/Abilities/Mime/MimePowersSystem.cs
+++ b/Content.Server/Abilities/Mime/MimePowersSystem.cs
@@ -1,16 +1,15 @@
using Content.Server.Popups;
+using Content.Server.Speech.Muting;
using Content.Shared.Actions;
+using Content.Shared.Actions.Events;
using Content.Shared.Alert;
using Content.Shared.Coordinates.Helpers;
-using Content.Shared.Physics;
-using Content.Shared.Doors.Components;
using Content.Shared.Maps;
using Content.Shared.Mobs.Components;
-using Robust.Shared.Physics.Components;
-using Robust.Shared.Timing;
-using Content.Server.Speech.Muting;
+using Content.Shared.Physics;
using Robust.Shared.Containers;
using Robust.Shared.Map;
+using Robust.Shared.Timing;
namespace Content.Server.Abilities.Mime
{
@@ -29,8 +28,10 @@ namespace Content.Server.Abilities.Mime
{
base.Initialize();
SubscribeLocalEvent(OnComponentInit);
+ SubscribeLocalEvent(OnComponentMapInit);
SubscribeLocalEvent(OnInvisibleWall);
}
+
public override void Update(float frameTime)
{
base.Update(frameTime);
@@ -53,10 +54,14 @@ namespace Content.Server.Abilities.Mime
private void OnComponentInit(EntityUid uid, MimePowersComponent component, ComponentInit args)
{
EnsureComp(uid);
- _actionsSystem.AddAction(uid, component.InvisibleWallAction, uid);
_alertsSystem.ShowAlert(uid, AlertType.VowOfSilence);
}
+ private void OnComponentMapInit(EntityUid uid, MimePowersComponent component, MapInitEvent args)
+ {
+ _actionsSystem.AddAction(uid, ref component.InvisibleWallActionEntity, component.InvisibleWallAction, uid);
+ }
+
///
/// Creates an invisible wall in a free space after some checks.
///
@@ -116,7 +121,7 @@ namespace Content.Server.Abilities.Mime
RemComp(uid);
_alertsSystem.ClearAlert(uid, AlertType.VowOfSilence);
_alertsSystem.ShowAlert(uid, AlertType.VowBroken);
- _actionsSystem.RemoveAction(uid, mimePowers.InvisibleWallAction);
+ _actionsSystem.RemoveAction(uid, mimePowers.InvisibleWallActionEntity);
}
///
@@ -139,11 +144,7 @@ namespace Content.Server.Abilities.Mime
AddComp(uid);
_alertsSystem.ClearAlert(uid, AlertType.VowBroken);
_alertsSystem.ShowAlert(uid, AlertType.VowOfSilence);
- _actionsSystem.AddAction(uid, mimePowers.InvisibleWallAction, uid);
+ _actionsSystem.AddAction(uid, ref mimePowers.InvisibleWallActionEntity, mimePowers.InvisibleWallAction, uid);
}
}
-
- public sealed partial class InvisibleWallActionEvent : InstantActionEvent
- {
- }
}
diff --git a/Content.Server/Actions/ActionOnInteractComponent.cs b/Content.Server/Actions/ActionOnInteractComponent.cs
index a668ee43f3..9efe3d6ba0 100644
--- a/Content.Server/Actions/ActionOnInteractComponent.cs
+++ b/Content.Server/Actions/ActionOnInteractComponent.cs
@@ -1,5 +1,6 @@
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Interaction;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
namespace Content.Server.Actions;
@@ -19,12 +20,8 @@ namespace Content.Server.Actions;
[RegisterComponent]
public sealed partial class ActionOnInteractComponent : Component
{
- [DataField("activateActions")]
- public List? ActivateActions;
+ [DataField("actions", customTypeSerializer: typeof(PrototypeIdListSerializer))]
+ public List? Actions;
- [DataField("entityActions")]
- public List? EntityActions;
-
- [DataField("worldActions")]
- public List? WorldActions;
+ [DataField("actionEntities")] public List? ActionEntities;
}
diff --git a/Content.Server/Actions/ActionOnInteractSystem.cs b/Content.Server/Actions/ActionOnInteractSystem.cs
index baa01e9123..31f579f7ec 100644
--- a/Content.Server/Actions/ActionOnInteractSystem.cs
+++ b/Content.Server/Actions/ActionOnInteractSystem.cs
@@ -1,5 +1,4 @@
using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Interaction;
using Robust.Shared.Random;
using Robust.Shared.Timing;
@@ -25,51 +24,41 @@ public sealed class ActionOnInteractSystem : EntitySystem
private void OnActivate(EntityUid uid, ActionOnInteractComponent component, ActivateInWorldEvent args)
{
- if (args.Handled || component.ActivateActions == null)
+ if (args.Handled || component.ActionEntities == null)
return;
- var options = new List();
- foreach (var action in component.ActivateActions)
- {
- if (ValidAction(action))
- options.Add(action);
- }
-
+ var options = GetValidActions(component.ActionEntities);
if (options.Count == 0)
return;
- var act = _random.Pick(options);
+ var (actId, act) = _random.Pick(options);
if (act.Event != null)
act.Event.Performer = args.User;
act.Provider = uid;
- _actions.PerformAction(args.User, null, act, act.Event, _timing.CurTime, false);
+ _actions.PerformAction(args.User, null, actId, act, act.Event, _timing.CurTime, false);
args.Handled = true;
}
private void OnAfterInteract(EntityUid uid, ActionOnInteractComponent component, AfterInteractEvent args)
{
- if (args.Handled)
+ if (args.Handled || component.ActionEntities == null)
return;
// First, try entity target actions
- if (args.Target != null && component.EntityActions != null)
+ if (args.Target != null)
{
- var entOptions = new List();
- foreach (var action in component.EntityActions)
+ var entOptions = GetValidActions(component.ActionEntities, args.CanReach);
+ for (var i = entOptions.Count - 1; i >= 0; i--)
{
- if (!ValidAction(action, args.CanReach))
- continue;
-
+ var action = entOptions[i].Comp;
if (!_actions.ValidateEntityTarget(args.User, args.Target.Value, action))
- continue;
-
- entOptions.Add(action);
+ entOptions.RemoveAt(i);
}
if (entOptions.Count > 0)
{
- var entAct = _random.Pick(entOptions);
+ var (entActId, entAct) = _random.Pick(entOptions);
if (entAct.Event != null)
{
entAct.Event.Performer = args.User;
@@ -77,32 +66,25 @@ public sealed class ActionOnInteractSystem : EntitySystem
}
entAct.Provider = uid;
- _actions.PerformAction(args.User, null, entAct, entAct.Event, _timing.CurTime, false);
+ _actions.PerformAction(args.User, null, entActId, entAct, entAct.Event, _timing.CurTime, false);
args.Handled = true;
return;
}
}
// else: try world target actions
- if (component.WorldActions == null)
- return;
-
- var options = new List();
- foreach (var action in component.WorldActions)
+ var options = GetValidActions(component.ActionEntities, args.CanReach);
+ for (var i = options.Count - 1; i >= 0; i--)
{
- if (!ValidAction(action, args.CanReach))
- continue;
-
+ var action = options[i].Comp;
if (!_actions.ValidateWorldTarget(args.User, args.ClickLocation, action))
- continue;
-
- options.Add(action);
+ options.RemoveAt(i);
}
if (options.Count == 0)
return;
- var act = _random.Pick(options);
+ var (actId, act) = _random.Pick(options);
if (act.Event != null)
{
act.Event.Performer = args.User;
@@ -110,22 +92,44 @@ public sealed class ActionOnInteractSystem : EntitySystem
}
act.Provider = uid;
- _actions.PerformAction(args.User, null, act, act.Event, _timing.CurTime, false);
+ _actions.PerformAction(args.User, null, actId, act, act.Event, _timing.CurTime, false);
args.Handled = true;
}
- private bool ValidAction(ActionType act, bool canReach = true)
+ private bool ValidAction(BaseActionComponent action, bool canReach = true)
{
- if (!act.Enabled)
+ if (!action.Enabled)
return false;
- if (act.Charges.HasValue && act.Charges <= 0)
+ if (action.Charges.HasValue && action.Charges <= 0)
return false;
var curTime = _timing.CurTime;
- if (act.Cooldown.HasValue && act.Cooldown.Value.End > curTime)
+ if (action.Cooldown.HasValue && action.Cooldown.Value.End > curTime)
return false;
- return canReach || act is TargetedAction { CheckCanAccess: false };
+ return canReach || action is BaseTargetActionComponent { CheckCanAccess: false };
+ }
+
+ private List<(EntityUid Id, T Comp)> GetValidActions(List? actions, bool canReach = true) where T : BaseActionComponent
+ {
+ var valid = new List<(EntityUid Id, T Comp)>();
+
+ if (actions == null)
+ return valid;
+
+ foreach (var id in actions)
+ {
+ if (!_actions.TryGetActionData(id, out var baseAction) ||
+ baseAction as T is not { } action ||
+ !ValidAction(action, canReach))
+ {
+ continue;
+ }
+
+ valid.Add((id, action));
+ }
+
+ return valid;
}
}
diff --git a/Content.Server/Actions/ActionsSystem.cs b/Content.Server/Actions/ActionsSystem.cs
index 495640d98b..21054bbd7a 100644
--- a/Content.Server/Actions/ActionsSystem.cs
+++ b/Content.Server/Actions/ActionsSystem.cs
@@ -1,9 +1,5 @@
-using Content.Server.Chat;
-using Content.Server.Chat.Systems;
using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
using JetBrains.Annotations;
-using Robust.Server.GameObjects;
namespace Content.Server.Actions
{
diff --git a/Content.Server/Animals/Components/EggLayerComponent.cs b/Content.Server/Animals/Components/EggLayerComponent.cs
index 980bcbf72b..551e665f72 100644
--- a/Content.Server/Animals/Components/EggLayerComponent.cs
+++ b/Content.Server/Animals/Components/EggLayerComponent.cs
@@ -1,8 +1,6 @@
-using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Storage;
using Robust.Shared.Audio;
-using Robust.Shared.Serialization;
+using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Animals.Components;
@@ -14,8 +12,8 @@ namespace Content.Server.Animals.Components;
[RegisterComponent]
public sealed partial class EggLayerComponent : Component
{
- [DataField("eggLayAction", customTypeSerializer: typeof(PrototypeIdSerializer))]
- public string EggLayAction = "AnimalLayEgg";
+ [DataField("eggLayAction", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string EggLayAction = "ActionAnimalLayEgg";
[ViewVariables(VVAccess.ReadWrite)]
[DataField("hungerUsage")]
@@ -51,5 +49,3 @@ public sealed partial class EggLayerComponent : Component
[DataField("accumulatedFrametime")]
public float AccumulatedFrametime;
}
-
-public sealed partial class EggLayInstantActionEvent : InstantActionEvent {}
diff --git a/Content.Server/Animals/Systems/EggLayerSystem.cs b/Content.Server/Animals/Systems/EggLayerSystem.cs
index a9ab3c5f29..abfd74ad01 100644
--- a/Content.Server/Animals/Systems/EggLayerSystem.cs
+++ b/Content.Server/Animals/Systems/EggLayerSystem.cs
@@ -1,20 +1,18 @@
using Content.Server.Actions;
using Content.Server.Animals.Components;
using Content.Server.Popups;
-using Content.Shared.Actions.ActionTypes;
+using Content.Shared.Actions.Events;
using Content.Shared.Nutrition.Components;
using Content.Shared.Nutrition.EntitySystems;
using Content.Shared.Storage;
using Robust.Server.GameObjects;
using Robust.Shared.Player;
-using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Server.Animals.Systems;
public sealed class EggLayerSystem : EntitySystem
{
- [Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly ActionsSystem _actions = default!;
[Dependency] private readonly AudioSystem _audio = default!;
@@ -54,10 +52,10 @@ public sealed class EggLayerSystem : EntitySystem
private void OnComponentInit(EntityUid uid, EggLayerComponent component, ComponentInit args)
{
- if (!_prototype.TryIndex(component.EggLayAction, out var action))
+ if (string.IsNullOrWhiteSpace(component.EggLayAction))
return;
- _actions.AddAction(uid, new InstantAction(action), uid);
+ _actions.AddAction(uid, Spawn(component.EggLayAction), uid);
component.CurrentEggLayCooldown = _random.NextFloat(component.EggLayCooldownMin, component.EggLayCooldownMax);
}
diff --git a/Content.Server/Atmos/Components/GasTankComponent.cs b/Content.Server/Atmos/Components/GasTankComponent.cs
index 5815fba489..8b411e38db 100644
--- a/Content.Server/Atmos/Components/GasTankComponent.cs
+++ b/Content.Server/Atmos/Components/GasTankComponent.cs
@@ -1,6 +1,7 @@
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Atmos;
using Robust.Shared.Audio;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Atmos.Components
{
@@ -93,8 +94,10 @@ namespace Content.Server.Atmos.Components
[DataField("tankFragmentScale"), ViewVariables(VVAccess.ReadWrite)]
public float TankFragmentScale = 2 * Atmospherics.OneAtmosphere;
- [DataField("toggleAction", required: true)]
- public InstantAction ToggleAction = new();
+ [DataField("toggleAction", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string ToggleAction = "ActionToggleInternals";
+
+ [DataField("toggleActionEntity")] public EntityUid? ToggleActionEntity;
///
/// Valve to release gas from tank
diff --git a/Content.Server/Atmos/EntitySystems/GasTankSystem.cs b/Content.Server/Atmos/EntitySystems/GasTankSystem.cs
index cdd174ce18..dcfcdf3331 100644
--- a/Content.Server/Atmos/EntitySystems/GasTankSystem.cs
+++ b/Content.Server/Atmos/EntitySystems/GasTankSystem.cs
@@ -1,4 +1,3 @@
-using System.Numerics;
using Content.Server.Atmos.Components;
using Content.Server.Body.Components;
using Content.Server.Body.Systems;
@@ -8,17 +7,17 @@ using Content.Server.UserInterface;
using Content.Shared.Actions;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
-using Content.Shared.Toggleable;
using Content.Shared.Examine;
+using Content.Shared.Toggleable;
+using Content.Shared.Verbs;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Server.Player;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
+using Robust.Shared.Physics.Systems;
using Robust.Shared.Player;
using Robust.Shared.Random;
-using Content.Shared.Verbs;
-using Robust.Shared.Physics.Systems;
namespace Content.Server.Atmos.EntitySystems
{
@@ -105,7 +104,7 @@ namespace Content.Server.Atmos.EntitySystems
private void OnGetActions(EntityUid uid, GasTankComponent component, GetItemActionsEvent args)
{
- args.Actions.Add(component.ToggleAction);
+ args.AddAction(ref component.ToggleActionEntity, component.ToggleAction);
}
private void OnExamined(EntityUid uid, GasTankComponent component, ExaminedEvent args)
@@ -233,7 +232,7 @@ namespace Content.Server.Atmos.EntitySystems
if (_internals.TryConnectTank(internals, component.Owner))
component.User = internals.Owner;
- _actions.SetToggled(component.ToggleAction, component.IsConnected);
+ _actions.SetToggled(component.ToggleActionEntity, component.IsConnected);
// Couldn't toggle!
if (!component.IsConnected)
@@ -255,7 +254,7 @@ namespace Content.Server.Atmos.EntitySystems
var internals = GetInternalsComponent(component);
component.User = null;
- _actions.SetToggled(component.ToggleAction, false);
+ _actions.SetToggled(component.ToggleActionEntity, false);
_internals.DisconnectTank(internals);
component.DisconnectStream?.Stop();
diff --git a/Content.Server/Bed/BedSystem.cs b/Content.Server/Bed/BedSystem.cs
index 9791eb2621..c9795928e5 100644
--- a/Content.Server/Bed/BedSystem.cs
+++ b/Content.Server/Bed/BedSystem.cs
@@ -2,9 +2,9 @@ using Content.Server.Actions;
using Content.Server.Bed.Components;
using Content.Server.Bed.Sleep;
using Content.Server.Body.Systems;
+using Content.Server.Construction;
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Bed;
using Content.Shared.Bed.Sleep;
using Content.Shared.Body.Components;
@@ -12,9 +12,7 @@ using Content.Shared.Buckle.Components;
using Content.Shared.Damage;
using Content.Shared.Emag.Components;
using Content.Shared.Emag.Systems;
-using Content.Server.Construction;
using Content.Shared.Mobs.Systems;
-using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Server.Bed
@@ -23,7 +21,6 @@ namespace Content.Server.Bed
{
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly ActionsSystem _actionsSystem = default!;
- [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly SleepingSystem _sleepingSystem = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
@@ -42,18 +39,17 @@ namespace Content.Server.Bed
private void ManageUpdateList(EntityUid uid, HealOnBuckleComponent component, ref BuckleChangeEvent args)
{
- _prototypeManager.TryIndex("Sleep", out var sleepAction);
if (args.Buckling)
{
AddComp(uid);
component.NextHealTime = _timing.CurTime + TimeSpan.FromSeconds(component.HealTime);
- if (sleepAction != null)
- _actionsSystem.AddAction(args.BuckledEntity, new InstantAction(sleepAction), null);
+ component.SleepAction = Spawn(SleepingSystem.SleepActionId);
+ _actionsSystem.AddAction(args.BuckledEntity, component.SleepAction.Value, null);
return;
}
- if (sleepAction != null)
- _actionsSystem.RemoveAction(args.BuckledEntity, sleepAction);
+ if (component.SleepAction != null)
+ _actionsSystem.RemoveAction(args.BuckledEntity, component.SleepAction.Value);
_sleepingSystem.TryWaking(args.BuckledEntity);
RemComp(uid);
diff --git a/Content.Server/Bed/Components/HealOnBuckleComponent.cs b/Content.Server/Bed/Components/HealOnBuckleComponent.cs
index f02a30535d..f29fe30429 100644
--- a/Content.Server/Bed/Components/HealOnBuckleComponent.cs
+++ b/Content.Server/Bed/Components/HealOnBuckleComponent.cs
@@ -17,5 +17,7 @@ namespace Content.Server.Bed.Components
public float SleepMultiplier = 3f;
public TimeSpan NextHealTime = TimeSpan.Zero; //Next heal
+
+ [DataField("sleepAction")] public EntityUid? SleepAction;
}
}
diff --git a/Content.Server/Bed/Sleep/SleepingSystem.cs b/Content.Server/Bed/Sleep/SleepingSystem.cs
index 93286fa3bc..4f7b66d9e5 100644
--- a/Content.Server/Bed/Sleep/SleepingSystem.cs
+++ b/Content.Server/Bed/Sleep/SleepingSystem.cs
@@ -1,7 +1,6 @@
using Content.Server.Actions;
using Content.Server.Popups;
using Content.Server.Sound.Components;
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Audio;
using Content.Shared.Bed.Sleep;
using Content.Shared.Damage;
@@ -10,16 +9,14 @@ using Content.Shared.IdentityManagement;
using Content.Shared.Interaction;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
-using Content.Shared.StatusEffect;
using Content.Shared.Slippery;
+using Content.Shared.StatusEffect;
using Content.Shared.Stunnable;
using Content.Shared.Verbs;
-using Robust.Shared.Audio;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
-using Content.Shared.Interaction.Components;
namespace Content.Server.Bed.Sleep
{
@@ -33,6 +30,8 @@ namespace Content.Server.Bed.Sleep
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!;
+ [ValidatePrototypeId] public const string SleepActionId = "ActionSleep";
+
public override void Initialize()
{
base.Initialize();
@@ -53,7 +52,6 @@ namespace Content.Server.Bed.Sleep
///
private void OnSleepStateChanged(EntityUid uid, MobStateComponent component, SleepStateChangedEvent args)
{
- _prototypeManager.TryIndex("Wake", out var wakeAction);
if (args.FellAsleep)
{
// Expiring status effects would remove the components needed for sleeping
@@ -72,16 +70,8 @@ namespace Content.Server.Bed.Sleep
emitSound.PopUp = sleepSound.PopUp;
}
- if (wakeAction != null)
- {
- var wakeInstance = new InstantAction(wakeAction);
- wakeInstance.Cooldown = (_gameTiming.CurTime, _gameTiming.CurTime + TimeSpan.FromSeconds(15));
- _actionsSystem.AddAction(uid, wakeInstance, null);
- }
return;
}
- if (wakeAction != null)
- _actionsSystem.RemoveAction(uid, wakeAction);
RemComp(uid);
RemComp(uid);
@@ -194,8 +184,7 @@ namespace Content.Server.Bed.Sleep
RaiseLocalEvent(uid, ref tryingToSleepEvent);
if (tryingToSleepEvent.Cancelled) return false;
- if (_prototypeManager.TryIndex("Sleep", out var sleepAction))
- _actionsSystem.RemoveAction(uid, sleepAction);
+ _actionsSystem.RemoveAction(uid, SleepActionId);
EnsureComp(uid);
return true;
diff --git a/Content.Server/Bible/BibleSystem.cs b/Content.Server/Bible/BibleSystem.cs
index 8186db5a49..18f34ba1cc 100644
--- a/Content.Server/Bible/BibleSystem.cs
+++ b/Content.Server/Bible/BibleSystem.cs
@@ -4,6 +4,7 @@ using Content.Server.Ghost.Roles.Events;
using Content.Server.Popups;
using Content.Shared.ActionBlocker;
using Content.Shared.Actions;
+using Content.Shared.Bible;
using Content.Shared.Damage;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction;
@@ -179,8 +180,9 @@ namespace Content.Server.Bible
if (component.AlreadySummoned)
return;
- args.Actions.Add(component.SummonAction);
+ args.AddAction(ref component.SummonActionEntity, component.SummonAction);
}
+
private void OnSummon(EntityUid uid, SummonableComponent component, SummonActionEvent args)
{
AttemptSummon(component, args.Performer, Transform(args.Performer));
@@ -238,12 +240,7 @@ namespace Content.Server.Bible
Transform(familiar).AttachParent(component.Owner);
}
component.AlreadySummoned = true;
- _actionsSystem.RemoveAction(user, component.SummonAction);
+ _actionsSystem.RemoveAction(user, component.SummonActionEntity);
}
}
-
- public sealed partial class SummonActionEvent : InstantActionEvent
- {
-
- }
}
diff --git a/Content.Server/Bible/Components/SummonableComponent.cs b/Content.Server/Bible/Components/SummonableComponent.cs
index 821f25bb80..62f536d5d6 100644
--- a/Content.Server/Bible/Components/SummonableComponent.cs
+++ b/Content.Server/Bible/Components/SummonableComponent.cs
@@ -1,7 +1,5 @@
-using Content.Shared.Actions.ActionTypes;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-using Robust.Shared.Utility;
namespace Content.Server.Bible.Components
{
@@ -18,7 +16,7 @@ namespace Content.Server.Bible.Components
public string? SpecialItemPrototype = null;
public bool AlreadySummoned = false;
- [DataField("requriesBibleUser")]
+ [DataField("requiresBibleUser")]
public bool RequiresBibleUser = true;
///
@@ -27,14 +25,11 @@ namespace Content.Server.Bible.Components
[ViewVariables]
public EntityUid? Summon = null;
- [DataField("summonAction")]
- public InstantAction SummonAction = new()
- {
- Icon = new SpriteSpecifier.Texture(new ("Clothing/Head/Hats/witch.rsi/icon.png")),
- DisplayName = "bible-summon-verb",
- Description = "bible-summon-verb-desc",
- Event = new SummonActionEvent(),
- };
+ [DataField("summonAction", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string SummonAction = "ActionBibleSummon";
+
+ [DataField("summonActionEntity")]
+ public EntityUid? SummonActionEntity;
/// Used for respawning
[DataField("accumulator")]
diff --git a/Content.Server/Clothing/Components/MaskComponent.cs b/Content.Server/Clothing/Components/MaskComponent.cs
index 3071c3edf9..704ea11ece 100644
--- a/Content.Server/Clothing/Components/MaskComponent.cs
+++ b/Content.Server/Clothing/Components/MaskComponent.cs
@@ -1,5 +1,5 @@
-using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Clothing.Components
{
@@ -7,14 +7,15 @@ namespace Content.Server.Clothing.Components
[RegisterComponent]
public sealed partial class MaskComponent : Component
{
+ [DataField("toggleAction", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string ToggleAction = "ActionToggleMask";
+
///
/// This mask can be toggled (pulled up/down)
///
- [DataField("toggleAction")]
- public InstantAction? ToggleAction = null;
+ [DataField("toggleActionEntity")]
+ public EntityUid? ToggleActionEntity;
public bool IsToggled = false;
}
-
- public sealed partial class ToggleMaskEvent : InstantActionEvent { }
}
diff --git a/Content.Server/Clothing/MaskSystem.cs b/Content.Server/Clothing/MaskSystem.cs
index acf63ad98d..fbf6ac6077 100644
--- a/Content.Server/Clothing/MaskSystem.cs
+++ b/Content.Server/Clothing/MaskSystem.cs
@@ -9,6 +9,7 @@ using Content.Server.Nutrition.EntitySystems;
using Content.Server.Popups;
using Content.Server.VoiceMask;
using Content.Shared.Actions;
+using Content.Shared.Clothing;
using Content.Shared.Clothing.Components;
using Content.Shared.Clothing.EntitySystems;
using Content.Shared.IdentityManagement.Components;
@@ -38,20 +39,20 @@ namespace Content.Server.Clothing
private void OnGetActions(EntityUid uid, MaskComponent component, GetItemActionsEvent args)
{
- if (component.ToggleAction != null && !args.InHands)
- args.Actions.Add(component.ToggleAction);
+ if (!args.InHands)
+ args.AddAction(ref component.ToggleActionEntity, component.ToggleAction);
}
private void OnToggleMask(EntityUid uid, MaskComponent mask, ToggleMaskEvent args)
{
- if (mask.ToggleAction == null)
+ if (mask.ToggleActionEntity == null)
return;
if (!_inventorySystem.TryGetSlotEntity(args.Performer, "mask", out var existing) || !mask.Owner.Equals(existing))
return;
mask.IsToggled ^= true;
- _actionSystem.SetToggled(mask.ToggleAction, mask.IsToggled);
+ _actionSystem.SetToggled(mask.ToggleActionEntity, mask.IsToggled);
// Pulling mask down can change identity, so we want to update that
_identity.QueueIdentityUpdate(args.Performer);
@@ -67,11 +68,11 @@ namespace Content.Server.Clothing
// set to untoggled when unequipped, so it isn't left in a 'pulled down' state
private void OnGotUnequipped(EntityUid uid, MaskComponent mask, GotUnequippedEvent args)
{
- if (mask.ToggleAction == null)
+ if (mask.ToggleActionEntity == null)
return;
mask.IsToggled = false;
- _actionSystem.SetToggled(mask.ToggleAction, mask.IsToggled);
+ _actionSystem.SetToggled(mask.ToggleActionEntity, mask.IsToggled);
ToggleMaskComponents(uid, mask, args.Equipee, true);
}
diff --git a/Content.Server/Devour/DevourSystem.cs b/Content.Server/Devour/DevourSystem.cs
index 5421751100..3b15e725fe 100644
--- a/Content.Server/Devour/DevourSystem.cs
+++ b/Content.Server/Devour/DevourSystem.cs
@@ -1,10 +1,8 @@
-using Content.Shared.Devour;
using Content.Server.Body.Systems;
-using Content.Shared.Humanoid;
using Content.Shared.Chemistry.Components;
-using Content.Server.Devour.Components;
-using Content.Shared.DoAfter;
-using Robust.Shared.Serialization;
+using Content.Shared.Devour;
+using Content.Shared.Devour.Components;
+using Content.Shared.Humanoid;
namespace Content.Server.Devour;
diff --git a/Content.Server/Dragon/Components/DragonComponent.cs b/Content.Server/Dragon/Components/DragonComponent.cs
index 8138f2a66b..f403979a00 100644
--- a/Content.Server/Dragon/Components/DragonComponent.cs
+++ b/Content.Server/Dragon/Components/DragonComponent.cs
@@ -1,10 +1,4 @@
-using System.Threading;
-using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
-using Content.Shared.Chemistry.Reagent;
-using Content.Shared.Whitelist;
using Robust.Shared.Audio;
-using Robust.Shared.Containers;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
@@ -42,11 +36,14 @@ namespace Content.Server.Dragon
///
[ViewVariables(VVAccess.ReadWrite), DataField("maxAccumulator")] public float RiftMaxAccumulator = 300f;
+ [DataField("spawnRiftAction", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string SpawnRiftAction = "ActionSpawnRift";
+
///
/// Spawns a rift which can summon more mobs.
///
- [DataField("spawnRiftAction")]
- public InstantAction? SpawnRiftAction;
+ [DataField("spawnRiftActionEntity")]
+ public EntityUid? SpawnRiftActionEntity;
[ViewVariables(VVAccess.ReadWrite), DataField("riftPrototype", customTypeSerializer: typeof(PrototypeIdSerializer))]
public string RiftPrototype = "CarpRift";
@@ -61,8 +58,4 @@ namespace Content.Server.Dragon
Params = AudioParams.Default.WithVolume(3f),
};
}
-
- public sealed partial class DragonDevourActionEvent : EntityTargetActionEvent {}
-
- public sealed partial class DragonSpawnRiftActionEvent : InstantActionEvent {}
}
diff --git a/Content.Server/Dragon/DragonSystem.cs b/Content.Server/Dragon/DragonSystem.cs
index 82aa10b037..0169e16c44 100644
--- a/Content.Server/Dragon/DragonSystem.cs
+++ b/Content.Server/Dragon/DragonSystem.cs
@@ -1,28 +1,20 @@
-using Content.Server.Body.Systems;
+using System.Numerics;
using Content.Server.Chat.Systems;
using Content.Server.GameTicking;
using Content.Server.NPC;
using Content.Server.NPC.Systems;
using Content.Server.Popups;
-using Content.Server.Station.Systems;
using Content.Shared.Actions;
-using Content.Shared.Chemistry.Components;
using Content.Shared.Damage;
-using Content.Shared.DoAfter;
using Content.Shared.Dragon;
using Content.Shared.Examine;
-using Content.Shared.Humanoid;
using Content.Shared.Maps;
using Content.Shared.Mobs;
-using Content.Shared.Mobs.Components;
using Content.Shared.Movement.Systems;
-using Robust.Shared.Containers;
-using Robust.Shared.Player;
+using Content.Shared.Sprite;
using Robust.Shared.GameStates;
using Robust.Shared.Map;
-using Robust.Shared.Random;
-using System.Numerics;
-using Content.Shared.Sprite;
+using Robust.Shared.Player;
using Robust.Shared.Serialization.Manager;
namespace Content.Server.Dragon;
@@ -56,6 +48,7 @@ public sealed partial class DragonSystem : EntitySystem
base.Initialize();
SubscribeLocalEvent(OnStartup);
+ SubscribeLocalEvent(OnMapInit);
SubscribeLocalEvent(OnShutdown);
SubscribeLocalEvent(OnDragonRift);
SubscribeLocalEvent(OnDragonMove);
@@ -302,10 +295,12 @@ public sealed partial class DragonSystem : EntitySystem
private void OnStartup(EntityUid uid, DragonComponent component, ComponentStartup args)
{
- if (component.SpawnRiftAction != null)
- _actionsSystem.AddAction(uid, component.SpawnRiftAction, null);
-
Roar(component);
}
+
+ private void OnMapInit(EntityUid uid, DragonComponent component, MapInitEvent args)
+ {
+ _actionsSystem.AddAction(uid, ref component.SpawnRiftActionEntity, component.SpawnRiftAction);
+ }
}
diff --git a/Content.Server/GameTicking/Rules/Components/ZombieRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/ZombieRuleComponent.cs
index bb708ece3e..5f31f52fc4 100644
--- a/Content.Server/GameTicking/Rules/Components/ZombieRuleComponent.cs
+++ b/Content.Server/GameTicking/Rules/Components/ZombieRuleComponent.cs
@@ -1,6 +1,6 @@
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Roles;
using Robust.Shared.Audio;
+using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
@@ -94,6 +94,6 @@ public sealed partial class ZombieRuleComponent : Component
[DataField("shuttleCalled")]
public bool ShuttleCalled;
- [ValidatePrototypeId]
- public const string ZombifySelfActionPrototype = "TurnUndead";
+ [ValidatePrototypeId]
+ public const string ZombifySelfActionPrototype = "ActionTurnUndead";
}
diff --git a/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs b/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs
index 96d11fed94..bdc40f102e 100644
--- a/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs
+++ b/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs
@@ -11,7 +11,6 @@ using Content.Server.RoundEnd;
using Content.Server.Station.Components;
using Content.Server.Station.Systems;
using Content.Server.Zombies;
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.CCVar;
using Content.Shared.Humanoid;
using Content.Shared.Mind;
@@ -195,9 +194,7 @@ public sealed class ZombieRuleSystem : GameRuleSystem
private void OnZombifySelf(EntityUid uid, ZombifyOnDeathComponent component, ZombifySelfActionEvent args)
{
_zombie.ZombifyEntity(uid);
-
- var action = new InstantAction(_prototypeManager.Index(ZombieRuleComponent.ZombifySelfActionPrototype));
- _action.RemoveAction(uid, action);
+ _action.RemoveAction(uid, ZombieRuleComponent.ZombifySelfActionPrototype);
}
private float GetInfectedFraction(bool includeOffStation = true, bool includeDead = false)
@@ -325,7 +322,7 @@ public sealed class ZombieRuleSystem : GameRuleSystem
EnsureComp(ownedEntity);
EnsureComp(ownedEntity);
var inCharacterName = MetaData(ownedEntity).EntityName;
- var action = new InstantAction(_prototypeManager.Index(ZombieRuleComponent.ZombifySelfActionPrototype));
+ var action = Spawn(ZombieRuleComponent.ZombifySelfActionPrototype);
_action.AddAction(mind.OwnedEntity.Value, action, null);
var message = Loc.GetString("zombie-patientzero-role-greeting");
diff --git a/Content.Server/Ghost/GhostSystem.cs b/Content.Server/Ghost/GhostSystem.cs
index 9e01cfa758..3b63bdfdc4 100644
--- a/Content.Server/Ghost/GhostSystem.cs
+++ b/Content.Server/Ghost/GhostSystem.cs
@@ -16,7 +16,6 @@ using Content.Shared.Mind.Components;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.Movement.Events;
-using Content.Shared.Roles.Jobs;
using Content.Shared.Storage.Components;
using Robust.Server.GameObjects;
using Robust.Server.Player;
@@ -48,6 +47,7 @@ namespace Content.Server.Ghost
SubscribeLocalEvent(OnGhostStartup);
SubscribeLocalEvent(OnGhostShutdown);
+ SubscribeLocalEvent(OnGhostMapInit);
SubscribeLocalEvent(OnGhostExamine);
@@ -121,13 +121,7 @@ namespace Content.Server.Ghost
eye.VisibilityMask |= (uint) VisibilityFlags.Ghost;
}
- var time = _gameTiming.CurTime;
- component.TimeOfDeath = time;
-
- // TODO ghost: remove once ghosts are persistent and aren't deleted when returning to body
- if (component.Action.UseDelay != null)
- component.Action.Cooldown = (time, time + component.Action.UseDelay.Value);
- _actions.AddAction(uid, component.Action, null);
+ component.TimeOfDeath = _gameTiming.CurTime;
}
private void OnGhostShutdown(EntityUid uid, GhostComponent component, ComponentShutdown args)
@@ -149,10 +143,26 @@ namespace Content.Server.Ghost
eye.VisibilityMask &= ~(uint) VisibilityFlags.Ghost;
}
- _actions.RemoveAction(uid, component.Action);
+ _actions.RemoveAction(uid, component.ActionEntity);
}
}
+ private void OnGhostMapInit(EntityUid uid, GhostComponent component, MapInitEvent args)
+ {
+ // TODO ghost: remove once ghosts are persistent and aren't deleted when returning to body
+ var time = _gameTiming.CurTime;
+ var action = _actions.AddAction(uid, ref component.ActionEntity, component.Action);
+ if (action?.UseDelay != null)
+ {
+ action.Cooldown = (time, time + action.UseDelay.Value);
+ Dirty(component.ActionEntity!.Value, action);
+ }
+
+ _actions.AddAction(uid, ref component.ToggleLightingActionEntity, component.ToggleLightingAction);
+ _actions.AddAction(uid, ref component.ToggleFoVActionEntity, component.ToggleFoVAction);
+ _actions.AddAction(uid, ref component.ToggleGhostsActionEntity, component.ToggleGhostsAction);
+ }
+
private void OnGhostExamine(EntityUid uid, GhostComponent component, ExaminedEvent args)
{
var timeSinceDeath = _gameTiming.RealTime.Subtract(component.TimeOfDeath);
diff --git a/Content.Server/Guardian/GuardianHostComponent.cs b/Content.Server/Guardian/GuardianHostComponent.cs
index e924bed776..6a747ae86a 100644
--- a/Content.Server/Guardian/GuardianHostComponent.cs
+++ b/Content.Server/Guardian/GuardianHostComponent.cs
@@ -1,7 +1,6 @@
-using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
using Robust.Shared.Containers;
-using Robust.Shared.Utility;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Guardian
{
@@ -24,17 +23,9 @@ namespace Content.Server.Guardian
///
[ViewVariables] public ContainerSlot GuardianContainer = default!;
- [DataField("action")]
- public InstantAction Action = new()
- {
- DisplayName = "action-name-guardian",
- Description = "action-description-guardian",
- Icon = new SpriteSpecifier.Texture(new ("Interface/Actions/manifest.png")),
- UseDelay = TimeSpan.FromSeconds(2),
- CheckCanInteract = false, // allow use while stunned, etc. Gets removed on death anyways.
- Event = new GuardianToggleActionEvent(),
- };
- }
+ [DataField("action", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string Action = "ActionToggleGuardian";
- public sealed partial class GuardianToggleActionEvent : InstantActionEvent { };
+ [DataField("actionEntity")] public EntityUid? ActionEntity;
+ }
}
diff --git a/Content.Server/Guardian/GuardianSystem.cs b/Content.Server/Guardian/GuardianSystem.cs
index 24d7a7e838..323a0fc515 100644
--- a/Content.Server/Guardian/GuardianSystem.cs
+++ b/Content.Server/Guardian/GuardianSystem.cs
@@ -1,12 +1,12 @@
-using Content.Server.Inventory;
-using Content.Server.Popups;
using Content.Server.Body.Systems;
+using Content.Server.Popups;
using Content.Shared.Actions;
using Content.Shared.Audio;
using Content.Shared.Damage;
using Content.Shared.DoAfter;
using Content.Shared.Examine;
using Content.Shared.Guardian;
+using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
@@ -16,7 +16,6 @@ using Robust.Server.GameObjects;
using Robust.Shared.Containers;
using Robust.Shared.Player;
using Robust.Shared.Utility;
-using Content.Shared.Hands.Components;
namespace Content.Server.Guardian
{
@@ -47,6 +46,7 @@ namespace Content.Server.Guardian
SubscribeLocalEvent(OnGuardianUnplayer);
SubscribeLocalEvent(OnHostInit);
+ SubscribeLocalEvent(OnHostMapInit);
SubscribeLocalEvent(OnHostMove);
SubscribeLocalEvent(OnHostStateChange);
SubscribeLocalEvent(OnHostShutdown);
@@ -90,7 +90,11 @@ namespace Content.Server.Guardian
private void OnHostInit(EntityUid uid, GuardianHostComponent component, ComponentInit args)
{
component.GuardianContainer = uid.EnsureContainer("GuardianContainer");
- _actionSystem.AddAction(uid, component.Action, null);
+ }
+
+ private void OnHostMapInit(EntityUid uid, GuardianHostComponent component, MapInitEvent args)
+ {
+ _actionSystem.AddAction(uid, ref component.ActionEntity, component.Action);
}
private void OnHostShutdown(EntityUid uid, GuardianHostComponent component, ComponentShutdown args)
@@ -102,7 +106,7 @@ namespace Content.Server.Guardian
_bodySystem.GibBody(component.HostedGuardian.Value);
EntityManager.QueueDeleteEntity(component.HostedGuardian.Value);
- _actionSystem.RemoveAction(uid, component.Action);
+ _actionSystem.RemoveAction(uid, component.ActionEntity);
}
private void OnGuardianAttackAttempt(EntityUid uid, GuardianComponent component, AttackAttemptEvent args)
diff --git a/Content.Server/Light/EntitySystems/HandheldLightSystem.cs b/Content.Server/Light/EntitySystems/HandheldLightSystem.cs
index 6417bad4b5..e74585f0b2 100644
--- a/Content.Server/Light/EntitySystems/HandheldLightSystem.cs
+++ b/Content.Server/Light/EntitySystems/HandheldLightSystem.cs
@@ -2,7 +2,6 @@ using Content.Server.Actions;
using Content.Server.Popups;
using Content.Server.PowerCell;
using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Examine;
using Content.Shared.Interaction;
using Content.Shared.Light;
@@ -12,11 +11,8 @@ using Content.Shared.Toggleable;
using Content.Shared.Verbs;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
-using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.GameStates;
-using Robust.Shared.Player;
-using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Server.Light.EntitySystems
@@ -27,7 +23,6 @@ namespace Content.Server.Light.EntitySystems
[Dependency] private readonly ActionsSystem _actions = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly PowerCellSystem _powerCell = default!;
- [Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
@@ -77,14 +72,7 @@ namespace Content.Server.Light.EntitySystems
private void OnGetActions(EntityUid uid, HandheldLightComponent component, GetItemActionsEvent args)
{
- if (component.ToggleAction == null
- && _proto.TryIndex(component.ToggleActionId, out InstantActionPrototype? act))
- {
- component.ToggleAction = new(act);
- }
-
- if (component.ToggleAction != null)
- args.Actions.Add(component.ToggleAction);
+ args.AddAction(ref component.ToggleActionEntity, component.ToggleAction);
}
private void OnToggleAction(EntityUid uid, HandheldLightComponent component, ToggleActionEvent args)
@@ -107,20 +95,12 @@ namespace Content.Server.Light.EntitySystems
private void OnMapInit(EntityUid uid, HandheldLightComponent component, MapInitEvent args)
{
- if (component.ToggleAction == null
- && _proto.TryIndex(component.ToggleActionId, out InstantActionPrototype? act))
- {
- component.ToggleAction = new(act);
- }
-
- if (component.ToggleAction != null)
- _actions.AddAction(uid, component.ToggleAction, null);
+ _actions.AddAction(uid, ref component.ToggleActionEntity, component.ToggleAction);
}
private void OnShutdown(EntityUid uid, HandheldLightComponent component, ComponentShutdown args)
{
- if (component.ToggleAction != null)
- _actions.RemoveAction(uid, component.ToggleAction);
+ _actions.RemoveAction(uid, component.ToggleActionEntity);
}
private byte? GetLevel(EntityUid uid, HandheldLightComponent component)
diff --git a/Content.Server/Light/EntitySystems/UnpoweredFlashlightSystem.cs b/Content.Server/Light/EntitySystems/UnpoweredFlashlightSystem.cs
index b136645264..6a7e7ba8a0 100644
--- a/Content.Server/Light/EntitySystems/UnpoweredFlashlightSystem.cs
+++ b/Content.Server/Light/EntitySystems/UnpoweredFlashlightSystem.cs
@@ -45,7 +45,7 @@ namespace Content.Server.Light.EntitySystems
private void OnGetActions(EntityUid uid, UnpoweredFlashlightComponent component, GetItemActionsEvent args)
{
- args.Actions.Add(component.ToggleAction);
+ args.AddAction(ref component.ToggleActionEntity, component.ToggleAction);
}
private void AddToggleLightVerbs(EntityUid uid, UnpoweredFlashlightComponent component, GetVerbsEvent args)
@@ -66,7 +66,7 @@ namespace Content.Server.Light.EntitySystems
private void OnMindAdded(EntityUid uid, UnpoweredFlashlightComponent component, MindAddedMessage args)
{
- _actionsSystem.AddAction(uid, component.ToggleAction, null);
+ _actionsSystem.AddAction(uid, ref component.ToggleActionEntity, component.ToggleAction);
}
private void OnGotEmagged(EntityUid uid, UnpoweredFlashlightComponent component, ref GotEmaggedEvent args)
@@ -97,7 +97,7 @@ namespace Content.Server.Light.EntitySystems
_audioSystem.PlayPvs(flashlight.ToggleSound, uid);
RaiseLocalEvent(uid, new LightToggleEvent(flashlight.LightOn), true);
- _actionsSystem.SetToggled(flashlight.ToggleAction, flashlight.LightOn);
+ _actionsSystem.SetToggled(flashlight.ToggleActionEntity, flashlight.LightOn);
}
}
}
diff --git a/Content.Server/Magic/Components/SpellbookComponent.cs b/Content.Server/Magic/Components/SpellbookComponent.cs
index 1f2d12183c..ebc3c88043 100644
--- a/Content.Server/Magic/Components/SpellbookComponent.cs
+++ b/Content.Server/Magic/Components/SpellbookComponent.cs
@@ -1,4 +1,4 @@
-using Content.Shared.Actions.ActionTypes;
+using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
namespace Content.Server.Magic.Components;
@@ -13,22 +13,14 @@ public sealed partial class SpellbookComponent : Component
/// List of spells that this book has. This is a combination of the WorldSpells, EntitySpells, and InstantSpells.
///
[ViewVariables]
- public readonly List Spells = new();
+ public readonly List Spells = new();
///
/// The three fields below is just used for initialization.
///
- [DataField("worldSpells", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))]
+ [DataField("spells", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))]
[ViewVariables(VVAccess.ReadWrite)]
- public Dictionary WorldSpells = new();
-
- [DataField("entitySpells", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))]
- [ViewVariables(VVAccess.ReadWrite)]
- public Dictionary EntitySpells = new();
-
- [DataField("instantSpells", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))]
- [ViewVariables(VVAccess.ReadWrite)]
- public Dictionary InstantSpells = new();
+ public Dictionary SpellActions = new();
[DataField("learnTime")]
[ViewVariables(VVAccess.ReadWrite)]
diff --git a/Content.Server/Magic/MagicSystem.cs b/Content.Server/Magic/MagicSystem.cs
index e75bae0249..1632b654b9 100644
--- a/Content.Server/Magic/MagicSystem.cs
+++ b/Content.Server/Magic/MagicSystem.cs
@@ -4,10 +4,8 @@ using Content.Server.Body.Systems;
using Content.Server.Chat.Systems;
using Content.Server.Doors.Systems;
using Content.Server.Magic.Components;
-using Content.Server.Magic.Events;
using Content.Server.Weapons.Ranged.Systems;
using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Body.Components;
using Content.Shared.Coordinates.Helpers;
using Content.Shared.DoAfter;
@@ -15,6 +13,7 @@ using Content.Shared.Doors.Components;
using Content.Shared.Doors.Systems;
using Content.Shared.Interaction.Events;
using Content.Shared.Magic;
+using Content.Shared.Magic.Events;
using Content.Shared.Maps;
using Content.Shared.Physics;
using Content.Shared.Spawners.Components;
@@ -22,11 +21,8 @@ using Content.Shared.Storage;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Map;
-using Robust.Shared.Player;
-using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Serialization.Manager;
-using Robust.Shared.Serialization.Manager.Exceptions;
namespace Content.Server.Magic;
@@ -38,7 +34,6 @@ public sealed class MagicSystem : EntitySystem
[Dependency] private readonly ISerializationManager _seriMan = default!;
[Dependency] private readonly IComponentFactory _compFact = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
- [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly DoorBoltSystem _boltsSystem = default!;
[Dependency] private readonly BodySystem _bodySystem = default!;
@@ -81,23 +76,9 @@ public sealed class MagicSystem : EntitySystem
private void OnInit(EntityUid uid, SpellbookComponent component, ComponentInit args)
{
//Negative charges means the spell can be used without it running out.
- foreach (var (id, charges) in component.WorldSpells)
+ foreach (var (id, charges) in component.SpellActions)
{
- var spell = new WorldTargetAction(_prototypeManager.Index(id));
- _actionsSystem.SetCharges(spell, charges < 0 ? null : charges);
- component.Spells.Add(spell);
- }
-
- foreach (var (id, charges) in component.InstantSpells)
- {
- var spell = new InstantAction(_prototypeManager.Index(id));
- _actionsSystem.SetCharges(spell, charges < 0 ? null : charges);
- component.Spells.Add(spell);
- }
-
- foreach (var (id, charges) in component.EntitySpells)
- {
- var spell = new EntityTargetAction(_prototypeManager.Index(id));
+ var spell = Spawn(id);
_actionsSystem.SetCharges(spell, charges < 0 ? null : charges);
component.Spells.Add(spell);
}
diff --git a/Content.Server/Medical/Stethoscope/Components/StethoscopeComponent.cs b/Content.Server/Medical/Stethoscope/Components/StethoscopeComponent.cs
index 8daf47809d..d7e971e953 100644
--- a/Content.Server/Medical/Stethoscope/Components/StethoscopeComponent.cs
+++ b/Content.Server/Medical/Stethoscope/Components/StethoscopeComponent.cs
@@ -1,8 +1,7 @@
-using System.Threading;
-using Content.Shared.Actions.ActionTypes;
-using Robust.Shared.Utility;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-namespace Content.Server.Medical.Components
+namespace Content.Server.Medical.Stethoscope.Components
{
///
/// Adds an innate verb when equipped to use a stethoscope.
@@ -15,12 +14,9 @@ namespace Content.Server.Medical.Components
[DataField("delay")]
public float Delay = 2.5f;
- public EntityTargetAction Action = new()
- {
- Icon = new SpriteSpecifier.Texture(new ("Clothing/Neck/Misc/stethoscope.rsi/icon.png")),
- DisplayName = "stethoscope-verb",
- Priority = -1,
- Event = new StethoscopeActionEvent(),
- };
+ [DataField("action", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string Action = "ActionStethoscope";
+
+ [DataField("actionEntity")] public EntityUid? ActionEntity;
}
}
diff --git a/Content.Server/Medical/Stethoscope/StethoscopeSystem.cs b/Content.Server/Medical/Stethoscope/StethoscopeSystem.cs
index 9aee322667..9f28d44d59 100644
--- a/Content.Server/Medical/Stethoscope/StethoscopeSystem.cs
+++ b/Content.Server/Medical/Stethoscope/StethoscopeSystem.cs
@@ -1,19 +1,21 @@
using Content.Server.Body.Components;
using Content.Server.Medical.Components;
+using Content.Server.Medical.Stethoscope.Components;
using Content.Server.Popups;
using Content.Shared.Actions;
using Content.Shared.Clothing.Components;
using Content.Shared.Damage;
+using Content.Shared.DoAfter;
using Content.Shared.FixedPoint;
using Content.Shared.Inventory.Events;
-using Content.Shared.Verbs;
+using Content.Shared.Medical;
+using Content.Shared.Medical.Stethoscope;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
-using Content.Shared.DoAfter;
-using Content.Shared.Medical;
+using Content.Shared.Verbs;
using Robust.Shared.Utility;
-namespace Content.Server.Medical
+namespace Content.Server.Medical.Stethoscope
{
public sealed class StethoscopeSystem : EntitySystem
{
@@ -97,7 +99,7 @@ namespace Content.Server.Medical
private void OnGetActions(EntityUid uid, StethoscopeComponent component, GetItemActionsEvent args)
{
- args.Actions.Add(component.Action);
+ args.AddAction(ref component.ActionEntity, component.Action);
}
// construct the doafter and start it
@@ -156,6 +158,4 @@ namespace Content.Server.Medical
return msg;
}
}
-
- public sealed partial class StethoscopeActionEvent : EntityTargetActionEvent {}
}
diff --git a/Content.Server/Mobs/CritMobActionsSystem.cs b/Content.Server/Mobs/CritMobActionsSystem.cs
index 7babea79ab..9d0a6b4a85 100644
--- a/Content.Server/Mobs/CritMobActionsSystem.cs
+++ b/Content.Server/Mobs/CritMobActionsSystem.cs
@@ -2,7 +2,7 @@
using Content.Server.Chat.Systems;
using Content.Server.Popups;
using Content.Server.Speech.Muting;
-using Content.Shared.Actions;
+using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Robust.Server.Console;
@@ -82,24 +82,3 @@ public sealed class CritMobActionsSystem : EntitySystem
args.Handled = true;
}
}
-
-///
-/// Only applies to mobs in crit capable of ghosting/succumbing
-///
-public sealed partial class CritSuccumbEvent : InstantActionEvent
-{
-}
-
-///
-/// Only applies/has functionality to mobs in crit that have
-///
-public sealed partial class CritFakeDeathEvent : InstantActionEvent
-{
-}
-
-///
-/// Only applies to mobs capable of speaking, as a last resort in crit
-///
-public sealed partial class CritLastWordsEvent : InstantActionEvent
-{
-}
diff --git a/Content.Server/Polymorph/Components/PolymorphableComponent.cs b/Content.Server/Polymorph/Components/PolymorphableComponent.cs
index cd928637e7..c05d36a842 100644
--- a/Content.Server/Polymorph/Components/PolymorphableComponent.cs
+++ b/Content.Server/Polymorph/Components/PolymorphableComponent.cs
@@ -1,4 +1,3 @@
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Polymorph;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
@@ -11,7 +10,7 @@ namespace Content.Server.Polymorph.Components
/// A list of all the polymorphs that the entity has.
/// Used to manage them and remove them if needed.
///
- public Dictionary? PolymorphActions = null;
+ public Dictionary? PolymorphActions = null;
///
/// The polymorphs that the entity starts out being able to do.
diff --git a/Content.Server/Polymorph/Systems/PolymorphSystem.cs b/Content.Server/Polymorph/Systems/PolymorphSystem.cs
index fda90f2cb0..d630cbcad7 100644
--- a/Content.Server/Polymorph/Systems/PolymorphSystem.cs
+++ b/Content.Server/Polymorph/Systems/PolymorphSystem.cs
@@ -5,7 +5,6 @@ using Content.Server.Mind.Commands;
using Content.Server.Nutrition;
using Content.Server.Polymorph.Components;
using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Buckle;
using Content.Shared.Damage;
using Content.Shared.Hands.EntitySystems;
@@ -46,6 +45,8 @@ namespace Content.Server.Polymorph.Systems
private ISawmill _sawmill = default!;
+ private const string RevertPolymorphId = "ActionRevertPolymorph";
+
public override void Initialize()
{
base.Initialize();
@@ -97,23 +98,21 @@ namespace Content.Server.Polymorph.Systems
if (proto.Forced)
return;
- var act = new InstantAction
+ var actionId = Spawn(RevertPolymorphId);
+ if (_actions.TryGetActionData(actionId, out var action))
{
- Event = new RevertPolymorphActionEvent(),
- EntityIcon = component.Parent,
- DisplayName = Loc.GetString("polymorph-revert-action-name"),
- Description = Loc.GetString("polymorph-revert-action-description"),
- UseDelay = TimeSpan.FromSeconds(proto.Delay),
- };
+ action.EntityIcon = component.Parent;
+ action.UseDelay = TimeSpan.FromSeconds(proto.Delay);
+ }
- _actions.AddAction(uid, act, null);
+ _actions.AddAction(uid, actionId, null, null, action);
}
private void OnBeforeFullyEaten(EntityUid uid, PolymorphedEntityComponent comp, BeforeFullyEatenEvent args)
{
if (!_proto.TryIndex(comp.Prototype, out var proto))
{
- _sawmill.Error("Invalid polymorph prototype {comp.Prototype}");
+ _sawmill.Error($"Invalid polymorph prototype {comp.Prototype}");
return;
}
@@ -334,23 +333,19 @@ namespace Content.Server.Polymorph.Systems
return;
var entproto = _proto.Index(polyproto.Entity);
-
- var act = new InstantAction
+ var actionId = Spawn(RevertPolymorphId);
+ if (_actions.TryGetActionData(actionId, out var baseAction) &&
+ baseAction is InstantActionComponent action)
{
- Event = new PolymorphActionEvent
- {
- Prototype = polyproto,
- },
- DisplayName = Loc.GetString("polymorph-self-action-name", ("target", entproto.Name)),
- Description = Loc.GetString("polymorph-self-action-description", ("target", entproto.Name)),
- Icon = new SpriteSpecifier.EntityPrototype(polyproto.Entity),
- ItemIconStyle = ItemActionIconStyle.NoItem,
- };
+ action.Event = new PolymorphActionEvent { Prototype = polyproto };
+ action.Icon = new SpriteSpecifier.EntityPrototype(polyproto.Entity);
+ _metaData.SetEntityName(actionId, Loc.GetString("polymorph-self-action-name", ("target", entproto.Name)));
+ _metaData.SetEntityDescription(actionId, Loc.GetString("polymorph-self-action-description", ("target", entproto.Name)));
- polycomp.PolymorphActions ??= new();
-
- polycomp.PolymorphActions.Add(id, act);
- _actions.AddAction(target, act, target);
+ polycomp.PolymorphActions ??= new Dictionary();
+ polycomp.PolymorphActions.Add(id, actionId);
+ _actions.AddAction(target, actionId, target);
+ }
}
[PublicAPI]
@@ -399,18 +394,4 @@ namespace Content.Server.Polymorph.Systems
UpdateCollide();
}
}
-
- public sealed partial class PolymorphActionEvent : InstantActionEvent
- {
- ///
- /// The polymorph prototype containing all the information about
- /// the specific polymorph.
- ///
- public PolymorphPrototype Prototype = default!;
- }
-
- public sealed partial class RevertPolymorphActionEvent : InstantActionEvent
- {
-
- }
}
diff --git a/Content.Server/RatKing/RatKingComponent.cs b/Content.Server/RatKing/RatKingComponent.cs
index c38c922243..22e9de4135 100644
--- a/Content.Server/RatKing/RatKingComponent.cs
+++ b/Content.Server/RatKing/RatKingComponent.cs
@@ -1,4 +1,3 @@
-using Content.Shared.Actions.ActionTypes;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
@@ -7,11 +6,13 @@ namespace Content.Server.RatKing
[RegisterComponent]
public sealed partial class RatKingComponent : Component
{
+ [DataField("actionRaiseArmy", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string ActionRaiseArmy = "ActionRatKingRaiseArmy";
+
///
/// The action for the Raise Army ability
///
- [DataField("actionRaiseArmy", required: true)]
- public InstantAction ActionRaiseArmy = new();
+ [DataField("actionRaiseArmyEntity")] public EntityUid? ActionRaiseArmyEntity;
///
/// The amount of hunger one use of Raise Army consumes
@@ -25,11 +26,14 @@ namespace Content.Server.RatKing
[ViewVariables(VVAccess.ReadWrite), DataField("armyMobSpawnId", customTypeSerializer: typeof(PrototypeIdSerializer))]
public string ArmyMobSpawnId = "MobRatServant";
+ [DataField("actionDomain", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string ActionDomain = "ActionRatKingDomain";
+
///
/// The action for the Domain ability
///
- [DataField("actionDomain", required: true)]
- public InstantAction ActionDomain = new();
+ [DataField("actionDomainEntity")]
+ public EntityUid? ActionDomainEntity;
///
/// The amount of hunger one use of Domain consumes
@@ -43,4 +47,4 @@ namespace Content.Server.RatKing
[DataField("molesMiasmaPerDomain")]
public float MolesMiasmaPerDomain = 100f;
}
-};
+}
diff --git a/Content.Server/RatKing/RatKingSystem.cs b/Content.Server/RatKing/RatKingSystem.cs
index 5f9d7376a0..cd3cbbb691 100644
--- a/Content.Server/RatKing/RatKingSystem.cs
+++ b/Content.Server/RatKing/RatKingSystem.cs
@@ -1,13 +1,11 @@
using Content.Server.Actions;
using Content.Server.Atmos.EntitySystems;
-using Content.Server.Nutrition.Components;
using Content.Server.Popups;
-using Content.Shared.Actions;
using Content.Shared.Atmos;
using Content.Shared.Nutrition.Components;
using Content.Shared.Nutrition.EntitySystems;
+using Content.Shared.RatKing;
using Robust.Server.GameObjects;
-using Robust.Shared.Player;
namespace Content.Server.RatKing
{
@@ -23,16 +21,16 @@ namespace Content.Server.RatKing
{
base.Initialize();
- SubscribeLocalEvent(OnStartup);
+ SubscribeLocalEvent(OnMapInit);
SubscribeLocalEvent(OnRaiseArmy);
SubscribeLocalEvent(OnDomain);
}
- private void OnStartup(EntityUid uid, RatKingComponent component, ComponentStartup args)
+ private void OnMapInit(EntityUid uid, RatKingComponent component, MapInitEvent args)
{
- _action.AddAction(uid, component.ActionRaiseArmy, null);
- _action.AddAction(uid, component.ActionDomain, null);
+ _action.AddAction(uid, ref component.ActionRaiseArmyEntity, component.ActionRaiseArmy);
+ _action.AddAction(uid, ref component.ActionDomainEntity, component.ActionDomain);
}
///
@@ -86,14 +84,4 @@ namespace Content.Server.RatKing
tileMix?.AdjustMoles(Gas.Miasma, component.MolesMiasmaPerDomain);
}
}
-
- public sealed partial class RatKingRaiseArmyActionEvent : InstantActionEvent
- {
-
- }
-
- public sealed partial class RatKingDomainActionEvent : InstantActionEvent
- {
-
- }
-};
+}
diff --git a/Content.Server/Revenant/EntitySystems/RevenantSystem.cs b/Content.Server/Revenant/EntitySystems/RevenantSystem.cs
index 73a04ca8a4..4028f2dce2 100644
--- a/Content.Server/Revenant/EntitySystems/RevenantSystem.cs
+++ b/Content.Server/Revenant/EntitySystems/RevenantSystem.cs
@@ -1,35 +1,33 @@
using System.Numerics;
using Content.Server.Actions;
-using Content.Shared.Popups;
-using Content.Shared.Alert;
-using Content.Shared.Damage;
-using Content.Shared.Interaction;
using Content.Server.GameTicking;
-using Content.Shared.Stunnable;
-using Content.Shared.Revenant;
-using Robust.Server.GameObjects;
-using Robust.Shared.Random;
-using Content.Shared.StatusEffect;
-using Content.Server.Visible;
-using Content.Shared.Examine;
-using Robust.Shared.Prototypes;
-using Content.Shared.Actions.ActionTypes;
-using Content.Shared.Tag;
using Content.Server.Store.Components;
using Content.Server.Store.Systems;
+using Content.Server.Visible;
+using Content.Shared.Alert;
+using Content.Shared.Damage;
using Content.Shared.DoAfter;
+using Content.Shared.Examine;
using Content.Shared.FixedPoint;
+using Content.Shared.Interaction;
using Content.Shared.Maps;
using Content.Shared.Mobs.Systems;
using Content.Shared.Physics;
+using Content.Shared.Popups;
+using Content.Shared.Revenant;
using Content.Shared.Revenant.Components;
+using Content.Shared.StatusEffect;
+using Content.Shared.Stunnable;
+using Content.Shared.Tag;
+using Robust.Server.GameObjects;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Random;
namespace Content.Server.Revenant.EntitySystems;
public sealed partial class RevenantSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _random = default!;
- [Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly ActionsSystem _action = default!;
[Dependency] private readonly AlertsSystem _alerts = default!;
[Dependency] private readonly DamageableSystem _damage = default!;
@@ -46,11 +44,15 @@ public sealed partial class RevenantSystem : EntitySystem
[Dependency] private readonly VisibilitySystem _visibility = default!;
[Dependency] private readonly GameTicker _ticker = default!;
+ [ValidatePrototypeId]
+ private const string RevenantShopId = "ActionRevenantShop";
+
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent(OnStartup);
+ SubscribeLocalEvent(OnMapInit);
SubscribeLocalEvent(OnShop);
SubscribeLocalEvent(OnDamage);
@@ -82,9 +84,11 @@ public sealed partial class RevenantSystem : EntitySystem
//ghost vision
if (TryComp(uid, out EyeComponent? eye))
eye.VisibilityMask |= (uint) (VisibilityFlags.Ghost);
+ }
- var shopaction = new InstantAction(_proto.Index("RevenantShop"));
- _action.AddAction(uid, shopaction, null);
+ private void OnMapInit(EntityUid uid, RevenantComponent component, MapInitEvent args)
+ {
+ _action.AddAction(uid, Spawn(RevenantShopId), null);
}
private void OnStatusAdded(EntityUid uid, RevenantComponent component, StatusEffectAddedEvent args)
diff --git a/Content.Server/Sericulture/SericultureComponent.cs b/Content.Server/Sericulture/SericultureComponent.cs
index 38528e5309..2d258accc8 100644
--- a/Content.Server/Sericulture/SericultureComponent.cs
+++ b/Content.Server/Sericulture/SericultureComponent.cs
@@ -1,9 +1,11 @@
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+
namespace Content.Server.Sericulture;
[RegisterComponent]
public sealed partial class SericultureComponent : Component
{
-
[DataField("popupText")]
public string PopupText = "sericulture-failure-hunger";
@@ -13,8 +15,10 @@ public sealed partial class SericultureComponent : Component
[DataField("entityProduced", required: true)]
public string EntityProduced = "";
- [DataField("actionProto", required: true)]
- public string ActionProto = "";
+ [DataField("action", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string Action = "ActionSericulture";
+
+ [DataField("actionEntity")] public EntityUid? ActionEntity;
///
/// How long will it take to make.
diff --git a/Content.Server/Sericulture/SericultureSystem.cs b/Content.Server/Sericulture/SericultureSystem.cs
index 3570695a5f..011e225c28 100644
--- a/Content.Server/Sericulture/SericultureSystem.cs
+++ b/Content.Server/Sericulture/SericultureSystem.cs
@@ -1,14 +1,11 @@
using Content.Server.Actions;
using Content.Server.DoAfter;
using Content.Server.Popups;
-using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
+using Content.Server.Stack;
using Content.Shared.DoAfter;
using Content.Shared.Nutrition.Components;
using Content.Shared.Nutrition.EntitySystems;
-using Robust.Shared.Prototypes;
using Content.Shared.Sericulture;
-using Content.Server.Stack;
namespace Content.Server.Sericulture;
@@ -16,7 +13,6 @@ public sealed partial class SericultureSystem : EntitySystem
{
[Dependency] private readonly ActionsSystem _actionsSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
- [Dependency] private readonly IPrototypeManager _protoManager = default!;
[Dependency] private readonly HungerSystem _hungerSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly StackSystem _stackSystem = default!;
@@ -25,26 +21,20 @@ public sealed partial class SericultureSystem : EntitySystem
{
base.Initialize();
- SubscribeLocalEvent(OnCompInit);
+ SubscribeLocalEvent(OnCompMapInit);
SubscribeLocalEvent(OnCompRemove);
SubscribeLocalEvent(OnSericultureStart);
SubscribeLocalEvent(OnSericultureDoAfter);
}
- private void OnCompInit(EntityUid uid, SericultureComponent comp, ComponentInit args)
+ private void OnCompMapInit(EntityUid uid, SericultureComponent comp, MapInitEvent args)
{
- if (!_protoManager.TryIndex(comp.ActionProto, out var actionProto))
- return;
-
- _actionsSystem.AddAction(uid, new InstantAction(actionProto), uid);
+ _actionsSystem.AddAction(uid, ref comp.ActionEntity, comp.Action, uid);
}
private void OnCompRemove(EntityUid uid, SericultureComponent comp, ComponentShutdown args)
{
- if (!_protoManager.TryIndex(comp.ActionProto, out var actionProto))
- return;
-
- _actionsSystem.RemoveAction(uid, new InstantAction(actionProto));
+ _actionsSystem.RemoveAction(uid, comp.ActionEntity);
}
private void OnSericultureStart(EntityUid uid, SericultureComponent comp, SericultureActionEvent args)
@@ -96,6 +86,4 @@ public sealed partial class SericultureSystem : EntitySystem
return false;
}
-
- public sealed partial class SericultureActionEvent : InstantActionEvent { }
}
diff --git a/Content.Server/Silicons/Borgs/BorgSystem.Modules.cs b/Content.Server/Silicons/Borgs/BorgSystem.Modules.cs
index 3e93933605..51987fb721 100644
--- a/Content.Server/Silicons/Borgs/BorgSystem.Modules.cs
+++ b/Content.Server/Silicons/Borgs/BorgSystem.Modules.cs
@@ -57,8 +57,14 @@ public sealed partial class BorgSystem
private void OnSelectableInstalled(EntityUid uid, SelectableBorgModuleComponent component, ref BorgModuleInstalledEvent args)
{
var chassis = args.ChassisEnt;
- component.ModuleSwapAction.EntityIcon = uid;
- _actions.AddAction(chassis, component.ModuleSwapAction, uid);
+
+ var action = _actions.AddAction(chassis, ref component.ModuleSwapActionEntity, component.ModuleSwapActionId, uid);
+ if (action != null)
+ {
+ action.EntityIcon = uid;
+ Dirty(component.ModuleSwapActionEntity!.Value, action);
+ }
+
SelectModule(chassis, uid, moduleComp: component);
}
diff --git a/Content.Server/Silicons/Laws/SiliconLawSystem.cs b/Content.Server/Silicons/Laws/SiliconLawSystem.cs
index e83fc9b6aa..1d5c2e35e8 100644
--- a/Content.Server/Silicons/Laws/SiliconLawSystem.cs
+++ b/Content.Server/Silicons/Laws/SiliconLawSystem.cs
@@ -5,7 +5,6 @@ using Content.Server.Radio.Components;
using Content.Server.Roles;
using Content.Server.Station.Systems;
using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Administration;
using Content.Shared.Chat;
using Content.Shared.Emag.Components;
@@ -43,7 +42,6 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
{
base.Initialize();
- SubscribeLocalEvent(OnComponentStartup);
SubscribeLocalEvent(OnComponentShutdown);
SubscribeLocalEvent(OnMapInit);
SubscribeLocalEvent(OnMindAdded);
@@ -58,20 +56,15 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
SubscribeLocalEvent(OnExamined);
}
- private void OnComponentStartup(EntityUid uid, SiliconLawBoundComponent component, ComponentStartup args)
- {
- component.ProvidedAction = new(_prototype.Index(component.ViewLawsAction));
- _actions.AddAction(uid, component.ProvidedAction, null);
- }
-
private void OnComponentShutdown(EntityUid uid, SiliconLawBoundComponent component, ComponentShutdown args)
{
- if (component.ProvidedAction != null)
- _actions.RemoveAction(uid, component.ProvidedAction);
+ if (component.ViewLawsActionEntity != null)
+ _actions.RemoveAction(uid, component.ViewLawsActionEntity);
}
private void OnMapInit(EntityUid uid, SiliconLawBoundComponent component, MapInitEvent args)
{
+ _actions.AddAction(uid, ref component.ViewLawsActionEntity, component.ViewLawsAction);
GetLaws(uid, component);
}
diff --git a/Content.Server/Speech/Components/VocalComponent.cs b/Content.Server/Speech/Components/VocalComponent.cs
index 292498474c..029d638a66 100644
--- a/Content.Server/Speech/Components/VocalComponent.cs
+++ b/Content.Server/Speech/Components/VocalComponent.cs
@@ -1,10 +1,8 @@
-using Content.Server.Humanoid;
using Content.Server.Speech.EntitySystems;
-using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Chat.Prototypes;
using Content.Shared.Humanoid;
using Robust.Shared.Audio;
+using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
@@ -33,11 +31,11 @@ public sealed partial class VocalComponent : Component
[DataField("wilhelmProbability")]
public float WilhelmProbability = 0.0002f;
- [DataField("screamActionId", customTypeSerializer: typeof(PrototypeIdSerializer))]
- public string ScreamActionId = "Scream";
+ [DataField("screamAction", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string ScreamAction = "ActionScream";
- [DataField("screamAction")]
- public InstantAction? ScreamAction;
+ [DataField("screamActionEntity")]
+ public EntityUid? ScreamActionEntity;
///
/// Currently loaded emote sounds prototype, based on entity sex.
@@ -46,8 +44,3 @@ public sealed partial class VocalComponent : Component
[ViewVariables]
public EmoteSoundsPrototype? EmoteSounds = null;
}
-
-public sealed partial class ScreamActionEvent : InstantActionEvent
-{
-
-}
diff --git a/Content.Server/Speech/EntitySystems/MeleeSpeechSystem.cs b/Content.Server/Speech/EntitySystems/MeleeSpeechSystem.cs
index 4e884f5f3b..815b0224c8 100644
--- a/Content.Server/Speech/EntitySystems/MeleeSpeechSystem.cs
+++ b/Content.Server/Speech/EntitySystems/MeleeSpeechSystem.cs
@@ -1,8 +1,8 @@
using Content.Server.Administration.Logs;
using Content.Shared.Actions;
+using Content.Shared.Database;
using Content.Shared.Speech.Components;
using Content.Shared.Speech.EntitySystems;
-using Content.Shared.Database;
using Robust.Server.GameObjects;
namespace Content.Server.Speech.EntitySystems;
@@ -18,17 +18,15 @@ public sealed class MeleeSpeechSystem : SharedMeleeSpeechSystem
SubscribeLocalEvent(OnBattlecryChanged);
SubscribeLocalEvent(OnConfigureAction);
SubscribeLocalEvent(OnGetActions);
- SubscribeLocalEvent(OnComponentInit);
+ SubscribeLocalEvent(OnComponentMapInit);
}
- private void OnComponentInit(EntityUid uid, MeleeSpeechComponent component, ComponentInit args)
+ private void OnComponentMapInit(EntityUid uid, MeleeSpeechComponent component, MapInitEvent args)
{
- if (component.ConfigureAction != null)
- _actionSystem.AddAction(uid, component.ConfigureAction, uid);
+ _actionSystem.AddAction(uid, ref component.ConfigureActionEntity, component.ConfigureAction, uid);
}
private void OnGetActions(EntityUid uid, MeleeSpeechComponent component, GetItemActionsEvent args)
{
- if (component.ConfigureAction != null)
- args.Actions.Add(component.ConfigureAction);
+ args.AddAction(ref component.ConfigureActionEntity, component.ConfigureAction);
}
private void OnBattlecryChanged(EntityUid uid, MeleeSpeechComponent comp, MeleeSpeechBattlecryChangedMessage args)
{
diff --git a/Content.Server/Speech/EntitySystems/VocalSystem.cs b/Content.Server/Speech/EntitySystems/VocalSystem.cs
index 244b5124c2..5dccb8bf9c 100644
--- a/Content.Server/Speech/EntitySystems/VocalSystem.cs
+++ b/Content.Server/Speech/EntitySystems/VocalSystem.cs
@@ -1,10 +1,9 @@
using Content.Server.Actions;
using Content.Server.Chat.Systems;
-using Content.Server.Humanoid;
using Content.Server.Speech.Components;
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Chat.Prototypes;
using Content.Shared.Humanoid;
+using Content.Shared.Speech;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
@@ -32,21 +31,16 @@ public sealed class VocalSystem : EntitySystem
private void OnMapInit(EntityUid uid, VocalComponent component, MapInitEvent args)
{
// try to add scream action when vocal comp added
- if (_proto.TryIndex(component.ScreamActionId, out InstantActionPrototype? proto))
- {
- component.ScreamAction = new InstantAction(proto);
- _actions.AddAction(uid, component.ScreamAction, null);
- }
-
+ _actions.AddAction(uid, ref component.ScreamActionEntity, component.ScreamAction);
LoadSounds(uid, component);
}
private void OnShutdown(EntityUid uid, VocalComponent component, ComponentShutdown args)
{
// remove scream action when component removed
- if (component.ScreamAction != null)
+ if (component.ScreamActionEntity != null)
{
- _actions.RemoveAction(uid, component.ScreamAction);
+ _actions.RemoveAction(uid, component.ScreamActionEntity);
}
}
@@ -76,7 +70,7 @@ public sealed class VocalSystem : EntitySystem
if (args.Handled)
return;
- _chat.TryEmoteWithChat(uid, component.ScreamActionId);
+ _chat.TryEmoteWithChat(uid, component.ScreamId);
args.Handled = true;
}
diff --git a/Content.Server/Store/Systems/StoreSystem.Ui.cs b/Content.Server/Store/Systems/StoreSystem.Ui.cs
index 19f5a2105e..5de4542509 100644
--- a/Content.Server/Store/Systems/StoreSystem.Ui.cs
+++ b/Content.Server/Store/Systems/StoreSystem.Ui.cs
@@ -1,16 +1,15 @@
+using System.Linq;
using Content.Server.Actions;
using Content.Server.Administration.Logs;
using Content.Server.PDA.Ringer;
using Content.Server.Stack;
using Content.Server.Store.Components;
using Content.Server.UserInterface;
-using Content.Shared.Actions.ActionTypes;
+using Content.Shared.Database;
using Content.Shared.FixedPoint;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Store;
-using Content.Shared.Database;
using Robust.Server.GameObjects;
-using System.Linq;
namespace Content.Server.Store.Systems;
@@ -162,10 +161,9 @@ public sealed partial class StoreSystem
}
//give action
- if (listing.ProductAction != null)
+ if (!string.IsNullOrWhiteSpace(listing.ProductAction))
{
- var action = new InstantAction(_proto.Index(listing.ProductAction));
- _actions.AddAction(buyer, action, null);
+ _actions.AddAction(buyer, Spawn(listing.ProductAction), null);
}
//broadcast event
diff --git a/Content.Server/UserInterface/ActivatableUISystem.cs b/Content.Server/UserInterface/ActivatableUISystem.cs
index bde518cc96..c7f07c2f70 100644
--- a/Content.Server/UserInterface/ActivatableUISystem.cs
+++ b/Content.Server/UserInterface/ActivatableUISystem.cs
@@ -5,6 +5,7 @@ using Content.Shared.Hands;
using Content.Shared.Hands.Components;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
+using Content.Shared.UserInterface;
using Content.Shared.Verbs;
using Robust.Server.GameObjects;
using Robust.Server.Player;
diff --git a/Content.Server/UserInterface/IntrinsicUIComponent.cs b/Content.Server/UserInterface/IntrinsicUIComponent.cs
index 2e4814e054..4d3c7ffba9 100644
--- a/Content.Server/UserInterface/IntrinsicUIComponent.cs
+++ b/Content.Server/UserInterface/IntrinsicUIComponent.cs
@@ -1,57 +1,32 @@
-using Content.Shared.Actions.ActionTypes;
-using Robust.Shared.Reflection;
-using Robust.Shared.Serialization;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.UserInterface;
[RegisterComponent]
-public sealed partial class IntrinsicUIComponent : Component, ISerializationHooks
+public sealed partial class IntrinsicUIComponent : Component
{
///
/// List of UIs and their actions that this entity has.
///
- [DataField("uis", required: true)]
- public List UIs = new();
-
- void ISerializationHooks.AfterDeserialization()
- {
- for (var i = 0; i < UIs.Count; i++)
- {
- var ui = UIs[i];
- ui.AfterDeserialization();
- UIs[i] = ui;
- }
- }
+ [DataField("uis", required: true)] public List UIs = new();
}
[DataDefinition]
-public partial struct IntrinsicUIEntry
+public partial class IntrinsicUIEntry
{
- [ViewVariables] public Enum? Key { get; private set; } = null;
-
///
/// The BUI key that this intrinsic UI should open.
///
[DataField("key", required: true)]
- private string _keyRaw = default!;
+ public Enum? Key { get; private set; }
+
+ [DataField("toggleAction", customTypeSerializer: typeof(PrototypeIdSerializer), required: true)]
+ public string? ToggleAction;
///
/// The action used for this BUI.
///
- [DataField("toggleAction", required: true)]
- public InstantAction ToggleAction = new();
-
- public void AfterDeserialization()
- {
- var reflectionManager = IoCManager.Resolve();
- if (reflectionManager.TryParseEnumReference(_keyRaw, out var key))
- Key = key;
-
- if (ToggleAction.Event is ToggleIntrinsicUIEvent ev)
- {
- ev.Key = Key;
- }
- }
-
- public IntrinsicUIEntry() {}
+ [DataField("toggleActionEntity")]
+ public EntityUid? ToggleActionEntity = new();
}
diff --git a/Content.Server/UserInterface/IntrinsicUISystem.cs b/Content.Server/UserInterface/IntrinsicUISystem.cs
index c1268b0974..c7360c15bf 100644
--- a/Content.Server/UserInterface/IntrinsicUISystem.cs
+++ b/Content.Server/UserInterface/IntrinsicUISystem.cs
@@ -1,6 +1,6 @@
using Content.Server.Actions;
using Content.Shared.Actions;
-using JetBrains.Annotations;
+using Content.Shared.UserInterface;
using Robust.Server.GameObjects;
namespace Content.Server.UserInterface;
@@ -28,7 +28,7 @@ public sealed class IntrinsicUISystem : EntitySystem
foreach (var entry in component.UIs)
{
- _actionsSystem.AddAction(uid, entry.ToggleAction, null, actions);
+ _actionsSystem.AddAction(uid, ref entry.ToggleActionEntity, entry.ToggleAction, null, actions);
}
}
@@ -68,13 +68,6 @@ public sealed class IntrinsicUISystem : EntitySystem
}
}
-[UsedImplicitly]
-public sealed partial class ToggleIntrinsicUIEvent : InstantActionEvent
-{
- [ViewVariables]
- public Enum? Key { get; set; }
-}
-
// Competing with ActivatableUI for horrible event names.
public sealed class IntrinsicUIOpenAttemptEvent : CancellableEntityEventArgs
{
diff --git a/Content.Server/VendingMachines/VendingMachineSystem.cs b/Content.Server/VendingMachines/VendingMachineSystem.cs
index f80d4d600a..38576b8951 100644
--- a/Content.Server/VendingMachines/VendingMachineSystem.cs
+++ b/Content.Server/VendingMachines/VendingMachineSystem.cs
@@ -8,7 +8,6 @@ using Content.Server.UserInterface;
using Content.Shared.Access.Components;
using Content.Shared.Access.Systems;
using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Damage;
using Content.Shared.Destructible;
using Content.Shared.DoAfter;
@@ -44,6 +43,7 @@ namespace Content.Server.VendingMachines
base.Initialize();
_sawmill = Logger.GetSawmill("vending");
+ SubscribeLocalEvent(OnComponentMapInit);
SubscribeLocalEvent(OnPowerChanged);
SubscribeLocalEvent(OnBreak);
SubscribeLocalEvent(OnEmagged);
@@ -62,6 +62,12 @@ namespace Content.Server.VendingMachines
SubscribeLocalEvent(OnPriceCalculation);
}
+ private void OnComponentMapInit(EntityUid uid, VendingMachineComponent component, MapInitEvent args)
+ {
+ _action.AddAction(uid, ref component.ActionEntity, component.Action, uid);
+ Dirty(uid, component);
+ }
+
private void OnVendingPrice(EntityUid uid, VendingMachineComponent component, ref PriceCalculationEvent args)
{
var price = 0.0;
@@ -88,12 +94,6 @@ namespace Content.Server.VendingMachines
{
TryUpdateVisualState(uid, component);
}
-
- if (component.Action != null)
- {
- var action = new InstantAction(PrototypeManager.Index(component.Action));
- _action.AddAction(uid, action, uid);
- }
}
private void OnActivatableUIOpenAttempt(EntityUid uid, VendingMachineComponent component, ActivatableUIOpenAttemptEvent args)
diff --git a/Content.Server/VoiceMask/VoiceMaskSystem.Equip.cs b/Content.Server/VoiceMask/VoiceMaskSystem.Equip.cs
index 74c7b3b77e..87b3b9ef32 100644
--- a/Content.Server/VoiceMask/VoiceMaskSystem.Equip.cs
+++ b/Content.Server/VoiceMask/VoiceMaskSystem.Equip.cs
@@ -1,9 +1,6 @@
using Content.Server.Actions;
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Inventory;
using Content.Shared.Inventory.Events;
-using Content.Shared.Speech;
-using Robust.Shared.Prototypes;
namespace Content.Server.VoiceMask;
@@ -12,7 +9,6 @@ public sealed partial class VoiceMaskSystem
{
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly ActionsSystem _actions = default!;
- [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
private const string MaskSlot = "mask";
@@ -26,12 +22,7 @@ public sealed partial class VoiceMaskSystem
var comp = EnsureComp(user);
comp.VoiceName = component.LastSetName;
- if (!_prototypeManager.TryIndex(component.Action, out var action))
- {
- throw new ArgumentException("Could not get voice masking prototype.");
- }
-
- _actions.AddAction(user, (InstantAction) action.Clone(), uid);
+ _actions.AddAction(user, ref component.ActionEntity, component.Action, uid);
}
private void OnUnequip(EntityUid uid, VoiceMaskerComponent compnent, GotUnequippedEvent args)
diff --git a/Content.Server/VoiceMask/VoiceMaskSystem.cs b/Content.Server/VoiceMask/VoiceMaskSystem.cs
index fe15df3829..5143237fa0 100644
--- a/Content.Server/VoiceMask/VoiceMaskSystem.cs
+++ b/Content.Server/VoiceMask/VoiceMaskSystem.cs
@@ -1,7 +1,6 @@
using Content.Server.Administration.Logs;
using Content.Server.Chat.Systems;
using Content.Server.Popups;
-using Content.Shared.Actions;
using Content.Shared.Database;
using Content.Shared.Inventory.Events;
using Content.Shared.Preferences;
@@ -88,7 +87,3 @@ public sealed partial class VoiceMaskSystem : EntitySystem
UserInterfaceSystem.SetUiState(bui, new VoiceMaskBuiState(component.VoiceName));
}
}
-
-public sealed partial class VoiceMaskSetNameEvent : InstantActionEvent
-{
-}
diff --git a/Content.Server/VoiceMask/VoiceMaskerComponent.cs b/Content.Server/VoiceMask/VoiceMaskerComponent.cs
index 4ca22cbf94..c3cc29c527 100644
--- a/Content.Server/VoiceMask/VoiceMaskerComponent.cs
+++ b/Content.Server/VoiceMask/VoiceMaskerComponent.cs
@@ -1,4 +1,4 @@
-using Content.Shared.Actions.ActionTypes;
+using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.VoiceMask;
@@ -8,6 +8,8 @@ public sealed partial class VoiceMaskerComponent : Component
{
[ViewVariables(VVAccess.ReadWrite)] public string LastSetName = "Unknown";
- [DataField("action", customTypeSerializer: typeof(PrototypeIdSerializer))]
- public string Action = "ChangeVoiceMask";
+ [DataField("action", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string Action = "ActionChangeVoiceMask";
+
+ [DataField("actionEntity")] public EntityUid? ActionEntity;
}
diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/ArtifactSystem.Actions.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/ArtifactSystem.Actions.cs
index 69f9450a4f..91aaaa417a 100644
--- a/Content.Server/Xenoarchaeology/XenoArtifacts/ArtifactSystem.Actions.cs
+++ b/Content.Server/Xenoarchaeology/XenoArtifacts/ArtifactSystem.Actions.cs
@@ -1,7 +1,5 @@
using Content.Server.Actions;
using Content.Server.Popups;
-using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Xenoarchaeology.XenoArtifacts;
using Robust.Shared.Prototypes;
@@ -12,32 +10,29 @@ public partial class ArtifactSystem
[Dependency] private readonly ActionsSystem _actions = default!;
[Dependency] private readonly PopupSystem _popup = default!;
+ [ValidatePrototypeId] private const string ArtifactActivateActionId = "ActionArtifactActivate";
+
///
/// Used to add the artifact activation action (hehe), which lets sentient artifacts activate themselves,
/// either through admemery or the sentience effect.
///
public void InitializeActions()
{
- SubscribeLocalEvent(OnStartup);
+ SubscribeLocalEvent(OnStartup);
SubscribeLocalEvent(OnRemove);
SubscribeLocalEvent(OnSelfActivate);
}
- private void OnStartup(EntityUid uid, ArtifactComponent component, ComponentStartup args)
+ private void OnStartup(EntityUid uid, ArtifactComponent component, MapInitEvent args)
{
- if (_prototype.TryIndex("ArtifactActivate", out var proto))
- {
- _actions.AddAction(uid, new InstantAction(proto), null);
- }
+ RandomizeArtifact(uid, component);
+ _actions.AddAction(uid, Spawn(ArtifactActivateActionId), null);
}
private void OnRemove(EntityUid uid, ArtifactComponent component, ComponentRemove args)
{
- if (_prototype.TryIndex("ArtifactActivate", out var proto))
- {
- _actions.RemoveAction(uid, new InstantAction(proto));
- }
+ _actions.RemoveAction(uid, ArtifactActivateActionId);
}
private void OnSelfActivate(EntityUid uid, ArtifactComponent component, ArtifactSelfActivateEvent args)
@@ -52,4 +47,3 @@ public partial class ArtifactSystem
args.Handled = true;
}
}
-
diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/ArtifactSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/ArtifactSystem.cs
index 573fa36a26..0791924caa 100644
--- a/Content.Server/Xenoarchaeology/XenoArtifacts/ArtifactSystem.cs
+++ b/Content.Server/Xenoarchaeology/XenoArtifacts/ArtifactSystem.cs
@@ -6,12 +6,12 @@ using Content.Server.Power.EntitySystems;
using Content.Server.Xenoarchaeology.Equipment.Components;
using Content.Server.Xenoarchaeology.XenoArtifacts.Events;
using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components;
+using Content.Shared.CCVar;
using Content.Shared.Xenoarchaeology.XenoArtifacts;
using JetBrains.Annotations;
+using Robust.Shared.Configuration;
using Robust.Shared.Random;
using Robust.Shared.Timing;
-using Robust.Shared.Configuration;
-using Content.Shared.CCVar;
namespace Content.Server.Xenoarchaeology.XenoArtifacts;
@@ -30,7 +30,6 @@ public sealed partial class ArtifactSystem : EntitySystem
_sawmill = Logger.GetSawmill("artifact");
- SubscribeLocalEvent(OnInit);
SubscribeLocalEvent(GetPrice);
SubscribeLocalEvent(OnRoundEnd);
@@ -38,11 +37,6 @@ public sealed partial class ArtifactSystem : EntitySystem
InitializeActions();
}
- private void OnInit(EntityUid uid, ArtifactComponent component, MapInitEvent args)
- {
- RandomizeArtifact(uid, component);
- }
-
///
/// Calculates the price of an artifact based on
/// how many nodes have been unlocked/triggered
diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/KnockArtifactSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/KnockArtifactSystem.cs
index 4554ff94b2..0245b220a3 100644
--- a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/KnockArtifactSystem.cs
+++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/KnockArtifactSystem.cs
@@ -1,6 +1,6 @@
-using Content.Server.Magic.Events;
-using Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
+using Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
using Content.Server.Xenoarchaeology.XenoArtifacts.Events;
+using Content.Shared.Magic.Events;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Systems;
diff --git a/Content.Shared/Actions/ActionEvents.cs b/Content.Shared/Actions/ActionEvents.cs
index c99151ecef..3d45d3ef1a 100644
--- a/Content.Shared/Actions/ActionEvents.cs
+++ b/Content.Shared/Actions/ActionEvents.cs
@@ -1,8 +1,8 @@
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Hands;
using Content.Shared.Inventory;
using Content.Shared.Inventory.Events;
using Robust.Shared.Map;
+using Robust.Shared.Network;
using Robust.Shared.Serialization;
namespace Content.Shared.Actions;
@@ -18,7 +18,9 @@ namespace Content.Shared.Actions;
///
public sealed class GetItemActionsEvent : EntityEventArgs
{
- public SortedSet Actions = new();
+ private readonly IEntityManager _entities;
+ private readonly INetManager _net;
+ public readonly SortedSet Actions = new();
///
/// User equipping the item.
@@ -35,11 +37,26 @@ public sealed class GetItemActionsEvent : EntityEventArgs
///
public bool InHands => SlotFlags == null;
- public GetItemActionsEvent(EntityUid user, SlotFlags? slotFlags = null)
+ public GetItemActionsEvent(IEntityManager entities, INetManager net, EntityUid user, SlotFlags? slotFlags = null)
{
+ _entities = entities;
+ _net = net;
User = user;
SlotFlags = slotFlags;
}
+
+ public void AddAction(ref EntityUid? actionId, string? prototypeId)
+ {
+ if (_entities.Deleted(actionId))
+ {
+ if (string.IsNullOrWhiteSpace(prototypeId) || _net.IsClient)
+ return;
+
+ actionId = _entities.Spawn(prototypeId);
+ }
+
+ Actions.Add(actionId.Value);
+ }
}
///
@@ -48,22 +65,22 @@ public sealed class GetItemActionsEvent : EntityEventArgs
[Serializable, NetSerializable]
public sealed class RequestPerformActionEvent : EntityEventArgs
{
- public readonly ActionType Action;
+ public readonly EntityUid Action;
public readonly EntityUid? EntityTarget;
public readonly EntityCoordinates? EntityCoordinatesTarget;
- public RequestPerformActionEvent(InstantAction action)
+ public RequestPerformActionEvent(EntityUid action)
{
Action = action;
}
- public RequestPerformActionEvent(EntityTargetAction action, EntityUid entityTarget)
+ public RequestPerformActionEvent(EntityUid action, EntityUid entityTarget)
{
Action = action;
EntityTarget = entityTarget;
}
- public RequestPerformActionEvent(WorldTargetAction action, EntityCoordinates entityCoordinatesTarget)
+ public RequestPerformActionEvent(EntityUid action, EntityCoordinates entityCoordinatesTarget)
{
Action = action;
EntityCoordinatesTarget = entityCoordinatesTarget;
diff --git a/Content.Shared/Actions/ActionTypes/ActionPrototypes.cs b/Content.Shared/Actions/ActionTypes/ActionPrototypes.cs
deleted file mode 100644
index 2e61ef32de..0000000000
--- a/Content.Shared/Actions/ActionTypes/ActionPrototypes.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-using Robust.Shared.Prototypes;
-
-namespace Content.Shared.Actions.ActionTypes;
-
-// These are just prototype definitions for actions. Allows actions to be defined once in yaml and re-used elsewhere.
-// Note that you still need to create a new instance of each action to properly track the state (cooldown, toggled,
-// enabled, etc). The prototypes should not be modified directly.
-//
-// If ever action states data is separated from the rest of the data, this might not be required
-// anymore.
-
-[Prototype("worldTargetAction")]
-public sealed partial class WorldTargetActionPrototype : WorldTargetAction, IPrototype
-{
- [IdDataField]
- public string ID { get; private set; } = default!;
-
- // This is a shitty hack to get around the fact that action-prototypes should not in general be sever-exclusive
- // prototypes, but some actions may need to use server-exclusive events, and there is no way to specify on a
- // per-prototype basis whether the client should ignore it when validating yaml.
- [DataField("serverEvent", serverOnly: true)]
- public WorldTargetActionEvent? ServerEvent
- {
- get => Event;
- set => Event = value;
- }
-}
-
-[Prototype("entityTargetAction")]
-public sealed partial class EntityTargetActionPrototype : EntityTargetAction, IPrototype
-{
- [IdDataField]
- public string ID { get; private set; } = default!;
-
- [DataField("serverEvent", serverOnly: true)]
- public EntityTargetActionEvent? ServerEvent
- {
- get => Event;
- set => Event = value;
- }
-}
-
-[Prototype("instantAction")]
-public sealed partial class InstantActionPrototype : InstantAction, IPrototype
-{
- [IdDataField]
- public string ID { get; private set; } = default!;
-
- [DataField("serverEvent", serverOnly: true)]
- public InstantActionEvent? ServerEvent
- {
- get => Event;
- set => Event = value;
- }
-}
-
diff --git a/Content.Shared/Actions/ActionTypes/ActionType.cs b/Content.Shared/Actions/ActionTypes/ActionType.cs
deleted file mode 100644
index 93a24e2b75..0000000000
--- a/Content.Shared/Actions/ActionTypes/ActionType.cs
+++ /dev/null
@@ -1,275 +0,0 @@
-using Robust.Shared.Audio;
-using Robust.Shared.Serialization;
-using Robust.Shared.Utility;
-
-namespace Content.Shared.Actions.ActionTypes;
-
-[ImplicitDataDefinitionForInheritors]
-[Serializable, NetSerializable]
-public abstract partial class ActionType : IEquatable, IComparable, ICloneable
-{
- ///
- /// Icon representing this action in the UI.
- ///
- [DataField("icon")]
- public SpriteSpecifier? Icon;
-
- ///
- /// For toggle actions only, icon to show when toggled on. If omitted, the action will simply be highlighted
- /// when turned on.
- ///
- [DataField("iconOn")]
- public SpriteSpecifier? IconOn;
-
- ///
- /// If not null, this color will modulate the action icon color.
- ///
- ///
- /// This currently only exists for decal-placement actions, so that the action icons correspond to the color of
- /// the decal. But this is probably useful for other actions, including maybe changing color on toggle.
- ///
- [DataField("iconColor")]
- public Color IconColor = Color.White;
-
- ///
- /// Name to show in UI.
- ///
- [DataField("name")]
- public string DisplayName = string.Empty;
-
- ///
- /// This is just with localized strings resolved and markup removed. If null, will be
- /// inferred from . This is cached to speed up game state handling.
- ///
- [NonSerialized]
- public string? RawName;
-
- ///
- /// Description to show in UI. Accepts formatting.
- ///
- [DataField("description")]
- public string Description = string.Empty;
-
- ///
- /// Keywords that can be used to search for this action in the action menu.
- ///
- [DataField("keywords")]
- public HashSet Keywords = new();
-
- ///
- /// Whether this action is currently enabled. If not enabled, this action cannot be performed.
- ///
- [DataField("enabled")]
- public bool Enabled = true;
-
- ///
- /// The toggle state of this action. Toggling switches the currently displayed icon, see and .
- ///
- ///
- /// The toggle can set directly via , but it will also be
- /// automatically toggled for targeted-actions while selecting a target.
- ///
- public bool Toggled;
-
- ///
- /// The current cooldown on the action.
- ///
- public (TimeSpan Start, TimeSpan End)? Cooldown;
-
- ///
- /// Time interval between action uses.
- ///
- [DataField("useDelay")]
- public TimeSpan? UseDelay;
-
- ///
- /// Convenience tool for actions with limited number of charges. Automatically decremented on use, and the
- /// action is disabled when it reaches zero. Does NOT automatically remove the action from the action bar.
- ///
- [DataField("charges")]
- public int? Charges;
-
- ///
- /// The entity that enables / provides this action. If the action is innate, this may be the user themselves. If
- /// this action has no provider (e.g., mapping tools), the this will result in broadcast events.
- ///
- public EntityUid? Provider;
-
- ///
- /// Entity to use for the action icon. Defaults to using .
- ///
- public EntityUid? EntityIcon
- {
- get => _entityIcon ?? Provider;
- set => _entityIcon = value;
- }
-
- private EntityUid? _entityIcon;
-
- ///
- /// Whether the action system should block this action if the user cannot currently interact. Some spells or
- /// abilities may want to disable this and implement their own checks.
- ///
- [DataField("checkCanInteract")]
- public bool CheckCanInteract = true;
-
- ///
- /// If true, will simply execute the action locally without sending to the server.
- ///
- [DataField("clientExclusive")]
- public bool ClientExclusive = false;
-
- ///
- /// Determines the order in which actions are automatically added the action bar.
- ///
- [DataField("priority")]
- public int Priority = 0;
-
- ///
- /// What entity, if any, currently has this action in the actions component?
- ///
- [ViewVariables]
- public EntityUid? AttachedEntity;
-
- ///
- /// Whether or not to automatically add this action to the action bar when it becomes available.
- ///
- [DataField("autoPopulate")]
- public bool AutoPopulate = true;
-
-
- ///
- /// Whether or not to automatically remove this action to the action bar when it becomes unavailable.
- ///
- [DataField("autoRemove")]
- public bool AutoRemove = true;
-
- ///
- /// Temporary actions are removed from the action component when removed from the action-bar/GUI. Currently,
- /// should only be used for client-exclusive actions (server is not notified).
- ///
- ///
- /// Currently there is no way for a player to just voluntarily remove actions. They can hide them from the
- /// toolbar, but not actually remove them. This is undesirable for things like dynamically added mapping
- /// entity-selection actions, as the # of actions would just keep increasing.
- ///
- [DataField("temporary")]
- public bool Temporary;
- // TODO re-add support for this
- // UI refactor seems to have just broken it.
-
- ///
- /// Determines the appearance of the entity-icon for actions that are enabled via some entity.
- ///
- [DataField("itemIconStyle")]
- public ItemActionIconStyle ItemIconStyle;
-
- ///
- /// If not null, this sound will be played when performing this action.
- ///
- [DataField("sound")]
- public SoundSpecifier? Sound;
-
- ///
- /// Compares two actions based on their properties. This is used to determine equality when the client requests the
- /// server to perform some action. Also determines the order in which actions are automatically added to the action bar.
- ///
- ///
- /// Basically: if an action has the same priority, name, and is enabled by the same entity, then the actions are considered equal.
- /// The entity-check is required to avoid toggling all flashlights simultaneously whenever a flashlight-hoarder uses an action.
- ///
- public virtual int CompareTo(object? obj)
- {
- if (obj is not ActionType otherAction)
- return -1;
-
- if (Priority != otherAction.Priority)
- return otherAction.Priority - Priority;
-
- RawName ??= FormattedMessage.RemoveMarkup(Loc.GetString(DisplayName));
- otherAction.RawName ??= FormattedMessage.RemoveMarkup(Loc.GetString(otherAction.DisplayName));
- var cmp = string.Compare(RawName, otherAction.RawName, StringComparison.CurrentCulture);
- if (cmp != 0)
- return cmp;
-
- if (Provider != otherAction.Provider)
- {
- if (Provider == null)
- return -1;
-
- if (otherAction.Provider == null)
- return 1;
-
- // uid to int casting... it says "Do NOT use this in content". You can't tell me what to do.
- return (int) Provider - (int) otherAction.Provider;
- }
-
- return 0;
- }
-
- ///
- /// Proper client-side state handling requires the ability to clone an action from the component state.
- /// Otherwise modifying the action can lead to modifying the stored server state.
- ///
- public abstract object Clone();
-
- public virtual void CopyFrom(object objectToClone)
- {
- if (objectToClone is not ActionType toClone)
- return;
-
- // This is pretty Ugly to look at. But actions are sent to the client in a component state, so they have to be
- // cloneable. Would be easy if this were a struct of only value-types, but I don't want to restrict actions like
- // that.
- Priority = toClone.Priority;
- Icon = toClone.Icon;
- IconOn = toClone.IconOn;
- DisplayName = toClone.DisplayName;
- RawName = null;
- Description = toClone.Description;
- Provider = toClone.Provider;
- AttachedEntity = toClone.AttachedEntity;
- Enabled = toClone.Enabled;
- Toggled = toClone.Toggled;
- Cooldown = toClone.Cooldown;
- Charges = toClone.Charges;
- Keywords = new(toClone.Keywords);
- AutoPopulate = toClone.AutoPopulate;
- AutoRemove = toClone.AutoRemove;
- ItemIconStyle = toClone.ItemIconStyle;
- CheckCanInteract = toClone.CheckCanInteract;
- UseDelay = toClone.UseDelay;
- Sound = toClone.Sound;
- ItemIconStyle = toClone.ItemIconStyle;
- _entityIcon = toClone._entityIcon;
- }
-
- public bool Equals(ActionType? other)
- {
- return CompareTo(other) == 0;
- }
-
- public static bool operator ==(ActionType? left, ActionType? right)
- {
- if (left is null)
- return right is null;
-
- return left.Equals(right);
- }
-
- public static bool operator !=(ActionType? left, ActionType? right)
- {
- return !(left == right);
- }
-
- public override int GetHashCode()
- {
- unchecked
- {
- var hashCode = Priority.GetHashCode();
- hashCode = (hashCode * 397) ^ DisplayName.GetHashCode();
- hashCode = (hashCode * 397) ^ Provider.GetHashCode();
- return hashCode;
- }
- }
-}
diff --git a/Content.Shared/Actions/ActionTypes/InstantAction.cs b/Content.Shared/Actions/ActionTypes/InstantAction.cs
deleted file mode 100644
index 34ee352406..0000000000
--- a/Content.Shared/Actions/ActionTypes/InstantAction.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-using Robust.Shared.Serialization;
-
-namespace Content.Shared.Actions.ActionTypes;
-
-///
-/// Instantaneous action with no extra targeting information. Will result in being raised.
-///
-[Serializable, NetSerializable]
-[Virtual]
-public partial class InstantAction : ActionType
-{
- ///
- /// The local-event to raise when this action is performed.
- ///
- [DataField("event")]
- [NonSerialized]
- public InstantActionEvent? Event;
-
- public InstantAction() { }
- public InstantAction(InstantAction toClone)
- {
- CopyFrom(toClone);
- }
-
- public override void CopyFrom(object objectToClone)
- {
- base.CopyFrom(objectToClone);
-
- // Server doesn't serialize events to us.
- // As such we don't want them to bulldoze any events we may have gotten locally.
- if (objectToClone is not InstantAction toClone)
- return;
-
- // Events should be re-usable, and shouldn't be modified during prediction.
- if (toClone.Event != null)
- Event = toClone.Event;
- }
-
- public override object Clone()
- {
- return new InstantAction(this);
- }
-}
diff --git a/Content.Shared/Actions/ActionTypes/TargetedAction.cs b/Content.Shared/Actions/ActionTypes/TargetedAction.cs
deleted file mode 100644
index 780972092a..0000000000
--- a/Content.Shared/Actions/ActionTypes/TargetedAction.cs
+++ /dev/null
@@ -1,153 +0,0 @@
-using Content.Shared.Interaction;
-using Content.Shared.Whitelist;
-using Robust.Shared.Serialization;
-
-namespace Content.Shared.Actions.ActionTypes;
-
-[Serializable, NetSerializable]
-public abstract partial class TargetedAction : ActionType
-{
- ///
- /// For entity- or map-targeting actions, if this is true the action will remain selected after it is used, so
- /// it can be continuously re-used. If this is false, the action will be deselected after one use.
- ///
- [DataField("repeat")]
- public bool Repeat;
-
- ///
- /// For entity- or map-targeting action, determines whether the action is deselected if the user doesn't click a valid target.
- ///
- [DataField("deselectOnMiss")]
- public bool DeselectOnMiss;
-
- ///
- /// Whether the action system should block this action if the user cannot actually access the target
- /// (unobstructed, in inventory, in backpack, etc). Some spells or abilities may want to disable this and
- /// implement their own checks.
- ///
- ///
- /// Even if this is false, the will still be checked.
- ///
- [DataField("checkCanAccess")]
- public bool CheckCanAccess = true;
-
- [DataField("range")]
- public float Range = SharedInteractionSystem.InteractionRange;
-
- ///
- /// If the target is invalid, this bool determines whether the left-click will default to performing a standard-interaction
- ///
- ///
- /// Interactions will still be blocked if the target-validation generates a pop-up
- ///
- [DataField("interactOnMiss")]
- public bool InteractOnMiss = false;
-
- ///
- /// If true, and if is enabled, then this action's icon will be drawn by that
- /// over lay in place of the currently held item "held item".
- ///
- [DataField("targetingIndicator")]
- public bool TargetingIndicator = true;
-
- public override void CopyFrom(object objectToClone)
- {
- base.CopyFrom(objectToClone);
-
- if (objectToClone is not TargetedAction toClone)
- return;
-
- Range = toClone.Range;
- CheckCanAccess = toClone.CheckCanAccess;
- DeselectOnMiss = toClone.DeselectOnMiss;
- Repeat = toClone.Repeat;
- InteractOnMiss = toClone.InteractOnMiss;
- TargetingIndicator = toClone.TargetingIndicator;
- }
-}
-
-///
-/// Action that targets some entity. Will result in being raised.
-///
-[Serializable, NetSerializable]
-[Virtual]
-public partial class EntityTargetAction : TargetedAction
-{
- ///
- /// The local-event to raise when this action is performed.
- ///
- [NonSerialized]
- [DataField("event")]
- public EntityTargetActionEvent? Event;
-
- [DataField("whitelist")]
- public EntityWhitelist? Whitelist;
-
- [DataField("canTargetSelf")]
- public bool CanTargetSelf = true;
-
- public EntityTargetAction() { }
- public EntityTargetAction(EntityTargetAction toClone)
- {
- CopyFrom(toClone);
- }
- public override void CopyFrom(object objectToClone)
- {
- base.CopyFrom(objectToClone);
-
- if (objectToClone is not EntityTargetAction toClone)
- return;
-
- CanTargetSelf = toClone.CanTargetSelf;
-
- // This isn't a deep copy, but I don't expect white-lists to ever be edited during prediction. So good enough?
- Whitelist = toClone.Whitelist;
-
- // Events should be re-usable, and shouldn't be modified during prediction.
- if (toClone.Event != null)
- Event = toClone.Event;
- }
-
- public override object Clone()
- {
- return new EntityTargetAction(this);
- }
-}
-
-///
-/// Action that targets some map coordinates. Will result in being raised.
-///
-[Serializable, NetSerializable]
-[Virtual]
-public partial class WorldTargetAction : TargetedAction
-{
- ///
- /// The local-event to raise when this action is performed.
- ///
- [DataField("event")]
- [NonSerialized]
- public WorldTargetActionEvent? Event;
-
- public WorldTargetAction() { }
- public WorldTargetAction(WorldTargetAction toClone)
- {
- CopyFrom(toClone);
- }
-
- public override void CopyFrom(object objectToClone)
- {
- base.CopyFrom(objectToClone);
-
- if (objectToClone is not WorldTargetAction toClone)
- return;
-
- // Events should be re-usable, and shouldn't be modified during prediction.
- if (toClone.Event != null)
- Event = toClone.Event;
- }
-
- public override object Clone()
- {
- return new WorldTargetAction(this);
- }
-}
diff --git a/Content.Shared/Actions/ActionsComponent.cs b/Content.Shared/Actions/ActionsComponent.cs
index 4472f8a095..c84484dfc0 100644
--- a/Content.Shared/Actions/ActionsComponent.cs
+++ b/Content.Shared/Actions/ActionsComponent.cs
@@ -1,4 +1,3 @@
-using Content.Shared.Actions.ActionTypes;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
@@ -9,10 +8,10 @@ namespace Content.Shared.Actions;
[Access(typeof(SharedActionsSystem))]
public sealed partial class ActionsComponent : Component
{
- [ViewVariables]
- [Access(typeof(SharedActionsSystem), Other = AccessPermissions.ReadExecute)]
- // FIXME Friends
- public SortedSet Actions = new();
+ ///
+ /// Handled on the client to track added and removed actions.
+ ///
+ [ViewVariables] public readonly Dictionary OldClientActions = new();
public override bool SendOnlyToOwner => true;
}
@@ -20,17 +19,16 @@ public sealed partial class ActionsComponent : Component
[Serializable, NetSerializable]
public sealed class ActionsComponentState : ComponentState
{
- public readonly List Actions;
+ public readonly List Actions;
- [NonSerialized]
- public SortedSet? SortedActions;
-
- public ActionsComponentState(List actions)
+ public ActionsComponentState(List actions)
{
Actions = actions;
}
}
+public readonly record struct ActionMetaData(bool ClientExclusive, bool AutoRemove);
+
///
/// Determines how the action icon appears in the hotbar for item actions.
///
diff --git a/Content.Shared/Actions/BaseActionComponent.cs b/Content.Shared/Actions/BaseActionComponent.cs
new file mode 100644
index 0000000000..7ece8e2b1f
--- /dev/null
+++ b/Content.Shared/Actions/BaseActionComponent.cs
@@ -0,0 +1,187 @@
+using Robust.Shared.Audio;
+using Robust.Shared.Serialization;
+using Robust.Shared.Utility;
+
+namespace Content.Shared.Actions;
+
+// TODO this should be an IncludeDataFields of each action component type, not use inheritance
+public abstract partial class BaseActionComponent : Component
+{
+ public abstract BaseActionEvent? BaseEvent { get; }
+
+ ///
+ /// Icon representing this action in the UI.
+ ///
+ [DataField("icon")] public SpriteSpecifier? Icon;
+
+ ///
+ /// For toggle actions only, icon to show when toggled on. If omitted, the action will simply be highlighted
+ /// when turned on.
+ ///
+ [DataField("iconOn")] public SpriteSpecifier? IconOn;
+
+ ///
+ /// If not null, this color will modulate the action icon color.
+ ///
+ ///
+ /// This currently only exists for decal-placement actions, so that the action icons correspond to the color of
+ /// the decal. But this is probably useful for other actions, including maybe changing color on toggle.
+ ///
+ [DataField("iconColor")] public Color IconColor = Color.White;
+
+ ///
+ /// Keywords that can be used to search for this action in the action menu.
+ ///
+ [DataField("keywords")] public HashSet Keywords = new();
+
+ ///
+ /// Whether this action is currently enabled. If not enabled, this action cannot be performed.
+ ///
+ [DataField("enabled")] public bool Enabled = true;
+
+ ///
+ /// The toggle state of this action. Toggling switches the currently displayed icon, see and .
+ ///
+ ///
+ /// The toggle can set directly via , but it will also be
+ /// automatically toggled for targeted-actions while selecting a target.
+ ///
+ public bool Toggled;
+
+ ///
+ /// The current cooldown on the action.
+ ///
+ public (TimeSpan Start, TimeSpan End)? Cooldown;
+
+ ///
+ /// Time interval between action uses.
+ ///
+ [DataField("useDelay")] public TimeSpan? UseDelay;
+
+ ///
+ /// Convenience tool for actions with limited number of charges. Automatically decremented on use, and the
+ /// action is disabled when it reaches zero. Does NOT automatically remove the action from the action bar.
+ ///
+ [DataField("charges")] public int? Charges;
+
+ ///
+ /// The entity that enables / provides this action. If the action is innate, this may be the user themselves. If
+ /// this action has no provider (e.g., mapping tools), the this will result in broadcast events.
+ ///
+ public EntityUid? Provider;
+
+ ///
+ /// Entity to use for the action icon. Defaults to using .
+ ///
+ public EntityUid? EntityIcon
+ {
+ get => _entityIcon ?? Provider;
+ set => _entityIcon = value;
+ }
+
+ private EntityUid? _entityIcon;
+
+ ///
+ /// Whether the action system should block this action if the user cannot currently interact. Some spells or
+ /// abilities may want to disable this and implement their own checks.
+ ///
+ [DataField("checkCanInteract")] public bool CheckCanInteract = true;
+
+ ///
+ /// If true, will simply execute the action locally without sending to the server.
+ ///
+ [DataField("clientExclusive")] public bool ClientExclusive = false;
+
+ ///
+ /// Determines the order in which actions are automatically added the action bar.
+ ///
+ [DataField("priority")] public int Priority = 0;
+
+ ///
+ /// What entity, if any, currently has this action in the actions component?
+ ///
+ [ViewVariables] public EntityUid? AttachedEntity;
+
+ ///
+ /// Whether or not to automatically add this action to the action bar when it becomes available.
+ ///
+ [DataField("autoPopulate")] public bool AutoPopulate = true;
+
+
+ ///
+ /// Whether or not to automatically remove this action to the action bar when it becomes unavailable.
+ ///
+ [DataField("autoRemove")] public bool AutoRemove = true;
+
+ ///
+ /// Temporary actions are removed from the action component when removed from the action-bar/GUI. Currently,
+ /// should only be used for client-exclusive actions (server is not notified).
+ ///
+ ///
+ /// Currently there is no way for a player to just voluntarily remove actions. They can hide them from the
+ /// toolbar, but not actually remove them. This is undesirable for things like dynamically added mapping
+ /// entity-selection actions, as the # of actions would just keep increasing.
+ ///
+ [DataField("temporary")] public bool Temporary;
+ // TODO re-add support for this
+ // UI refactor seems to have just broken it.
+
+ ///
+ /// Determines the appearance of the entity-icon for actions that are enabled via some entity.
+ ///
+ [DataField("itemIconStyle")] public ItemActionIconStyle ItemIconStyle;
+
+ ///
+ /// If not null, this sound will be played when performing this action.
+ ///
+ [DataField("sound")] public SoundSpecifier? Sound;
+}
+
+[Serializable, NetSerializable]
+public abstract class BaseActionComponentState : ComponentState
+{
+ public SpriteSpecifier? Icon;
+ public SpriteSpecifier? IconOn;
+ public Color IconColor;
+ public HashSet Keywords;
+ public bool Enabled;
+ public bool Toggled;
+ public (TimeSpan Start, TimeSpan End)? Cooldown;
+ public TimeSpan? UseDelay;
+ public int? Charges;
+ public EntityUid? Provider;
+ public EntityUid? EntityIcon;
+ public bool CheckCanInteract;
+ public bool ClientExclusive;
+ public int Priority;
+ public EntityUid? AttachedEntity;
+ public bool AutoPopulate;
+ public bool AutoRemove;
+ public bool Temporary;
+ public ItemActionIconStyle ItemIconStyle;
+ public SoundSpecifier? Sound;
+
+ protected BaseActionComponentState(BaseActionComponent component)
+ {
+ Icon = component.Icon;
+ IconOn = component.IconOn;
+ IconColor = component.IconColor;
+ Keywords = component.Keywords;
+ Enabled = component.Enabled;
+ Toggled = component.Toggled;
+ Cooldown = component.Cooldown;
+ UseDelay = component.UseDelay;
+ Charges = component.Charges;
+ Provider = component.Provider;
+ EntityIcon = component.EntityIcon;
+ CheckCanInteract = component.CheckCanInteract;
+ ClientExclusive = component.ClientExclusive;
+ Priority = component.Priority;
+ AttachedEntity = component.AttachedEntity;
+ AutoPopulate = component.AutoPopulate;
+ AutoRemove = component.AutoRemove;
+ Temporary = component.Temporary;
+ ItemIconStyle = component.ItemIconStyle;
+ Sound = component.Sound;
+ }
+}
diff --git a/Content.Shared/Actions/BaseTargetActionComponent.cs b/Content.Shared/Actions/BaseTargetActionComponent.cs
new file mode 100644
index 0000000000..7e40b10c32
--- /dev/null
+++ b/Content.Shared/Actions/BaseTargetActionComponent.cs
@@ -0,0 +1,43 @@
+using Content.Shared.Interaction;
+
+namespace Content.Shared.Actions;
+
+public abstract partial class BaseTargetActionComponent : BaseActionComponent
+{
+ ///
+ /// For entity- or map-targeting actions, if this is true the action will remain selected after it is used, so
+ /// it can be continuously re-used. If this is false, the action will be deselected after one use.
+ ///
+ [DataField("repeat")] public bool Repeat;
+
+ ///
+ /// For entity- or map-targeting action, determines whether the action is deselected if the user doesn't click a valid target.
+ ///
+ [DataField("deselectOnMiss")] public bool DeselectOnMiss;
+
+ ///
+ /// Whether the action system should block this action if the user cannot actually access the target
+ /// (unobstructed, in inventory, in backpack, etc). Some spells or abilities may want to disable this and
+ /// implement their own checks.
+ ///
+ ///
+ /// Even if this is false, the will still be checked.
+ ///
+ [DataField("checkCanAccess")] public bool CheckCanAccess = true;
+
+ [DataField("range")] public float Range = SharedInteractionSystem.InteractionRange;
+
+ ///
+ /// If the target is invalid, this bool determines whether the left-click will default to performing a standard-interaction
+ ///
+ ///
+ /// Interactions will still be blocked if the target-validation generates a pop-up
+ ///
+ [DataField("interactOnMiss")] public bool InteractOnMiss = false;
+
+ ///
+ /// If true, and if is enabled, then this action's icon will be drawn by that
+ /// over lay in place of the currently held item "held item".
+ ///
+ [DataField("targetingIndicator")] public bool TargetingIndicator = true;
+}
diff --git a/Content.Shared/Actions/EntityTargetActionComponent.cs b/Content.Shared/Actions/EntityTargetActionComponent.cs
new file mode 100644
index 0000000000..33c4bf8cef
--- /dev/null
+++ b/Content.Shared/Actions/EntityTargetActionComponent.cs
@@ -0,0 +1,35 @@
+using Content.Shared.Whitelist;
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Actions;
+
+[RegisterComponent, NetworkedComponent]
+public sealed partial class EntityTargetActionComponent : BaseTargetActionComponent
+{
+ public override BaseActionEvent? BaseEvent => Event;
+
+ ///
+ /// The local-event to raise when this action is performed.
+ ///
+ [DataField("event")]
+ [NonSerialized]
+ public EntityTargetActionEvent? Event;
+
+ [DataField("whitelist")] public EntityWhitelist? Whitelist;
+
+ [DataField("canTargetSelf")] public bool CanTargetSelf = true;
+}
+
+[Serializable, NetSerializable]
+public sealed class EntityTargetActionComponentState : BaseActionComponentState
+{
+ public EntityWhitelist? Whitelist;
+ public bool CanTargetSelf;
+
+ public EntityTargetActionComponentState(EntityTargetActionComponent component) : base(component)
+ {
+ Whitelist = component.Whitelist;
+ CanTargetSelf = component.CanTargetSelf;
+ }
+}
diff --git a/Content.Shared/Actions/Events/EggLayInstantActionEvent.cs b/Content.Shared/Actions/Events/EggLayInstantActionEvent.cs
new file mode 100644
index 0000000000..5cdcfce985
--- /dev/null
+++ b/Content.Shared/Actions/Events/EggLayInstantActionEvent.cs
@@ -0,0 +1,3 @@
+namespace Content.Shared.Actions.Events;
+
+public sealed partial class EggLayInstantActionEvent : InstantActionEvent {}
diff --git a/Content.Shared/Actions/Events/GetActionDataEvent.cs b/Content.Shared/Actions/Events/GetActionDataEvent.cs
new file mode 100644
index 0000000000..be77cfd426
--- /dev/null
+++ b/Content.Shared/Actions/Events/GetActionDataEvent.cs
@@ -0,0 +1,4 @@
+namespace Content.Shared.Actions.Events;
+
+[ByRefEvent]
+public record struct GetActionDataEvent(BaseActionComponent? Action);
diff --git a/Content.Shared/Actions/Events/InvisibleWallActionEvent.cs b/Content.Shared/Actions/Events/InvisibleWallActionEvent.cs
new file mode 100644
index 0000000000..d84b9910e0
--- /dev/null
+++ b/Content.Shared/Actions/Events/InvisibleWallActionEvent.cs
@@ -0,0 +1,5 @@
+namespace Content.Shared.Actions.Events;
+
+public sealed partial class InvisibleWallActionEvent : InstantActionEvent
+{
+}
diff --git a/Content.Shared/Actions/InstantActionComponent.cs b/Content.Shared/Actions/InstantActionComponent.cs
new file mode 100644
index 0000000000..f97ca04a46
--- /dev/null
+++ b/Content.Shared/Actions/InstantActionComponent.cs
@@ -0,0 +1,25 @@
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Actions;
+
+[RegisterComponent, NetworkedComponent]
+public sealed partial class InstantActionComponent : BaseActionComponent
+{
+ public override BaseActionEvent? BaseEvent => Event;
+
+ ///
+ /// The local-event to raise when this action is performed.
+ ///
+ [DataField("event")]
+ [NonSerialized]
+ public InstantActionEvent? Event;
+}
+
+[Serializable, NetSerializable]
+public sealed class InstantActionComponentState : BaseActionComponentState
+{
+ public InstantActionComponentState(InstantActionComponent component) : base(component)
+ {
+ }
+}
diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs
index 9fc1b83748..00514fcea3 100644
--- a/Content.Shared/Actions/SharedActionsSystem.cs
+++ b/Content.Shared/Actions/SharedActionsSystem.cs
@@ -1,5 +1,7 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
using Content.Shared.ActionBlocker;
-using Content.Shared.Actions.ActionTypes;
+using Content.Shared.Actions.Events;
using Content.Shared.Administration.Logs;
using Content.Shared.Database;
using Content.Shared.Hands;
@@ -8,16 +10,18 @@ using Content.Shared.Inventory.Events;
using Robust.Shared.Containers;
using Robust.Shared.GameStates;
using Robust.Shared.Map;
-using Robust.Shared.Prototypes;
+using Robust.Shared.Network;
using Robust.Shared.Timing;
-using System.Linq;
namespace Content.Shared.Actions;
public abstract class SharedActionsSystem : EntitySystem
{
+ private const string ActionContainerId = "ActionContainer";
+
[Dependency] protected readonly IGameTiming GameTiming = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
+ [Dependency] private readonly INetManager _net = default!;
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
@@ -34,14 +38,149 @@ public abstract class SharedActionsSystem : EntitySystem
SubscribeLocalEvent(OnDidUnequip);
SubscribeLocalEvent(OnHandUnequipped);
- SubscribeLocalEvent(GetState);
+ SubscribeLocalEvent(OnActionsMapInit);
+ SubscribeLocalEvent(OnActionsGetState);
+ SubscribeLocalEvent(OnActionsShutdown);
+
+ SubscribeLocalEvent(OnInstantGetState);
+ SubscribeLocalEvent(OnEntityTargetGetState);
+ SubscribeLocalEvent(OnWorldTargetGetState);
+
+ SubscribeLocalEvent(OnInstantHandleState);
+ SubscribeLocalEvent(OnEntityTargetHandleState);
+ SubscribeLocalEvent(OnWorldTargetHandleState);
+
+ SubscribeLocalEvent(OnGetActionData);
+ SubscribeLocalEvent(OnGetActionData);
+ SubscribeLocalEvent(OnGetActionData);
SubscribeAllEvent(OnActionRequest);
}
- #region ComponentStateManagement
- public virtual void Dirty(ActionType action)
+ private void OnInstantGetState(EntityUid uid, InstantActionComponent component, ref ComponentGetState args)
{
+ args.State = new InstantActionComponentState(component);
+ }
+
+ private void OnEntityTargetGetState(EntityUid uid, EntityTargetActionComponent component, ref ComponentGetState args)
+ {
+ args.State = new EntityTargetActionComponentState(component);
+ }
+
+ private void OnWorldTargetGetState(EntityUid uid, WorldTargetActionComponent component, ref ComponentGetState args)
+ {
+ args.State = new WorldTargetActionComponentState(component);
+ }
+
+ private void BaseHandleState(BaseActionComponent component, BaseActionComponentState state)
+ {
+ component.Icon = state.Icon;
+ component.IconOn = state.IconOn;
+ component.IconColor = state.IconColor;
+ component.Keywords = new HashSet(state.Keywords);
+ component.Enabled = state.Enabled;
+ component.Toggled = state.Toggled;
+ component.Cooldown = state.Cooldown;
+ component.UseDelay = state.UseDelay;
+ component.Charges = state.Charges;
+ component.Provider = state.Provider;
+ component.EntityIcon = state.EntityIcon;
+ component.CheckCanInteract = state.CheckCanInteract;
+ component.ClientExclusive = state.ClientExclusive;
+ component.Priority = state.Priority;
+ component.AttachedEntity = state.AttachedEntity;
+ component.AutoPopulate = state.AutoPopulate;
+ component.AutoRemove = state.AutoRemove;
+ component.Temporary = state.Temporary;
+ component.ItemIconStyle = state.ItemIconStyle;
+ component.Sound = state.Sound;
+ }
+
+ private void OnInstantHandleState(EntityUid uid, InstantActionComponent component, ref ComponentHandleState args)
+ {
+ if (args.Current is not InstantActionComponentState state)
+ return;
+
+ BaseHandleState(component, state);
+ }
+
+ private void OnEntityTargetHandleState(EntityUid uid, EntityTargetActionComponent component, ref ComponentHandleState args)
+ {
+ if (args.Current is not EntityTargetActionComponentState state)
+ return;
+
+ BaseHandleState(component, state);
+ component.Whitelist = state.Whitelist;
+ component.CanTargetSelf = state.CanTargetSelf;
+ }
+
+ private void OnWorldTargetHandleState(EntityUid uid, WorldTargetActionComponent component, ref ComponentHandleState args)
+ {
+ if (args.Current is not WorldTargetActionComponentState state)
+ return;
+
+ BaseHandleState(component, state);
+ }
+
+ private void OnGetActionData(EntityUid uid, T component, ref GetActionDataEvent args) where T : BaseActionComponent
+ {
+ args.Action = component;
+ }
+
+ public BaseActionComponent? GetActionData(EntityUid? actionId)
+ {
+ if (actionId == null)
+ return null;
+
+ // TODO split up logic between each action component with different subscriptions
+ // good luck future coder
+ var ev = new GetActionDataEvent();
+ RaiseLocalEvent(actionId.Value, ref ev);
+ return ev.Action;
+ }
+
+ public bool TryGetActionData(
+ [NotNullWhen(true)] EntityUid? actionId,
+ [NotNullWhen(true)] out BaseActionComponent? action)
+ {
+ action = null;
+ return actionId != null && (action = GetActionData(actionId)) != null;
+ }
+
+ protected Container EnsureContainer(EntityUid holderId)
+ {
+ return _containerSystem.EnsureContainer(holderId, ActionContainerId);
+ }
+
+ protected bool TryGetContainer(
+ EntityUid holderId,
+ [NotNullWhen(true)] out IContainer? container,
+ ContainerManagerComponent? containerManager = null)
+ {
+ return _containerSystem.TryGetContainer(holderId, ActionContainerId, out container, containerManager);
+ }
+
+ public void SetCooldown(EntityUid? actionId, TimeSpan start, TimeSpan end)
+ {
+ if (actionId == null)
+ return;
+
+ var action = GetActionData(actionId);
+ if (action == null)
+ return;
+
+ action.Cooldown = (start, end);
+ Dirty(actionId.Value, action);
+ }
+
+ #region ComponentStateManagement
+ public virtual void Dirty(EntityUid? actionId)
+ {
+ if (!TryGetActionData(actionId, out var action))
+ return;
+
+ Dirty(actionId.Value, action);
+
if (action.AttachedEntity == null)
return;
@@ -51,39 +190,63 @@ public abstract class SharedActionsSystem : EntitySystem
return;
}
- Dirty(comp);
+ Dirty(action.AttachedEntity.Value, comp);
}
- public void SetToggled(ActionType action, bool toggled)
+ public void SetToggled(EntityUid? actionId, bool toggled)
{
- if (action.Toggled == toggled)
+ if (!TryGetActionData(actionId, out var action) ||
+ action.Toggled == toggled)
+ {
return;
+ }
action.Toggled = toggled;
- Dirty(action);
+ Dirty(actionId.Value, action);
}
- public void SetEnabled(ActionType action, bool enabled)
+ public void SetEnabled(EntityUid? actionId, bool enabled)
{
- if (action.Enabled == enabled)
+ if (!TryGetActionData(actionId, out var action) ||
+ action.Enabled == enabled)
+ {
return;
+ }
action.Enabled = enabled;
- Dirty(action);
+ Dirty(actionId.Value, action);
}
- public void SetCharges(ActionType action, int? charges)
+ public void SetCharges(EntityUid? actionId, int? charges)
{
- if (action.Charges == charges)
+ if (!TryGetActionData(actionId, out var action) ||
+ action.Charges == charges)
+ {
return;
+ }
action.Charges = charges;
- Dirty(action);
+ Dirty(actionId.Value, action);
}
- private void GetState(EntityUid uid, ActionsComponent component, ref ComponentGetState args)
+ private void OnActionsMapInit(EntityUid uid, ActionsComponent component, MapInitEvent args)
{
- args.State = new ActionsComponentState(component.Actions.ToList());
+ EnsureContainer(uid);
+ }
+
+ private void OnActionsGetState(EntityUid uid, ActionsComponent component, ref ComponentGetState args)
+ {
+ var actions = new List();
+ if (TryGetContainer(uid, out var container))
+ actions.AddRange(container.ContainedEntities);
+
+ args.State = new ActionsComponentState(actions);
+ }
+
+ private void OnActionsShutdown(EntityUid uid, ActionsComponent component, ComponentShutdown args)
+ {
+ if (TryGetContainer(uid, out var container))
+ container.Shutdown(EntityManager);
}
#endregion
@@ -101,33 +264,36 @@ public abstract class SharedActionsSystem : EntitySystem
if (!TryComp(user, out ActionsComponent? component))
return;
+ if (!TryComp(ev.Action, out MetaDataComponent? metaData))
+ return;
+
+ var name = Name(ev.Action, metaData);
+
// Does the user actually have the requested action?
- if (!component.Actions.TryGetValue(ev.Action, out var act))
+ if (!TryGetContainer(user, out var container) || !container.Contains(ev.Action))
{
_adminLogger.Add(LogType.Action,
- $"{ToPrettyString(user):user} attempted to perform an action that they do not have: {ev.Action.DisplayName}.");
+ $"{ToPrettyString(user):user} attempted to perform an action that they do not have: {name}.");
return;
}
- if (!act.Enabled)
+ var action = GetActionData(ev.Action);
+ if (action == null || !action.Enabled)
return;
var curTime = GameTiming.CurTime;
- if (act.Cooldown.HasValue && act.Cooldown.Value.End > curTime)
+ if (action.Cooldown.HasValue && action.Cooldown.Value.End > curTime)
return;
BaseActionEvent? performEvent = null;
// Validate request by checking action blockers and the like:
- var name = Loc.GetString(act.DisplayName);
-
- switch (act)
+ switch (action)
{
- case EntityTargetAction entityAction:
-
+ case EntityTargetActionComponent entityAction:
if (ev.EntityTarget is not { Valid: true } entityTarget)
{
- Log.Error($"Attempted to perform an entity-targeted action without a target! Action: {entityAction.DisplayName}");
+ Log.Error($"Attempted to perform an entity-targeted action without a target! Action: {name}");
return;
}
@@ -137,7 +303,7 @@ public abstract class SharedActionsSystem : EntitySystem
if (!ValidateEntityTarget(user, entityTarget, entityAction))
return;
- if (act.Provider == null)
+ if (action.Provider == null)
{
_adminLogger.Add(LogType.Action,
$"{ToPrettyString(user):user} is performing the {name:action} action targeted at {ToPrettyString(entityTarget):target}.");
@@ -145,22 +311,21 @@ public abstract class SharedActionsSystem : EntitySystem
else
{
_adminLogger.Add(LogType.Action,
- $"{ToPrettyString(user):user} is performing the {name:action} action (provided by {ToPrettyString(act.Provider.Value):provider}) targeted at {ToPrettyString(entityTarget):target}.");
+ $"{ToPrettyString(user):user} is performing the {name:action} action (provided by {ToPrettyString(action.Provider.Value):provider}) targeted at {ToPrettyString(entityTarget):target}.");
}
if (entityAction.Event != null)
{
entityAction.Event.Target = entityTarget;
+ Dirty(ev.Action, entityAction);
performEvent = entityAction.Event;
}
break;
-
- case WorldTargetAction worldAction:
-
+ case WorldTargetActionComponent worldAction:
if (ev.EntityCoordinatesTarget is not { } entityCoordinatesTarget)
{
- Log.Error($"Attempted to perform a world-targeted action without a target! Action: {worldAction.DisplayName}");
+ Log.Error($"Attempted to perform a world-targeted action without a target! Action: {name}");
return;
}
@@ -169,7 +334,7 @@ public abstract class SharedActionsSystem : EntitySystem
if (!ValidateWorldTarget(user, entityCoordinatesTarget, worldAction))
return;
- if (act.Provider == null)
+ if (action.Provider == null)
{
_adminLogger.Add(LogType.Action,
$"{ToPrettyString(user):user} is performing the {name:action} action targeted at {entityCoordinatesTarget:target}.");
@@ -177,23 +342,22 @@ public abstract class SharedActionsSystem : EntitySystem
else
{
_adminLogger.Add(LogType.Action,
- $"{ToPrettyString(user):user} is performing the {name:action} action (provided by {ToPrettyString(act.Provider.Value):provider}) targeted at {entityCoordinatesTarget:target}.");
+ $"{ToPrettyString(user):user} is performing the {name:action} action (provided by {ToPrettyString(action.Provider.Value):provider}) targeted at {entityCoordinatesTarget:target}.");
}
if (worldAction.Event != null)
{
worldAction.Event.Target = entityCoordinatesTarget;
+ Dirty(ev.Action, worldAction);
performEvent = worldAction.Event;
}
break;
-
- case InstantAction instantAction:
-
- if (act.CheckCanInteract && !_actionBlockerSystem.CanInteract(user, null))
+ case InstantActionComponent instantAction:
+ if (action.CheckCanInteract && !_actionBlockerSystem.CanInteract(user, null))
return;
- if (act.Provider == null)
+ if (action.Provider == null)
{
_adminLogger.Add(LogType.Action,
$"{ToPrettyString(user):user} is performing the {name:action} action.");
@@ -201,7 +365,7 @@ public abstract class SharedActionsSystem : EntitySystem
else
{
_adminLogger.Add(LogType.Action,
- $"{ToPrettyString(user):user} is performing the {name:action} action provided by {ToPrettyString(act.Provider.Value):provider}.");
+ $"{ToPrettyString(user):user} is performing the {name:action} action provided by {ToPrettyString(action.Provider.Value):provider}.");
}
performEvent = instantAction.Event;
@@ -212,10 +376,10 @@ public abstract class SharedActionsSystem : EntitySystem
performEvent.Performer = user;
// All checks passed. Perform the action!
- PerformAction(user, component, act, performEvent, curTime);
+ PerformAction(user, component, ev.Action, action, performEvent, curTime);
}
- public bool ValidateEntityTarget(EntityUid user, EntityUid target, EntityTargetAction action)
+ public bool ValidateEntityTarget(EntityUid user, EntityUid target, EntityTargetActionComponent action)
{
if (!target.IsValid() || Deleted(target))
return false;
@@ -254,7 +418,7 @@ public abstract class SharedActionsSystem : EntitySystem
return _interactionSystem.CanAccessViaStorage(user, target);
}
- public bool ValidateWorldTarget(EntityUid user, EntityCoordinates coords, WorldTargetAction action)
+ public bool ValidateWorldTarget(EntityUid user, EntityCoordinates coords, WorldTargetActionComponent action)
{
if (action.CheckCanInteract && !_actionBlockerSystem.CanInteract(user, null))
return false;
@@ -276,7 +440,7 @@ public abstract class SharedActionsSystem : EntitySystem
return _interactionSystem.InRangeUnobstructed(user, coords, range: action.Range);
}
- public void PerformAction(EntityUid performer, ActionsComponent? component, ActionType action, BaseActionEvent? actionEvent, TimeSpan curTime, bool predicted = true)
+ public void PerformAction(EntityUid performer, ActionsComponent? component, EntityUid actionId, BaseActionComponent action, BaseActionEvent? actionEvent, TimeSpan curTime, bool predicted = true)
{
var handled = false;
@@ -320,89 +484,180 @@ public abstract class SharedActionsSystem : EntitySystem
action.Cooldown = (curTime, curTime + action.UseDelay.Value);
}
+ Dirty(actionId, action);
+
if (dirty && component != null)
- Dirty(component);
+ Dirty(performer, component);
}
#endregion
#region AddRemoveActions
///
- /// Add an action to an action component. If the entity has no action component, this will give them one.
+ /// Add an action to an action holder.
+ /// If the holder has no actions component, this will give them one.
///
- /// Entity to receive the actions
- /// The action to add
- /// The entity that enables these actions (e.g., flashlight). May be null (innate actions).
- public virtual void AddAction(EntityUid uid, ActionType action, EntityUid? provider, ActionsComponent? comp = null, bool dirty = true)
+ public BaseActionComponent? AddAction(EntityUid holderId, ref EntityUid? actionId, string? actionPrototypeId, EntityUid? provider = null, ActionsComponent? holderComp = null)
{
- // Because action classes have state data, e.g. cooldowns and uses-remaining, people should not be adding prototypes directly
- if (action is IPrototype)
+ if (Deleted(actionId))
{
- Log.Error("Attempted to directly add a prototype action. You need to clone a prototype in order to use it.");
+ if (_net.IsClient)
+ return null;
+
+ if (string.IsNullOrWhiteSpace(actionPrototypeId))
+ return null;
+
+ actionId = Spawn(actionPrototypeId);
+ }
+
+ AddAction(holderId, actionId.Value, provider, holderComp);
+ return GetActionData(actionId);
+ }
+
+ ///
+ /// Add an action to an action holder.
+ /// If the holder has no actions component, this will give them one.
+ ///
+ /// Entity to receive the actions
+ /// Action entity to add
+ /// The entity that enables these actions (e.g., flashlight). May be null (innate actions).
+ /// Component of
+ /// Component of
+ /// Action container of
+ public virtual void AddAction(EntityUid holderId, EntityUid actionId, EntityUid? provider, ActionsComponent? holder = null, BaseActionComponent? action = null, bool dirty = true, IContainer? actionContainer = null)
+ {
+ action ??= GetActionData(actionId);
+ // TODO remove when action subscriptions are split up
+ if (action == null)
+ {
+ Log.Warning($"No {nameof(BaseActionComponent)} found on entity {actionId}");
return;
}
- comp ??= EnsureComp(uid);
+ holder ??= EnsureComp(holderId);
action.Provider = provider;
- action.AttachedEntity = uid;
- AddActionInternal(comp, action);
+ action.AttachedEntity = holderId;
+ Dirty(actionId, action);
+
+ actionContainer ??= EnsureContainer(holderId);
+ AddActionInternal(actionId, actionContainer);
if (dirty)
- Dirty(comp);
+ Dirty(holderId, holder);
}
- protected virtual void AddActionInternal(ActionsComponent comp, ActionType action)
+ protected virtual void AddActionInternal(EntityUid actionId, IContainer container)
{
- comp.Actions.Add(action);
+ container.Insert(actionId);
}
///
/// Add actions to an action component. If the entity has no action component, this will give them one.
///
- /// Entity to receive the actions
+ /// Entity to receive the actions
/// The actions to add
/// The entity that enables these actions (e.g., flashlight). May be null (innate actions).
- public void AddActions(EntityUid uid, IEnumerable actions, EntityUid? provider, ActionsComponent? comp = null, bool dirty = true)
+ public void AddActions(EntityUid holderId, IEnumerable actions, EntityUid? provider, ActionsComponent? comp = null, bool dirty = true)
{
- comp ??= EnsureComp(uid);
+ comp ??= EnsureComp(holderId);
var allClientExclusive = true;
+ var container = EnsureContainer(holderId);
- foreach (var action in actions)
+ foreach (var actionId in actions)
{
- AddAction(uid, action, provider, comp, false);
+ var action = GetActionData(actionId);
+ if (action == null)
+ continue;
+
+ AddAction(holderId, actionId, provider, comp, action, false, container);
allClientExclusive = allClientExclusive && action.ClientExclusive;
}
if (dirty && !allClientExclusive)
- Dirty(comp);
+ Dirty(holderId, comp);
+ }
+
+ public IEnumerable<(EntityUid Id, BaseActionComponent Comp)> GetActions(EntityUid holderId, IContainer? container = null)
+ {
+ if (container == null &&
+ !TryGetContainer(holderId, out container))
+ {
+ yield break;
+ }
+
+ foreach (var actionId in container.ContainedEntities)
+ {
+ if (!TryGetActionData(actionId, out var action))
+ continue;
+
+ yield return (actionId, action);
+ }
}
///
/// Remove any actions that were enabled by some other entity. Useful when unequiping items that grant actions.
///
- public void RemoveProvidedActions(EntityUid uid, EntityUid provider, ActionsComponent? comp = null)
+ public void RemoveProvidedActions(EntityUid holderId, EntityUid provider, ActionsComponent? comp = null, ContainerManagerComponent? actionContainer = null)
{
- if (!Resolve(uid, ref comp, false))
+ if (!Resolve(holderId, ref comp, ref actionContainer, false))
return;
- foreach (var act in comp.Actions.ToArray())
+ if (!TryGetContainer(holderId, out var container, actionContainer))
+ return;
+
+ foreach (var actionId in container.ContainedEntities.ToArray())
{
- if (act.Provider == provider)
- RemoveAction(uid, act, comp, dirty: false);
+ var action = GetActionData(actionId);
+ if (action?.Provider == provider)
+ RemoveAction(holderId, actionId, comp, dirty: false, actionContainer: actionContainer);
}
- Dirty(comp);
+
+ Dirty(holderId, comp);
}
- public virtual void RemoveAction(EntityUid uid, ActionType action, ActionsComponent? comp = null, bool dirty = true)
+ public virtual void RemoveAction(EntityUid holderId, EntityUid? actionId, ActionsComponent? comp = null, BaseActionComponent? action = null, bool dirty = true, ContainerManagerComponent? actionContainer = null)
{
- if (!Resolve(uid, ref comp, false))
+ if (actionId == null ||
+ !Resolve(holderId, ref comp, ref actionContainer, false) ||
+ !TryGetContainer(holderId, out var container, actionContainer) ||
+ !container.Contains(actionId.Value) ||
+ TerminatingOrDeleted(actionId.Value))
+ {
return;
+ }
- comp.Actions.Remove(action);
- action.AttachedEntity = null;
+ action ??= GetActionData(actionId);
+ container.Remove(actionId.Value);
+
+ if (action != null)
+ {
+ action.AttachedEntity = null;
+ Dirty(actionId.Value, action);
+ }
if (dirty)
- Dirty(comp);
+ Dirty(holderId, comp);
+ }
+
+ ///
+ /// Removes all actions with the given prototype id.
+ ///
+ public void RemoveAction(EntityUid holderId, string actionPrototypeId, ActionsComponent? holderComp = null, ContainerManagerComponent? actionContainer = null)
+ {
+ if (!Resolve(holderId, ref holderComp, ref actionContainer, false))
+ return;
+
+ var actions = new List<(EntityUid Id, BaseActionComponent Comp)>();
+ foreach (var (id, comp) in GetActions(holderId))
+ {
+ if (Prototype(id)?.ID == actionPrototypeId)
+ actions.Add((id, comp));
+ }
+
+ foreach (var action in actions)
+ {
+ RemoveAction(holderId, action.Id, holderComp, action.Comp, actionContainer: actionContainer);
+ }
}
#endregion
@@ -410,7 +665,7 @@ public abstract class SharedActionsSystem : EntitySystem
#region EquipHandlers
private void OnDidEquip(EntityUid uid, ActionsComponent component, DidEquipEvent args)
{
- var ev = new GetItemActionsEvent(args.Equipee, args.SlotFlags);
+ var ev = new GetItemActionsEvent(EntityManager, _net, args.Equipee, args.SlotFlags);
RaiseLocalEvent(args.Equipment, ev);
if (ev.Actions.Count == 0)
@@ -421,7 +676,7 @@ public abstract class SharedActionsSystem : EntitySystem
private void OnHandEquipped(EntityUid uid, ActionsComponent component, DidEquipHandEvent args)
{
- var ev = new GetItemActionsEvent(args.User);
+ var ev = new GetItemActionsEvent(EntityManager, _net, args.User);
RaiseLocalEvent(args.Equipped, ev);
if (ev.Actions.Count == 0)
diff --git a/Content.Shared/Actions/WorldTargetActionComponent.cs b/Content.Shared/Actions/WorldTargetActionComponent.cs
new file mode 100644
index 0000000000..f2ee34f530
--- /dev/null
+++ b/Content.Shared/Actions/WorldTargetActionComponent.cs
@@ -0,0 +1,25 @@
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Actions;
+
+[RegisterComponent, NetworkedComponent]
+public sealed partial class WorldTargetActionComponent : BaseTargetActionComponent
+{
+ public override BaseActionEvent? BaseEvent => Event;
+
+ ///
+ /// The local-event to raise when this action is performed.
+ ///
+ [DataField("event")]
+ [NonSerialized]
+ public WorldTargetActionEvent? Event;
+}
+
+[Serializable, NetSerializable]
+public sealed class WorldTargetActionComponentState : BaseActionComponentState
+{
+ public WorldTargetActionComponentState(WorldTargetActionComponent component) : base(component)
+ {
+ }
+}
diff --git a/Content.Shared/Bed/Sleep/SharedSleepingSystem.cs b/Content.Shared/Bed/Sleep/SharedSleepingSystem.cs
index 2ecd6c601c..d049699d9d 100644
--- a/Content.Shared/Bed/Sleep/SharedSleepingSystem.cs
+++ b/Content.Shared/Bed/Sleep/SharedSleepingSystem.cs
@@ -1,18 +1,25 @@
-using Content.Shared.Speech;
using Content.Shared.Actions;
using Content.Shared.Bed.Sleep;
using Content.Shared.Eye.Blinding.Systems;
+using Content.Shared.Speech;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Timing;
namespace Content.Server.Bed.Sleep
{
public abstract class SharedSleepingSystem : EntitySystem
{
+ [Dependency] private readonly IGameTiming _gameTiming = default!;
+ [Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
[Dependency] private readonly BlindableSystem _blindableSystem = default!;
+ [ValidatePrototypeId] private const string WakeActionId = "ActionWake";
+
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent(OnStartup);
+ SubscribeLocalEvent(OnMapInit);
SubscribeLocalEvent(OnShutdown);
SubscribeLocalEvent(OnSpeakAttempt);
SubscribeLocalEvent(OnSeeAttempt);
@@ -22,7 +29,7 @@ namespace Content.Server.Bed.Sleep
private void OnSleepUnpaused(EntityUid uid, SleepingComponent component, ref EntityUnpausedEvent args)
{
component.CoolDownEnd += args.PausedTime;
- Dirty(component);
+ Dirty(uid, component);
}
private void OnStartup(EntityUid uid, SleepingComponent component, ComponentStartup args)
@@ -32,8 +39,17 @@ namespace Content.Server.Bed.Sleep
_blindableSystem.UpdateIsBlind(uid);
}
+ private void OnMapInit(EntityUid uid, SleepingComponent component, MapInitEvent args)
+ {
+ component.WakeAction = Spawn(WakeActionId);
+ _actionsSystem.SetCooldown(component.WakeAction, _gameTiming.CurTime, _gameTiming.CurTime + TimeSpan.FromSeconds(15));
+ _actionsSystem.AddAction(uid, component.WakeAction.Value, null);
+ }
+
private void OnShutdown(EntityUid uid, SleepingComponent component, ComponentShutdown args)
{
+ _actionsSystem.RemoveAction(uid, component.WakeAction);
+
var ev = new SleepStateChangedEvent(false);
RaiseLocalEvent(uid, ev);
_blindableSystem.UpdateIsBlind(uid);
diff --git a/Content.Shared/Bed/Sleep/SleepingComponent.cs b/Content.Shared/Bed/Sleep/SleepingComponent.cs
index e4100ff6da..94838b658f 100644
--- a/Content.Shared/Bed/Sleep/SleepingComponent.cs
+++ b/Content.Shared/Bed/Sleep/SleepingComponent.cs
@@ -25,4 +25,6 @@ public sealed partial class SleepingComponent : Component
[DataField("cooldownEnd", customTypeSerializer:typeof(TimeOffsetSerializer))]
public TimeSpan CoolDownEnd;
+
+ [DataField("wakeAction")] public EntityUid? WakeAction;
}
diff --git a/Content.Shared/Bible/SummonActionEvent.cs b/Content.Shared/Bible/SummonActionEvent.cs
new file mode 100644
index 0000000000..3d57f4095e
--- /dev/null
+++ b/Content.Shared/Bible/SummonActionEvent.cs
@@ -0,0 +1,8 @@
+using Content.Shared.Actions;
+
+namespace Content.Shared.Bible;
+
+public sealed partial class SummonActionEvent : InstantActionEvent
+{
+
+}
\ No newline at end of file
diff --git a/Content.Shared/Blocking/BlockingSystem.cs b/Content.Shared/Blocking/BlockingSystem.cs
index 7a7cf02e1d..3735058154 100644
--- a/Content.Shared/Blocking/BlockingSystem.cs
+++ b/Content.Shared/Blocking/BlockingSystem.cs
@@ -1,8 +1,6 @@
using System.Linq;
using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Damage;
-using Content.Shared.Damage.Prototypes;
using Content.Shared.Examine;
using Content.Shared.Hands;
using Content.Shared.Hands.Components;
@@ -80,11 +78,7 @@ public sealed partial class BlockingSystem : EntitySystem
private void OnGetActions(EntityUid uid, BlockingComponent component, GetItemActionsEvent args)
{
- if (component.BlockingToggleAction == null && _proto.TryIndex(component.BlockingToggleActionId, out InstantActionPrototype? act))
- component.BlockingToggleAction = new(act);
-
- if (component.BlockingToggleAction != null)
- args.Actions.Add(component.BlockingToggleAction);
+ args.AddAction(ref component.BlockingToggleActionEntity, component.BlockingToggleAction);
}
private void OnToggleAction(EntityUid uid, BlockingComponent component, ToggleActionEvent args)
@@ -191,7 +185,7 @@ public sealed partial class BlockingSystem : EntitySystem
CantBlockError(user);
return false;
}
- _actionsSystem.SetToggled(component.BlockingToggleAction, true);
+ _actionsSystem.SetToggled(component.BlockingToggleActionEntity, true);
_popupSystem.PopupEntity(msgUser, user, user);
_popupSystem.PopupEntity(msgOther, user, Filter.PvsExcept(user), true);
}
@@ -252,7 +246,7 @@ public sealed partial class BlockingSystem : EntitySystem
if (xform.Anchored)
_transformSystem.Unanchor(user, xform);
- _actionsSystem.SetToggled(component.BlockingToggleAction, false);
+ _actionsSystem.SetToggled(component.BlockingToggleActionEntity, false);
_fixtureSystem.DestroyFixture(user, BlockingComponent.BlockFixtureID, body: physicsComponent);
_physics.SetBodyType(user, blockingUserComponent.OriginalBodyType, body: physicsComponent);
_popupSystem.PopupEntity(msgUser, user, user);
diff --git a/Content.Shared/Blocking/Components/BlockingComponent.cs b/Content.Shared/Blocking/Components/BlockingComponent.cs
index 89f3a85de7..b33a7d7a73 100644
--- a/Content.Shared/Blocking/Components/BlockingComponent.cs
+++ b/Content.Shared/Blocking/Components/BlockingComponent.cs
@@ -1,7 +1,7 @@
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Damage;
using Robust.Shared.Audio;
using Robust.Shared.Physics.Collision.Shapes;
+using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared.Blocking;
@@ -47,11 +47,11 @@ public sealed partial class BlockingComponent : Component
[DataField("activeBlockModifier", required: true)]
public DamageModifierSet ActiveBlockDamageModifier = default!;
- [DataField("blockingToggleActionId", customTypeSerializer: typeof(PrototypeIdSerializer))]
- public string BlockingToggleActionId = "ToggleBlock";
+ [DataField("blockingToggleAction", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string BlockingToggleAction = "ActionToggleBlock";
- [DataField("blockingToggleAction")]
- public InstantAction? BlockingToggleAction;
+ [DataField("blockingToggleActionEntity")]
+ public EntityUid? BlockingToggleActionEntity;
///
/// The sound to be played when you get hit while actively blocking
diff --git a/Content.Shared/CartridgeLoader/CartridgeLoaderComponent.cs b/Content.Shared/CartridgeLoader/CartridgeLoaderComponent.cs
index 9acbc0cfc5..5a7c40f3d1 100644
--- a/Content.Shared/CartridgeLoader/CartridgeLoaderComponent.cs
+++ b/Content.Shared/CartridgeLoader/CartridgeLoaderComponent.cs
@@ -1,6 +1,5 @@
using Content.Shared.Containers.ItemSlots;
using Robust.Shared.GameStates;
-using Robust.Shared.Serialization.TypeSerializers.Implementations;
namespace Content.Shared.CartridgeLoader;
@@ -42,6 +41,6 @@ public sealed partial class CartridgeLoaderComponent : Component
[DataField("diskSpace")]
public int DiskSpace = 5;
- [DataField("uiKey", required: true, customTypeSerializer: typeof(EnumSerializer))]
+ [DataField("uiKey", required: true)]
public Enum UiKey = default!;
}
diff --git a/Content.Shared/Clothing/ClothingEvents.cs b/Content.Shared/Clothing/ClothingEvents.cs
index 639212dd4d..028537f8b2 100644
--- a/Content.Shared/Clothing/ClothingEvents.cs
+++ b/Content.Shared/Clothing/ClothingEvents.cs
@@ -1,4 +1,6 @@
+using Content.Shared.Actions;
+
namespace Content.Shared.Clothing;
///
@@ -55,3 +57,5 @@ public sealed class EquipmentVisualsUpdatedEvent : EntityEventArgs
RevealedLayers = revealedLayers;
}
}
+
+public sealed partial class ToggleMaskEvent : InstantActionEvent { }
diff --git a/Content.Shared/Clothing/Components/StealthClothingComponent.cs b/Content.Shared/Clothing/Components/StealthClothingComponent.cs
index 2571d89b1f..fe84fbe76c 100644
--- a/Content.Shared/Clothing/Components/StealthClothingComponent.cs
+++ b/Content.Shared/Clothing/Components/StealthClothingComponent.cs
@@ -1,7 +1,8 @@
using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Clothing.EntitySystems;
using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared.Clothing.Components;
@@ -23,14 +24,13 @@ public sealed partial class StealthClothingComponent : Component
[DataField("visibility"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
public float Visibility;
+ [DataField("toggleAction", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string ToggleAction = "ActionTogglePhaseCloak";
+
///
/// The action for enabling and disabling stealth.
///
- [DataField("toggleAction")]
- public InstantAction ToggleAction = new()
- {
- Event = new ToggleStealthEvent()
- };
+ [DataField("toggleActionEntity")] public EntityUid? ToggleActionEntity;
}
///
diff --git a/Content.Shared/Clothing/Components/ToggleableClothingComponent.cs b/Content.Shared/Clothing/Components/ToggleableClothingComponent.cs
index 74c5d7a6ba..b87cd3fee5 100644
--- a/Content.Shared/Clothing/Components/ToggleableClothingComponent.cs
+++ b/Content.Shared/Clothing/Components/ToggleableClothingComponent.cs
@@ -1,4 +1,3 @@
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Clothing.EntitySystems;
using Content.Shared.Inventory;
using Robust.Shared.Containers;
@@ -20,9 +19,11 @@ public sealed partial class ToggleableClothingComponent : Component
///
/// Action used to toggle the clothing on or off.
///
- [DataField("actionId", customTypeSerializer: typeof(PrototypeIdSerializer))]
- public string ActionId = "ToggleSuitPiece";
- public InstantAction? ToggleAction = null;
+ [DataField("action", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string Action = "ActionToggleSuitPiece";
+
+ [DataField("actionEntity")]
+ public EntityUid? ActionEntity;
///
/// Default clothing entity prototype to spawn into the clothing container.
@@ -66,7 +67,7 @@ public sealed partial class ToggleableClothingComponent : Component
public TimeSpan? StripDelay = TimeSpan.FromSeconds(3);
///
- /// Text shown in the toggle-clothing verb. Defaults to using the name of the action.
+ /// Text shown in the toggle-clothing verb. Defaults to using the name of the action.
///
[DataField("verbText")]
public string? VerbText;
diff --git a/Content.Shared/Clothing/EntitySystems/StealthClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/StealthClothingSystem.cs
index dc8de4385b..2fbaa6ea20 100644
--- a/Content.Shared/Clothing/EntitySystems/StealthClothingSystem.cs
+++ b/Content.Shared/Clothing/EntitySystems/StealthClothingSystem.cs
@@ -3,7 +3,6 @@ using Content.Shared.Clothing.Components;
using Content.Shared.Inventory.Events;
using Content.Shared.Stealth;
using Content.Shared.Stealth.Components;
-using Robust.Shared.GameStates;
namespace Content.Shared.Clothing.EntitySystems;
@@ -59,7 +58,7 @@ public sealed class StealthClothingSystem : EntitySystem
if (ev.Cancelled)
return;
- args.Actions.Add(comp.ToggleAction);
+ args.AddAction(ref comp.ToggleActionEntity, comp.ToggleAction);
}
///
diff --git a/Content.Shared/Clothing/EntitySystems/ToggleableClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/ToggleableClothingSystem.cs
index 9201cf0797..06ef6728fa 100644
--- a/Content.Shared/Clothing/EntitySystems/ToggleableClothingSystem.cs
+++ b/Content.Shared/Clothing/EntitySystems/ToggleableClothingSystem.cs
@@ -1,5 +1,4 @@
using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Clothing.Components;
using Content.Shared.DoAfter;
using Content.Shared.IdentityManagement;
@@ -10,7 +9,6 @@ using Content.Shared.Popups;
using Content.Shared.Strip;
using Content.Shared.Verbs;
using Robust.Shared.Containers;
-using Robust.Shared.Network;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
@@ -60,7 +58,7 @@ public sealed class ToggleableClothingSystem : EntitySystem
if (!args.CanAccess || !args.CanInteract || component.ClothingUid == null || component.Container == null)
return;
- var text = component.VerbText ?? component.ToggleAction?.DisplayName;
+ var text = component.VerbText ?? (component.ActionEntity == null ? null : Name(component.ActionEntity.Value));
if (text == null)
return;
@@ -179,8 +177,11 @@ public sealed class ToggleableClothingSystem : EntitySystem
// automatically be deleted.
// remove action.
- if (component.ToggleAction?.AttachedEntity != null)
- _actionsSystem.RemoveAction(component.ToggleAction.AttachedEntity.Value, component.ToggleAction);
+ if (_actionsSystem.TryGetActionData(component.ActionEntity, out var action) &&
+ action.AttachedEntity != null)
+ {
+ _actionsSystem.RemoveAction(action.AttachedEntity.Value, component.ActionEntity);
+ }
if (component.ClothingUid != null)
QueueDel(component.ClothingUid.Value);
@@ -200,8 +201,11 @@ public sealed class ToggleableClothingSystem : EntitySystem
return;
// remove action.
- if (toggleComp.ToggleAction?.AttachedEntity != null)
- _actionsSystem.RemoveAction(toggleComp.ToggleAction.AttachedEntity.Value, toggleComp.ToggleAction);
+ if (_actionsSystem.TryGetActionData(toggleComp.ActionEntity, out var action) &&
+ action.AttachedEntity != null)
+ {
+ _actionsSystem.RemoveAction(action.AttachedEntity.Value, toggleComp.ActionEntity);
+ }
RemComp(component.AttachedUid, toggleComp);
}
@@ -259,8 +263,7 @@ public sealed class ToggleableClothingSystem : EntitySystem
if (component.ClothingUid == null || (args.SlotFlags & component.RequiredFlags) != component.RequiredFlags)
return;
- if (component.ToggleAction != null)
- args.Actions.Add(component.ToggleAction);
+ args.AddAction(ref component.ActionEntity, component.Action);
}
private void OnInit(EntityUid uid, ToggleableClothingComponent component, ComponentInit args)
@@ -280,13 +283,9 @@ public sealed class ToggleableClothingSystem : EntitySystem
return;
}
- if (component.ToggleAction == null
- && _proto.TryIndex(component.ActionId, out InstantActionPrototype? act))
- {
- component.ToggleAction = new(act);
- }
+ component.ActionEntity ??= Spawn(component.Action);
- if (component.ClothingUid != null && component.ToggleAction != null)
+ if (component.ClothingUid != null && component.ActionEntity != null)
{
DebugTools.Assert(Exists(component.ClothingUid), "Toggleable clothing is missing expected entity.");
DebugTools.Assert(TryComp(component.ClothingUid, out AttachedClothingComponent? comp), "Toggleable clothing is missing an attached component");
@@ -300,10 +299,10 @@ public sealed class ToggleableClothingSystem : EntitySystem
component.Container.Insert(component.ClothingUid.Value, EntityManager, ownerTransform: xform);
}
- if (component.ToggleAction != null)
+ if (_actionsSystem.TryGetActionData(component.ActionEntity, out var action))
{
- component.ToggleAction.EntityIcon = component.ClothingUid;
- _actionsSystem.Dirty(component.ToggleAction);
+ action.EntityIcon = component.ClothingUid;
+ _actionsSystem.Dirty(component.ActionEntity);
}
}
}
diff --git a/Content.Shared/Clothing/MagbootsComponent.cs b/Content.Shared/Clothing/MagbootsComponent.cs
index f9980c8014..f90a5576c5 100644
--- a/Content.Shared/Clothing/MagbootsComponent.cs
+++ b/Content.Shared/Clothing/MagbootsComponent.cs
@@ -1,5 +1,6 @@
-using Content.Shared.Actions.ActionTypes;
using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared.Clothing;
@@ -7,8 +8,11 @@ namespace Content.Shared.Clothing;
[Access(typeof(SharedMagbootsSystem))]
public sealed partial class MagbootsComponent : Component
{
- [DataField("toggleAction", required: true)]
- public InstantAction ToggleAction = new();
+ [DataField("toggleAction", customTypeSerializer: typeof(PrototypeIdSerializer), required: true)]
+ public string? ToggleAction;
+
+ [DataField("toggleActionEntity")]
+ public EntityUid? ToggleActionEntity;
[DataField("on"), AutoNetworkedField]
public bool On;
diff --git a/Content.Shared/Clothing/SharedMagbootsSystem.cs b/Content.Shared/Clothing/SharedMagbootsSystem.cs
index 9a470f45b3..69b7849992 100644
--- a/Content.Shared/Clothing/SharedMagbootsSystem.cs
+++ b/Content.Shared/Clothing/SharedMagbootsSystem.cs
@@ -62,7 +62,7 @@ public abstract class SharedMagbootsSystem : EntitySystem
protected void OnChanged(EntityUid uid, MagbootsComponent component)
{
- _sharedActions.SetToggled(component.ToggleAction, component.On);
+ _sharedActions.SetToggled(component.ToggleActionEntity, component.On);
_clothingSpeedModifier.SetClothingSpeedModifierEnabled(uid, component.On);
}
@@ -86,7 +86,7 @@ public abstract class SharedMagbootsSystem : EntitySystem
private void OnGetActions(EntityUid uid, MagbootsComponent component, GetItemActionsEvent args)
{
- args.Actions.Add(component.ToggleAction);
+ args.AddAction(ref component.ToggleActionEntity, component.ToggleAction);
}
}
diff --git a/Content.Shared/CombatMode/CombatModeComponent.cs b/Content.Shared/CombatMode/CombatModeComponent.cs
index 511a37a980..12d1cf264a 100644
--- a/Content.Shared/CombatMode/CombatModeComponent.cs
+++ b/Content.Shared/CombatMode/CombatModeComponent.cs
@@ -1,7 +1,7 @@
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Targeting;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared.CombatMode
@@ -32,11 +32,11 @@ namespace Content.Shared.CombatMode
#endregion
- [DataField("combatToggleActionId", customTypeSerializer: typeof(PrototypeIdSerializer))]
- public string CombatToggleActionId = "CombatModeToggle";
+ [DataField("combatToggleAction", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string CombatToggleAction = "ActionCombatModeToggle";
- [DataField("combatToggleAction")]
- public InstantAction? CombatToggleAction;
+ [DataField("combatToggleActionEntity")]
+ public EntityUid? CombatToggleActionEntity;
[ViewVariables(VVAccess.ReadWrite), DataField("isInCombatMode"), AutoNetworkedField]
public bool IsInCombatMode;
diff --git a/Content.Shared/CombatMode/Pacification/PacificationSystem.cs b/Content.Shared/CombatMode/Pacification/PacificationSystem.cs
index e8807d4bbc..c52605e09f 100644
--- a/Content.Shared/CombatMode/Pacification/PacificationSystem.cs
+++ b/Content.Shared/CombatMode/Pacification/PacificationSystem.cs
@@ -1,7 +1,6 @@
using Content.Shared.Actions;
using Content.Shared.Alert;
using Content.Shared.Interaction.Events;
-using Content.Shared.Popups;
namespace Content.Shared.CombatMode.Pacification;
@@ -33,10 +32,7 @@ public sealed class PacificationSystem : EntitySystem
_combatSystem.SetCanDisarm(uid, false, combatMode);
_combatSystem.SetInCombatMode(uid, false, combatMode);
-
- if (combatMode.CombatToggleAction != null)
- _actionsSystem.SetEnabled(combatMode.CombatToggleAction, false);
-
+ _actionsSystem.SetEnabled(combatMode.CombatToggleActionEntity, false);
_alertsSystem.ShowAlert(uid, AlertType.Pacified);
}
@@ -48,9 +44,7 @@ public sealed class PacificationSystem : EntitySystem
if (combatMode.CanDisarm != null)
_combatSystem.SetCanDisarm(uid, true, combatMode);
- if (combatMode.CombatToggleAction != null)
- _actionsSystem.SetEnabled(combatMode.CombatToggleAction, true);
-
+ _actionsSystem.SetEnabled(combatMode.CombatToggleActionEntity, true);
_alertsSystem.ClearAlert(uid, AlertType.Pacified);
}
}
diff --git a/Content.Shared/CombatMode/SharedCombatModeSystem.cs b/Content.Shared/CombatMode/SharedCombatModeSystem.cs
index 53e65d031b..5b208a08f9 100644
--- a/Content.Shared/CombatMode/SharedCombatModeSystem.cs
+++ b/Content.Shared/CombatMode/SharedCombatModeSystem.cs
@@ -1,10 +1,7 @@
using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Popups;
using Content.Shared.Targeting;
using Robust.Shared.Network;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Serialization;
using Robust.Shared.Timing;
namespace Content.Shared.CombatMode;
@@ -13,7 +10,6 @@ public abstract class SharedCombatModeSystem : EntitySystem
{
[Dependency] protected readonly IGameTiming Timing = default!;
[Dependency] private readonly INetManager _netMan = default!;
- [Dependency] private readonly IPrototypeManager _protoMan = default!;
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
@@ -21,27 +17,19 @@ public abstract class SharedCombatModeSystem : EntitySystem
{
base.Initialize();
- SubscribeLocalEvent(OnStartup);
+ SubscribeLocalEvent(OnStartup);
SubscribeLocalEvent(OnShutdown);
SubscribeLocalEvent(OnActionPerform);
}
- private void OnStartup(EntityUid uid, CombatModeComponent component, ComponentStartup args)
+ private void OnStartup(EntityUid uid, CombatModeComponent component, MapInitEvent args)
{
- if (component.CombatToggleAction == null
- && _protoMan.TryIndex(component.CombatToggleActionId, out InstantActionPrototype? toggleProto))
- {
- component.CombatToggleAction = new(toggleProto);
- }
-
- if (component.CombatToggleAction != null)
- _actionsSystem.AddAction(uid, component.CombatToggleAction, null);
+ _actionsSystem.AddAction(uid, ref component.CombatToggleActionEntity, component.CombatToggleAction);
}
private void OnShutdown(EntityUid uid, CombatModeComponent component, ComponentShutdown args)
{
- if (component.CombatToggleAction != null)
- _actionsSystem.RemoveAction(uid, component.CombatToggleAction);
+ _actionsSystem.RemoveAction(uid, component.CombatToggleActionEntity);
}
private void OnActionPerform(EntityUid uid, CombatModeComponent component, ToggleCombatActionEvent args)
@@ -86,8 +74,8 @@ public abstract class SharedCombatModeSystem : EntitySystem
component.IsInCombatMode = value;
Dirty(entity, component);
- if (component.CombatToggleAction != null)
- _actionsSystem.SetToggled(component.CombatToggleAction, component.IsInCombatMode);
+ if (component.CombatToggleActionEntity != null)
+ _actionsSystem.SetToggled(component.CombatToggleActionEntity, component.IsInCombatMode);
}
public virtual void SetActiveZone(EntityUid entity, TargetingZone zone,
diff --git a/Content.Shared/Cuffs/SharedCuffableSystem.cs b/Content.Shared/Cuffs/SharedCuffableSystem.cs
index 7dd1ec9065..e2f4eafad9 100644
--- a/Content.Shared/Cuffs/SharedCuffableSystem.cs
+++ b/Content.Shared/Cuffs/SharedCuffableSystem.cs
@@ -141,7 +141,8 @@ namespace Content.Shared.Cuffs
private void OnCuffsRemovedFromContainer(EntityUid uid, CuffableComponent component, EntRemovedFromContainerMessage args)
{
- if (args.Container.ID != component.Container.ID)
+ // ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract
+ if (args.Container.ID != component.Container?.ID)
return;
_handVirtualItem.DeleteInHandsMatching(uid, args.Entity);
diff --git a/Content.Shared/Decals/PlaceDecalActionEvent.cs b/Content.Shared/Decals/PlaceDecalActionEvent.cs
new file mode 100644
index 0000000000..3991cd36c9
--- /dev/null
+++ b/Content.Shared/Decals/PlaceDecalActionEvent.cs
@@ -0,0 +1,25 @@
+using Content.Shared.Actions;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+
+namespace Content.Shared.Decals;
+
+public sealed partial class PlaceDecalActionEvent : WorldTargetActionEvent
+{
+ [DataField("decalId", customTypeSerializer:typeof(PrototypeIdSerializer), required:true)]
+ public string DecalId = string.Empty;
+
+ [DataField("color")]
+ public Color Color;
+
+ [DataField("rotation")]
+ public double Rotation;
+
+ [DataField("snap")]
+ public bool Snap;
+
+ [DataField("zIndex")]
+ public int ZIndex;
+
+ [DataField("cleanable")]
+ public bool Cleanable;
+}
diff --git a/Content.Shared/Devour/Components/DevourerComponent.cs b/Content.Shared/Devour/Components/DevourerComponent.cs
index ccba438a17..fbeec28ca5 100644
--- a/Content.Shared/Devour/Components/DevourerComponent.cs
+++ b/Content.Shared/Devour/Components/DevourerComponent.cs
@@ -1,21 +1,22 @@
-using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Chemistry.Reagent;
-using Content.Shared.Devour;
using Content.Shared.Whitelist;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-namespace Content.Server.Devour.Components;
+namespace Content.Shared.Devour.Components;
[RegisterComponent, NetworkedComponent]
[Access(typeof(SharedDevourSystem))]
public sealed partial class DevourerComponent : Component
{
- [DataField("devourAction")]
- public EntityTargetAction? DevourAction;
+ [DataField("devourAction", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string? DevourAction = "ActionDevour";
+
+ [DataField("devourActionEntity")]
+ public EntityUid? DevourActionEntity;
[ViewVariables(VVAccess.ReadWrite), DataField("soundDevour")]
public SoundSpecifier? SoundDevour = new SoundPathSpecifier("/Audio/Effects/demon_consume.ogg")
diff --git a/Content.Shared/Devour/SharedDevourSystem.cs b/Content.Shared/Devour/SharedDevourSystem.cs
index 4ced634975..5ac052d3a4 100644
--- a/Content.Shared/Devour/SharedDevourSystem.cs
+++ b/Content.Shared/Devour/SharedDevourSystem.cs
@@ -1,5 +1,5 @@
-using Content.Server.Devour.Components;
using Content.Shared.Actions;
+using Content.Shared.Devour.Components;
using Content.Shared.DoAfter;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
@@ -21,18 +21,17 @@ public abstract class SharedDevourSystem : EntitySystem
{
base.Initialize();
- SubscribeLocalEvent(OnStartup);
+ SubscribeLocalEvent(OnMapInit);
SubscribeLocalEvent(OnDevourAction);
}
- protected void OnStartup(EntityUid uid, DevourerComponent component, ComponentStartup args)
+ protected void OnMapInit(EntityUid uid, DevourerComponent component, MapInitEvent args)
{
//Devourer doesn't actually chew, since he sends targets right into his stomach.
//I did it mom, I added ERP content into upstream. Legally!
component.Stomach = _containerSystem.EnsureContainer(uid, "stomach");
- if (component.DevourAction != null)
- _actionsSystem.AddAction(uid, component.DevourAction, null);
+ _actionsSystem.AddAction(uid, ref component.DevourActionEntity, component.DevourAction);
}
///
diff --git a/Content.Shared/Dragon/DragonEvents.cs b/Content.Shared/Dragon/DragonEvents.cs
new file mode 100644
index 0000000000..e3db9c32da
--- /dev/null
+++ b/Content.Shared/Dragon/DragonEvents.cs
@@ -0,0 +1,11 @@
+using Content.Shared.Actions;
+
+namespace Content.Shared.Dragon;
+
+public sealed partial class DragonDevourActionEvent : EntityTargetActionEvent
+{
+}
+
+public sealed partial class DragonSpawnRiftActionEvent : InstantActionEvent
+{
+}
diff --git a/Content.Shared/Ghost/GhostComponent.cs b/Content.Shared/Ghost/GhostComponent.cs
index b45bdb2cf3..8aea2c0833 100644
--- a/Content.Shared/Ghost/GhostComponent.cs
+++ b/Content.Shared/Ghost/GhostComponent.cs
@@ -1,9 +1,8 @@
using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
using Robust.Shared.GameStates;
-using Robust.Shared.Serialization;
+using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
-using Robust.Shared.Utility;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared.Ghost;
@@ -15,35 +14,23 @@ public sealed partial class GhostComponent : Component
[ViewVariables]
public bool IsAttached;
- public InstantAction ToggleLightingAction = new()
- {
- Icon = new SpriteSpecifier.Texture(new ("Interface/VerbIcons/light.svg.192dpi.png")),
- DisplayName = "ghost-gui-toggle-lighting-manager-name",
- Description = "ghost-gui-toggle-lighting-manager-desc",
- ClientExclusive = true,
- CheckCanInteract = false,
- Event = new ToggleLightingActionEvent(),
- };
+ [DataField("toggleLightingAction", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string ToggleLightingAction = "ActionToggleLighting";
- public InstantAction ToggleFoVAction = new()
- {
- Icon = new SpriteSpecifier.Texture(new ("Interface/VerbIcons/vv.svg.192dpi.png")),
- DisplayName = "ghost-gui-toggle-fov-name",
- Description = "ghost-gui-toggle-fov-desc",
- ClientExclusive = true,
- CheckCanInteract = false,
- Event = new ToggleFoVActionEvent(),
- };
+ [DataField("toggleLightingActionEntity")]
+ public EntityUid? ToggleLightingActionEntity;
- public InstantAction ToggleGhostsAction = new()
- {
- Icon = new SpriteSpecifier.Rsi(new ("Mobs/Ghosts/ghost_human.rsi"), "icon"),
- DisplayName = "ghost-gui-toggle-ghost-visibility-name",
- Description = "ghost-gui-toggle-ghost-visibility-desc",
- ClientExclusive = true,
- CheckCanInteract = false,
- Event = new ToggleGhostsActionEvent(),
- };
+ [DataField("toggleFovAction", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string ToggleFoVAction = "ActionToggleFov";
+
+ [DataField("toggleFovActionEntity")]
+ public EntityUid? ToggleFoVActionEntity;
+
+ [DataField("toggleGhostsAction", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string ToggleGhostsAction = "ActionToggleGhosts";
+
+ [DataField("toggleGhostsActionEntity")]
+ public EntityUid? ToggleGhostsActionEntity;
[ViewVariables(VVAccess.ReadWrite), DataField("timeOfDeath", customTypeSerializer:typeof(TimeOffsetSerializer))]
public TimeSpan TimeOfDeath = TimeSpan.Zero;
@@ -54,16 +41,10 @@ public sealed partial class GhostComponent : Component
[DataField("booMaxTargets")]
public int BooMaxTargets = 3;
- [DataField("action")]
- public InstantAction Action = new()
- {
- UseDelay = TimeSpan.FromSeconds(120),
- Icon = new SpriteSpecifier.Texture(new ("Interface/Actions/scream.png")),
- DisplayName = "action-name-boo",
- Description = "action-description-boo",
- CheckCanInteract = false,
- Event = new BooActionEvent(),
- };
+ [DataField("action", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string Action = "ActionGhostBoo";
+
+ [DataField("actionEntity")] public EntityUid? ActionEntity;
// TODO: instead of this funny stuff just give it access and update in system dirtying when needed
[ViewVariables(VVAccess.ReadWrite)]
diff --git a/Content.Shared/Guardian/GuardianToggleActionEvent.cs b/Content.Shared/Guardian/GuardianToggleActionEvent.cs
new file mode 100644
index 0000000000..15328984fe
--- /dev/null
+++ b/Content.Shared/Guardian/GuardianToggleActionEvent.cs
@@ -0,0 +1,7 @@
+using Content.Shared.Actions;
+
+namespace Content.Shared.Guardian;
+
+public sealed partial class GuardianToggleActionEvent : InstantActionEvent
+{
+}
diff --git a/Content.Shared/Implants/SharedSubdermalImplantSystem.cs b/Content.Shared/Implants/SharedSubdermalImplantSystem.cs
index 2a41273512..2f98093525 100644
--- a/Content.Shared/Implants/SharedSubdermalImplantSystem.cs
+++ b/Content.Shared/Implants/SharedSubdermalImplantSystem.cs
@@ -1,6 +1,5 @@
using System.Linq;
using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Implants.Components;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
@@ -36,9 +35,9 @@ public abstract class SharedSubdermalImplantSystem : EntitySystem
if (component.ImplantedEntity == null)
return;
- if (component.ImplantAction != null)
+ if (!string.IsNullOrWhiteSpace(component.ImplantAction))
{
- var action = new InstantAction(_prototypeManager.Index(component.ImplantAction));
+ var action = Spawn(component.ImplantAction);
_actionsSystem.AddAction(component.ImplantedEntity.Value, action, uid);
}
diff --git a/Content.Shared/Light/Components/HandheldLightComponent.cs b/Content.Shared/Light/Components/HandheldLightComponent.cs
index 8edf558566..9e3cad7552 100644
--- a/Content.Shared/Light/Components/HandheldLightComponent.cs
+++ b/Content.Shared/Light/Components/HandheldLightComponent.cs
@@ -1,6 +1,6 @@
-using Content.Shared.Actions.ActionTypes;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
@@ -35,8 +35,8 @@ public sealed partial class HandheldLightComponent : Component
[DataField("addPrefix")]
public bool AddPrefix = false;
- [DataField("toggleActionId", customTypeSerializer: typeof(PrototypeIdSerializer))]
- public string ToggleActionId = "ToggleLight";
+ [DataField("toggleAction", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string ToggleAction = "ActionToggleLight";
///
/// Whether or not the light can be toggled via standard interactions
@@ -45,8 +45,8 @@ public sealed partial class HandheldLightComponent : Component
[DataField("toggleOnInteract")]
public bool ToggleOnInteract = true;
- [DataField("toggleAction")]
- public InstantAction? ToggleAction;
+ [DataField("toggleActionEntity")]
+ public EntityUid? ToggleActionEntity;
public const int StatusLevels = 6;
diff --git a/Content.Shared/Light/Components/UnpoweredFlashlightComponent.cs b/Content.Shared/Light/Components/UnpoweredFlashlightComponent.cs
index 4011f6937b..e2146abe2f 100644
--- a/Content.Shared/Light/Components/UnpoweredFlashlightComponent.cs
+++ b/Content.Shared/Light/Components/UnpoweredFlashlightComponent.cs
@@ -1,6 +1,7 @@
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Decals;
using Robust.Shared.Audio;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared.Light.Components;
@@ -16,8 +17,10 @@ public sealed partial class UnpoweredFlashlightComponent : Component
[ViewVariables] public bool LightOn = false;
- [DataField("toggleAction", required: true)]
- public InstantAction ToggleAction = new();
+ [DataField("toggleAction", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string? ToggleAction = "ActionToggleLight";
+
+ [DataField("toggleActionEntity")] public EntityUid? ToggleActionEntity;
///
/// ID that determines the list
diff --git a/Content.Shared/Light/SharedHandheldLightSystem.cs b/Content.Shared/Light/SharedHandheldLightSystem.cs
index 88fd07ba20..ad209c95f8 100644
--- a/Content.Shared/Light/SharedHandheldLightSystem.cs
+++ b/Content.Shared/Light/SharedHandheldLightSystem.cs
@@ -3,10 +3,7 @@ using Content.Shared.Clothing.EntitySystems;
using Content.Shared.Item;
using Content.Shared.Light.Components;
using Content.Shared.Toggleable;
-using Robust.Shared.Audio;
using Robust.Shared.GameStates;
-using Robust.Shared.Player;
-using Robust.Shared.Utility;
namespace Content.Shared.Light;
@@ -74,8 +71,8 @@ public abstract class SharedHandheldLightSystem : EntitySystem
_clothingSys.SetEquippedPrefix(uid, prefix);
}
- if (component.ToggleAction != null)
- _actionSystem.SetToggled(component.ToggleAction, component.Activated);
+ if (component.ToggleActionEntity != null)
+ _actionSystem.SetToggled(component.ToggleActionEntity, component.Activated);
_appearance.SetData(uid, ToggleableLightVisuals.Enabled, component.Activated, appearance);
}
diff --git a/Content.Server/Magic/Events/ChangeComponentsSpellEvent.cs b/Content.Shared/Magic/Events/ChangeComponentSpellEvent.cs
similarity index 90%
rename from Content.Server/Magic/Events/ChangeComponentsSpellEvent.cs
rename to Content.Shared/Magic/Events/ChangeComponentSpellEvent.cs
index df8c25f801..61e75c8b1a 100644
--- a/Content.Server/Magic/Events/ChangeComponentsSpellEvent.cs
+++ b/Content.Shared/Magic/Events/ChangeComponentSpellEvent.cs
@@ -1,7 +1,7 @@
-using Content.Shared.Actions;
+using Content.Shared.Actions;
using Robust.Shared.Prototypes;
-namespace Content.Server.Magic.Events;
+namespace Content.Shared.Magic.Events;
///
/// Spell that uses the magic of ECS to add & remove components. Components are first removed, then added.
diff --git a/Content.Server/Magic/Events/InstantSpawnSpellEvent.cs b/Content.Shared/Magic/Events/InstantSpawnSpellEvent.cs
similarity index 64%
rename from Content.Server/Magic/Events/InstantSpawnSpellEvent.cs
rename to Content.Shared/Magic/Events/InstantSpawnSpellEvent.cs
index f475c33648..ef8d689862 100644
--- a/Content.Server/Magic/Events/InstantSpawnSpellEvent.cs
+++ b/Content.Shared/Magic/Events/InstantSpawnSpellEvent.cs
@@ -2,7 +2,7 @@
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-namespace Content.Server.Magic.Events;
+namespace Content.Shared.Magic.Events;
public sealed partial class InstantSpawnSpellEvent : InstantActionEvent, ISpeakSpell
{
@@ -23,23 +23,3 @@ public sealed partial class InstantSpawnSpellEvent : InstantActionEvent, ISpeakS
///
[DataField("posData")] public MagicSpawnData Pos = new TargetCasterPos();
}
-
-[ImplicitDataDefinitionForInheritors]
-public abstract partial class MagicSpawnData
-{
-
-}
-
-///
-/// Spawns 1 at the caster's feet.
-///
-public sealed partial class TargetCasterPos : MagicSpawnData {}
-
-///
-/// Targets the 3 tiles in front of the caster.
-///
-public sealed partial class TargetInFront : MagicSpawnData
-{
- [DataField("width")]
- public int Width = 3;
-}
diff --git a/Content.Server/Magic/Events/KnockSpellEvent.cs b/Content.Shared/Magic/Events/KnockSpellEvent.cs
similarity index 94%
rename from Content.Server/Magic/Events/KnockSpellEvent.cs
rename to Content.Shared/Magic/Events/KnockSpellEvent.cs
index 3e8897f129..a3b0be5575 100644
--- a/Content.Server/Magic/Events/KnockSpellEvent.cs
+++ b/Content.Shared/Magic/Events/KnockSpellEvent.cs
@@ -1,7 +1,7 @@
using Content.Shared.Actions;
using Robust.Shared.Audio;
-namespace Content.Server.Magic.Events;
+namespace Content.Shared.Magic.Events;
public sealed partial class KnockSpellEvent : InstantActionEvent, ISpeakSpell
{
diff --git a/Content.Server/Magic/Events/ProjectileSpellEvent.cs b/Content.Shared/Magic/Events/ProjectileSpellEvent.cs
similarity index 88%
rename from Content.Server/Magic/Events/ProjectileSpellEvent.cs
rename to Content.Shared/Magic/Events/ProjectileSpellEvent.cs
index 95a27afe5d..4496625769 100644
--- a/Content.Server/Magic/Events/ProjectileSpellEvent.cs
+++ b/Content.Shared/Magic/Events/ProjectileSpellEvent.cs
@@ -1,9 +1,8 @@
-using Content.Shared.Actions;
-using Robust.Shared.Audio;
+using Content.Shared.Actions;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-namespace Content.Server.Magic.Events;
+namespace Content.Shared.Magic.Events;
public sealed partial class ProjectileSpellEvent : WorldTargetActionEvent, ISpeakSpell
{
diff --git a/Content.Server/Magic/Events/SmiteSpellEvent.cs b/Content.Shared/Magic/Events/SmiteSpellEvent.cs
similarity index 91%
rename from Content.Server/Magic/Events/SmiteSpellEvent.cs
rename to Content.Shared/Magic/Events/SmiteSpellEvent.cs
index 0311546c4b..08ec63c05e 100644
--- a/Content.Server/Magic/Events/SmiteSpellEvent.cs
+++ b/Content.Shared/Magic/Events/SmiteSpellEvent.cs
@@ -1,6 +1,6 @@
using Content.Shared.Actions;
-namespace Content.Server.Magic.Events;
+namespace Content.Shared.Magic.Events;
public sealed partial class SmiteSpellEvent : EntityTargetActionEvent, ISpeakSpell
{
diff --git a/Content.Server/Magic/Events/TeleportSpellEvent.cs b/Content.Shared/Magic/Events/TeleportSpellEvent.cs
similarity index 92%
rename from Content.Server/Magic/Events/TeleportSpellEvent.cs
rename to Content.Shared/Magic/Events/TeleportSpellEvent.cs
index 50273e742d..b24f6ec72f 100644
--- a/Content.Server/Magic/Events/TeleportSpellEvent.cs
+++ b/Content.Shared/Magic/Events/TeleportSpellEvent.cs
@@ -1,7 +1,7 @@
using Content.Shared.Actions;
using Robust.Shared.Audio;
-namespace Content.Server.Magic.Events;
+namespace Content.Shared.Magic.Events;
public sealed partial class TeleportSpellEvent : WorldTargetActionEvent, ISpeakSpell
{
diff --git a/Content.Server/Magic/Events/WorldSpawnSpellEvent.cs b/Content.Shared/Magic/Events/WorldSpawnSpellEvent.cs
similarity index 95%
rename from Content.Server/Magic/Events/WorldSpawnSpellEvent.cs
rename to Content.Shared/Magic/Events/WorldSpawnSpellEvent.cs
index 4130eeaa77..4355cab842 100644
--- a/Content.Server/Magic/Events/WorldSpawnSpellEvent.cs
+++ b/Content.Shared/Magic/Events/WorldSpawnSpellEvent.cs
@@ -2,7 +2,7 @@
using Content.Shared.Actions;
using Content.Shared.Storage;
-namespace Content.Server.Magic.Events;
+namespace Content.Shared.Magic.Events;
public sealed partial class WorldSpawnSpellEvent : WorldTargetActionEvent, ISpeakSpell
{
@@ -30,4 +30,3 @@ public sealed partial class WorldSpawnSpellEvent : WorldTargetActionEvent, ISpea
[DataField("speech")]
public string? Speech { get; private set; }
}
-
diff --git a/Content.Server/Magic/Events/ISpeakSpell.cs b/Content.Shared/Magic/ISpeakSpell.cs
similarity index 82%
rename from Content.Server/Magic/Events/ISpeakSpell.cs
rename to Content.Shared/Magic/ISpeakSpell.cs
index d7c7dbe250..954b99417f 100644
--- a/Content.Server/Magic/Events/ISpeakSpell.cs
+++ b/Content.Shared/Magic/ISpeakSpell.cs
@@ -1,4 +1,4 @@
-namespace Content.Server.Magic.Events;
+namespace Content.Shared.Magic;
public interface ISpeakSpell // The speak n spell interface
{
@@ -7,4 +7,3 @@ public interface ISpeakSpell // The speak n spell interface
///
public string? Speech { get; }
}
-
diff --git a/Content.Shared/Magic/MagicSpawnData.cs b/Content.Shared/Magic/MagicSpawnData.cs
new file mode 100644
index 0000000000..cd96d4ad76
--- /dev/null
+++ b/Content.Shared/Magic/MagicSpawnData.cs
@@ -0,0 +1,20 @@
+namespace Content.Shared.Magic;
+
+[ImplicitDataDefinitionForInheritors]
+public abstract partial class MagicSpawnData
+{
+
+}
+
+///
+/// Spawns 1 at the caster's feet.
+///
+public sealed partial class TargetCasterPos : MagicSpawnData {}
+
+///
+/// Targets the 3 tiles in front of the caster.
+///
+public sealed partial class TargetInFront : MagicSpawnData
+{
+ [DataField("width")] public int Width = 3;
+}
diff --git a/Content.Shared/Mapping/StartPlacementActionEvent.cs b/Content.Shared/Mapping/StartPlacementActionEvent.cs
new file mode 100644
index 0000000000..9e5e267898
--- /dev/null
+++ b/Content.Shared/Mapping/StartPlacementActionEvent.cs
@@ -0,0 +1,18 @@
+using Content.Shared.Actions;
+
+namespace Content.Shared.Mapping;
+
+public sealed partial class StartPlacementActionEvent : InstantActionEvent
+{
+ [DataField("entityType")]
+ public string? EntityType;
+
+ [DataField("tileId")]
+ public string? TileId;
+
+ [DataField("placementOption")]
+ public string? PlacementOption;
+
+ [DataField("eraser")]
+ public bool Eraser;
+}
diff --git a/Content.Shared/Mech/Components/MechComponent.cs b/Content.Shared/Mech/Components/MechComponent.cs
index aa437786c0..54cb1ea06e 100644
--- a/Content.Shared/Mech/Components/MechComponent.cs
+++ b/Content.Shared/Mech/Components/MechComponent.cs
@@ -1,4 +1,3 @@
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.FixedPoint;
using Content.Shared.Whitelist;
using Robust.Shared.Containers;
@@ -142,12 +141,12 @@ public sealed partial class MechComponent : Component
public List StartingEquipment = new();
#region Action Prototypes
- [DataField("mechCycleAction", customTypeSerializer: typeof(PrototypeIdSerializer))]
- public string MechCycleAction = "MechCycleEquipment";
- [DataField("mechUiAction", customTypeSerializer: typeof(PrototypeIdSerializer))]
- public string MechUiAction = "MechOpenUI";
- [DataField("mechEjectAction", customTypeSerializer: typeof(PrototypeIdSerializer))]
- public string MechEjectAction = "MechEject";
+ [DataField("mechCycleAction", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string MechCycleAction = "ActionMechCycleEquipment";
+ [DataField("mechUiAction", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string MechUiAction = "ActionMechOpenUI";
+ [DataField("mechEjectAction", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string MechEjectAction = "ActionMechEject";
#endregion
#region Visualizer States
diff --git a/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs b/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs
index 4c5326c523..76c3be24bb 100644
--- a/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs
+++ b/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs
@@ -3,11 +3,9 @@ using Content.Shared.Access.Components;
using Content.Shared.Access.Systems;
using Content.Shared.ActionBlocker;
using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Destructible;
using Content.Shared.DoAfter;
using Content.Shared.FixedPoint;
-using Content.Shared.Hands.Components;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Components;
using Content.Shared.Interaction.Events;
@@ -176,14 +174,12 @@ public abstract class SharedMechSystem : EntitySystem
_mover.SetRelay(pilot, mech);
_interaction.SetRelay(pilot, mech, irelay);
rider.Mech = mech;
- Dirty(rider);
+ Dirty(pilot, rider);
- _actions.AddAction(pilot,
- new InstantAction(_prototype.Index(component.MechCycleAction)), mech);
- _actions.AddAction(pilot, new InstantAction(_prototype.Index(component.MechUiAction)),
+ _actions.AddAction(pilot, Spawn(component.MechCycleAction), mech);
+ _actions.AddAction(pilot, Spawn(component.MechUiAction),
mech);
- _actions.AddAction(pilot,
- new InstantAction(_prototype.Index(component.MechEjectAction)), mech);
+ _actions.AddAction(pilot, Spawn(component.MechEjectAction), mech);
}
private void RemoveUser(EntityUid mech, EntityUid pilot)
diff --git a/Content.Shared/Medical/Stethoscope/StethoscopeActionEvent.cs b/Content.Shared/Medical/Stethoscope/StethoscopeActionEvent.cs
new file mode 100644
index 0000000000..11ac8a2684
--- /dev/null
+++ b/Content.Shared/Medical/Stethoscope/StethoscopeActionEvent.cs
@@ -0,0 +1,7 @@
+using Content.Shared.Actions;
+
+namespace Content.Shared.Medical.Stethoscope;
+
+public sealed partial class StethoscopeActionEvent : EntityTargetActionEvent
+{
+}
diff --git a/Content.Shared/Mobs/Components/MobStateActionsComponent.cs b/Content.Shared/Mobs/Components/MobStateActionsComponent.cs
index 8faea0e248..8c72ee618d 100644
--- a/Content.Shared/Mobs/Components/MobStateActionsComponent.cs
+++ b/Content.Shared/Mobs/Components/MobStateActionsComponent.cs
@@ -18,9 +18,9 @@ public sealed partial class MobStateActionsComponent : Component
///
/// actions:
/// Critical:
- /// - CritSuccumb
+ /// - ActionCritSuccumb
/// Alive:
- /// - AnimalLayEgg
+ /// - ActionAnimalLayEgg
///
[DataField("actions")]
public Dictionary> Actions = new();
diff --git a/Content.Shared/Mobs/CritMobActions.cs b/Content.Shared/Mobs/CritMobActions.cs
new file mode 100644
index 0000000000..763915e559
--- /dev/null
+++ b/Content.Shared/Mobs/CritMobActions.cs
@@ -0,0 +1,24 @@
+using Content.Shared.Actions;
+
+namespace Content.Shared.Mobs;
+
+///
+/// Only applies to mobs in crit capable of ghosting/succumbing
+///
+public sealed partial class CritSuccumbEvent : InstantActionEvent
+{
+}
+
+///
+/// Only applies/has functionality to mobs in crit that have
+///
+public sealed partial class CritFakeDeathEvent : InstantActionEvent
+{
+}
+
+///
+/// Only applies to mobs capable of speaking, as a last resort in crit
+///
+public sealed partial class CritLastWordsEvent : InstantActionEvent
+{
+}
diff --git a/Content.Shared/Mobs/Systems/MobStateActionsSystem.cs b/Content.Shared/Mobs/Systems/MobStateActionsSystem.cs
index b25babfe06..acb1483b52 100644
--- a/Content.Shared/Mobs/Systems/MobStateActionsSystem.cs
+++ b/Content.Shared/Mobs/Systems/MobStateActionsSystem.cs
@@ -1,5 +1,4 @@
using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
using Content.Shared.Mobs.Components;
using Robust.Shared.Prototypes;
@@ -31,10 +30,10 @@ public sealed class MobStateActionsSystem : EntitySystem
foreach (var item in acts)
{
- if (!_proto.TryIndex(item, out var proto))
+ if (!_proto.TryIndex(item, out var proto))
continue;
- var instance = new InstantAction(proto);
+ var instance = Spawn(item);
if (state == args.OldMobState)
{
// Don't remove actions that would be getting readded anyway
diff --git a/Content.Shared/Movement/Components/JetpackComponent.cs b/Content.Shared/Movement/Components/JetpackComponent.cs
index 8a3fb8931a..73a8267500 100644
--- a/Content.Shared/Movement/Components/JetpackComponent.cs
+++ b/Content.Shared/Movement/Components/JetpackComponent.cs
@@ -1,5 +1,6 @@
-using Content.Shared.Actions.ActionTypes;
using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared.Movement.Components;
@@ -9,8 +10,10 @@ public sealed partial class JetpackComponent : Component
[ViewVariables(VVAccess.ReadWrite), DataField("moleUsage")]
public float MoleUsage = 0.012f;
- [DataField("toggleAction", required: true)]
- public InstantAction ToggleAction = new();
+ [DataField("toggleAction", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string? ToggleAction = "ActionToggleJetpack";
+
+ [DataField("toggleActionEntity")] public EntityUid? ToggleActionEntity;
[ViewVariables(VVAccess.ReadWrite), DataField("acceleration")]
public float Acceleration = 1f;
diff --git a/Content.Shared/Movement/Systems/SharedJetpackSystem.cs b/Content.Shared/Movement/Systems/SharedJetpackSystem.cs
index 77bf97b25a..0047de376c 100644
--- a/Content.Shared/Movement/Systems/SharedJetpackSystem.cs
+++ b/Content.Shared/Movement/Systems/SharedJetpackSystem.cs
@@ -132,7 +132,7 @@ public abstract class SharedJetpackSystem : EntitySystem
private void OnJetpackGetAction(EntityUid uid, JetpackComponent component, GetItemActionsEvent args)
{
- args.Actions.Add(component.ToggleAction);
+ args.AddAction(ref component.ToggleActionEntity, component.ToggleAction);
}
private bool IsEnabled(EntityUid uid)
@@ -182,7 +182,7 @@ public abstract class SharedJetpackSystem : EntitySystem
}
Appearance.SetData(uid, JetpackVisuals.Enabled, enabled);
- Dirty(component);
+ Dirty(uid, component);
}
public bool IsUserFlying(EntityUid uid)
diff --git a/Content.Shared/PAI/PAIComponent.cs b/Content.Shared/PAI/PAIComponent.cs
index ec0569865e..9574007e7f 100644
--- a/Content.Shared/PAI/PAIComponent.cs
+++ b/Content.Shared/PAI/PAIComponent.cs
@@ -1,5 +1,6 @@
-using Content.Shared.Actions.ActionTypes;
using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared.PAI
{
@@ -23,8 +24,12 @@ namespace Content.Shared.PAI
[DataField("lastUSer"), ViewVariables(VVAccess.ReadWrite)]
public EntityUid? LastUser;
- [DataField("midiAction", required: true, serverOnly: true)] // server only, as it uses a server-BUI event !type
- public InstantAction? MidiAction;
+ [DataField("midiActionId", serverOnly: true,
+ customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string? MidiActionId = "ActionPAIPlayMidi";
+
+ [DataField("midiAction", serverOnly: true)] // server only, as it uses a server-BUI event !type
+ public EntityUid? MidiAction;
}
}
diff --git a/Content.Shared/PAI/SharedPAISystem.cs b/Content.Shared/PAI/SharedPAISystem.cs
index a5196cee1b..59db9dd54d 100644
--- a/Content.Shared/PAI/SharedPAISystem.cs
+++ b/Content.Shared/PAI/SharedPAISystem.cs
@@ -1,9 +1,4 @@
-using Content.Shared.ActionBlocker;
using Content.Shared.Actions;
-using Content.Shared.Hands;
-using Content.Shared.Interaction.Events;
-using Content.Shared.Item;
-using Content.Shared.Movement.Events;
namespace Content.Shared.PAI
{
@@ -24,20 +19,18 @@ namespace Content.Shared.PAI
{
base.Initialize();
- SubscribeLocalEvent(OnStartup);
+ SubscribeLocalEvent