DoAfter support for Actions (#38253)
* Adds Action DoAfter Events * Adds DoAfterArgs fields to DoAfterComp * Adds a base doafter action * Adds Attempt action doafter logic * Adds doafter logic to actions * Changes Action Attempt Doafter and action doafter to take in Performer and the original use delay. Use delay now triggers when a repeated action is cancelled. * Readds the TryPerformAction method and readds request perform action into the action doafter events * Adds a force skip to DoAfter Cancel so we can skip the complete check * Adds a Delay Reduction field to the comp and to the comp state * Fixes doafter mispredict, changes doafter comp check to a guard clause, sets delay reduction if it exists. * Cancels ActionDoAfter if charges is 0 * Serializes Attempt Frequency * Comment for rework * Changes todo into a comment * Moves doafterargs to doafterargscomp * Adds DoAfterArgs comp to BaseDoAfterAction * Removes unused trycomp with actionDoAfter * Replaces DoAfterRepateUseDelay const with timespan.zero * Removes unused usings * Makes SharedActionsSystem partial, adds DoAfter partial class to ActionSystem, moves ActionDoAfter logic to the SharedActionsSystem.DoAfter class * Cleanup and prediction * Renames OnActionDoAfterAttempt to OnActionDoAfter, moves both to Shared Action DoAfter * Removes ActionAttemptDoAfterEvent and moves its summaries to ActionDoAfterEvent. Converts OnActionDoAfterAttempt into TryStartActionDoAfter * Removes Extra check for charges and actiondoafters * Sloptimization * Cleanup * Cleanup * Adds param descs --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
This commit is contained in:
@@ -5,6 +5,7 @@ using Content.Shared.Actions.Components;
|
||||
using Content.Shared.Actions.Events;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Inventory.Events;
|
||||
@@ -19,7 +20,7 @@ using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Actions;
|
||||
|
||||
public abstract class SharedActionsSystem : EntitySystem
|
||||
public abstract partial class SharedActionsSystem : EntitySystem
|
||||
{
|
||||
[Dependency] protected readonly IGameTiming GameTiming = default!;
|
||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||
@@ -30,6 +31,7 @@ public abstract class SharedActionsSystem : EntitySystem
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
||||
|
||||
private EntityQuery<ActionComponent> _actionQuery;
|
||||
private EntityQuery<ActionsComponent> _actionsQuery;
|
||||
@@ -38,6 +40,7 @@ public abstract class SharedActionsSystem : EntitySystem
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
InitializeActionDoAfter();
|
||||
|
||||
_actionQuery = GetEntityQuery<ActionComponent>();
|
||||
_actionsQuery = GetEntityQuery<ActionsComponent>();
|
||||
@@ -256,20 +259,31 @@ public abstract class SharedActionsSystem : EntitySystem
|
||||
#region Execution
|
||||
/// <summary>
|
||||
/// When receiving a request to perform an action, this validates whether the action is allowed. If it is, it
|
||||
/// will raise the relevant <see cref="InstantActionEvent"/>
|
||||
/// will raise the relevant action event
|
||||
/// </summary>
|
||||
private void OnActionRequest(RequestPerformActionEvent ev, EntitySessionEventArgs args)
|
||||
{
|
||||
if (args.SenderSession.AttachedEntity is not { } user)
|
||||
return;
|
||||
|
||||
TryPerformAction(ev, user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="OnActionRequest"/>
|
||||
/// </summary>
|
||||
/// <param name="ev">The Request Perform Action Event</param>
|
||||
/// <param name="user">The user/performer of the action</param>
|
||||
/// <param name="skipDoActionRequest">Should this skip the initial doaction request?</param>
|
||||
private bool TryPerformAction(RequestPerformActionEvent ev, EntityUid user, bool skipDoActionRequest = false)
|
||||
{
|
||||
if (!_actionsQuery.TryComp(user, out var component))
|
||||
return;
|
||||
return false;
|
||||
|
||||
var actionEnt = GetEntity(ev.Action);
|
||||
|
||||
if (!TryComp(actionEnt, out MetaDataComponent? metaData))
|
||||
return;
|
||||
return false;
|
||||
|
||||
var name = Name(actionEnt, metaData);
|
||||
|
||||
@@ -278,26 +292,25 @@ public abstract class SharedActionsSystem : EntitySystem
|
||||
{
|
||||
_adminLogger.Add(LogType.Action,
|
||||
$"{ToPrettyString(user):user} attempted to perform an action that they do not have: {name}.");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GetAction(actionEnt) is not {} action)
|
||||
return;
|
||||
return false;
|
||||
|
||||
DebugTools.Assert(action.Comp.AttachedEntity == user);
|
||||
if (!action.Comp.Enabled)
|
||||
return;
|
||||
return false;
|
||||
|
||||
var curTime = GameTiming.CurTime;
|
||||
if (IsCooldownActive(action, curTime))
|
||||
return;
|
||||
return false;
|
||||
|
||||
// check for action use prevention
|
||||
// TODO: make code below use this event with a dedicated component
|
||||
var attemptEv = new ActionAttemptEvent(user);
|
||||
RaiseLocalEvent(action, ref attemptEv);
|
||||
if (attemptEv.Cancelled)
|
||||
return;
|
||||
return false;
|
||||
|
||||
// Validate request by checking action blockers and the like
|
||||
var provider = action.Comp.Container ?? user;
|
||||
@@ -309,10 +322,16 @@ public abstract class SharedActionsSystem : EntitySystem
|
||||
};
|
||||
RaiseLocalEvent(action, ref validateEv);
|
||||
if (validateEv.Invalid)
|
||||
return;
|
||||
return false;
|
||||
|
||||
if (TryComp<DoAfterArgsComponent>(action, out var actionDoAfterComp) && TryComp<DoAfterComponent>(user, out var performerDoAfterComp) && !skipDoActionRequest)
|
||||
{
|
||||
return TryStartActionDoAfter((action, actionDoAfterComp), (user, performerDoAfterComp), action.Comp.UseDelay, ev);
|
||||
}
|
||||
|
||||
// All checks passed. Perform the action!
|
||||
PerformAction((user, component), action);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnValidate(Entity<ActionComponent> ent, ref ActionValidateEvent args)
|
||||
@@ -530,8 +549,6 @@ public abstract class SharedActionsSystem : EntitySystem
|
||||
{
|
||||
var handled = false;
|
||||
|
||||
var toggledBefore = action.Comp.Toggled;
|
||||
|
||||
// Note that attached entity and attached container are allowed to be null here.
|
||||
if (action.Comp.AttachedEntity != null && action.Comp.AttachedEntity != performer)
|
||||
{
|
||||
@@ -552,6 +569,7 @@ public abstract class SharedActionsSystem : EntitySystem
|
||||
ev.Performer = performer;
|
||||
ev.Action = action;
|
||||
|
||||
// TODO: This is where we'd add support for event lists
|
||||
if (!action.Comp.RaiseOnUser && action.Comp.Container is {} container && !_mindQuery.HasComp(container))
|
||||
target = container;
|
||||
|
||||
@@ -564,13 +582,12 @@ public abstract class SharedActionsSystem : EntitySystem
|
||||
if (!handled)
|
||||
return; // no interaction occurred.
|
||||
|
||||
// play sound, reduce charges, start cooldown
|
||||
if (ev?.Toggle == true)
|
||||
// play sound, start cooldown
|
||||
if (ev.Toggle)
|
||||
SetToggled((action, action), !action.Comp.Toggled);
|
||||
|
||||
_audio.PlayPredicted(action.Comp.Sound, performer, predicted ? performer : null);
|
||||
|
||||
// TODO: move to ActionCooldown ActionPerformedEvent?
|
||||
RemoveCooldown((action, action));
|
||||
StartUseDelay((action, action));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user