diff --git a/Content.Shared/DoAfter/DoAfter.cs b/Content.Shared/DoAfter/DoAfter.cs index 0a8e197c19..ed8db8c7b2 100644 --- a/Content.Shared/DoAfter/DoAfter.cs +++ b/Content.Shared/DoAfter/DoAfter.cs @@ -48,10 +48,10 @@ public sealed class DoAfter public EntityCoordinates UserPosition; /// - /// Position of the target relative to their parent when the do after was started. + /// Distance from the user to the target when the do after was started. /// - [DataField("targetPosition")] - public EntityCoordinates TargetPosition; + [DataField("targetDistance")] + public float TargetDistance; /// /// If is true, this is the hand that was selected when the doafter started. @@ -94,7 +94,7 @@ public sealed class DoAfter CancelledTime = other.CancelledTime; Completed = other.Completed; UserPosition = other.UserPosition; - TargetPosition = other.TargetPosition; + TargetDistance = other.TargetDistance; InitialHand = other.InitialHand; InitialItem = other.InitialItem; } diff --git a/Content.Shared/DoAfter/DoAfterArgs.cs b/Content.Shared/DoAfter/DoAfterArgs.cs index 30d56fb0e7..618297d235 100644 --- a/Content.Shared/DoAfter/DoAfterArgs.cs +++ b/Content.Shared/DoAfter/DoAfterArgs.cs @@ -79,6 +79,13 @@ public sealed class DoAfterArgs [DataField("breakOnUserMove")] public bool BreakOnUserMove; + /// + /// If this is true then any movement, even when weightless, will break the doafter. + /// When there is no gravity, BreakOnUserMove is ignored. If it is false to begin with nothing will change. + /// + [DataField("breakOnWeightlessMove")] + public bool BreakOnWeightlessMove; + /// /// If do_after stops when the target moves (if there is a target) /// @@ -219,6 +226,7 @@ public sealed class DoAfterArgs NeedHand = other.NeedHand; BreakOnHandChange = other.BreakOnHandChange; BreakOnUserMove = other.BreakOnUserMove; + BreakOnWeightlessMove = other.BreakOnWeightlessMove; BreakOnTargetMove = other.BreakOnTargetMove; MovementThreshold = other.MovementThreshold; DistanceThreshold = other.DistanceThreshold; diff --git a/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs b/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs index 981133f7f0..0ab3eb40b7 100644 --- a/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs +++ b/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs @@ -1,3 +1,4 @@ +using Content.Shared.Gravity; using Content.Shared.Hands.Components; using Robust.Shared.Utility; @@ -6,6 +7,7 @@ namespace Content.Shared.DoAfter; public abstract partial class SharedDoAfterSystem : EntitySystem { [Dependency] private readonly IDynamicTypeFactory _factory = default!; + [Dependency] private readonly SharedGravitySystem _gravity = default!; public override void Update(float frameTime) { @@ -151,18 +153,23 @@ public abstract partial class SharedDoAfterSystem : EntitySystem if (args.Used is { } @using && !xformQuery.TryGetComponent(@using, out usedXform)) return true; - // TODO: Handle Inertia in space // TODO: Re-use existing xform query for these calculations. - if (args.BreakOnUserMove && !userXform.Coordinates - .InRange(EntityManager, _transform, doAfter.UserPosition, args.MovementThreshold)) + // when there is no gravity you will be drifting 99% of the time making many doafters impossible + // so this just ignores your movement if you are weightless (unless the doafter sets BreakOnWeightlessMove then moving will still break it) + if (args.BreakOnUserMove + && !userXform.Coordinates.InRange(EntityManager, _transform, doAfter.UserPosition, args.MovementThreshold) + && (args.BreakOnWeightlessMove || !_gravity.IsWeightless(args.User, xform: userXform))) return true; if (args.BreakOnTargetMove) { DebugTools.Assert(targetXform != null, "Break on move is true, but no target specified?"); - if (targetXform != null && !targetXform.Coordinates.InRange(EntityManager, _transform, - doAfter.TargetPosition, args.MovementThreshold)) - return true; + if (targetXform != null && targetXform.Coordinates.TryDistance(EntityManager, userXform.Coordinates, out var distance)) + { + // once the target moves too far from you the do after breaks + if (Math.Abs(distance - doAfter.TargetDistance) > args.MovementThreshold) + return true; + } } if (args.AttemptFrequency == AttemptFrequency.EveryTick && !TryAttemptEvent(doAfter)) diff --git a/Content.Shared/DoAfter/SharedDoAfterSystem.cs b/Content.Shared/DoAfter/SharedDoAfterSystem.cs index 27011816cd..2a045ffb0d 100644 --- a/Content.Shared/DoAfter/SharedDoAfterSystem.cs +++ b/Content.Shared/DoAfter/SharedDoAfterSystem.cs @@ -193,12 +193,15 @@ public abstract partial class SharedDoAfterSystem : EntitySystem id = new DoAfterId(args.User, comp.NextId++); var doAfter = new DoAfter(id.Value.Index, args, GameTiming.CurTime); - if (args.BreakOnUserMove) + if (args.BreakOnUserMove || args.BreakOnTargetMove) doAfter.UserPosition = Transform(args.User).Coordinates; if (args.Target != null && args.BreakOnTargetMove) + { // Target should never be null if the bool is set. - doAfter.TargetPosition = Transform(args.Target.Value).Coordinates; + var targetPosition = Transform(args.Target.Value).Coordinates; + doAfter.UserPosition.TryDistance(EntityManager, targetPosition, out doAfter.TargetDistance); + } // For this we need to stay on the same hand slot and need the same item in that hand slot // (or if there is no item there we need to keep it free).