Action container rejig (#20260)
Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
This commit is contained in:
@@ -4,7 +4,6 @@ using Content.Shared.Actions;
|
|||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Client.Player;
|
using Robust.Client.Player;
|
||||||
using Robust.Shared.Containers;
|
|
||||||
using Robust.Shared.ContentPack;
|
using Robust.Shared.ContentPack;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Input.Binding;
|
using Robust.Shared.Input.Binding;
|
||||||
@@ -28,8 +27,8 @@ namespace Content.Client.Actions
|
|||||||
[Dependency] private readonly ISerializationManager _serialization = default!;
|
[Dependency] private readonly ISerializationManager _serialization = default!;
|
||||||
[Dependency] private readonly MetaDataSystem _metaData = default!;
|
[Dependency] private readonly MetaDataSystem _metaData = default!;
|
||||||
|
|
||||||
public event Action<EntityUid>? ActionAdded;
|
public event Action<EntityUid>? OnActionAdded;
|
||||||
public event Action<EntityUid>? ActionRemoved;
|
public event Action<EntityUid>? OnActionRemoved;
|
||||||
public event OnActionReplaced? ActionReplaced;
|
public event OnActionReplaced? ActionReplaced;
|
||||||
public event Action? ActionsUpdated;
|
public event Action? ActionsUpdated;
|
||||||
public event Action<ActionsComponent>? LinkActions;
|
public event Action<ActionsComponent>? LinkActions;
|
||||||
@@ -37,11 +36,8 @@ namespace Content.Client.Actions
|
|||||||
public event Action? ClearAssignments;
|
public event Action? ClearAssignments;
|
||||||
public event Action<List<SlotAssignment>>? AssignSlot;
|
public event Action<List<SlotAssignment>>? AssignSlot;
|
||||||
|
|
||||||
/// <summary>
|
private readonly List<EntityUid> _removed = new();
|
||||||
/// Queue of entities with <see cref="ActionsComponent"/> that needs to be updated after
|
private readonly List<(EntityUid, BaseActionComponent?)> _added = new();
|
||||||
/// handling a state.
|
|
||||||
/// </summary>
|
|
||||||
private readonly Queue<EntityUid> _actionHoldersQueue = new();
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -49,15 +45,73 @@ namespace Content.Client.Actions
|
|||||||
SubscribeLocalEvent<ActionsComponent, PlayerAttachedEvent>(OnPlayerAttached);
|
SubscribeLocalEvent<ActionsComponent, PlayerAttachedEvent>(OnPlayerAttached);
|
||||||
SubscribeLocalEvent<ActionsComponent, PlayerDetachedEvent>(OnPlayerDetached);
|
SubscribeLocalEvent<ActionsComponent, PlayerDetachedEvent>(OnPlayerDetached);
|
||||||
SubscribeLocalEvent<ActionsComponent, ComponentHandleState>(HandleComponentState);
|
SubscribeLocalEvent<ActionsComponent, ComponentHandleState>(HandleComponentState);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<InstantActionComponent, ComponentHandleState>(OnInstantHandleState);
|
||||||
|
SubscribeLocalEvent<EntityTargetActionComponent, ComponentHandleState>(OnEntityTargetHandleState);
|
||||||
|
SubscribeLocalEvent<WorldTargetActionComponent, ComponentHandleState>(OnWorldTargetHandleState);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Dirty(EntityUid? actionId)
|
private void OnInstantHandleState(EntityUid uid, InstantActionComponent component, ref ComponentHandleState args)
|
||||||
{
|
{
|
||||||
var action = GetActionData(actionId);
|
if (args.Current is not InstantActionComponentState state)
|
||||||
if (_playerManager.LocalPlayer?.ControlledEntity != action?.AttachedEntity)
|
return;
|
||||||
|
|
||||||
|
BaseHandleState<InstantActionComponent>(uid, component, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEntityTargetHandleState(EntityUid uid, EntityTargetActionComponent component, ref ComponentHandleState args)
|
||||||
|
{
|
||||||
|
if (args.Current is not EntityTargetActionComponentState state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
component.Whitelist = state.Whitelist;
|
||||||
|
component.CanTargetSelf = state.CanTargetSelf;
|
||||||
|
BaseHandleState<EntityTargetActionComponent>(uid, component, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnWorldTargetHandleState(EntityUid uid, WorldTargetActionComponent component, ref ComponentHandleState args)
|
||||||
|
{
|
||||||
|
if (args.Current is not WorldTargetActionComponentState state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
BaseHandleState<WorldTargetActionComponent>(uid, component, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BaseHandleState<T>(EntityUid uid, BaseActionComponent component, BaseActionComponentState state) where T : BaseActionComponent
|
||||||
|
{
|
||||||
|
component.Icon = state.Icon;
|
||||||
|
component.IconOn = state.IconOn;
|
||||||
|
component.IconColor = state.IconColor;
|
||||||
|
component.Keywords = new HashSet<string>(state.Keywords);
|
||||||
|
component.Enabled = state.Enabled;
|
||||||
|
component.Toggled = state.Toggled;
|
||||||
|
component.Cooldown = state.Cooldown;
|
||||||
|
component.UseDelay = state.UseDelay;
|
||||||
|
component.Charges = state.Charges;
|
||||||
|
component.Container = EnsureEntity<T>(state.Container, uid);
|
||||||
|
component.EntityIcon = EnsureEntity<T>(state.EntityIcon, uid);
|
||||||
|
component.CheckCanInteract = state.CheckCanInteract;
|
||||||
|
component.ClientExclusive = state.ClientExclusive;
|
||||||
|
component.Priority = state.Priority;
|
||||||
|
component.AttachedEntity = EnsureEntity<T>(state.AttachedEntity, uid);
|
||||||
|
component.AutoPopulate = state.AutoPopulate;
|
||||||
|
component.Temporary = state.Temporary;
|
||||||
|
component.ItemIconStyle = state.ItemIconStyle;
|
||||||
|
component.Sound = state.Sound;
|
||||||
|
|
||||||
|
if (_playerManager.LocalPlayer?.ControlledEntity == component.AttachedEntity)
|
||||||
|
ActionsUpdated?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateAction(EntityUid? actionId, BaseActionComponent? action = null)
|
||||||
|
{
|
||||||
|
if (!ResolveActionData(actionId, ref action))
|
||||||
|
return;
|
||||||
|
|
||||||
|
base.UpdateAction(actionId, action);
|
||||||
|
if (_playerManager.LocalPlayer?.ControlledEntity != action.AttachedEntity)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
base.Dirty(actionId);
|
|
||||||
ActionsUpdated?.Invoke();
|
ActionsUpdated?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,70 +120,73 @@ namespace Content.Client.Actions
|
|||||||
if (args.Current is not ActionsComponentState state)
|
if (args.Current is not ActionsComponentState state)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
component.Actions.Clear();
|
_added.Clear();
|
||||||
component.Actions.UnionWith(EnsureEntitySet<ActionsComponent>(state.Actions, uid));
|
_removed.Clear();
|
||||||
|
var stateEnts = EnsureEntitySet<ActionsComponent>(state.Actions, uid);
|
||||||
_actionHoldersQueue.Enqueue(uid);
|
foreach (var act in component.Actions)
|
||||||
}
|
|
||||||
|
|
||||||
protected override void AddActionInternal(EntityUid holderId, EntityUid actionId, BaseContainer container, ActionsComponent holder)
|
|
||||||
{
|
|
||||||
// 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 (container.Contains(actionId))
|
|
||||||
{
|
{
|
||||||
ActionReplaced?.Invoke(actionId);
|
if (!stateEnts.Contains(act) && !IsClientSide(act))
|
||||||
|
_removed.Add(act);
|
||||||
}
|
}
|
||||||
else
|
component.Actions.ExceptWith(_removed);
|
||||||
{
|
|
||||||
base.AddActionInternal(holderId, actionId, container, holder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void AddAction(EntityUid holderId, EntityUid actionId, EntityUid? provider, ActionsComponent? holder = null, BaseActionComponent? action = null, bool dirty = true, BaseContainer? actionContainer = null)
|
foreach (var actionId in stateEnts)
|
||||||
{
|
|
||||||
if (!Resolve(holderId, ref holder, false))
|
|
||||||
return;
|
|
||||||
|
|
||||||
action ??= GetActionData(actionId);
|
|
||||||
if (action == null)
|
|
||||||
{
|
{
|
||||||
Log.Warning($"No {nameof(BaseActionComponent)} found on entity {actionId}");
|
if (!actionId.IsValid())
|
||||||
return;
|
continue;
|
||||||
|
|
||||||
|
if (!component.Actions.Add(actionId))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
TryGetActionData(actionId, out var action);
|
||||||
|
_added.Add((actionId, action));
|
||||||
}
|
}
|
||||||
|
|
||||||
dirty &= !action.ClientExclusive;
|
if (_playerManager.LocalPlayer?.ControlledEntity != uid)
|
||||||
base.AddAction(holderId, actionId, provider, holder, action, dirty, actionContainer);
|
return;
|
||||||
|
|
||||||
if (holderId == _playerManager.LocalPlayer?.ControlledEntity)
|
foreach (var action in _removed)
|
||||||
ActionAdded?.Invoke(actionId);
|
{
|
||||||
|
OnActionRemoved?.Invoke(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
_added.Sort(ActionComparer);
|
||||||
|
|
||||||
|
foreach (var action in _added)
|
||||||
|
{
|
||||||
|
OnActionAdded?.Invoke(action.Item1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionsUpdated?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void RemoveAction(EntityUid holderId, EntityUid? actionId, ActionsComponent? comp = null, BaseActionComponent? action = null, bool dirty = true)
|
public static int ActionComparer((EntityUid, BaseActionComponent?) a, (EntityUid, BaseActionComponent?) b)
|
||||||
{
|
{
|
||||||
if (GameTiming.ApplyingState)
|
var priorityA = a.Item2?.Priority ?? 0;
|
||||||
|
var priorityB = b.Item2?.Priority ?? 0;
|
||||||
|
if (priorityA != priorityB)
|
||||||
|
return priorityA - priorityB;
|
||||||
|
|
||||||
|
priorityA = a.Item2?.Container?.Id ?? 0;
|
||||||
|
priorityB = b.Item2?.Container?.Id ?? 0;
|
||||||
|
return priorityA - priorityB;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ActionAdded(EntityUid performer, EntityUid actionId, ActionsComponent comp,
|
||||||
|
BaseActionComponent action)
|
||||||
|
{
|
||||||
|
if (_playerManager.LocalPlayer?.ControlledEntity != performer)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!Resolve(holderId, ref comp, false))
|
OnActionAdded?.Invoke(actionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ActionRemoved(EntityUid performer, EntityUid actionId, ActionsComponent comp, BaseActionComponent action)
|
||||||
|
{
|
||||||
|
if (_playerManager.LocalPlayer?.ControlledEntity != performer)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (actionId == null)
|
OnActionRemoved?.Invoke(actionId);
|
||||||
return;
|
|
||||||
|
|
||||||
action ??= GetActionData(actionId);
|
|
||||||
|
|
||||||
if (action is { ClientExclusive: false })
|
|
||||||
return;
|
|
||||||
|
|
||||||
dirty &= !action?.ClientExclusive ?? true;
|
|
||||||
base.RemoveAction(holderId, actionId, comp, action, dirty);
|
|
||||||
|
|
||||||
if (_playerManager.LocalPlayer?.ControlledEntity != holderId)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (action == null || action.AutoRemove)
|
|
||||||
ActionRemoved?.Invoke(actionId.Value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<(EntityUid Id, BaseActionComponent Comp)> GetClientActions()
|
public IEnumerable<(EntityUid Id, BaseActionComponent Comp)> GetClientActions()
|
||||||
@@ -180,9 +237,6 @@ namespace Content.Client.Actions
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.Provider != null && Deleted(action.Provider))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (action is not InstantActionComponent instantAction)
|
if (action is not InstantActionComponent instantAction)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -233,8 +287,8 @@ namespace Content.Client.Actions
|
|||||||
|
|
||||||
var action = _serialization.Read<BaseActionComponent>(actionNode, notNullableOverride: true);
|
var action = _serialization.Read<BaseActionComponent>(actionNode, notNullableOverride: true);
|
||||||
var actionId = Spawn(null);
|
var actionId = Spawn(null);
|
||||||
AddComp<Component>(actionId, action);
|
AddComp(actionId, action);
|
||||||
AddAction(user, actionId, null);
|
AddActionDirect(user, actionId);
|
||||||
|
|
||||||
if (map.TryGet<ValueDataNode>("name", out var nameNode))
|
if (map.TryGet<ValueDataNode>("name", out var nameNode))
|
||||||
_metaData.SetEntityName(actionId, nameNode.Value);
|
_metaData.SetEntityName(actionId, nameNode.Value);
|
||||||
@@ -254,95 +308,6 @@ namespace Content.Client.Actions
|
|||||||
AssignSlot?.Invoke(assignments);
|
AssignSlot?.Invoke(assignments);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
|
||||||
{
|
|
||||||
base.Update(frameTime);
|
|
||||||
|
|
||||||
if (_actionHoldersQueue.Count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var removed = new List<EntityUid>();
|
|
||||||
var added = new List<(EntityUid Id, BaseActionComponent Comp)>();
|
|
||||||
var query = GetEntityQuery<ActionsComponent>();
|
|
||||||
var queue = new Queue<EntityUid>(_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 (!holder.Actions.Contains(act))
|
|
||||||
{
|
|
||||||
holder.OldClientActions.Remove(act);
|
|
||||||
if (data.AutoRemove)
|
|
||||||
removed.Add(act);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Anything that remains is a new action
|
|
||||||
foreach (var newAct in holder.Actions)
|
|
||||||
{
|
|
||||||
if (!TryGetActionData(newAct, out var serverData))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!holder.OldClientActions.ContainsKey(newAct))
|
|
||||||
added.Add((newAct, serverData));
|
|
||||||
|
|
||||||
holder.OldClientActions[newAct] = new ActionMetaData(serverData.ClientExclusive, serverData.AutoRemove);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_playerManager.LocalPlayer?.ControlledEntity != holderId)
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var action in removed)
|
|
||||||
{
|
|
||||||
ActionRemoved?.Invoke(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
added.Sort(static (a, b) =>
|
|
||||||
{
|
|
||||||
if (a.Comp.Priority != b.Comp.Priority)
|
|
||||||
return a.Comp.Priority - b.Comp.Priority;
|
|
||||||
|
|
||||||
if (a.Comp.Provider != b.Comp.Provider)
|
|
||||||
{
|
|
||||||
if (a.Comp.Provider == null)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (b.Comp.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) a.Comp.Provider - (int) b.Comp.Provider;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
foreach (var action in added)
|
|
||||||
{
|
|
||||||
ActionAdded?.Invoke(action.Item1);
|
|
||||||
}
|
|
||||||
|
|
||||||
ActionsUpdated?.Invoke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public record struct SlotAssignment(byte Hotbar, byte Slot, EntityUid ActionId);
|
public record struct SlotAssignment(byte Hotbar, byte Slot, EntityUid ActionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ namespace Content.Client.Ghost
|
|||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<GhostComponent, ComponentInit>(OnGhostInit);
|
SubscribeLocalEvent<GhostComponent, ComponentStartup>(OnStartup);
|
||||||
SubscribeLocalEvent<GhostComponent, ComponentRemove>(OnGhostRemove);
|
SubscribeLocalEvent<GhostComponent, ComponentRemove>(OnGhostRemove);
|
||||||
SubscribeLocalEvent<GhostComponent, AfterAutoHandleStateEvent>(OnGhostState);
|
SubscribeLocalEvent<GhostComponent, AfterAutoHandleStateEvent>(OnGhostState);
|
||||||
|
|
||||||
@@ -72,16 +72,10 @@ namespace Content.Client.Ghost
|
|||||||
SubscribeLocalEvent<GhostComponent, ToggleGhostsActionEvent>(OnToggleGhosts);
|
SubscribeLocalEvent<GhostComponent, ToggleGhostsActionEvent>(OnToggleGhosts);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnGhostInit(EntityUid uid, GhostComponent component, ComponentInit args)
|
private void OnStartup(EntityUid uid, GhostComponent component, ComponentStartup args)
|
||||||
{
|
{
|
||||||
if (TryComp(uid, out SpriteComponent? sprite))
|
if (TryComp(uid, out SpriteComponent? sprite))
|
||||||
{
|
|
||||||
sprite.Visible = GhostVisibility;
|
sprite.Visible = GhostVisibility;
|
||||||
}
|
|
||||||
|
|
||||||
_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)
|
private void OnToggleLighting(EntityUid uid, GhostComponent component, ToggleLightingActionEvent args)
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ public sealed class NetworkConfiguratorLinkOverlay : Overlay
|
|||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
private readonly DeviceListSystem _deviceListSystem;
|
private readonly DeviceListSystem _deviceListSystem;
|
||||||
|
|
||||||
private Dictionary<EntityUid, Color> _colors = new();
|
public Dictionary<EntityUid, Color> Colors = new();
|
||||||
|
public EntityUid? Action;
|
||||||
|
|
||||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||||
|
|
||||||
@@ -25,11 +26,6 @@ public sealed class NetworkConfiguratorLinkOverlay : Overlay
|
|||||||
_deviceListSystem = _entityManager.System<DeviceListSystem>();
|
_deviceListSystem = _entityManager.System<DeviceListSystem>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearEntity(EntityUid uid)
|
|
||||||
{
|
|
||||||
_colors.Remove(uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Draw(in OverlayDrawArgs args)
|
protected override void Draw(in OverlayDrawArgs args)
|
||||||
{
|
{
|
||||||
foreach (var tracker in _entityManager.EntityQuery<NetworkConfiguratorActiveLinkOverlayComponent>())
|
foreach (var tracker in _entityManager.EntityQuery<NetworkConfiguratorActiveLinkOverlayComponent>())
|
||||||
@@ -40,13 +36,13 @@ public sealed class NetworkConfiguratorLinkOverlay : Overlay
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_colors.TryGetValue(tracker.Owner, out var color))
|
if (!Colors.TryGetValue(tracker.Owner, out var color))
|
||||||
{
|
{
|
||||||
color = new Color(
|
color = new Color(
|
||||||
_random.Next(0, 255),
|
_random.Next(0, 255),
|
||||||
_random.Next(0, 255),
|
_random.Next(0, 255),
|
||||||
_random.Next(0, 255));
|
_random.Next(0, 255));
|
||||||
_colors.Add(tracker.Owner, color);
|
Colors.Add(tracker.Owner, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
var sourceTransform = _entityManager.GetComponent<TransformComponent>(tracker.Owner);
|
var sourceTransform = _entityManager.GetComponent<TransformComponent>(tracker.Owner);
|
||||||
@@ -70,7 +66,7 @@ public sealed class NetworkConfiguratorLinkOverlay : Overlay
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
args.WorldHandle.DrawLine(sourceTransform.WorldPosition, linkTransform.WorldPosition, _colors[tracker.Owner]);
|
args.WorldHandle.DrawLine(sourceTransform.WorldPosition, linkTransform.WorldPosition, Colors[tracker.Owner]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Content.Client.Actions;
|
|||||||
using Content.Client.Items;
|
using Content.Client.Items;
|
||||||
using Content.Client.Message;
|
using Content.Client.Message;
|
||||||
using Content.Client.Stylesheets;
|
using Content.Client.Stylesheets;
|
||||||
|
using Content.Shared.Actions;
|
||||||
using Content.Shared.DeviceNetwork.Components;
|
using Content.Shared.DeviceNetwork.Components;
|
||||||
using Content.Shared.DeviceNetwork.Systems;
|
using Content.Shared.DeviceNetwork.Systems;
|
||||||
using Content.Shared.Input;
|
using Content.Shared.Input;
|
||||||
@@ -61,26 +62,26 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
|
|||||||
|
|
||||||
if (!toggle)
|
if (!toggle)
|
||||||
{
|
{
|
||||||
if (_overlay.HasOverlay<NetworkConfiguratorLinkOverlay>())
|
|
||||||
{
|
|
||||||
_overlay.GetOverlay<NetworkConfiguratorLinkOverlay>().ClearEntity(component.ActiveDeviceList.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
RemComp<NetworkConfiguratorActiveLinkOverlayComponent>(component.ActiveDeviceList.Value);
|
RemComp<NetworkConfiguratorActiveLinkOverlayComponent>(component.ActiveDeviceList.Value);
|
||||||
if (!EntityQuery<NetworkConfiguratorActiveLinkOverlayComponent>().Any())
|
if (!_overlay.TryGetOverlay(out NetworkConfiguratorLinkOverlay? overlay))
|
||||||
{
|
return;
|
||||||
_overlay.RemoveOverlay<NetworkConfiguratorLinkOverlay>();
|
|
||||||
_actions.RemoveAction(_playerManager.LocalPlayer.ControlledEntity.Value, Action);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
overlay.Colors.Remove(component.ActiveDeviceList.Value);
|
||||||
|
if (overlay.Colors.Count > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_actions.RemoveAction(overlay.Action);
|
||||||
|
_overlay.RemoveOverlay<NetworkConfiguratorLinkOverlay>();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_overlay.HasOverlay<NetworkConfiguratorLinkOverlay>())
|
if (!_overlay.HasOverlay<NetworkConfiguratorLinkOverlay>())
|
||||||
{
|
{
|
||||||
_overlay.AddOverlay(new NetworkConfiguratorLinkOverlay());
|
var overlay = new NetworkConfiguratorLinkOverlay();
|
||||||
_actions.AddAction(_playerManager.LocalPlayer.ControlledEntity.Value, Spawn(Action), null);
|
_overlay.AddOverlay(overlay);
|
||||||
|
var player = _playerManager.LocalPlayer.ControlledEntity.Value;
|
||||||
|
overlay.Action = Spawn(Action);
|
||||||
|
_actions.AddActionDirect(player, overlay.Action.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
EnsureComp<NetworkConfiguratorActiveLinkOverlayComponent>(component.ActiveDeviceList.Value);
|
EnsureComp<NetworkConfiguratorActiveLinkOverlayComponent>(component.ActiveDeviceList.Value);
|
||||||
@@ -88,7 +89,7 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
|
|||||||
|
|
||||||
public void ClearAllOverlays()
|
public void ClearAllOverlays()
|
||||||
{
|
{
|
||||||
if (!_overlay.HasOverlay<NetworkConfiguratorLinkOverlay>())
|
if (!_overlay.TryGetOverlay(out NetworkConfiguratorLinkOverlay? overlay))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -98,12 +99,8 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
|
|||||||
RemCompDeferred<NetworkConfiguratorActiveLinkOverlayComponent>(tracker.Owner);
|
RemCompDeferred<NetworkConfiguratorActiveLinkOverlayComponent>(tracker.Owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
_overlay.RemoveOverlay<NetworkConfiguratorLinkOverlay>();
|
_actions.RemoveAction(overlay.Action);
|
||||||
|
_overlay.RemoveOverlay(overlay);
|
||||||
if (_playerManager.LocalPlayer?.ControlledEntity != null)
|
|
||||||
{
|
|
||||||
_actions.RemoveAction(_playerManager.LocalPlayer.ControlledEntity.Value, Action);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// hacky solution related to mapping
|
// hacky solution related to mapping
|
||||||
|
|||||||
@@ -111,8 +111,8 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
|||||||
{
|
{
|
||||||
if (_actionsSystem != null)
|
if (_actionsSystem != null)
|
||||||
{
|
{
|
||||||
_actionsSystem.ActionAdded += OnActionAdded;
|
_actionsSystem.OnActionAdded += OnActionAdded;
|
||||||
_actionsSystem.ActionRemoved += OnActionRemoved;
|
_actionsSystem.OnActionRemoved += OnActionRemoved;
|
||||||
_actionsSystem.ActionReplaced += OnActionReplaced;
|
_actionsSystem.ActionReplaced += OnActionReplaced;
|
||||||
_actionsSystem.ActionsUpdated += OnActionsUpdated;
|
_actionsSystem.ActionsUpdated += OnActionsUpdated;
|
||||||
}
|
}
|
||||||
@@ -319,8 +319,8 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
|||||||
{
|
{
|
||||||
if (_actionsSystem != null)
|
if (_actionsSystem != null)
|
||||||
{
|
{
|
||||||
_actionsSystem.ActionAdded -= OnActionAdded;
|
_actionsSystem.OnActionAdded -= OnActionAdded;
|
||||||
_actionsSystem.ActionRemoved -= OnActionRemoved;
|
_actionsSystem.OnActionRemoved -= OnActionRemoved;
|
||||||
_actionsSystem.ActionReplaced -= OnActionReplaced;
|
_actionsSystem.ActionReplaced -= OnActionReplaced;
|
||||||
_actionsSystem.ActionsUpdated -= OnActionsUpdated;
|
_actionsSystem.ActionsUpdated -= OnActionsUpdated;
|
||||||
}
|
}
|
||||||
@@ -522,8 +522,8 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
|||||||
return filter switch
|
return filter switch
|
||||||
{
|
{
|
||||||
Filters.Enabled => action.Enabled,
|
Filters.Enabled => action.Enabled,
|
||||||
Filters.Item => action.Provider != null && action.Provider != _playerManager.LocalPlayer?.ControlledEntity,
|
Filters.Item => action.Container != null && action.Container != _playerManager.LocalPlayer?.ControlledEntity,
|
||||||
Filters.Innate => action.Provider == null || action.Provider == _playerManager.LocalPlayer?.ControlledEntity,
|
Filters.Innate => action.Container == null || action.Container == _playerManager.LocalPlayer?.ControlledEntity,
|
||||||
Filters.Instant => action is InstantActionComponent,
|
Filters.Instant => action is InstantActionComponent,
|
||||||
Filters.Targeted => action is BaseTargetActionComponent,
|
Filters.Targeted => action is BaseTargetActionComponent,
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(filter), filter, null)
|
_ => throw new ArgumentOutOfRangeException(nameof(filter), filter, null)
|
||||||
@@ -561,6 +561,9 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
|||||||
if (_window is not { Disposed: false } || _actionsSystem == null)
|
if (_window is not { Disposed: false } || _actionsSystem == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (_playerManager.LocalPlayer?.ControlledEntity is not { } player)
|
||||||
|
return;
|
||||||
|
|
||||||
var search = _window.SearchBar.Text;
|
var search = _window.SearchBar.Text;
|
||||||
var filters = _window.FilterButton.SelectedKeys;
|
var filters = _window.FilterButton.SelectedKeys;
|
||||||
var actions = _actionsSystem.GetClientActions();
|
var actions = _actionsSystem.GetClientActions();
|
||||||
@@ -583,10 +586,10 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
|||||||
if (name.Contains(search, StringComparison.OrdinalIgnoreCase))
|
if (name.Contains(search, StringComparison.OrdinalIgnoreCase))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (action.Comp.Provider == null || action.Comp.Provider == _playerManager.LocalPlayer?.ControlledEntity)
|
if (action.Comp.Container == null || action.Comp.Container == player)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var providerName = EntityManager.GetComponent<MetaDataComponent>(action.Comp.Provider.Value).EntityName;
|
var providerName = EntityManager.GetComponent<MetaDataComponent>(action.Comp.Container.Value).EntityName;
|
||||||
return providerName.Contains(search, StringComparison.OrdinalIgnoreCase);
|
return providerName.Contains(search, StringComparison.OrdinalIgnoreCase);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -744,11 +747,9 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
|||||||
{
|
{
|
||||||
if (_actionsSystem != null && _actionsSystem.TryGetActionData(_menuDragHelper.Dragged?.ActionId, out var action))
|
if (_actionsSystem != null && _actionsSystem.TryGetActionData(_menuDragHelper.Dragged?.ActionId, out var action))
|
||||||
{
|
{
|
||||||
var entIcon = action.EntityIcon;
|
if (action.EntityIcon is {} entIcon)
|
||||||
|
|
||||||
if (entIcon != null)
|
|
||||||
{
|
{
|
||||||
_dragShadow.Texture = EntityManager.GetComponent<SpriteComponent>(entIcon.Value).Icon?
|
_dragShadow.Texture = EntityManager.GetComponent<SpriteComponent>(entIcon).Icon?
|
||||||
.GetFrame(RsiDirection.South, 0);
|
.GetFrame(RsiDirection.South, 0);
|
||||||
}
|
}
|
||||||
else if (action.Icon != null)
|
else if (action.Icon != null)
|
||||||
@@ -902,6 +903,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var actions = _actionsSystem.GetClientActions().Where(action => action.Comp.AutoPopulate).ToList();
|
var actions = _actionsSystem.GetClientActions().Where(action => action.Comp.AutoPopulate).ToList();
|
||||||
|
actions.Sort(ActionComparer);
|
||||||
|
|
||||||
var offset = 0;
|
var offset = 0;
|
||||||
var totalPages = _pages.Count;
|
var totalPages = _pages.Count;
|
||||||
@@ -965,11 +967,11 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
|||||||
action.Toggled = true;
|
action.Toggled = true;
|
||||||
|
|
||||||
// override "held-item" overlay
|
// override "held-item" overlay
|
||||||
var provider = action.Provider;
|
var provider = action.Container;
|
||||||
|
|
||||||
if (action.TargetingIndicator && _overlays.TryGetOverlay<ShowHandItemOverlay>(out var handOverlay))
|
if (action.TargetingIndicator && _overlays.TryGetOverlay<ShowHandItemOverlay>(out var handOverlay))
|
||||||
{
|
{
|
||||||
if (action.ItemIconStyle == ItemActionIconStyle.BigItem && action.Provider != null)
|
if (action.ItemIconStyle == ItemActionIconStyle.BigItem && action.Container != null)
|
||||||
{
|
{
|
||||||
handOverlay.EntityOverride = provider;
|
handOverlay.EntityOverride = provider;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,14 +19,6 @@ public class ActionButtonContainer : GridContainer
|
|||||||
public ActionButton this[int index]
|
public ActionButton this[int index]
|
||||||
{
|
{
|
||||||
get => (ActionButton) GetChild(index);
|
get => (ActionButton) GetChild(index);
|
||||||
set
|
|
||||||
{
|
|
||||||
AddChild(value);
|
|
||||||
value.SetPositionInParent(index);
|
|
||||||
value.ActionPressed += ActionPressed;
|
|
||||||
value.ActionUnpressed += ActionUnpressed;
|
|
||||||
value.ActionFocusExited += ActionFocusExited;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetActionData(params EntityUid?[] actionTypes)
|
public void SetActionData(params EntityUid?[] actionTypes)
|
||||||
@@ -63,6 +55,16 @@ public class ActionButtonContainer : GridContainer
|
|||||||
button.ActionFocusExited += ActionFocusExited;
|
button.ActionFocusExited += ActionFocusExited;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void ChildRemoved(Control newChild)
|
||||||
|
{
|
||||||
|
if (newChild is not ActionButton button)
|
||||||
|
return;
|
||||||
|
|
||||||
|
button.ActionPressed -= ActionPressed;
|
||||||
|
button.ActionUnpressed -= ActionUnpressed;
|
||||||
|
button.ActionFocusExited -= ActionFocusExited;
|
||||||
|
}
|
||||||
|
|
||||||
public bool TryGetButtonIndex(ActionButton button, out int position)
|
public bool TryGetButtonIndex(ActionButton button, out int position)
|
||||||
{
|
{
|
||||||
if (button.Parent != this)
|
if (button.Parent != this)
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ namespace Content.Server.Actions;
|
|||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public sealed partial class ActionOnInteractComponent : Component
|
public sealed partial class ActionOnInteractComponent : Component
|
||||||
{
|
{
|
||||||
[DataField("actions", customTypeSerializer: typeof(PrototypeIdListSerializer<EntityPrototype>))]
|
[DataField(required:true)]
|
||||||
public List<string>? Actions;
|
public List<EntProtoId>? Actions;
|
||||||
|
|
||||||
[DataField("actionEntities")] public List<EntityUid>? ActionEntities;
|
[DataField] public List<EntityUid>? ActionEntities;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ public sealed class ActionOnInteractSystem : EntitySystem
|
|||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
[Dependency] private readonly IGameTiming _timing = default!;
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
||||||
|
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -20,6 +21,19 @@ public sealed class ActionOnInteractSystem : EntitySystem
|
|||||||
|
|
||||||
SubscribeLocalEvent<ActionOnInteractComponent, ActivateInWorldEvent>(OnActivate);
|
SubscribeLocalEvent<ActionOnInteractComponent, ActivateInWorldEvent>(OnActivate);
|
||||||
SubscribeLocalEvent<ActionOnInteractComponent, AfterInteractEvent>(OnAfterInteract);
|
SubscribeLocalEvent<ActionOnInteractComponent, AfterInteractEvent>(OnAfterInteract);
|
||||||
|
SubscribeLocalEvent<ActionOnInteractComponent, MapInitEvent>(OnMapInit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnMapInit(EntityUid uid, ActionOnInteractComponent component, MapInitEvent args)
|
||||||
|
{
|
||||||
|
if (component.Actions == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var comp = EnsureComp<ActionsContainerComponent>(uid);
|
||||||
|
foreach (var id in component.Actions)
|
||||||
|
{
|
||||||
|
_actionContainer.AddAction(uid, id, comp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnActivate(EntityUid uid, ActionOnInteractComponent component, ActivateInWorldEvent args)
|
private void OnActivate(EntityUid uid, ActionOnInteractComponent component, ActivateInWorldEvent args)
|
||||||
@@ -35,7 +49,6 @@ public sealed class ActionOnInteractSystem : EntitySystem
|
|||||||
if (act.Event != null)
|
if (act.Event != null)
|
||||||
act.Event.Performer = args.User;
|
act.Event.Performer = args.User;
|
||||||
|
|
||||||
act.Provider = uid;
|
|
||||||
_actions.PerformAction(args.User, null, actId, act, act.Event, _timing.CurTime, false);
|
_actions.PerformAction(args.User, null, actId, act, act.Event, _timing.CurTime, false);
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
}
|
}
|
||||||
@@ -65,7 +78,6 @@ public sealed class ActionOnInteractSystem : EntitySystem
|
|||||||
entAct.Event.Target = args.Target.Value;
|
entAct.Event.Target = args.Target.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
entAct.Provider = uid;
|
|
||||||
_actions.PerformAction(args.User, null, entActId, entAct, entAct.Event, _timing.CurTime, false);
|
_actions.PerformAction(args.User, null, entActId, entAct, entAct.Event, _timing.CurTime, false);
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
return;
|
return;
|
||||||
@@ -91,7 +103,6 @@ public sealed class ActionOnInteractSystem : EntitySystem
|
|||||||
act.Event.Target = args.ClickLocation;
|
act.Event.Target = args.ClickLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
act.Provider = uid;
|
|
||||||
_actions.PerformAction(args.User, null, actId, act, act.Event, _timing.CurTime, false);
|
_actions.PerformAction(args.User, null, actId, act, act.Event, _timing.CurTime, false);
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,4 +48,6 @@ public sealed partial class EggLayerComponent : Component
|
|||||||
|
|
||||||
[DataField("accumulatedFrametime")]
|
[DataField("accumulatedFrametime")]
|
||||||
public float AccumulatedFrametime;
|
public float AccumulatedFrametime;
|
||||||
|
|
||||||
|
[DataField] public EntityUid? Action;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public sealed class EggLayerSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<EggLayerComponent, ComponentInit>(OnComponentInit);
|
SubscribeLocalEvent<EggLayerComponent, MapInitEvent>(OnMapInit);
|
||||||
SubscribeLocalEvent<EggLayerComponent, EggLayInstantActionEvent>(OnEggLayAction);
|
SubscribeLocalEvent<EggLayerComponent, EggLayInstantActionEvent>(OnEggLayAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,12 +50,9 @@ public sealed class EggLayerSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnComponentInit(EntityUid uid, EggLayerComponent component, ComponentInit args)
|
private void OnMapInit(EntityUid uid, EggLayerComponent component, MapInitEvent args)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(component.EggLayAction))
|
_actions.AddAction(uid, ref component.Action, component.EggLayAction);
|
||||||
return;
|
|
||||||
|
|
||||||
_actions.AddAction(uid, Spawn(component.EggLayAction), uid);
|
|
||||||
component.CurrentEggLayCooldown = _random.NextFloat(component.EggLayCooldownMin, component.EggLayCooldownMax);
|
component.CurrentEggLayCooldown = _random.NextFloat(component.EggLayCooldownMin, component.EggLayCooldownMax);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,14 +43,11 @@ namespace Content.Server.Bed
|
|||||||
{
|
{
|
||||||
AddComp<HealOnBuckleHealingComponent>(uid);
|
AddComp<HealOnBuckleHealingComponent>(uid);
|
||||||
component.NextHealTime = _timing.CurTime + TimeSpan.FromSeconds(component.HealTime);
|
component.NextHealTime = _timing.CurTime + TimeSpan.FromSeconds(component.HealTime);
|
||||||
component.SleepAction = Spawn(SleepingSystem.SleepActionId);
|
_actionsSystem.AddAction(args.BuckledEntity, ref component.SleepAction, SleepingSystem.SleepActionId, uid);
|
||||||
_actionsSystem.AddAction(args.BuckledEntity, component.SleepAction.Value, null);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (component.SleepAction != null)
|
_actionsSystem.RemoveAction(args.BuckledEntity, component.SleepAction);
|
||||||
_actionsSystem.RemoveAction(args.BuckledEntity, component.SleepAction.Value);
|
|
||||||
|
|
||||||
_sleepingSystem.TryWaking(args.BuckledEntity);
|
_sleepingSystem.TryWaking(args.BuckledEntity);
|
||||||
RemComp<HealOnBuckleHealingComponent>(uid);
|
RemComp<HealOnBuckleHealingComponent>(uid);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,9 +23,7 @@ namespace Content.Server.Bed.Sleep
|
|||||||
public sealed class SleepingSystem : SharedSleepingSystem
|
public sealed class SleepingSystem : SharedSleepingSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
|
||||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||||
[Dependency] private readonly ActionsSystem _actionsSystem = default!;
|
|
||||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
[Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!;
|
[Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!;
|
||||||
@@ -182,9 +180,8 @@ namespace Content.Server.Bed.Sleep
|
|||||||
|
|
||||||
var tryingToSleepEvent = new TryingToSleepEvent(uid);
|
var tryingToSleepEvent = new TryingToSleepEvent(uid);
|
||||||
RaiseLocalEvent(uid, ref tryingToSleepEvent);
|
RaiseLocalEvent(uid, ref tryingToSleepEvent);
|
||||||
if (tryingToSleepEvent.Cancelled) return false;
|
if (tryingToSleepEvent.Cancelled)
|
||||||
|
return false;
|
||||||
_actionsSystem.RemoveAction(uid, SleepActionId);
|
|
||||||
|
|
||||||
EnsureComp<SleepingComponent>(uid);
|
EnsureComp<SleepingComponent>(uid);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ public sealed partial class DragonSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<DragonComponent, ComponentStartup>(OnStartup);
|
SubscribeLocalEvent<DragonComponent, MapInitEvent>(OnInit);
|
||||||
SubscribeLocalEvent<DragonComponent, ComponentShutdown>(OnShutdown);
|
SubscribeLocalEvent<DragonComponent, ComponentShutdown>(OnShutdown);
|
||||||
SubscribeLocalEvent<DragonComponent, DragonSpawnRiftActionEvent>(OnDragonRift);
|
SubscribeLocalEvent<DragonComponent, DragonSpawnRiftActionEvent>(OnDragonRift);
|
||||||
SubscribeLocalEvent<DragonComponent, RefreshMovementSpeedModifiersEvent>(OnDragonMove);
|
SubscribeLocalEvent<DragonComponent, RefreshMovementSpeedModifiersEvent>(OnDragonMove);
|
||||||
@@ -292,7 +292,7 @@ public sealed partial class DragonSystem : EntitySystem
|
|||||||
_audioSystem.Play(component.SoundRoar, Filter.Pvs(component.Owner, 4f, EntityManager), component.Owner, true, component.SoundRoar.Params);
|
_audioSystem.Play(component.SoundRoar, Filter.Pvs(component.Owner, 4f, EntityManager), component.Owner, true, component.SoundRoar.Params);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnStartup(EntityUid uid, DragonComponent component, ComponentStartup args)
|
private void OnInit(EntityUid uid, DragonComponent component, MapInitEvent args)
|
||||||
{
|
{
|
||||||
Roar(component);
|
Roar(component);
|
||||||
_actionsSystem.AddAction(uid, ref component.SpawnRiftActionEntity, component.SpawnRiftAction);
|
_actionsSystem.AddAction(uid, ref component.SpawnRiftActionEntity, component.SpawnRiftAction);
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ namespace Content.Server.GameTicking.Rules;
|
|||||||
|
|
||||||
public sealed class ZombieRuleSystem : GameRuleSystem<ZombieRuleComponent>
|
public sealed class ZombieRuleSystem : GameRuleSystem<ZombieRuleComponent>
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||||
[Dependency] private readonly IChatManager _chatManager = default!;
|
[Dependency] private readonly IChatManager _chatManager = default!;
|
||||||
@@ -55,7 +54,7 @@ public sealed class ZombieRuleSystem : GameRuleSystem<ZombieRuleComponent>
|
|||||||
|
|
||||||
SubscribeLocalEvent<RoundStartAttemptEvent>(OnStartAttempt);
|
SubscribeLocalEvent<RoundStartAttemptEvent>(OnStartAttempt);
|
||||||
SubscribeLocalEvent<RoundEndTextAppendEvent>(OnRoundEndText);
|
SubscribeLocalEvent<RoundEndTextAppendEvent>(OnRoundEndText);
|
||||||
SubscribeLocalEvent<ZombifyOnDeathComponent, ZombifySelfActionEvent>(OnZombifySelf);
|
SubscribeLocalEvent<PendingZombieComponent, ZombifySelfActionEvent>(OnZombifySelf);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnRoundEndText(RoundEndTextAppendEvent ev)
|
private void OnRoundEndText(RoundEndTextAppendEvent ev)
|
||||||
@@ -191,10 +190,11 @@ public sealed class ZombieRuleSystem : GameRuleSystem<ZombieRuleComponent>
|
|||||||
InfectInitialPlayers(component);
|
InfectInitialPlayers(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnZombifySelf(EntityUid uid, ZombifyOnDeathComponent component, ZombifySelfActionEvent args)
|
private void OnZombifySelf(EntityUid uid, PendingZombieComponent component, ZombifySelfActionEvent args)
|
||||||
{
|
{
|
||||||
_zombie.ZombifyEntity(uid);
|
_zombie.ZombifyEntity(uid);
|
||||||
_action.RemoveAction(uid, ZombieRuleComponent.ZombifySelfActionPrototype);
|
if (component.Action != null)
|
||||||
|
Del(component.Action.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private float GetInfectedFraction(bool includeOffStation = true, bool includeDead = false)
|
private float GetInfectedFraction(bool includeOffStation = true, bool includeDead = false)
|
||||||
@@ -322,8 +322,7 @@ public sealed class ZombieRuleSystem : GameRuleSystem<ZombieRuleComponent>
|
|||||||
EnsureComp<ZombifyOnDeathComponent>(ownedEntity);
|
EnsureComp<ZombifyOnDeathComponent>(ownedEntity);
|
||||||
EnsureComp<IncurableZombieComponent>(ownedEntity);
|
EnsureComp<IncurableZombieComponent>(ownedEntity);
|
||||||
var inCharacterName = MetaData(ownedEntity).EntityName;
|
var inCharacterName = MetaData(ownedEntity).EntityName;
|
||||||
var action = Spawn(ZombieRuleComponent.ZombifySelfActionPrototype);
|
_action.AddAction(ownedEntity, ref pending.Action, ZombieRuleComponent.ZombifySelfActionPrototype, ownedEntity);
|
||||||
_action.AddAction(mind.OwnedEntity.Value, action, null);
|
|
||||||
|
|
||||||
var message = Loc.GetString("zombie-patientzero-role-greeting");
|
var message = Loc.GetString("zombie-patientzero-role-greeting");
|
||||||
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message));
|
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message));
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ namespace Content.Server.Ghost
|
|||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<GhostComponent, ComponentStartup>(OnGhostStartup);
|
SubscribeLocalEvent<GhostComponent, ComponentStartup>(OnGhostStartup);
|
||||||
|
SubscribeLocalEvent<GhostComponent, MapInitEvent>(OnMapInit);
|
||||||
SubscribeLocalEvent<GhostComponent, ComponentShutdown>(OnGhostShutdown);
|
SubscribeLocalEvent<GhostComponent, ComponentShutdown>(OnGhostShutdown);
|
||||||
|
|
||||||
SubscribeLocalEvent<GhostComponent, ExaminedEvent>(OnGhostExamine);
|
SubscribeLocalEvent<GhostComponent, ExaminedEvent>(OnGhostExamine);
|
||||||
@@ -121,18 +122,6 @@ namespace Content.Server.Ghost
|
|||||||
|
|
||||||
var time = _gameTiming.CurTime;
|
var time = _gameTiming.CurTime;
|
||||||
component.TimeOfDeath = time;
|
component.TimeOfDeath = time;
|
||||||
|
|
||||||
// TODO ghost: remove once ghosts are persistent and aren't deleted when returning to body
|
|
||||||
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 OnGhostShutdown(EntityUid uid, GhostComponent component, ComponentShutdown args)
|
private void OnGhostShutdown(EntityUid uid, GhostComponent component, ComponentShutdown args)
|
||||||
@@ -151,8 +140,7 @@ namespace Content.Server.Ghost
|
|||||||
|
|
||||||
// Entity can't see ghosts anymore.
|
// Entity can't see ghosts anymore.
|
||||||
SetCanSeeGhosts(uid, false);
|
SetCanSeeGhosts(uid, false);
|
||||||
|
_actions.RemoveAction(uid, component.BooActionEntity);
|
||||||
_actions.RemoveAction(uid, component.ActionEntity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetCanSeeGhosts(EntityUid uid, bool canSee, EyeComponent? eyeComponent = null)
|
private void SetCanSeeGhosts(EntityUid uid, bool canSee, EyeComponent? eyeComponent = null)
|
||||||
@@ -166,6 +154,21 @@ namespace Content.Server.Ghost
|
|||||||
_eye.SetVisibilityMask(uid, eyeComponent.VisibilityMask & ~(int) VisibilityFlags.Ghost, eyeComponent);
|
_eye.SetVisibilityMask(uid, eyeComponent.VisibilityMask & ~(int) VisibilityFlags.Ghost, eyeComponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnMapInit(EntityUid uid, GhostComponent component, MapInitEvent args)
|
||||||
|
{
|
||||||
|
if (_actions.AddAction(uid, ref component.BooActionEntity, out var act, component.BooAction)
|
||||||
|
&& act.UseDelay != null)
|
||||||
|
{
|
||||||
|
var start = _gameTiming.CurTime;
|
||||||
|
var end = start + act.UseDelay.Value;
|
||||||
|
_actions.SetCooldown(component.BooActionEntity.Value, start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
_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)
|
private void OnGhostExamine(EntityUid uid, GhostComponent component, ExaminedEvent args)
|
||||||
{
|
{
|
||||||
var timeSinceDeath = _gameTiming.RealTime.Subtract(component.TimeOfDeath);
|
var timeSinceDeath = _gameTiming.RealTime.Subtract(component.TimeOfDeath);
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ namespace Content.Server.Light.EntitySystems
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||||
[Dependency] private readonly ActionsSystem _actions = default!;
|
[Dependency] private readonly ActionsSystem _actions = default!;
|
||||||
|
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
|
||||||
[Dependency] private readonly PopupSystem _popup = default!;
|
[Dependency] private readonly PopupSystem _popup = default!;
|
||||||
[Dependency] private readonly PowerCellSystem _powerCell = default!;
|
[Dependency] private readonly PowerCellSystem _powerCell = default!;
|
||||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||||
@@ -97,7 +98,7 @@ namespace Content.Server.Light.EntitySystems
|
|||||||
|
|
||||||
private void OnMapInit(EntityUid uid, HandheldLightComponent component, MapInitEvent args)
|
private void OnMapInit(EntityUid uid, HandheldLightComponent component, MapInitEvent args)
|
||||||
{
|
{
|
||||||
_actions.AddAction(uid, ref component.ToggleActionEntity, component.ToggleAction);
|
_actionContainer.EnsureAction(uid, ref component.ToggleActionEntity, component.ToggleAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnShutdown(EntityUid uid, HandheldLightComponent component, ComponentShutdown args)
|
private void OnShutdown(EntityUid uid, HandheldLightComponent component, ComponentShutdown args)
|
||||||
|
|||||||
@@ -46,12 +46,13 @@ public sealed class MagicSystem : EntitySystem
|
|||||||
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
[Dependency] private readonly ChatSystem _chat = default!;
|
[Dependency] private readonly ChatSystem _chat = default!;
|
||||||
|
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<SpellbookComponent, ComponentInit>(OnInit);
|
SubscribeLocalEvent<SpellbookComponent, MapInitEvent>(OnInit);
|
||||||
SubscribeLocalEvent<SpellbookComponent, UseInHandEvent>(OnUse);
|
SubscribeLocalEvent<SpellbookComponent, UseInHandEvent>(OnUse);
|
||||||
SubscribeLocalEvent<SpellbookComponent, SpellbookDoAfterEvent>(OnDoAfter);
|
SubscribeLocalEvent<SpellbookComponent, SpellbookDoAfterEvent>(OnDoAfter);
|
||||||
|
|
||||||
@@ -69,18 +70,36 @@ public sealed class MagicSystem : EntitySystem
|
|||||||
if (args.Handled || args.Cancelled)
|
if (args.Handled || args.Cancelled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_actionsSystem.AddActions(args.Args.User, component.Spells, component.LearnPermanently ? null : uid);
|
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
}
|
if (!component.LearnPermanently)
|
||||||
|
{
|
||||||
|
_actionsSystem.GrantActions(args.Args.User, component.Spells, uid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
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.SpellActions)
|
foreach (var (id, charges) in component.SpellActions)
|
||||||
{
|
{
|
||||||
var spell = Spawn(id);
|
EntityUid? actionId = null;
|
||||||
|
if (_actionsSystem.AddAction(uid, ref actionId, id))
|
||||||
|
_actionsSystem.SetCharges(actionId, charges < 0 ? null : charges);
|
||||||
|
}
|
||||||
|
|
||||||
|
component.SpellActions.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInit(EntityUid uid, SpellbookComponent component, MapInitEvent args)
|
||||||
|
{
|
||||||
|
if (!component.LearnPermanently)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var (id, charges) in component.SpellActions)
|
||||||
|
{
|
||||||
|
var spell = _actionContainer.AddAction(uid, id);
|
||||||
|
if (spell == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
_actionsSystem.SetCharges(spell, charges < 0 ? null : charges);
|
_actionsSystem.SetCharges(spell, charges < 0 ? null : charges);
|
||||||
component.Spells.Add(spell);
|
component.Spells.Add(spell.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,5 +25,7 @@ namespace Content.Server.Polymorph.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("time")]
|
[DataField("time")]
|
||||||
public float Time;
|
public float Time;
|
||||||
|
|
||||||
|
[DataField] public EntityUid? Action;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Linq;
|
||||||
using Content.Server.Actions;
|
using Content.Server.Actions;
|
||||||
using Content.Server.Humanoid;
|
using Content.Server.Humanoid;
|
||||||
using Content.Server.Inventory;
|
using Content.Server.Inventory;
|
||||||
@@ -29,6 +30,7 @@ namespace Content.Server.Polymorph.Systems
|
|||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||||
[Dependency] private readonly ActionsSystem _actions = default!;
|
[Dependency] private readonly ActionsSystem _actions = default!;
|
||||||
|
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
|
||||||
[Dependency] private readonly AudioSystem _audio = default!;
|
[Dependency] private readonly AudioSystem _audio = default!;
|
||||||
[Dependency] private readonly SharedBuckleSystem _buckle = default!;
|
[Dependency] private readonly SharedBuckleSystem _buckle = default!;
|
||||||
[Dependency] private readonly ContainerSystem _container = default!;
|
[Dependency] private readonly ContainerSystem _container = default!;
|
||||||
@@ -53,7 +55,7 @@ namespace Content.Server.Polymorph.Systems
|
|||||||
|
|
||||||
SubscribeLocalEvent<PolymorphableComponent, ComponentStartup>(OnStartup);
|
SubscribeLocalEvent<PolymorphableComponent, ComponentStartup>(OnStartup);
|
||||||
SubscribeLocalEvent<PolymorphableComponent, PolymorphActionEvent>(OnPolymorphActionEvent);
|
SubscribeLocalEvent<PolymorphableComponent, PolymorphActionEvent>(OnPolymorphActionEvent);
|
||||||
SubscribeLocalEvent<PolymorphedEntityComponent, ComponentStartup>(OnStartup);
|
SubscribeLocalEvent<PolymorphedEntityComponent, MapInitEvent>(OnMapInit);
|
||||||
SubscribeLocalEvent<PolymorphedEntityComponent, BeforeFullyEatenEvent>(OnBeforeFullyEaten);
|
SubscribeLocalEvent<PolymorphedEntityComponent, BeforeFullyEatenEvent>(OnBeforeFullyEaten);
|
||||||
SubscribeLocalEvent<PolymorphedEntityComponent, BeforeFullySlicedEvent>(OnBeforeFullySliced);
|
SubscribeLocalEvent<PolymorphedEntityComponent, BeforeFullySlicedEvent>(OnBeforeFullySliced);
|
||||||
SubscribeLocalEvent<PolymorphedEntityComponent, RevertPolymorphActionEvent>(OnRevertPolymorphActionEvent);
|
SubscribeLocalEvent<PolymorphedEntityComponent, RevertPolymorphActionEvent>(OnRevertPolymorphActionEvent);
|
||||||
@@ -85,7 +87,7 @@ namespace Content.Server.Polymorph.Systems
|
|||||||
Revert(uid, component);
|
Revert(uid, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnStartup(EntityUid uid, PolymorphedEntityComponent component, ComponentStartup args)
|
private void OnMapInit(EntityUid uid, PolymorphedEntityComponent component, MapInitEvent args)
|
||||||
{
|
{
|
||||||
if (!_proto.TryIndex(component.Prototype, out PolymorphPrototype? proto))
|
if (!_proto.TryIndex(component.Prototype, out PolymorphPrototype? proto))
|
||||||
{
|
{
|
||||||
@@ -98,14 +100,11 @@ namespace Content.Server.Polymorph.Systems
|
|||||||
if (proto.Forced)
|
if (proto.Forced)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var actionId = Spawn(RevertPolymorphId);
|
if (_actions.AddAction(uid, ref component.Action, out var action, RevertPolymorphId))
|
||||||
if (_actions.TryGetActionData(actionId, out var action))
|
|
||||||
{
|
{
|
||||||
action.EntityIcon = component.Parent;
|
action.EntityIcon = component.Parent;
|
||||||
action.UseDelay = TimeSpan.FromSeconds(proto.Delay);
|
action.UseDelay = TimeSpan.FromSeconds(proto.Delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
_actions.AddAction(uid, actionId, null, null, action);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBeforeFullyEaten(EntityUid uid, PolymorphedEntityComponent comp, BeforeFullyEatenEvent args)
|
private void OnBeforeFullyEaten(EntityUid uid, PolymorphedEntityComponent comp, BeforeFullyEatenEvent args)
|
||||||
@@ -332,20 +331,26 @@ namespace Content.Server.Polymorph.Systems
|
|||||||
if (!TryComp<PolymorphableComponent>(target, out var polycomp))
|
if (!TryComp<PolymorphableComponent>(target, out var polycomp))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var entproto = _proto.Index<EntityPrototype>(polyproto.Entity);
|
polycomp.PolymorphActions ??= new Dictionary<string, EntityUid>();
|
||||||
var actionId = Spawn(RevertPolymorphId);
|
if (polycomp.PolymorphActions.ContainsKey(id))
|
||||||
if (_actions.TryGetActionData(actionId, out var baseAction) &&
|
return;
|
||||||
baseAction is InstantActionComponent action)
|
|
||||||
{
|
|
||||||
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 Dictionary<string, EntityUid>();
|
var entproto = _proto.Index<EntityPrototype>(polyproto.Entity);
|
||||||
polycomp.PolymorphActions.Add(id, actionId);
|
|
||||||
_actions.AddAction(target, actionId, target);
|
EntityUid? actionId = default!;
|
||||||
}
|
if (!_actions.AddAction(target, ref actionId, RevertPolymorphId, target))
|
||||||
|
return;
|
||||||
|
|
||||||
|
polycomp.PolymorphActions.Add(id, actionId.Value);
|
||||||
|
_metaData.SetEntityName(actionId.Value, Loc.GetString("polymorph-self-action-name", ("target", entproto.Name)));
|
||||||
|
_metaData.SetEntityDescription(actionId.Value, Loc.GetString("polymorph-self-action-description", ("target", entproto.Name)));
|
||||||
|
|
||||||
|
if (!_actions.TryGetActionData(actionId, out var baseAction))
|
||||||
|
return;
|
||||||
|
|
||||||
|
baseAction.Icon = new SpriteSpecifier.EntityPrototype(polyproto.Entity);
|
||||||
|
if (baseAction is InstantActionComponent action)
|
||||||
|
action.Event = new PolymorphActionEvent { Prototype = polyproto };
|
||||||
}
|
}
|
||||||
|
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ public sealed partial class RevenantSystem : EntitySystem
|
|||||||
|
|
||||||
private void OnMapInit(EntityUid uid, RevenantComponent component, MapInitEvent args)
|
private void OnMapInit(EntityUid uid, RevenantComponent component, MapInitEvent args)
|
||||||
{
|
{
|
||||||
_action.AddAction(uid, Spawn(RevenantShopId), null);
|
_action.AddAction(uid, ref component.Action, RevenantShopId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnStatusAdded(EntityUid uid, RevenantComponent component, StatusEffectAddedEvent args)
|
private void OnStatusAdded(EntityUid uid, RevenantComponent component, StatusEffectAddedEvent args)
|
||||||
|
|||||||
@@ -59,11 +59,10 @@ public sealed partial class BorgSystem
|
|||||||
{
|
{
|
||||||
var chassis = args.ChassisEnt;
|
var chassis = args.ChassisEnt;
|
||||||
|
|
||||||
var action = _actions.AddAction(chassis, ref component.ModuleSwapActionEntity, component.ModuleSwapActionId, uid);
|
if (_actions.AddAction(chassis, ref component.ModuleSwapActionEntity, out var action, component.ModuleSwapActionId, uid))
|
||||||
if (action != null)
|
|
||||||
{
|
{
|
||||||
action.EntityIcon = uid;
|
action.EntityIcon = uid;
|
||||||
Dirty(component.ModuleSwapActionEntity!.Value, action);
|
Dirty(component.ModuleSwapActionEntity.Value, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TryComp(chassis, out BorgChassisComponent? chassisComp))
|
if (!TryComp(chassis, out BorgChassisComponent? chassisComp))
|
||||||
|
|||||||
@@ -163,7 +163,8 @@ public sealed partial class StoreSystem
|
|||||||
//give action
|
//give action
|
||||||
if (!string.IsNullOrWhiteSpace(listing.ProductAction))
|
if (!string.IsNullOrWhiteSpace(listing.ProductAction))
|
||||||
{
|
{
|
||||||
_actions.AddAction(buyer, Spawn(listing.ProductAction), null);
|
// I guess we just allow duplicate actions?
|
||||||
|
_actions.AddAction(buyer, listing.ProductAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
//broadcast event
|
//broadcast event
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ public sealed class IntrinsicUISystem : EntitySystem
|
|||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
SubscribeLocalEvent<IntrinsicUIComponent, ComponentStartup>(OnGetActions);
|
SubscribeLocalEvent<IntrinsicUIComponent, MapInitEvent>(InitActions);
|
||||||
SubscribeLocalEvent<IntrinsicUIComponent, ToggleIntrinsicUIEvent>(OnActionToggle);
|
SubscribeLocalEvent<IntrinsicUIComponent, ToggleIntrinsicUIEvent>(OnActionToggle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,14 +21,11 @@ public sealed class IntrinsicUISystem : EntitySystem
|
|||||||
args.Handled = InteractUI(uid, args.Key, component);
|
args.Handled = InteractUI(uid, args.Key, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnGetActions(EntityUid uid, IntrinsicUIComponent component, ComponentStartup args)
|
private void InitActions(EntityUid uid, IntrinsicUIComponent component, MapInitEvent args)
|
||||||
{
|
{
|
||||||
if (!TryComp<ActionsComponent>(uid, out var actions))
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var entry in component.UIs)
|
foreach (var entry in component.UIs)
|
||||||
{
|
{
|
||||||
_actionsSystem.AddAction(uid, ref entry.ToggleActionEntity, entry.ToggleAction, null, actions);
|
_actionsSystem.AddAction(uid, ref entry.ToggleActionEntity, entry.ToggleAction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,4 +51,6 @@ public sealed partial class PendingZombieComponent : Component
|
|||||||
"zombie-infection-warning",
|
"zombie-infection-warning",
|
||||||
"zombie-infection-underway"
|
"zombie-infection-underway"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[DataField] public EntityUid? Action;
|
||||||
}
|
}
|
||||||
|
|||||||
17
Content.Shared/Actions/ActionContainerComponent.cs
Normal file
17
Content.Shared/Actions/ActionContainerComponent.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using Robust.Shared.Containers;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Actions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This component indicates that this entity contains actions inside of some container.
|
||||||
|
/// </summary>
|
||||||
|
[NetworkedComponent, RegisterComponent]
|
||||||
|
[Access(typeof(ActionContainerSystem), typeof(SharedActionsSystem))]
|
||||||
|
public sealed partial class ActionsContainerComponent : Component
|
||||||
|
{
|
||||||
|
public const string ContainerId = "actions";
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public Container Container = default!;
|
||||||
|
}
|
||||||
214
Content.Shared/Actions/ActionContainerSystem.cs
Normal file
214
Content.Shared/Actions/ActionContainerSystem.cs
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Robust.Shared.Containers;
|
||||||
|
using Robust.Shared.Network;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Shared.Actions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles storing & spawning action entities in a container.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ActionContainerSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
|
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||||
|
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
||||||
|
[Dependency] private readonly INetManager _netMan = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<ActionsContainerComponent, ComponentInit>(OnInit);
|
||||||
|
SubscribeLocalEvent<ActionsContainerComponent, ComponentShutdown>(OnShutdown);
|
||||||
|
SubscribeLocalEvent<ActionsContainerComponent, EntRemovedFromContainerMessage>(OnEntityRemoved);
|
||||||
|
SubscribeLocalEvent<ActionsContainerComponent, EntInsertedIntoContainerMessage>(OnEntityInserted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Spawns a new action entity and adds it to the given container.
|
||||||
|
/// </summary>
|
||||||
|
public EntityUid? AddAction(EntityUid uid, string actionPrototypeId, ActionsContainerComponent? comp = null)
|
||||||
|
{
|
||||||
|
EntityUid? result = default;
|
||||||
|
EnsureAction(uid, ref result, actionPrototypeId, comp);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensures that a given entityUid refers to a valid entity action contained by the given container.
|
||||||
|
/// If the entity does not exist, it will attempt to spawn a new action.
|
||||||
|
/// Returns false if the given entity exists, but is not in a valid state.
|
||||||
|
/// </summary>
|
||||||
|
public bool EnsureAction(EntityUid uid,
|
||||||
|
[NotNullWhen(true)] ref EntityUid? actionId,
|
||||||
|
string actionPrototypeId,
|
||||||
|
ActionsContainerComponent? comp = null)
|
||||||
|
{
|
||||||
|
return EnsureAction(uid, ref actionId, out _, actionPrototypeId, comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="EnsureAction(Robust.Shared.GameObjects.EntityUid,ref System.Nullable{Robust.Shared.GameObjects.EntityUid},string?,Content.Shared.Actions.ActionsContainerComponent?)"/>
|
||||||
|
public bool EnsureAction(EntityUid uid,
|
||||||
|
[NotNullWhen(true)] ref EntityUid? actionId,
|
||||||
|
[NotNullWhen(true)] out BaseActionComponent? action,
|
||||||
|
string? actionPrototypeId,
|
||||||
|
ActionsContainerComponent? comp = null)
|
||||||
|
{
|
||||||
|
action = null;
|
||||||
|
|
||||||
|
DebugTools.Assert(comp == null || comp.Owner == uid);
|
||||||
|
comp ??= EnsureComp<ActionsContainerComponent>(uid);
|
||||||
|
|
||||||
|
if (Exists(actionId))
|
||||||
|
{
|
||||||
|
if (!comp.Container.Contains(actionId.Value))
|
||||||
|
{
|
||||||
|
Log.Error($"Action {ToPrettyString(actionId.Value)} is not contained in the expected container {ToPrettyString(uid)}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_actions.TryGetActionData(actionId, out action))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DebugTools.Assert(Transform(actionId.Value).ParentUid == uid);
|
||||||
|
DebugTools.Assert(_container.IsEntityInContainer(actionId.Value));
|
||||||
|
DebugTools.Assert(action.Container == uid);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Null prototypes are never valid entities, they mean that someone didn't provide a proper prototype.
|
||||||
|
if (actionPrototypeId == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Client cannot predict entity spawning.
|
||||||
|
if (_netMan.IsClient && !IsClientSide(uid))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
actionId = Spawn(actionPrototypeId);
|
||||||
|
if (AddAction(uid, actionId.Value, action, comp) && _actions.TryGetActionData(actionId, out action))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
Del(actionId.Value);
|
||||||
|
actionId = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a pre-existing action to an action container.
|
||||||
|
/// </summary>
|
||||||
|
public bool AddAction(EntityUid uid, EntityUid actionId, BaseActionComponent? action = null, ActionsContainerComponent? comp = null)
|
||||||
|
{
|
||||||
|
if (!_actions.ResolveActionData(actionId, ref action))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (action.Container != null)
|
||||||
|
{
|
||||||
|
Log.Error($"Attempted to insert an action {ToPrettyString(actionId)} that was already in a container {ToPrettyString(action.Container.Value)}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugTools.Assert(comp == null || comp.Owner == uid);
|
||||||
|
comp ??= EnsureComp<ActionsContainerComponent>(uid);
|
||||||
|
if (!comp.Container.Insert(actionId))
|
||||||
|
{
|
||||||
|
Log.Error($"Failed to insert action {ToPrettyString(actionId)} into {ToPrettyString(uid)}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container insert events should have updated the component's fields:
|
||||||
|
DebugTools.Assert(comp.Container.Contains(actionId));
|
||||||
|
DebugTools.Assert(action.Container == uid);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInit(EntityUid uid, ActionsContainerComponent component, ComponentInit args)
|
||||||
|
{
|
||||||
|
component.Container = _container.EnsureContainer<Container>(uid, ActionsContainerComponent.ContainerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnShutdown(EntityUid uid, ActionsContainerComponent component, ComponentShutdown args)
|
||||||
|
{
|
||||||
|
component.Container.Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEntityInserted(EntityUid uid, ActionsContainerComponent component, EntInsertedIntoContainerMessage args)
|
||||||
|
{
|
||||||
|
if (args.Container.ID != ActionsContainerComponent.ContainerId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!_actions.TryGetActionData(args.Entity, out var data))
|
||||||
|
return;
|
||||||
|
|
||||||
|
DebugTools.Assert(data.AttachedEntity == null || data.Container != EntityUid.Invalid);
|
||||||
|
DebugTools.Assert(data.Container == null || data.Container == uid);
|
||||||
|
|
||||||
|
data.Container = uid;
|
||||||
|
Dirty(uid, component);
|
||||||
|
|
||||||
|
var ev = new ActionAddedEvent(args.Entity, data);
|
||||||
|
RaiseLocalEvent(uid, ref ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEntityRemoved(EntityUid uid, ActionsContainerComponent component, EntRemovedFromContainerMessage args)
|
||||||
|
{
|
||||||
|
if (args.Container.ID != ActionsContainerComponent.ContainerId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Actions should only be getting removed while terminating or moving outside of PVS range.
|
||||||
|
DebugTools.Assert(Terminating(args.Entity)
|
||||||
|
|| _netMan.IsServer // I love gibbing code
|
||||||
|
|| _timing.ApplyingState);
|
||||||
|
|
||||||
|
if (!_actions.TryGetActionData(args.Entity, out var data, false))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// No event - the only entity that should care about this is the entity that the action was provided to.
|
||||||
|
if (data.AttachedEntity != null)
|
||||||
|
_actions.RemoveAction(data.AttachedEntity.Value, args.Entity, null, data);
|
||||||
|
|
||||||
|
var ev = new ActionRemovedEvent(args.Entity, data);
|
||||||
|
RaiseLocalEvent(uid, ref ev);
|
||||||
|
|
||||||
|
if (_netMan.IsServer)
|
||||||
|
{
|
||||||
|
// TODO Actions
|
||||||
|
// log an error or warning here once gibbing code is fixed.
|
||||||
|
QueueDel(uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised directed at an action container when a new action entity gets inserted.
|
||||||
|
/// </summary>
|
||||||
|
[ByRefEvent]
|
||||||
|
public readonly struct ActionAddedEvent
|
||||||
|
{
|
||||||
|
public readonly EntityUid Action;
|
||||||
|
public readonly BaseActionComponent Component;
|
||||||
|
|
||||||
|
public ActionAddedEvent(EntityUid action, BaseActionComponent component)
|
||||||
|
{
|
||||||
|
Action = action;
|
||||||
|
Component = component;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised directed at an action container when an action entity gets removed.
|
||||||
|
/// </summary>
|
||||||
|
[ByRefEvent]
|
||||||
|
public readonly struct ActionRemovedEvent
|
||||||
|
{
|
||||||
|
public readonly EntityUid Action;
|
||||||
|
public readonly BaseActionComponent Component;
|
||||||
|
|
||||||
|
public ActionRemovedEvent(EntityUid action, BaseActionComponent component)
|
||||||
|
{
|
||||||
|
Action = action;
|
||||||
|
Component = component;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@ using Content.Shared.Hands;
|
|||||||
using Content.Shared.Inventory;
|
using Content.Shared.Inventory;
|
||||||
using Content.Shared.Inventory.Events;
|
using Content.Shared.Inventory.Events;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Network;
|
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
namespace Content.Shared.Actions;
|
namespace Content.Shared.Actions;
|
||||||
@@ -18,8 +17,7 @@ namespace Content.Shared.Actions;
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
public sealed class GetItemActionsEvent : EntityEventArgs
|
public sealed class GetItemActionsEvent : EntityEventArgs
|
||||||
{
|
{
|
||||||
private readonly IEntityManager _entities;
|
private readonly ActionContainerSystem _system;
|
||||||
private readonly INetManager _net;
|
|
||||||
public readonly SortedSet<EntityUid> Actions = new();
|
public readonly SortedSet<EntityUid> Actions = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -27,6 +25,12 @@ public sealed class GetItemActionsEvent : EntityEventArgs
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public EntityUid User;
|
public EntityUid User;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The entity that is being asked to provide the actions. This is used as a default argument to <see cref="AddAction(ref System.Nullable{Robust.Shared.GameObjects.EntityUid},string,Robust.Shared.GameObjects.EntityUid)"/>.
|
||||||
|
/// I.e., if a new action needs to be spawned, then it will be inserted into this entity unless otherwise specified.
|
||||||
|
/// </summary>
|
||||||
|
public EntityUid Provider;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Slot flags for the inventory slot that this item got equipped to. Null if not in a slot (i.e., if equipped to hands).
|
/// Slot flags for the inventory slot that this item got equipped to. Null if not in a slot (i.e., if equipped to hands).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -37,25 +41,36 @@ public sealed class GetItemActionsEvent : EntityEventArgs
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool InHands => SlotFlags == null;
|
public bool InHands => SlotFlags == null;
|
||||||
|
|
||||||
public GetItemActionsEvent(IEntityManager entities, INetManager net, EntityUid user, SlotFlags? slotFlags = null)
|
public GetItemActionsEvent(ActionContainerSystem system, EntityUid user, EntityUid provider, SlotFlags? slotFlags = null)
|
||||||
{
|
{
|
||||||
_entities = entities;
|
_system = system;
|
||||||
_net = net;
|
|
||||||
User = user;
|
User = user;
|
||||||
|
Provider = provider;
|
||||||
SlotFlags = slotFlags;
|
SlotFlags = slotFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddAction(ref EntityUid? actionId, string? prototypeId)
|
/// <summary>
|
||||||
|
/// Grant the given action. If the EntityUid does not refer to a valid action entity, it will create a new action and
|
||||||
|
/// store it in <see cref="container"/>.
|
||||||
|
/// </summary>
|
||||||
|
public void AddAction(ref EntityUid? actionId, string prototypeId, EntityUid container)
|
||||||
{
|
{
|
||||||
if (_entities.Deleted(actionId))
|
if (_system.EnsureAction(container, ref actionId, prototypeId))
|
||||||
{
|
Actions.Add(actionId.Value);
|
||||||
if (string.IsNullOrWhiteSpace(prototypeId) || _net.IsClient)
|
}
|
||||||
return;
|
|
||||||
|
|
||||||
actionId = _entities.Spawn(prototypeId);
|
/// <summary>
|
||||||
}
|
/// Grant the given action. If the EntityUid does not refer to a valid action entity, it will create a new action and
|
||||||
|
/// store it in <see cref="Provider"/>.
|
||||||
|
/// </summary>
|
||||||
|
public void AddAction(ref EntityUid? actionId, string prototypeId)
|
||||||
|
{
|
||||||
|
AddAction(ref actionId, prototypeId, Provider);
|
||||||
|
}
|
||||||
|
|
||||||
Actions.Add(actionId.Value);
|
public void AddAction(EntityUid actionId)
|
||||||
|
{
|
||||||
|
Actions.Add(actionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,13 +9,10 @@ namespace Content.Shared.Actions;
|
|||||||
public sealed partial class ActionsComponent : Component
|
public sealed partial class ActionsComponent : Component
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handled on the client to track added and removed actions.
|
/// List of actions currently granted to this entity.
|
||||||
|
/// On the client, this may contain a mixture of client-side and networked entities.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables] public readonly Dictionary<EntityUid, ActionMetaData> OldClientActions = new();
|
[DataField] public HashSet<EntityUid> Actions = new();
|
||||||
|
|
||||||
[ViewVariables] public readonly HashSet<EntityUid> Actions = new();
|
|
||||||
|
|
||||||
public override bool SendOnlyToOwner => true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
@@ -29,7 +26,7 @@ public sealed class ActionsComponentState : ComponentState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly record struct ActionMetaData(bool ClientExclusive, bool AutoRemove);
|
public readonly record struct ActionMetaData(bool ClientExclusive);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines how the action icon appears in the hotbar for item actions.
|
/// Determines how the action icon appears in the hotbar for item actions.
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ using Robust.Shared.Utility;
|
|||||||
namespace Content.Shared.Actions;
|
namespace Content.Shared.Actions;
|
||||||
|
|
||||||
// TODO this should be an IncludeDataFields of each action component type, not use inheritance
|
// TODO this should be an IncludeDataFields of each action component type, not use inheritance
|
||||||
|
|
||||||
|
// TODO add access attribute. Need to figure out what to do with decal & mapping actions.
|
||||||
|
// [Access(typeof(SharedActionsSystem))]
|
||||||
public abstract partial class BaseActionComponent : Component
|
public abstract partial class BaseActionComponent : Component
|
||||||
{
|
{
|
||||||
public abstract BaseActionEvent? BaseEvent { get; }
|
public abstract BaseActionEvent? BaseEvent { get; }
|
||||||
@@ -46,11 +49,13 @@ public abstract partial class BaseActionComponent : Component
|
|||||||
/// The toggle can set directly via <see cref="SharedActionsSystem.SetToggled"/>, but it will also be
|
/// The toggle can set directly via <see cref="SharedActionsSystem.SetToggled"/>, but it will also be
|
||||||
/// automatically toggled for targeted-actions while selecting a target.
|
/// automatically toggled for targeted-actions while selecting a target.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
|
[DataField]
|
||||||
public bool Toggled;
|
public bool Toggled;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current cooldown on the action.
|
/// The current cooldown on the action.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
// TODO serialization
|
||||||
public (TimeSpan Start, TimeSpan End)? Cooldown;
|
public (TimeSpan Start, TimeSpan End)? Cooldown;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -65,21 +70,34 @@ public abstract partial class BaseActionComponent : Component
|
|||||||
[DataField("charges")] public int? Charges;
|
[DataField("charges")] public int? Charges;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The entity that enables / provides this action. If the action is innate, this may be the user themselves. If
|
/// The entity that contains this action. If the action is innate, this may be the user themselves.
|
||||||
/// this action has no provider (e.g., mapping tools), the this will result in broadcast events.
|
/// This should almost always be non-null.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public EntityUid? Provider;
|
[Access(typeof(ActionContainerSystem), typeof(SharedActionsSystem))]
|
||||||
|
[DataField]
|
||||||
|
public EntityUid? Container;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Entity to use for the action icon. Defaults to using <see cref="Provider"/>.
|
/// Entity to use for the action icon. If no entity is provided and the <see cref="Container"/> differs from
|
||||||
|
/// <see cref="AttachedEntity"/>, then it will default to using <see cref="Container"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public EntityUid? EntityIcon
|
public EntityUid? EntityIcon
|
||||||
{
|
{
|
||||||
get => _entityIcon ?? Provider;
|
get
|
||||||
set => _entityIcon = value;
|
{
|
||||||
|
if (EntIcon != null)
|
||||||
|
return EntIcon;
|
||||||
|
|
||||||
|
if (AttachedEntity != Container)
|
||||||
|
return Container;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
set => EntIcon = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private EntityUid? _entityIcon;
|
[DataField]
|
||||||
|
public EntityUid? EntIcon;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the action system should block this action if the user cannot currently interact. Some spells or
|
/// Whether the action system should block this action if the user cannot currently interact. Some spells or
|
||||||
@@ -88,7 +106,7 @@ public abstract partial class BaseActionComponent : Component
|
|||||||
[DataField("checkCanInteract")] public bool CheckCanInteract = true;
|
[DataField("checkCanInteract")] public bool CheckCanInteract = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If true, will simply execute the action locally without sending to the server.
|
/// If true, this will cause the action to only execute locally without ever notifying the server.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("clientExclusive")] public bool ClientExclusive = false;
|
[DataField("clientExclusive")] public bool ClientExclusive = false;
|
||||||
|
|
||||||
@@ -107,24 +125,10 @@ public abstract partial class BaseActionComponent : Component
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("autoPopulate")] public bool AutoPopulate = true;
|
[DataField("autoPopulate")] public bool AutoPopulate = true;
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether or not to automatically remove this action to the action bar when it becomes unavailable.
|
/// Temporary actions are deleted when they get removed a <see cref="ActionsComponent"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("autoRemove")] public bool AutoRemove = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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).
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// 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.
|
|
||||||
/// </remarks>
|
|
||||||
[DataField("temporary")] public bool Temporary;
|
[DataField("temporary")] public bool Temporary;
|
||||||
// TODO re-add support for this
|
|
||||||
// UI refactor seems to have just broken it.
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines the appearance of the entity-icon for actions that are enabled via some entity.
|
/// Determines the appearance of the entity-icon for actions that are enabled via some entity.
|
||||||
@@ -149,20 +153,22 @@ public abstract class BaseActionComponentState : ComponentState
|
|||||||
public (TimeSpan Start, TimeSpan End)? Cooldown;
|
public (TimeSpan Start, TimeSpan End)? Cooldown;
|
||||||
public TimeSpan? UseDelay;
|
public TimeSpan? UseDelay;
|
||||||
public int? Charges;
|
public int? Charges;
|
||||||
public NetEntity? Provider;
|
public NetEntity? Container;
|
||||||
public NetEntity? EntityIcon;
|
public NetEntity? EntityIcon;
|
||||||
public bool CheckCanInteract;
|
public bool CheckCanInteract;
|
||||||
public bool ClientExclusive;
|
public bool ClientExclusive;
|
||||||
public int Priority;
|
public int Priority;
|
||||||
public NetEntity? AttachedEntity;
|
public NetEntity? AttachedEntity;
|
||||||
public bool AutoPopulate;
|
public bool AutoPopulate;
|
||||||
public bool AutoRemove;
|
|
||||||
public bool Temporary;
|
public bool Temporary;
|
||||||
public ItemActionIconStyle ItemIconStyle;
|
public ItemActionIconStyle ItemIconStyle;
|
||||||
public SoundSpecifier? Sound;
|
public SoundSpecifier? Sound;
|
||||||
|
|
||||||
protected BaseActionComponentState(BaseActionComponent component, IEntityManager entManager)
|
protected BaseActionComponentState(BaseActionComponent component, IEntityManager entManager)
|
||||||
{
|
{
|
||||||
|
Container = entManager.GetNetEntity(component.Container);
|
||||||
|
EntityIcon = entManager.GetNetEntity(component.EntIcon);
|
||||||
|
AttachedEntity = entManager.GetNetEntity(component.AttachedEntity);
|
||||||
Icon = component.Icon;
|
Icon = component.Icon;
|
||||||
IconOn = component.IconOn;
|
IconOn = component.IconOn;
|
||||||
IconColor = component.IconColor;
|
IconColor = component.IconColor;
|
||||||
@@ -172,20 +178,10 @@ public abstract class BaseActionComponentState : ComponentState
|
|||||||
Cooldown = component.Cooldown;
|
Cooldown = component.Cooldown;
|
||||||
UseDelay = component.UseDelay;
|
UseDelay = component.UseDelay;
|
||||||
Charges = component.Charges;
|
Charges = component.Charges;
|
||||||
|
|
||||||
// TODO ACTION REFACTOR fix bugs
|
|
||||||
if (entManager.TryGetNetEntity(component.Provider, out var provider))
|
|
||||||
Provider = provider;
|
|
||||||
if (entManager.TryGetNetEntity(component.EntityIcon, out var icon))
|
|
||||||
EntityIcon = icon;
|
|
||||||
if (entManager.TryGetNetEntity(component.AttachedEntity, out var attached))
|
|
||||||
AttachedEntity = attached;
|
|
||||||
|
|
||||||
CheckCanInteract = component.CheckCanInteract;
|
CheckCanInteract = component.CheckCanInteract;
|
||||||
ClientExclusive = component.ClientExclusive;
|
ClientExclusive = component.ClientExclusive;
|
||||||
Priority = component.Priority;
|
Priority = component.Priority;
|
||||||
AutoPopulate = component.AutoPopulate;
|
AutoPopulate = component.AutoPopulate;
|
||||||
AutoRemove = component.AutoRemove;
|
|
||||||
Temporary = component.Temporary;
|
Temporary = component.Temporary;
|
||||||
ItemIconStyle = component.ItemIconStyle;
|
ItemIconStyle = component.ItemIconStyle;
|
||||||
Sound = component.Sound;
|
Sound = component.Sound;
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ using Content.Shared.Inventory.Events;
|
|||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Network;
|
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
@@ -18,18 +17,15 @@ namespace Content.Shared.Actions;
|
|||||||
|
|
||||||
public abstract class SharedActionsSystem : EntitySystem
|
public abstract class SharedActionsSystem : EntitySystem
|
||||||
{
|
{
|
||||||
private const string ActionContainerId = "ActionContainer";
|
|
||||||
private const string ProvidedActionContainerId = "ProvidedActionContainer";
|
|
||||||
|
|
||||||
[Dependency] protected readonly IGameTiming GameTiming = default!;
|
[Dependency] protected readonly IGameTiming GameTiming = default!;
|
||||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||||
[Dependency] private readonly INetManager _net = default!;
|
|
||||||
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
|
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
|
||||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||||
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
|
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
|
||||||
[Dependency] private readonly RotateToFaceSystem _rotateToFaceSystem = default!;
|
[Dependency] private readonly RotateToFaceSystem _rotateToFaceSystem = default!;
|
||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
||||||
|
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -40,26 +36,16 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
SubscribeLocalEvent<ActionsComponent, DidUnequipEvent>(OnDidUnequip);
|
SubscribeLocalEvent<ActionsComponent, DidUnequipEvent>(OnDidUnequip);
|
||||||
SubscribeLocalEvent<ActionsComponent, DidUnequipHandEvent>(OnHandUnequipped);
|
SubscribeLocalEvent<ActionsComponent, DidUnequipHandEvent>(OnHandUnequipped);
|
||||||
|
|
||||||
SubscribeLocalEvent<ActionsComponent, MapInitEvent>(OnActionsMapInit);
|
|
||||||
SubscribeLocalEvent<ActionsComponent, ComponentGetState>(OnActionsGetState);
|
SubscribeLocalEvent<ActionsComponent, ComponentGetState>(OnActionsGetState);
|
||||||
SubscribeLocalEvent<ActionsComponent, ComponentShutdown>(OnActionsShutdown);
|
|
||||||
|
|
||||||
SubscribeLocalEvent<InstantActionComponent, ComponentGetState>(OnInstantGetState);
|
SubscribeLocalEvent<InstantActionComponent, ComponentGetState>(OnInstantGetState);
|
||||||
SubscribeLocalEvent<EntityTargetActionComponent, ComponentGetState>(OnEntityTargetGetState);
|
SubscribeLocalEvent<EntityTargetActionComponent, ComponentGetState>(OnEntityTargetGetState);
|
||||||
SubscribeLocalEvent<WorldTargetActionComponent, ComponentGetState>(OnWorldTargetGetState);
|
SubscribeLocalEvent<WorldTargetActionComponent, ComponentGetState>(OnWorldTargetGetState);
|
||||||
|
|
||||||
SubscribeLocalEvent<InstantActionComponent, ComponentHandleState>(OnInstantHandleState);
|
|
||||||
SubscribeLocalEvent<EntityTargetActionComponent, ComponentHandleState>(OnEntityTargetHandleState);
|
|
||||||
SubscribeLocalEvent<WorldTargetActionComponent, ComponentHandleState>(OnWorldTargetHandleState);
|
|
||||||
|
|
||||||
SubscribeLocalEvent<InstantActionComponent, GetActionDataEvent>(OnGetActionData);
|
SubscribeLocalEvent<InstantActionComponent, GetActionDataEvent>(OnGetActionData);
|
||||||
SubscribeLocalEvent<EntityTargetActionComponent, GetActionDataEvent>(OnGetActionData);
|
SubscribeLocalEvent<EntityTargetActionComponent, GetActionDataEvent>(OnGetActionData);
|
||||||
SubscribeLocalEvent<WorldTargetActionComponent, GetActionDataEvent>(OnGetActionData);
|
SubscribeLocalEvent<WorldTargetActionComponent, GetActionDataEvent>(OnGetActionData);
|
||||||
|
|
||||||
SubscribeLocalEvent<InstantActionComponent, EntGotRemovedFromContainerMessage>(OnEntGotRemovedFromContainer);
|
|
||||||
SubscribeLocalEvent<EntityTargetActionComponent, EntGotRemovedFromContainerMessage>(OnEntGotRemovedFromContainer);
|
|
||||||
SubscribeLocalEvent<WorldTargetActionComponent, EntGotRemovedFromContainerMessage>(OnEntGotRemovedFromContainer);
|
|
||||||
|
|
||||||
SubscribeAllEvent<RequestPerformActionEvent>(OnActionRequest);
|
SubscribeAllEvent<RequestPerformActionEvent>(OnActionRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,117 +64,43 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
args.State = new WorldTargetActionComponentState(component, EntityManager);
|
args.State = new WorldTargetActionComponentState(component, EntityManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BaseHandleState<T>(EntityUid uid, BaseActionComponent component, BaseActionComponentState state) where T : BaseActionComponent
|
|
||||||
{
|
|
||||||
component.Icon = state.Icon;
|
|
||||||
component.IconOn = state.IconOn;
|
|
||||||
component.IconColor = state.IconColor;
|
|
||||||
component.Keywords = new HashSet<string>(state.Keywords);
|
|
||||||
component.Enabled = state.Enabled;
|
|
||||||
component.Toggled = state.Toggled;
|
|
||||||
component.Cooldown = state.Cooldown;
|
|
||||||
component.UseDelay = state.UseDelay;
|
|
||||||
component.Charges = state.Charges;
|
|
||||||
component.Provider = EnsureEntity<T>(state.Provider, uid);
|
|
||||||
component.EntityIcon = EnsureEntity<T>(state.EntityIcon, uid);
|
|
||||||
component.CheckCanInteract = state.CheckCanInteract;
|
|
||||||
component.ClientExclusive = state.ClientExclusive;
|
|
||||||
component.Priority = state.Priority;
|
|
||||||
component.AttachedEntity = EnsureEntity<T>(state.AttachedEntity, uid);
|
|
||||||
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<InstantActionComponent>(uid, component, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnEntityTargetHandleState(EntityUid uid, EntityTargetActionComponent component, ref ComponentHandleState args)
|
|
||||||
{
|
|
||||||
if (args.Current is not EntityTargetActionComponentState state)
|
|
||||||
return;
|
|
||||||
|
|
||||||
BaseHandleState<EntityTargetActionComponent>(uid, 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<WorldTargetActionComponent>(uid, component, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnGetActionData<T>(EntityUid uid, T component, ref GetActionDataEvent args) where T : BaseActionComponent
|
private void OnGetActionData<T>(EntityUid uid, T component, ref GetActionDataEvent args) where T : BaseActionComponent
|
||||||
{
|
{
|
||||||
args.Action = component;
|
args.Action = component;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnEntGotRemovedFromContainer<T>(EntityUid uid, T component, EntGotRemovedFromContainerMessage args) where T : BaseActionComponent
|
|
||||||
{
|
|
||||||
if (args.Container.ID != ProvidedActionContainerId)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (TryComp(component.AttachedEntity, out ActionsComponent? actions))
|
|
||||||
{
|
|
||||||
actions.Actions.Remove(uid);
|
|
||||||
Dirty(component.AttachedEntity.Value, actions);
|
|
||||||
|
|
||||||
if (TryGetActionData(uid, out var action))
|
|
||||||
action.AttachedEntity = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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(
|
public bool TryGetActionData(
|
||||||
[NotNullWhen(true)] EntityUid? actionId,
|
[NotNullWhen(true)] EntityUid? uid,
|
||||||
[NotNullWhen(true)] out BaseActionComponent? action)
|
[NotNullWhen(true)] out BaseActionComponent? result,
|
||||||
|
bool logError = true)
|
||||||
{
|
{
|
||||||
action = null;
|
result = null;
|
||||||
return actionId != null && (action = GetActionData(actionId)) != null;
|
if (!Exists(uid))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var ev = new GetActionDataEvent();
|
||||||
|
RaiseLocalEvent(uid.Value, ref ev);
|
||||||
|
result = ev.Action;
|
||||||
|
|
||||||
|
if (result != null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
Log.Error($"Failed to get action from action entity: {ToPrettyString(uid.Value)}");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Container EnsureContainer(EntityUid holderId, EntityUid? providerId)
|
public bool ResolveActionData(
|
||||||
|
[NotNullWhen(true)] EntityUid? uid,
|
||||||
|
[NotNullWhen(true)] ref BaseActionComponent? result,
|
||||||
|
bool logError = true)
|
||||||
{
|
{
|
||||||
return providerId == null
|
if (result != null)
|
||||||
? _containerSystem.EnsureContainer<Container>(holderId, ActionContainerId)
|
{
|
||||||
: _containerSystem.EnsureContainer<Container>(providerId.Value, ProvidedActionContainerId);
|
DebugTools.Assert(result.Owner == uid);
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
protected bool TryGetContainer(
|
return TryGetActionData(uid, out result, logError);
|
||||||
EntityUid holderId,
|
|
||||||
[NotNullWhen(true)] out BaseContainer? container,
|
|
||||||
ContainerManagerComponent? containerManager = null)
|
|
||||||
{
|
|
||||||
return _containerSystem.TryGetContainer(holderId, ActionContainerId, out container, containerManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected bool TryGetProvidedContainer(
|
|
||||||
EntityUid providerId,
|
|
||||||
[NotNullWhen(true)] out BaseContainer? container,
|
|
||||||
ContainerManagerComponent? containerManager = null)
|
|
||||||
{
|
|
||||||
return _containerSystem.TryGetContainer(providerId, ProvidedActionContainerId, out container, containerManager);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetCooldown(EntityUid? actionId, TimeSpan start, TimeSpan end)
|
public void SetCooldown(EntityUid? actionId, TimeSpan start, TimeSpan end)
|
||||||
@@ -196,8 +108,7 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
if (actionId == null)
|
if (actionId == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var action = GetActionData(actionId);
|
if (!TryGetActionData(actionId, out var action))
|
||||||
if (action == null)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
action.Cooldown = (start, end);
|
action.Cooldown = (start, end);
|
||||||
@@ -218,25 +129,9 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
#region ComponentStateManagement
|
#region ComponentStateManagement
|
||||||
public virtual void Dirty(EntityUid? actionId)
|
protected virtual void UpdateAction(EntityUid? actionId, BaseActionComponent? action = null)
|
||||||
{
|
{
|
||||||
if (!TryGetActionData(actionId, out var action))
|
// See client-side code.
|
||||||
return;
|
|
||||||
|
|
||||||
Dirty(actionId.Value, action);
|
|
||||||
|
|
||||||
if (action.AttachedEntity == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var ent = action.AttachedEntity;
|
|
||||||
|
|
||||||
if (!TryComp(ent, out ActionsComponent? comp))
|
|
||||||
{
|
|
||||||
action.AttachedEntity = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Dirty(action.AttachedEntity.Value, comp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetToggled(EntityUid? actionId, bool toggled)
|
public void SetToggled(EntityUid? actionId, bool toggled)
|
||||||
@@ -248,6 +143,7 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
action.Toggled = toggled;
|
action.Toggled = toggled;
|
||||||
|
UpdateAction(actionId, action);
|
||||||
Dirty(actionId.Value, action);
|
Dirty(actionId.Value, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,6 +156,7 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
action.Enabled = enabled;
|
action.Enabled = enabled;
|
||||||
|
UpdateAction(actionId, action);
|
||||||
Dirty(actionId.Value, action);
|
Dirty(actionId.Value, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,25 +169,15 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
action.Charges = charges;
|
action.Charges = charges;
|
||||||
|
UpdateAction(actionId, action);
|
||||||
Dirty(actionId.Value, action);
|
Dirty(actionId.Value, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnActionsMapInit(EntityUid uid, ActionsComponent component, MapInitEvent args)
|
|
||||||
{
|
|
||||||
EnsureContainer(uid, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnActionsGetState(EntityUid uid, ActionsComponent component, ref ComponentGetState args)
|
private void OnActionsGetState(EntityUid uid, ActionsComponent component, ref ComponentGetState args)
|
||||||
{
|
{
|
||||||
args.State = new ActionsComponentState(GetNetEntitySet(component.Actions));
|
args.State = new ActionsComponentState(GetNetEntitySet(component.Actions));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnActionsShutdown(EntityUid uid, ActionsComponent component, ComponentShutdown args)
|
|
||||||
{
|
|
||||||
if (TryGetContainer(uid, out var container))
|
|
||||||
container.Shutdown(EntityManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Execution
|
#region Execution
|
||||||
@@ -321,8 +208,12 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var action = GetActionData(actionEnt);
|
if (!TryGetActionData(actionEnt, out var action))
|
||||||
if (action == null || !action.Enabled)
|
return;
|
||||||
|
|
||||||
|
DebugTools.Assert(action.AttachedEntity == user);
|
||||||
|
|
||||||
|
if (!action.Enabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var curTime = GameTiming.CurTime;
|
var curTime = GameTiming.CurTime;
|
||||||
@@ -349,16 +240,8 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
if (!ValidateEntityTarget(user, entityTarget, entityAction))
|
if (!ValidateEntityTarget(user, entityTarget, entityAction))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (action.Provider == null)
|
_adminLogger.Add(LogType.Action,
|
||||||
{
|
$"{ToPrettyString(user):user} is performing the {name:action} action (provided by {ToPrettyString(action.Container ?? user):provider}) targeted at {ToPrettyString(entityTarget):target}.");
|
||||||
_adminLogger.Add(LogType.Action,
|
|
||||||
$"{ToPrettyString(user):user} is performing the {name:action} action targeted at {ToPrettyString(entityTarget):target}.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_adminLogger.Add(LogType.Action,
|
|
||||||
$"{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)
|
if (entityAction.Event != null)
|
||||||
{
|
{
|
||||||
@@ -381,16 +264,8 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
if (!ValidateWorldTarget(user, entityCoordinatesTarget, worldAction))
|
if (!ValidateWorldTarget(user, entityCoordinatesTarget, worldAction))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (action.Provider == null)
|
_adminLogger.Add(LogType.Action,
|
||||||
{
|
$"{ToPrettyString(user):user} is performing the {name:action} action (provided by {ToPrettyString(action.Container ?? user):provider}) targeted at {entityCoordinatesTarget:target}.");
|
||||||
_adminLogger.Add(LogType.Action,
|
|
||||||
$"{ToPrettyString(user):user} is performing the {name:action} action targeted at {entityCoordinatesTarget:target}.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_adminLogger.Add(LogType.Action,
|
|
||||||
$"{ToPrettyString(user):user} is performing the {name:action} action (provided by {ToPrettyString(action.Provider.Value):provider}) targeted at {entityCoordinatesTarget:target}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (worldAction.Event != null)
|
if (worldAction.Event != null)
|
||||||
{
|
{
|
||||||
@@ -404,16 +279,8 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
if (action.CheckCanInteract && !_actionBlockerSystem.CanInteract(user, null))
|
if (action.CheckCanInteract && !_actionBlockerSystem.CanInteract(user, null))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (action.Provider == null)
|
_adminLogger.Add(LogType.Action,
|
||||||
{
|
$"{ToPrettyString(user):user} is performing the {name:action} action provided by {ToPrettyString(action.Container ?? user):provider}.");
|
||||||
_adminLogger.Add(LogType.Action,
|
|
||||||
$"{ToPrettyString(user):user} is performing the {name:action} action.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_adminLogger.Add(LogType.Action,
|
|
||||||
$"{ToPrettyString(user):user} is performing the {name:action} action provided by {ToPrettyString(action.Provider.Value):provider}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
performEvent = instantAction.Event;
|
performEvent = instantAction.Event;
|
||||||
break;
|
break;
|
||||||
@@ -493,17 +360,18 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
|
|
||||||
var toggledBefore = action.Toggled;
|
var toggledBefore = action.Toggled;
|
||||||
|
|
||||||
|
// Note that attached entity is allowed to be null here.
|
||||||
|
if (action.AttachedEntity != null && action.AttachedEntity != performer)
|
||||||
|
{
|
||||||
|
Log.Error($"{ToPrettyString(performer)} is attempting to perform an action {ToPrettyString(actionId)} that is attached to another entity {ToPrettyString(action.AttachedEntity.Value)}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (actionEvent != null)
|
if (actionEvent != null)
|
||||||
{
|
{
|
||||||
// This here is required because of client-side prediction (RaisePredictiveEvent results in event re-use).
|
// This here is required because of client-side prediction (RaisePredictiveEvent results in event re-use).
|
||||||
actionEvent.Handled = false;
|
actionEvent.Handled = false;
|
||||||
var provider = action.Provider;
|
RaiseLocalEvent(action.Container ?? performer, (object) actionEvent, broadcast: true);
|
||||||
|
|
||||||
if (provider == null)
|
|
||||||
RaiseLocalEvent(performer, (object) actionEvent, broadcast: true);
|
|
||||||
else
|
|
||||||
RaiseLocalEvent(provider.Value, (object) actionEvent, broadcast: true);
|
|
||||||
|
|
||||||
handled = actionEvent.Handled;
|
handled = actionEvent.Handled;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -540,91 +408,129 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region AddRemoveActions
|
#region AddRemoveActions
|
||||||
/// <summary>
|
|
||||||
/// Add an action to an action holder.
|
public EntityUid? AddAction(EntityUid performer,
|
||||||
/// If the holder has no actions component, this will give them one.
|
string? actionPrototypeId,
|
||||||
/// </summary>
|
EntityUid container = default,
|
||||||
public BaseActionComponent? AddAction(EntityUid holderId, ref EntityUid? actionId, string? actionPrototypeId, EntityUid? provider = null, ActionsComponent? holderComp = null)
|
ActionsComponent? component = null)
|
||||||
{
|
{
|
||||||
if (Deleted(actionId))
|
EntityUid? actionId = null;
|
||||||
{
|
AddAction(performer, ref actionId, out _, actionPrototypeId, container, component);
|
||||||
if (_net.IsClient)
|
return actionId;
|
||||||
return null;
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(actionPrototypeId))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
actionId = Spawn(actionPrototypeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
AddAction(holderId, actionId.Value, provider, holderComp);
|
|
||||||
return GetActionData(actionId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add an action to an action holder.
|
/// Adds an action to an action holder. If the given entity does not exist, it will attempt to spawn one.
|
||||||
/// If the holder has no actions component, this will give them one.
|
/// If the holder has no actions component, this will give them one.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="holderId">Entity to receive the actions</param>
|
/// <param name="performer">Entity to receive the actions</param>
|
||||||
/// <param name="actionId">Action entity to add</param>
|
/// <param name="actionId">Action entity to add</param>
|
||||||
/// <param name="provider">The entity that enables these actions (e.g., flashlight). May be null (innate actions).</param>
|
/// <param name="component">The <see cref="performer"/>'s action component of </param>
|
||||||
/// <param name="holder">Component of <see cref="holderId"/></param>
|
/// <param name="actionPrototypeId">The action entity prototype id to use if <see cref="actionId"/> is invalid.</param>
|
||||||
/// <param name="action">Component of <see cref="actionId"/></param>
|
/// <param name="container">The entity that contains/enables this action (e.g., flashlight)..</param>
|
||||||
/// <param name="actionContainer">Action container of <see cref="holderId"/></param>
|
public bool AddAction(EntityUid performer,
|
||||||
public virtual void AddAction(EntityUid holderId, EntityUid actionId, EntityUid? provider, ActionsComponent? holder = null, BaseActionComponent? action = null, bool dirty = true, BaseContainer? actionContainer = null)
|
[NotNullWhen(true)] ref EntityUid? actionId,
|
||||||
|
string? actionPrototypeId,
|
||||||
|
EntityUid container = default,
|
||||||
|
ActionsComponent? component = null)
|
||||||
{
|
{
|
||||||
action ??= GetActionData(actionId);
|
return AddAction(performer, ref actionId, out _, actionPrototypeId, container, component);
|
||||||
// TODO remove when action subscriptions are split up
|
|
||||||
if (action == null)
|
|
||||||
{
|
|
||||||
Log.Warning($"No {nameof(BaseActionComponent)} found on entity {actionId}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
holder ??= EnsureComp<ActionsComponent>(holderId);
|
|
||||||
action.Provider = provider;
|
|
||||||
action.AttachedEntity = holderId;
|
|
||||||
Dirty(actionId, action);
|
|
||||||
|
|
||||||
actionContainer ??= EnsureContainer(holderId, provider);
|
|
||||||
AddActionInternal(holderId, actionId, actionContainer, holder);
|
|
||||||
|
|
||||||
if (dirty)
|
|
||||||
Dirty(holderId, holder);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void AddActionInternal(EntityUid holderId, EntityUid actionId, BaseContainer container, ActionsComponent holder)
|
/// <inheritdoc cref="AddAction(Robust.Shared.GameObjects.EntityUid,ref System.Nullable{Robust.Shared.GameObjects.EntityUid},string?,Robust.Shared.GameObjects.EntityUid,Content.Shared.Actions.ActionsComponent?)"/>
|
||||||
|
public bool AddAction(EntityUid performer,
|
||||||
|
[NotNullWhen(true)] ref EntityUid? actionId,
|
||||||
|
[NotNullWhen(true)] out BaseActionComponent? action,
|
||||||
|
string? actionPrototypeId,
|
||||||
|
EntityUid container = default,
|
||||||
|
ActionsComponent? component = null)
|
||||||
{
|
{
|
||||||
container.Insert(actionId);
|
if (!container.IsValid())
|
||||||
holder.Actions.Add(actionId);
|
container = performer;
|
||||||
Dirty(holderId, holder);
|
|
||||||
|
if (!_actionContainer.EnsureAction(container, ref actionId, out action, actionPrototypeId))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return AddActionDirect(performer, actionId.Value, component, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add actions to an action component. If the entity has no action component, this will give them one.
|
/// Adds a pre-existing action.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="holderId">Entity to receive the actions</param>
|
public bool AddAction(EntityUid performer,
|
||||||
/// <param name="actions">The actions to add</param>
|
EntityUid actionId,
|
||||||
/// <param name="provider">The entity that enables these actions (e.g., flashlight). May be null (innate actions).</param>
|
EntityUid container,
|
||||||
public void AddActions(EntityUid holderId, IEnumerable<EntityUid> actions, EntityUid? provider, ActionsComponent? comp = null, bool dirty = true)
|
ActionsComponent? comp = null,
|
||||||
|
BaseActionComponent? action = null,
|
||||||
|
ActionsContainerComponent? containerComp = null
|
||||||
|
)
|
||||||
{
|
{
|
||||||
comp ??= EnsureComp<ActionsComponent>(holderId);
|
if (!ResolveActionData(actionId, ref action))
|
||||||
|
return false;
|
||||||
|
|
||||||
var allClientExclusive = true;
|
if (action.Container != container
|
||||||
var container = EnsureContainer(holderId, provider);
|
|| !Resolve(container, ref containerComp)
|
||||||
|
|| !containerComp.Container.Contains(actionId))
|
||||||
|
{
|
||||||
|
Log.Error($"Attempted to add an action with an invalid container: {ToPrettyString(actionId)}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AddActionDirect(performer, actionId, comp, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a pre-existing action. This also bypasses the requirement that the given action must be stored in a
|
||||||
|
/// valid action container.
|
||||||
|
/// </summary>
|
||||||
|
public bool AddActionDirect(EntityUid performer,
|
||||||
|
EntityUid actionId,
|
||||||
|
ActionsComponent? comp = null,
|
||||||
|
BaseActionComponent? action = null)
|
||||||
|
{
|
||||||
|
if (!ResolveActionData(actionId, ref action))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DebugTools.Assert(action.Container == null ||
|
||||||
|
(TryComp(action.Container, out ActionsContainerComponent? containerComp)
|
||||||
|
&& containerComp.Container.Contains(actionId)));
|
||||||
|
|
||||||
|
DebugTools.Assert(comp == null || comp.Owner == performer);
|
||||||
|
comp ??= EnsureComp<ActionsComponent>(performer);
|
||||||
|
action.AttachedEntity = performer;
|
||||||
|
comp.Actions.Add(actionId);
|
||||||
|
Dirty(actionId, action);
|
||||||
|
Dirty(performer, comp);
|
||||||
|
ActionAdded(performer, actionId, comp, action);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This method gets called after a new action got added.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void ActionAdded(EntityUid performer, EntityUid actionId, ActionsComponent comp, BaseActionComponent action)
|
||||||
|
{
|
||||||
|
// See client-side system for UI code.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Grant pre-existing actions. If the entity has no action component, this will give them one.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="performer">Entity to receive the actions</param>
|
||||||
|
/// <param name="actions">The actions to add</param>
|
||||||
|
/// <param name="container">The entity that enables these actions (e.g., flashlight). May be null (innate actions).</param>
|
||||||
|
public void GrantActions(EntityUid performer, IEnumerable<EntityUid> actions, EntityUid container, ActionsComponent? comp = null, ActionsContainerComponent? containerComp = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(container, ref containerComp))
|
||||||
|
return;
|
||||||
|
|
||||||
|
DebugTools.Assert(comp == null || comp.Owner == performer);
|
||||||
|
comp ??= EnsureComp<ActionsComponent>(performer);
|
||||||
|
|
||||||
foreach (var actionId in actions)
|
foreach (var actionId in actions)
|
||||||
{
|
{
|
||||||
var action = GetActionData(actionId);
|
AddAction(performer, actionId, container, comp, containerComp: containerComp);
|
||||||
if (action == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
AddAction(holderId, actionId, provider, comp, action, false, container);
|
|
||||||
allClientExclusive = allClientExclusive && action.ClientExclusive;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dirty && !allClientExclusive)
|
|
||||||
Dirty(holderId, comp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<(EntityUid Id, BaseActionComponent Comp)> GetActions(EntityUid holderId, ActionsComponent? actions = null)
|
public IEnumerable<(EntityUid Id, BaseActionComponent Comp)> GetActions(EntityUid holderId, ActionsComponent? actions = null)
|
||||||
@@ -644,74 +550,72 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Remove any actions that were enabled by some other entity. Useful when unequiping items that grant actions.
|
/// Remove any actions that were enabled by some other entity. Useful when unequiping items that grant actions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void RemoveProvidedActions(EntityUid holderId, EntityUid provider, ActionsComponent? comp = null)
|
public void RemoveProvidedActions(EntityUid performer, EntityUid container, ActionsComponent? comp = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(holderId, ref comp, false))
|
if (!Resolve(performer, ref comp, false))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!TryGetProvidedContainer(provider, out var container))
|
foreach (var actionId in comp.Actions.ToArray())
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var actionId in container.ContainedEntities.ToArray())
|
|
||||||
{
|
{
|
||||||
var action = GetActionData(actionId);
|
if (!TryGetActionData(actionId, out var action))
|
||||||
if (action?.Provider == provider)
|
return;
|
||||||
RemoveAction(holderId, actionId, comp, dirty: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Dirty(holderId, comp);
|
if (action.Container == container)
|
||||||
|
RemoveAction(performer, actionId, comp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void RemoveAction(EntityUid holderId, EntityUid? actionId, ActionsComponent? comp = null, BaseActionComponent? action = null, bool dirty = true)
|
public void RemoveAction(EntityUid? actionId)
|
||||||
{
|
{
|
||||||
if (actionId == null ||
|
if (actionId == null)
|
||||||
!Resolve(holderId, ref comp, false) ||
|
return;
|
||||||
TerminatingOrDeleted(actionId.Value))
|
|
||||||
|
if (!TryGetActionData(actionId, out var action))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!TryComp(action.AttachedEntity, out ActionsComponent? comp))
|
||||||
|
return;
|
||||||
|
|
||||||
|
RemoveAction(action.AttachedEntity.Value, actionId, comp, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveAction(EntityUid performer, EntityUid? actionId, ActionsComponent? comp = null, BaseActionComponent? action = null)
|
||||||
|
{
|
||||||
|
if (actionId == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!ResolveActionData(actionId, ref action))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!Resolve(performer, ref comp, false))
|
||||||
{
|
{
|
||||||
|
DebugTools.AssertNull(action.AttachedEntity);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
action ??= GetActionData(actionId);
|
if (action.AttachedEntity == null)
|
||||||
|
|
||||||
if (TryGetContainer(holderId, out var container) && container.Contains(actionId.Value))
|
|
||||||
QueueDel(actionId.Value);
|
|
||||||
|
|
||||||
comp.Actions.Remove(actionId.Value);
|
|
||||||
|
|
||||||
if (action != null)
|
|
||||||
{
|
{
|
||||||
action.AttachedEntity = null;
|
// action was already removed?
|
||||||
Dirty(actionId.Value, action);
|
DebugTools.Assert(!comp.Actions.Contains(actionId.Value) || GameTiming.ApplyingState);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dirty)
|
DebugTools.Assert(action.AttachedEntity == performer);
|
||||||
Dirty(holderId, comp);
|
comp.Actions.Remove(actionId.Value);
|
||||||
|
action.AttachedEntity = null;
|
||||||
DebugTools.Assert(Transform(actionId.Value).ParentUid.IsValid());
|
Dirty(actionId.Value, action);
|
||||||
|
Dirty(performer, comp);
|
||||||
|
ActionRemoved(performer, actionId.Value, comp, action);
|
||||||
|
if (action.Temporary)
|
||||||
|
QueueDel(actionId.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes all actions with the given prototype id.
|
/// This method gets called after an action got removed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void RemoveAction(EntityUid holderId, string actionPrototypeId, ActionsComponent? holderComp = null)
|
protected virtual void ActionRemoved(EntityUid performer, EntityUid actionId, ActionsComponent comp, BaseActionComponent action)
|
||||||
{
|
{
|
||||||
if (!Resolve(holderId, ref holderComp, false))
|
// See client-side system for UI code.
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (actions.Count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var action in actions)
|
|
||||||
{
|
|
||||||
RemoveAction(holderId, action.Id, holderComp, action.Comp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -719,34 +623,55 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
#region EquipHandlers
|
#region EquipHandlers
|
||||||
private void OnDidEquip(EntityUid uid, ActionsComponent component, DidEquipEvent args)
|
private void OnDidEquip(EntityUid uid, ActionsComponent component, DidEquipEvent args)
|
||||||
{
|
{
|
||||||
var ev = new GetItemActionsEvent(EntityManager, _net, args.Equipee, args.SlotFlags);
|
if (GameTiming.ApplyingState)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var ev = new GetItemActionsEvent(_actionContainer, args.Equipee, args.Equipment, args.SlotFlags);
|
||||||
RaiseLocalEvent(args.Equipment, ev);
|
RaiseLocalEvent(args.Equipment, ev);
|
||||||
|
|
||||||
if (ev.Actions.Count == 0)
|
if (ev.Actions.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
AddActions(args.Equipee, ev.Actions, args.Equipment, component);
|
GrantActions(args.Equipee, ev.Actions, args.Equipment, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnHandEquipped(EntityUid uid, ActionsComponent component, DidEquipHandEvent args)
|
private void OnHandEquipped(EntityUid uid, ActionsComponent component, DidEquipHandEvent args)
|
||||||
{
|
{
|
||||||
var ev = new GetItemActionsEvent(EntityManager, _net, args.User);
|
if (GameTiming.ApplyingState)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var ev = new GetItemActionsEvent(_actionContainer, args.User, args.Equipped);
|
||||||
RaiseLocalEvent(args.Equipped, ev);
|
RaiseLocalEvent(args.Equipped, ev);
|
||||||
|
|
||||||
if (ev.Actions.Count == 0)
|
if (ev.Actions.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
AddActions(args.User, ev.Actions, args.Equipped, component);
|
GrantActions(args.User, ev.Actions, args.Equipped, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDidUnequip(EntityUid uid, ActionsComponent component, DidUnequipEvent args)
|
private void OnDidUnequip(EntityUid uid, ActionsComponent component, DidUnequipEvent args)
|
||||||
{
|
{
|
||||||
|
if (GameTiming.ApplyingState)
|
||||||
|
return;
|
||||||
|
|
||||||
RemoveProvidedActions(uid, args.Equipment, component);
|
RemoveProvidedActions(uid, args.Equipment, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnHandUnequipped(EntityUid uid, ActionsComponent component, DidUnequipHandEvent args)
|
private void OnHandUnequipped(EntityUid uid, ActionsComponent component, DidUnequipHandEvent args)
|
||||||
{
|
{
|
||||||
|
if (GameTiming.ApplyingState)
|
||||||
|
return;
|
||||||
|
|
||||||
RemoveProvidedActions(uid, args.Unequipped, component);
|
RemoveProvidedActions(uid, args.Unequipped, component);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
public void SetEntityIcon(EntityUid uid, EntityUid? icon, BaseActionComponent? action = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref action))
|
||||||
|
return;
|
||||||
|
|
||||||
|
action.EntityIcon = icon;
|
||||||
|
Dirty(uid, action);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ namespace Content.Server.Bed.Sleep
|
|||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
SubscribeLocalEvent<SleepingComponent, ComponentStartup>(OnStartup);
|
SubscribeLocalEvent<SleepingComponent, MapInitEvent>(OnMapInit);
|
||||||
SubscribeLocalEvent<SleepingComponent, ComponentShutdown>(OnShutdown);
|
SubscribeLocalEvent<SleepingComponent, ComponentShutdown>(OnShutdown);
|
||||||
SubscribeLocalEvent<SleepingComponent, SpeakAttemptEvent>(OnSpeakAttempt);
|
SubscribeLocalEvent<SleepingComponent, SpeakAttemptEvent>(OnSpeakAttempt);
|
||||||
SubscribeLocalEvent<SleepingComponent, CanSeeAttemptEvent>(OnSeeAttempt);
|
SubscribeLocalEvent<SleepingComponent, CanSeeAttemptEvent>(OnSeeAttempt);
|
||||||
@@ -33,24 +33,20 @@ namespace Content.Server.Bed.Sleep
|
|||||||
Dirty(uid, component);
|
Dirty(uid, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnStartup(EntityUid uid, SleepingComponent component, ComponentStartup args)
|
private void OnMapInit(EntityUid uid, SleepingComponent component, MapInitEvent args)
|
||||||
{
|
{
|
||||||
var ev = new SleepStateChangedEvent(true);
|
var ev = new SleepStateChangedEvent(true);
|
||||||
RaiseLocalEvent(uid, ev);
|
RaiseLocalEvent(uid, ev);
|
||||||
_blindableSystem.UpdateIsBlind(uid);
|
_blindableSystem.UpdateIsBlind(uid);
|
||||||
|
_actionsSystem.AddAction(uid, ref component.WakeAction, WakeActionId, uid);
|
||||||
|
|
||||||
if (_net.IsClient)
|
// TODO remove hardcoded time.
|
||||||
return;
|
|
||||||
|
|
||||||
component.WakeAction = Spawn(WakeActionId);
|
|
||||||
_actionsSystem.SetCooldown(component.WakeAction, _gameTiming.CurTime, _gameTiming.CurTime + TimeSpan.FromSeconds(15));
|
_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)
|
private void OnShutdown(EntityUid uid, SleepingComponent component, ComponentShutdown args)
|
||||||
{
|
{
|
||||||
_actionsSystem.RemoveAction(uid, component.WakeAction);
|
_actionsSystem.RemoveAction(uid, component.WakeAction);
|
||||||
|
|
||||||
var ev = new SleepStateChangedEvent(false);
|
var ev = new SleepStateChangedEvent(false);
|
||||||
RaiseLocalEvent(uid, ev);
|
RaiseLocalEvent(uid, ev);
|
||||||
_blindableSystem.UpdateIsBlind(uid);
|
_blindableSystem.UpdateIsBlind(uid);
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ public sealed class ToggleableClothingSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
|
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
|
||||||
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
|
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
|
||||||
|
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
|
||||||
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
||||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||||
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
||||||
@@ -258,10 +259,12 @@ public sealed class ToggleableClothingSystem : EntitySystem
|
|||||||
|
|
||||||
private void OnGetActions(EntityUid uid, ToggleableClothingComponent component, GetItemActionsEvent args)
|
private void OnGetActions(EntityUid uid, ToggleableClothingComponent component, GetItemActionsEvent args)
|
||||||
{
|
{
|
||||||
if (component.ClothingUid == null || (args.SlotFlags & component.RequiredFlags) != component.RequiredFlags)
|
if (component.ClothingUid != null
|
||||||
return;
|
&& component.ActionEntity != null
|
||||||
|
&& (args.SlotFlags & component.RequiredFlags) == component.RequiredFlags)
|
||||||
args.AddAction(ref component.ActionEntity, component.Action);
|
{
|
||||||
|
args.AddAction(component.ActionEntity.Value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnInit(EntityUid uid, ToggleableClothingComponent component, ComponentInit args)
|
private void OnInit(EntityUid uid, ToggleableClothingComponent component, ComponentInit args)
|
||||||
@@ -275,7 +278,7 @@ public sealed class ToggleableClothingSystem : EntitySystem
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnMapInit(EntityUid uid, ToggleableClothingComponent component, MapInitEvent args)
|
private void OnMapInit(EntityUid uid, ToggleableClothingComponent component, MapInitEvent args)
|
||||||
{
|
{
|
||||||
if (component.Container!.ContainedEntity is EntityUid ent)
|
if (component.Container!.ContainedEntity is {} ent)
|
||||||
{
|
{
|
||||||
DebugTools.Assert(component.ClothingUid == ent, "Unexpected entity present inside of a toggleable clothing container.");
|
DebugTools.Assert(component.ClothingUid == ent, "Unexpected entity present inside of a toggleable clothing container.");
|
||||||
return;
|
return;
|
||||||
@@ -295,11 +298,8 @@ public sealed class ToggleableClothingSystem : EntitySystem
|
|||||||
component.Container.Insert(component.ClothingUid.Value, EntityManager, ownerTransform: xform);
|
component.Container.Insert(component.ClothingUid.Value, EntityManager, ownerTransform: xform);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_actionsSystem.TryGetActionData(component.ActionEntity, out var action))
|
if (_actionContainer.EnsureAction(uid, ref component.ActionEntity, out var action, component.Action))
|
||||||
{
|
_actionsSystem.SetEntityIcon(component.ActionEntity.Value, component.ClothingUid, action);
|
||||||
action.EntityIcon = component.ClothingUid;
|
|
||||||
_actionsSystem.Dirty(component.ActionEntity);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ namespace Content.Shared.Clothing;
|
|||||||
[Access(typeof(SharedMagbootsSystem))]
|
[Access(typeof(SharedMagbootsSystem))]
|
||||||
public sealed partial class MagbootsComponent : Component
|
public sealed partial class MagbootsComponent : Component
|
||||||
{
|
{
|
||||||
[DataField("toggleAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>), required: true)]
|
[DataField]
|
||||||
public string? ToggleAction;
|
public EntProtoId ToggleAction = "ActionToggleMagboots";
|
||||||
|
|
||||||
[DataField("toggleActionEntity")]
|
[DataField, AutoNetworkedField]
|
||||||
public EntityUid? ToggleActionEntity;
|
public EntityUid? ToggleActionEntity;
|
||||||
|
|
||||||
[DataField("on"), AutoNetworkedField]
|
[DataField("on"), AutoNetworkedField]
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ namespace Content.Shared.CombatMode
|
|||||||
[DataField("combatToggleAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
[DataField("combatToggleAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||||
public string CombatToggleAction = "ActionCombatModeToggle";
|
public string CombatToggleAction = "ActionCombatModeToggle";
|
||||||
|
|
||||||
[DataField("combatToggleActionEntity")]
|
[DataField, AutoNetworkedField]
|
||||||
public EntityUid? CombatToggleActionEntity;
|
public EntityUid? CombatToggleActionEntity;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("isInCombatMode"), AutoNetworkedField]
|
[ViewVariables(VVAccess.ReadWrite), DataField("isInCombatMode"), AutoNetworkedField]
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ public abstract class SharedDevourSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<DevourerComponent, ComponentStartup>(OnStartup);
|
SubscribeLocalEvent<DevourerComponent, MapInitEvent>(OnInit);
|
||||||
SubscribeLocalEvent<DevourerComponent, DevourActionEvent>(OnDevourAction);
|
SubscribeLocalEvent<DevourerComponent, DevourActionEvent>(OnDevourAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void OnStartup(EntityUid uid, DevourerComponent component, ComponentStartup args)
|
protected void OnInit(EntityUid uid, DevourerComponent component, MapInitEvent args)
|
||||||
{
|
{
|
||||||
//Devourer doesn't actually chew, since he sends targets right into his stomach.
|
//Devourer doesn't actually chew, since he sends targets right into his stomach.
|
||||||
//I did it mom, I added ERP content into upstream. Legally!
|
//I did it mom, I added ERP content into upstream. Legally!
|
||||||
|
|||||||
@@ -17,19 +17,19 @@ public sealed partial class GhostComponent : Component
|
|||||||
[DataField("toggleLightingAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
[DataField("toggleLightingAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||||
public string ToggleLightingAction = "ActionToggleLighting";
|
public string ToggleLightingAction = "ActionToggleLighting";
|
||||||
|
|
||||||
[DataField("toggleLightingActionEntity")]
|
[DataField, AutoNetworkedField]
|
||||||
public EntityUid? ToggleLightingActionEntity;
|
public EntityUid? ToggleLightingActionEntity;
|
||||||
|
|
||||||
[DataField("toggleFovAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
[DataField("toggleFovAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||||
public string ToggleFoVAction = "ActionToggleFov";
|
public string ToggleFoVAction = "ActionToggleFov";
|
||||||
|
|
||||||
[DataField("toggleFovActionEntity")]
|
[DataField, AutoNetworkedField]
|
||||||
public EntityUid? ToggleFoVActionEntity;
|
public EntityUid? ToggleFoVActionEntity;
|
||||||
|
|
||||||
[DataField("toggleGhostsAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
[DataField("toggleGhostsAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||||
public string ToggleGhostsAction = "ActionToggleGhosts";
|
public string ToggleGhostsAction = "ActionToggleGhosts";
|
||||||
|
|
||||||
[DataField("toggleGhostsActionEntity")]
|
[DataField, AutoNetworkedField]
|
||||||
public EntityUid? ToggleGhostsActionEntity;
|
public EntityUid? ToggleGhostsActionEntity;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("timeOfDeath", customTypeSerializer:typeof(TimeOffsetSerializer))]
|
[ViewVariables(VVAccess.ReadWrite), DataField("timeOfDeath", customTypeSerializer:typeof(TimeOffsetSerializer))]
|
||||||
@@ -41,10 +41,11 @@ public sealed partial class GhostComponent : Component
|
|||||||
[DataField("booMaxTargets")]
|
[DataField("booMaxTargets")]
|
||||||
public int BooMaxTargets = 3;
|
public int BooMaxTargets = 3;
|
||||||
|
|
||||||
[DataField("action", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
[DataField]
|
||||||
public string Action = "ActionGhostBoo";
|
public EntProtoId BooAction = "ActionGhostBoo";
|
||||||
|
|
||||||
[DataField("actionEntity")] public EntityUid? ActionEntity;
|
[DataField, AutoNetworkedField]
|
||||||
|
public EntityUid? BooActionEntity;
|
||||||
|
|
||||||
// TODO: instead of this funny stuff just give it access and update in system dirtying when needed
|
// TODO: instead of this funny stuff just give it access and update in system dirtying when needed
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using Content.Shared.Actions;
|
using Content.Shared.Actions;
|
||||||
using Content.Shared.Radio;
|
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Shared.Implants.Components;
|
namespace Content.Shared.Implants.Components;
|
||||||
|
|
||||||
@@ -10,7 +9,7 @@ namespace Content.Shared.Implants.Components;
|
|||||||
/// The actions can be activated via an action, a passive ability (ie tracking), or a reactive ability (ie on death) or some sort of combination
|
/// The actions can be activated via an action, a passive ability (ie tracking), or a reactive ability (ie on death) or some sort of combination
|
||||||
/// They're added and removed with implanters
|
/// They're added and removed with implanters
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent, NetworkedComponent]
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
public sealed partial class SubdermalImplantComponent : Component
|
public sealed partial class SubdermalImplantComponent : Component
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -18,19 +17,22 @@ public sealed partial class SubdermalImplantComponent : Component
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
[DataField("implantAction")]
|
[DataField("implantAction")]
|
||||||
public string? ImplantAction;
|
public EntProtoId? ImplantAction;
|
||||||
|
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public EntityUid? Action;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The entity this implant is inside
|
/// The entity this implant is inside
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables]
|
[ViewVariables, AutoNetworkedField]
|
||||||
public EntityUid? ImplantedEntity;
|
public EntityUid? ImplantedEntity;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Should this implant be removeable?
|
/// Should this implant be removeable?
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
[DataField("permanent")]
|
[DataField("permanent"), AutoNetworkedField]
|
||||||
public bool Permanent = false;
|
public bool Permanent = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,8 +37,7 @@ public abstract class SharedSubdermalImplantSystem : EntitySystem
|
|||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(component.ImplantAction))
|
if (!string.IsNullOrWhiteSpace(component.ImplantAction))
|
||||||
{
|
{
|
||||||
var action = Spawn(component.ImplantAction);
|
_actionsSystem.AddAction(component.ImplantedEntity.Value, ref component.Action, component.ImplantAction, uid);
|
||||||
_actionsSystem.AddAction(component.ImplantedEntity.Value, action, uid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//replace micro bomb with macro bomb
|
//replace micro bomb with macro bomb
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Content.Shared.Decals;
|
using Content.Shared.Decals;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||||
|
|
||||||
@@ -9,24 +10,25 @@ namespace Content.Shared.Light.Components;
|
|||||||
/// This is simplified version of <see cref="HandheldLightComponent"/>.
|
/// This is simplified version of <see cref="HandheldLightComponent"/>.
|
||||||
/// It doesn't consume any power and can be toggle only by verb.
|
/// It doesn't consume any power and can be toggle only by verb.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent]
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
public sealed partial class UnpoweredFlashlightComponent : Component
|
public sealed partial class UnpoweredFlashlightComponent : Component
|
||||||
{
|
{
|
||||||
[DataField("toggleFlashlightSound")]
|
[DataField("toggleFlashlightSound")]
|
||||||
public SoundSpecifier ToggleSound = new SoundPathSpecifier("/Audio/Items/flashlight_pda.ogg");
|
public SoundSpecifier ToggleSound = new SoundPathSpecifier("/Audio/Items/flashlight_pda.ogg");
|
||||||
|
|
||||||
[ViewVariables] public bool LightOn = false;
|
[DataField, AutoNetworkedField]
|
||||||
|
public bool LightOn = false;
|
||||||
|
|
||||||
[DataField("toggleAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
[DataField]
|
||||||
public string? ToggleAction = "ActionToggleLight";
|
public EntProtoId ToggleAction = "ActionToggleLight";
|
||||||
|
|
||||||
[DataField("toggleActionEntity")] public EntityUid? ToggleActionEntity;
|
[DataField, AutoNetworkedField]
|
||||||
|
public EntityUid? ToggleActionEntity;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <see cref="ColorPalettePrototype"/> ID that determines the list
|
/// <see cref="ColorPalettePrototype"/> ID that determines the list
|
||||||
/// of colors to select from when we get emagged
|
/// of colors to select from when we get emagged
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("emaggedColorsPrototype")]
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
public ProtoId<ColorPalettePrototype> EmaggedColorsPrototype = "Emagged";
|
||||||
public string EmaggedColorsPrototype = "Emagged";
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,6 +157,10 @@ public sealed partial class MechComponent : Component
|
|||||||
[DataField("brokenState")]
|
[DataField("brokenState")]
|
||||||
public string? BrokenState;
|
public string? BrokenState;
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
[DataField] public EntityUid? MechCycleActionEntity;
|
||||||
|
[DataField] public EntityUid? MechUiActionEntity;
|
||||||
|
[DataField] public EntityUid? MechEjectActionEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -179,10 +179,9 @@ public abstract class SharedMechSystem : EntitySystem
|
|||||||
if (_net.IsClient)
|
if (_net.IsClient)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_actions.AddAction(pilot, Spawn(component.MechCycleAction), mech);
|
_actions.AddAction(pilot, ref component.MechCycleActionEntity, component.MechCycleAction, mech);
|
||||||
_actions.AddAction(pilot, Spawn(component.MechUiAction),
|
_actions.AddAction(pilot, ref component.MechUiActionEntity, component.MechUiAction, mech);
|
||||||
mech);
|
_actions.AddAction(pilot, ref component.MechEjectActionEntity, component.MechEjectAction, mech);
|
||||||
_actions.AddAction(pilot, Spawn(component.MechEjectAction), mech);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RemoveUser(EntityUid mech, EntityUid pilot)
|
private void RemoveUser(EntityUid mech, EntityUid pilot)
|
||||||
|
|||||||
@@ -24,4 +24,6 @@ public sealed partial class MobStateActionsComponent : Component
|
|||||||
/// </example>
|
/// </example>
|
||||||
[DataField("actions")]
|
[DataField("actions")]
|
||||||
public Dictionary<MobState, List<string>> Actions = new();
|
public Dictionary<MobState, List<string>> Actions = new();
|
||||||
|
|
||||||
|
[DataField] public List<EntityUid> GrantedActions = new();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Content.Shared.Actions;
|
using Content.Shared.Actions;
|
||||||
using Content.Shared.Mobs.Components;
|
using Content.Shared.Mobs.Components;
|
||||||
using Robust.Shared.Network;
|
|
||||||
|
|
||||||
namespace Content.Shared.Mobs.Systems;
|
namespace Content.Shared.Mobs.Systems;
|
||||||
|
|
||||||
@@ -9,7 +8,6 @@ namespace Content.Shared.Mobs.Systems;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class MobStateActionsSystem : EntitySystem
|
public sealed class MobStateActionsSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly INetManager _net = default!;
|
|
||||||
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -20,33 +18,25 @@ public sealed class MobStateActionsSystem : EntitySystem
|
|||||||
|
|
||||||
private void OnMobStateChanged(EntityUid uid, MobStateActionsComponent component, MobStateChangedEvent args)
|
private void OnMobStateChanged(EntityUid uid, MobStateActionsComponent component, MobStateChangedEvent args)
|
||||||
{
|
{
|
||||||
if (_net.IsClient)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!TryComp<ActionsComponent>(uid, out var action))
|
if (!TryComp<ActionsComponent>(uid, out var action))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var (state, acts) in component.Actions)
|
foreach (var act in component.GrantedActions)
|
||||||
{
|
{
|
||||||
if (state != args.NewMobState && state != args.OldMobState)
|
Del(act);
|
||||||
continue;
|
|
||||||
|
|
||||||
foreach (var item in acts)
|
|
||||||
{
|
|
||||||
if (state == args.OldMobState)
|
|
||||||
{
|
|
||||||
// Don't remove actions that would be getting readded anyway
|
|
||||||
if (component.Actions.TryGetValue(args.NewMobState, out var value)
|
|
||||||
&& value.Contains(item))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
_actions.RemoveAction(uid, item, action);
|
|
||||||
}
|
|
||||||
else if (state == args.NewMobState)
|
|
||||||
{
|
|
||||||
_actions.AddAction(uid, Spawn(item), null, action);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
component.GrantedActions.Clear();
|
||||||
|
|
||||||
|
if (!component.Actions.TryGetValue(args.NewMobState, out var toGrant))
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var id in toGrant)
|
||||||
|
{
|
||||||
|
EntityUid? act = null;
|
||||||
|
if (_actions.AddAction(uid, ref act, id, uid, action))
|
||||||
|
component.GrantedActions.Add(act.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Dirty(uid, component);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ public sealed partial class JetpackComponent : Component
|
|||||||
[ViewVariables(VVAccess.ReadWrite), DataField("moleUsage")]
|
[ViewVariables(VVAccess.ReadWrite), DataField("moleUsage")]
|
||||||
public float MoleUsage = 0.012f;
|
public float MoleUsage = 0.012f;
|
||||||
|
|
||||||
[DataField("toggleAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
[DataField] public EntProtoId ToggleAction = "ActionToggleJetpack";
|
||||||
public string? ToggleAction = "ActionToggleJetpack";
|
|
||||||
|
|
||||||
[DataField("toggleActionEntity")] public EntityUid? ToggleActionEntity;
|
[DataField("toggleActionEntity")] public EntityUid? ToggleActionEntity;
|
||||||
|
|
||||||
|
|||||||
@@ -194,4 +194,6 @@ public sealed partial class RevenantComponent : Component
|
|||||||
[DataField("harvestingState")]
|
[DataField("harvestingState")]
|
||||||
public string HarvestingState = "harvesting";
|
public string HarvestingState = "harvesting";
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
[DataField] public EntityUid? Action;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,8 +27,7 @@ public sealed partial class MeleeSpeechComponent : Component
|
|||||||
[AutoNetworkedField]
|
[AutoNetworkedField]
|
||||||
public int MaxBattlecryLength = 12;
|
public int MaxBattlecryLength = 12;
|
||||||
|
|
||||||
[DataField("configureAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
[DataField] public EntProtoId ConfigureAction = "ActionConfigureMeleeSpeech";
|
||||||
public string? ConfigureAction = "ActionConfigureMeleeSpeech";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The action to open the battlecry UI
|
/// The action to open the battlecry UI
|
||||||
|
|||||||
@@ -15,20 +15,18 @@ public abstract class SharedSpiderSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<SpiderComponent, ComponentStartup>(OnSpiderStartup);
|
SubscribeLocalEvent<SpiderComponent, MapInitEvent>(OnInit);
|
||||||
SubscribeLocalEvent<SpiderWebObjectComponent, ComponentStartup>(OnWebStartup);
|
SubscribeLocalEvent<SpiderWebObjectComponent, ComponentStartup>(OnWebStartup);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnSpiderStartup(EntityUid uid, SpiderComponent component, ComponentStartup args)
|
private void OnInit(EntityUid uid, SpiderComponent component, MapInitEvent args)
|
||||||
{
|
{
|
||||||
if (_net.IsClient)
|
_action.AddAction(uid, ref component.Action, component.WebAction, uid);
|
||||||
return;
|
|
||||||
|
|
||||||
_action.AddAction(uid, Spawn(component.WebAction), null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnWebStartup(EntityUid uid, SpiderWebObjectComponent component, ComponentStartup args)
|
private void OnWebStartup(EntityUid uid, SpiderWebObjectComponent component, ComponentStartup args)
|
||||||
{
|
{
|
||||||
|
// TODO dont use this. use some general random appearance system
|
||||||
_appearance.SetData(uid, SpiderWebVisuals.Variant, _robustRandom.Next(1, 3));
|
_appearance.SetData(uid, SpiderWebVisuals.Variant, _robustRandom.Next(1, 3));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ public sealed partial class SpiderComponent : Component
|
|||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
[DataField("webAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
[DataField("webAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||||
public string WebAction = "ActionSpiderWeb";
|
public string WebAction = "ActionSpiderWeb";
|
||||||
|
|
||||||
|
[DataField] public EntityUid? Action;
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed partial class SpiderWebActionEvent : InstantActionEvent { }
|
public sealed partial class SpiderWebActionEvent : InstantActionEvent { }
|
||||||
|
|||||||
@@ -209,6 +209,9 @@
|
|||||||
noSpawn: true
|
noSpawn: true
|
||||||
components:
|
components:
|
||||||
- type: InstantAction
|
- type: InstantAction
|
||||||
|
clientExclusive: true
|
||||||
|
checkCanInteract: false
|
||||||
|
temporary: true
|
||||||
icon: { sprite: Objects/Tools/multitool.rsi, state: icon }
|
icon: { sprite: Objects/Tools/multitool.rsi, state: icon }
|
||||||
event: !type:ClearAllOverlaysEvent
|
event: !type:ClearAllOverlaysEvent
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
- type: Clothing
|
- type: Clothing
|
||||||
sprite: Clothing/Shoes/Boots/magboots.rsi
|
sprite: Clothing/Shoes/Boots/magboots.rsi
|
||||||
- type: Magboots
|
- type: Magboots
|
||||||
toggleAction: ActionToggleMagboots
|
|
||||||
- type: ClothingSpeedModifier
|
- type: ClothingSpeedModifier
|
||||||
walkModifier: 0.85
|
walkModifier: 0.85
|
||||||
sprintModifier: 0.8
|
sprintModifier: 0.8
|
||||||
|
|||||||
Reference in New Issue
Block a user