diff --git a/Content.Server/GameObjects/EntitySystems/DoAfter/DoAfter.cs b/Content.Server/GameObjects/EntitySystems/DoAfter/DoAfter.cs index 2f45195624..7cf9bdb56d 100644 --- a/Content.Server/GameObjects/EntitySystems/DoAfter/DoAfter.cs +++ b/Content.Server/GameObjects/EntitySystems/DoAfter/DoAfter.cs @@ -1,7 +1,6 @@ #nullable enable using System; using System.Threading.Tasks; -using Content.Server.GameObjects.Components.Damage; using Content.Server.GameObjects.Components.GUI; using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.GameObjects.Components.Mobs; @@ -86,7 +85,16 @@ namespace Content.Server.GameObjects.EntitySystems.DoAfter if (IsFinished()) { - Tcs.SetResult(DoAfterStatus.Finished); + // Do the final checks here + if (!TryPostCheck()) + { + Tcs.SetResult(DoAfterStatus.Cancelled); + } + else + { + Tcs.SetResult(DoAfterStatus.Finished); + } + return; } @@ -98,6 +106,11 @@ namespace Content.Server.GameObjects.EntitySystems.DoAfter private bool IsCancelled() { + if (EventArgs.User.Deleted || EventArgs.Target?.Deleted == true) + { + return true; + } + //https://github.com/tgstation/tgstation/blob/1aa293ea337283a0191140a878eeba319221e5df/code/__HELPERS/mobs.dm if (EventArgs.CancelToken.IsCancellationRequested) { @@ -157,10 +170,15 @@ namespace Content.Server.GameObjects.EntitySystems.DoAfter } } } - + return false; } + private bool TryPostCheck() + { + return EventArgs.PostCheck?.Invoke() != false; + } + private bool IsFinished() { if (Elapsed <= EventArgs.Delay) diff --git a/Content.Server/GameObjects/EntitySystems/DoAfter/DoAfterEventArgs.cs b/Content.Server/GameObjects/EntitySystems/DoAfter/DoAfterEventArgs.cs index 7906c616eb..1f46dbd7ee 100644 --- a/Content.Server/GameObjects/EntitySystems/DoAfter/DoAfterEventArgs.cs +++ b/Content.Server/GameObjects/EntitySystems/DoAfter/DoAfterEventArgs.cs @@ -1,6 +1,9 @@ #nullable enable using System; using System.Threading; +using Content.Shared.GameObjects.EntitySystems; +using Content.Shared.Physics; +using Robust.Shared.GameObjects.Systems; using Robust.Shared.Interfaces.GameObjects; // ReSharper disable UnassignedReadonlyField @@ -9,6 +12,18 @@ namespace Content.Server.GameObjects.EntitySystems.DoAfter { public sealed class DoAfterEventArgs { + // Premade checks + public Func GetInRangeUnobstructed(int collisionMask = (int) CollisionGroup.MobMask) + { + if (Target == null) + { + throw new InvalidOperationException("Can't supply a null target to DoAfterEventArgs.GetInRangeUnobstructed"); + } + var interactionSystem = EntitySystem.Get(); + Func ignored = entity => entity == User || entity == Target; + return () => interactionSystem.InRangeUnobstructed(User.Transform.MapPosition, Target.Transform.MapPosition, collisionMask: collisionMask, predicate: ignored); + } + /// /// The entity invoking do_after /// @@ -49,6 +64,14 @@ namespace Content.Server.GameObjects.EntitySystems.DoAfter public bool BreakOnDamage { get; set; } public bool BreakOnStun { get; set; } + /// + /// Requires a function call once at the end (like InRangeUnobstructed). + /// + /// + /// Anything that needs a pre-check should do it itself so no DoAfterState is ever sent to the client. + /// + public Func? PostCheck { get; set; } = null; + /// /// Additional conditions that need to be met. Return false to cancel. ///