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:
35
Content.Shared/Actions/Events/ActionDoAfterEvent.cs
Normal file
35
Content.Shared/Actions/Events/ActionDoAfterEvent.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using Content.Shared.DoAfter;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Actions.Events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The event that triggers when an action doafter is completed or cancelled
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed partial class ActionDoAfterEvent : DoAfterEvent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The action performer
|
||||||
|
/// </summary>
|
||||||
|
public readonly NetEntity Performer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The original action use delay, used for repeating actions
|
||||||
|
/// </summary>
|
||||||
|
public readonly TimeSpan? OriginalUseDelay;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The original request, for validating
|
||||||
|
/// </summary>
|
||||||
|
public readonly RequestPerformActionEvent Input;
|
||||||
|
|
||||||
|
public ActionDoAfterEvent(NetEntity performer, TimeSpan? originalUseDelay, RequestPerformActionEvent input)
|
||||||
|
{
|
||||||
|
Performer = performer;
|
||||||
|
OriginalUseDelay = originalUseDelay;
|
||||||
|
Input = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override DoAfterEvent Clone() => this;
|
||||||
|
}
|
||||||
85
Content.Shared/Actions/SharedActionsSystem.DoAfter.cs
Normal file
85
Content.Shared/Actions/SharedActionsSystem.DoAfter.cs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
using Content.Shared.Actions.Events;
|
||||||
|
using Content.Shared.DoAfter;
|
||||||
|
|
||||||
|
namespace Content.Shared.Actions;
|
||||||
|
|
||||||
|
public abstract partial class SharedActionsSystem
|
||||||
|
{
|
||||||
|
protected void InitializeActionDoAfter()
|
||||||
|
{
|
||||||
|
SubscribeLocalEvent<DoAfterArgsComponent, ActionDoAfterEvent>(OnActionDoAfter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryStartActionDoAfter(Entity<DoAfterArgsComponent> ent, Entity<DoAfterComponent?> performer, TimeSpan? originalUseDelay, RequestPerformActionEvent input)
|
||||||
|
{
|
||||||
|
// relay to user
|
||||||
|
if (!Resolve(performer, ref performer.Comp))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var delay = ent.Comp.Delay;
|
||||||
|
|
||||||
|
var netEnt = GetNetEntity(performer);
|
||||||
|
|
||||||
|
var actionDoAfterEvent = new ActionDoAfterEvent(netEnt, originalUseDelay, input);
|
||||||
|
|
||||||
|
var doAfterArgs = new DoAfterArgs(EntityManager, performer, delay, actionDoAfterEvent, ent.Owner, performer)
|
||||||
|
{
|
||||||
|
AttemptFrequency = ent.Comp.AttemptFrequency,
|
||||||
|
Broadcast = ent.Comp.Broadcast,
|
||||||
|
Hidden = ent.Comp.Hidden,
|
||||||
|
NeedHand = ent.Comp.NeedHand,
|
||||||
|
BreakOnHandChange = ent.Comp.BreakOnHandChange,
|
||||||
|
BreakOnDropItem = ent.Comp.BreakOnDropItem,
|
||||||
|
BreakOnMove = ent.Comp.BreakOnMove,
|
||||||
|
BreakOnWeightlessMove = ent.Comp.BreakOnWeightlessMove,
|
||||||
|
MovementThreshold = ent.Comp.MovementThreshold,
|
||||||
|
DistanceThreshold = ent.Comp.DistanceThreshold,
|
||||||
|
BreakOnDamage = ent.Comp.BreakOnDamage,
|
||||||
|
DamageThreshold = ent.Comp.DamageThreshold,
|
||||||
|
RequireCanInteract = ent.Comp.RequireCanInteract
|
||||||
|
};
|
||||||
|
|
||||||
|
return _doAfter.TryStartDoAfter(doAfterArgs, performer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnActionDoAfter(Entity<DoAfterArgsComponent> ent, ref ActionDoAfterEvent args)
|
||||||
|
{
|
||||||
|
if (!_actionQuery.TryComp(ent, out var actionComp))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var performer = GetEntity(args.Performer);
|
||||||
|
var action = (ent, actionComp);
|
||||||
|
|
||||||
|
// If this doafter is on repeat and was cancelled, start use delay as expected
|
||||||
|
if (args.Cancelled && ent.Comp.Repeat)
|
||||||
|
{
|
||||||
|
SetUseDelay(action, args.OriginalUseDelay);
|
||||||
|
RemoveCooldown(action);
|
||||||
|
StartUseDelay(action);
|
||||||
|
UpdateAction(action);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
args.Repeat = ent.Comp.Repeat;
|
||||||
|
|
||||||
|
// Set the use delay to 0 so this can repeat properly
|
||||||
|
if (ent.Comp.Repeat)
|
||||||
|
{
|
||||||
|
SetUseDelay(action, TimeSpan.Zero);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.Cancelled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Post original doafter, reduce the time on it now for other casts if ables
|
||||||
|
if (ent.Comp.DelayReduction != null)
|
||||||
|
args.Args.Delay = ent.Comp.DelayReduction.Value;
|
||||||
|
|
||||||
|
// Validate again for charges, blockers, etc
|
||||||
|
if (TryPerformAction(args.Input, performer, skipDoActionRequest: true))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Cancel this doafter if we can't validate the action
|
||||||
|
_doAfter.Cancel(args.DoAfter.Id, force: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ using Content.Shared.Actions.Components;
|
|||||||
using Content.Shared.Actions.Events;
|
using Content.Shared.Actions.Events;
|
||||||
using Content.Shared.Administration.Logs;
|
using Content.Shared.Administration.Logs;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
|
using Content.Shared.DoAfter;
|
||||||
using Content.Shared.Hands;
|
using Content.Shared.Hands;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Inventory.Events;
|
using Content.Shared.Inventory.Events;
|
||||||
@@ -19,7 +20,7 @@ using Robust.Shared.Utility;
|
|||||||
|
|
||||||
namespace Content.Shared.Actions;
|
namespace Content.Shared.Actions;
|
||||||
|
|
||||||
public abstract class SharedActionsSystem : EntitySystem
|
public abstract partial class SharedActionsSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] protected readonly IGameTiming GameTiming = default!;
|
[Dependency] protected readonly IGameTiming GameTiming = default!;
|
||||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = 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 SharedAudioSystem _audio = default!;
|
||||||
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
|
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
|
||||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||||
|
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
||||||
|
|
||||||
private EntityQuery<ActionComponent> _actionQuery;
|
private EntityQuery<ActionComponent> _actionQuery;
|
||||||
private EntityQuery<ActionsComponent> _actionsQuery;
|
private EntityQuery<ActionsComponent> _actionsQuery;
|
||||||
@@ -38,6 +40,7 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
InitializeActionDoAfter();
|
||||||
|
|
||||||
_actionQuery = GetEntityQuery<ActionComponent>();
|
_actionQuery = GetEntityQuery<ActionComponent>();
|
||||||
_actionsQuery = GetEntityQuery<ActionsComponent>();
|
_actionsQuery = GetEntityQuery<ActionsComponent>();
|
||||||
@@ -256,20 +259,31 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
#region Execution
|
#region Execution
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// When receiving a request to perform an action, this validates whether the action is allowed. If it is, it
|
/// 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>
|
/// </summary>
|
||||||
private void OnActionRequest(RequestPerformActionEvent ev, EntitySessionEventArgs args)
|
private void OnActionRequest(RequestPerformActionEvent ev, EntitySessionEventArgs args)
|
||||||
{
|
{
|
||||||
if (args.SenderSession.AttachedEntity is not { } user)
|
if (args.SenderSession.AttachedEntity is not { } user)
|
||||||
return;
|
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))
|
if (!_actionsQuery.TryComp(user, out var component))
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
var actionEnt = GetEntity(ev.Action);
|
var actionEnt = GetEntity(ev.Action);
|
||||||
|
|
||||||
if (!TryComp(actionEnt, out MetaDataComponent? metaData))
|
if (!TryComp(actionEnt, out MetaDataComponent? metaData))
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
var name = Name(actionEnt, metaData);
|
var name = Name(actionEnt, metaData);
|
||||||
|
|
||||||
@@ -278,26 +292,25 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
_adminLogger.Add(LogType.Action,
|
_adminLogger.Add(LogType.Action,
|
||||||
$"{ToPrettyString(user):user} attempted to perform an action that they do not have: {name}.");
|
$"{ToPrettyString(user):user} attempted to perform an action that they do not have: {name}.");
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GetAction(actionEnt) is not {} action)
|
if (GetAction(actionEnt) is not {} action)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
DebugTools.Assert(action.Comp.AttachedEntity == user);
|
DebugTools.Assert(action.Comp.AttachedEntity == user);
|
||||||
if (!action.Comp.Enabled)
|
if (!action.Comp.Enabled)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
var curTime = GameTiming.CurTime;
|
var curTime = GameTiming.CurTime;
|
||||||
if (IsCooldownActive(action, curTime))
|
if (IsCooldownActive(action, curTime))
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
// check for action use prevention
|
// check for action use prevention
|
||||||
// TODO: make code below use this event with a dedicated component
|
|
||||||
var attemptEv = new ActionAttemptEvent(user);
|
var attemptEv = new ActionAttemptEvent(user);
|
||||||
RaiseLocalEvent(action, ref attemptEv);
|
RaiseLocalEvent(action, ref attemptEv);
|
||||||
if (attemptEv.Cancelled)
|
if (attemptEv.Cancelled)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
// Validate request by checking action blockers and the like
|
// Validate request by checking action blockers and the like
|
||||||
var provider = action.Comp.Container ?? user;
|
var provider = action.Comp.Container ?? user;
|
||||||
@@ -309,10 +322,16 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
};
|
};
|
||||||
RaiseLocalEvent(action, ref validateEv);
|
RaiseLocalEvent(action, ref validateEv);
|
||||||
if (validateEv.Invalid)
|
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!
|
// All checks passed. Perform the action!
|
||||||
PerformAction((user, component), action);
|
PerformAction((user, component), action);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnValidate(Entity<ActionComponent> ent, ref ActionValidateEvent args)
|
private void OnValidate(Entity<ActionComponent> ent, ref ActionValidateEvent args)
|
||||||
@@ -530,8 +549,6 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
var handled = false;
|
var handled = false;
|
||||||
|
|
||||||
var toggledBefore = action.Comp.Toggled;
|
|
||||||
|
|
||||||
// Note that attached entity and attached container are allowed to be null here.
|
// Note that attached entity and attached container are allowed to be null here.
|
||||||
if (action.Comp.AttachedEntity != null && action.Comp.AttachedEntity != performer)
|
if (action.Comp.AttachedEntity != null && action.Comp.AttachedEntity != performer)
|
||||||
{
|
{
|
||||||
@@ -552,6 +569,7 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
ev.Performer = performer;
|
ev.Performer = performer;
|
||||||
ev.Action = action;
|
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))
|
if (!action.Comp.RaiseOnUser && action.Comp.Container is {} container && !_mindQuery.HasComp(container))
|
||||||
target = container;
|
target = container;
|
||||||
|
|
||||||
@@ -564,13 +582,12 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
if (!handled)
|
if (!handled)
|
||||||
return; // no interaction occurred.
|
return; // no interaction occurred.
|
||||||
|
|
||||||
// play sound, reduce charges, start cooldown
|
// play sound, start cooldown
|
||||||
if (ev?.Toggle == true)
|
if (ev.Toggle)
|
||||||
SetToggled((action, action), !action.Comp.Toggled);
|
SetToggled((action, action), !action.Comp.Toggled);
|
||||||
|
|
||||||
_audio.PlayPredicted(action.Comp.Sound, performer, predicted ? performer : null);
|
_audio.PlayPredicted(action.Comp.Sound, performer, predicted ? performer : null);
|
||||||
|
|
||||||
// TODO: move to ActionCooldown ActionPerformedEvent?
|
|
||||||
RemoveCooldown((action, action));
|
RemoveCooldown((action, action));
|
||||||
StartUseDelay((action, action));
|
StartUseDelay((action, action));
|
||||||
|
|
||||||
|
|||||||
@@ -319,6 +319,7 @@ public enum DuplicateConditions : byte
|
|||||||
All = SameTool | SameTarget | SameEvent,
|
All = SameTool | SameTarget | SameEvent,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
public enum AttemptFrequency : byte
|
public enum AttemptFrequency : byte
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
116
Content.Shared/DoAfter/DoAfterArgsComponent.cs
Normal file
116
Content.Shared/DoAfter/DoAfterArgsComponent.cs
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
using Content.Shared.FixedPoint;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.DoAfter;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For setting DoAfterArgs on an entity level
|
||||||
|
/// Would require some setup, will require a rework eventually
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
[Access(typeof(SharedDoAfterSystem))]
|
||||||
|
public sealed partial class DoAfterArgsComponent : Component
|
||||||
|
{
|
||||||
|
#region DoAfterArgsSettings
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="DoAfterArgs.AttemptFrequency"/>
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public AttemptFrequency AttemptFrequency;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="DoAfterArgs.Broadcast"/>
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool Broadcast;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="DoAfterArgs.Delay"/>
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public TimeSpan Delay = TimeSpan.FromSeconds(2);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="DoAfterArgs.Hidden"/>
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool Hidden;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Should this DoAfter repeat after being completed?
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool Repeat;
|
||||||
|
|
||||||
|
#region Break/Cancellation Options
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="DoAfterArgs.NeedHand"/>
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool NeedHand;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="DoAfterArgs.BreakOnHandChange"/>
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool BreakOnHandChange = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="DoAfterArgs.BreakOnDropItem"/>
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool BreakOnDropItem = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="DoAfterArgs.BreakOnMove"/>
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool BreakOnMove;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="DoAfterArgs.BreakOnWeightlessMove"/>
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool BreakOnWeightlessMove = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="DoAfterArgs.MovementThreshold"/>
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public float MovementThreshold = 0.3f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="DoAfterArgs.DistanceThreshold"/>
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public float? DistanceThreshold;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="DoAfterArgs.BreakOnDamage"/>
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool BreakOnDamage;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="DoAfterArgs.DamageThreshold"/>
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public FixedPoint2 DamageThreshold = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc cref="DoAfterArgs.RequireCanInteract"/>
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool RequireCanInteract = true;
|
||||||
|
// End Break/Cancellation Options
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// What should the delay be reduced to after completion?
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public TimeSpan? DelayReduction;
|
||||||
|
|
||||||
|
// End DoAfterArgsSettings
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Content.Shared.FixedPoint;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
@@ -8,10 +9,16 @@ namespace Content.Shared.DoAfter;
|
|||||||
[Access(typeof(SharedDoAfterSystem))]
|
[Access(typeof(SharedDoAfterSystem))]
|
||||||
public sealed partial class DoAfterComponent : Component
|
public sealed partial class DoAfterComponent : Component
|
||||||
{
|
{
|
||||||
[DataField("nextId")]
|
/// <summary>
|
||||||
|
/// The id of the next doafter
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
public ushort NextId;
|
public ushort NextId;
|
||||||
|
|
||||||
[DataField("doAfters")]
|
/// <summary>
|
||||||
|
/// collection of id + doafter
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
public Dictionary<ushort, DoAfter> DoAfters = new();
|
public Dictionary<ushort, DoAfter> DoAfters = new();
|
||||||
|
|
||||||
// Used by obsolete async do afters
|
// Used by obsolete async do afters
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ public abstract partial class SharedDoAfterSystem : EntitySystem
|
|||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<DoAfterComponent, DamageChangedEvent>(OnDamage);
|
SubscribeLocalEvent<DoAfterComponent, DamageChangedEvent>(OnDamage);
|
||||||
SubscribeLocalEvent<DoAfterComponent, EntityUnpausedEvent>(OnUnpaused);
|
SubscribeLocalEvent<DoAfterComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||||
SubscribeLocalEvent<DoAfterComponent, ComponentGetState>(OnDoAfterGetState);
|
SubscribeLocalEvent<DoAfterComponent, ComponentGetState>(OnDoAfterGetState);
|
||||||
@@ -313,16 +314,16 @@ public abstract partial class SharedDoAfterSystem : EntitySystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Cancels an active DoAfter.
|
/// Cancels an active DoAfter.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Cancel(DoAfterId? id, DoAfterComponent? comp = null)
|
public void Cancel(DoAfterId? id, DoAfterComponent? comp = null, bool force = false)
|
||||||
{
|
{
|
||||||
if (id != null)
|
if (id != null)
|
||||||
Cancel(id.Value.Uid, id.Value.Index, comp);
|
Cancel(id.Value.Uid, id.Value.Index, comp, force);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Cancels an active DoAfter.
|
/// Cancels an active DoAfter.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Cancel(EntityUid entity, ushort id, DoAfterComponent? comp = null)
|
public void Cancel(EntityUid entity, ushort id, DoAfterComponent? comp = null, bool force = false)
|
||||||
{
|
{
|
||||||
if (!Resolve(entity, ref comp, false))
|
if (!Resolve(entity, ref comp, false))
|
||||||
return;
|
return;
|
||||||
@@ -333,13 +334,13 @@ public abstract partial class SharedDoAfterSystem : EntitySystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
InternalCancel(doAfter, comp);
|
InternalCancel(doAfter, comp, force: force);
|
||||||
Dirty(entity, comp);
|
Dirty(entity, comp);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InternalCancel(DoAfter doAfter, DoAfterComponent component)
|
private void InternalCancel(DoAfter doAfter, DoAfterComponent component, bool force = false)
|
||||||
{
|
{
|
||||||
if (doAfter.Cancelled || doAfter.Completed)
|
if (doAfter.Cancelled || (doAfter.Completed && !force))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Caller is responsible for dirtying the component.
|
// Caller is responsible for dirtying the component.
|
||||||
|
|||||||
@@ -7,6 +7,14 @@
|
|||||||
components:
|
components:
|
||||||
- type: Action
|
- type: Action
|
||||||
|
|
||||||
|
# base proto for an action that requires a DoAfter
|
||||||
|
- type: entity
|
||||||
|
abstract: true
|
||||||
|
parent: BaseAction
|
||||||
|
id: BaseDoAfterAction
|
||||||
|
components:
|
||||||
|
- type: DoAfterArgs
|
||||||
|
|
||||||
# an action that is done all in le head and cant be prevented by any means
|
# an action that is done all in le head and cant be prevented by any means
|
||||||
- type: entity
|
- type: entity
|
||||||
abstract: true
|
abstract: true
|
||||||
|
|||||||
Reference in New Issue
Block a user