Action stuff (#31305)

* Action stuff

- Cleanup some event stuff
- Avoid dirtying entity unnecessarily
- Add ActionGrant as an easy way to apply / remove actions via compregistry.

* Fix merge
This commit is contained in:
metalgearsloth
2024-08-25 22:43:31 +10:00
committed by GitHub
parent f96dddd9fb
commit 2e029a7d5b
10 changed files with 168 additions and 40 deletions

View File

@@ -187,5 +187,10 @@ public abstract partial class BaseActionEvent : HandledEntityEventArgs
/// <summary>
/// The action the event belongs to.
/// </summary>
public EntityUid Action;
public Entity<BaseActionComponent> Action;
/// <summary>
/// Should we toggle the action entity?
/// </summary>
public bool Toggle;
}

View File

@@ -0,0 +1,17 @@
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared.Actions;
/// <summary>
/// Grants actions on MapInit and removes them on shutdown
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(ActionGrantSystem))]
public sealed partial class ActionGrantComponent : Component
{
[DataField(required: true), AutoNetworkedField, AlwaysPushInheritance]
public List<EntProtoId> Actions = new();
[DataField, AutoNetworkedField]
public List<EntityUid> ActionEntities = new();
}

View File

@@ -0,0 +1,48 @@
namespace Content.Shared.Actions;
/// <summary>
/// <see cref="ActionGrantComponent"/>
/// </summary>
public sealed class ActionGrantSystem : EntitySystem
{
[Dependency] private readonly SharedActionsSystem _actions = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ActionGrantComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<ActionGrantComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<ItemActionGrantComponent, GetItemActionsEvent>(OnItemGet);
}
private void OnItemGet(Entity<ItemActionGrantComponent> ent, ref GetItemActionsEvent args)
{
if (!TryComp(ent.Owner, out ActionGrantComponent? grant))
return;
foreach (var action in grant.ActionEntities)
{
args.AddAction(action);
}
}
private void OnMapInit(Entity<ActionGrantComponent> ent, ref MapInitEvent args)
{
foreach (var action in ent.Comp.Actions)
{
EntityUid? actionEnt = null;
_actions.AddAction(ent.Owner, ref actionEnt, action);
if (actionEnt != null)
ent.Comp.ActionEntities.Add(actionEnt.Value);
}
}
private void OnShutdown(Entity<ActionGrantComponent> ent, ref ComponentShutdown args)
{
foreach (var actionEnt in ent.Comp.ActionEntities)
{
_actions.RemoveAction(ent.Owner, actionEnt);
}
}
}

View File

@@ -0,0 +1,27 @@
using Robust.Shared.Prototypes;
namespace Content.Shared.Actions.Events;
/// <summary>
/// Adds / removes the component upon action.
/// </summary>
[Virtual]
public partial class ActionComponentChangeEvent : InstantActionEvent
{
[DataField(required: true)]
public ComponentRegistry Components = new();
}
/// <summary>
/// Similar to <see cref="ActionComponentChangeEvent"/> except raises an event to attempt to relay it.
/// </summary>
public sealed partial class RelayedActionComponentChangeEvent : ActionComponentChangeEvent
{
}
[ByRefEvent]
public record struct AttemptRelayActionComponentChangeEvent
{
public EntityUid? Target;
}

View File

@@ -0,0 +1,14 @@
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared.Actions;
/// <summary>
/// Works in tandem with <see cref="ActionGrantComponent"/> by granting those actions to the equipper entity.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(ActionGrantSystem))]
public sealed partial class ItemActionGrantComponent : Component
{
[DataField(required: true), AutoNetworkedField, AlwaysPushInheritance]
public List<EntProtoId> Actions = new();
}

View File

@@ -11,7 +11,6 @@ using Content.Shared.Mind;
using Content.Shared.Rejuvenate;
using Content.Shared.Whitelist;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
using Robust.Shared.GameStates;
using Robust.Shared.Map;
using Robust.Shared.Timing;
@@ -45,6 +44,8 @@ public abstract class SharedActionsSystem : EntitySystem
SubscribeLocalEvent<WorldTargetActionComponent, ComponentShutdown>(OnActionShutdown);
SubscribeLocalEvent<EntityWorldTargetActionComponent, ComponentShutdown>(OnActionShutdown);
SubscribeLocalEvent<ActionsComponent, ActionComponentChangeEvent>(OnActionCompChange);
SubscribeLocalEvent<ActionsComponent, RelayedActionComponentChangeEvent>(OnRelayActionCompChange);
SubscribeLocalEvent<ActionsComponent, DidEquipEvent>(OnDidEquip);
SubscribeLocalEvent<ActionsComponent, DidEquipHandEvent>(OnHandEquipped);
SubscribeLocalEvent<ActionsComponent, DidUnequipEvent>(OnDidUnequip);
@@ -490,12 +491,6 @@ public abstract class SharedActionsSystem : EntitySystem
break;
}
if (performEvent != null)
{
performEvent.Performer = user;
performEvent.Action = actionEnt;
}
// All checks passed. Perform the action!
PerformAction(user, component, actionEnt, action, performEvent, curTime);
}
@@ -641,6 +636,8 @@ public abstract class SharedActionsSystem : EntitySystem
// This here is required because of client-side prediction (RaisePredictiveEvent results in event re-use).
actionEvent.Handled = false;
var target = performer;
actionEvent.Performer = performer;
actionEvent.Action = (actionId, action);
if (!action.RaiseOnUser && action.Container != null && !HasComp<MindComponent>(action.Container))
target = action.Container.Value;
@@ -653,10 +650,14 @@ public abstract class SharedActionsSystem : EntitySystem
return; // no interaction occurred.
// play sound, reduce charges, start cooldown, and mark as dirty (if required).
if (actionEvent?.Toggle == true)
{
action.Toggled = !action.Toggled;
}
_audio.PlayPredicted(action.Sound, performer,predicted ? performer : null);
_audio.PlayPredicted(action.Sound, performer, predicted ? performer : null);
var dirty = toggledBefore == action.Toggled;
var dirty = toggledBefore != action.Toggled;
if (action.Charges != null)
{
@@ -673,10 +674,11 @@ public abstract class SharedActionsSystem : EntitySystem
action.Cooldown = (curTime, curTime + action.UseDelay.Value);
}
Dirty(actionId, action);
if (dirty && component != null)
Dirty(performer, component);
if (dirty)
{
Dirty(actionId, action);
UpdateAction(actionId, action);
}
var ev = new ActionPerformedEvent(performer);
RaiseLocalEvent(actionId, ref ev);
@@ -975,6 +977,47 @@ public abstract class SharedActionsSystem : EntitySystem
#endregion
private void OnRelayActionCompChange(Entity<ActionsComponent> ent, ref RelayedActionComponentChangeEvent args)
{
if (args.Handled)
return;
var ev = new AttemptRelayActionComponentChangeEvent();
RaiseLocalEvent(ent.Owner, ref ev);
var target = ev.Target ?? ent.Owner;
args.Handled = true;
args.Toggle = true;
if (!args.Action.Comp.Toggled)
{
EntityManager.AddComponents(target, args.Components);
}
else
{
EntityManager.RemoveComponents(target, args.Components);
}
}
private void OnActionCompChange(Entity<ActionsComponent> ent, ref ActionComponentChangeEvent args)
{
if (args.Handled)
return;
args.Handled = true;
args.Toggle = true;
var target = ent.Owner;
if (!args.Action.Comp.Toggled)
{
EntityManager.AddComponents(target, args.Components);
}
else
{
EntityManager.RemoveComponents(target, args.Components);
}
}
#region EquipHandlers
private void OnDidEquip(EntityUid uid, ActionsComponent component, DidEquipEvent args)
{