]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
DoAfter Refactor (#13225)
authorkeronshb <54602815+keronshb@users.noreply.github.com>
Sat, 25 Feb 2023 00:01:25 +0000 (19:01 -0500)
committerGitHub <noreply@github.com>
Sat, 25 Feb 2023 00:01:25 +0000 (01:01 +0100)
Co-authored-by: DrSmugleaf <drsmugleaf@gmail.com>
129 files changed:
Content.Client/DoAfter/DoAfterComponent.cs [deleted file]
Content.Client/DoAfter/DoAfterOverlay.cs
Content.Client/DoAfter/DoAfterSystem.cs
Content.Client/Medical/Cryogenics/CryoPodSystem.cs
Content.IntegrationTests/Tests/DoAfter/DoAfterServerTest.cs
Content.Server/AirlockPainter/AirlockPainterSystem.cs
Content.Server/Alert/Click/RemoveEnsnare.cs
Content.Server/Animals/Systems/UdderSystem.cs
Content.Server/Anomaly/AnomalySystem.Scanner.cs
Content.Server/Anomaly/AnomalySystem.cs
Content.Server/Anomaly/Components/AnomalyScannerComponent.cs
Content.Server/Body/Components/InternalsComponent.cs
Content.Server/Body/Systems/InternalsSystem.cs
Content.Server/Botany/Components/BotanySwabComponent.cs
Content.Server/Botany/Systems/BotanySwabSystem.cs
Content.Server/Chemistry/Components/InjectorComponent.cs
Content.Server/Chemistry/EntitySystems/ChemistrySystem.Injector.cs
Content.Server/Climbing/ClimbSystem.cs
Content.Server/Construction/AnchorableSystem.cs
Content.Server/Construction/Components/PartExchangerComponent.cs
Content.Server/Construction/ConstructionSystem.Initial.cs
Content.Server/Construction/ConstructionSystem.Interactions.cs
Content.Server/Construction/ConstructionSystem.cs
Content.Server/Construction/PartExchangerSystem.cs
Content.Server/Construction/RefiningSystem.cs
Content.Server/Cuffs/Components/CuffableComponent.cs
Content.Server/Cuffs/Components/HandcuffComponent.cs
Content.Server/Disease/Components/DiseaseSwabComponent.cs
Content.Server/Disease/Components/DiseaseVaccineComponent.cs
Content.Server/Disease/DiseaseDiagnosisSystem.cs
Content.Server/Disease/DiseaseSystem.cs
Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs
Content.Server/DoAfter/DoAfter.cs [deleted file]
Content.Server/DoAfter/DoAfterComponent.cs [deleted file]
Content.Server/DoAfter/DoAfterEventArgs.cs [deleted file]
Content.Server/DoAfter/DoAfterSystem.cs
Content.Server/Doors/Systems/DoorSystem.cs
Content.Server/Dragon/Components/DragonComponent.cs
Content.Server/Dragon/DragonSystem.cs
Content.Server/Engineering/EntitySystems/DisassembleOnAltVerbSystem.cs
Content.Server/Engineering/EntitySystems/SpawnAfterInteractSystem.cs
Content.Server/Ensnaring/EnsnareableSystem.Ensnaring.cs
Content.Server/Ensnaring/EnsnareableSystem.cs
Content.Server/Eye/Blinding/EyeProtection/EyeProtectionSystem.cs
Content.Server/Fluids/Components/SpillableComponent.cs
Content.Server/Fluids/EntitySystems/MoppingSystem.cs
Content.Server/Fluids/EntitySystems/SpillableSystem.cs
Content.Server/Forensics/Components/ForensicPadComponent.cs
Content.Server/Forensics/Systems/ForensicPadSystem.cs
Content.Server/Forensics/Systems/ForensicScannerSystem.cs
Content.Server/Gatherable/Components/GatheringToolComponent.cs
Content.Server/Gatherable/GatherableSystem.cs
Content.Server/Guardian/GuardianSystem.cs
Content.Server/Implants/ImplanterSystem.cs
Content.Server/Kitchen/EntitySystems/KitchenSpikeSystem.cs
Content.Server/Kitchen/EntitySystems/SharpSystem.cs
Content.Server/Light/Components/PoweredLightComponent.cs
Content.Server/Light/EntitySystems/PoweredLightSystem.cs
Content.Server/Magic/Components/SpellbookComponent.cs
Content.Server/Magic/MagicSystem.cs
Content.Server/Mech/Components/MechComponent.cs
Content.Server/Mech/Equipment/Components/MechGrabberComponent.cs
Content.Server/Mech/Equipment/EntitySystems/MechGrabberSystem.cs
Content.Server/Mech/Systems/MechEquipmentSystem.cs
Content.Server/Mech/Systems/MechSystem.cs
Content.Server/Medical/BiomassReclaimer/BiomassReclaimerComponent.cs
Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs
Content.Server/Medical/Components/HealingComponent.cs
Content.Server/Medical/Components/HealthAnalyzerComponent.cs
Content.Server/Medical/CryoPodSystem.cs
Content.Server/Medical/HealingSystem.cs
Content.Server/Medical/HealthAnalyzerSystem.cs
Content.Server/Medical/Stethoscope/Components/StethoscopeComponent.cs
Content.Server/Medical/Stethoscope/StethoscopeSystem.cs
Content.Server/Nuke/NukeComponent.cs
Content.Server/Nuke/NukeSystem.cs
Content.Server/Nutrition/Components/DrinkComponent.cs
Content.Server/Nutrition/Components/FoodComponent.cs
Content.Server/Nutrition/EntitySystems/DrinkSystem.cs
Content.Server/Nutrition/EntitySystems/FoodSystem.cs
Content.Server/Nutrition/EntitySystems/UtensilSystem.cs
Content.Server/Nutrition/IngestionEvents.cs
Content.Server/Power/EntitySystems/ApcSystem.cs
Content.Server/Power/EntitySystems/CableSystem.cs
Content.Server/RCD/Systems/RCDSystem.cs
Content.Server/Repairable/RepairableSystem.cs
Content.Server/Resist/CanEscapeInventoryComponent.cs
Content.Server/Resist/EscapeInventorySystem.cs
Content.Server/Resist/ResistLockerComponent.cs
Content.Server/Resist/ResistLockerSystem.cs
Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs
Content.Server/Sticky/Components/StickyComponent.cs
Content.Server/Sticky/Systems/StickySystem.cs
Content.Server/Storage/Components/BluespaceLockerComponent.cs
Content.Server/Storage/Components/ServerStorageComponent.cs
Content.Server/Storage/EntitySystems/BluespaceLockerSystem.cs
Content.Server/Storage/EntitySystems/DumpableSystem.cs
Content.Server/Storage/EntitySystems/StorageSystem.cs
Content.Server/Strip/StrippableSystem.cs
Content.Server/Teleportation/HandTeleporterSystem.cs
Content.Server/Toilet/ToiletSystem.cs
Content.Server/Tools/Components/LatticeCuttingComponent.cs
Content.Server/Tools/Components/TilePryingComponent.cs
Content.Server/Tools/Systems/WeldableSystem.cs
Content.Server/Tools/ToolSystem.LatticeCutting.cs
Content.Server/Tools/ToolSystem.TilePrying.cs
Content.Server/Tools/ToolSystem.cs
Content.Server/VendingMachines/Restock/VendingMachineRestockComponent.cs
Content.Server/VendingMachines/Restock/VendingMachineRestockSystem.cs
Content.Server/VendingMachines/VendingMachineSystem.cs
Content.Server/Wieldable/WieldableSystem.cs
Content.Server/Wires/WiresSystem.cs
Content.Shared/Construction/Components/AnchorableComponent.cs
Content.Shared/DoAfter/ActiveDoAfterComponent.cs [new file with mode: 0644]
Content.Shared/DoAfter/DoAfter.cs [new file with mode: 0644]
Content.Shared/DoAfter/DoAfterComponent.cs [new file with mode: 0644]
Content.Shared/DoAfter/DoAfterEventArgs.cs [new file with mode: 0644]
Content.Shared/DoAfter/SharedDoAfterComponent.cs [deleted file]
Content.Shared/DoAfter/SharedDoAfterSystem.cs [new file with mode: 0644]
Content.Shared/Ensnaring/Components/EnsnaringComponent.cs
Content.Shared/Mech/Equipment/Components/MechEquipmentComponent.cs
Content.Shared/Medical/Cryogenics/SharedCryoPodComponent.cs
Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs
Content.Shared/Radio/EntitySystems/EncryptionKeySystem.cs
Content.Shared/Revenant/Components/RevenantComponent.cs
Content.Shared/Storage/Components/DumpableComponent.cs
Content.Shared/Teleportation/Components/HandTeleporterComponent.cs
Content.Shared/Tools/Components/ToolComponent.cs
Content.Shared/Tools/Systems/SharedToolSystem.MultipleTool.cs

diff --git a/Content.Client/DoAfter/DoAfterComponent.cs b/Content.Client/DoAfter/DoAfterComponent.cs
deleted file mode 100644 (file)
index 8857b37..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-using Content.Shared.DoAfter;
-
-namespace Content.Client.DoAfter
-{
-    [RegisterComponent, Access(typeof(DoAfterSystem))]
-    public sealed class DoAfterComponent : SharedDoAfterComponent
-    {
-        public readonly Dictionary<byte, ClientDoAfter> DoAfters = new();
-
-        public readonly Dictionary<byte, ClientDoAfter> CancelledDoAfters = new();
-    }
-}
index 84b4fbc271ba6b9e1d779edab22700ebef5325ac..1af3d8a6b25ea5e562b013171d6fb35935371ba7 100644 (file)
@@ -1,4 +1,4 @@
-using Content.Client.Resources;
+using Content.Shared.DoAfter;
 using Robust.Client.GameObjects;
 using Robust.Client.Graphics;
 using Robust.Shared.Enums;
@@ -54,11 +54,11 @@ public sealed class DoAfterOverlay : Overlay
             var index = 0;
             var worldMatrix = Matrix3.CreateTranslation(worldPosition);
 
-            foreach (var (_, doAfter) in comp.DoAfters)
+            foreach (var doAfter in comp.DoAfters.Values)
             {
-                var elapsed = doAfter.Accumulator;
+                var elapsed = doAfter.Elapsed;
                 var displayRatio = MathF.Min(1.0f,
-                    elapsed / doAfter.Delay);
+                    (float)elapsed.TotalSeconds / doAfter.Delay);
 
                 Matrix3.Multiply(scaleMatrix, worldMatrix, out var scaledWorld);
                 Matrix3.Multiply(rotationMatrix, scaledWorld, out var matty);
@@ -94,7 +94,7 @@ public sealed class DoAfterOverlay : Overlay
                 // if we're cancelled then flick red / off.
                 if (cancelled)
                 {
-                    var flash = Math.Floor(doAfter.CancelledAccumulator / flashTime) % 2 == 0;
+                    var flash = Math.Floor((float)doAfter.CancelledElapsed.TotalSeconds / flashTime) % 2 == 0;
                     color = new Color(1f, 0f, 0f, flash ? 1f : 0f);
                 }
                 else
index d36a32d474408ca2ff8e000cb72b0d3a0324232a..c4e06376da7a0a8456e67052f02bfd433b88586f 100644 (file)
 using Content.Shared.DoAfter;
-using JetBrains.Annotations;
 using Robust.Client.Graphics;
 using Robust.Client.Player;
 using Robust.Shared.GameStates;
 using Robust.Shared.Prototypes;
-using Robust.Shared.Timing;
 using Robust.Shared.Utility;
 
-namespace Content.Client.DoAfter
+namespace Content.Client.DoAfter;
+
+/// <summary>
+/// Handles events that need to happen after a certain amount of time where the event could be cancelled by factors
+/// such as moving.
+/// </summary>
+public sealed class DoAfterSystem : SharedDoAfterSystem
 {
+    [Dependency] private readonly IOverlayManager _overlay = default!;
+    [Dependency] private readonly IPlayerManager _player = default!;
+    [Dependency] private readonly IPrototypeManager _prototype = default!;
+
     /// <summary>
-    /// Handles events that need to happen after a certain amount of time where the event could be cancelled by factors
-    /// such as moving.
+    ///     We'll use an excess time so stuff like finishing effects can show.
     /// </summary>
-    [UsedImplicitly]
-    public sealed class DoAfterSystem : EntitySystem
-    {
-        [Dependency] private readonly IGameTiming _gameTiming = default!;
-        [Dependency] private readonly IOverlayManager _overlay = default!;
-        [Dependency] private readonly IPlayerManager _player = default!;
-        [Dependency] private readonly IPrototypeManager _prototype = default!;
+    public const float ExcessTime = 0.5f;
 
-        /// <summary>
-        ///     We'll use an excess time so stuff like finishing effects can show.
-        /// </summary>
-        public const float ExcessTime = 0.5f;
+    public override void Initialize()
+    {
+        base.Initialize();
+        UpdatesOutsidePrediction = true;
+        SubscribeNetworkEvent<CancelledDoAfterMessage>(OnCancelledDoAfter);
+        SubscribeLocalEvent<DoAfterComponent, ComponentHandleState>(OnDoAfterHandleState);
+        _overlay.AddOverlay(new DoAfterOverlay(EntityManager, _prototype));
+    }
 
-        public override void Initialize()
-        {
-            base.Initialize();
-            UpdatesOutsidePrediction = true;
-            SubscribeNetworkEvent<CancelledDoAfterMessage>(OnCancelledDoAfter);
-            SubscribeLocalEvent<DoAfterComponent, ComponentHandleState>(OnDoAfterHandleState);
-            _overlay.AddOverlay(
-                new DoAfterOverlay(
-                    EntityManager,
-                    _prototype));
-        }
+    public override void Shutdown()
+    {
+        base.Shutdown();
+        _overlay.RemoveOverlay<DoAfterOverlay>();
+    }
 
-        public override void Shutdown()
-        {
-            base.Shutdown();
-            _overlay.RemoveOverlay<DoAfterOverlay>();
-        }
+    private void OnDoAfterHandleState(EntityUid uid, DoAfterComponent component, ref ComponentHandleState args)
+    {
+        if (args.Current is not DoAfterComponentState state)
+            return;
 
-        private void OnDoAfterHandleState(EntityUid uid, DoAfterComponent component, ref ComponentHandleState args)
+        foreach (var (_, doAfter) in state.DoAfters)
         {
-            if (args.Current is not DoAfterComponentState state)
-                return;
+            if (component.DoAfters.ContainsKey(doAfter.ID))
+                continue;
 
-            var toRemove = new RemQueue<ClientDoAfter>();
-
-            foreach (var (id, doAfter) in component.DoAfters)
-            {
-                var found = false;
-
-                foreach (var clientdoAfter in state.DoAfters)
-                {
-                    if (clientdoAfter.ID == id)
-                    {
-                        found = true;
-                        break;
-                    }
-                }
-
-                if (!found)
-                {
-                    toRemove.Add(doAfter);
-                }
-            }
-
-            foreach (var doAfter in toRemove)
-            {
-                Remove(component, doAfter);
-            }
-
-            foreach (var doAfter in state.DoAfters)
-            {
-                if (component.DoAfters.ContainsKey(doAfter.ID))
-                    continue;
-
-                component.DoAfters.Add(doAfter.ID, doAfter);
-            }
+            component.DoAfters.Add(doAfter.ID, doAfter);
         }
+    }
 
-        private void OnCancelledDoAfter(CancelledDoAfterMessage ev)
-        {
-            if (!TryComp<DoAfterComponent>(ev.Uid, out var doAfter))
-                return;
+    private void OnCancelledDoAfter(CancelledDoAfterMessage ev)
+    {
+        if (!TryComp<DoAfterComponent>(ev.Uid, out var doAfter))
+            return;
 
-            Cancel(doAfter, ev.ID);
-        }
+        Cancel(doAfter, ev.ID);
+    }
 
-        /// <summary>
-        ///     Remove a DoAfter without showing a cancellation graphic.
-        /// </summary>
-        public void Remove(DoAfterComponent component, ClientDoAfter clientDoAfter)
-        {
-            component.DoAfters.Remove(clientDoAfter.ID);
+    /// <summary>
+    ///     Remove a DoAfter without showing a cancellation graphic.
+    /// </summary>
+    public void Remove(DoAfterComponent component, Shared.DoAfter.DoAfter doAfter, bool found = false)
+    {
+        component.DoAfters.Remove(doAfter.ID);
+        component.CancelledDoAfters.Remove(doAfter.ID);
+    }
 
-            var found = false;
+    /// <summary>
+    ///     Mark a DoAfter as cancelled and show a cancellation graphic.
+    /// </summary>
+    ///     Actual removal is handled by DoAfterEntitySystem.
+    public void Cancel(DoAfterComponent component, byte id)
+    {
+        if (component.CancelledDoAfters.ContainsKey(id))
+            return;
 
-            component.CancelledDoAfters.Remove(clientDoAfter.ID);
+        if (!component.DoAfters.ContainsKey(id))
+            return;
 
-            if (!found)
-                component.DoAfters.Remove(clientDoAfter.ID);
-        }
-
-        /// <summary>
-        ///     Mark a DoAfter as cancelled and show a cancellation graphic.
-        /// </summary>
-        ///     Actual removal is handled by DoAfterEntitySystem.
-        public void Cancel(DoAfterComponent component, byte id)
-        {
-            if (component.CancelledDoAfters.ContainsKey(id))
-                return;
+        var doAfterMessage = component.DoAfters[id];
+        doAfterMessage.Cancelled = true;
+        doAfterMessage.CancelledTime = GameTiming.CurTime;
+        component.CancelledDoAfters.Add(id, doAfterMessage);
+    }
 
-            if (!component.DoAfters.ContainsKey(id))
-                return;
+    // TODO separate DoAfter & ActiveDoAfter components for the entity query.
+    public override void Update(float frameTime)
+    {
+        if (!GameTiming.IsFirstTimePredicted)
+            return;
 
-            var doAfterMessage = component.DoAfters[id];
-            doAfterMessage.Cancelled = true;
-            component.CancelledDoAfters.Add(id, doAfterMessage);
-        }
+        var playerEntity = _player.LocalPlayer?.ControlledEntity;
 
-        // TODO separate DoAfter & ActiveDoAfter components for the entity query.
-        public override void Update(float frameTime)
+        foreach (var (comp, xform) in EntityQuery<DoAfterComponent, TransformComponent>())
         {
-            if (!_gameTiming.IsFirstTimePredicted)
-                return;
+            var doAfters = comp.DoAfters;
 
-            var playerEntity = _player.LocalPlayer?.ControlledEntity;
+            if (doAfters.Count == 0)
+                continue;
 
-            foreach (var (comp, xform) in EntityQuery<DoAfterComponent, TransformComponent>())
-            {
-                var doAfters = comp.DoAfters;
+            var userGrid = xform.Coordinates;
+            var toRemove = new RemQueue<Shared.DoAfter.DoAfter>();
 
-                if (doAfters.Count == 0)
+            // Check cancellations / finishes
+            foreach (var (id, doAfter) in doAfters)
+            {
+                // If we've passed the final time (after the excess to show completion graphic) then remove.
+                if ((float)doAfter.Elapsed.TotalSeconds + (float)doAfter.CancelledElapsed.TotalSeconds >
+                    doAfter.Delay + ExcessTime)
                 {
+                    toRemove.Add(doAfter);
                     continue;
                 }
 
-                var userGrid = xform.Coordinates;
-                var toRemove = new RemQueue<ClientDoAfter>();
-
-                // Check cancellations / finishes
-                foreach (var (id, doAfter) in doAfters)
+                if (doAfter.Cancelled)
                 {
-                    // If we've passed the final time (after the excess to show completion graphic) then remove.
-                    if ((doAfter.Accumulator + doAfter.CancelledAccumulator) > doAfter.Delay + ExcessTime)
-                    {
-                        toRemove.Add(doAfter);
-                        continue;
-                    }
-
-                    if (doAfter.Cancelled)
-                    {
-                        doAfter.CancelledAccumulator += frameTime;
-                        continue;
-                    }
-
-                    doAfter.Accumulator += frameTime;
+                    doAfter.CancelledElapsed = GameTiming.CurTime - doAfter.CancelledTime;
+                    continue;
+                }
 
-                    // Well we finished so don't try to predict cancels.
-                    if (doAfter.Accumulator > doAfter.Delay)
-                    {
-                        continue;
-                    }
+                doAfter.Elapsed = GameTiming.CurTime - doAfter.StartTime;
 
-                    // Predictions
-                    if (comp.Owner != playerEntity)
-                        continue;
+                // Well we finished so don't try to predict cancels.
+                if ((float)doAfter.Elapsed.TotalSeconds > doAfter.Delay)
+                    continue;
 
-                    // TODO: Add these back in when I work out some system for changing the accumulation rate
-                    // based on ping. Right now these would show as cancelled near completion if we moved at the end
-                    // despite succeeding.
+                // Predictions
+                if (comp.Owner != playerEntity)
                     continue;
 
-                    if (doAfter.BreakOnUserMove)
-                    {
-                        if (!userGrid.InRange(EntityManager, doAfter.UserGrid, doAfter.MovementThreshold))
-                        {
-                            Cancel(comp, id);
-                            continue;
-                        }
-                    }
+                // TODO: Add these back in when I work out some system for changing the accumulation rate
+                // based on ping. Right now these would show as cancelled near completion if we moved at the end
+                // despite succeeding.
+                continue;
 
-                    if (doAfter.BreakOnTargetMove)
+                if (doAfter.EventArgs.BreakOnUserMove)
+                {
+                    if (!userGrid.InRange(EntityManager, doAfter.UserGrid, doAfter.EventArgs.MovementThreshold))
                     {
-                        if (!EntityManager.Deleted(doAfter.Target) &&
-                            !Transform(doAfter.Target.Value).Coordinates.InRange(EntityManager, doAfter.TargetGrid,
-                                doAfter.MovementThreshold))
-                        {
-                            Cancel(comp, id);
-                            continue;
-                        }
+                        Cancel(comp, id);
+                        continue;
                     }
                 }
 
-                foreach (var doAfter in toRemove)
-                {
-                    Remove(comp, doAfter);
-                }
-
-                // Remove cancelled DoAfters after ExcessTime has elapsed
-                var toRemoveCancelled = new List<ClientDoAfter>();
-
-                foreach (var (_, doAfter) in comp.CancelledDoAfters)
+                if (doAfter.EventArgs.BreakOnTargetMove)
                 {
-                    if (doAfter.CancelledAccumulator > ExcessTime)
+                    if (!Deleted(doAfter.EventArgs.Target) &&
+                        !Transform(doAfter.EventArgs.Target.Value).Coordinates.InRange(EntityManager,
+                            doAfter.TargetGrid,
+                            doAfter.EventArgs.MovementThreshold))
                     {
-                        toRemoveCancelled.Add(doAfter);
+                        Cancel(comp, id);
+                        continue;
                     }
                 }
+            }
 
-                foreach (var doAfter in toRemoveCancelled)
-                {
-                    Remove(comp, doAfter);
-                }
+            foreach (var doAfter in toRemove)
+            {
+                Remove(comp, doAfter);
+            }
+
+            // Remove cancelled DoAfters after ExcessTime has elapsed
+            var toRemoveCancelled = new RemQueue<Shared.DoAfter.DoAfter>();
+
+            foreach (var (_, doAfter) in comp.CancelledDoAfters)
+            {
+                var cancelledElapsed = (float)doAfter.CancelledElapsed.TotalSeconds;
+
+                if (cancelledElapsed >  ExcessTime)
+                    toRemoveCancelled.Add(doAfter);
+            }
+
+            foreach (var doAfter in toRemoveCancelled)
+            {
+                Remove(comp, doAfter);
             }
         }
     }
index b3aba4e8045cef7b1b9e0a51d561d7c536722309..fffbe3469b3936dbccb914afdece059c0762342a 100644 (file)
@@ -18,8 +18,6 @@ public sealed class CryoPodSystem: SharedCryoPodSystem
         SubscribeLocalEvent<CryoPodComponent, ComponentInit>(OnComponentInit);
         SubscribeLocalEvent<CryoPodComponent, GetVerbsEvent<AlternativeVerb>>(AddAlternativeVerbs);
         SubscribeLocalEvent<CryoPodComponent, GotEmaggedEvent>(OnEmagged);
-        SubscribeLocalEvent<CryoPodComponent, DoInsertCryoPodEvent>(DoInsertCryoPod);
-        SubscribeLocalEvent<CryoPodComponent, DoInsertCancelledCryoPodEvent>(DoInsertCancelCryoPod);
         SubscribeLocalEvent<CryoPodComponent, CryoPodPryFinished>(OnCryoPodPryFinished);
         SubscribeLocalEvent<CryoPodComponent, CryoPodPryInterrupted>(OnCryoPodPryInterrupted);
 
index 7a6c3f7f8791bdc55b02565ddf66f4281c7b79d3..186a6a6d8ca69b9ebc328da9b681e2659721e6ba 100644 (file)
@@ -1,6 +1,7 @@
 using System.Threading;
 using System.Threading.Tasks;
 using Content.Server.DoAfter;
+using Content.Shared.DoAfter;
 using NUnit.Framework;
 using Robust.Shared.GameObjects;
 using Robust.Shared.IoC;
@@ -21,17 +22,34 @@ namespace Content.IntegrationTests.Tests.DoAfter
   - type: DoAfter
 ";
 
+        public sealed class TestDoAfterSystem : EntitySystem
+        {
+            public override void Initialize()
+            {
+                SubscribeLocalEvent<DoAfterEvent<TestDoAfterData>>(OnTestDoAfterFinishEvent);
+            }
+
+            private void OnTestDoAfterFinishEvent(DoAfterEvent<TestDoAfterData> ev)
+            {
+                ev.AdditionalData.Cancelled = ev.Cancelled;
+            }
+        }
+
+        private sealed class TestDoAfterData
+        {
+            public bool Cancelled;
+        };
+
         [Test]
         public async Task TestFinished()
         {
-            Task<DoAfterStatus> task = null;
             await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
             var server = pairTracker.Pair.Server;
             await server.WaitIdleAsync();
 
-            var mapManager = server.ResolveDependency<IMapManager>();
             var entityManager = server.ResolveDependency<IEntityManager>();
             var doAfterSystem = entityManager.EntitySysManager.GetEntitySystem<DoAfterSystem>();
+            var data = new TestDoAfterData();
 
             // That it finishes successfully
             await server.WaitPost(() =>
@@ -39,15 +57,12 @@ namespace Content.IntegrationTests.Tests.DoAfter
                 var tickTime = 1.0f / IoCManager.Resolve<IGameTiming>().TickRate;
                 var mob = entityManager.SpawnEntity("Dummy", MapCoordinates.Nullspace);
                 var cancelToken = new CancellationTokenSource();
-                var args = new DoAfterEventArgs(mob, tickTime / 2, cancelToken.Token);
-                task = doAfterSystem.WaitDoAfter(args);
+                var args = new DoAfterEventArgs(mob, tickTime / 2, cancelToken.Token) { Broadcast = true };
+                doAfterSystem.DoAfter(args, data);
             });
 
             await server.WaitRunTicks(1);
-            Assert.That(task.Status, Is.EqualTo(TaskStatus.RanToCompletion));
-#pragma warning disable RA0004
-            Assert.That(task.Result == DoAfterStatus.Finished);
-#pragma warning restore RA0004
+            Assert.That(data.Cancelled, Is.False);
 
             await pairTracker.CleanReturnAsync();
         }
@@ -55,13 +70,11 @@ namespace Content.IntegrationTests.Tests.DoAfter
         [Test]
         public async Task TestCancelled()
         {
-            Task<DoAfterStatus> task = null;
-
             await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
             var server = pairTracker.Pair.Server;
             var entityManager = server.ResolveDependency<IEntityManager>();
-            var mapManager = server.ResolveDependency<IMapManager>();
             var doAfterSystem = entityManager.EntitySysManager.GetEntitySystem<DoAfterSystem>();
+            var data = new TestDoAfterData();
 
             await server.WaitPost(() =>
             {
@@ -69,16 +82,13 @@ namespace Content.IntegrationTests.Tests.DoAfter
 
                 var mob = entityManager.SpawnEntity("Dummy", MapCoordinates.Nullspace);
                 var cancelToken = new CancellationTokenSource();
-                var args = new DoAfterEventArgs(mob, tickTime * 2, cancelToken.Token);
-                task = doAfterSystem.WaitDoAfter(args);
+                var args = new DoAfterEventArgs(mob, tickTime * 2, cancelToken.Token) { Broadcast = true };
+                doAfterSystem.DoAfter(args, data);
                 cancelToken.Cancel();
             });
 
             await server.WaitRunTicks(3);
-            Assert.That(task.Status, Is.EqualTo(TaskStatus.RanToCompletion));
-#pragma warning disable RA0004
-            Assert.That(task.Result, Is.EqualTo(DoAfterStatus.Cancelled), $"Result was {task.Result}");
-#pragma warning restore RA0004
+            Assert.That(data.Cancelled, Is.False);
 
             await pairTracker.CleanReturnAsync();
         }
index f208366221fb1c5cb206fe8835a89aa3ef3fed7d..b21ee825e073c82f2fea22466ae77992349d6b8b 100644 (file)
@@ -4,12 +4,12 @@ using Content.Server.Popups;
 using Content.Server.UserInterface;
 using Content.Shared.AirlockPainter;
 using Content.Shared.AirlockPainter.Prototypes;
+using Content.Shared.DoAfter;
 using Content.Shared.Database;
 using Content.Shared.Doors.Components;
 using Content.Shared.Interaction;
 using JetBrains.Annotations;
 using Robust.Server.GameObjects;
-using Robust.Shared.Audio;
 using Robust.Shared.Player;
 
 namespace Content.Server.AirlockPainter
@@ -24,6 +24,7 @@ namespace Content.Server.AirlockPainter
         [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
         [Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
         [Dependency] private readonly PopupSystem _popupSystem = default!;
+        [Dependency] private readonly SharedAudioSystem _audio = default!;
         [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
 
         public override void Initialize()
@@ -33,27 +34,26 @@ namespace Content.Server.AirlockPainter
             SubscribeLocalEvent<AirlockPainterComponent, AfterInteractEvent>(AfterInteractOn);
             SubscribeLocalEvent<AirlockPainterComponent, ActivateInWorldEvent>(OnActivate);
             SubscribeLocalEvent<AirlockPainterComponent, AirlockPainterSpritePickedMessage>(OnSpritePicked);
-            SubscribeLocalEvent<AirlockPainterDoAfterComplete>(OnDoAfterComplete);
-            SubscribeLocalEvent<AirlockPainterDoAfterCancelled>(OnDoAfterCancelled);
+            SubscribeLocalEvent<AirlockPainterComponent, DoAfterEvent<AirlockPainterData>>(OnDoAfter);
         }
 
-        private void OnDoAfterComplete(AirlockPainterDoAfterComplete ev)
+        private void OnDoAfter(EntityUid uid, AirlockPainterComponent component, DoAfterEvent<AirlockPainterData> args)
         {
-            ev.Component.IsSpraying = false;
-            if (TryComp<AppearanceComponent>(ev.Target, out var appearance) &&
-                TryComp(ev.Target, out PaintableAirlockComponent? _))
+            if (args.Handled || args.Cancelled)
             {
-                SoundSystem.Play(ev.Component.SpraySound.GetSound(), Filter.Pvs(ev.UsedTool, entityManager:EntityManager), ev.UsedTool);
-                _appearance.SetData(ev.Target, DoorVisuals.BaseRSI, ev.Sprite, appearance);
+                component.IsSpraying = false;
+                return;
+            }
 
-                // Log success
-                _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(ev.User):user} painted {ToPrettyString(ev.Target):target}");
+            if (args.Args.Target != null)
+            {
+                _audio.Play(component.SpraySound, Filter.Pvs(uid, entityManager:EntityManager), uid, true);
+                _appearance.SetData(args.Args.Target.Value, DoorVisuals.BaseRSI, args.AdditionalData.Sprite);
+                _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(args.Args.User):user} painted {ToPrettyString(args.Args.Target.Value):target}");
+                component.IsSpraying = false;
             }
-        }
 
-        private void OnDoAfterCancelled(AirlockPainterDoAfterCancelled ev)
-        {
-            ev.Component.IsSpraying = false;
+            args.Handled = true;
         }
 
         private void OnActivate(EntityUid uid, AirlockPainterComponent component, ActivateInWorldEvent args)
@@ -87,51 +87,22 @@ namespace Content.Server.AirlockPainter
                 return;
             }
             component.IsSpraying = true;
-            var doAfterEventArgs = new DoAfterEventArgs(args.User, component.SprayTime, default, target)
+
+            var airlockPainterData = new AirlockPainterData(sprite);
+            var doAfterEventArgs = new DoAfterEventArgs(args.User, component.SprayTime, target:target, used:uid)
             {
                 BreakOnTargetMove = true,
                 BreakOnUserMove = true,
                 BreakOnDamage = true,
                 BreakOnStun = true,
                 NeedHand = true,
-                BroadcastFinishedEvent = new AirlockPainterDoAfterComplete(uid, target, sprite, component, args.User),
-                BroadcastCancelledEvent = new AirlockPainterDoAfterCancelled(component),
             };
-            _doAfterSystem.DoAfter(doAfterEventArgs);
+            _doAfterSystem.DoAfter(doAfterEventArgs, airlockPainterData);
 
             // Log attempt
             _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(args.User):user} is painting {ToPrettyString(uid):target} to '{style}' at {Transform(uid).Coordinates:targetlocation}");
         }
 
-        private sealed class AirlockPainterDoAfterComplete : EntityEventArgs
-        {
-            public readonly EntityUid User;
-            public readonly EntityUid UsedTool;
-            public readonly EntityUid Target;
-            public readonly string Sprite;
-            public readonly AirlockPainterComponent Component;
-
-            public AirlockPainterDoAfterComplete(EntityUid usedTool, EntityUid target, string sprite,
-                AirlockPainterComponent component, EntityUid user)
-            {
-                User = user;
-                UsedTool = usedTool;
-                Target = target;
-                Sprite = sprite;
-                Component = component;
-            }
-        }
-
-        private sealed class AirlockPainterDoAfterCancelled : EntityEventArgs
-        {
-            public readonly AirlockPainterComponent Component;
-
-            public AirlockPainterDoAfterCancelled(AirlockPainterComponent component)
-            {
-                Component = component;
-            }
-        }
-
         private void OnSpritePicked(EntityUid uid, AirlockPainterComponent component, AirlockPainterSpritePickedMessage args)
         {
             component.Index = args.Index;
@@ -147,5 +118,10 @@ namespace Content.Server.AirlockPainter
             _userInterfaceSystem.TrySetUiState(uid, AirlockPainterUiKey.Key,
                 new AirlockPainterBoundUserInterfaceState(component.Index));
         }
+
+        private record struct AirlockPainterData(string Sprite)
+        {
+            public string Sprite = Sprite;
+        }
     }
 }
index 7d519a6b73dfe1b32f721326325174f4adb2941b..975734152acf523ca2df4a6420d89749a9120397 100644 (file)
@@ -18,7 +18,7 @@ public sealed class RemoveEnsnare : IAlertClick
                 if (!entManager.TryGetComponent(ensnare, out EnsnaringComponent? ensnaringComponent))
                     return;
 
-                entManager.EntitySysManager.GetEntitySystem<EnsnareableSystem>().TryFree(player, ensnaringComponent);
+                entManager.EntitySysManager.GetEntitySystem<EnsnareableSystem>().TryFree(player, ensnare, ensnaringComponent);
             }
         }
     }
index 20b5fbdf231641979e5bea87fb3fd6dfbaca2999..adb8a2bf83f35f186dabe85972a48a3298aa3480 100644 (file)
@@ -4,11 +4,11 @@ using Content.Server.Chemistry.EntitySystems;
 using Content.Server.DoAfter;
 using Content.Server.Nutrition.Components;
 using Content.Server.Popups;
+using Content.Shared.DoAfter;
 using Content.Shared.IdentityManagement;
 using Content.Shared.Nutrition.Components;
 using Content.Shared.Popups;
 using Content.Shared.Verbs;
-using Robust.Shared.Player;
 
 namespace Content.Server.Animals.Systems
 {
@@ -26,10 +26,8 @@ namespace Content.Server.Animals.Systems
             base.Initialize();
 
             SubscribeLocalEvent<UdderComponent, GetVerbsEvent<AlternativeVerb>>(AddMilkVerb);
-            SubscribeLocalEvent<UdderComponent, MilkingFinishedEvent>(OnMilkingFinished);
-            SubscribeLocalEvent<UdderComponent, MilkingFailEvent>(OnMilkingFailed);
+            SubscribeLocalEvent<UdderComponent, DoAfterEvent>(OnDoAfter);
         }
-
         public override void Update(float frameTime)
         {
             foreach (var udder in EntityManager.EntityQuery<UdderComponent>(false))
@@ -74,34 +72,41 @@ namespace Content.Server.Animals.Systems
 
             udder.BeingMilked = true;
 
-            var doargs = new DoAfterEventArgs(userUid, 5, default, uid)
+            var doargs = new DoAfterEventArgs(userUid, 5, target:uid, used:containerUid)
             {
                 BreakOnUserMove = true,
                 BreakOnDamage = true,
                 BreakOnStun = true,
                 BreakOnTargetMove = true,
-                MovementThreshold = 1.0f,
-                TargetFinishedEvent = new MilkingFinishedEvent(userUid, containerUid),
-                TargetCancelledEvent = new MilkingFailEvent()
+                MovementThreshold = 1.0f
             };
 
             _doAfterSystem.DoAfter(doargs);
         }
 
-        private void OnMilkingFinished(EntityUid uid, UdderComponent udder, MilkingFinishedEvent ev)
+        private void OnDoAfter(EntityUid uid, UdderComponent component, DoAfterEvent args)
         {
-            udder.BeingMilked = false;
+            if (args.Cancelled)
+            {
+                component.BeingMilked = false;
+                return;
+            }
 
-            if (!_solutionContainerSystem.TryGetSolution(uid, udder.TargetSolutionName, out var solution))
+            if (args.Handled || args.Args.Used == null)
                 return;
 
-            if (!_solutionContainerSystem.TryGetRefillableSolution(ev.ContainerUid, out var targetSolution))
+            component.BeingMilked = false;
+
+            if (!_solutionContainerSystem.TryGetSolution(uid, component.TargetSolutionName, out var solution))
+                return;
+
+            if (!_solutionContainerSystem.TryGetRefillableSolution(args.Args.Used.Value, out var targetSolution))
                 return;
 
             var quantity = solution.Volume;
             if(quantity == 0)
             {
-                _popupSystem.PopupEntity(Loc.GetString("udder-system-dry"), uid, ev.UserUid);
+                _popupSystem.PopupEntity(Loc.GetString("udder-system-dry"), uid, args.Args.User);
                 return;
             }
 
@@ -109,15 +114,12 @@ namespace Content.Server.Animals.Systems
                 quantity = targetSolution.AvailableVolume;
 
             var split = _solutionContainerSystem.SplitSolution(uid, solution, quantity);
-            _solutionContainerSystem.TryAddSolution(ev.ContainerUid, targetSolution, split);
+            _solutionContainerSystem.TryAddSolution(args.Args.Used.Value, targetSolution, split);
 
-            _popupSystem.PopupEntity(Loc.GetString("udder-system-success", ("amount", quantity), ("target", Identity.Entity(ev.ContainerUid, EntityManager))), uid,
-                ev.UserUid, PopupType.Medium);
-        }
+            _popupSystem.PopupEntity(Loc.GetString("udder-system-success", ("amount", quantity), ("target", Identity.Entity(args.Args.Used.Value, EntityManager))), uid,
+                args.Args.User, PopupType.Medium);
 
-        private void OnMilkingFailed(EntityUid uid, UdderComponent component, MilkingFailEvent ev)
-        {
-            component.BeingMilked = false;
+            args.Handled = true;
         }
 
         private void AddMilkVerb(EntityUid uid, UdderComponent component, GetVerbsEvent<AlternativeVerb> args)
@@ -138,20 +140,5 @@ namespace Content.Server.Animals.Systems
             };
             args.Verbs.Add(verb);
         }
-
-        private sealed class MilkingFinishedEvent : EntityEventArgs
-        {
-            public EntityUid UserUid;
-            public EntityUid ContainerUid;
-
-            public MilkingFinishedEvent(EntityUid userUid, EntityUid containerUid)
-            {
-                UserUid = userUid;
-                ContainerUid = containerUid;
-            }
-        }
-
-        private sealed class MilkingFailEvent : EntityEventArgs
-        { }
     }
 }
index cdb18d41b77ce078716d2bed2a43cb244be8476a..70a3a1026834f8db46e957d1aba4210f2eb358c8 100644 (file)
@@ -1,7 +1,7 @@
 using Content.Server.Anomaly.Components;
-using Content.Server.DoAfter;
 using Content.Shared.Anomaly;
 using Content.Shared.Anomaly.Components;
+using Content.Shared.DoAfter;
 using Content.Shared.Interaction;
 using Robust.Server.GameObjects;
 using Robust.Shared.Utility;
@@ -17,8 +17,7 @@ public sealed partial class AnomalySystem
     {
         SubscribeLocalEvent<AnomalyScannerComponent, BoundUIOpenedEvent>(OnScannerUiOpened);
         SubscribeLocalEvent<AnomalyScannerComponent, AfterInteractEvent>(OnScannerAfterInteract);
-        SubscribeLocalEvent<AnomalyScannerComponent, AnomalyScanFinishedEvent>(OnScannerDoAfterFinished);
-        SubscribeLocalEvent<AnomalyScannerComponent, AnomalyScanCancelledEvent>(OnScannerDoAfterCancelled);
+        SubscribeLocalEvent<AnomalyScannerComponent, DoAfterEvent>(OnDoAfter);
 
         SubscribeLocalEvent<AnomalyShutdownEvent>(OnScannerAnomalyShutdown);
         SubscribeLocalEvent<AnomalySeverityChangedEvent>(OnScannerAnomalySeverityChanged);
@@ -73,38 +72,29 @@ public sealed partial class AnomalySystem
 
     private void OnScannerAfterInteract(EntityUid uid, AnomalyScannerComponent component, AfterInteractEvent args)
     {
-        if (component.TokenSource != null)
-            return;
-
         if (args.Target is not { } target)
             return;
         if (!HasComp<AnomalyComponent>(target))
             return;
 
-        component.TokenSource = new();
-        _doAfter.DoAfter(new DoAfterEventArgs(args.User, component.ScanDoAfterDuration, component.TokenSource.Token, target, uid)
+        _doAfter.DoAfter(new DoAfterEventArgs(args.User, component.ScanDoAfterDuration, target:target, used:uid)
         {
-            DistanceThreshold = 2f,
-            UsedFinishedEvent = new AnomalyScanFinishedEvent(target, args.User),
-            UsedCancelledEvent = new AnomalyScanCancelledEvent()
+            DistanceThreshold = 2f
         });
     }
 
-    private void OnScannerDoAfterFinished(EntityUid uid, AnomalyScannerComponent component, AnomalyScanFinishedEvent args)
+    private void OnDoAfter(EntityUid uid, AnomalyScannerComponent component, DoAfterEvent args)
     {
-        component.TokenSource = null;
+        if (args.Cancelled || args.Handled || args.Args.Target == null)
+            return;
 
         Audio.PlayPvs(component.CompleteSound, uid);
         Popup.PopupEntity(Loc.GetString("anomaly-scanner-component-scan-complete"), uid);
-        UpdateScannerWithNewAnomaly(uid, args.Anomaly, component);
+        UpdateScannerWithNewAnomaly(uid, args.Args.Target.Value, component);
 
-        if (TryComp<ActorComponent>(args.User, out var actor))
-            _ui.TryOpen(uid, AnomalyScannerUiKey.Key, actor.PlayerSession);
-    }
+        if (TryComp<ActorComponent>(args.Args.User, out var actor)) _ui.TryOpen(uid, AnomalyScannerUiKey.Key, actor.PlayerSession);
 
-    private void OnScannerDoAfterCancelled(EntityUid uid, AnomalyScannerComponent component, AnomalyScanCancelledEvent args)
-    {
-        component.TokenSource = null;
+        args.Handled = true;
     }
 
     public void UpdateScannerUi(EntityUid uid, AnomalyScannerComponent? component = null)
index 3de46afb46b423e043337e9f9331409ba185465f..3e7122695f4838e701aeecbe9f55f613123d4e2c 100644 (file)
@@ -7,6 +7,7 @@ using Content.Server.Materials;
 using Content.Server.Radio.EntitySystems;
 using Content.Shared.Anomaly;
 using Content.Shared.Anomaly.Components;
+using Content.Shared.DoAfter;
 using Robust.Server.GameObjects;
 using Robust.Shared.Configuration;
 using Robust.Shared.Physics.Events;
index 6fd8c1787dde572b509bbe0a83d22ccf2bdb5aa0..cf40950224fc4ba623ae5a14556a1eb004b77681 100644 (file)
@@ -22,28 +22,9 @@ public sealed class AnomalyScannerComponent : Component
     [DataField("scanDoAfterDuration")]
     public float ScanDoAfterDuration = 5;
 
-    public CancellationTokenSource? TokenSource;
-
     /// <summary>
     /// The sound plays when the scan finished
     /// </summary>
     [DataField("completeSound")]
     public SoundSpecifier? CompleteSound = new SoundPathSpecifier("/Audio/Items/beep.ogg");
 }
-
-public sealed class AnomalyScanFinishedEvent : EntityEventArgs
-{
-    public EntityUid Anomaly;
-
-    public EntityUid User;
-
-    public AnomalyScanFinishedEvent(EntityUid anomaly, EntityUid user)
-    {
-        Anomaly = anomaly;
-        User = user;
-    }
-}
-
-public sealed class AnomalyScanCancelledEvent : EntityEventArgs
-{
-}
index abac2fe8921b00e57c821c067215ef14e85ad79c..6fa1732127cb6f535d72c7d72848b651815ad5a7 100644 (file)
@@ -16,7 +16,5 @@ namespace Content.Server.Body.Components
         [ViewVariables(VVAccess.ReadWrite)]
         [DataField("delay")]
         public float Delay = 3;
-
-        public CancellationTokenSource? CancelToken = null;
     }
 }
index bf5807b0c97d001fd3823a79adcfaa568f0f8279..db9aff7e887ce35d5cdf9927a974fa21f3185913 100644 (file)
@@ -10,7 +10,7 @@ using Robust.Shared.Prototypes;
 using Content.Shared.Verbs;
 using Content.Server.Popups;
 using Content.Server.DoAfter;
-using System.Threading;
+using Content.Shared.DoAfter;
 
 namespace Content.Server.Body.Systems;
 
@@ -33,8 +33,7 @@ public sealed class InternalsSystem : EntitySystem
         SubscribeLocalEvent<InternalsComponent, ComponentStartup>(OnInternalsStartup);
         SubscribeLocalEvent<InternalsComponent, ComponentShutdown>(OnInternalsShutdown);
         SubscribeLocalEvent<InternalsComponent, GetVerbsEvent<InteractionVerb>>(OnGetInteractionVerbs);
-        SubscribeLocalEvent<InternalsComponent, ToggleOtherInternalsCompleteEvent>(OnToggleOtherInternalsComplete);
-        SubscribeLocalEvent<InternalsComponent, ToggleOtherInternalsCancelledEvent>(OnToggleOtherInternalCanceled);
+        SubscribeLocalEvent<InternalsComponent, DoAfterEvent<InternalsData>>(OnDoAfter);
     }
 
     private void OnGetInteractionVerbs(EntityUid uid, InternalsComponent component, GetVerbsEvent<InteractionVerb> args)
@@ -59,9 +58,7 @@ public sealed class InternalsSystem : EntitySystem
     public void ToggleInternals(EntityUid uid, EntityUid user, bool force, InternalsComponent? internals = null)
     {
         if (!Resolve(uid, ref internals, false))
-        {
             return;
-        }
 
         // Toggle off if they're on
         if (AreInternalsWorking(internals))
@@ -77,7 +74,7 @@ public sealed class InternalsSystem : EntitySystem
             return;
         }
 
-        var tank = FindBestGasTank(internals);
+        var tank = FindBestGasTank(uid ,internals);
 
         if (tank == null)
         {
@@ -85,24 +82,26 @@ public sealed class InternalsSystem : EntitySystem
             return;
         }
 
+        var isUser = uid == user;
+
+        var internalsData = new InternalsData();
+
         if (!force)
         {
             // Is the target not you? If yes, use a do-after to give them time to respond.
             //If no, do a short delay. There's no reason it should be beyond 1 second.
-            var delay = uid != user ? internals.Delay : 1.0f;
+            var delay = !isUser ? internals.Delay : 1.0f;
 
-            internals.CancelToken?.Cancel();
-            internals.CancelToken = new CancellationTokenSource();
-            _doAfter.DoAfter(new DoAfterEventArgs(user, delay, internals.CancelToken.Token, uid)
+            _doAfter.DoAfter(new DoAfterEventArgs(user, delay, target:uid)
             {
                 BreakOnUserMove = true,
                 BreakOnDamage = true,
                 BreakOnStun = true,
                 BreakOnTargetMove = true,
                 MovementThreshold = 0.1f,
-                TargetFinishedEvent = new ToggleOtherInternalsCompleteEvent(user, tank),
-                TargetCancelledEvent = new ToggleOtherInternalsCancelledEvent(),
-            });
+                RaiseOnUser = isUser,
+                RaiseOnTarget = !isUser
+            }, internalsData);
 
             return;
         }
@@ -110,20 +109,19 @@ public sealed class InternalsSystem : EntitySystem
         _gasTank.ConnectToInternals(tank);
     }
 
-    private void OnToggleOtherInternalsComplete(EntityUid uid, InternalsComponent component, ToggleOtherInternalsCompleteEvent ev)
+    private void OnDoAfter(EntityUid uid, InternalsComponent component, DoAfterEvent<InternalsData> args)
     {
-        component.CancelToken = null;
-        ToggleInternals(uid, ev.User, true, component);
-    }
+        if (args.Cancelled || args.Handled)
+            return;
 
-    private static void OnToggleOtherInternalCanceled(EntityUid uid, InternalsComponent component, ToggleOtherInternalsCancelledEvent ev)
-    {
-        component.CancelToken = null;
+        ToggleInternals(uid, args.Args.User, true, component);
+
+        args.Handled = true;
     }
 
     private void OnInternalsStartup(EntityUid uid, InternalsComponent component, ComponentStartup args)
     {
-        _alerts.ShowAlert(component.Owner, AlertType.Internals, GetSeverity(component));
+        _alerts.ShowAlert(uid, AlertType.Internals, GetSeverity(component));
     }
 
     private void OnInternalsShutdown(EntityUid uid, InternalsComponent component, ComponentShutdown args)
@@ -138,7 +136,7 @@ public sealed class InternalsSystem : EntitySystem
             var gasTank = Comp<GasTankComponent>(component.GasTankEntity!.Value);
             args.Gas = _gasTank.RemoveAirVolume(gasTank, Atmospherics.BreathVolume);
             // TODO: Should listen to gas tank updates instead I guess?
-            _alerts.ShowAlert(component.Owner, AlertType.Internals, GetSeverity(component));
+            _alerts.ShowAlert(uid, AlertType.Internals, GetSeverity(component));
         }
     }
     public void DisconnectBreathTool(InternalsComponent component)
@@ -168,12 +166,11 @@ public sealed class InternalsSystem : EntitySystem
 
     public void DisconnectTank(InternalsComponent? component)
     {
-        if (component == null) return;
+        if (component == null)
+            return;
 
         if (TryComp(component.GasTankEntity, out GasTankComponent? tank))
-        {
             _gasTank.DisconnectFromInternals(tank);
-        }
 
         component.GasTankEntity = null;
         _alerts.ShowAlert(component.Owner, AlertType.Internals, GetSeverity(component));
@@ -185,9 +182,7 @@ public sealed class InternalsSystem : EntitySystem
             return false;
 
         if (TryComp(component.GasTankEntity, out GasTankComponent? tank))
-        {
             _gasTank.DisconnectFromInternals(tank);
-        }
 
         component.GasTankEntity = tankEntity;
         _alerts.ShowAlert(component.Owner, AlertType.Internals, GetSeverity(component));
@@ -203,7 +198,8 @@ public sealed class InternalsSystem : EntitySystem
 
     private short GetSeverity(InternalsComponent component)
     {
-        if (component.BreathToolEntity == null || !AreInternalsWorking(component)) return 2;
+        if (component.BreathToolEntity == null || !AreInternalsWorking(component))
+            return 2;
 
         // If pressure in the tank is below low pressure threshhold, flash warning on internals UI
         if (TryComp<GasTankComponent>(component.GasTankEntity, out var gasTank) && gasTank.IsLowPressure)
@@ -212,7 +208,7 @@ public sealed class InternalsSystem : EntitySystem
         return 1;
     }
 
-    public GasTankComponent? FindBestGasTank(InternalsComponent component)
+    public GasTankComponent? FindBestGasTank(EntityUid internalsOwner, InternalsComponent component)
     {
         // Prioritise
         // 1. back equipped tanks
@@ -222,14 +218,14 @@ public sealed class InternalsSystem : EntitySystem
         InventoryComponent? inventory = null;
         ContainerManagerComponent? containerManager = null;
 
-        if (_inventory.TryGetSlotEntity(component.Owner, "back", out var backEntity, inventory, containerManager) &&
+        if (_inventory.TryGetSlotEntity(internalsOwner, "back", out var backEntity, inventory, containerManager) &&
             TryComp<GasTankComponent>(backEntity, out var backGasTank) &&
             _gasTank.CanConnectToInternals(backGasTank))
         {
             return backGasTank;
         }
 
-        if (_inventory.TryGetSlotEntity(component.Owner, "suitstorage", out var entity, inventory, containerManager) &&
+        if (_inventory.TryGetSlotEntity(internalsOwner, "suitstorage", out var entity, inventory, containerManager) &&
             TryComp<GasTankComponent>(entity, out var gasTank) &&
             _gasTank.CanConnectToInternals(gasTank))
         {
@@ -238,12 +234,10 @@ public sealed class InternalsSystem : EntitySystem
 
         var tanks = new List<GasTankComponent>();
 
-        foreach (var hand in _hands.EnumerateHands(component.Owner))
+        foreach (var hand in _hands.EnumerateHands(internalsOwner))
         {
             if (TryComp(hand.HeldEntity, out gasTank) && _gasTank.CanConnectToInternals(gasTank))
-            {
                 tanks.Add(gasTank);
-            }
         }
 
         if (tanks.Count > 0)
@@ -252,16 +246,14 @@ public sealed class InternalsSystem : EntitySystem
             return tanks[0];
         }
 
-        if (Resolve(component.Owner, ref inventory, false))
+        if (Resolve(internalsOwner, ref inventory, false))
         {
-            var enumerator = new InventorySystem.ContainerSlotEnumerator(component.Owner, inventory.TemplateId, _protoManager, _inventory, SlotFlags.POCKET | SlotFlags.BELT);
+            var enumerator = new InventorySystem.ContainerSlotEnumerator(internalsOwner, inventory.TemplateId, _protoManager, _inventory, SlotFlags.POCKET | SlotFlags.BELT);
 
             while (enumerator.MoveNext(out var container))
             {
                 if (TryComp(container.ContainedEntity, out gasTank) && _gasTank.CanConnectToInternals(gasTank))
-                {
                     tanks.Add(gasTank);
-                }
             }
 
             if (tanks.Count > 0)
@@ -273,11 +265,9 @@ public sealed class InternalsSystem : EntitySystem
 
         return null;
     }
-    private readonly record struct ToggleOtherInternalsCompleteEvent(EntityUid User, GasTankComponent Tank)
+
+    private record struct InternalsData
     {
-        public readonly EntityUid User = User;
-        public readonly GasTankComponent Tank = Tank;
-    }
 
-    private readonly record struct ToggleOtherInternalsCancelledEvent;
+    }
 }
index d2b1b7057a88a4e353cd26469048abec60191271..e772d3b49251d4f1317a7963adc26b66e029597f 100644 (file)
@@ -11,11 +11,6 @@ namespace Content.Server.Botany
         [DataField("swabDelay")]
         public float SwabDelay = 2f;
 
-        /// <summary>
-        /// Token for interrupting swabbing do after.
-        /// </summary>
-        public CancellationTokenSource? CancelToken;
-
         /// <summary>
         /// SeedData from the first plant that got swabbed.
         /// </summary>
index f4417ed622582678d0475f3642d57951cb4744d1..09b6c38ccc5d73492997659db1882a44feab5d11 100644 (file)
-using System.Threading;
 using Content.Server.Botany.Components;
 using Content.Server.DoAfter;
-using Content.Server.Hands.Components;
-using Content.Server.Nutrition.EntitySystems;
 using Content.Server.Popups;
+using Content.Shared.DoAfter;
 using Content.Shared.Examine;
 using Content.Shared.Interaction;
-using Content.Shared.Inventory;
-using Content.Shared.Tools.Components;
-using Robust.Shared.Audio;
-using Robust.Shared.Player;
-using Robust.Shared.Random;
-using Robust.Shared.Utility;
 
-namespace Content.Server.Botany.Systems
+namespace Content.Server.Botany.Systems;
+
+public sealed class BotanySwabSystem : EntitySystem
 {
-    public sealed class BotanySwabSystem : EntitySystem
-    {
-        [Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
-        [Dependency] private readonly PopupSystem _popupSystem = default!;
-        [Dependency] private readonly MutationSystem _mutationSystem = default!;
+    [Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
+    [Dependency] private readonly PopupSystem _popupSystem = default!;
+    [Dependency] private readonly MutationSystem _mutationSystem = default!;
 
-        public override void Initialize()
-        {
-            base.Initialize();
-            SubscribeLocalEvent<BotanySwabComponent, AfterInteractEvent>(OnAfterInteract);
-            SubscribeLocalEvent<BotanySwabComponent, ExaminedEvent>(OnExamined);
-            // Private Events
-            SubscribeLocalEvent<TargetSwabSuccessfulEvent>(OnTargetSwabSuccessful);
-            SubscribeLocalEvent<SwabCancelledEvent>(OnSwabCancelled);
-        }
+    public override void Initialize()
+    {
+        base.Initialize();
+        SubscribeLocalEvent<BotanySwabComponent, ExaminedEvent>(OnExamined);
+        SubscribeLocalEvent<BotanySwabComponent, AfterInteractEvent>(OnAfterInteract);
+        SubscribeLocalEvent<DoAfterEvent>(OnDoAfter);
+    }
 
-        /// <summary>
-        /// Handles swabbing a plant.
-        /// </summary>
-        private void OnAfterInteract(EntityUid uid, BotanySwabComponent swab, AfterInteractEvent args)
+    /// <summary>
+    /// This handles swab examination text
+    /// so you can tell if they are used or not.
+    /// </summary>
+    private void OnExamined(EntityUid uid, BotanySwabComponent swab, ExaminedEvent args)
+    {
+        if (args.IsInDetailsRange)
         {
-            if (swab.CancelToken != null)
-            {
-                swab.CancelToken.Cancel();
-                swab.CancelToken = null;
-                return;
-            }
-
-            if (args.Target == null || !args.CanReach)
-                return;
-
-            if (!TryComp<PlantHolderComponent>(args.Target, out var plant))
-                return;
-
-            swab.CancelToken = new CancellationTokenSource();
-            _doAfterSystem.DoAfter(new DoAfterEventArgs(args.User, swab.SwabDelay, swab.CancelToken.Token, target: args.Target)
-            {
-                BroadcastFinishedEvent = new TargetSwabSuccessfulEvent(args.User, args.Target, swab, plant),
-                BroadcastCancelledEvent = new SwabCancelledEvent(swab),
-                BreakOnTargetMove = true,
-                BreakOnUserMove = true,
-                BreakOnStun = true,
-                NeedHand = true
-            });
+            if (swab.SeedData != null)
+                args.PushMarkup(Loc.GetString("swab-used"));
+            else
+                args.PushMarkup(Loc.GetString("swab-unused"));
         }
+    }
 
-        /// <summary>
-        /// This handles swab examination text
-        /// so you can tell if they are used or not.
-        /// </summary>
-        private void OnExamined(EntityUid uid, BotanySwabComponent swab, ExaminedEvent args)
-        {
-            if (args.IsInDetailsRange)
-            {
-                if (swab.SeedData != null)
-                    args.PushMarkup(Loc.GetString("swab-used"));
-                else
-                    args.PushMarkup(Loc.GetString("swab-unused"));
-            }
-        }
+    /// <summary>
+    /// Handles swabbing a plant.
+    /// </summary>
+    private void OnAfterInteract(EntityUid uid, BotanySwabComponent swab, AfterInteractEvent args)
+    {
+        if (args.Target == null || !args.CanReach)
+            return;
 
-        /// <summary>
-        /// Save seed data or cross-pollenate.
-        /// </summary>
-        private void OnTargetSwabSuccessful(TargetSwabSuccessfulEvent args)
+        _doAfterSystem.DoAfter(new DoAfterEventArgs(args.User, swab.SwabDelay, target: args.Target, used: uid)
         {
-            if (args.Target == null)
-                return;
-
-            if (args.Swab.SeedData == null)
-            {
-                // Pick up pollen
-                args.Swab.SeedData = args.Plant.Seed;
-                _popupSystem.PopupEntity(Loc.GetString("botany-swab-from"), args.Target.Value, args.User);
-            }
-            else
-            {
-                var old = args.Plant.Seed; // Save old plant pollen
-                if (old == null)
-                    return;
-                args.Plant.Seed = _mutationSystem.Cross(args.Swab.SeedData, old); // Cross-pollenate
-                args.Swab.SeedData = old; // Transfer old plant pollen to swab
-                _popupSystem.PopupEntity(Loc.GetString("botany-swab-to"), args.Target.Value, args.User);
-            }
+            Broadcast = true,
+            BreakOnTargetMove = true,
+            BreakOnUserMove = true,
+            BreakOnStun = true,
+            NeedHand = true
+        });
+    }
 
-            if (args.Swab.CancelToken != null)
-            {
-                args.Swab.CancelToken.Cancel();
-                args.Swab.CancelToken = null;
-            }
-        }
+    /// <summary>
+    /// Save seed data or cross-pollenate.
+    /// </summary>
+    private void OnDoAfter(DoAfterEvent args)
+    {
+        if (args.Cancelled || args.Handled || !TryComp<PlantHolderComponent>(args.Args.Target, out var plant) || !TryComp<BotanySwabComponent>(args.Args.Used, out var swab))
+            return;
 
-        private static void OnSwabCancelled(SwabCancelledEvent args)
+        if (swab.SeedData == null)
         {
-            args.Swab.CancelToken = null;
+            // Pick up pollen
+            swab.SeedData = plant.Seed;
+            _popupSystem.PopupEntity(Loc.GetString("botany-swab-from"), args.Args.Target.Value, args.Args.User);
         }
-
-        private sealed class SwabCancelledEvent : EntityEventArgs
+        else
         {
-            public readonly BotanySwabComponent Swab;
-            public SwabCancelledEvent(BotanySwabComponent swab)
-            {
-                Swab = swab;
-            }
+            var old = plant.Seed;
+            if (old == null)
+                return;
+            plant.Seed = _mutationSystem.Cross(swab.SeedData, old); // Cross-pollenate
+            swab.SeedData = old; // Transfer old plant pollen to swab
+            _popupSystem.PopupEntity(Loc.GetString("botany-swab-to"), args.Args.Target.Value, args.Args.User);
         }
 
-        private sealed class TargetSwabSuccessfulEvent : EntityEventArgs
-        {
-            public EntityUid User { get; }
-            public EntityUid? Target { get; }
-            public BotanySwabComponent Swab { get; }
-
-            public PlantHolderComponent Plant { get; }
-
-            public TargetSwabSuccessfulEvent(EntityUid user, EntityUid? target, BotanySwabComponent swab, PlantHolderComponent plant)
-            {
-                User = user;
-                Target = target;
-                Swab = swab;
-                Plant = plant;
-            }
-        }
+        args.Handled = true;
     }
 }
 
index d86fe329e6b1f71d4575d8acf6867cd40a5e31b6..e9d6b7c42ea13e71b1f8c97c62ec7a744f207078 100644 (file)
@@ -1,4 +1,3 @@
-using System.Threading;
 using Content.Shared.Chemistry.Components;
 using Content.Shared.FixedPoint;
 
@@ -63,12 +62,6 @@ namespace Content.Server.Chemistry.Components
         [DataField("delay")]
         public float Delay = 5;
 
-        /// <summary>
-        ///     Token for interrupting a do-after action (e.g., injection another player). If not null, implies
-        ///     component is currently "in use".
-        /// </summary>
-        public CancellationTokenSource? CancelToken;
-
         [DataField("toggleState")] private InjectorToggleMode _toggleState;
 
         /// <summary>
index b60215893bb328beb5be5d2839b90bbd617e8fdb..b011f39f5d89c1d710dcd7772c1db458421f3d9e 100644 (file)
@@ -1,19 +1,15 @@
 using Content.Server.Body.Components;
 using Content.Server.Chemistry.Components;
 using Content.Server.Chemistry.Components.SolutionManager;
-using Content.Server.CombatMode;
-using Content.Server.DoAfter;
 using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.Database;
 using Content.Shared.FixedPoint;
-using Content.Shared.Hands;
 using Content.Shared.IdentityManagement;
 using Content.Shared.Interaction;
 using Content.Shared.Interaction.Events;
 using Robust.Shared.GameStates;
-using Robust.Shared.Player;
-using System.Threading;
+using Content.Shared.DoAfter;
 using Content.Shared.Mobs.Components;
 using Content.Shared.Verbs;
 using Robust.Server.GameObjects;
@@ -32,14 +28,11 @@ public sealed partial class ChemistrySystem
     {
         SubscribeLocalEvent<InjectorComponent, GetVerbsEvent<AlternativeVerb>>(AddSetTransferVerbs);
         SubscribeLocalEvent<InjectorComponent, SolutionChangedEvent>(OnSolutionChange);
-        SubscribeLocalEvent<InjectorComponent, HandDeselectedEvent>(OnInjectorDeselected);
+        SubscribeLocalEvent<InjectorComponent, DoAfterEvent>(OnInjectDoAfter);
         SubscribeLocalEvent<InjectorComponent, ComponentStartup>(OnInjectorStartup);
         SubscribeLocalEvent<InjectorComponent, UseInHandEvent>(OnInjectorUse);
         SubscribeLocalEvent<InjectorComponent, AfterInteractEvent>(OnInjectorAfterInteract);
         SubscribeLocalEvent<InjectorComponent, ComponentGetState>(OnInjectorGetState);
-
-        SubscribeLocalEvent<InjectionCompleteEvent>(OnInjectionComplete);
-        SubscribeLocalEvent<InjectionCancelledEvent>(OnInjectionCancelled);
     }
 
     private void AddSetTransferVerbs(EntityUid uid, InjectorComponent component, GetVerbsEvent<AlternativeVerb> args)
@@ -63,7 +56,7 @@ public sealed partial class ChemistrySystem
             verb.Act = () =>
             {
                 component.TransferAmount = FixedPoint2.New(amount);
-                args.User.PopupMessage(Loc.GetString("comp-solution-transfer-set-amount", ("amount", amount)));
+                _popup.PopupEntity(Loc.GetString("comp-solution-transfer-set-amount", ("amount", amount)), args.User, args.User);
             };
 
             // we want to sort by size, not alphabetically by the verb text.
@@ -74,38 +67,27 @@ public sealed partial class ChemistrySystem
         }
     }
 
-    private static void OnInjectionCancelled(InjectionCancelledEvent ev)
-    {
-        ev.Component.CancelToken = null;
-    }
-
-    private void OnInjectionComplete(InjectionCompleteEvent ev)
-    {
-        ev.Component.CancelToken = null;
-        UseInjector(ev.Target, ev.User, ev.Component);
-    }
-
-    private void UseInjector(EntityUid target, EntityUid user, InjectorComponent component)
+    private void UseInjector(EntityUid target, EntityUid user, EntityUid injector, InjectorComponent component)
     {
         // Handle injecting/drawing for solutions
         if (component.ToggleState == SharedInjectorComponent.InjectorToggleMode.Inject)
         {
             if (_solutions.TryGetInjectableSolution(target, out var injectableSolution))
             {
-                TryInject(component, target, injectableSolution, user, false);
+                TryInject(component, injector, target, injectableSolution, user, false);
             }
             else if (_solutions.TryGetRefillableSolution(target, out var refillableSolution))
             {
-                TryInject(component, target, refillableSolution, user, true);
+                TryInject(component, injector, target, refillableSolution, user, true);
             }
             else if (TryComp<BloodstreamComponent>(target, out var bloodstream))
             {
-                TryInjectIntoBloodstream(component, bloodstream, user);
+                TryInjectIntoBloodstream(component, injector, target, bloodstream, user);
             }
             else
             {
                 _popup.PopupEntity(Loc.GetString("injector-component-cannot-transfer-message",
-                    ("target", Identity.Entity(target, EntityManager))), component.Owner, user);
+                    ("target", Identity.Entity(target, EntityManager))), injector, user);
             }
         }
         else if (component.ToggleState == SharedInjectorComponent.InjectorToggleMode.Draw)
@@ -113,29 +95,23 @@ public sealed partial class ChemistrySystem
             // Draw from a bloodstream, if the target has that
             if (TryComp<BloodstreamComponent>(target, out var stream))
             {
-                TryDraw(component, target, stream.BloodSolution, user, stream);
+                TryDraw(component, injector, target, stream.BloodSolution, user, stream);
                 return;
             }
 
             // Draw from an object (food, beaker, etc)
             if (_solutions.TryGetDrawableSolution(target, out var drawableSolution))
             {
-                TryDraw(component, target, drawableSolution, user);
+                TryDraw(component, injector, target, drawableSolution, user);
             }
             else
             {
                 _popup.PopupEntity(Loc.GetString("injector-component-cannot-draw-message",
-                    ("target", Identity.Entity(target, EntityManager))), component.Owner, user);
+                    ("target", Identity.Entity(target, EntityManager))), injector, user);
             }
         }
     }
 
-    private static void OnInjectorDeselected(EntityUid uid, InjectorComponent component, HandDeselectedEvent args)
-    {
-        component.CancelToken?.Cancel();
-        component.CancelToken = null;
-    }
-
     private void OnSolutionChange(EntityUid uid, InjectorComponent component, SolutionChangedEvent args)
     {
         Dirty(component);
@@ -151,38 +127,38 @@ public sealed partial class ChemistrySystem
         args.State = new SharedInjectorComponent.InjectorComponentState(currentVolume, maxVolume, component.ToggleState);
     }
 
-    private void OnInjectorAfterInteract(EntityUid uid, InjectorComponent component, AfterInteractEvent args)
+    private void OnInjectDoAfter(EntityUid uid, InjectorComponent component, DoAfterEvent args)
     {
-        if (args.Handled || !args.CanReach)
+        if (args.Handled || args.Cancelled || args.Args.Target == null)
             return;
 
-        if (component.CancelToken != null)
-        {
-            args.Handled = true;
+        UseInjector(args.Args.Target.Value, args.Args.User, uid, component);
+
+        args.Handled = true;
+    }
+
+    private void OnInjectorAfterInteract(EntityUid uid, InjectorComponent component, AfterInteractEvent args)
+    {
+        if (args.Handled || !args.CanReach)
             return;
-        }
 
         //Make sure we have the attacking entity
-        if (args.Target is not { Valid: true } target ||
-            !HasComp<SolutionContainerManagerComponent>(uid))
-        {
+        if (args.Target is not { Valid: true } target || !HasComp<SolutionContainerManagerComponent>(uid))
             return;
-        }
 
         // Is the target a mob? If yes, use a do-after to give them time to respond.
-        if (HasComp<MobStateComponent>(target) ||
-            HasComp<BloodstreamComponent>(target))
+        if (HasComp<MobStateComponent>(target) || HasComp<BloodstreamComponent>(target))
         {
             // Are use using an injector capible of targeting a mob?
             if (component.IgnoreMobs)
                 return;
 
-            InjectDoAfter(component, args.User, target);
+            InjectDoAfter(component, args.User, target, uid);
             args.Handled = true;
             return;
         }
 
-        UseInjector(target, args.User, component);
+        UseInjector(target, args.User, uid, component);
         args.Handled = true;
     }
 
@@ -197,14 +173,14 @@ public sealed partial class ChemistrySystem
         if (args.Handled)
             return;
 
-        Toggle(component, args.User);
+        Toggle(component, args.User, uid);
         args.Handled = true;
     }
 
     /// <summary>
     /// Toggle between draw/inject state if applicable
     /// </summary>
-    private void Toggle(InjectorComponent component, EntityUid user)
+    private void Toggle(InjectorComponent component, EntityUid user, EntityUid injector)
     {
         if (component.InjectOnly)
         {
@@ -226,18 +202,18 @@ public sealed partial class ChemistrySystem
                 throw new ArgumentOutOfRangeException();
         }
 
-        _popup.PopupEntity(Loc.GetString(msg), component.Owner, user);
+        _popup.PopupEntity(Loc.GetString(msg), injector, user);
     }
 
     /// <summary>
     /// Send informative pop-up messages and wait for a do-after to complete.
     /// </summary>
-    private void InjectDoAfter(InjectorComponent component, EntityUid user, EntityUid target)
+    private void InjectDoAfter(InjectorComponent component, EntityUid user, EntityUid target, EntityUid injector)
     {
         // Create a pop-up for the user
         _popup.PopupEntity(Loc.GetString("injector-component-injecting-user"), target, user);
 
-        if (!_solutions.TryGetSolution(component.Owner, InjectorComponent.SolutionName, out var solution))
+        if (!_solutions.TryGetSolution(injector, InjectorComponent.SolutionName, out var solution))
             return;
 
         var actualDelay = MathF.Max(component.Delay, 1f);
@@ -277,62 +253,48 @@ public sealed partial class ChemistrySystem
             actualDelay /= 2;
 
             if (component.ToggleState == SharedInjectorComponent.InjectorToggleMode.Inject)
-                _adminLogger.Add(LogType.Ingestion,
-                    $"{EntityManager.ToPrettyString(user):user} is attempting to inject themselves with a solution {SolutionContainerSystem.ToPrettyString(solution):solution}.");
+                _adminLogger.Add(LogType.Ingestion, $"{EntityManager.ToPrettyString(user):user} is attempting to inject themselves with a solution {SolutionContainerSystem.ToPrettyString(solution):solution}.");
         }
 
-        component.CancelToken = new CancellationTokenSource();
-
-        _doAfter.DoAfter(new DoAfterEventArgs(user, actualDelay, component.CancelToken.Token, target)
+        _doAfter.DoAfter(new DoAfterEventArgs(user, actualDelay, target:target, used:injector)
         {
             BreakOnUserMove = true,
             BreakOnDamage = true,
             BreakOnStun = true,
             BreakOnTargetMove = true,
-            MovementThreshold = 0.1f,
-            BroadcastFinishedEvent = new InjectionCompleteEvent()
-            {
-                Component = component,
-                User = user,
-                Target = target,
-            },
-            BroadcastCancelledEvent = new InjectionCancelledEvent()
-            {
-                Component = component,
-            }
+            MovementThreshold = 0.1f
         });
     }
 
-    private void TryInjectIntoBloodstream(InjectorComponent component, BloodstreamComponent targetBloodstream, EntityUid user)
+    private void TryInjectIntoBloodstream(InjectorComponent component, EntityUid injector, EntityUid target, BloodstreamComponent targetBloodstream, EntityUid user)
     {
         // Get transfer amount. May be smaller than _transferAmount if not enough room
         var realTransferAmount = FixedPoint2.Min(component.TransferAmount, targetBloodstream.ChemicalSolution.AvailableVolume);
 
         if (realTransferAmount <= 0)
         {
-            _popup.PopupEntity(Loc.GetString("injector-component-cannot-inject-message", ("target", Identity.Entity(targetBloodstream.Owner, EntityManager))),
-                component.Owner, user);
+            _popup.PopupEntity(Loc.GetString("injector-component-cannot-inject-message", ("target", Identity.Entity(target, EntityManager))), injector, user);
             return;
         }
 
         // Move units from attackSolution to targetSolution
         var removedSolution = _solutions.SplitSolution(user, targetBloodstream.ChemicalSolution, realTransferAmount);
 
-        _blood.TryAddToChemicals((targetBloodstream).Owner, removedSolution, targetBloodstream);
+        _blood.TryAddToChemicals(target, removedSolution, targetBloodstream);
 
-        removedSolution.DoEntityReaction(targetBloodstream.Owner, ReactionMethod.Injection);
+        _reactiveSystem.DoEntityReaction(target, removedSolution, ReactionMethod.Injection);
 
         _popup.PopupEntity(Loc.GetString("injector-component-inject-success-message",
                 ("amount", removedSolution.Volume),
-                ("target", Identity.Entity(targetBloodstream.Owner, EntityManager))), component.Owner, user);
+                ("target", Identity.Entity(target, EntityManager))), injector, user);
 
         Dirty(component);
-        AfterInject(component);
+        AfterInject(component, injector);
     }
 
-    private void TryInject(InjectorComponent component, EntityUid targetEntity, Solution targetSolution, EntityUid user, bool asRefill)
+    private void TryInject(InjectorComponent component, EntityUid injector, EntityUid targetEntity, Solution targetSolution, EntityUid user, bool asRefill)
     {
-        if (!_solutions.TryGetSolution(component.Owner, InjectorComponent.SolutionName, out var solution)
+        if (!_solutions.TryGetSolution(injector, InjectorComponent.SolutionName, out var solution)
             || solution.Volume == 0)
         {
             return;
@@ -344,14 +306,14 @@ public sealed partial class ChemistrySystem
         if (realTransferAmount <= 0)
         {
             _popup.PopupEntity(Loc.GetString("injector-component-target-already-full-message", ("target", Identity.Entity(targetEntity, EntityManager))),
-                component.Owner, user);
+                injector, user);
             return;
         }
 
         // Move units from attackSolution to targetSolution
-        var removedSolution = _solutions.SplitSolution(component.Owner, solution, realTransferAmount);
+        var removedSolution = _solutions.SplitSolution(injector, solution, realTransferAmount);
 
-        removedSolution.DoEntityReaction(targetEntity, ReactionMethod.Injection);
+        _reactiveSystem.DoEntityReaction(targetEntity, removedSolution, ReactionMethod.Injection);
 
         if (!asRefill)
         {
@@ -364,35 +326,35 @@ public sealed partial class ChemistrySystem
 
         _popup.PopupEntity(Loc.GetString("injector-component-transfer-success-message",
                 ("amount", removedSolution.Volume),
-                ("target", Identity.Entity(targetEntity, EntityManager))), component.Owner, user);
+                ("target", Identity.Entity(targetEntity, EntityManager))), injector, user);
 
         Dirty(component);
-        AfterInject(component);
+        AfterInject(component, injector);
     }
 
-    private void AfterInject(InjectorComponent component)
+    private void AfterInject(InjectorComponent component, EntityUid injector)
     {
         // Automatically set syringe to draw after completely draining it.
-        if (_solutions.TryGetSolution(component.Owner, InjectorComponent.SolutionName, out var solution)
+        if (_solutions.TryGetSolution(injector, InjectorComponent.SolutionName, out var solution)
             && solution.Volume == 0)
         {
             component.ToggleState = SharedInjectorComponent.InjectorToggleMode.Draw;
         }
     }
 
-    private void AfterDraw(InjectorComponent component)
+    private void AfterDraw(InjectorComponent component, EntityUid injector)
     {
         // Automatically set syringe to inject after completely filling it.
-        if (_solutions.TryGetSolution(component.Owner, InjectorComponent.SolutionName, out var solution)
+        if (_solutions.TryGetSolution(injector, InjectorComponent.SolutionName, out var solution)
             && solution.AvailableVolume == 0)
         {
             component.ToggleState = SharedInjectorComponent.InjectorToggleMode.Inject;
         }
     }
 
-    private void TryDraw(InjectorComponent component, EntityUid targetEntity, Solution targetSolution, EntityUid user, BloodstreamComponent? stream = null)
+    private void TryDraw(InjectorComponent component, EntityUid injector, EntityUid targetEntity, Solution targetSolution, EntityUid user, BloodstreamComponent? stream = null)
     {
-        if (!_solutions.TryGetSolution(component.Owner, InjectorComponent.SolutionName, out var solution)
+        if (!_solutions.TryGetSolution(injector, InjectorComponent.SolutionName, out var solution)
             || solution.AvailableVolume == 0)
         {
             return;
@@ -404,34 +366,34 @@ public sealed partial class ChemistrySystem
         if (realTransferAmount <= 0)
         {
             _popup.PopupEntity(Loc.GetString("injector-component-target-is-empty-message", ("target", Identity.Entity(targetEntity, EntityManager))),
-                component.Owner, user);
+                injector, user);
             return;
         }
 
         // We have some snowflaked behavior for streams.
         if (stream != null)
         {
-            DrawFromBlood(user, targetEntity, component, solution, stream, realTransferAmount);
+            DrawFromBlood(user, injector, targetEntity, component, solution, stream, realTransferAmount);
             return;
         }
 
         // Move units from attackSolution to targetSolution
         var removedSolution = _solutions.Draw(targetEntity, targetSolution, realTransferAmount);
 
-        if (!_solutions.TryAddSolution(component.Owner, solution, removedSolution))
+        if (!_solutions.TryAddSolution(injector, solution, removedSolution))
         {
             return;
         }
 
         _popup.PopupEntity(Loc.GetString("injector-component-draw-success-message",
                 ("amount", removedSolution.Volume),
-                ("target", Identity.Entity(targetEntity, EntityManager))), component.Owner, user);
+                ("target", Identity.Entity(targetEntity, EntityManager))), injector, user);
 
         Dirty(component);
-        AfterDraw(component);
+        AfterDraw(component, injector);
     }
 
-    private void DrawFromBlood(EntityUid user, EntityUid target, InjectorComponent component, Solution injectorSolution, BloodstreamComponent stream, FixedPoint2 transferAmount)
+    private void DrawFromBlood(EntityUid user, EntityUid injector, EntityUid target, InjectorComponent component, Solution injectorSolution, BloodstreamComponent stream, FixedPoint2 transferAmount)
     {
         var drawAmount = (float) transferAmount;
         var bloodAmount = drawAmount;
@@ -445,25 +407,14 @@ public sealed partial class ChemistrySystem
         var bloodTemp = stream.BloodSolution.SplitSolution(bloodAmount);
         var chemTemp = stream.ChemicalSolution.SplitSolution(chemAmount);
 
-        _solutions.TryAddSolution(component.Owner, injectorSolution, bloodTemp);
-        _solutions.TryAddSolution(component.Owner, injectorSolution, chemTemp);
+        _solutions.TryAddSolution(injector, injectorSolution, bloodTemp);
+        _solutions.TryAddSolution(injector, injectorSolution, chemTemp);
 
         _popup.PopupEntity(Loc.GetString("injector-component-draw-success-message",
                 ("amount", transferAmount),
-                ("target", Identity.Entity(target, EntityManager))), component.Owner, user);
+                ("target", Identity.Entity(target, EntityManager))), injector, user);
 
         Dirty(component);
-        AfterDraw(component);
-    }
-    private sealed class InjectionCompleteEvent : EntityEventArgs
-    {
-        public InjectorComponent Component { get; init; } = default!;
-        public EntityUid User { get; init; }
-        public EntityUid Target { get; init; }
-    }
-
-    private sealed class InjectionCancelledEvent : EntityEventArgs
-    {
-        public InjectorComponent Component { get; init; } = default!;
+        AfterDraw(component, injector);
     }
 }
index 6f11cb0351033d141ffaa9ca2004fbf5fa0c34c0..1908b2b17f83f59058c8f77ad1743052cb807724 100644 (file)
@@ -11,6 +11,7 @@ using Content.Shared.Buckle.Components;
 using Content.Shared.Climbing;
 using Content.Shared.Climbing.Events;
 using Content.Shared.Damage;
+using Content.Shared.DoAfter;
 using Content.Shared.DragDrop;
 using Content.Shared.GameTicking;
 using Content.Shared.IdentityManagement;
@@ -18,8 +19,6 @@ using Content.Shared.Physics;
 using Content.Shared.Popups;
 using Content.Shared.Verbs;
 using JetBrains.Annotations;
-using Robust.Server.GameObjects;
-using Robust.Shared.Configuration;
 using Robust.Shared.GameStates;
 using Robust.Shared.Physics;
 using Robust.Shared.Physics.Collision.Shapes;
@@ -34,7 +33,6 @@ namespace Content.Server.Climbing;
 [UsedImplicitly]
 public sealed class ClimbSystem : SharedClimbSystem
 {
-    [Dependency] private readonly IConfigurationManager _cfg = default!;
     [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
     [Dependency] private readonly BodySystem _bodySystem = default!;
     [Dependency] private readonly DamageableSystem _damageableSystem = default!;
@@ -43,7 +41,6 @@ public sealed class ClimbSystem : SharedClimbSystem
     [Dependency] private readonly PopupSystem _popupSystem = default!;
     [Dependency] private readonly InteractionSystem _interactionSystem = default!;
     [Dependency] private readonly StunSystem _stunSystem = default!;
-    [Dependency] private readonly AudioSystem _audioSystem = default!;
     [Dependency] private readonly SharedPhysicsSystem _physics = default!;
     [Dependency] private readonly BonkSystem _bonkSystem = default!;
 
@@ -60,7 +57,7 @@ public sealed class ClimbSystem : SharedClimbSystem
         SubscribeLocalEvent<ClimbableComponent, GetVerbsEvent<AlternativeVerb>>(AddClimbableVerb);
         SubscribeLocalEvent<ClimbableComponent, DragDropTargetEvent>(OnClimbableDragDrop);
 
-        SubscribeLocalEvent<ClimbingComponent, ClimbFinishedEvent>(OnClimbFinished);
+        SubscribeLocalEvent<ClimbingComponent, DoAfterEvent<ClimbExtraEvent>>(OnDoAfter);
         SubscribeLocalEvent<ClimbingComponent, EndCollideEvent>(OnClimbEndCollide);
         SubscribeLocalEvent<ClimbingComponent, BuckleChangeEvent>(OnBuckleChange);
         SubscribeLocalEvent<ClimbingComponent, ComponentGetState>(OnClimbingGetState);
@@ -117,19 +114,29 @@ public sealed class ClimbSystem : SharedClimbSystem
         if (_bonkSystem.TryBonk(entityToMove, climbable))
             return;
 
-        _doAfterSystem.DoAfter(new DoAfterEventArgs(user, component.ClimbDelay, default, climbable, entityToMove)
+        var ev = new ClimbExtraEvent();
+
+        var args = new DoAfterEventArgs(user, component.ClimbDelay, target: climbable, used: entityToMove)
         {
             BreakOnTargetMove = true,
             BreakOnUserMove = true,
             BreakOnDamage = true,
             BreakOnStun = true,
-            UsedFinishedEvent = new ClimbFinishedEvent(user, climbable, entityToMove)
-        });
+            RaiseOnUser = user == entityToMove,
+            RaiseOnTarget = user != entityToMove
+        };
+
+        _doAfterSystem.DoAfter(args, ev);
     }
 
-    private void OnClimbFinished(EntityUid uid, ClimbingComponent climbing, ClimbFinishedEvent args)
+    private void OnDoAfter(EntityUid uid, ClimbingComponent component, DoAfterEvent<ClimbExtraEvent> args)
     {
-        Climb(uid, args.User, args.Instigator, args.Climbable, climbing: climbing);
+        if (args.Handled || args.Cancelled || args.Args.Target == null || args.Args.Used == null)
+            return;
+
+        Climb(uid, args.Args.User, args.Args.Used.Value, args.Args.Target.Value, climbing: component);
+
+        args.Handled = true;
     }
 
     private void Climb(EntityUid uid, EntityUid user, EntityUid instigator, EntityUid climbable, bool silent = false, ClimbingComponent? climbing = null,
@@ -428,20 +435,11 @@ public sealed class ClimbSystem : SharedClimbSystem
     {
         _fixtureRemoveQueue.Clear();
     }
-}
 
-internal sealed class ClimbFinishedEvent : EntityEventArgs
-{
-    public ClimbFinishedEvent(EntityUid user, EntityUid climbable, EntityUid instigator)
+    private sealed class ClimbExtraEvent : EntityEventArgs
     {
-        User = user;
-        Climbable = climbable;
-        Instigator = instigator;
+        //Honestly this is only here because otherwise this activates on every single doafter on a human
     }
-
-    public EntityUid User { get; }
-    public EntityUid Climbable { get; }
-    public EntityUid Instigator { get; }
 }
 
 /// <summary>
index 0a2ab04fcb275a891fcd0d6c6f735578ce4a8809..571d09e2a39bca5e2ecb224c3796398025e0a16c 100644 (file)
@@ -1,19 +1,16 @@
-using System.Threading;
 using Content.Server.Administration.Logs;
 using Content.Server.Coordinates.Helpers;
 using Content.Server.Popups;
 using Content.Server.Pulling;
-using Content.Server.Tools;
 using Content.Shared.Construction.Components;
 using Content.Shared.Construction.EntitySystems;
 using Content.Shared.Database;
 using Content.Shared.Examine;
 using Content.Shared.Pulling.Components;
+using Content.Shared.Tools;
 using Content.Shared.Tools.Components;
 using Robust.Shared.Map;
-using Robust.Shared.Physics;
 using Robust.Shared.Physics.Components;
-using Robust.Shared.Player;
 
 namespace Content.Server.Construction
 {
@@ -22,16 +19,14 @@ namespace Content.Server.Construction
         [Dependency] private readonly IAdminLogManager _adminLogger = default!;
         [Dependency] private readonly IMapManager _mapManager = default!;
         [Dependency] private readonly PopupSystem _popup = default!;
-        [Dependency] private readonly ToolSystem _tool = default!;
+        [Dependency] private readonly SharedToolSystem _tool = default!;
         [Dependency] private readonly PullingSystem _pulling = default!;
 
         public override void Initialize()
         {
             base.Initialize();
             SubscribeLocalEvent<AnchorableComponent, TryAnchorCompletedEvent>(OnAnchorComplete);
-            SubscribeLocalEvent<AnchorableComponent, TryAnchorCancelledEvent>(OnAnchorCancelled);
             SubscribeLocalEvent<AnchorableComponent, TryUnanchorCompletedEvent>(OnUnanchorComplete);
-            SubscribeLocalEvent<AnchorableComponent, TryUnanchorCancelledEvent>(OnUnanchorCancelled);
             SubscribeLocalEvent<AnchorableComponent, ExaminedEvent>(OnAnchoredExamine);
         }
 
@@ -42,14 +37,8 @@ namespace Content.Server.Construction
             args.PushMarkup(Loc.GetString(messageId, ("target", uid)));
         }
 
-        private void OnUnanchorCancelled(EntityUid uid, AnchorableComponent component, TryUnanchorCancelledEvent args)
-        {
-            component.CancelToken = null;
-        }
-
         private void OnUnanchorComplete(EntityUid uid, AnchorableComponent component, TryUnanchorCompletedEvent args)
         {
-            component.CancelToken = null;
             var xform = Transform(uid);
 
             RaiseLocalEvent(uid, new BeforeUnanchoredEvent(args.User, args.Using));
@@ -65,14 +54,8 @@ namespace Content.Server.Construction
             );
         }
 
-        private void OnAnchorCancelled(EntityUid uid, AnchorableComponent component, TryAnchorCancelledEvent args)
-        {
-            component.CancelToken = null;
-        }
-
         private void OnAnchorComplete(EntityUid uid, AnchorableComponent component, TryAnchorCompletedEvent args)
         {
-            component.CancelToken = null;
             var xform = Transform(uid);
             if (TryComp<PhysicsComponent>(uid, out var anchorBody) &&
                 !TileFree(xform.Coordinates, anchorBody))
@@ -144,8 +127,7 @@ namespace Content.Server.Construction
         /// <returns>true if it is valid, false otherwise</returns>
         private bool Valid(EntityUid uid, EntityUid userUid, EntityUid usingUid, bool anchoring, AnchorableComponent? anchorable = null, ToolComponent? usingTool = null)
         {
-            if (!Resolve(uid, ref anchorable) ||
-                anchorable.CancelToken != null)
+            if (!Resolve(uid, ref anchorable))
                 return false;
 
             if (!Resolve(usingUid, ref usingTool))
@@ -194,10 +176,8 @@ namespace Content.Server.Construction
                 return;
             }
 
-            anchorable.CancelToken = new CancellationTokenSource();
-
-            _tool.UseTool(usingUid, userUid, uid, 0f, anchorable.Delay, usingTool.Qualities,
-                new TryAnchorCompletedEvent(userUid, usingUid), new TryAnchorCancelledEvent(userUid, usingUid), uid, cancelToken: anchorable.CancelToken.Token);
+            var toolEvData = new ToolEventData(new TryAnchorCompletedEvent(userUid, usingUid), targetEntity:uid);
+            _tool.UseTool(usingUid, userUid, uid, anchorable.Delay, usingTool.Qualities, toolEvData);
         }
 
         /// <summary>
@@ -209,18 +189,17 @@ namespace Content.Server.Construction
             TransformComponent? transform = null,
             ToolComponent? usingTool = null)
         {
-            if (!Resolve(uid, ref anchorable, ref transform) ||
-                anchorable.CancelToken != null)
+            if (!Resolve(uid, ref anchorable, ref transform))
                 return;
 
-            if (!Resolve(usingUid, ref usingTool)) return;
-
-            if (!Valid(uid, userUid, usingUid, false)) return;
+            if (!Resolve(usingUid, ref usingTool))
+                return;
 
-            anchorable.CancelToken = new CancellationTokenSource();
+            if (!Valid(uid, userUid, usingUid, false))
+                return;
 
-            _tool.UseTool(usingUid, userUid, uid, 0f, anchorable.Delay, usingTool.Qualities,
-                new TryUnanchorCompletedEvent(userUid, usingUid), new TryUnanchorCancelledEvent(userUid, usingUid), uid, cancelToken: anchorable.CancelToken.Token);
+            var toolEvData = new ToolEventData(new TryUnanchorCompletedEvent(userUid, usingUid), targetEntity:uid);
+            _tool.UseTool(usingUid, userUid, uid, anchorable.Delay, usingTool.Qualities, toolEvData);
         }
 
         /// <summary>
@@ -271,12 +250,6 @@ namespace Content.Server.Construction
             }
         }
 
-        private sealed class TryUnanchorCancelledEvent : AnchorEvent
-        {
-            public TryUnanchorCancelledEvent(EntityUid userUid, EntityUid usingUid) : base(userUid, usingUid)
-            {
-            }
-        }
 
         private sealed class TryAnchorCompletedEvent : AnchorEvent
         {
@@ -284,12 +257,5 @@ namespace Content.Server.Construction
             {
             }
         }
-
-        private sealed class TryAnchorCancelledEvent : AnchorEvent
-        {
-            public TryAnchorCancelledEvent(EntityUid userUid, EntityUid usingUid) : base(userUid, usingUid)
-            {
-            }
-        }
     }
 }
index d51a6609c07c065f6622afddcbc9ba14bab7ee39..fb7d3a89c6e2078da53da5c3ecc7fcc03f3cb00e 100644 (file)
@@ -27,6 +27,4 @@ public sealed class PartExchangerComponent : Component
     public SoundSpecifier ExchangeSound = new SoundPathSpecifier("/Audio/Items/rped.ogg");
 
     public IPlayingAudioStream? AudioStream;
-
-    public CancellationTokenSource? Token;
 }
index 721dd11fc7043d4d3fc5116706fd3d008dc576e0..e72848031a14c2095235f6b15b36332c909e4c5b 100644 (file)
@@ -11,6 +11,7 @@ using Content.Shared.Construction.Prototypes;
 using Content.Shared.Construction.Steps;
 using Content.Shared.Coordinates;
 using Content.Shared.Database;
+using Content.Shared.DoAfter;
 using Content.Shared.Hands.EntitySystems;
 using Content.Shared.Interaction;
 using Content.Shared.Inventory;
index 503303bf4be0251d9219ef53337706cd8c178ad4..7634bdf00cdeaf7e2af253aba98b5596417bd774 100644 (file)
@@ -1,15 +1,15 @@
 using Content.Server.Administration.Logs;
 using Content.Server.Construction.Components;
-using Content.Server.DoAfter;
 using Content.Server.Temperature.Components;
 using Content.Server.Temperature.Systems;
 using Content.Shared.Construction;
-using Content.Shared.Construction.EntitySystems;
 using Content.Shared.Construction.Steps;
-using Content.Shared.Database;
+using Content.Shared.DoAfter;
 using Content.Shared.Interaction;
+using Content.Shared.Tools.Components;
 using Robust.Shared.Containers;
 #if EXCEPTION_TOLERANCE
+// ReSharper disable once RedundantUsingDirective
 using Robust.Shared.Exceptions;
 #endif
 
@@ -36,6 +36,7 @@ namespace Content.Server.Construction
             SubscribeLocalEvent<ConstructionDoAfterCancelled>(OnDoAfterCancelled);
             SubscribeLocalEvent<ConstructionComponent, ConstructionDoAfterComplete>(EnqueueEvent);
             SubscribeLocalEvent<ConstructionComponent, ConstructionDoAfterCancelled>(EnqueueEvent);
+            SubscribeLocalEvent<ConstructionComponent, DoAfterEvent<ConstructionData>>(OnDoAfter);
 
             #endregion
 
@@ -300,20 +301,19 @@ namespace Content.Server.Construction
                     // If we still haven't completed this step's DoAfter...
                     if (doAfterState == DoAfterState.None && insertStep.DoAfter > 0)
                     {
-                        _doAfterSystem.DoAfter(
-                            new DoAfterEventArgs(interactUsing.User, step.DoAfter, default, interactUsing.Target)
-                            {
-                                BreakOnDamage = false,
-                                BreakOnStun = true,
-                                BreakOnTargetMove = true,
-                                BreakOnUserMove = true,
-                                NeedHand = true,
-
-                                // These events will be broadcast and handled by this very same system, that will
-                                // raise them directed to the target. These events wrap the original event.
-                                BroadcastFinishedEvent = new ConstructionDoAfterComplete(uid, ev),
-                                BroadcastCancelledEvent = new ConstructionDoAfterCancelled(uid, ev)
-                            });
+                        // These events will be broadcast and handled by this very same system, that will
+                        // raise them directed to the target. These events wrap the original event.
+                        var constructionData = new ConstructionData(new ConstructionDoAfterComplete(uid, ev), new ConstructionDoAfterCancelled(uid, ev));
+                        var doAfterEventArgs = new DoAfterEventArgs(interactUsing.User, step.DoAfter, target: interactUsing.Target)
+                        {
+                            BreakOnDamage = false,
+                            BreakOnStun = true,
+                            BreakOnTargetMove = true,
+                            BreakOnUserMove = true,
+                            NeedHand = true
+                        };
+
+                        _doAfterSystem.DoAfter(doAfterEventArgs, constructionData);
 
                         // To properly signal that we're waiting for a DoAfter, we have to set the flag on the component
                         // and then also return the DoAfter HandleResult.
@@ -375,9 +375,9 @@ namespace Content.Server.Construction
                     if (doAfterState != DoAfterState.None)
                         return doAfterState == DoAfterState.Completed ? HandleResult.True : HandleResult.False;
 
-                    if (!_toolSystem.UseTool(interactUsing.Used, interactUsing.User,
-                        uid, toolInsertStep.Fuel, toolInsertStep.DoAfter, toolInsertStep.Tool,
-                        new ConstructionDoAfterComplete(uid, ev), new ConstructionDoAfterCancelled(uid, ev)))
+                    var toolEvData = new ToolEventData(new ConstructionDoAfterComplete(uid, ev), toolInsertStep.Fuel, new ConstructionDoAfterCancelled(uid, ev));
+
+                    if(!_toolSystem.UseTool(interactUsing.Used, interactUsing.User, uid, toolInsertStep.DoAfter, new [] {toolInsertStep.Tool}, toolEvData))
                         return HandleResult.False;
 
                     // In the case we're not waiting for a doAfter, then this step is complete!
@@ -546,6 +546,21 @@ namespace Content.Server.Construction
             _constructionUpdateQueue.Add(uid);
         }
 
+        private void OnDoAfter(EntityUid uid, ConstructionComponent component, DoAfterEvent<ConstructionData> args)
+        {
+            if (!Exists(args.Args.Target) || args.Handled)
+                return;
+
+            if (args.Cancelled)
+            {
+                RaiseLocalEvent(args.Args.Target.Value, args.AdditionalData.CancelEvent);
+                args.Handled = true;
+            }
+
+            RaiseLocalEvent(args.Args.Target.Value, args.AdditionalData.CompleteEvent);
+            args.Handled = true;
+        }
+
         private void OnDoAfterComplete(ConstructionDoAfterComplete ev)
         {
             // Make extra sure the target entity exists...
@@ -570,6 +585,18 @@ namespace Content.Server.Construction
 
         #region Event Definitions
 
+        private sealed class ConstructionData
+        {
+            public readonly object CompleteEvent;
+            public readonly object CancelEvent;
+
+            public ConstructionData(object completeEvent, object cancelEvent)
+            {
+                CompleteEvent = completeEvent;
+                CancelEvent = cancelEvent;
+            }
+        }
+
         /// <summary>
         ///     This event signals that a construction interaction's DoAfter has completed successfully.
         ///     This wraps the original event and also keeps some custom data that event handlers might need.
index 2de4145829e52fcd7c480f2359a331e6976f2acc..45b8f232df23ec632dedfa5f40d8c187b9956652 100644 (file)
@@ -1,8 +1,8 @@
 using Content.Server.Construction.Components;
 using Content.Server.DoAfter;
 using Content.Server.Stack;
-using Content.Server.Tools;
 using Content.Shared.Construction;
+using Content.Shared.Tools;
 using JetBrains.Annotations;
 using Robust.Server.Containers;
 using Robust.Shared.Prototypes;
@@ -22,7 +22,7 @@ namespace Content.Server.Construction
         [Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
         [Dependency] private readonly ContainerSystem _container = default!;
         [Dependency] private readonly StackSystem _stackSystem = default!;
-        [Dependency] private readonly ToolSystem _toolSystem = default!;
+        [Dependency] private readonly SharedToolSystem _toolSystem = default!;
 
         private const string SawmillName = "Construction";
         private ISawmill _sawmill = default!;
index 7389353a27f3567f57289c1fb69b58549df4a7ef..837bf808dbda0e2477e1ded10df72504bd1d9198 100644 (file)
@@ -1,15 +1,14 @@
 using System.Linq;
-using System.Threading;
 using Content.Server.Construction.Components;
 using Content.Server.DoAfter;
 using Content.Server.Storage.Components;
 using Content.Server.Storage.EntitySystems;
 using Content.Server.Wires;
+using Content.Shared.DoAfter;
 using Content.Shared.Construction.Components;
 using Content.Shared.Interaction;
 using Content.Shared.Popups;
 using Robust.Shared.Containers;
-using Robust.Shared.Player;
 using Robust.Shared.Utility;
 
 namespace Content.Server.Construction;
@@ -27,16 +26,20 @@ public sealed class PartExchangerSystem : EntitySystem
     public override void Initialize()
     {
         SubscribeLocalEvent<PartExchangerComponent, AfterInteractEvent>(OnAfterInteract);
-        SubscribeLocalEvent<PartExchangerComponent, RpedExchangeFinishedEvent>(OnFinished);
-        SubscribeLocalEvent<PartExchangerComponent, RpedExchangeCancelledEvent>(OnCancelled);
+        SubscribeLocalEvent<PartExchangerComponent, DoAfterEvent>(OnDoAfter);
     }
 
-    private void OnFinished(EntityUid uid, PartExchangerComponent component, RpedExchangeFinishedEvent args)
+    private void OnDoAfter(EntityUid uid, PartExchangerComponent component, DoAfterEvent args)
     {
-        component.Token = null;
+        if (args.Cancelled || args.Handled || args.Args.Target == null)
+        {
+            component.AudioStream?.Stop();
+            return;
+        }
+
         component.AudioStream?.Stop();
 
-        if (!TryComp<MachineComponent>(args.Target, out var machine))
+        if (!TryComp<MachineComponent>(args.Args.Target.Value, out var machine))
             return;
 
         if (!TryComp<ServerStorageComponent>(uid, out var storage) || storage.Storage == null)
@@ -59,7 +62,7 @@ public sealed class PartExchangerSystem : EntitySystem
             if (TryComp<MachinePartComponent>(ent, out var part))
             {
                 machineParts.Add(part);
-                _container.RemoveEntity(machine.Owner, ent);
+                _container.RemoveEntity(args.Args.Target.Value, ent);
             }
         }
 
@@ -85,19 +88,12 @@ public sealed class PartExchangerSystem : EntitySystem
             _storage.Insert(uid, unused.Owner, null, false);
         }
         _construction.RefreshParts(machine);
-    }
 
-    private void OnCancelled(EntityUid uid, PartExchangerComponent component, RpedExchangeCancelledEvent args)
-    {
-        component.Token = null;
-        component.AudioStream?.Stop();
+        args.Handled = true;
     }
 
     private void OnAfterInteract(EntityUid uid, PartExchangerComponent component, AfterInteractEvent args)
     {
-        if (component.Token != null)
-            return;
-
         if (component.DoDistanceCheck && !args.CanReach)
             return;
 
@@ -116,28 +112,11 @@ public sealed class PartExchangerSystem : EntitySystem
 
         component.AudioStream = _audio.PlayPvs(component.ExchangeSound, uid);
 
-        component.Token = new CancellationTokenSource();
-        _doAfter.DoAfter(new DoAfterEventArgs(args.User, component.ExchangeDuration, component.Token.Token, args.Target, args.Used)
+        _doAfter.DoAfter(new DoAfterEventArgs(args.User, component.ExchangeDuration, target:args.Target, used:args.Used)
         {
             BreakOnDamage = true,
             BreakOnStun = true,
-            BreakOnUserMove = true,
-            UsedFinishedEvent = new RpedExchangeFinishedEvent(args.Target.Value),
-            UsedCancelledEvent = new RpedExchangeCancelledEvent()
+            BreakOnUserMove = true
         });
     }
 }
-
-public sealed class RpedExchangeFinishedEvent : EntityEventArgs
-{
-    public readonly EntityUid Target;
-
-    public RpedExchangeFinishedEvent(EntityUid target)
-    {
-        Target = target;
-    }
-}
-
-public readonly struct RpedExchangeCancelledEvent
-{
-}
index 644b8fec7a787a4181b2258cb1ba8937c4e54631..73b4f8d760e6eb57d394ff491982873587ad7118 100644 (file)
@@ -1,15 +1,15 @@
 using Content.Server.Construction.Components;
-using Content.Server.Tools;
 using Content.Server.Stack;
 using Content.Shared.Interaction;
 using Content.Shared.Stacks;
+using Content.Shared.Tools;
 using Content.Shared.Tools.Components;
 
 namespace Content.Server.Construction
 {
     public sealed class RefiningSystem : EntitySystem
     {
-        [Dependency] private readonly ToolSystem _toolSystem = default!;
+        [Dependency] private readonly SharedToolSystem _toolSystem = default!;
         [Dependency] private readonly StackSystem _stackSystem = default!;
         public override void Initialize()
         {
@@ -29,7 +29,9 @@ namespace Content.Server.Construction
 
             component.BeingWelded = true;
 
-            if (!await _toolSystem.UseTool(args.Used, args.User, uid, component.RefineFuel, component.RefineTime, component.QualityNeeded))
+            var toolEvData = new ToolEventData(null);
+
+            if (!_toolSystem.UseTool(args.Used, args.User, uid, component.RefineTime, component.QualityNeeded, toolEvData, component.RefineFuel))
             {
                 // failed to veld - abort refine
                 component.BeingWelded = false;
index 2e6194ca12d3f859d8cffe4eff8c6ddfdcf25d84..8d36b33f84b5e27541fa9458467c9d53f461853d 100644 (file)
@@ -18,6 +18,7 @@ using Robust.Shared.Audio;
 using Robust.Shared.Containers;
 using Robust.Shared.Player;
 using Content.Server.Recycling.Components;
+using Content.Shared.DoAfter;
 using Robust.Shared.Map;
 
 namespace Content.Server.Cuffs.Components
index 793595d9412e70b07a2a326b33fb97cd78e52888..e8dda6cd5599b997e2c569db785ffb7bf6d7f248 100644 (file)
@@ -3,6 +3,7 @@ using Content.Server.Administration.Logs;
 using Content.Server.DoAfter;
 using Content.Shared.Cuffs.Components;
 using Content.Shared.Database;
+using Content.Shared.DoAfter;
 using Content.Shared.Popups;
 using Content.Shared.Stunnable;
 using Robust.Shared.Audio;
index e55417f1c2c0be3e2ecf6df03badf4b460b7e715..a4e6c0aa7762a5461755e5ec4d4069f2d65c6b69 100644 (file)
@@ -1,4 +1,3 @@
-using System.Threading;
 using Content.Shared.Disease;
 
 namespace Content.Server.Disease.Components
@@ -20,10 +19,6 @@ namespace Content.Server.Disease.Components
         /// </summary>
         public bool Used = false;
         /// <summary>
-        /// Token for interrupting swabbing do after.
-        /// </summary>
-        public CancellationTokenSource? CancelToken;
-        /// <summary>
         /// The disease prototype currently on the swab
         /// </summary>
         [ViewVariables]
index fc2349729f8dc016d2209d58e6d35126f0f63b1b..b949f6b580421661196516a557cbc77515a01e60 100644 (file)
@@ -18,10 +18,6 @@ namespace Content.Server.Disease.Components
         /// If this vaccine has been used
         /// </summary>
         public bool Used = false;
-        /// <summary>
-        /// Token for interrupting injection do after.
-        /// </summary>
-        public CancellationTokenSource? CancelToken;
 
         /// <summary>
         /// The disease prototype currently on the vaccine
index 2cabef4e6379402800c7c4832602ea3a17c21d97..28b4dcd7c6cb671e9722cbfcf781360537b7217d 100644 (file)
@@ -1,4 +1,3 @@
-using System.Threading;
 using Content.Server.Disease.Components;
 using Content.Shared.Disease;
 using Content.Shared.Interaction;
@@ -17,6 +16,7 @@ using Robust.Shared.Audio;
 using Robust.Shared.Utility;
 using Content.Shared.Tools.Components;
 using Content.Server.Station.Systems;
+using Content.Shared.DoAfter;
 using Content.Shared.IdentityManagement;
 using Robust.Server.GameObjects;
 
@@ -47,8 +47,7 @@ namespace Content.Server.Disease
             // Private Events
             SubscribeLocalEvent<DiseaseDiagnoserComponent, DiseaseMachineFinishedEvent>(OnDiagnoserFinished);
             SubscribeLocalEvent<DiseaseVaccineCreatorComponent, DiseaseMachineFinishedEvent>(OnVaccinatorFinished);
-            SubscribeLocalEvent<TargetSwabSuccessfulEvent>(OnTargetSwabSuccessful);
-            SubscribeLocalEvent<SwabCancelledEvent>(OnSwabCancelled);
+            SubscribeLocalEvent<DoAfterEvent>(OnSwabDoAfter);
         }
 
         private Queue<EntityUid> AddQueue = new();
@@ -100,18 +99,9 @@ namespace Content.Server.Disease
         /// </summary>
         private void OnAfterInteract(EntityUid uid, DiseaseSwabComponent swab, AfterInteractEvent args)
         {
-            if (swab.CancelToken != null)
-            {
-                swab.CancelToken.Cancel();
-                swab.CancelToken = null;
-                return;
-            }
             if (args.Target == null || !args.CanReach)
                 return;
 
-            if (!TryComp<DiseaseCarrierComponent>(args.Target, out var carrier))
-                return;
-
             if (swab.Used)
             {
                 _popupSystem.PopupEntity(Loc.GetString("swab-already-used"), args.User, args.User);
@@ -126,11 +116,8 @@ namespace Content.Server.Disease
                 return;
             }
 
-            swab.CancelToken = new CancellationTokenSource();
-            _doAfterSystem.DoAfter(new DoAfterEventArgs(args.User, swab.SwabDelay, swab.CancelToken.Token, target: args.Target)
+            _doAfterSystem.DoAfter(new DoAfterEventArgs(args.User, swab.SwabDelay, target: args.Target, used: uid)
             {
-                BroadcastFinishedEvent = new TargetSwabSuccessfulEvent(args.User, args.Target, swab, carrier),
-                BroadcastCancelledEvent = new SwabCancelledEvent(swab),
                 BreakOnTargetMove = true,
                 BreakOnUserMove = true,
                 BreakOnStun = true,
@@ -309,36 +296,27 @@ namespace Content.Server.Disease
         {
             UpdateAppearance(uid, args.Powered, false);
         }
-        ///
-        /// Private events
-        ///
 
         /// <summary>
         /// Copies a disease prototype to the swab
         /// after the doafter completes.
         /// </summary>
-        private void OnTargetSwabSuccessful(TargetSwabSuccessfulEvent args)
+        private void OnSwabDoAfter(DoAfterEvent args)
         {
-            if (args.Target == null)
+            if (args.Handled || args.Cancelled || !TryComp<DiseaseCarrierComponent>(args.Args.Target, out var carrier) || !TryComp<DiseaseSwabComponent>(args.Args.Target, out var swab))
                 return;
 
-            args.Swab.Used = true;
-            _popupSystem.PopupEntity(Loc.GetString("swab-swabbed", ("target", Identity.Entity(args.Target.Value, EntityManager))), args.Target.Value, args.User);
+            swab.Used = true;
+            _popupSystem.PopupEntity(Loc.GetString("swab-swabbed", ("target", Identity.Entity(args.Args.Target.Value, EntityManager))), args.Args.Target.Value, args.Args.User);
 
-            if (args.Swab.Disease != null || args.Carrier.Diseases.Count == 0)
+            if (swab.Disease != null || carrier.Diseases.Count == 0)
                 return;
 
-            args.Swab.Disease = _random.Pick(args.Carrier.Diseases);
-        }
+            swab.Disease = _random.Pick(carrier.Diseases);
 
-        /// <summary>
-        /// Cancels the swab doafter if needed.
-        /// </summary>
-        private static void OnSwabCancelled(SwabCancelledEvent args)
-        {
-            args.Swab.CancelToken = null;
         }
 
+
         /// <summary>
         /// Prints a diagnostic report with its findings.
         /// Also cancels the animation.
@@ -410,38 +388,6 @@ namespace Content.Server.Disease
             vaxxComp.Disease = args.Machine.Disease;
         }
 
-        /// <summary>
-        /// Cancels the mouth-swabbing doafter
-        /// </summary>
-        private sealed class SwabCancelledEvent : EntityEventArgs
-        {
-            public readonly DiseaseSwabComponent Swab;
-            public SwabCancelledEvent(DiseaseSwabComponent swab)
-            {
-                Swab = swab;
-            }
-        }
-
-        /// <summary>
-        /// Fires if the doafter for swabbing someone's mouth succeeds
-        /// </summary>
-        private sealed class TargetSwabSuccessfulEvent : EntityEventArgs
-        {
-            public EntityUid User { get; }
-            public EntityUid? Target { get; }
-            public DiseaseSwabComponent Swab { get; }
-
-            public DiseaseCarrierComponent Carrier { get; }
-
-            public TargetSwabSuccessfulEvent(EntityUid user, EntityUid? target, DiseaseSwabComponent swab, DiseaseCarrierComponent carrier)
-            {
-                User = user;
-                Target = target;
-                Swab = swab;
-                Carrier = carrier;
-            }
-        }
-
         /// <summary>
         /// Fires when a disease machine is done
         /// with its production delay and ready to
index f057f890b6370fc621bcabb094b908a506f5c07d..db6f0dc6a532625e1038190bc58245341facffb0 100644 (file)
@@ -1,4 +1,3 @@
-using System.Threading;
 using Content.Server.Body.Systems;
 using Content.Server.Chat.Systems;
 using Content.Server.Disease.Components;
@@ -9,6 +8,7 @@ using Content.Shared.Clothing.Components;
 using Content.Shared.Disease;
 using Content.Shared.Disease.Components;
 using Content.Shared.Disease.Events;
+using Content.Shared.DoAfter;
 using Content.Shared.Examine;
 using Content.Shared.IdentityManagement;
 using Content.Shared.Interaction;
@@ -20,7 +20,6 @@ using Content.Shared.Mobs.Systems;
 using Content.Shared.Rejuvenate;
 using Robust.Shared.Audio;
 using Robust.Server.GameObjects;
-using Robust.Shared.Player;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Random;
 using Robust.Shared.Serialization.Manager;
@@ -59,8 +58,7 @@ namespace Content.Server.Disease
             // Handling stuff from other systems
             SubscribeLocalEvent<DiseaseCarrierComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
             // Private events stuff
-            SubscribeLocalEvent<TargetVaxxSuccessfulEvent>(OnTargetVaxxSuccessful);
-            SubscribeLocalEvent<VaxxCancelledEvent>(OnVaxxCancelled);
+            SubscribeLocalEvent<DiseaseVaccineComponent, DoAfterEvent>(OnDoAfter);
         }
 
         private Queue<EntityUid> AddQueue = new();
@@ -278,22 +276,7 @@ namespace Content.Server.Disease
         /// </summary>
         private void OnAfterInteract(EntityUid uid, DiseaseVaccineComponent vaxx, AfterInteractEvent args)
         {
-            if (vaxx.CancelToken != null)
-            {
-                vaxx.CancelToken.Cancel();
-                vaxx.CancelToken = null;
-                return;
-            }
-            if (args.Target == null)
-                return;
-
-            if (!args.CanReach)
-                return;
-
-            if (vaxx.CancelToken != null)
-                return;
-
-            if (!TryComp<DiseaseCarrierComponent>(args.Target, out var carrier))
+            if (args.Target == null || !args.CanReach)
                 return;
 
             if (vaxx.Used)
@@ -302,11 +285,8 @@ namespace Content.Server.Disease
                 return;
             }
 
-            vaxx.CancelToken = new CancellationTokenSource();
-            _doAfterSystem.DoAfter(new DoAfterEventArgs(args.User, vaxx.InjectDelay, vaxx.CancelToken.Token, target: args.Target)
+            _doAfterSystem.DoAfter(new DoAfterEventArgs(args.User, vaxx.InjectDelay, target: args.Target, used:uid)
             {
-                BroadcastFinishedEvent = new TargetVaxxSuccessfulEvent(args.User, args.Target, vaxx, carrier),
-                BroadcastCancelledEvent = new VaxxCancelledEvent(vaxx),
                 BreakOnTargetMove = true,
                 BreakOnUserMove = true,
                 BreakOnStun = true,
@@ -350,7 +330,6 @@ namespace Content.Server.Disease
         }
     }
 
-
         ///
         /// Helper functions
         ///
@@ -490,60 +469,23 @@ namespace Content.Server.Disease
             carrier.PastDiseases.Add(disease);
         }
 
-        ///
-        /// Private Events Stuff
-        ///
-
-        /// <summary>
-        /// Injects the vaccine into the target
-        /// if the doafter is completed
-        /// </summary>
-        private void OnTargetVaxxSuccessful(TargetVaxxSuccessfulEvent args)
+        private void OnDoAfter(EntityUid uid, DiseaseVaccineComponent component, DoAfterEvent args)
         {
-            if (args.Vaxx.Disease == null)
+            if (args.Handled || args.Cancelled || !TryComp<DiseaseCarrierComponent>(args.Args.Target, out var carrier) || component.Disease == null)
                 return;
-            Vaccinate(args.Carrier, args.Vaxx.Disease);
-            EntityManager.DeleteEntity(args.Vaxx.Owner);
-        }
 
-        /// <summary>
-        /// Cancels the vaccine doafter
-        /// </summary>
-        private static void OnVaxxCancelled(VaxxCancelledEvent args)
-        {
-            args.Vaxx.CancelToken = null;
-        }
-        /// These two are standard doafter stuff you can ignore
-        private sealed class VaxxCancelledEvent : EntityEventArgs
-        {
-            public readonly DiseaseVaccineComponent Vaxx;
-            public VaxxCancelledEvent(DiseaseVaccineComponent vaxx)
-            {
-                Vaxx = vaxx;
-            }
-        }
-        private sealed class TargetVaxxSuccessfulEvent : EntityEventArgs
-        {
-            public EntityUid User { get; }
-            public EntityUid? Target { get; }
-            public DiseaseVaccineComponent Vaxx { get; }
-            public DiseaseCarrierComponent Carrier { get; }
-            public TargetVaxxSuccessfulEvent(EntityUid user, EntityUid? target, DiseaseVaccineComponent vaxx, DiseaseCarrierComponent carrier)
-            {
-                User = user;
-                Target = target;
-                Vaxx = vaxx;
-                Carrier = carrier;
-            }
+            Vaccinate(carrier, component.Disease);
+            EntityManager.DeleteEntity(uid);
+            args.Handled = true;
         }
     }
 
-        /// <summary>
-        /// This event is fired by chems
-        /// and other brute-force rather than
-        /// specific cures. It will roll the dice to attempt
-        /// to cure each disease on the target
-        /// </summary>
+    /// <summary>
+    /// This event is fired by chems
+    /// and other brute-force rather than
+    /// specific cures. It will roll the dice to attempt
+    /// to cure each disease on the target
+    /// </summary>
     public sealed class CureDiseaseAttemptEvent : EntityEventArgs
     {
         public float CureChance { get; }
index 0e7ed86331a8f0c2e5605dd58a0867e457271371..9afc18faa300882e3c3147acc7ea3fed8595076b 100644 (file)
@@ -16,6 +16,7 @@ using Content.Shared.Database;
 using Content.Shared.Destructible;
 using Content.Shared.Disposal;
 using Content.Shared.Disposal.Components;
+using Content.Shared.DoAfter;
 using Content.Shared.DragDrop;
 using Content.Shared.Hands.Components;
 using Content.Shared.Hands.EntitySystems;
@@ -28,7 +29,6 @@ using Content.Shared.Verbs;
 using Robust.Server.GameObjects;
 using Robust.Shared.Containers;
 using Robust.Shared.Physics.Components;
-using Robust.Shared.Player;
 using Robust.Shared.Random;
 using Robust.Shared.Map.Components;
 
@@ -78,7 +78,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems
             SubscribeLocalEvent<DisposalUnitComponent, GetVerbsEvent<Verb>>(AddClimbInsideVerb);
 
             // Units
-            SubscribeLocalEvent<DoInsertDisposalUnitEvent>(DoInsertDisposalUnit);
+            SubscribeLocalEvent<DisposalUnitComponent, DoAfterEvent>(OnDoAfter);
 
             //UI
             SubscribeLocalEvent<DisposalUnitComponent, SharedDisposalUnitComponent.UiButtonPressedMessage>(OnUiButtonPressed);
@@ -155,28 +155,21 @@ namespace Content.Server.Disposal.Unit.EntitySystems
                 {
                     _handsSystem.TryDropIntoContainer(args.User, args.Using.Value, component.Container, checkActionBlocker: false, args.Hands);
                     _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} inserted {ToPrettyString(args.Using.Value)} into {ToPrettyString(uid)}");
-                    AfterInsert(uid, component, args.Using.Value);
+                    AfterInsert(uid, component, args.Using.Value, args.User);
                 }
             };
 
             args.Verbs.Add(insertVerb);
         }
 
-        private void DoInsertDisposalUnit(DoInsertDisposalUnitEvent ev)
+        private void OnDoAfter(EntityUid uid, DisposalUnitComponent component, DoAfterEvent args)
         {
-            var toInsert = ev.ToInsert;
-
-            if (!TryComp(ev.Unit, out DisposalUnitComponent? unit))
-            {
+            if (args.Handled || args.Cancelled || args.Args.Target == null || args.Args.Used == null)
                 return;
-            }
 
-            if (!unit.Container.Insert(toInsert))
-                return;
-            if (ev.User != null)
-                _adminLogger.Add(LogType.Action, LogImpact.Medium,
-                    $"{ToPrettyString(ev.User.Value):player} inserted {ToPrettyString(toInsert)} into {ToPrettyString(ev.Unit)}");
-            AfterInsert(ev.Unit, unit, toInsert);
+            AfterInsert(uid, component, args.Args.Target.Value, args.Args.User);
+
+            args.Handled = true;
         }
 
         public void DoInsertDisposalUnit(EntityUid uid, EntityUid toInsert, EntityUid user, DisposalUnitComponent? disposal = null)
@@ -188,7 +181,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems
                 return;
 
             _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(user):player} inserted {ToPrettyString(toInsert)} into {ToPrettyString(uid)}");
-            AfterInsert(uid, disposal, toInsert);
+            AfterInsert(uid, disposal, toInsert, user);
         }
 
         public override void Update(float frameTime)
@@ -273,7 +266,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems
             }
 
             _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} inserted {ToPrettyString(args.Used)} into {ToPrettyString(uid)}");
-            AfterInsert(uid, component, args.Used);
+            AfterInsert(uid, component, args.Used, args.User);
             args.Handled = true;
         }
 
@@ -454,6 +447,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems
                     TryComp(ejectedId, out PhysicsComponent? body))
                 {
                     // TODO: We need to use a specific collision method (which sloth hasn't coded yet) for actual bounds overlaps.
+                    // TODO: Come do this sloth :^)
                     // Check for itemcomp as we won't just block the disposal unit "sleeping" for something it can't collide with anyway.
                     if (!HasComp<ItemComponent>(ejectedId)
                         && _lookup.GetWorldAABB(ejectedId).Intersects(disposalsBounds!.Value))
@@ -485,24 +479,22 @@ namespace Content.Server.Disposal.Unit.EntitySystems
                 return false;
 
             var delay = userId == toInsertId ? unit.EntryDelay : unit.DraggedEntryDelay;
-            var ev = new DoInsertDisposalUnitEvent(userId, toInsertId, unitId);
 
             if (delay <= 0 || userId == null)
             {
-                DoInsertDisposalUnit(ev);
+                AfterInsert(unitId, unit, toInsertId, userId);
                 return true;
             }
 
             // Can't check if our target AND disposals moves currently so we'll just check target.
             // if you really want to check if disposals moves then add a predicate.
-            var doAfterArgs = new DoAfterEventArgs(userId.Value, delay, default, toInsertId)
+            var doAfterArgs = new DoAfterEventArgs(userId.Value, delay, target:toInsertId, used:unitId)
             {
                 BreakOnDamage = true,
                 BreakOnStun = true,
                 BreakOnTargetMove = true,
                 BreakOnUserMove = true,
                 NeedHand = false,
-                BroadcastFinishedEvent = ev
             };
 
             _doAfterSystem.DoAfter(doAfterArgs);
@@ -727,8 +719,14 @@ namespace Content.Server.Disposal.Unit.EntitySystems
             }, component.AutomaticEngageToken.Token);
         }
 
-        public void AfterInsert(EntityUid uid, DisposalUnitComponent component, EntityUid inserted)
+        public void AfterInsert(EntityUid uid, DisposalUnitComponent component, EntityUid inserted, EntityUid? user = null)
         {
+            if (!component.Container.Insert(inserted))
+                return;
+
+            if (user != inserted && user != null)
+                _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(user.Value):player} inserted {ToPrettyString(inserted)} into {ToPrettyString(uid)}");
+
             TryQueueEngage(uid, component);
 
             if (TryComp(inserted, out ActorComponent? actor))
diff --git a/Content.Server/DoAfter/DoAfter.cs b/Content.Server/DoAfter/DoAfter.cs
deleted file mode 100644 (file)
index 61f4e00..0000000
+++ /dev/null
@@ -1,209 +0,0 @@
-using System.Threading.Tasks;
-using Content.Server.Hands.Components;
-using Content.Shared.Stunnable;
-using Robust.Shared.Map;
-using Robust.Shared.Timing;
-
-namespace Content.Server.DoAfter
-{
-    public sealed class DoAfter
-    {
-        public Task<DoAfterStatus> AsTask { get; }
-
-        private TaskCompletionSource<DoAfterStatus> Tcs { get; }
-
-        public readonly DoAfterEventArgs EventArgs;
-
-        public TimeSpan StartTime { get; }
-
-        public float Elapsed { get; set; }
-
-        public EntityCoordinates UserGrid { get; }
-
-        public EntityCoordinates TargetGrid { get; }
-
-#pragma warning disable RA0004
-        public DoAfterStatus Status => AsTask.IsCompletedSuccessfully ? AsTask.Result : DoAfterStatus.Running;
-#pragma warning restore RA0004
-
-        // NeedHand
-        private readonly string? _activeHand;
-        private readonly EntityUid? _activeItem;
-
-        public DoAfter(DoAfterEventArgs eventArgs, IEntityManager entityManager)
-        {
-            EventArgs = eventArgs;
-            StartTime = IoCManager.Resolve<IGameTiming>().CurTime;
-
-            if (eventArgs.BreakOnUserMove)
-            {
-                UserGrid = entityManager.GetComponent<TransformComponent>(eventArgs.User).Coordinates;
-            }
-
-            if (eventArgs.Target != null && eventArgs.BreakOnTargetMove)
-            {
-                // Target should never be null if the bool is set.
-                TargetGrid = entityManager.GetComponent<TransformComponent>(eventArgs.Target!.Value).Coordinates;
-            }
-
-            // 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).
-            if (eventArgs.NeedHand && entityManager.TryGetComponent(eventArgs.User, out HandsComponent? handsComponent))
-            {
-                _activeHand = handsComponent.ActiveHand?.Name;
-                _activeItem = handsComponent.ActiveHandEntity;
-            }
-
-            Tcs = new TaskCompletionSource<DoAfterStatus>();
-            AsTask = Tcs.Task;
-        }
-
-        public void Cancel()
-        {
-            if (Status == DoAfterStatus.Running)
-                Tcs.SetResult(DoAfterStatus.Cancelled);
-        }
-
-        public void Run(float frameTime, IEntityManager entityManager)
-        {
-            switch (Status)
-            {
-                case DoAfterStatus.Running:
-                    break;
-                case DoAfterStatus.Cancelled:
-                case DoAfterStatus.Finished:
-                    return;
-                default:
-                    throw new ArgumentOutOfRangeException();
-            }
-
-            Elapsed += frameTime;
-
-            if (IsFinished())
-            {
-                // Do the final checks here
-                if (!TryPostCheck())
-                {
-                    Tcs.SetResult(DoAfterStatus.Cancelled);
-                }
-                else
-                {
-                    Tcs.SetResult(DoAfterStatus.Finished);
-                }
-
-                return;
-            }
-
-            if (IsCancelled(entityManager))
-            {
-                Tcs.SetResult(DoAfterStatus.Cancelled);
-            }
-        }
-
-        private bool IsCancelled(IEntityManager entityManager)
-        {
-            if (!entityManager.EntityExists(EventArgs.User) || EventArgs.Target is {} target && !entityManager.EntityExists(target))
-            {
-                return true;
-            }
-
-            //https://github.com/tgstation/tgstation/blob/1aa293ea337283a0191140a878eeba319221e5df/code/__HELPERS/mobs.dm
-            if (EventArgs.CancelToken.IsCancellationRequested)
-            {
-                return true;
-            }
-
-            // TODO :Handle inertia in space.
-            if (EventArgs.BreakOnUserMove && !entityManager.GetComponent<TransformComponent>(EventArgs.User).Coordinates.InRange(
-                entityManager, UserGrid, EventArgs.MovementThreshold))
-            {
-                return true;
-            }
-
-            if (EventArgs.Target != null &&
-                EventArgs.BreakOnTargetMove &&
-                !entityManager.GetComponent<TransformComponent>(EventArgs.Target!.Value).Coordinates.InRange(entityManager, TargetGrid, EventArgs.MovementThreshold))
-            {
-                return true;
-            }
-
-            if (EventArgs.ExtraCheck != null && !EventArgs.ExtraCheck.Invoke())
-            {
-                return true;
-            }
-
-            if (EventArgs.BreakOnStun &&
-                entityManager.HasComponent<StunnedComponent>(EventArgs.User))
-            {
-                return true;
-            }
-
-            if (EventArgs.NeedHand)
-            {
-                if (!entityManager.TryGetComponent(EventArgs.User, out HandsComponent? handsComponent))
-                {
-                    // If we had a hand but no longer have it that's still a paddlin'
-                    if (_activeHand != null)
-                    {
-                        return true;
-                    }
-                }
-                else
-                {
-                    var currentActiveHand = handsComponent.ActiveHand?.Name;
-                    if (_activeHand != currentActiveHand)
-                    {
-                        return true;
-                    }
-
-                    var currentItem = handsComponent.ActiveHandEntity;
-                    if (_activeItem != currentItem)
-                    {
-                        return true;
-                    }
-                }
-            }
-
-            if (EventArgs.DistanceThreshold != null)
-            {
-                var xformQuery = entityManager.GetEntityQuery<TransformComponent>();
-                TransformComponent? userXform = null;
-
-                // Check user distance to target AND used entities.
-                if (EventArgs.Target != null && !EventArgs.User.Equals(EventArgs.Target))
-                {
-                    //recalculate Target location in case Target has also moved
-                    var targetCoordinates = xformQuery.GetComponent(EventArgs.Target.Value).Coordinates;
-                    userXform ??= xformQuery.GetComponent(EventArgs.User);
-                    if (!userXform.Coordinates.InRange(entityManager, targetCoordinates, EventArgs.DistanceThreshold.Value))
-                        return true;
-                }
-
-                if (EventArgs.Used != null)
-                {
-                    var targetCoordinates = xformQuery.GetComponent(EventArgs.Used.Value).Coordinates;
-                    userXform ??= xformQuery.GetComponent(EventArgs.User);
-                    if (!userXform.Coordinates.InRange(entityManager, targetCoordinates, EventArgs.DistanceThreshold.Value))
-                        return true;
-                }
-            }
-
-            return false;
-        }
-
-        private bool TryPostCheck()
-        {
-            return EventArgs.PostCheck?.Invoke() != false;
-        }
-
-        private bool IsFinished()
-        {
-            if (Elapsed <= EventArgs.Delay)
-            {
-                return false;
-            }
-
-            return true;
-        }
-    }
-}
diff --git a/Content.Server/DoAfter/DoAfterComponent.cs b/Content.Server/DoAfter/DoAfterComponent.cs
deleted file mode 100644 (file)
index adf5e38..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-using Content.Shared.DoAfter;
-
-namespace Content.Server.DoAfter
-{
-    [RegisterComponent, Access(typeof(DoAfterSystem))]
-    public sealed class DoAfterComponent : SharedDoAfterComponent
-    {
-        public readonly Dictionary<DoAfter, byte> DoAfters = new();
-
-        // So the client knows which one to update (and so we don't send all of the do_afters every time 1 updates)
-        // we'll just send them the index. Doesn't matter if it wraps around.
-        public byte RunningIndex;
-    }
-
-    /// <summary>
-    ///     Added to entities that are currently performing any doafters.
-    /// </summary>
-    [RegisterComponent]
-    public sealed class ActiveDoAfterComponent : Component {}
-}
diff --git a/Content.Server/DoAfter/DoAfterEventArgs.cs b/Content.Server/DoAfter/DoAfterEventArgs.cs
deleted file mode 100644 (file)
index 7ba46c6..0000000
+++ /dev/null
@@ -1,144 +0,0 @@
-using System.Threading;
-using Content.Shared.FixedPoint;
-using Robust.Shared.Utility;
-
-namespace Content.Server.DoAfter
-{
-    public sealed class DoAfterEventArgs
-    {
-        /// <summary>
-        ///     The entity invoking do_after
-        /// </summary>
-        public EntityUid User { get; }
-
-        /// <summary>
-        ///     How long does the do_after require to complete
-        /// </summary>
-        public float Delay { get; }
-
-        /// <summary>
-        ///     Applicable target (if relevant)
-        /// </summary>
-        public EntityUid? Target { get; }
-
-        /// <summary>
-        ///     Entity used by the User on the Target.
-        /// </summary>
-        public EntityUid? Used { get; set; }
-
-        /// <summary>
-        ///     Manually cancel the do_after so it no longer runs
-        /// </summary>
-        public CancellationToken CancelToken { get; }
-
-        // Break the chains
-        /// <summary>
-        ///     Whether we need to keep our active hand as is (i.e. can't change hand or change item).
-        ///     This also covers requiring the hand to be free (if applicable).
-        /// </summary>
-        public bool NeedHand { get; set; }
-
-        /// <summary>
-        ///     If do_after stops when the user moves
-        /// </summary>
-        public bool BreakOnUserMove { get; set; }
-
-        /// <summary>
-        ///     If do_after stops when the target moves (if there is a target)
-        /// </summary>
-        public bool BreakOnTargetMove { get; set; }
-
-        /// <summary>
-        ///     Threshold for user and target movement
-        /// </summary>
-        public float MovementThreshold { get; set; }
-
-        public bool BreakOnDamage { get; set; }
-
-        /// <summary>
-        ///     Threshold for user damage
-        /// </summary>
-        public FixedPoint2 DamageThreshold { get; set; }
-        public bool BreakOnStun { get; set; }
-
-        /// <summary>
-        ///     Threshold for distance user from the used OR target entities.
-        /// </summary>
-        public float? DistanceThreshold { get; set; }
-
-        /// <summary>
-        ///     Requires a function call once at the end (like InRangeUnobstructed).
-        /// </summary>
-        /// <remarks>
-        ///     Anything that needs a pre-check should do it itself so no DoAfterState is ever sent to the client.
-        /// </remarks>
-        public Func<bool>? PostCheck { get; set; } = null;
-
-        /// <summary>
-        ///     Additional conditions that need to be met. Return false to cancel.
-        /// </summary>
-        public Func<bool>? ExtraCheck { get; set; }
-
-        /// <summary>
-        ///     Event to be raised directed to the <see cref="User"/> entity when the DoAfter is cancelled.
-        /// </summary>
-        public object? UserCancelledEvent { get; set; }
-
-        /// <summary>
-        ///     Event to be raised directed to the <see cref="User"/> entity when the DoAfter is finished successfully.
-        /// </summary>
-        public object? UserFinishedEvent { get; set; }
-
-        /// <summary>
-        ///     Event to be raised directed to the <see cref="Used"/> entity when the DoAfter is cancelled.
-        /// </summary>
-        public object? UsedCancelledEvent { get; set; }
-
-        /// <summary>
-        ///     Event to be raised directed to the <see cref="Used"/> entity when the DoAfter is finished successfully.
-        /// </summary>
-        public object? UsedFinishedEvent { get; set; }
-
-        /// <summary>
-        ///     Event to be raised directed to the <see cref="Target"/> entity when the DoAfter is cancelled.
-        /// </summary>
-        public object? TargetCancelledEvent { get; set; }
-
-        /// <summary>
-        ///     Event to be raised directed to the <see cref="Target"/> entity when the DoAfter is finished successfully.
-        /// </summary>
-        public object? TargetFinishedEvent { get; set; }
-
-        /// <summary>
-        ///     Event to be broadcast when the DoAfter is cancelled.
-        /// </summary>
-        public object? BroadcastCancelledEvent { get; set; }
-
-        /// <summary>
-        ///     Event to be broadcast when the DoAfter is finished successfully.
-        /// </summary>
-        public object? BroadcastFinishedEvent { get; set; }
-
-        public DoAfterEventArgs(
-            EntityUid user,
-            float delay,
-            CancellationToken cancelToken = default,
-            EntityUid? target = null,
-            EntityUid? used = null)
-        {
-            User = user;
-            Delay = delay;
-            CancelToken = cancelToken;
-            Target = target;
-            Used = used;
-            MovementThreshold = 0.1f;
-            DamageThreshold = 1.0;
-
-            if (Target == null)
-            {
-                DebugTools.Assert(!BreakOnTargetMove);
-                BreakOnTargetMove = false;
-            }
-        }
-    }
-}
index 84ad75092a7476e4659539f5c650024ac766787e..ad0d8d27d88d492572dec30dcfc8212712210679 100644 (file)
-using System.Linq;
-using System.Threading.Tasks;
-using Content.Shared.Damage;
 using Content.Shared.DoAfter;
 using Content.Shared.Mobs;
 using JetBrains.Annotations;
-using Robust.Shared.GameStates;
 
-namespace Content.Server.DoAfter
-{
-    [UsedImplicitly]
-    public sealed class DoAfterSystem : EntitySystem
-    {
-        // We cache these lists as to not allocate them every update tick...
-        private readonly Queue<DoAfter> _cancelled = new();
-        private readonly Queue<DoAfter> _finished = new();
-
-        public override void Initialize()
-        {
-            base.Initialize();
-            SubscribeLocalEvent<DoAfterComponent, DamageChangedEvent>(OnDamage);
-            SubscribeLocalEvent<DoAfterComponent, MobStateChangedEvent>(OnStateChanged);
-            SubscribeLocalEvent<DoAfterComponent, ComponentGetState>(OnDoAfterGetState);
-        }
-
-        public void Add(DoAfterComponent component, DoAfter doAfter)
-        {
-            component.DoAfters.Add(doAfter, component.RunningIndex);
-            EnsureComp<ActiveDoAfterComponent>(component.Owner);
-            component.RunningIndex++;
-            Dirty(component);
-        }
-
-        public void Cancelled(DoAfterComponent component, DoAfter doAfter)
-        {
-            if (!component.DoAfters.TryGetValue(doAfter, out var index))
-                return;
-
-            component.DoAfters.Remove(doAfter);
-
-            if (component.DoAfters.Count == 0)
-            {
-                RemComp<ActiveDoAfterComponent>(component.Owner);
-            }
-
-            RaiseNetworkEvent(new CancelledDoAfterMessage(component.Owner, index));
-        }
-
-        /// <summary>
-        ///     Call when the particular DoAfter is finished.
-        ///     Client should be tracking this independently.
-        /// </summary>
-        public void Finished(DoAfterComponent component, DoAfter doAfter)
-        {
-            if (!component.DoAfters.ContainsKey(doAfter))
-                return;
-
-            component.DoAfters.Remove(doAfter);
-
-            if (component.DoAfters.Count == 0)
-            {
-                RemComp<ActiveDoAfterComponent>(component.Owner);
-            }
-        }
-
-        private void OnDoAfterGetState(EntityUid uid, DoAfterComponent component, ref ComponentGetState args)
-        {
-            var toAdd = new List<ClientDoAfter>(component.DoAfters.Count);
-
-            foreach (var (doAfter, _) in component.DoAfters)
-            {
-                // THE ALMIGHTY PYRAMID
-                var clientDoAfter = new ClientDoAfter(
-                    component.DoAfters[doAfter],
-                    doAfter.UserGrid,
-                    doAfter.TargetGrid,
-                    doAfter.StartTime,
-                    doAfter.EventArgs.Delay,
-                    doAfter.EventArgs.BreakOnUserMove,
-                    doAfter.EventArgs.BreakOnTargetMove,
-                    doAfter.EventArgs.MovementThreshold,
-                    doAfter.EventArgs.DamageThreshold,
-                    doAfter.EventArgs.Target);
-
-                toAdd.Add(clientDoAfter);
-            }
-
-            args.State = new DoAfterComponentState(toAdd);
-        }
-
-        private void OnStateChanged(EntityUid uid, DoAfterComponent component, MobStateChangedEvent args)
-        {
-            if (args.NewMobState == MobState.Alive)
-                return;
-
-            foreach (var (doAfter, _) in component.DoAfters)
-            {
-                doAfter.Cancel();
-            }
-        }
-
-        /// <summary>
-        /// Cancels DoAfter if it breaks on damage and it meets the threshold
-        /// </summary>
-        /// <param name="_">
-        /// The EntityUID of the user
-        /// </param>
-        /// <param name="component"></param>
-        /// <param name="args"></param>
-        public void OnDamage(EntityUid _, DoAfterComponent component, DamageChangedEvent args)
-        {
-            if (!args.InterruptsDoAfters || !args.DamageIncreased || args.DamageDelta == null)
-                return;
-
-            foreach (var (doAfter, _) in component.DoAfters)
-            {
-                if (doAfter.EventArgs.BreakOnDamage && args.DamageDelta?.Total.Float() > doAfter.EventArgs.DamageThreshold)
-                {
-                    doAfter.Cancel();
-                }
-            }
-        }
+namespace Content.Server.DoAfter;
 
-        public override void Update(float frameTime)
-        {
-            base.Update(frameTime);
-
-            foreach (var (_, comp) in EntityManager.EntityQuery<ActiveDoAfterComponent, DoAfterComponent>())
-            {
-                foreach (var (doAfter, _) in comp.DoAfters.ToArray())
-                {
-                    doAfter.Run(frameTime, EntityManager);
-
-                    switch (doAfter.Status)
-                    {
-                        case DoAfterStatus.Running:
-                            break;
-                        case DoAfterStatus.Cancelled:
-                            _cancelled.Enqueue(doAfter);
-                            break;
-                        case DoAfterStatus.Finished:
-                            _finished.Enqueue(doAfter);
-                            break;
-                        default:
-                            throw new ArgumentOutOfRangeException();
-                    }
-                }
-
-                while (_cancelled.TryDequeue(out var doAfter))
-                {
-                    Cancelled(comp, doAfter);
-
-                    if(EntityManager.EntityExists(doAfter.EventArgs.User) && doAfter.EventArgs.UserCancelledEvent != null)
-                        RaiseLocalEvent(doAfter.EventArgs.User, doAfter.EventArgs.UserCancelledEvent, false);
-
-                    if (doAfter.EventArgs.Used is {} used && EntityManager.EntityExists(used) && doAfter.EventArgs.UsedCancelledEvent != null)
-                        RaiseLocalEvent(used, doAfter.EventArgs.UsedCancelledEvent);
-
-                    if(doAfter.EventArgs.Target is {} target && EntityManager.EntityExists(target) && doAfter.EventArgs.TargetCancelledEvent != null)
-                        RaiseLocalEvent(target, doAfter.EventArgs.TargetCancelledEvent, false);
-
-                    if(doAfter.EventArgs.BroadcastCancelledEvent != null)
-                        RaiseLocalEvent(doAfter.EventArgs.BroadcastCancelledEvent);
-                }
-
-                while (_finished.TryDequeue(out var doAfter))
-                {
-                    Finished(comp, doAfter);
-
-                    if(EntityManager.EntityExists(doAfter.EventArgs.User) && doAfter.EventArgs.UserFinishedEvent != null)
-                        RaiseLocalEvent(doAfter.EventArgs.User, doAfter.EventArgs.UserFinishedEvent, false);
-
-                    if(doAfter.EventArgs.Used is {} used && EntityManager.EntityExists(used) && doAfter.EventArgs.UsedFinishedEvent != null)
-                        RaiseLocalEvent(used, doAfter.EventArgs.UsedFinishedEvent);
-
-                    if(doAfter.EventArgs.Target is {} target && EntityManager.EntityExists(target) && doAfter.EventArgs.TargetFinishedEvent != null)
-                        RaiseLocalEvent(target, doAfter.EventArgs.TargetFinishedEvent, false);
-
-                    if(doAfter.EventArgs.BroadcastFinishedEvent != null)
-                        RaiseLocalEvent(doAfter.EventArgs.BroadcastFinishedEvent);
-                }
-            }
-        }
-
-        /// <summary>
-        ///     Tasks that are delayed until the specified time has passed
-        ///     These can be potentially cancelled by the user moving or when other things happen.
-        /// </summary>
-        /// <param name="eventArgs"></param>
-        /// <returns></returns>
-        public async Task<DoAfterStatus> WaitDoAfter(DoAfterEventArgs eventArgs)
-        {
-            var doAfter = CreateDoAfter(eventArgs);
-
-            await doAfter.AsTask;
-
-            return doAfter.Status;
-        }
-
-        /// <summary>
-        ///     Creates a DoAfter without waiting for it to finish. You can use events with this.
-        ///     These can be potentially cancelled by the user moving or when other things happen.
-        /// </summary>
-        /// <param name="eventArgs"></param>
-        public void DoAfter(DoAfterEventArgs eventArgs)
-        {
-            CreateDoAfter(eventArgs);
-        }
-
-        private DoAfter CreateDoAfter(DoAfterEventArgs eventArgs)
-        {
-            // Setup
-            var doAfter = new DoAfter(eventArgs, EntityManager);
-            // Caller's gonna be responsible for this I guess
-            var doAfterComponent = Comp<DoAfterComponent>(eventArgs.User);
-            Add(doAfterComponent, doAfter);
-            return doAfter;
-        }
-    }
+[UsedImplicitly]
+public sealed class DoAfterSystem : SharedDoAfterSystem
+{
 
-    public enum DoAfterStatus : byte
-    {
-        Running,
-        Cancelled,
-        Finished,
-    }
 }
index cac9876d273abf9f46b1b64d8a2ecd560d19069a..bb01665b8dbb62e2d9cfc37d0b5412d2b4c1346a 100644 (file)
@@ -2,8 +2,6 @@ using Content.Server.Access;
 using Content.Server.Atmos.Components;
 using Content.Server.Atmos.EntitySystems;
 using Content.Server.Construction;
-using Content.Server.Doors.Components;
-using Content.Server.Tools;
 using Content.Server.Tools.Systems;
 using Content.Shared.Access.Components;
 using Content.Shared.Access.Systems;
@@ -17,9 +15,9 @@ using Content.Shared.Tools.Components;
 using Content.Shared.Verbs;
 using Robust.Shared.Audio;
 using Robust.Shared.Containers;
-using Robust.Shared.Player;
 using System.Linq;
 using Content.Server.Power.EntitySystems;
+using Content.Shared.Tools;
 using Robust.Shared.Physics.Components;
 using Robust.Shared.Physics.Events;
 
@@ -31,7 +29,7 @@ public sealed class DoorSystem : SharedDoorSystem
     [Dependency] private readonly AirlockSystem _airlock = default!;
     [Dependency] private readonly AirtightSystem _airtightSystem = default!;
     [Dependency] private readonly ConstructionSystem _constructionSystem = default!;
-    [Dependency] private readonly ToolSystem _toolSystem = default!;
+    [Dependency] private readonly SharedToolSystem _toolSystem = default!;
     [Dependency] private readonly SharedContainerSystem _containerSystem = default!;
 
     public override void Initialize()
@@ -197,9 +195,8 @@ public sealed class DoorSystem : SharedDoorSystem
         RaiseLocalEvent(target, modEv, false);
 
         door.BeingPried = true;
-        _toolSystem.UseTool(tool, user, target, 0f, modEv.PryTimeModifier * door.PryTime, door.PryingQuality,
-                new PryFinishedEvent(), new PryCancelledEvent(), target);
-
+        var toolEvData = new ToolEventData(new PryFinishedEvent(), cancelledEv: new PryCancelledEvent(),targetEntity: target);
+        _toolSystem.UseTool(tool, user, target, modEv.PryTimeModifier * door.PryTime, new[] { door.PryingQuality }, toolEvData);
         return true; // we might not actually succeeded, but a do-after has started
     }
 
index 490d132a08c91b16544f96627f785fbcef27122a..2222b64a158d6588934bb7675ac2839f44cc6d13 100644 (file)
@@ -102,8 +102,6 @@ namespace Content.Server.Dragon
                 Params = AudioParams.Default.WithVolume(3f),
             };
 
-        public CancellationTokenSource? CancelToken;
-
         [ViewVariables(VVAccess.ReadWrite), DataField("devourWhitelist")]
         public EntityWhitelist? DevourWhitelist = new()
         {
index adc6ab92daadd80ab33175fee62869c623554338..f4d3809b85327bc1c5e665d4c6bf91982fd0d535 100644 (file)
@@ -9,7 +9,6 @@ using System.Threading;
 using Content.Server.Chat.Systems;
 using Content.Server.GameTicking;
 using Content.Server.GameTicking.Rules;
-using Content.Server.Humanoid;
 using Content.Server.NPC;
 using Content.Shared.Damage;
 using Content.Shared.Dragon;
@@ -20,6 +19,7 @@ using Robust.Shared.GameStates;
 using Robust.Shared.Map;
 using Robust.Shared.Random;
 using Content.Server.NPC.Systems;
+using Content.Shared.DoAfter;
 using Content.Shared.Humanoid;
 using Content.Shared.Mobs;
 using Content.Shared.Mobs.Components;
@@ -59,13 +59,12 @@ namespace Content.Server.Dragon
 
             SubscribeLocalEvent<DragonComponent, ComponentStartup>(OnStartup);
             SubscribeLocalEvent<DragonComponent, ComponentShutdown>(OnShutdown);
-            SubscribeLocalEvent<DragonComponent, DragonDevourComplete>(OnDragonDevourComplete);
             SubscribeLocalEvent<DragonComponent, DragonDevourActionEvent>(OnDevourAction);
             SubscribeLocalEvent<DragonComponent, DragonSpawnRiftActionEvent>(OnDragonRift);
             SubscribeLocalEvent<DragonComponent, RefreshMovementSpeedModifiersEvent>(OnDragonMove);
 
-            SubscribeLocalEvent<DragonComponent, DragonStructureDevourComplete>(OnDragonStructureDevourComplete);
-            SubscribeLocalEvent<DragonComponent, DragonDevourCancelledEvent>(OnDragonDevourCancelled);
+            SubscribeLocalEvent<DragonComponent, DoAfterEvent>(OnDoAfter);
+
             SubscribeLocalEvent<DragonComponent, MobStateChangedEvent>(OnMobStateChanged);
 
             SubscribeLocalEvent<DragonRiftComponent, ComponentShutdown>(OnRiftShutdown);
@@ -76,6 +75,30 @@ namespace Content.Server.Dragon
             SubscribeLocalEvent<RoundEndTextAppendEvent>(OnRiftRoundEnd);
         }
 
+        private void OnDoAfter(EntityUid uid, DragonComponent component, DoAfterEvent args)
+        {
+            if (args.Handled || args.Cancelled)
+                return;
+
+            var ichorInjection = new Solution(component.DevourChem, component.DevourHealRate);
+
+            //Humanoid devours allow dragon to get eggs, corpses included
+            if (HasComp<HumanoidAppearanceComponent>(args.Args.Target))
+            {
+                ichorInjection.ScaleSolution(0.5f);
+                component.DragonStomach.Insert(args.Args.Target.Value);
+                _bloodstreamSystem.TryAddToChemicals(uid, ichorInjection);
+            }
+
+            //TODO: Figure out a better way of removing structures via devour that still entails standing still and waiting for a DoAfter. Somehow.
+            //If it's not human, it must be a structure
+            else if (args.Args.Target != null)
+                EntityManager.QueueDeleteEntity(args.Args.Target.Value);
+
+            if (component.SoundDevour != null)
+                _audioSystem.PlayPvs(component.SoundDevour, uid, component.SoundDevour.Params);
+        }
+
         public override void Update(float frameTime)
         {
             base.Update(frameTime);
@@ -292,39 +315,6 @@ namespace Content.Server.Dragon
             }
         }
 
-        private void OnDragonDevourCancelled(EntityUid uid, DragonComponent component, DragonDevourCancelledEvent args)
-        {
-            component.CancelToken = null;
-        }
-
-        private void OnDragonDevourComplete(EntityUid uid, DragonComponent component, DragonDevourComplete args)
-        {
-            component.CancelToken = null;
-            var ichorInjection = new Solution(component.DevourChem, component.DevourHealRate);
-
-            //Humanoid devours allow dragon to get eggs, corpses included
-            if (!EntityManager.HasComponent<HumanoidAppearanceComponent>(args.Target))
-            {
-                ichorInjection.ScaleSolution(0.5f);
-            }
-
-            _bloodstreamSystem.TryAddToChemicals(uid, ichorInjection);
-            component.DragonStomach.Insert(args.Target);
-
-            if (component.SoundDevour != null)
-                _audioSystem.PlayPvs(component.SoundDevour, uid, component.SoundDevour.Params);
-        }
-
-        private void OnDragonStructureDevourComplete(EntityUid uid, DragonComponent component, DragonStructureDevourComplete args)
-        {
-            component.CancelToken = null;
-            //TODO: Figure out a better way of removing structures via devour that still entails standing still and waiting for a DoAfter. Somehow.
-            EntityManager.QueueDeleteEntity(args.Target);
-
-            if (component.SoundDevour != null)
-                _audioSystem.PlayPvs(component.SoundDevour, uid, component.SoundDevour.Params);
-        }
-
         private void Roar(DragonComponent component)
         {
             if (component.SoundRoar != null)
@@ -351,12 +341,8 @@ namespace Content.Server.Dragon
         /// </summary>
         private void OnDevourAction(EntityUid uid, DragonComponent component, DragonDevourActionEvent args)
         {
-            if (component.CancelToken != null ||
-                args.Handled ||
-                component.DevourWhitelist?.IsValid(args.Target, EntityManager) != true)
-            {
+            if (args.Handled || component.DevourWhitelist?.IsValid(args.Target, EntityManager) != true)
                 return;
-            }
 
             args.Handled = true;
             var target = args.Target;
@@ -368,12 +354,9 @@ namespace Content.Server.Dragon
                 {
                     case MobState.Critical:
                     case MobState.Dead:
-                        component.CancelToken = new CancellationTokenSource();
 
-                        _doAfterSystem.DoAfter(new DoAfterEventArgs(uid, component.DevourTime, component.CancelToken.Token, target)
+                        _doAfterSystem.DoAfter(new DoAfterEventArgs(uid, component.DevourTime, target:target)
                         {
-                            UserFinishedEvent = new DragonDevourComplete(uid, target),
-                            UserCancelledEvent = new DragonDevourCancelledEvent(),
                             BreakOnTargetMove = true,
                             BreakOnUserMove = true,
                             BreakOnStun = true,
@@ -392,42 +375,12 @@ namespace Content.Server.Dragon
             if (component.SoundStructureDevour != null)
                 _audioSystem.PlayPvs(component.SoundStructureDevour, uid, component.SoundStructureDevour.Params);
 
-            component.CancelToken = new CancellationTokenSource();
-
-            _doAfterSystem.DoAfter(new DoAfterEventArgs(uid, component.StructureDevourTime, component.CancelToken.Token, target)
+            _doAfterSystem.DoAfter(new DoAfterEventArgs(uid, component.StructureDevourTime, target:target)
             {
-                UserFinishedEvent = new DragonStructureDevourComplete(uid, target),
-                UserCancelledEvent = new DragonDevourCancelledEvent(),
                 BreakOnTargetMove = true,
                 BreakOnUserMove = true,
                 BreakOnStun = true,
             });
         }
-
-        private sealed class DragonDevourComplete : EntityEventArgs
-        {
-            public EntityUid User { get; }
-            public EntityUid Target { get; }
-
-            public DragonDevourComplete(EntityUid user, EntityUid target)
-            {
-                User = user;
-                Target = target;
-            }
-        }
-
-        private sealed class DragonStructureDevourComplete : EntityEventArgs
-        {
-            public EntityUid User { get; }
-            public EntityUid Target { get; }
-
-            public DragonStructureDevourComplete(EntityUid user, EntityUid target)
-            {
-                 User = user;
-                 Target = target;
-            }
-        }
-
-        private sealed class DragonDevourCancelledEvent : EntityEventArgs {}
     }
 }
index b4fd4251039989864265ba8ecd3a7eb3cad209c5..79d25f3520b5873cf4ae24a42a74fcbe40c990ff 100644 (file)
@@ -1,5 +1,6 @@
 using Content.Server.DoAfter;
 using Content.Server.Engineering.Components;
+using Content.Shared.DoAfter;
 using Content.Shared.Hands.EntitySystems;
 using Content.Shared.Verbs;
 using JetBrains.Annotations;
index 3fc4a40d0fae162347405bcaf8c5cca020370182..988b72ae883ee081710bbe7dc83913ca40cb987e 100644 (file)
@@ -2,6 +2,7 @@ using Content.Server.Coordinates.Helpers;
 using Content.Server.DoAfter;
 using Content.Server.Engineering.Components;
 using Content.Server.Stack;
+using Content.Shared.DoAfter;
 using Content.Shared.Interaction;
 using Content.Shared.Maps;
 using Content.Shared.Stacks;
index 0bfde38c787c9805e9b36f36eaf6785390344b02..dbb45f1f9b5c701c4ac3c8e2c6efdb878684e940 100644 (file)
@@ -1,6 +1,7 @@
 using System.Threading;
 using Content.Server.DoAfter;
 using Content.Shared.Alert;
+using Content.Shared.DoAfter;
 using Content.Shared.Ensnaring.Components;
 using Content.Shared.IdentityManagement;
 using Content.Shared.StepTrigger.Systems;
@@ -27,7 +28,7 @@ public sealed partial class EnsnareableSystem
             return;
 
         if (ensnared.IsEnsnared)
-            ForceFree(component);
+            ForceFree(uid, component);
     }
 
     private void AttemptStepTrigger(EntityUid uid, EnsnaringComponent component, ref StepTriggerAttemptEvent args)
@@ -37,7 +38,7 @@ public sealed partial class EnsnareableSystem
 
     private void OnStepTrigger(EntityUid uid, EnsnaringComponent component, ref StepTriggeredEvent args)
     {
-        TryEnsnare(args.Tripper, component);
+        TryEnsnare(args.Tripper, uid, component);
     }
 
     private void OnThrowHit(EntityUid uid, EnsnaringComponent component, ThrowDoHitEvent args)
@@ -45,26 +46,27 @@ public sealed partial class EnsnareableSystem
         if (!component.CanThrowTrigger)
             return;
 
-        TryEnsnare(args.Target, component);
+        TryEnsnare(args.Target, uid, component);
     }
 
     /// <summary>
     /// Used where you want to try to ensnare an entity with the <see cref="EnsnareableComponent"/>
     /// </summary>
     /// <param name="target">The entity that will be ensnared</param>
+    /// <paramref name="ensnare"> The entity that is used to ensnare</param>
     /// <param name="component">The ensnaring component</param>
-    public void TryEnsnare(EntityUid target, EnsnaringComponent component)
+    public void TryEnsnare(EntityUid target, EntityUid ensnare, EnsnaringComponent component)
     {
         //Don't do anything if they don't have the ensnareable component.
         if (!TryComp<EnsnareableComponent>(target, out var ensnareable))
             return;
 
         component.Ensnared = target;
-        ensnareable.Container.Insert(component.Owner);
+        ensnareable.Container.Insert(ensnare);
         ensnareable.IsEnsnared = true;
         Dirty(ensnareable);
 
-        UpdateAlert(ensnareable);
+        UpdateAlert(ensnare, ensnareable);
         var ev = new EnsnareEvent(component.WalkSpeed, component.SprintSpeed);
         RaiseLocalEvent(target, ev);
     }
@@ -73,18 +75,14 @@ public sealed partial class EnsnareableSystem
     /// Used where you want to try to free an entity with the <see cref="EnsnareableComponent"/>
     /// </summary>
     /// <param name="target">The entity that will be free</param>
+    /// <param name="ensnare">The entity used to ensnare</param>
     /// <param name="component">The ensnaring component</param>
-    public void TryFree(EntityUid target, EnsnaringComponent component, EntityUid? user = null)
+    public void TryFree(EntityUid target, EntityUid ensnare, EnsnaringComponent component, EntityUid? user = null)
     {
         //Don't do anything if they don't have the ensnareable component.
         if (!HasComp<EnsnareableComponent>(target))
             return;
 
-        if (component.CancelToken != null)
-            return;
-
-        component.CancelToken = new CancellationTokenSource();
-
         var isOwner = !(user != null && target != user);
         var freeTime = isOwner ? component.BreakoutTime : component.FreeTime;
         bool breakOnMove;
@@ -94,55 +92,47 @@ public sealed partial class EnsnareableSystem
         else
             breakOnMove = true;
 
-        var doAfterEventArgs = new DoAfterEventArgs(target, freeTime, component.CancelToken.Token, target)
+        var doAfterEventArgs = new DoAfterEventArgs(target, freeTime, target: target, used:ensnare)
         {
             BreakOnUserMove = breakOnMove,
             BreakOnTargetMove = breakOnMove,
             BreakOnDamage = false,
             BreakOnStun = true,
-            NeedHand = true,
-            TargetFinishedEvent = new FreeEnsnareDoAfterComplete(component.Owner),
-            TargetCancelledEvent = new FreeEnsnareDoAfterCancel(component.Owner),
+            NeedHand = true
         };
 
         _doAfter.DoAfter(doAfterEventArgs);
 
         if (isOwner)
-            _popup.PopupEntity(Loc.GetString("ensnare-component-try-free", ("ensnare", component.Owner)), target, target);
+            _popup.PopupEntity(Loc.GetString("ensnare-component-try-free", ("ensnare", ensnare)), target, target);
 
         if (!isOwner && user != null)
-        {
-            _popup.PopupEntity(Loc.GetString("ensnare-component-try-free-other", ("ensnare", component.Owner), ("user", Identity.Entity(target, EntityManager))), user.Value, user.Value);
-        }
+            _popup.PopupEntity(Loc.GetString("ensnare-component-try-free-other", ("ensnare", ensnare), ("user", Identity.Entity(target, EntityManager))), user.Value, user.Value);
     }
 
     /// <summary>
     /// Used to force free someone for things like if the <see cref="EnsnaringComponent"/> is removed
     /// </summary>
-    public void ForceFree(EnsnaringComponent component)
+    public void ForceFree(EntityUid ensnare, EnsnaringComponent component)
     {
         if (!TryComp<EnsnareableComponent>(component.Ensnared, out var ensnareable))
             return;
 
-        ensnareable.Container.ForceRemove(component.Owner);
+        ensnareable.Container.ForceRemove(ensnare);
         ensnareable.IsEnsnared = false;
         Dirty(ensnareable);
         component.Ensnared = null;
 
-        UpdateAlert(ensnareable);
+        UpdateAlert(ensnare, ensnareable);
         var ev = new EnsnareRemoveEvent();
-        RaiseLocalEvent(component.Owner, ev);
+        RaiseLocalEvent(ensnare, ev);
     }
 
-    public void UpdateAlert(EnsnareableComponent component)
+    public void UpdateAlert(EntityUid ensnare, EnsnareableComponent component)
     {
         if (!component.IsEnsnared)
-        {
-            _alerts.ClearAlert(component.Owner, AlertType.Ensnared);
-        }
+            _alerts.ClearAlert(ensnare, AlertType.Ensnared);
         else
-        {
-            _alerts.ShowAlert(component.Owner, AlertType.Ensnared);
-        }
+            _alerts.ShowAlert(ensnare, AlertType.Ensnared);
     }
 }
index cbb025459f7bb7f6be50006d8a10eb07f75bdb43..d883cdb4188c47297f1c08a2e98b7d493bc0180d 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Server.Popups;
+using Content.Shared.DoAfter;
 using Content.Shared.Ensnaring;
 using Content.Shared.Ensnaring.Components;
 using Content.Shared.Popups;
@@ -19,43 +20,36 @@ public sealed partial class EnsnareableSystem : SharedEnsnareableSystem
         InitializeEnsnaring();
 
         SubscribeLocalEvent<EnsnareableComponent, ComponentInit>(OnEnsnareableInit);
-        SubscribeLocalEvent<EnsnareableComponent, FreeEnsnareDoAfterComplete>(OnFreeComplete);
-        SubscribeLocalEvent<EnsnareableComponent, FreeEnsnareDoAfterCancel>(OnFreeFail);
+        SubscribeLocalEvent<EnsnareableComponent, DoAfterEvent>(OnDoAfter);
     }
 
     private void OnEnsnareableInit(EntityUid uid, EnsnareableComponent component, ComponentInit args)
     {
-        component.Container = _container.EnsureContainer<Container>(component.Owner, "ensnare");
+        component.Container = _container.EnsureContainer<Container>(uid, "ensnare");
     }
 
-    private void OnFreeComplete(EntityUid uid, EnsnareableComponent component, FreeEnsnareDoAfterComplete args)
+    private void OnDoAfter(EntityUid uid, EnsnareableComponent component, DoAfterEvent args)
     {
-        if (!TryComp<EnsnaringComponent>(args.EnsnaringEntity, out var ensnaring))
+        if (args.Handled || !TryComp<EnsnaringComponent>(args.Args.Used, out var ensnaring))
             return;
 
-        component.Container.Remove(args.EnsnaringEntity);
+        if (args.Cancelled)
+        {
+            _popup.PopupEntity(Loc.GetString("ensnare-component-try-free-fail", ("ensnare", args.Args.Used)), uid, uid, PopupType.Large);
+            return;
+        }
+
+        component.Container.Remove(args.Args.Used.Value);
         component.IsEnsnared = false;
         Dirty(component);
         ensnaring.Ensnared = null;
 
-        _popup.PopupEntity(Loc.GetString("ensnare-component-try-free-complete", ("ensnare", args.EnsnaringEntity)),
-            uid, uid, PopupType.Large);
+        _popup.PopupEntity(Loc.GetString("ensnare-component-try-free-complete", ("ensnare", args.Args.Used)), uid, uid, PopupType.Large);
 
-        UpdateAlert(component);
+        UpdateAlert(args.Args.Used.Value, component);
         var ev = new EnsnareRemoveEvent();
         RaiseLocalEvent(uid, ev);
 
-        ensnaring.CancelToken = null;
-    }
-
-    private void OnFreeFail(EntityUid uid, EnsnareableComponent component, FreeEnsnareDoAfterCancel args)
-    {
-        if (!TryComp<EnsnaringComponent>(args.EnsnaringEntity, out var ensnaring))
-            return;
-
-        ensnaring.CancelToken = null;
-
-        _popup.PopupEntity(Loc.GetString("ensnare-component-try-free-fail", ("ensnare", args.EnsnaringEntity)),
-            uid, uid, PopupType.Large);
+        args.Handled = true;
     }
 }
index 80e1de68a816657b292226be626337f689fd5ca7..4872cd359a40f2a7925c9e45db58c5dbc4188e70 100644 (file)
@@ -5,6 +5,7 @@ using Content.Shared.Clothing.Components;
 using Content.Shared.Inventory;
 using Content.Shared.Inventory.Events;
 using Content.Server.Tools;
+using Content.Shared.Tools.Components;
 
 namespace Content.Server.Eye.Blinding.EyeProtection
 {
index 2d1f23d5c831ad9b7af3ddcec2168b849a2cb34f..eb121215666803227bae1f8eebc40b3f031f3fbd 100644 (file)
@@ -1,5 +1,3 @@
-using System.Threading;
-
 namespace Content.Server.Fluids.Components;
 
 [RegisterComponent]
@@ -17,6 +15,4 @@ public sealed class SpillableComponent : Component
 
     [DataField("spillDelay")]
     public float? SpillDelay;
-
-    public CancellationTokenSource? CancelToken;
 }
index 44939ad969b09bf836bd06cd95c7ab388b78b1a3..627227920536d36d5f7911973756a0e9c5821d3e 100644 (file)
@@ -5,6 +5,7 @@ using Content.Server.DoAfter;
 using Content.Server.Fluids.Components;
 using Content.Server.Popups;
 using Content.Shared.Chemistry.Components;
+using Content.Shared.DoAfter;
 using Content.Shared.FixedPoint;
 using Content.Shared.Fluids;
 using Content.Shared.Interaction;
@@ -34,9 +35,7 @@ public sealed class MoppingSystem : SharedMoppingSystem
         base.Initialize();
         SubscribeLocalEvent<AbsorbentComponent, ComponentInit>(OnAbsorbentInit);
         SubscribeLocalEvent<AbsorbentComponent, AfterInteractEvent>(OnAfterInteract);
-        SubscribeLocalEvent<AbsorbentComponent, SolutionChangedEvent>(OnAbsorbentSolutionChange);
-        SubscribeLocalEvent<TransferCancelledEvent>(OnTransferCancelled);
-        SubscribeLocalEvent<TransferCompleteEvent>(OnTransferComplete);
+        SubscribeLocalEvent<AbsorbentComponent, DoAfterEvent<AbsorbantData>>(OnDoAfter);
     }
 
     private void OnAbsorbentInit(EntityUid uid, AbsorbentComponent component, ComponentInit args)
@@ -256,63 +255,38 @@ public sealed class MoppingSystem : SharedMoppingSystem
         if (!component.InteractingEntities.Add(target))
             return;
 
-        var doAfterArgs = new DoAfterEventArgs(user, delay, target: target)
+        var aborbantData = new AbsorbantData(targetSolution, msg, sfx, transferAmount);
+
+        var doAfterArgs = new DoAfterEventArgs(user, delay, target: target, used:used)
         {
             BreakOnUserMove = true,
             BreakOnStun = true,
             BreakOnDamage = true,
-            MovementThreshold = 0.2f,
-            BroadcastCancelledEvent = new TransferCancelledEvent(target, component),
-            BroadcastFinishedEvent = new TransferCompleteEvent(used, target, component, targetSolution, msg, sfx, transferAmount)
+            MovementThreshold = 0.2f
         };
 
-        _doAfterSystem.DoAfter(doAfterArgs);
+        _doAfterSystem.DoAfter(doAfterArgs, aborbantData);
     }
 
-    private void OnTransferComplete(TransferCompleteEvent ev)
+    private void OnDoAfter(EntityUid uid, AbsorbentComponent component, DoAfterEvent<AbsorbantData> args)
     {
-        _audio.PlayPvs(ev.Sound, ev.Tool);
-        _popups.PopupEntity(ev.Message, ev.Tool);
-        _solutionSystem.TryTransferSolution(ev.Target, ev.Tool, ev.TargetSolution, AbsorbentComponent.SolutionName, ev.TransferAmount);
-        ev.Component.InteractingEntities.Remove(ev.Target);
-    }
+        if (args.Handled || args.Cancelled || args.Args.Target == null)
+            return;
 
-    private void OnTransferCancelled(TransferCancelledEvent ev)
-    {
-        ev.Component.InteractingEntities.Remove(ev.Target);
-    }
-}
+        _audio.PlayPvs(args.AdditionalData.Sound, uid);
+        _popups.PopupEntity(Loc.GetString(args.AdditionalData.Message, ("target", args.Args.Target.Value), ("used", uid)), uid);
+        _solutionSystem.TryTransferSolution(args.Args.Target.Value, uid, args.AdditionalData.TargetSolution,
+            AbsorbentComponent.SolutionName, args.AdditionalData.TransferAmount);
+        component.InteractingEntities.Remove(args.Args.Target.Value);
 
-public sealed class TransferCompleteEvent : EntityEventArgs
-{
-    public readonly EntityUid Tool;
-    public readonly EntityUid Target;
-    public readonly AbsorbentComponent Component;
-    public readonly string TargetSolution;
-    public readonly string Message;
-    public readonly SoundSpecifier Sound;
-    public readonly FixedPoint2 TransferAmount;
-
-    public TransferCompleteEvent(EntityUid tool, EntityUid target, AbsorbentComponent component, string targetSolution, string message, SoundSpecifier sound, FixedPoint2 transferAmount)
-    {
-        Tool = tool;
-        Target = target;
-        Component = component;
-        TargetSolution = targetSolution;
-        Message = Loc.GetString(message, ("target", target), ("used", tool));
-        Sound = sound;
-        TransferAmount = transferAmount;
+        args.Handled = true;
     }
-}
-
-public sealed class TransferCancelledEvent : EntityEventArgs
-{
-    public readonly EntityUid Target;
-    public readonly AbsorbentComponent Component;
 
-    public TransferCancelledEvent(EntityUid target, AbsorbentComponent component)
+    private record struct AbsorbantData(string TargetSolution, string Message, SoundSpecifier Sound, FixedPoint2 TransferAmount)
     {
-        Target = target;
-        Component = component;
+        public readonly string TargetSolution = TargetSolution;
+        public readonly string Message = Message;
+        public readonly SoundSpecifier Sound = Sound;
+        public readonly FixedPoint2 TransferAmount = TransferAmount;
     }
 }
index 32fbe0e769e76fc3f0d7278a47aff3653c070d2a..32e6dcbaa45597aea721abf99ea7dec321cb0e7d 100644 (file)
@@ -16,7 +16,7 @@ using Robust.Shared.Map;
 using Robust.Shared.Prototypes;
 using System.Diagnostics.CodeAnalysis;
 using System.Linq;
-using System.Threading;
+using Content.Shared.DoAfter;
 
 namespace Content.Server.Fluids.EntitySystems;
 
@@ -38,8 +38,7 @@ public sealed class SpillableSystem : EntitySystem
         SubscribeLocalEvent<SpillableComponent, GetVerbsEvent<Verb>>(AddSpillVerb);
         SubscribeLocalEvent<SpillableComponent, GotEquippedEvent>(OnGotEquipped);
         SubscribeLocalEvent<SpillableComponent, SolutionSpikeOverflowEvent>(OnSpikeOverflow);
-        SubscribeLocalEvent<SpillableComponent, SpillFinishedEvent>(OnSpillFinished);
-        SubscribeLocalEvent<SpillableComponent, SpillCancelledEvent>(OnSpillCancelled);
+        SubscribeLocalEvent<SpillableComponent, DoAfterEvent>(OnDoAfter);
     }
 
     private void OnSpikeOverflow(EntityUid uid, SpillableComponent component, SolutionSpikeOverflowEvent args)
@@ -142,20 +141,14 @@ public sealed class SpillableSystem : EntitySystem
         {
             verb.Act = () =>
             {
-                if (component.CancelToken == null)
+                _doAfterSystem.DoAfter(new DoAfterEventArgs(args.User, component.SpillDelay.Value, target:uid)
                 {
-                    component.CancelToken = new CancellationTokenSource();
-                    _doAfterSystem.DoAfter(new DoAfterEventArgs(args.User, component.SpillDelay.Value, component.CancelToken.Token, component.Owner)
-                    {
-                        BreakOnTargetMove = true,
-                        BreakOnUserMove = true,
-                        BreakOnDamage = true,
-                        BreakOnStun = true,
-                        NeedHand = true,
-                        TargetFinishedEvent = new SpillFinishedEvent(args.User, component.Owner, solution),
-                        TargetCancelledEvent = new SpillCancelledEvent(component.Owner)
-                    });
-                }
+                    BreakOnTargetMove = true,
+                    BreakOnUserMove = true,
+                    BreakOnDamage = true,
+                    BreakOnStun = true,
+                    NeedHand = true
+                });
             };
         }
         verb.Impact = LogImpact.Medium; // dangerous reagent reaction are logged separately.
@@ -263,46 +256,19 @@ public sealed class SpillableSystem : EntitySystem
         return puddleComponent;
     }
 
-    private void OnSpillFinished(EntityUid uid, SpillableComponent component, SpillFinishedEvent ev)
+    private void OnDoAfter(EntityUid uid, SpillableComponent component, DoAfterEvent args)
     {
-        component.CancelToken = null;
+        if (args.Handled || args.Cancelled || args.Args.Target == null)
+            return;
 
         //solution gone by other means before doafter completes
-        if (ev.Solution == null || ev.Solution.Volume == 0)
+        if (!_solutionContainerSystem.TryGetDrainableSolution(uid, out var solution) || solution.Volume == 0)
             return;
 
-        var puddleSolution = _solutionContainerSystem.SplitSolution(uid,
-            ev.Solution, ev.Solution.Volume);
-
-        SpillAt(puddleSolution, Transform(component.Owner).Coordinates, "PuddleSmear");
-    }
-
-    private void OnSpillCancelled(EntityUid uid, SpillableComponent component, SpillCancelledEvent ev)
-    {
-        component.CancelToken = null;
-    }
-
-    internal sealed class SpillFinishedEvent : EntityEventArgs
-    {
-        public SpillFinishedEvent(EntityUid user, EntityUid spillable, Solution solution)
-        {
-            User = user;
-            Spillable = spillable;
-            Solution = solution;
-        }
-
-        public EntityUid User { get; }
-        public EntityUid Spillable { get; }
-        public Solution Solution { get; }
-    }
+        var puddleSolution = _solutionContainerSystem.SplitSolution(uid, solution, solution.Volume);
 
-    private sealed class SpillCancelledEvent : EntityEventArgs
-    {
-        public EntityUid Spillable;
+        SpillAt(puddleSolution, Transform(uid).Coordinates, "PuddleSmear");
 
-        public SpillCancelledEvent(EntityUid spillable)
-        {
-            Spillable = spillable;
-        }
+        args.Handled = true;
     }
 }
index ffdf7b7683e2d3c09fb490d90130488a8c494234..8476d17a0dc973d9651f279c9666d3d28adcbf13 100644 (file)
@@ -1,5 +1,3 @@
-using System.Threading;
-
 namespace Content.Server.Forensics
 {
     /// <summary>
@@ -8,8 +6,6 @@ namespace Content.Server.Forensics
     [RegisterComponent]
     public sealed class ForensicPadComponent : Component
     {
-        public CancellationTokenSource? CancelToken;
-
         [DataField("scanDelay")]
         public float ScanDelay = 3.0f;
 
index 1ac262c05fc09d44490156c8e588e2fdcf80ea5f..e7f2355056532ed639c99fe622a20eea84e7f5b4 100644 (file)
@@ -1,11 +1,11 @@
-using System.Threading;
 using Content.Shared.Examine;
 using Content.Shared.Interaction;
 using Content.Shared.Inventory;
 using Content.Server.DoAfter;
 using Content.Server.Popups;
+using Content.Shared.DoAfter;
 using Content.Shared.IdentityManagement;
-using Robust.Shared.Player;
+using Robust.Shared.Serialization;
 
 namespace Content.Server.Forensics
 {
@@ -16,7 +16,6 @@ namespace Content.Server.Forensics
     {
         [Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
         [Dependency] private readonly InventorySystem _inventory = default!;
-
         [Dependency] private readonly PopupSystem _popupSystem = default!;
 
         public override void Initialize()
@@ -24,8 +23,7 @@ namespace Content.Server.Forensics
             base.Initialize();
             SubscribeLocalEvent<ForensicPadComponent, ExaminedEvent>(OnExamined);
             SubscribeLocalEvent<ForensicPadComponent, AfterInteractEvent>(OnAfterInteract);
-            SubscribeLocalEvent<TargetPadSuccessfulEvent>(OnTargetPadSuccessful);
-            SubscribeLocalEvent<PadCancelledEvent>(OnPadCancelled);
+            SubscribeLocalEvent<ForensicPadComponent, DoAfterEvent<ForensicPadData>>(OnDoAfter);
         }
 
         private void OnExamined(EntityUid uid, ForensicPadComponent component, ExaminedEvent args)
@@ -44,7 +42,7 @@ namespace Content.Server.Forensics
 
         private void OnAfterInteract(EntityUid uid, ForensicPadComponent component, AfterInteractEvent args)
         {
-            if (component.CancelToken != null || !args.CanReach || args.Target == null)
+            if (!args.CanReach || args.Target == null)
                 return;
 
             if (HasComp<ForensicScannerComponent>(args.Target))
@@ -71,74 +69,58 @@ namespace Content.Server.Forensics
                     _popupSystem.PopupEntity(Loc.GetString("forensic-pad-start-scan-user", ("target", Identity.Entity(args.Target.Value, EntityManager))), args.Target.Value, args.User);
                     _popupSystem.PopupEntity(Loc.GetString("forensic-pad-start-scan-target", ("user", Identity.Entity(args.User, EntityManager))), args.Target.Value, args.Target.Value);
                 }
-                StartScan(args.User, args.Target.Value, component, fingerprint.Fingerprint);
+                StartScan(uid, args.User, args.Target.Value, component, fingerprint.Fingerprint);
                 return;
             }
 
             if (TryComp<FiberComponent>(args.Target, out var fiber))
-                StartScan(args.User, args.Target.Value, component, string.IsNullOrEmpty(fiber.FiberColor) ? Loc.GetString("forensic-fibers", ("material", fiber.FiberMaterial)) : Loc.GetString("forensic-fibers-colored", ("color", fiber.FiberColor), ("material", fiber.FiberMaterial)));
+                StartScan(uid, args.User, args.Target.Value, component, string.IsNullOrEmpty(fiber.FiberColor) ? Loc.GetString("forensic-fibers", ("material", fiber.FiberMaterial)) : Loc.GetString("forensic-fibers-colored", ("color", fiber.FiberColor), ("material", fiber.FiberMaterial)));
         }
 
-        private void StartScan(EntityUid user, EntityUid target, ForensicPadComponent pad, string sample)
+        private void StartScan(EntityUid used, EntityUid user, EntityUid target, ForensicPadComponent pad, string sample)
         {
-            pad.CancelToken = new CancellationTokenSource();
-            _doAfterSystem.DoAfter(new DoAfterEventArgs(user, pad.ScanDelay, pad.CancelToken.Token, target: target)
+            var padData = new ForensicPadData(sample);
+
+            var doAfterEventArgs = new DoAfterEventArgs(user, pad.ScanDelay, target: target, used: used)
             {
-                BroadcastFinishedEvent = new TargetPadSuccessfulEvent(user, target, pad.Owner, sample),
-                BroadcastCancelledEvent = new PadCancelledEvent(pad.Owner),
                 BreakOnTargetMove = true,
                 BreakOnUserMove = true,
                 BreakOnStun = true,
                 NeedHand = true
-            });
-        }
-
-        /// <summary>
-        /// When the forensic pad is successfully used, take their fingerprint sample and flag the pad as used.
-        /// </summary>
-        private void OnTargetPadSuccessful(TargetPadSuccessfulEvent ev)
-        {
-            if (!EntityManager.TryGetComponent(ev.Pad, out ForensicPadComponent? component))
-                return;
+            };
 
-            if (HasComp<FingerprintComponent>(ev.Target))
-                MetaData(component.Owner).EntityName = Loc.GetString("forensic-pad-fingerprint-name", ("entity", ev.Target));
-            else
-                MetaData(component.Owner).EntityName = Loc.GetString("forensic-pad-gloves-name", ("entity", ev.Target));
-
-            component.CancelToken = null;
-            component.Sample = ev.Sample;
-            component.Used = true;
-        }
-        private void OnPadCancelled(PadCancelledEvent ev)
-        {
-            if (!EntityManager.TryGetComponent(ev.Pad, out ForensicPadComponent? component))
-                return;
-            component.CancelToken = null;
+            _doAfterSystem.DoAfter(doAfterEventArgs, padData);
         }
 
-        private sealed class PadCancelledEvent : EntityEventArgs
+        private void OnDoAfter(EntityUid uid, ForensicPadComponent component, DoAfterEvent<ForensicPadData> args)
         {
-            public EntityUid Pad;
+            if (args.Handled
+                || args.Cancelled
+                || !EntityManager.TryGetComponent(args.Args.Used, out ForensicPadComponent? padComponent))
+            {
+                return;
+            }
 
-            public PadCancelledEvent(EntityUid pad)
+            if (args.Args.Target != null)
             {
-                Pad = pad;
+                if (HasComp<FingerprintComponent>(args.Args.Target))
+                    MetaData(uid).EntityName = Loc.GetString("forensic-pad-fingerprint-name", ("entity", args.Args.Target));
+                else
+                    MetaData(uid).EntityName = Loc.GetString("forensic-pad-gloves-name", ("entity", args.Args.Target));
             }
+
+            padComponent.Sample = args.AdditionalData.Sample;
+            padComponent.Used = true;
+
+            args.Handled = true;
         }
 
-        private sealed class TargetPadSuccessfulEvent : EntityEventArgs
+        private sealed class ForensicPadData
         {
-            public EntityUid User;
-            public EntityUid Target;
-            public EntityUid Pad;
-            public string Sample = string.Empty;
+            public string Sample;
 
-            public TargetPadSuccessfulEvent(EntityUid user, EntityUid target, EntityUid pad, string sample)
+            public ForensicPadData(string sample)
             {
-                User = user;
-                Target = target;
-                Pad = pad;
                 Sample = sample;
             }
         }
index 146414b8d39cc45f5192c0a292f0278e26209770..1f7df3d82fa1194b969bcb6b3f7615248e5d32b0 100644 (file)
@@ -1,14 +1,13 @@
 using System.Linq;
 using System.Text; // todo: remove this stinky LINQy
-using System.Threading;
 using Robust.Server.GameObjects;
 using Robust.Shared.Audio;
-using Robust.Shared.Player;
 using Robust.Shared.Timing;
 using Content.Server.DoAfter;
 using Content.Server.Paper;
 using Content.Server.Popups;
 using Content.Server.UserInterface;
+using Content.Shared.DoAfter;
 using Content.Shared.Forensics;
 using Content.Shared.Hands.EntitySystems;
 using Content.Shared.Interaction;
@@ -40,8 +39,7 @@ namespace Content.Server.Forensics
             SubscribeLocalEvent<ForensicScannerComponent, GetVerbsEvent<UtilityVerb>>(OnUtilityVerb);
             SubscribeLocalEvent<ForensicScannerComponent, ForensicScannerPrintMessage>(OnPrint);
             SubscribeLocalEvent<ForensicScannerComponent, ForensicScannerClearMessage>(OnClear);
-            SubscribeLocalEvent<TargetScanSuccessfulEvent>(OnTargetScanSuccessful);
-            SubscribeLocalEvent<ScanCancelledEvent>(OnScanCancelled);
+            SubscribeLocalEvent<ForensicScannerComponent, DoAfterEvent>(OnDoAfter);
         }
 
         private void UpdateUserInterface(EntityUid uid, ForensicScannerComponent component)
@@ -54,40 +52,35 @@ namespace Content.Server.Forensics
                 component.PrintReadyAt);
 
             if (!_uiSystem.TrySetUiState(uid, ForensicScannerUiKey.Key, state))
-            {
                 _sawmill.Warning($"{ToPrettyString(uid)} was unable to set UI state.");
-            }
         }
 
-        private void OnScanCancelled(ScanCancelledEvent ev)
+        private void OnDoAfter(EntityUid uid, ForensicScannerComponent component, DoAfterEvent args)
         {
-            if (!EntityManager.TryGetComponent(ev.Scanner, out ForensicScannerComponent? scanner))
+            if (args.Handled || args.Cancelled)
                 return;
 
-            scanner.CancelToken = null;
-        }
-
-        private void OnTargetScanSuccessful(TargetScanSuccessfulEvent ev)
-        {
-            if (!EntityManager.TryGetComponent(ev.Scanner, out ForensicScannerComponent? scanner))
+            if (!EntityManager.TryGetComponent(uid, out ForensicScannerComponent? scanner))
                 return;
 
-            scanner.CancelToken = null;
-
-            if (!TryComp<ForensicsComponent>(ev.Target, out var forensics))
+            if (args.Args.Target != null)
             {
-                scanner.Fingerprints = new();
-                scanner.Fibers = new();
-            }
-            else
-            {
-                scanner.Fingerprints = forensics.Fingerprints.ToList();
-                scanner.Fibers = forensics.Fibers.ToList();
-            }
+                if (!TryComp<ForensicsComponent>(args.Args.Target, out var forensics))
+                {
+                    scanner.Fingerprints = new();
+                    scanner.Fibers = new();
+                }
+
+                else
+                {
+                    scanner.Fingerprints = forensics.Fingerprints.ToList();
+                    scanner.Fibers = forensics.Fibers.ToList();
+                }
 
-            scanner.LastScannedName = MetaData(ev.Target).EntityName;
+                scanner.LastScannedName = MetaData(args.Args.Target.Value).EntityName;
+            }
 
-            OpenUserInterface(ev.User, scanner);
+            OpenUserInterface(args.Args.User, scanner);
         }
 
         /// <remarks>
@@ -95,11 +88,8 @@ namespace Content.Server.Forensics
         /// </remarks>
         private void StartScan(EntityUid uid, ForensicScannerComponent component, EntityUid user, EntityUid target)
         {
-            component.CancelToken = new CancellationTokenSource();
-            _doAfterSystem.DoAfter(new DoAfterEventArgs(user, component.ScanDelay, component.CancelToken.Token, target: target)
+            _doAfterSystem.DoAfter(new DoAfterEventArgs(user, component.ScanDelay, target: target, used: uid)
             {
-                BroadcastFinishedEvent = new TargetScanSuccessfulEvent(user, target, component.Owner),
-                BroadcastCancelledEvent = new ScanCancelledEvent(component.Owner),
                 BreakOnTargetMove = true,
                 BreakOnUserMove = true,
                 BreakOnStun = true,
@@ -244,28 +234,5 @@ namespace Content.Server.Forensics
 
             UpdateUserInterface(uid, component);
         }
-
-        private sealed class ScanCancelledEvent : EntityEventArgs
-        {
-            public EntityUid Scanner;
-
-            public ScanCancelledEvent(EntityUid scanner)
-            {
-                Scanner = scanner;
-            }
-        }
-
-        private sealed class TargetScanSuccessfulEvent : EntityEventArgs
-        {
-            public EntityUid User;
-            public EntityUid Target;
-            public EntityUid Scanner;
-            public TargetScanSuccessfulEvent(EntityUid user, EntityUid target, EntityUid scanner)
-            {
-                User = user;
-                Target = target;
-                Scanner = scanner;
-            }
-        }
     }
 }
index c4e506e538d199f94cce43325e790a0d1133362a..9bf5bb20187c11968ddba6d4ddb9eed5e3b5b9bd 100644 (file)
@@ -32,6 +32,6 @@ namespace Content.Server.Gatherable.Components
         public int MaxGatheringEntities = 1;
 
         [ViewVariables]
-        public readonly Dictionary<EntityUid, CancellationTokenSource> GatheringEntities = new();
+        public readonly List<EntityUid> GatheringEntities = new();
     }
 }
index f574bd5dd99079f747d1dfc58ebd520414d5badc..e82a5130d34660c07ce4ee818798d9643887643d 100644 (file)
@@ -3,6 +3,7 @@ using Content.Server.Destructible;
 using Content.Server.DoAfter;
 using Content.Server.Gatherable.Components;
 using Content.Shared.Damage;
+using Content.Shared.DoAfter;
 using Content.Shared.Destructible;
 using Content.Shared.EntityList;
 using Content.Shared.Interaction;
@@ -27,15 +28,12 @@ public sealed class GatherableSystem : EntitySystem
         base.Initialize();
 
         SubscribeLocalEvent<GatherableComponent, InteractUsingEvent>(OnInteractUsing);
-        SubscribeLocalEvent<GatheringDoafterCancel>(OnDoafterCancel);
-        SubscribeLocalEvent<GatherableComponent, GatheringDoafterSuccess>(OnDoafterSuccess);
+        SubscribeLocalEvent<GatherableComponent, DoAfterEvent>(OnDoAfter);
     }
 
     private void OnInteractUsing(EntityUid uid, GatherableComponent component, InteractUsingEvent args)
     {
-        if (!TryComp<GatheringToolComponent>(args.Used, out var tool) ||
-            component.ToolWhitelist?.IsValid(args.Used) == false ||
-            tool.GatheringEntities.TryGetValue(uid, out var cancelToken))
+        if (!TryComp<GatheringToolComponent>(args.Used, out var tool) || component.ToolWhitelist?.IsValid(args.Used) == false)
             return;
 
         // Can't gather too many entities at once.
@@ -46,38 +44,39 @@ public sealed class GatherableSystem : EntitySystem
         var damageTime = (damageRequired / tool.Damage.Total).Float();
         damageTime = Math.Max(1f, damageTime);
 
-        cancelToken = new CancellationTokenSource();
-        tool.GatheringEntities[uid] = cancelToken;
-
-        var doAfter = new DoAfterEventArgs(args.User, damageTime, cancelToken.Token, uid)
+        var doAfter = new DoAfterEventArgs(args.User, damageTime, target: uid, used: args.Used)
         {
             BreakOnDamage = true,
             BreakOnStun = true,
             BreakOnTargetMove = true,
             BreakOnUserMove = true,
             MovementThreshold = 0.25f,
-            BroadcastCancelledEvent = new GatheringDoafterCancel { Tool = args.Used, Resource = uid },
-            TargetFinishedEvent = new GatheringDoafterSuccess { Tool = args.Used, Resource = uid, Player = args.User }
         };
 
         _doAfterSystem.DoAfter(doAfter);
     }
 
-    private void OnDoafterSuccess(EntityUid uid, GatherableComponent component, GatheringDoafterSuccess ev)
+    private void OnDoAfter(EntityUid uid, GatherableComponent component, DoAfterEvent args)
     {
-        if (!TryComp(ev.Tool, out GatheringToolComponent? tool))
+        if(!TryComp<GatheringToolComponent>(args.Args.Used, out var tool) || args.Args.Target == null)
+            return;
+
+        if (args.Handled || args.Cancelled)
+        {
+            tool.GatheringEntities.Remove(args.Args.Target.Value);
             return;
+        }
 
         // Complete the gathering process
-        _destructible.DestroyEntity(uid);
-        _audio.PlayPvs(tool.GatheringSound, ev.Resource);
-        tool.GatheringEntities.Remove(ev.Resource);
+        _destructible.DestroyEntity(args.Args.Target.Value);
+        _audio.PlayPvs(tool.GatheringSound, args.Args.Target.Value);
+        tool.GatheringEntities.Remove(args.Args.Target.Value);
 
         // Spawn the loot!
         if (component.MappedLoot == null)
             return;
 
-        var playerPos = Transform(ev.Player).MapPosition;
+        var playerPos = Transform(args.Args.User).MapPosition;
 
         foreach (var (tag, table) in component.MappedLoot)
         {
@@ -91,27 +90,7 @@ public sealed class GatherableSystem : EntitySystem
             var spawnPos = playerPos.Offset(_random.NextVector2(0.3f));
             Spawn(spawnLoot[0], spawnPos);
         }
-    }
-
-    private void OnDoafterCancel(GatheringDoafterCancel ev)
-    {
-        if (!TryComp<GatheringToolComponent>(ev.Tool, out var tool))
-            return;
-
-        tool.GatheringEntities.Remove(ev.Resource);
-    }
-
-    private sealed class GatheringDoafterCancel : EntityEventArgs
-    {
-        public EntityUid Tool;
-        public EntityUid Resource;
-    }
-
-    private sealed class GatheringDoafterSuccess : EntityEventArgs
-    {
-        public EntityUid Tool;
-        public EntityUid Resource;
-        public EntityUid Player;
+        args.Handled = true;
     }
 }
 
index bad11e1da8a1f204afb6853ad18ec3469f511e4d..b1ca676e04dce985e94e1b1a61463e387fd770d2 100644 (file)
@@ -3,6 +3,7 @@ using Content.Server.Popups;
 using Content.Shared.Actions;
 using Content.Shared.Audio;
 using Content.Shared.Damage;
+using Content.Shared.DoAfter;
 using Content.Shared.Examine;
 using Content.Shared.Hands.EntitySystems;
 using Content.Shared.Interaction;
@@ -10,7 +11,6 @@ using Content.Shared.Interaction.Events;
 using Content.Shared.Mobs;
 using Content.Shared.Popups;
 using Robust.Server.GameObjects;
-using Robust.Shared.Audio;
 using Robust.Shared.Containers;
 using Robust.Shared.Player;
 using Robust.Shared.Utility;
@@ -27,6 +27,7 @@ namespace Content.Server.Guardian
         [Dependency] private readonly DamageableSystem _damageSystem = default!;
         [Dependency] private readonly SharedActionsSystem _actionSystem = default!;
         [Dependency] private readonly SharedHandsSystem _handsSystem = default!;
+        [Dependency] private readonly SharedAudioSystem _audio = default!;
 
         public override void Initialize()
         {
@@ -34,8 +35,7 @@ namespace Content.Server.Guardian
             SubscribeLocalEvent<GuardianCreatorComponent, UseInHandEvent>(OnCreatorUse);
             SubscribeLocalEvent<GuardianCreatorComponent, AfterInteractEvent>(OnCreatorInteract);
             SubscribeLocalEvent<GuardianCreatorComponent, ExaminedEvent>(OnCreatorExamine);
-            SubscribeLocalEvent<GuardianCreatorInjectedEvent>(OnCreatorInject);
-            SubscribeLocalEvent<GuardianCreatorInjectCancelledEvent>(OnCreatorCancelled);
+            SubscribeLocalEvent<GuardianCreatorComponent, DoAfterEvent>(OnDoAfter);
 
             SubscribeLocalEvent<GuardianComponent, MoveEvent>(OnGuardianMove);
             SubscribeLocalEvent<GuardianComponent, DamageChangedEvent>(OnGuardianDamaged);
@@ -58,7 +58,7 @@ namespace Content.Server.Guardian
                 return;
 
             if (component.HostedGuardian != null)
-                ToggleGuardian(component);
+                ToggleGuardian(uid, component);
 
             args.Handled = true;
         }
@@ -67,19 +67,18 @@ namespace Content.Server.Guardian
         {
             var host = component.Host;
 
-            if (!TryComp<GuardianHostComponent>(host, out var hostComponent)) return;
-
-            if (LifeStage(host) >= EntityLifeStage.MapInitialized)
+            if (!TryComp<GuardianHostComponent>(host, out var hostComponent) || LifeStage(host) >= EntityLifeStage.MapInitialized)
                 return;
 
-            RetractGuardian(hostComponent, component);
+            RetractGuardian(host, hostComponent, uid, component);
         }
 
         private void OnGuardianPlayer(EntityUid uid, GuardianComponent component, PlayerAttachedEvent args)
         {
             var host = component.Host;
 
-            if (!HasComp<GuardianHostComponent>(host)) return;
+            if (!HasComp<GuardianHostComponent>(host))
+                return;
 
             _popupSystem.PopupEntity(Loc.GetString("guardian-available"), host, host);
         }
@@ -92,7 +91,9 @@ namespace Content.Server.Guardian
 
         private void OnHostShutdown(EntityUid uid, GuardianHostComponent component, ComponentShutdown args)
         {
-            if (component.HostedGuardian == null) return;
+            if (component.HostedGuardian == null)
+                return;
+
             EntityManager.QueueDeleteEntity(component.HostedGuardian.Value);
             _actionSystem.RemoveAction(uid, component.Action);
         }
@@ -107,19 +108,15 @@ namespace Content.Server.Guardian
             args.Cancel();
         }
 
-        public void ToggleGuardian(GuardianHostComponent hostComponent)
+        public void ToggleGuardian(EntityUid user, GuardianHostComponent hostComponent)
         {
-            if (hostComponent.HostedGuardian == null ||
-                !TryComp(hostComponent.HostedGuardian, out GuardianComponent? guardianComponent)) return;
+            if (hostComponent.HostedGuardian == null || !TryComp<GuardianComponent>(hostComponent.HostedGuardian, out var guardianComponent))
+                return;
 
             if (guardianComponent.GuardianLoose)
-            {
-                RetractGuardian(hostComponent, guardianComponent);
-            }
+                RetractGuardian(user, hostComponent, hostComponent.HostedGuardian.Value, guardianComponent);
             else
-            {
-                ReleaseGuardian(hostComponent, guardianComponent);
-            }
+                ReleaseGuardian(user, hostComponent, hostComponent.HostedGuardian.Value, guardianComponent);
         }
 
         /// <summary>
@@ -127,24 +124,22 @@ namespace Content.Server.Guardian
         /// </summary>
         private void OnCreatorUse(EntityUid uid, GuardianCreatorComponent component, UseInHandEvent args)
         {
-            if (args.Handled) return;
-            args.Handled = true;
-            UseCreator(args.User, args.User, component);
+            if (args.Handled)
+                return;
+
+            //args.Handled = true;
+            UseCreator(args.User, args.User, uid, component);
         }
 
         private void OnCreatorInteract(EntityUid uid, GuardianCreatorComponent component, AfterInteractEvent args)
         {
-            if (args.Handled || args.Target == null || !args.CanReach) return;
-            args.Handled = true;
-            UseCreator(args.User, args.Target.Value, component);
-        }
+            if (args.Handled || args.Target == null || !args.CanReach)
+                return;
 
-        private void OnCreatorCancelled(GuardianCreatorInjectCancelledEvent ev)
-        {
-            ev.Component.Injecting = false;
+            //args.Handled = true;
+            UseCreator(args.User, args.Target.Value, uid, component);
         }
-
-        private void UseCreator(EntityUid user, EntityUid target, GuardianCreatorComponent component)
+        private void UseCreator(EntityUid user, EntityUid target, EntityUid injector, GuardianCreatorComponent component)
         {
             if (component.Used)
             {
@@ -166,55 +161,52 @@ namespace Content.Server.Guardian
                 return;
             }
 
-            if (component.Injecting) return;
+            if (component.Injecting)
+                return;
 
             component.Injecting = true;
 
-            _doAfterSystem.DoAfter(new DoAfterEventArgs(user, component.InjectionDelay, target: target)
+            _doAfterSystem.DoAfter(new DoAfterEventArgs(user, component.InjectionDelay, target: target, used: injector)
             {
-                BroadcastFinishedEvent = new GuardianCreatorInjectedEvent(user, target, component),
-                BroadcastCancelledEvent = new GuardianCreatorInjectCancelledEvent(target, component),
                 BreakOnTargetMove = true,
-                BreakOnUserMove = true,
+                BreakOnUserMove = true
             });
         }
 
-        private void OnCreatorInject(GuardianCreatorInjectedEvent ev)
+        private void OnDoAfter(EntityUid uid, GuardianCreatorComponent component, DoAfterEvent args)
         {
-            var comp = ev.Component;
+            if (args.Handled || args.Args.Target == null)
+                return;
 
-            if (comp.Deleted ||
-                comp.Used ||
-                !_handsSystem.IsHolding(ev.User, comp.Owner, out _) ||
-                HasComp<GuardianHostComponent>(ev.Target))
+            if (args.Cancelled || component.Deleted || component.Used || !_handsSystem.IsHolding(args.Args.User, uid, out _) || HasComp<GuardianHostComponent>(args.Args.Target))
             {
-                comp.Injecting = false;
+                component.Injecting = false;
                 return;
             }
 
-            var hostXform = EntityManager.GetComponent<TransformComponent>(ev.Target);
-            var host = EntityManager.EnsureComponent<GuardianHostComponent>(ev.Target);
+            var hostXform = Transform(args.Args.Target.Value);
+            var host = EnsureComp<GuardianHostComponent>(args.Args.Target.Value);
             // Use map position so it's not inadvertantly parented to the host + if it's in a container it spawns outside I guess.
-            var guardian = EntityManager.SpawnEntity(comp.GuardianProto, hostXform.MapPosition);
+            var guardian = Spawn(component.GuardianProto, hostXform.MapPosition);
 
             host.GuardianContainer.Insert(guardian);
             host.HostedGuardian = guardian;
 
-            if (TryComp(guardian, out GuardianComponent? guardianComponent))
+            if (TryComp<GuardianComponent>(guardian, out var guardianComp))
             {
-                guardianComponent.Host = ev.Target;
-
-                SoundSystem.Play("/Audio/Effects/guardian_inject.ogg", Filter.Pvs(ev.Target), ev.Target);
-
-                _popupSystem.PopupEntity(Loc.GetString("guardian-created"), ev.Target, ev.Target);
+                guardianComp.Host = args.Args.Target.Value;
+                _audio.Play("/Audio/Effects/guardian_inject.ogg", Filter.Pvs(args.Args.Target.Value), args.Args.Target.Value, true);
+                _popupSystem.PopupEntity(Loc.GetString("guardian-created"), args.Args.Target.Value, args.Args.Target.Value);
                 // Exhaust the activator
-                comp.Used = true;
+                component.Used = true;
             }
             else
             {
                 Logger.ErrorS("guardian", $"Tried to spawn a guardian that doesn't have {nameof(GuardianComponent)}");
                 EntityManager.QueueDeleteEntity(guardian);
             }
+
+            args.Handled = true;
         }
 
         /// <summary>
@@ -222,16 +214,18 @@ namespace Content.Server.Guardian
         /// </summary>
         private void OnHostStateChange(EntityUid uid, GuardianHostComponent component, MobStateChangedEvent args)
         {
-            if (component.HostedGuardian == null) return;
+            if (component.HostedGuardian == null)
+                return;
 
             if (args.NewMobState == MobState.Critical)
             {
                 _popupSystem.PopupEntity(Loc.GetString("guardian-critical-warn"), component.HostedGuardian.Value, component.HostedGuardian.Value);
-                SoundSystem.Play("/Audio/Effects/guardian_warn.ogg", Filter.Pvs(component.HostedGuardian.Value), component.HostedGuardian.Value);
+                _audio.Play("/Audio/Effects/guardian_warn.ogg", Filter.Pvs(component.HostedGuardian.Value), component.HostedGuardian.Value, true);
             }
             else if (args.NewMobState == MobState.Dead)
             {
-                SoundSystem.Play("/Audio/Voice/Human/malescream_guardian.ogg", Filter.Pvs(uid), uid, AudioHelpers.WithVariation(0.20f));
+                //TODO: Replace WithVariation with datafield
+                _audio.Play("/Audio/Voice/Human/malescream_guardian.ogg", Filter.Pvs(uid), uid, true, AudioHelpers.WithVariation(0.20f));
                 EntityManager.RemoveComponent<GuardianHostComponent>(uid);
             }
         }
@@ -241,7 +235,8 @@ namespace Content.Server.Guardian
         /// </summary>
         private void OnGuardianDamaged(EntityUid uid, GuardianComponent component, DamageChangedEvent args)
         {
-            if (args.DamageDelta == null) return;
+            if (args.DamageDelta == null)
+                return;
 
             _damageSystem.TryChangeDamage(component.Host, args.DamageDelta * component.DamageShare, origin: args.Origin);
             _popupSystem.PopupEntity(Loc.GetString("guardian-entity-taking-damage"), component.Host, component.Host);
@@ -254,9 +249,7 @@ namespace Content.Server.Guardian
         private void OnCreatorExamine(EntityUid uid, GuardianCreatorComponent component, ExaminedEvent args)
         {
            if (component.Used)
-           {
                args.PushMarkup(Loc.GetString("guardian-activator-empty-examine"));
-           }
         }
 
         /// <summary>
@@ -266,7 +259,10 @@ namespace Content.Server.Guardian
         {
             if (component.HostedGuardian == null ||
                 !TryComp(component.HostedGuardian, out GuardianComponent? guardianComponent) ||
-                !guardianComponent.GuardianLoose) return;
+                !guardianComponent.GuardianLoose)
+            {
+                return;
+            }
 
             CheckGuardianMove(uid, component.HostedGuardian.Value, component);
         }
@@ -276,7 +272,8 @@ namespace Content.Server.Guardian
         /// </summary>
         private void OnGuardianMove(EntityUid uid, GuardianComponent component, ref MoveEvent args)
         {
-            if (!component.GuardianLoose) return;
+            if (!component.GuardianLoose)
+                return;
 
             CheckGuardianMove(component.Host, uid, guardianComponent: component);
         }
@@ -298,78 +295,51 @@ namespace Content.Server.Guardian
                 return;
             }
 
-            if (!guardianComponent.GuardianLoose) return;
+            if (!guardianComponent.GuardianLoose)
+                return;
 
             if (!guardianXform.Coordinates.InRange(EntityManager, hostXform.Coordinates, guardianComponent.DistanceAllowed))
-            {
-                RetractGuardian(hostComponent, guardianComponent);
-            }
+                RetractGuardian(hostUid, hostComponent, guardianUid, guardianComponent);
         }
 
-        private bool CanRelease(GuardianHostComponent host, GuardianComponent guardian)
+        private bool CanRelease(EntityUid guardian)
         {
-            return HasComp<ActorComponent>(guardian.Owner);
+            return HasComp<ActorComponent>(guardian);
         }
 
-        private void ReleaseGuardian(GuardianHostComponent hostComponent, GuardianComponent guardianComponent)
+        private void ReleaseGuardian(EntityUid host, GuardianHostComponent hostComponent, EntityUid guardian, GuardianComponent guardianComponent)
         {
             if (guardianComponent.GuardianLoose)
             {
-                DebugTools.Assert(!hostComponent.GuardianContainer.Contains(guardianComponent.Owner));
+                DebugTools.Assert(!hostComponent.GuardianContainer.Contains(guardian));
                 return;
             }
 
-            if (!CanRelease(hostComponent, guardianComponent))
+            if (!CanRelease(guardian))
             {
-                _popupSystem.PopupEntity(Loc.GetString("guardian-no-soul"), hostComponent.Owner, hostComponent.Owner);
+                _popupSystem.PopupEntity(Loc.GetString("guardian-no-soul"), host, host);
                 return;
             }
 
-            DebugTools.Assert(hostComponent.GuardianContainer.Contains(guardianComponent.Owner));
-            hostComponent.GuardianContainer.Remove(guardianComponent.Owner);
-            DebugTools.Assert(!hostComponent.GuardianContainer.Contains(guardianComponent.Owner));
+            DebugTools.Assert(hostComponent.GuardianContainer.Contains(guardian));
+            hostComponent.GuardianContainer.Remove(guardian);
+            DebugTools.Assert(!hostComponent.GuardianContainer.Contains(guardian));
 
             guardianComponent.GuardianLoose = true;
         }
 
-        private void RetractGuardian(GuardianHostComponent hostComponent, GuardianComponent guardianComponent)
+        private void RetractGuardian(EntityUid host,GuardianHostComponent hostComponent, EntityUid guardian, GuardianComponent guardianComponent)
         {
             if (!guardianComponent.GuardianLoose)
             {
-                DebugTools.Assert(hostComponent.GuardianContainer.Contains(guardianComponent.Owner));
+                DebugTools.Assert(hostComponent.GuardianContainer.Contains(guardian));
                 return;
             }
 
-            hostComponent.GuardianContainer.Insert(guardianComponent.Owner);
-            DebugTools.Assert(hostComponent.GuardianContainer.Contains(guardianComponent.Owner));
-            _popupSystem.PopupEntity(Loc.GetString("guardian-entity-recall"), hostComponent.Owner);
+            hostComponent.GuardianContainer.Insert(guardian);
+            DebugTools.Assert(hostComponent.GuardianContainer.Contains(guardian));
+            _popupSystem.PopupEntity(Loc.GetString("guardian-entity-recall"), host);
             guardianComponent.GuardianLoose = false;
         }
-
-        private sealed class GuardianCreatorInjectedEvent : EntityEventArgs
-        {
-            public EntityUid User { get; }
-            public EntityUid Target { get; }
-            public GuardianCreatorComponent Component { get; }
-
-            public GuardianCreatorInjectedEvent(EntityUid user, EntityUid target, GuardianCreatorComponent component)
-            {
-                User = user;
-                Target = target;
-                Component = component;
-            }
-        }
-
-        private sealed class GuardianCreatorInjectCancelledEvent : EntityEventArgs
-        {
-            public EntityUid Target { get; }
-            public GuardianCreatorComponent Component { get; }
-
-            public GuardianCreatorInjectCancelledEvent(EntityUid target, GuardianCreatorComponent component)
-            {
-                Target = target;
-                Component = component;
-            }
-        }
     }
 }
index 369070041a25dd86761084f2f5e4321eed45a677..451f4d074ec134930a0ed56431bd4b3ad28615b2 100644 (file)
@@ -2,6 +2,7 @@ using System.Threading;
 using Content.Server.DoAfter;
 using Content.Server.Guardian;
 using Content.Server.Popups;
+using Content.Shared.DoAfter;
 using Content.Shared.Hands;
 using Content.Shared.IdentityManagement;
 using Content.Shared.Implants;
@@ -11,7 +12,6 @@ using Content.Shared.Mobs.Components;
 using Content.Shared.Popups;
 using Robust.Shared.Containers;
 using Robust.Shared.GameStates;
-using Robust.Shared.Player;
 
 namespace Content.Server.Implants;
 
@@ -30,9 +30,8 @@ public sealed partial class ImplanterSystem : SharedImplanterSystem
         SubscribeLocalEvent<ImplanterComponent, AfterInteractEvent>(OnImplanterAfterInteract);
         SubscribeLocalEvent<ImplanterComponent, ComponentGetState>(OnImplanterGetState);
 
-        SubscribeLocalEvent<ImplanterComponent, ImplanterImplantCompleteEvent>(OnImplantAttemptSuccess);
-        SubscribeLocalEvent<ImplanterComponent, ImplanterDrawCompleteEvent>(OnDrawAttemptSuccess);
-        SubscribeLocalEvent<ImplanterComponent, ImplanterCancelledEvent>(OnImplantAttemptFail);
+        SubscribeLocalEvent<ImplanterComponent, DoAfterEvent<ImplantEvent>>(OnImplant);
+        SubscribeLocalEvent<ImplanterComponent, DoAfterEvent<DrawEvent>>(OnDraw);
     }
 
     private void OnImplanterAfterInteract(EntityUid uid, ImplanterComponent component, AfterInteractEvent args)
@@ -40,12 +39,6 @@ public sealed partial class ImplanterSystem : SharedImplanterSystem
         if (args.Target == null || !args.CanReach || args.Handled)
             return;
 
-        if (component.CancelToken != null)
-        {
-            args.Handled = true;
-            return;
-        }
-
         //Simplemobs and regular mobs should be injectable, but only regular mobs have mind.
         //So just don't implant/draw anything that isn't living or is a guardian
         //TODO: Rework a bit when surgery is in to work with implant cases
@@ -84,6 +77,9 @@ public sealed partial class ImplanterSystem : SharedImplanterSystem
     /// <param name="implanter">The implanter being used</param>
     public void TryImplant(ImplanterComponent component, EntityUid user, EntityUid target, EntityUid implanter)
     {
+        if (component.CancelToken != null)
+            return;
+
         _popup.PopupEntity(Loc.GetString("injector-component-injecting-user"), target, user);
 
         var userName = Identity.Entity(user, EntityManager);
@@ -92,15 +88,16 @@ public sealed partial class ImplanterSystem : SharedImplanterSystem
         component.CancelToken?.Cancel();
         component.CancelToken = new CancellationTokenSource();
 
-        _doAfter.DoAfter(new DoAfterEventArgs(user, component.ImplantTime, component.CancelToken.Token, target, implanter)
+        var implantEvent = new ImplantEvent();
+
+        _doAfter.DoAfter(new DoAfterEventArgs(user, component.ImplantTime, component.CancelToken.Token,target:target, used:implanter)
         {
             BreakOnUserMove = true,
             BreakOnTargetMove = true,
             BreakOnDamage = true,
             BreakOnStun = true,
-            UsedFinishedEvent = new ImplanterImplantCompleteEvent(implanter, target),
-            UserCancelledEvent = new ImplanterCancelledEvent()
-        });
+            NeedHand = true
+        }, implantEvent);
     }
 
     /// <summary>
@@ -118,15 +115,16 @@ public sealed partial class ImplanterSystem : SharedImplanterSystem
         component.CancelToken?.Cancel();
         component.CancelToken = new CancellationTokenSource();
 
-        _doAfter.DoAfter(new DoAfterEventArgs(user, component.DrawTime, component.CancelToken.Token, target ,implanter)
+        var drawEvent = new DrawEvent();
+
+        _doAfter.DoAfter(new DoAfterEventArgs(user, component.DrawTime, target:target,used:implanter)
         {
             BreakOnUserMove = true,
             BreakOnTargetMove = true,
             BreakOnDamage = true,
             BreakOnStun = true,
-            UsedFinishedEvent = new ImplanterDrawCompleteEvent(implanter, user, target),
-            UsedCancelledEvent = new ImplanterCancelledEvent()
-        });
+            NeedHand = true
+        }, drawEvent);
     }
 
     private void OnImplanterGetState(EntityUid uid, ImplanterComponent component, ref ComponentGetState args)
@@ -134,55 +132,47 @@ public sealed partial class ImplanterSystem : SharedImplanterSystem
         args.State = new ImplanterComponentState(component.CurrentMode, component.ImplantOnly);
     }
 
-    private void OnImplantAttemptSuccess(EntityUid uid, ImplanterComponent component, ImplanterImplantCompleteEvent args)
+    private void OnImplant(EntityUid uid, ImplanterComponent component, DoAfterEvent<ImplantEvent> args)
     {
-        component.CancelToken?.Cancel();
-        component.CancelToken = null;
-        Implant(args.Implanter, args.Target, component);
-    }
+        if (args.Cancelled)
+        {
+            component.CancelToken = null;
+            return;
+        }
 
-    private void OnDrawAttemptSuccess(EntityUid uid, ImplanterComponent component, ImplanterDrawCompleteEvent args)
-    {
-        component.CancelToken?.Cancel();
-        component.CancelToken = null;
-        Draw(args.Implanter, args.User, args.Target, component);
-    }
+        if (args.Handled || args.Args.Target == null || args.Args.Used == null)
+            return;
 
-    private void OnImplantAttemptFail(EntityUid uid, ImplanterComponent component, ImplanterCancelledEvent args)
-    {
-        component.CancelToken?.Cancel();
+        Implant(args.Args.Used.Value, args.Args.Target.Value, component);
+
+        args.Handled = true;
         component.CancelToken = null;
     }
 
-    private sealed class ImplanterImplantCompleteEvent : EntityEventArgs
+    private void OnDraw(EntityUid uid, ImplanterComponent component, DoAfterEvent<DrawEvent> args)
     {
-        public EntityUid Implanter;
-        public EntityUid Target;
-
-        public ImplanterImplantCompleteEvent(EntityUid implanter, EntityUid target)
+        if (args.Cancelled)
         {
-            Implanter = implanter;
-            Target = target;
+            component.CancelToken = null;
+            return;
         }
+
+        if (args.Handled || args.Args.Used == null || args.Args.Target == null)
+            return;
+
+        Draw(args.Args.Used.Value, args.Args.User, args.Args.Target.Value, component);
+
+        args.Handled = true;
+        component.CancelToken = null;
     }
 
-    private sealed class ImplanterCancelledEvent : EntityEventArgs
+    private sealed class ImplantEvent : EntityEventArgs
     {
 
     }
 
-    private sealed class ImplanterDrawCompleteEvent : EntityEventArgs
+    private sealed class DrawEvent : EntityEventArgs
     {
-        public EntityUid Implanter;
-        public EntityUid User;
-        public EntityUid Target;
 
-        public ImplanterDrawCompleteEvent(EntityUid implanter, EntityUid user, EntityUid target)
-        {
-            Implanter = implanter;
-            User = user;
-            Target = target;
-        }
     }
-
 }
index 78d4a01debd0b5e553c29fadcdc1b3c33a9c5795..19514310ba257869878350ae70a617f9d89982b2 100644 (file)
@@ -1,14 +1,13 @@
 using Content.Server.Administration.Logs;
 using Content.Server.DoAfter;
 using Content.Server.Kitchen.Components;
-using Content.Server.Nutrition.Components;
 using Content.Server.Popups;
 using Content.Shared.Database;
+using Content.Shared.DoAfter;
 using Content.Shared.DragDrop;
 using Content.Shared.IdentityManagement;
 using Content.Shared.Interaction;
 using Content.Shared.Nutrition.Components;
-using Robust.Shared.Audio;
 using Robust.Shared.Player;
 using Content.Shared.Storage;
 using Robust.Shared.Random;
@@ -18,7 +17,6 @@ using Content.Shared.Kitchen;
 using Content.Shared.Mobs.Components;
 using Content.Shared.Mobs.Systems;
 using Content.Shared.Popups;
-using Robust.Server.GameObjects;
 
 namespace Content.Server.Kitchen.EntitySystems
 {
@@ -30,6 +28,7 @@ namespace Content.Server.Kitchen.EntitySystems
         [Dependency] private readonly MobStateSystem _mobStateSystem = default!;
         [Dependency] private readonly IRobustRandom _random = default!;
         [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
+        [Dependency] private readonly SharedAudioSystem _audio = default!;
 
         public override void Initialize()
         {
@@ -40,8 +39,7 @@ namespace Content.Server.Kitchen.EntitySystems
             SubscribeLocalEvent<KitchenSpikeComponent, DragDropTargetEvent>(OnDragDrop);
 
             //DoAfter
-            SubscribeLocalEvent<KitchenSpikeComponent, SpikingFinishedEvent>(OnSpikingFinished);
-            SubscribeLocalEvent<KitchenSpikeComponent, SpikingFailEvent>(OnSpikingFail);
+            SubscribeLocalEvent<KitchenSpikeComponent, DoAfterEvent>(OnDoAfter);
 
             SubscribeLocalEvent<KitchenSpikeComponent, SuicideEvent>(OnSuicide);
 
@@ -56,35 +54,32 @@ namespace Content.Server.Kitchen.EntitySystems
 
         private void OnSuicide(EntityUid uid, KitchenSpikeComponent component, SuicideEvent args)
         {
-            if (args.Handled) return;
+            if (args.Handled)
+                return;
             args.SetHandled(SuicideKind.Piercing);
             var victim = args.Victim;
             var othersMessage = Loc.GetString("comp-kitchen-spike-suicide-other", ("victim", victim));
-            victim.PopupMessageOtherClients(othersMessage);
+            _popupSystem.PopupEntity(othersMessage, victim);
 
             var selfMessage = Loc.GetString("comp-kitchen-spike-suicide-self");
-            victim.PopupMessage(selfMessage);
+            _popupSystem.PopupEntity(selfMessage, victim, victim);
         }
 
-        private void OnSpikingFail(EntityUid uid, KitchenSpikeComponent component, SpikingFailEvent args)
+        private void OnDoAfter(EntityUid uid, KitchenSpikeComponent component, DoAfterEvent args)
         {
-            component.InUse = false;
+            if (args.Args.Target == null)
+                return;
 
-            if (EntityManager.TryGetComponent<ButcherableComponent>(args.VictimUid, out var butcherable))
+            if (TryComp<ButcherableComponent>(args.Args.Target.Value, out var butcherable))
                 butcherable.BeingButchered = false;
-        }
 
-        private void OnSpikingFinished(EntityUid uid, KitchenSpikeComponent component, SpikingFinishedEvent args)
-        {
-            component.InUse = false;
+            if (args.Handled || args.Cancelled)
+                return;
 
-            if (EntityManager.TryGetComponent<ButcherableComponent>(args.VictimUid, out var butcherable))
-                butcherable.BeingButchered = false;
+            if (Spikeable(uid, args.Args.User, args.Args.Target.Value, component, butcherable))
+                Spike(uid, args.Args.User, args.Args.Target.Value, component);
 
-            if (Spikeable(uid, args.UserUid, args.VictimUid, component, butcherable))
-            {
-                Spike(uid, args.UserUid, args.VictimUid, component);
-            }
+            args.Handled = true;
         }
 
         private void OnDragDrop(EntityUid uid, KitchenSpikeComponent component, ref DragDropTargetEvent args)
@@ -96,8 +91,8 @@ namespace Content.Server.Kitchen.EntitySystems
 
             if (Spikeable(uid, args.User, args.Dragged, component))
                 TrySpike(uid, args.User, args.Dragged, component);
-
         }
+
         private void OnInteractHand(EntityUid uid, KitchenSpikeComponent component, InteractHandEvent args)
         {
             if (args.Handled)
@@ -142,7 +137,7 @@ namespace Content.Server.Kitchen.EntitySystems
             // TODO: Need to be able to leave them on the spike to do DoT, see ss13.
             EntityManager.QueueDeleteEntity(victimUid);
 
-            SoundSystem.Play(component.SpikeSound.GetSound(), Filter.Pvs(uid), uid);
+            _audio.Play(component.SpikeSound, Filter.Pvs(uid), uid, true);
         }
 
         private bool TryGetPiece(EntityUid uid, EntityUid user, EntityUid used,
@@ -164,9 +159,7 @@ namespace Content.Server.Kitchen.EntitySystems
                 Loc.GetString("comp-kitchen-spike-meat-name", ("name", Name(ent)), ("victim", component.Victim));
 
             if (component.PrototypesToSpawn.Count != 0)
-            {
                 _popupSystem.PopupEntity(component.MeatSource1p, uid, user, PopupType.MediumCaution);
-            }
             else
             {
                 UpdateAppearance(uid, null, component);
@@ -243,42 +236,18 @@ namespace Content.Server.Kitchen.EntitySystems
             butcherable.BeingButchered = true;
             component.InUse = true;
 
-            var doAfterArgs = new DoAfterEventArgs(userUid, component.SpikeDelay + butcherable.ButcherDelay, default, uid)
+            var doAfterArgs = new DoAfterEventArgs(userUid, component.SpikeDelay + butcherable.ButcherDelay, target:victimUid, used:uid)
             {
                 BreakOnTargetMove = true,
                 BreakOnUserMove = true,
                 BreakOnDamage = true,
                 BreakOnStun = true,
-                NeedHand = true,
-                TargetFinishedEvent = new SpikingFinishedEvent(userUid, victimUid),
-                TargetCancelledEvent = new SpikingFailEvent(victimUid)
+                NeedHand = true
             };
 
             _doAfter.DoAfter(doAfterArgs);
 
             return true;
         }
-
-        private sealed class SpikingFinishedEvent : EntityEventArgs
-        {
-            public EntityUid VictimUid;
-            public EntityUid UserUid;
-
-            public SpikingFinishedEvent(EntityUid userUid, EntityUid victimUid)
-            {
-                UserUid = userUid;
-                VictimUid = victimUid;
-            }
-        }
-
-        private sealed class SpikingFailEvent : EntityEventArgs
-        {
-            public EntityUid VictimUid;
-
-            public SpikingFailEvent(EntityUid victimUid)
-            {
-                VictimUid = victimUid;
-            }
-        }
     }
 }
index 4aceecd419707b97ae5914b7a2a6e585e157e6a6..05894146fcd721e7c8474475fb0d1bed58a71d4b 100644 (file)
@@ -1,4 +1,4 @@
-using Content.Server.Body.Systems;
+using Content.Server.Body.Systems;
 using Content.Server.DoAfter;
 using Content.Server.Kitchen.Components;
 using Content.Shared.Body.Components;
@@ -8,10 +8,10 @@ using Content.Shared.Popups;
 using Content.Shared.Storage;
 using Content.Shared.Verbs;
 using Content.Shared.Destructible;
+using Content.Shared.DoAfter;
 using Content.Shared.Mobs.Components;
 using Content.Shared.Mobs.Systems;
 using Robust.Server.Containers;
-using Robust.Shared.Player;
 using Robust.Shared.Random;
 
 namespace Content.Server.Kitchen.EntitySystems;
@@ -31,8 +31,7 @@ public sealed class SharpSystem : EntitySystem
         base.Initialize();
 
         SubscribeLocalEvent<SharpComponent, AfterInteractEvent>(OnAfterInteract);
-        SubscribeLocalEvent<SharpButcherDoafterComplete>(OnDoafterComplete);
-        SubscribeLocalEvent<SharpButcherDoafterCancelled>(OnDoafterCancelled);
+        SubscribeLocalEvent<SharpComponent, DoAfterEvent>(OnDoAfter);
 
         SubscribeLocalEvent<ButcherableComponent, GetVerbsEvent<InteractionVerb>>(OnGetInteractionVerbs);
     }
@@ -63,64 +62,56 @@ public sealed class SharpSystem : EntitySystem
             return;
 
         var doAfter =
-            new DoAfterEventArgs(user, sharp.ButcherDelayModifier * butcher.ButcherDelay, default, target)
+            new DoAfterEventArgs(user, sharp.ButcherDelayModifier * butcher.ButcherDelay, target: target, used: knife)
             {
                 BreakOnTargetMove = true,
                 BreakOnUserMove = true,
                 BreakOnDamage = true,
                 BreakOnStun = true,
-                NeedHand = true,
-                BroadcastFinishedEvent = new SharpButcherDoafterComplete { User = user, Entity = target, Sharp = knife },
-                BroadcastCancelledEvent = new SharpButcherDoafterCancelled { Entity = target, Sharp = knife }
+                NeedHand = true
             };
 
         _doAfterSystem.DoAfter(doAfter);
     }
 
-    private void OnDoafterComplete(SharpButcherDoafterComplete ev)
+    private void OnDoAfter(EntityUid uid, SharpComponent component, DoAfterEvent args)
     {
-        if (!TryComp<ButcherableComponent>(ev.Entity, out var butcher))
+        if (args.Handled || args.Cancelled || !TryComp<ButcherableComponent>(args.Args.Target, out var butcher))
             return;
 
-        if (!TryComp<SharpComponent>(ev.Sharp, out var sharp))
-            return;
-
-        sharp.Butchering.Remove(ev.Entity);
+        component.Butchering.Remove(args.Args.Target.Value);
 
-        if (_containerSystem.IsEntityInContainer(ev.Entity))
+        if (_containerSystem.IsEntityInContainer(args.Args.Target.Value))
+        {
+            args.Handled = true;
             return;
+        }
 
         var spawnEntities = EntitySpawnCollection.GetSpawns(butcher.SpawnedEntities, _robustRandom);
-        var coords = Transform(ev.Entity).MapPosition;
-        EntityUid popupEnt = default;
+        var coords = Transform(args.Args.Target.Value).MapPosition;
+        EntityUid popupEnt = default!;
         foreach (var proto in spawnEntities)
         {
             // distribute the spawned items randomly in a small radius around the origin
             popupEnt = Spawn(proto, coords.Offset(_robustRandom.NextVector2(0.25f)));
         }
 
-        var hasBody = TryComp<BodyComponent>(ev.Entity, out var body);
+        var hasBody = TryComp<BodyComponent>(args.Args.Target.Value, out var body);
 
         // only show a big popup when butchering living things.
         var popupType = PopupType.Small;
         if (hasBody)
             popupType = PopupType.LargeCaution;
 
-        _popupSystem.PopupEntity(Loc.GetString("butcherable-knife-butchered-success", ("target", ev.Entity), ("knife", ev.Sharp)),
-            popupEnt, ev.User, popupType);
+        _popupSystem.PopupEntity(Loc.GetString("butcherable-knife-butchered-success", ("target", args.Args.Target.Value), ("knife", uid)),
+            popupEnt, args.Args.User, popupType);
 
         if (hasBody)
-            _bodySystem.GibBody(body!.Owner, body: body);
+            _bodySystem.GibBody(args.Args.Target.Value, body: body);
 
-        _destructibleSystem.DestroyEntity(ev.Entity);
-    }
-
-    private void OnDoafterCancelled(SharpButcherDoafterCancelled ev)
-    {
-        if (!TryComp<SharpComponent>(ev.Sharp, out var sharp))
-            return;
+        _destructibleSystem.DestroyEntity(args.Args.Target.Value);
 
-        sharp.Butchering.Remove(ev.Entity);
+        args.Handled = true;
     }
 
     private void OnGetInteractionVerbs(EntityUid uid, ButcherableComponent component, GetVerbsEvent<InteractionVerb> args)
@@ -165,16 +156,3 @@ public sealed class SharpSystem : EntitySystem
         args.Verbs.Add(verb);
     }
 }
-
-public sealed class SharpButcherDoafterComplete : EntityEventArgs
-{
-    public EntityUid Entity;
-    public EntityUid Sharp;
-    public EntityUid User;
-}
-
-public sealed class SharpButcherDoafterCancelled : EntityEventArgs
-{
-    public EntityUid Entity;
-    public EntityUid Sharp;
-}
index 0088aad3353ebaee136c03697d9d8bde866ae272..be44d20f54727bf343909682184f0877107c06fd 100644 (file)
@@ -64,8 +64,6 @@ namespace Content.Server.Light.Components
         [DataField("togglePort", customTypeSerializer: typeof(PrototypeIdSerializer<ReceiverPortPrototype>))]
         public string TogglePort = "Toggle";
 
-        public CancellationTokenSource? CancelToken;
-
         /// <summary>
         /// How long it takes to eject a bulb from this
         /// </summary>
index cfcbf97bc1d9e0546f8fcabf19a64216549d04ed..bef74772a73b9865a2fce5b8eacf19b9ae949c98 100644 (file)
@@ -21,7 +21,7 @@ using Robust.Shared.Audio;
 using Robust.Shared.Containers;
 using Robust.Shared.Player;
 using Robust.Shared.Timing;
-using System.Threading;
+using Content.Shared.DoAfter;
 
 namespace Content.Server.Light.EntitySystems
 {
@@ -40,6 +40,7 @@ namespace Content.Server.Light.EntitySystems
         [Dependency] private readonly SignalLinkerSystem _signalSystem = default!;
         [Dependency] private readonly SharedContainerSystem _containerSystem = default!;
         [Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
+        [Dependency] private readonly SharedAudioSystem _audio = default!;
         [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
 
         private static readonly TimeSpan ThunkDelay = TimeSpan.FromSeconds(2);
@@ -61,8 +62,7 @@ namespace Content.Server.Light.EntitySystems
 
             SubscribeLocalEvent<PoweredLightComponent, PowerChangedEvent>(OnPowerChanged);
 
-            SubscribeLocalEvent<PoweredLightComponent, EjectBulbCompleteEvent>(OnEjectBulbComplete);
-            SubscribeLocalEvent<PoweredLightComponent, EjectBulbCancelledEvent>(OnEjectBulbCancelled);
+            SubscribeLocalEvent<PoweredLightComponent, DoAfterEvent>(OnDoAfter);
         }
 
         private void OnInit(EntityUid uid, PoweredLightComponent light, ComponentInit args)
@@ -95,9 +95,6 @@ namespace Content.Server.Light.EntitySystems
             if (args.Handled)
                 return;
 
-            if (light.CancelToken != null)
-                return;
-
             // check if light has bulb to eject
             var bulbUid = GetBulb(uid, light);
             if (bulbUid == null)
@@ -122,10 +119,9 @@ namespace Content.Server.Light.EntitySystems
                     var damage = _damageableSystem.TryChangeDamage(userUid, light.Damage, origin: userUid);
 
                     if (damage != null)
-                        _adminLogger.Add(LogType.Damaged,
-                            $"{ToPrettyString(args.User):user} burned their hand on {ToPrettyString(args.Target):target} and received {damage.Total:damage} damage");
+                        _adminLogger.Add(LogType.Damaged, $"{ToPrettyString(args.User):user} burned their hand on {ToPrettyString(args.Target):target} and received {damage.Total:damage} damage");
 
-                    SoundSystem.Play(light.BurnHandSound.GetSound(), Filter.Pvs(uid), uid);
+                    _audio.Play(light.BurnHandSound, Filter.Pvs(uid), uid, true);
 
                     args.Handled = true;
                     return;
@@ -141,22 +137,11 @@ namespace Content.Server.Light.EntitySystems
             }
 
             // removing a working bulb, so require a delay
-            light.CancelToken = new CancellationTokenSource();
-            _doAfterSystem.DoAfter(new DoAfterEventArgs(userUid, light.EjectBulbDelay, light.CancelToken.Token, uid)
+            _doAfterSystem.DoAfter(new DoAfterEventArgs(userUid, light.EjectBulbDelay, target:uid)
             {
                 BreakOnUserMove = true,
                 BreakOnDamage = true,
-                BreakOnStun = true,
-                TargetFinishedEvent = new EjectBulbCompleteEvent()
-                {
-                    Component = light,
-                    User = userUid,
-                    Target = uid,
-                },
-                TargetCancelledEvent = new EjectBulbCancelledEvent()
-                {
-                    Component = light,
-                }
+                BreakOnStun = true
             });
 
             args.Handled = true;
@@ -287,7 +272,7 @@ namespace Content.Server.Light.EntitySystems
                         if (time > light.LastThunk + ThunkDelay)
                         {
                             light.LastThunk = time;
-                            SoundSystem.Play(light.TurnOnSound.GetSound(), Filter.Pvs(uid), uid, AudioParams.Default.WithVolume(-10f));
+                            _audio.Play(light.TurnOnSound, Filter.Pvs(uid), uid, true, AudioParams.Default.WithVolume(-10f));
                         }
                     }
                     else
@@ -358,7 +343,7 @@ namespace Content.Server.Light.EntitySystems
 
             light.IsBlinking = isNowBlinking;
 
-            if (!EntityManager.TryGetComponent(light.Owner, out AppearanceComponent? appearance))
+            if (!EntityManager.TryGetComponent(uid, out AppearanceComponent? appearance))
                 return;
 
             _appearance.SetData(uid, PoweredLightVisuals.Blinking, isNowBlinking, appearance);
@@ -427,27 +412,14 @@ namespace Content.Server.Light.EntitySystems
             UpdateLight(uid, light);
         }
 
-        private void OnEjectBulbComplete(EntityUid uid, PoweredLightComponent component, EjectBulbCompleteEvent args)
-        {
-            args.Component.CancelToken = null;
-            EjectBulb(args.Target, args.User, args.Component);
-        }
-
-        private static void OnEjectBulbCancelled(EntityUid uid, PoweredLightComponent component, EjectBulbCancelledEvent args)
+        private void OnDoAfter(EntityUid uid, PoweredLightComponent component, DoAfterEvent args)
         {
-            args.Component.CancelToken = null;
-        }
+            if (args.Handled || args.Cancelled || args.Args.Target == null)
+                return;
 
-        private sealed class EjectBulbCompleteEvent : EntityEventArgs
-        {
-            public PoweredLightComponent Component { get; init; } = default!;
-            public EntityUid User { get; init; }
-            public EntityUid Target { get; init; }
-        }
+            EjectBulb(args.Args.Target.Value, args.Args.User, component);
 
-        private sealed class EjectBulbCancelledEvent : EntityEventArgs
-        {
-            public PoweredLightComponent Component { get; init; } = default!;
+            args.Handled = true;
         }
     }
 }
index 334921dc29f2dd13d036b79e6c4927dd26b8b2d0..0432336dfddf175d7abfbf56c8edc24bdfb28dfa 100644 (file)
@@ -1,5 +1,4 @@
-using System.Threading;
-using Content.Shared.Actions.ActionTypes;
+using Content.Shared.Actions.ActionTypes;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
 
 namespace Content.Server.Magic;
@@ -30,6 +29,4 @@ public sealed class SpellbookComponent : Component
 
     [DataField("learnTime")]
     public float LearnTime = .75f;
-
-    public CancellationTokenSource? CancelToken;
 }
index 79fbfaf1d57c35125405933e332598cb7706f1b7..5cdb549b7a81d589b9a0fcaa9c4a027067651645 100644 (file)
@@ -9,6 +9,7 @@ using Content.Server.Weapons.Ranged.Systems;
 using Content.Shared.Actions;
 using Content.Shared.Actions.ActionTypes;
 using Content.Shared.Body.Components;
+using Content.Shared.DoAfter;
 using Content.Shared.Doors.Components;
 using Content.Shared.Doors.Systems;
 using Content.Shared.Interaction.Events;
@@ -53,8 +54,7 @@ public sealed class MagicSystem : EntitySystem
 
         SubscribeLocalEvent<SpellbookComponent, ComponentInit>(OnInit);
         SubscribeLocalEvent<SpellbookComponent, UseInHandEvent>(OnUse);
-        SubscribeLocalEvent<SpellbookComponent, LearnDoAfterComplete>(OnLearnComplete);
-        SubscribeLocalEvent<SpellbookComponent, LearnDoAfterCancel>(OnLearnCancel);
+        SubscribeLocalEvent<SpellbookComponent, DoAfterEvent>(OnDoAfter);
 
         SubscribeLocalEvent<InstantSpawnSpellEvent>(OnInstantSpawn);
         SubscribeLocalEvent<TeleportSpellEvent>(OnTeleportSpell);
@@ -65,6 +65,15 @@ public sealed class MagicSystem : EntitySystem
         SubscribeLocalEvent<ChangeComponentsSpellEvent>(OnChangeComponentsSpell);
     }
 
+    private void OnDoAfter(EntityUid uid, SpellbookComponent component, DoAfterEvent args)
+    {
+        if (args.Handled || args.Cancelled)
+            return;
+
+        _actionsSystem.AddActions(args.Args.User, component.Spells, uid);
+        args.Handled = true;
+    }
+
     private void OnInit(EntityUid uid, SpellbookComponent component, ComponentInit args)
     {
         //Negative charges means the spell can be used without it running out.
@@ -102,35 +111,18 @@ public sealed class MagicSystem : EntitySystem
 
     private void AttemptLearn(EntityUid uid, SpellbookComponent component, UseInHandEvent args)
     {
-        if (component.CancelToken != null) return;
-
-        component.CancelToken = new CancellationTokenSource();
-
-        var doAfterEventArgs = new DoAfterEventArgs(args.User, component.LearnTime, component.CancelToken.Token, uid)
+        var doAfterEventArgs = new DoAfterEventArgs(args.User, component.LearnTime, target:uid)
         {
             BreakOnTargetMove = true,
             BreakOnUserMove = true,
             BreakOnDamage = true,
             BreakOnStun = true,
-            NeedHand = true, //What, are you going to read with your eyes only??
-            TargetFinishedEvent = new LearnDoAfterComplete(args.User),
-            TargetCancelledEvent = new LearnDoAfterCancel(),
+            NeedHand = true //What, are you going to read with your eyes only??
         };
 
         _doAfter.DoAfter(doAfterEventArgs);
     }
 
-    private void OnLearnComplete(EntityUid uid, SpellbookComponent component, LearnDoAfterComplete ev)
-    {
-        component.CancelToken = null;
-        _actionsSystem.AddActions(ev.User, component.Spells, uid);
-    }
-
-    private void OnLearnCancel(EntityUid uid, SpellbookComponent component, LearnDoAfterCancel args)
-    {
-        component.CancelToken = null;
-    }
-
     #region Spells
 
     /// <summary>
@@ -383,20 +375,4 @@ public sealed class MagicSystem : EntitySystem
     }
 
     #endregion
-
-    #region DoAfterClasses
-
-    private sealed class LearnDoAfterComplete : EntityEventArgs
-    {
-        public readonly EntityUid User;
-
-        public LearnDoAfterComplete(EntityUid uid)
-        {
-            User = uid;
-        }
-    }
-
-    private sealed class LearnDoAfterCancel : EntityEventArgs { }
-
-    #endregion
 }
index 8a9f1c7c2ce91c18048ea9128c7a69152c12130a..b233d0309e073b6acbe47e21dd1d79c1c28e154b 100644 (file)
@@ -1,5 +1,4 @@
-using System.Threading;
-using Content.Server.Atmos;
+using Content.Server.Atmos;
 using Content.Shared.Mech.Components;
 using Robust.Shared.GameStates;
 using Robust.Shared.Prototypes;
@@ -32,8 +31,6 @@ public sealed class MechComponent : SharedMechComponent
     [DataField("batteryRemovalDelay")]
     public float BatteryRemovalDelay = 2;
 
-    public CancellationTokenSource? EntryTokenSource;
-
     /// <summary>
     /// Whether or not the mech is airtight.
     /// </summary>
index 77881de7cbf0a6fbd65a96ef387df60d855e10dd..c93e69e48d9196715a976f0a5e47a202059d67c3 100644 (file)
@@ -46,29 +46,4 @@ public sealed class MechGrabberComponent : Component
 
     [ViewVariables(VVAccess.ReadWrite)]
     public Container ItemContainer = default!;
-    public CancellationTokenSource? Token;
-}
-
-/// <summary>
-/// Event raised on the user when the grab is complete.
-/// </summary>
-public sealed class MechGrabberGrabFinishedEvent : EntityEventArgs
-{
-    /// <summary>
-    /// The thing that was grabbed.
-    /// </summary>
-    public EntityUid Grabbed;
-
-    public MechGrabberGrabFinishedEvent(EntityUid grabbed)
-    {
-        Grabbed = grabbed;
-    }
-}
-
-/// <summary>
-/// Event raised on the user when the grab fails
-/// </summary>
-public sealed class MechGrabberGrabCancelledEvent : EntityEventArgs
-{
-
 }
index e0a59ed8cea761ddf22b078d46ad21d022c4e208..baf4eafd212b3798cd2575d6551a483387e29fbd 100644 (file)
@@ -4,6 +4,8 @@ using Content.Server.Interaction;
 using Content.Server.Mech.Components;
 using Content.Server.Mech.Equipment.Components;
 using Content.Server.Mech.Systems;
+using Content.Shared.DoAfter;
+using Content.Shared.Construction.Components;
 using Content.Shared.Interaction;
 using Content.Shared.Mech;
 using Content.Shared.Mech.Equipment.Components;
@@ -39,8 +41,7 @@ public sealed class MechGrabberSystem : EntitySystem
         SubscribeLocalEvent<MechGrabberComponent, AttemptRemoveMechEquipmentEvent>(OnAttemptRemove);
 
         SubscribeLocalEvent<MechGrabberComponent, InteractNoHandEvent>(OnInteract);
-        SubscribeLocalEvent<MechGrabberComponent, MechGrabberGrabFinishedEvent>(OnGrabFinished);
-        SubscribeLocalEvent<MechGrabberComponent, MechGrabberGrabCancelledEvent>(OnGrabCancelled);
+        SubscribeLocalEvent<MechGrabberComponent, DoAfterEvent>(OnMechGrab);
     }
 
     private void OnGrabberMessage(EntityUid uid, MechGrabberComponent component, MechEquipmentUiMessageRelayEvent args)
@@ -144,40 +145,37 @@ public sealed class MechGrabberSystem : EntitySystem
         if (mech.Energy + component.GrabEnergyDelta < 0)
             return;
 
-        if (component.Token != null)
-            return;
-
         if (!_interaction.InRangeUnobstructed(args.User, target))
             return;
 
         args.Handled = true;
-        component.Token = new();
         component.AudioStream = _audio.PlayPvs(component.GrabSound, uid);
-        _doAfter.DoAfter(new DoAfterEventArgs(args.User, component.GrabDelay, component.Token.Token, target, uid)
+        _doAfter.DoAfter(new DoAfterEventArgs(args.User, component.GrabDelay, target:target, used:uid)
         {
             BreakOnTargetMove = true,
-            BreakOnUserMove = true,
-            UsedFinishedEvent = new MechGrabberGrabFinishedEvent(target),
-            UsedCancelledEvent = new MechGrabberGrabCancelledEvent()
+            BreakOnUserMove = true
         });
     }
 
-    private void OnGrabFinished(EntityUid uid, MechGrabberComponent component, MechGrabberGrabFinishedEvent args)
+    private void OnMechGrab(EntityUid uid, MechGrabberComponent component, DoAfterEvent args)
     {
-        component.Token = null;
+        if (args.Cancelled)
+        {
+            component.AudioStream?.Stop();
+            return;
+        }
+
+        if (args.Handled || args.Args.Target == null)
+            return;
 
         if (!TryComp<MechEquipmentComponent>(uid, out var equipmentComponent) || equipmentComponent.EquipmentOwner == null)
             return;
         if (!_mech.TryChangeEnergy(equipmentComponent.EquipmentOwner.Value, component.GrabEnergyDelta))
             return;
 
-        component.ItemContainer.Insert(args.Grabbed);
+        component.ItemContainer.Insert(args.Args.Target.Value);
         _mech.UpdateUserInterface(equipmentComponent.EquipmentOwner.Value);
-    }
 
-    private void OnGrabCancelled(EntityUid uid, MechGrabberComponent component, MechGrabberGrabCancelledEvent args)
-    {
-        component.AudioStream?.Stop();
-        component.Token = null;
+        args.Handled = true;
     }
 }
index 087c2e3aa3cfa97c385178bde6941c8463fb9254..8a9032a21dba68ebd6dbc2bc936ca9b28b663c9b 100644 (file)
@@ -1,9 +1,9 @@
 using Content.Server.DoAfter;
 using Content.Server.Mech.Components;
 using Content.Server.Popups;
+using Content.Shared.DoAfter;
 using Content.Shared.Interaction;
 using Content.Shared.Mech.Equipment.Components;
-using Robust.Shared.Player;
 
 namespace Content.Server.Mech.Systems;
 
@@ -20,15 +20,11 @@ public sealed class MechEquipmentSystem : EntitySystem
     public override void Initialize()
     {
         SubscribeLocalEvent<MechEquipmentComponent, AfterInteractEvent>(OnUsed);
-        SubscribeLocalEvent<MechEquipmentComponent, MechEquipmentInstallFinished>(OnFinished);
-        SubscribeLocalEvent<MechEquipmentComponent, MechEquipmentInstallCancelled>(OnCancelled);
+        SubscribeLocalEvent<MechEquipmentComponent, DoAfterEvent<InsertEquipmentEvent>>(OnInsertEquipment);
     }
 
     private void OnUsed(EntityUid uid, MechEquipmentComponent component, AfterInteractEvent args)
     {
-        if (component.TokenSource != null)
-            return;
-
         if (args.Handled || !args.CanReach || args.Target == null)
             return;
 
@@ -50,26 +46,30 @@ public sealed class MechEquipmentSystem : EntitySystem
 
         _popup.PopupEntity(Loc.GetString("mech-equipment-begin-install", ("item", uid)), mech);
 
-        component.TokenSource = new();
-        _doAfter.DoAfter(new DoAfterEventArgs(args.User, component.InstallDuration, component.TokenSource.Token, mech, uid)
+        var insertEquipment = new InsertEquipmentEvent();
+        var doAfterEventArgs = new DoAfterEventArgs(args.User, component.InstallDuration, target: mech, used: uid)
         {
             BreakOnStun = true,
             BreakOnTargetMove = true,
-            BreakOnUserMove = true,
-            UsedFinishedEvent = new MechEquipmentInstallFinished(mech),
-            UsedCancelledEvent = new MechEquipmentInstallCancelled()
-        });
+            BreakOnUserMove = true
+        };
+
+        _doAfter.DoAfter(doAfterEventArgs, insertEquipment);
     }
 
-    private void OnFinished(EntityUid uid, MechEquipmentComponent component, MechEquipmentInstallFinished args)
+    private void OnInsertEquipment(EntityUid uid, MechEquipmentComponent component, DoAfterEvent<InsertEquipmentEvent> args)
     {
-        component.TokenSource = null;
-        _popup.PopupEntity(Loc.GetString("mech-equipment-finish-install", ("item", uid)), args.Mech);
-        _mech.InsertEquipment(args.Mech, uid);
+        if (args.Handled || args.Cancelled || args.Args.Target == null)
+            return;
+
+        _popup.PopupEntity(Loc.GetString("mech-equipment-finish-install", ("item", uid)), args.Args.Target.Value);
+        _mech.InsertEquipment(args.Args.Target.Value, uid);
+
+        args.Handled = true;
     }
 
-    private void OnCancelled(EntityUid uid, MechEquipmentComponent component, MechEquipmentInstallCancelled args)
+    private sealed class InsertEquipmentEvent : EntityEventArgs
     {
-        component.TokenSource = null;
+
     }
 }
index 1a9b8ff4e05edcab5e6e4584027062042b87d97b..b23fb221065da18221ed8fc573b98687036a3650 100644 (file)
@@ -1,19 +1,17 @@
 using System.Linq;
-using System.Threading;
 using Content.Server.Atmos.EntitySystems;
 using Content.Server.DoAfter;
 using Content.Server.Mech.Components;
 using Content.Server.Power.Components;
-using Content.Server.Tools;
 using Content.Server.Wires;
 using Content.Shared.ActionBlocker;
 using Content.Shared.Damage;
+using Content.Shared.DoAfter;
 using Content.Shared.FixedPoint;
 using Content.Shared.Interaction;
 using Content.Shared.Mech;
 using Content.Shared.Mech.Components;
 using Content.Shared.Mech.EntitySystems;
-using Content.Shared.Movement.Components;
 using Content.Shared.Movement.Events;
 using Content.Shared.Tools.Components;
 using Content.Shared.Verbs;
@@ -47,12 +45,9 @@ public sealed class MechSystem : SharedMechSystem
         SubscribeLocalEvent<MechComponent, MapInitEvent>(OnMapInit);
         SubscribeLocalEvent<MechComponent, GetVerbsEvent<AlternativeVerb>>(OnAlternativeVerb);
         SubscribeLocalEvent<MechComponent, MechOpenUiEvent>(OnOpenUi);
-        SubscribeLocalEvent<MechComponent, MechEntryFinishedEvent>(OnEntryFinished);
-        SubscribeLocalEvent<MechComponent, MechEntryCanclledEvent>(OnEntryExitCancelled);
-        SubscribeLocalEvent<MechComponent, MechExitFinishedEvent>(OnExitFinished);
-        SubscribeLocalEvent<MechComponent, MechExitCanclledEvent>(OnEntryExitCancelled);
-        SubscribeLocalEvent<MechComponent, MechRemoveBatteryFinishedEvent>(OnRemoveBatteryFinished);
-        SubscribeLocalEvent<MechComponent, MechRemoveBatteryCancelledEvent>(OnRemoveBatteryCancelled);
+        SubscribeLocalEvent<MechComponent, DoAfterEvent<RemoveBatteryEvent>>(OnRemoveBattery);
+        SubscribeLocalEvent<MechComponent, DoAfterEvent<MechEntryEvent>>(OnMechEntry);
+        SubscribeLocalEvent<MechComponent, DoAfterEvent<MechExitEvent>>(OnMechExit);
 
         SubscribeLocalEvent<MechComponent, DamageChangedEvent>(OnDamageChanged);
         SubscribeLocalEvent<MechComponent, MechEquipmentRemoveMessage>(OnRemoveEquipmentMessage);
@@ -87,32 +82,29 @@ public sealed class MechSystem : SharedMechSystem
             return;
         }
 
-        if (component.EntryTokenSource == null &&
-            TryComp<ToolComponent>(args.Used, out var tool) &&
-            tool.Qualities.Contains("Prying") &&
-            component.BatterySlot.ContainedEntity != null)
+        if (TryComp<ToolComponent>(args.Used, out var tool) && tool.Qualities.Contains("Prying") && component.BatterySlot.ContainedEntity != null)
         {
-            component.EntryTokenSource = new();
-            _doAfter.DoAfter(new DoAfterEventArgs(args.User, component.BatteryRemovalDelay, component.EntryTokenSource.Token, uid, args.Target)
+            var removeBattery = new RemoveBatteryEvent();
+
+            var doAfterEventArgs = new DoAfterEventArgs(args.User, component.BatteryRemovalDelay, target: uid, used: args.Target)
             {
                 BreakOnTargetMove = true,
-                BreakOnUserMove = true,
-                TargetFinishedEvent = new MechRemoveBatteryFinishedEvent(),
-                TargetCancelledEvent = new MechRemoveBatteryCancelledEvent()
-            });
+                BreakOnUserMove = true
+            };
+
+            _doAfter.DoAfter(doAfterEventArgs, removeBattery);
         }
     }
 
-    private void OnRemoveBatteryFinished(EntityUid uid, MechComponent component, MechRemoveBatteryFinishedEvent args)
+    private void OnRemoveBattery(EntityUid uid, MechComponent component, DoAfterEvent<RemoveBatteryEvent> args)
     {
-        component.EntryTokenSource = null;
+        if (args.Cancelled || args.Handled)
+            return;
+
         RemoveBattery(uid, component);
         _actionBlocker.UpdateCanMove(uid);
-    }
 
-    private void OnRemoveBatteryCancelled(EntityUid uid, MechComponent component, MechRemoveBatteryCancelledEvent args)
-    {
-        component.EntryTokenSource = null;
+        args.Handled = true;
     }
 
     private void OnMapInit(EntityUid uid, MechComponent component, MapInitEvent args)
@@ -171,16 +163,14 @@ public sealed class MechSystem : SharedMechSystem
                 Text = Loc.GetString("mech-verb-enter"),
                 Act = () =>
                 {
-                    if (component.EntryTokenSource != null)
-                        return;
-                    component.EntryTokenSource = new CancellationTokenSource();
-                    _doAfter.DoAfter(new DoAfterEventArgs(args.User, component.EntryDelay, component.EntryTokenSource.Token, uid)
+                    var mechEntryEvent = new MechEntryEvent();
+                    var doAfterEventArgs = new DoAfterEventArgs(args.User, component.EntryDelay, target: uid)
                     {
                         BreakOnUserMove = true,
                         BreakOnStun = true,
-                        TargetFinishedEvent = new MechEntryFinishedEvent(args.User),
-                        TargetCancelledEvent = new MechEntryCanclledEvent()
-                    });
+                    };
+
+                    _doAfter.DoAfter(doAfterEventArgs, mechEntryEvent);
                 }
             };
             var openUiVerb = new AlternativeVerb //can't hijack someone else's mech
@@ -199,45 +189,46 @@ public sealed class MechSystem : SharedMechSystem
                 Priority = 1, // Promote to top to make ejecting the ALT-click action
                 Act = () =>
                 {
-                    if (component.EntryTokenSource != null)
-                        return;
                     if (args.User == component.PilotSlot.ContainedEntity)
                     {
                         TryEject(uid, component);
                         return;
                     }
 
-                    component.EntryTokenSource = new CancellationTokenSource();
-                    _doAfter.DoAfter(new DoAfterEventArgs(args.User, component.ExitDelay, component.EntryTokenSource.Token, uid)
+                    var mechExitEvent = new MechExitEvent();
+                    var doAfterEventArgs = new DoAfterEventArgs(args.User, component.ExitDelay, target: uid)
                     {
                         BreakOnUserMove = true,
                         BreakOnTargetMove = true,
-                        BreakOnStun = true,
-                        TargetFinishedEvent = new MechExitFinishedEvent(),
-                        TargetCancelledEvent = new MechExitCanclledEvent()
-                    });
+                        BreakOnStun = true
+                    };
+
+                    _doAfter.DoAfter(doAfterEventArgs, mechExitEvent);
                 }
             };
             args.Verbs.Add(ejectVerb);
         }
     }
 
-    private void OnEntryFinished(EntityUid uid, MechComponent component, MechEntryFinishedEvent args)
+    private void OnMechEntry(EntityUid uid, MechComponent component, DoAfterEvent<MechEntryEvent> args)
     {
-        component.EntryTokenSource = null;
-        TryInsert(uid, args.User, component);
+        if (args.Cancelled || args.Handled)
+            return;
+
+        TryInsert(uid, args.Args.User, component);
         _actionBlocker.UpdateCanMove(uid);
+
+        args.Handled = true;
     }
 
-    private void OnExitFinished(EntityUid uid, MechComponent component, MechExitFinishedEvent args)
+    private void OnMechExit(EntityUid uid, MechComponent component, DoAfterEvent<MechExitEvent> args)
     {
-        component.EntryTokenSource = null;
+        if (args.Cancelled || args.Handled)
+            return;
+
         TryEject(uid, component);
-    }
 
-    private void OnEntryExitCancelled(EntityUid uid, MechComponent component, EntityEventArgs args)
-    {
-        component.EntryTokenSource = null;
+        args.Handled = true;
     }
 
     private void OnDamageChanged(EntityUid uid, SharedMechComponent component, DamageChangedEvent args)
@@ -454,4 +445,27 @@ public sealed class MechSystem : SharedMechSystem
         args.Handled = true;
     }
     #endregion
+
+    /// <summary>
+    ///     Event raised when the battery is successfully removed from the mech,
+    ///     on both success and failure
+    /// </summary>
+    private sealed class RemoveBatteryEvent : EntityEventArgs
+    {
+    }
+
+    /// <summary>
+    ///     Event raised when a person enters a mech, on both success and failure
+    /// </summary>
+    private sealed class MechEntryEvent : EntityEventArgs
+    {
+    }
+
+    /// <summary>
+    ///     Event raised when a person removes someone from a mech,
+    ///     on both success and failure
+    /// </summary>
+    private sealed class MechExitEvent : EntityEventArgs
+    {
+    }
 }
index 5f2c6139d2a1d9d9b63744f536aff1135bc9703f..4022c25d141fb992b07a908160a3be3be3ab585c 100644 (file)
@@ -9,8 +9,6 @@ namespace Content.Server.Medical.BiomassReclaimer
     [RegisterComponent]
     public sealed class BiomassReclaimerComponent : Component
     {
-        public CancellationTokenSource? CancelToken;
-
         /// <summary>
         /// This gets set for each mob it processes.
         /// When it hits 0, there is a chance for the reclaimer to either spill blood or throw an item.
index 7d351059300529ce91568251f0b0b0bf48e5a956..b418e6a6e3337f6fd884d57d018d54dc0ffe8e1b 100644 (file)
@@ -17,6 +17,7 @@ using Content.Server.Construction;
 using Content.Server.DoAfter;
 using Content.Server.Materials;
 using Content.Server.Mind.Components;
+using Content.Shared.DoAfter;
 using Content.Shared.Humanoid;
 using Content.Shared.Interaction.Events;
 using Content.Shared.Mobs.Components;
@@ -96,8 +97,7 @@ namespace Content.Server.Medical.BiomassReclaimer
             SubscribeLocalEvent<BiomassReclaimerComponent, UpgradeExamineEvent>(OnUpgradeExamine);
             SubscribeLocalEvent<BiomassReclaimerComponent, PowerChangedEvent>(OnPowerChanged);
             SubscribeLocalEvent<BiomassReclaimerComponent, SuicideEvent>(OnSuicide);
-            SubscribeLocalEvent<ReclaimSuccessfulEvent>(OnReclaimSuccessful);
-            SubscribeLocalEvent<ReclaimCancelledEvent>(OnReclaimCancelled);
+            SubscribeLocalEvent<BiomassReclaimerComponent, DoAfterEvent>(OnDoAfter);
         }
 
         private void OnSuicide(EntityUid uid, BiomassReclaimerComponent component, SuicideEvent args)
@@ -146,20 +146,14 @@ namespace Content.Server.Medical.BiomassReclaimer
         }
         private void OnAfterInteractUsing(EntityUid uid, BiomassReclaimerComponent component, AfterInteractUsingEvent args)
         {
-            if (!args.CanReach)
-                return;
-
-            if (component.CancelToken != null || args.Target == null)
+            if (!args.CanReach || args.Target == null)
                 return;
 
             if (!HasComp<MobStateComponent>(args.Used) || !CanGib(uid, args.Used, component))
                 return;
 
-            component.CancelToken = new CancellationTokenSource();
-            _doAfterSystem.DoAfter(new DoAfterEventArgs(args.User, 7f, component.CancelToken.Token, args.Target, args.Used)
+            _doAfterSystem.DoAfter(new DoAfterEventArgs(args.User, 7f, target:args.Target, used:args.Used)
             {
-                BroadcastFinishedEvent = new ReclaimSuccessfulEvent(args.User, args.Used, uid),
-                BroadcastCancelledEvent = new ReclaimCancelledEvent(uid),
                 BreakOnTargetMove = true,
                 BreakOnUserMove = true,
                 BreakOnStun = true,
@@ -200,21 +194,15 @@ namespace Content.Server.Medical.BiomassReclaimer
             args.AddPercentageUpgrade("biomass-reclaimer-component-upgrade-biomass-yield", component.YieldPerUnitMass / component.BaseYieldPerUnitMass);
         }
 
-        private void OnReclaimSuccessful(ReclaimSuccessfulEvent args)
+        private void OnDoAfter(EntityUid uid, BiomassReclaimerComponent component, DoAfterEvent args)
         {
-            if (!TryComp<BiomassReclaimerComponent>(args.Reclaimer, out var reclaimer))
+            if (args.Handled || args.Cancelled || args.Args.Target == null || HasComp<BiomassReclaimerComponent>(args.Args.Target.Value))
                 return;
 
-            _adminLogger.Add(LogType.Action, LogImpact.Extreme, $"{ToPrettyString(args.User):player} used a biomass reclaimer to gib {ToPrettyString(args.Target):target} in {ToPrettyString(args.Reclaimer):reclaimer}");
-            reclaimer.CancelToken = null;
-            StartProcessing(args.Target, reclaimer);
-        }
+            _adminLogger.Add(LogType.Action, LogImpact.Extreme, $"{ToPrettyString(args.Args.User):player} used a biomass reclaimer to gib {ToPrettyString(args.Args.Target.Value):target} in {ToPrettyString(uid):reclaimer}");
+            StartProcessing(args.Args.Target.Value, component);
 
-        private void OnReclaimCancelled(ReclaimCancelledEvent args)
-        {
-            if (!TryComp<BiomassReclaimerComponent>(args.Reclaimer, out var reclaimer))
-                return;
-            reclaimer.CancelToken = null;
+            args.Handled = true;
         }
 
         private void StartProcessing(EntityUid toProcess, BiomassReclaimerComponent component, PhysicsComponent? physics = null)
@@ -266,28 +254,5 @@ namespace Content.Server.Medical.BiomassReclaimer
 
             return true;
         }
-
-        private readonly struct ReclaimCancelledEvent
-        {
-            public readonly EntityUid Reclaimer;
-
-            public ReclaimCancelledEvent(EntityUid reclaimer)
-            {
-                Reclaimer = reclaimer;
-            }
-        }
-
-        private readonly struct ReclaimSuccessfulEvent
-        {
-            public readonly EntityUid User;
-            public readonly EntityUid Target;
-            public readonly EntityUid Reclaimer;
-            public ReclaimSuccessfulEvent(EntityUid user, EntityUid target, EntityUid reclaimer)
-            {
-                User = user;
-                Target = target;
-                Reclaimer = reclaimer;
-            }
-        }
     }
 }
index a48b372dd6ead57bd59812dce3e4321f92e6d08a..edebf7697afb895020d8d0687de5cc48c154ff26 100644 (file)
@@ -45,8 +45,6 @@ namespace Content.Server.Medical.Components
         [DataField("selfHealPenaltyMultiplier")]
         public float SelfHealPenaltyMultiplier = 3f;
 
-        public CancellationTokenSource? CancelToken = null;
-
         /// <summary>
         ///     Sound played on healing begin
         /// </summary>
index 237cd4b4ff50c9b45f750e1941bcf7285d305567..793b6e628dc8691b63b913895959b4f7557f33a3 100644 (file)
@@ -20,10 +20,7 @@ namespace Content.Server.Medical.Components
         /// </summary>
         [DataField("scanDelay")]
         public float ScanDelay = 0.8f;
-        /// <summary>
-        ///     Token for interrupting scanning do after.
-        /// </summary>
-        public CancellationTokenSource? CancelToken;
+
         public BoundUserInterface? UserInterface => Owner.GetUIOrNull(HealthAnalyzerUiKey.Key);
 
         /// <summary>
index b7890310446ec709a6c533546708f4daa36731da..a778322caf940a1515cfdce119eeb8fdda553656 100644 (file)
@@ -1,4 +1,3 @@
-using System.Threading;
 using Content.Server.Atmos;
 using Content.Server.Atmos.EntitySystems;
 using Content.Server.Atmos.Piping.Components;
@@ -14,18 +13,19 @@ using Content.Server.NodeContainer;
 using Content.Server.NodeContainer.NodeGroups;
 using Content.Server.NodeContainer.Nodes;
 using Content.Server.Power.Components;
-using Content.Server.Tools;
 using Content.Server.UserInterface;
 using Content.Shared.Chemistry;
 using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.Containers.ItemSlots;
+using Content.Shared.DoAfter;
 using Content.Shared.DragDrop;
 using Content.Shared.Emag.Systems;
 using Content.Shared.Examine;
 using Content.Shared.Interaction;
 using Content.Shared.Medical.Cryogenics;
 using Content.Shared.MedicalScanner;
+using Content.Shared.Tools;
 using Content.Shared.Tools.Components;
 using Content.Shared.Verbs;
 using Robust.Server.GameObjects;
@@ -44,7 +44,7 @@ public sealed partial class CryoPodSystem: SharedCryoPodSystem
     [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
     [Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
     [Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
-    [Dependency] private readonly ToolSystem _toolSystem = default!;
+    [Dependency] private readonly SharedToolSystem _toolSystem = default!;
     [Dependency] private readonly IGameTiming _gameTiming = default!;
     [Dependency] private readonly MetaDataSystem _metaDataSystem = default!;
     [Dependency] private readonly ReactiveSystem _reactiveSystem = default!;
@@ -57,8 +57,7 @@ public sealed partial class CryoPodSystem: SharedCryoPodSystem
         SubscribeLocalEvent<CryoPodComponent, ComponentInit>(OnComponentInit);
         SubscribeLocalEvent<CryoPodComponent, GetVerbsEvent<AlternativeVerb>>(AddAlternativeVerbs);
         SubscribeLocalEvent<CryoPodComponent, GotEmaggedEvent>(OnEmagged);
-        SubscribeLocalEvent<CryoPodComponent, DoInsertCryoPodEvent>(DoInsertCryoPod);
-        SubscribeLocalEvent<CryoPodComponent, DoInsertCancelledCryoPodEvent>(DoInsertCancelCryoPod);
+        SubscribeLocalEvent<CryoPodComponent, DoAfterEvent>(OnDoAfter);
         SubscribeLocalEvent<CryoPodComponent, CryoPodPryFinished>(OnCryoPodPryFinished);
         SubscribeLocalEvent<CryoPodComponent, CryoPodPryInterrupted>(OnCryoPodPryInterrupted);
 
@@ -130,32 +129,30 @@ public sealed partial class CryoPodSystem: SharedCryoPodSystem
     private void HandleDragDropOn(EntityUid uid, CryoPodComponent cryoPodComponent, ref DragDropTargetEvent args)
     {
         if (cryoPodComponent.BodyContainer.ContainedEntity != null)
-        {
             return;
-        }
 
-        if (cryoPodComponent.DragDropCancelToken != null)
-        {
-            cryoPodComponent.DragDropCancelToken.Cancel();
-            cryoPodComponent.DragDropCancelToken = null;
-            return;
-        }
-
-        cryoPodComponent.DragDropCancelToken = new CancellationTokenSource();
-        var doAfterArgs = new DoAfterEventArgs(args.User, cryoPodComponent.EntryDelay, cryoPodComponent.DragDropCancelToken.Token, uid, args.Dragged)
+        var doAfterArgs = new DoAfterEventArgs(args.User, cryoPodComponent.EntryDelay, target:args.Dragged, used:uid)
         {
             BreakOnDamage = true,
             BreakOnStun = true,
             BreakOnTargetMove = true,
             BreakOnUserMove = true,
             NeedHand = false,
-            TargetFinishedEvent = new DoInsertCryoPodEvent(args.Dragged),
-            TargetCancelledEvent = new DoInsertCancelledCryoPodEvent()
         };
         _doAfterSystem.DoAfter(doAfterArgs);
         args.Handled = true;
     }
 
+    private void OnDoAfter(EntityUid uid, CryoPodComponent component, DoAfterEvent args)
+    {
+        if (args.Cancelled || args.Handled || args.Args.Target == null)
+            return;
+
+        InsertBody(uid, args.Args.Target.Value, component);
+
+        args.Handled = true;
+    }
+
     private void OnActivateUIAttempt(EntityUid uid, CryoPodComponent cryoPodComponent, ActivatableUIOpenAttemptEvent args)
     {
         if (args.Cancelled)
@@ -190,9 +187,8 @@ public sealed partial class CryoPodSystem: SharedCryoPodSystem
                 return;
             cryoPodComponent.IsPrying = true;
 
-            _toolSystem.UseTool(args.Used, args.User, uid, 0f,
-                cryoPodComponent.PryDelay, "Prying",
-                new CryoPodPryFinished(), new CryoPodPryInterrupted(), uid);
+            var toolEvData = new ToolEventData(new CryoPodPryFinished(), targetEntity:uid);
+            _toolSystem.UseTool(args.Used, args.User, uid, cryoPodComponent.PryDelay, new [] {"Prying"}, toolEvData);
 
             args.Handled = true;
         }
@@ -200,7 +196,7 @@ public sealed partial class CryoPodSystem: SharedCryoPodSystem
 
     private void OnExamined(EntityUid uid, CryoPodComponent component, ExaminedEvent args)
     {
-        var container = _itemSlotsSystem.GetItemOrNull(component.Owner, component.SolutionContainerName);
+        var container = _itemSlotsSystem.GetItemOrNull(uid, component.SolutionContainerName);
         if (args.IsInDetailsRange && container != null && _solutionContainerSystem.TryGetFitsInDispenser(container.Value, out var containerSolution))
         {
             args.PushMarkup(Loc.GetString("cryo-pod-examine", ("beaker", Name(container.Value))));
index 4fbcfb08338a16d999c374b227ef0c3af949a108..a908801605af665b51de820bda3ccd1415a33267 100644 (file)
@@ -1,4 +1,3 @@
-using System.Threading;
 using Content.Server.Administration.Logs;
 using Content.Server.Body.Systems;
 using Content.Server.DoAfter;
@@ -7,6 +6,7 @@ using Content.Server.Stack;
 using Content.Shared.Audio;
 using Content.Shared.Damage;
 using Content.Shared.Database;
+using Content.Shared.DoAfter;
 using Content.Shared.Interaction;
 using Content.Shared.Interaction.Events;
 using Content.Shared.Mobs;
@@ -35,51 +35,39 @@ public sealed class HealingSystem : EntitySystem
         base.Initialize();
         SubscribeLocalEvent<HealingComponent, UseInHandEvent>(OnHealingUse);
         SubscribeLocalEvent<HealingComponent, AfterInteractEvent>(OnHealingAfterInteract);
-        SubscribeLocalEvent<HealingCancelledEvent>(OnHealingCancelled);
-        SubscribeLocalEvent<DamageableComponent, HealingCompleteEvent>(OnHealingComplete);
+        SubscribeLocalEvent<DamageableComponent, DoAfterEvent<HealingData>>(OnDoAfter);
     }
 
-    private void OnHealingComplete(EntityUid uid, DamageableComponent component, HealingCompleteEvent args)
+    private void OnDoAfter(EntityUid uid, DamageableComponent component, DoAfterEvent<HealingData> args)
     {
-        if (_mobStateSystem.IsDead(uid))
+        if (args.Handled || args.Cancelled || _mobStateSystem.IsDead(uid) || args.Args.Used == null)
             return;
 
-        if (TryComp<StackComponent>(args.Component.Owner, out var stack) && stack.Count < 1)
+        if (component.DamageContainerID is not null && !component.DamageContainerID.Equals(component.DamageContainerID))
             return;
 
-        if (component.DamageContainerID is not null &&
-            !component.DamageContainerID.Equals(component.DamageContainerID))
-            return;
-
-        if (args.Component.BloodlossModifier != 0)
-        {
-            // Heal some bloodloss damage.
-            _bloodstreamSystem.TryModifyBleedAmount(uid, args.Component.BloodlossModifier);
-        }
+        // Heal some bloodloss damage.
+        if (args.AdditionalData.HealingComponent.BloodlossModifier != 0)
+            _bloodstreamSystem.TryModifyBleedAmount(uid, args.AdditionalData.HealingComponent.BloodlossModifier);
 
-        var healed = _damageable.TryChangeDamage(uid, args.Component.Damage, true, origin: args.User);
+        var healed = _damageable.TryChangeDamage(uid, args.AdditionalData.HealingComponent.Damage, true, origin: args.Args.User);
 
-        // Reverify that we can heal the damage.
         if (healed == null)
             return;
 
-        _stacks.Use(args.Component.Owner, 1, stack);
+        // Reverify that we can heal the damage.
+        _stacks.Use(args.Args.Used.Value, 1, args.AdditionalData.Stack);
+
+        if (uid != args.Args.User)
+            _adminLogger.Add(LogType.Healed, $"{EntityManager.ToPrettyString(args.Args.User):user} healed {EntityManager.ToPrettyString(uid):target} for {healed.Total:damage} damage");
 
-        if (uid != args.User)
-            _adminLogger.Add(LogType.Healed, $"{EntityManager.ToPrettyString(args.User):user} healed {EntityManager.ToPrettyString(uid):target} for {healed.Total:damage} damage");
         else
-            _adminLogger.Add(LogType.Healed, $"{EntityManager.ToPrettyString(args.User):user} healed themselves for {healed.Total:damage} damage");
+            _adminLogger.Add(LogType.Healed, $"{EntityManager.ToPrettyString(args.Args.User):user} healed themselves for {healed.Total:damage} damage");
 
-        if (args.Component.HealingEndSound != null)
-        {
-            _audio.PlayPvs(args.Component.HealingEndSound, uid,
-                AudioHelpers.WithVariation(0.125f, _random).WithVolume(-5f));
-        }
-    }
+        if (args.AdditionalData.HealingComponent.HealingEndSound != null)
+            _audio.PlayPvs(args.AdditionalData.HealingComponent.HealingEndSound, uid, AudioHelpers.WithVariation(0.125f, _random).WithVolume(-5f));
 
-    private static void OnHealingCancelled(HealingCancelledEvent ev)
-    {
-        ev.Component.CancelToken = null;
+        args.Handled = true;
     }
 
     private void OnHealingUse(EntityUid uid, HealingComponent component, UseInHandEvent args)
@@ -102,15 +90,7 @@ public sealed class HealingSystem : EntitySystem
 
     private bool TryHeal(EntityUid uid, EntityUid user, EntityUid target, HealingComponent component)
     {
-        if (component.CancelToken != null)
-        {
-            return false;
-        }
-
-        if (_mobStateSystem.IsDead(target))
-            return false;
-
-        if (!TryComp<DamageableComponent>(target, out var targetDamage))
+        if (_mobStateSystem.IsDead(target) || !TryComp<DamageableComponent>(target, out var targetDamage))
             return false;
 
         if (targetDamage.TotalDamage == 0)
@@ -119,28 +99,22 @@ public sealed class HealingSystem : EntitySystem
         if (component.DamageContainerID is not null && !component.DamageContainerID.Equals(targetDamage.DamageContainerID))
             return false;
 
-        if (user != target &&
-            !_interactionSystem.InRangeUnobstructed(user, target, popup: true))
-        {
+        if (user != target && !_interactionSystem.InRangeUnobstructed(user, target, popup: true))
             return false;
-        }
 
-        if (TryComp<StackComponent>(component.Owner, out var stack) && stack.Count < 1)
+        if (!TryComp<StackComponent>(uid, out var stack) || stack.Count < 1)
             return false;
 
-        component.CancelToken = new CancellationTokenSource();
-
         if (component.HealingBeginSound != null)
-        {
-            _audio.PlayPvs(component.HealingBeginSound, uid,
-                AudioHelpers.WithVariation(0.125f, _random).WithVolume(-5f));
-        }
+            _audio.PlayPvs(component.HealingBeginSound, uid, AudioHelpers.WithVariation(0.125f, _random).WithVolume(-5f));
 
         var delay = user != target
             ? component.Delay
             : component.Delay * GetScaledHealingPenalty(user, component);
 
-        _doAfter.DoAfter(new DoAfterEventArgs(user, delay, component.CancelToken.Token, target)
+        var healingData = new HealingData(component, stack);
+
+        var doAfterEventArgs = new DoAfterEventArgs(user, delay, target: target, used: uid)
         {
             BreakOnUserMove = true,
             BreakOnTargetMove = true,
@@ -148,22 +122,11 @@ public sealed class HealingSystem : EntitySystem
             // not being able to heal your own ticking damage would be frustrating.
             BreakOnStun = true,
             NeedHand = true,
-            TargetFinishedEvent = new HealingCompleteEvent
-            {
-                User = user,
-                Component = component,
-            },
-            BroadcastCancelledEvent = new HealingCancelledEvent
-            {
-                Component = component,
-            },
             // Juusstt in case damageble gets removed it avoids having to re-cancel the token. Won't need this when DoAfterEvent<T> gets added.
-            PostCheck = () =>
-            {
-                component.CancelToken = null;
-                return true;
-            },
-        });
+            PostCheck = () => true
+        };
+
+        _doAfter.DoAfter(doAfterEventArgs, healingData);
 
         return true;
     }
@@ -179,27 +142,18 @@ public sealed class HealingSystem : EntitySystem
         var output = component.Delay;
         if (!TryComp<MobThresholdsComponent>(uid, out var mobThreshold) || !TryComp<DamageableComponent>(uid, out var damageable))
             return output;
-
-
-        if (!_mobThresholdSystem.TryGetThresholdForState(uid, MobState.Critical, out var amount,
-                mobThreshold))
-        {
+        if (!_mobThresholdSystem.TryGetThresholdForState(uid, MobState.Critical, out var amount, mobThreshold))
             return 1;
-        }
+
         var percentDamage = (float) (damageable.TotalDamage / amount);
         //basically make it scale from 1 to the multiplier.
         var modifier = percentDamage * (component.SelfHealPenaltyMultiplier - 1) + 1;
         return Math.Max(modifier, 1);
     }
 
-    private sealed class HealingCompleteEvent : EntityEventArgs
-    {
-        public EntityUid User;
-        public HealingComponent Component = default!;
-    }
-
-    private sealed class HealingCancelledEvent : EntityEventArgs
+    private record struct HealingData(HealingComponent HealingComponent, StackComponent Stack)
     {
-        public HealingComponent Component = default!;
+        public HealingComponent HealingComponent = HealingComponent;
+        public StackComponent Stack = Stack;
     }
 }
index 0f5f1d09592c42e2134b45d9c7b7f9a8c7a00ea9..0ba0770036384a191659cf9afcd4309fbe7b6dfc 100644 (file)
@@ -1,15 +1,13 @@
-using System.Threading;
 using Content.Server.DoAfter;
 using Content.Server.Medical.Components;
 using Content.Server.Disease;
 using Content.Server.Popups;
 using Content.Shared.Damage;
+using Content.Shared.DoAfter;
 using Content.Shared.IdentityManagement;
 using Content.Shared.Interaction;
 using Content.Shared.Mobs.Components;
-using Content.Shared.Audio;
 using Robust.Server.GameObjects;
-using Robust.Shared.Player;
 using static Content.Shared.MedicalScanner.SharedHealthAnalyzerComponent;
 
 namespace Content.Server.Medical
@@ -20,14 +18,14 @@ namespace Content.Server.Medical
         [Dependency] private readonly DiseaseSystem _disease = default!;
         [Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
         [Dependency] private readonly PopupSystem _popupSystem = default!;
+        [Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
 
         public override void Initialize()
         {
             base.Initialize();
             SubscribeLocalEvent<HealthAnalyzerComponent, ActivateInWorldEvent>(HandleActivateInWorld);
             SubscribeLocalEvent<HealthAnalyzerComponent, AfterInteractEvent>(OnAfterInteract);
-            SubscribeLocalEvent<TargetScanSuccessfulEvent>(OnTargetScanSuccessful);
-            SubscribeLocalEvent<ScanCancelledEvent>(OnScanCancelled);
+            SubscribeLocalEvent<HealthAnalyzerComponent, DoAfterEvent>(OnDoAfter);
         }
 
         private void HandleActivateInWorld(EntityUid uid, HealthAnalyzerComponent healthAnalyzer, ActivateInWorldEvent args)
@@ -37,33 +35,13 @@ namespace Content.Server.Medical
 
         private void OnAfterInteract(EntityUid uid, HealthAnalyzerComponent healthAnalyzer, AfterInteractEvent args)
         {
-            if (healthAnalyzer.CancelToken != null)
-            {
-                healthAnalyzer.CancelToken.Cancel();
-                healthAnalyzer.CancelToken = null;
+            if (args.Target == null || !args.CanReach || !HasComp<MobStateComponent>(args.Target))
                 return;
-            }
-
-            if (args.Target == null)
-                return;
-
-            if (!args.CanReach)
-                return;
-
-            if (healthAnalyzer.CancelToken != null)
-                return;
-
-            if (!HasComp<MobStateComponent>(args.Target))
-                return;
-
-            healthAnalyzer.CancelToken = new CancellationTokenSource();
 
             _audio.PlayPvs(healthAnalyzer.ScanningBeginSound, uid);
 
-            _doAfterSystem.DoAfter(new DoAfterEventArgs(args.User, healthAnalyzer.ScanDelay, healthAnalyzer.CancelToken.Token, target: args.Target)
+            _doAfterSystem.DoAfter(new DoAfterEventArgs(args.User, healthAnalyzer.ScanDelay, target: args.Target, used:uid)
             {
-                BroadcastFinishedEvent = new TargetScanSuccessfulEvent(args.User, args.Target, healthAnalyzer),
-                BroadcastCancelledEvent = new ScanCancelledEvent(healthAnalyzer),
                 BreakOnTargetMove = true,
                 BreakOnUserMove = true,
                 BreakOnStun = true,
@@ -71,37 +49,47 @@ namespace Content.Server.Medical
             });
         }
 
-        private void OnTargetScanSuccessful(TargetScanSuccessfulEvent args)
+        private void OnDoAfter(EntityUid uid, HealthAnalyzerComponent component, DoAfterEvent args)
         {
-            args.Component.CancelToken = null;
+            if (args.Handled || args.Cancelled || args.Args.Target == null)
+                return;
 
-            _audio.PlayPvs(args.Component.ScanningEndSound, args.User);
+            _audio.PlayPvs(component.ScanningEndSound, args.Args.User);
 
-            UpdateScannedUser(args.Component.Owner, args.User, args.Target, args.Component);
+            UpdateScannedUser(uid, args.Args.User, args.Args.Target.Value, component);
             // Below is for the traitor item
             // Piggybacking off another component's doafter is complete CBT so I gave up
             // and put it on the same component
-            if (string.IsNullOrEmpty(args.Component.Disease) || args.Target == null)
+            if (string.IsNullOrEmpty(component.Disease))
+            {
+                args.Handled = true;
                 return;
+            }
 
-            _disease.TryAddDisease(args.Target.Value, args.Component.Disease);
+            _disease.TryAddDisease(args.Args.Target.Value, component.Disease);
 
-            if (args.User == args.Target)
+            if (args.Args.User == args.Args.Target)
             {
-                _popupSystem.PopupEntity(Loc.GetString("disease-scanner-gave-self", ("disease", args.Component.Disease)),
-                    args.User, args.User);
-                return;
+                _popupSystem.PopupEntity(Loc.GetString("disease-scanner-gave-self", ("disease", component.Disease)),
+                    args.Args.User, args.Args.User);
+            }
+
+
+            else
+            {
+                _popupSystem.PopupEntity(Loc.GetString("disease-scanner-gave-other", ("target", Identity.Entity(args.Args.Target.Value, EntityManager)),
+                    ("disease", component.Disease)), args.Args.User, args.Args.User);
             }
-            _popupSystem.PopupEntity(Loc.GetString("disease-scanner-gave-other", ("target", Identity.Entity(args.Target.Value, EntityManager)), ("disease", args.Component.Disease)),
-                args.User, args.User);
+
+            args.Handled = true;
         }
 
         private void OpenUserInterface(EntityUid user, HealthAnalyzerComponent healthAnalyzer)
         {
-            if (!TryComp<ActorComponent>(user, out var actor))
+            if (!TryComp<ActorComponent>(user, out var actor) || healthAnalyzer.UserInterface == null)
                 return;
 
-            healthAnalyzer.UserInterface?.Open(actor.PlayerSession);
+            _uiSystem.OpenUi(healthAnalyzer.UserInterface ,actor.PlayerSession);
         }
 
         public void UpdateScannedUser(EntityUid uid, EntityUid user, EntityUid? target, HealthAnalyzerComponent? healthAnalyzer)
@@ -116,35 +104,7 @@ namespace Content.Server.Medical
                 return;
 
             OpenUserInterface(user, healthAnalyzer);
-            healthAnalyzer.UserInterface?.SendMessage(new HealthAnalyzerScannedUserMessage(target));
-        }
-
-        private static void OnScanCancelled(ScanCancelledEvent args)
-        {
-            args.HealthAnalyzer.CancelToken = null;
-        }
-
-        private sealed class ScanCancelledEvent : EntityEventArgs
-        {
-            public readonly HealthAnalyzerComponent HealthAnalyzer;
-            public ScanCancelledEvent(HealthAnalyzerComponent healthAnalyzer)
-            {
-                HealthAnalyzer = healthAnalyzer;
-            }
-        }
-
-        private sealed class TargetScanSuccessfulEvent : EntityEventArgs
-        {
-            public EntityUid User { get; }
-            public EntityUid? Target { get; }
-            public HealthAnalyzerComponent Component { get; }
-
-            public TargetScanSuccessfulEvent(EntityUid user, EntityUid? target, HealthAnalyzerComponent component)
-            {
-                User = user;
-                Target = target;
-                Component = component;
-            }
+            _uiSystem.SendUiMessage(healthAnalyzer.UserInterface, new HealthAnalyzerScannedUserMessage(target));
         }
     }
 }
index e0af81530bf08b624186ad2b24492eb395d3a575..6e27ecc6b106f82ee709f3c94b1790053e2aace4 100644 (file)
@@ -12,8 +12,6 @@ namespace Content.Server.Medical.Components
     {
         public bool IsActive = false;
 
-        public CancellationTokenSource? CancelToken;
-
         [DataField("delay")]
         public float Delay = 2.5f;
 
index a78e5bbe2b70544885e672426fed330810dea0e1..1ce71c8c710df257bb9aad2d98b88b6cce6f9a94 100644 (file)
@@ -8,10 +8,9 @@ using Content.Shared.Damage;
 using Content.Shared.FixedPoint;
 using Content.Shared.Inventory.Events;
 using Content.Shared.Verbs;
-using Robust.Shared.Player;
-using System.Threading;
 using Content.Shared.Mobs.Components;
 using Content.Shared.Mobs.Systems;
+using Content.Shared.DoAfter;
 
 namespace Content.Server.Medical
 {
@@ -29,8 +28,7 @@ namespace Content.Server.Medical
             SubscribeLocalEvent<WearingStethoscopeComponent, GetVerbsEvent<InnateVerb>>(AddStethoscopeVerb);
             SubscribeLocalEvent<StethoscopeComponent, GetItemActionsEvent>(OnGetActions);
             SubscribeLocalEvent<StethoscopeComponent, StethoscopeActionEvent>(OnStethoscopeAction);
-            SubscribeLocalEvent<ListenSuccessfulEvent>(OnListenSuccess);
-            SubscribeLocalEvent<ListenCancelledEvent>(OnListenCancelled);
+            SubscribeLocalEvent<StethoscopeComponent, DoAfterEvent>(OnDoAfter);
         }
 
         /// <summary>
@@ -81,7 +79,7 @@ namespace Content.Server.Medical
             {
                 Act = () =>
                 {
-                    StartListening(uid, args.Target, stetho); // start doafter
+                    StartListening(component.Stethoscope, uid, args.Target, stetho); // start doafter
                 },
                 Text = Loc.GetString("stethoscope-verb"),
                 IconTexture = "Clothing/Neck/Misc/stethoscope.rsi/icon.png",
@@ -93,7 +91,7 @@ namespace Content.Server.Medical
 
         private void OnStethoscopeAction(EntityUid uid, StethoscopeComponent component, StethoscopeActionEvent args)
         {
-            StartListening(args.Performer, args.Target, component);
+            StartListening(uid, args.Performer, args.Target, component);
         }
 
         private void OnGetActions(EntityUid uid, StethoscopeComponent component, GetItemActionsEvent args)
@@ -101,27 +99,11 @@ namespace Content.Server.Medical
             args.Actions.Add(component.Action);
         }
 
-        // doafter succeeded / failed
-        private void OnListenSuccess(ListenSuccessfulEvent ev)
-        {
-            ev.Component.CancelToken = null;
-            ExamineWithStethoscope(ev.User, ev.Target);
-        }
-
-        private void OnListenCancelled(ListenCancelledEvent ev)
-        {
-            if (ev.Component == null)
-                return;
-            ev.Component.CancelToken = null;
-        }
         // construct the doafter and start it
-        private void StartListening(EntityUid user, EntityUid target, StethoscopeComponent comp)
+        private void StartListening(EntityUid scope, EntityUid user, EntityUid target, StethoscopeComponent comp)
         {
-            comp.CancelToken = new CancellationTokenSource();
-            _doAfterSystem.DoAfter(new DoAfterEventArgs(user, comp.Delay, comp.CancelToken.Token, target: target)
+            _doAfterSystem.DoAfter(new DoAfterEventArgs(user, comp.Delay, target: target, used:scope)
             {
-                BroadcastFinishedEvent = new ListenSuccessfulEvent(user, target, comp),
-                BroadcastCancelledEvent = new ListenCancelledEvent(user, comp),
                 BreakOnTargetMove = true,
                 BreakOnUserMove = true,
                 BreakOnStun = true,
@@ -129,6 +111,14 @@ namespace Content.Server.Medical
             });
         }
 
+        private void OnDoAfter(EntityUid uid, StethoscopeComponent component, DoAfterEvent args)
+        {
+            if (args.Handled || args.Cancelled || args.Args.Target == null)
+                return;
+
+            ExamineWithStethoscope(args.Args.User, args.Args.Target.Value);
+        }
+
         /// <summary>
         /// Return a value based on the total oxyloss of the target.
         /// Could be expanded in the future with reagent effects etc.
@@ -165,34 +155,6 @@ namespace Content.Server.Medical
             };
             return msg;
         }
-
-        // events for the doafter
-        private sealed class ListenSuccessfulEvent : EntityEventArgs
-        {
-            public EntityUid User;
-            public EntityUid Target;
-            public StethoscopeComponent Component;
-
-            public ListenSuccessfulEvent(EntityUid user, EntityUid target, StethoscopeComponent component)
-            {
-                User = user;
-                Target = target;
-                Component = component;
-            }
-        }
-
-        private sealed class ListenCancelledEvent : EntityEventArgs
-        {
-            public EntityUid Uid;
-            public StethoscopeComponent Component;
-
-            public ListenCancelledEvent(EntityUid uid, StethoscopeComponent component)
-            {
-                Uid = uid;
-                Component = component;
-            }
-        }
-
     }
 
     public sealed class StethoscopeActionEvent : EntityTargetActionEvent {}
index 16cfc3919dcca3c3a5eb39fa5665b9508ac19410..2d91af1f6ed0a5f02906d0b20b0839b53a27d750 100644 (file)
@@ -174,8 +174,6 @@ namespace Content.Server.Nuke
         /// </summary>
         public bool PlayedAlertSound = false;
 
-        public CancellationToken? DisarmCancelToken = null;
-
         public IPlayingAudioStream? AlertAudioStream = default;
     }
 }
index 2c870f504b4d6d8614b34f0737874305686bbf20..47e10929a30ccc95d26d05e41971897bf7ba728a 100644 (file)
@@ -6,13 +6,13 @@ using Content.Server.DoAfter;
 using Content.Server.Explosion.EntitySystems;
 using Content.Server.Popups;
 using Content.Server.Station.Systems;
-using Content.Server.UserInterface;
 using Content.Shared.Audio;
 using Content.Shared.Construction.Components;
 using Content.Shared.Containers.ItemSlots;
+using Content.Shared.DoAfter;
 using Content.Shared.Nuke;
 using Content.Shared.Popups;
-using Robust.Shared.Audio;
+using Robust.Server.GameObjects;
 using Robust.Shared.Containers;
 using Robust.Shared.Player;
 using Robust.Shared.Random;
@@ -30,6 +30,8 @@ namespace Content.Server.Nuke
         [Dependency] private readonly ChatSystem _chatSystem = default!;
         [Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
         [Dependency] private readonly IRobustRandom _random = default!;
+        [Dependency] private readonly SharedAudioSystem _audio = default!;
+        [Dependency] private readonly UserInterfaceSystem _ui = default!;
 
         /// <summary>
         ///     Used to calculate when the nuke song should start playing for maximum kino with the nuke sfx
@@ -65,8 +67,7 @@ namespace Content.Server.Nuke
             SubscribeLocalEvent<NukeComponent, NukeKeypadEnterMessage>(OnEnterButtonPressed);
 
             // Doafter events
-            SubscribeLocalEvent<NukeComponent, NukeDisarmSuccessEvent>(OnDisarmSuccess);
-            SubscribeLocalEvent<NukeComponent, NukeDisarmCancelledEvent>(OnDisarmCancelled);
+            SubscribeLocalEvent<NukeComponent, DoAfterEvent>(OnDoAfter);
         }
 
         private void OnInit(EntityUid uid, NukeComponent component, ComponentInit args)
@@ -211,7 +212,7 @@ namespace Content.Server.Nuke
 
         private void OnClearButtonPressed(EntityUid uid, NukeComponent component, NukeKeypadClearMessage args)
         {
-            PlaySound(uid, component.KeypadPressSound, 0f, component);
+            _audio.Play(component.KeypadPressSound, Filter.Pvs(uid), uid, true);
 
             if (component.Status != NukeStatus.AWAIT_CODE)
                 return;
@@ -241,17 +242,18 @@ namespace Content.Server.Nuke
 
         #region Doafter Events
 
-        private void OnDisarmSuccess(EntityUid uid, NukeComponent component, NukeDisarmSuccessEvent args)
+        private void OnDoAfter(EntityUid uid, NukeComponent component, DoAfterEvent args)
         {
-            component.DisarmCancelToken = null;
+            if(args.Handled || args.Cancelled)
+                return;
+
             DisarmBomb(uid, component);
-        }
 
-        private void OnDisarmCancelled(EntityUid uid, NukeComponent component, NukeDisarmCancelledEvent args)
-        {
-            component.DisarmCancelToken = null;
-        }
+            var ev = new NukeDisarmSuccessEvent();
+            RaiseLocalEvent(ev);
 
+            args.Handled = true;
+        }
         #endregion
 
         private void TickCooldown(EntityUid uid, float frameTime, NukeComponent? nuke = null)
@@ -268,7 +270,7 @@ namespace Content.Server.Nuke
                 UpdateStatus(uid, nuke);
             }
 
-            UpdateUserInterface(nuke.Owner, nuke);
+            UpdateUserInterface(uid, nuke);
         }
 
         private void TickTimer(EntityUid uid, float frameTime, NukeComponent? nuke = null)
@@ -289,7 +291,7 @@ namespace Content.Server.Nuke
             // play alert sound if time is running out
             if (nuke.RemainingTime <= nuke.AlertSoundTime && !nuke.PlayedAlertSound)
             {
-                nuke.AlertAudioStream = SoundSystem.Play(nuke.AlertSound.GetSound(), Filter.Broadcast());
+                nuke.AlertAudioStream = _audio.Play(nuke.AlertSound, Filter.Broadcast(), uid, true);
                 _soundSystem.StopStationEventMusic(uid, StationEventMusicType.Nuke);
                 nuke.PlayedAlertSound = true;
             }
@@ -329,12 +331,12 @@ namespace Content.Server.Nuke
                     {
                         component.Status = NukeStatus.AWAIT_ARM;
                         component.RemainingTime = component.Timer;
-                        PlaySound(uid, component.AccessGrantedSound, 0, component);
+                        _audio.Play(component.AccessGrantedSound, Filter.Pvs(uid), uid, true);
                     }
                     else
                     {
                         component.EnteredCode = "";
-                        PlaySound(uid, component.AccessDeniedSound, 0, component);
+                        _audio.Play(component.AccessDeniedSound, Filter.Pvs(uid), uid, true);
                     }
 
                     break;
@@ -353,7 +355,7 @@ namespace Content.Server.Nuke
             if (!Resolve(uid, ref component))
                 return;
 
-            var ui = component.Owner.GetUIOrNull(NukeUiKey.Key);
+            var ui = _ui.GetUiOrNull(uid, NukeUiKey.Key);
             if (ui == null)
                 return;
 
@@ -365,7 +367,7 @@ namespace Content.Server.Nuke
                            (component.Status == NukeStatus.AWAIT_ARM ||
                             component.Status == NukeStatus.ARMED);
 
-            var state = new NukeUiState()
+            var state = new NukeUiState
             {
                 Status = component.Status,
                 RemainingTime = (int) component.RemainingTime,
@@ -377,7 +379,7 @@ namespace Content.Server.Nuke
                 CooldownTime = (int) component.CooldownTime
             };
 
-            ui.SetState(state);
+            _ui.SetUiState(ui, state);
         }
 
         private void PlayNukeKeypadSound(EntityUid uid, int number, NukeComponent? component = null)
@@ -407,18 +409,7 @@ namespace Content.Server.Nuke
             // Don't double-dip on the octave shifting
             component.LastPlayedKeypadSemitones = number == 0 ? component.LastPlayedKeypadSemitones : semitoneShift;
 
-            SoundSystem.Play(component.KeypadPressSound.GetSound(), Filter.Pvs(uid), uid,
-                AudioHelpers.ShiftSemitone(semitoneShift).WithVolume(-5f));
-        }
-
-        private void PlaySound(EntityUid uid, SoundSpecifier sound, float varyPitch = 0f,
-            NukeComponent? component = null)
-        {
-            if (!Resolve(uid, ref component))
-                return;
-
-            SoundSystem.Play(sound.GetSound(),
-                Filter.Pvs(uid), uid, AudioHelpers.WithVariation(varyPitch).WithVolume(-5f));
+            _audio.Play(component.KeypadPressSound, Filter.Pvs(uid), uid, true, AudioHelpers.ShiftSemitone(semitoneShift).WithVolume(-5f));
         }
 
         public string GenerateRandomNumberString(int length)
@@ -465,7 +456,7 @@ namespace Content.Server.Nuke
             var sender = Loc.GetString("nuke-component-announcement-sender");
             _chatSystem.DispatchStationAnnouncement(uid, announcement, sender, false, null, Color.Red);
 
-            NukeArmedAudio(component);
+            _soundSystem.PlayGlobalOnStation(uid, _audio.GetSound(component.ArmSound));
 
             _itemSlots.SetLock(uid, component.DiskSlot, true);
             nukeXform.Anchored = true;
@@ -494,7 +485,8 @@ namespace Content.Server.Nuke
             _chatSystem.DispatchStationAnnouncement(uid, announcement, sender, false);
 
             component.PlayedNukeSong = false;
-            NukeDisarmedAudio(component);
+            _soundSystem.PlayGlobalOnStation(uid, _audio.GetSound(component.DisarmSound));
+            _soundSystem.StopStationEventMusic(uid, StationEventMusicType.Nuke);
 
             // disable sound and reset it
             component.PlayedAlertSound = false;
@@ -547,7 +539,7 @@ namespace Content.Server.Nuke
                 OwningStation = transform.GridUid,
             });
 
-            _soundSystem.StopStationEventMusic(component.Owner, StationEventMusicType.Nuke);
+            _soundSystem.StopStationEventMusic(uid, StationEventMusicType.Nuke);
             EntityManager.DeleteEntity(uid);
         }
 
@@ -567,34 +559,19 @@ namespace Content.Server.Nuke
 
         private void DisarmBombDoafter(EntityUid uid, EntityUid user, NukeComponent nuke)
         {
-            nuke.DisarmCancelToken = new();
-            var doafter = new DoAfterEventArgs(user, nuke.DisarmDoafterLength, nuke.DisarmCancelToken.Value, uid)
+            var doafter = new DoAfterEventArgs(user, nuke.DisarmDoafterLength, target: uid)
             {
-                TargetCancelledEvent = new NukeDisarmCancelledEvent(),
-                TargetFinishedEvent = new NukeDisarmSuccessEvent(),
-                BroadcastFinishedEvent = new NukeDisarmSuccessEvent(),
                 BreakOnDamage = true,
                 BreakOnStun = true,
                 BreakOnTargetMove = true,
                 BreakOnUserMove = true,
-                NeedHand = true,
+                NeedHand = true
             };
 
             _doAfterSystem.DoAfter(doafter);
             _popups.PopupEntity(Loc.GetString("nuke-component-doafter-warning"), user,
                 user, PopupType.LargeCaution);
         }
-
-        private void NukeArmedAudio(NukeComponent component)
-        {
-            _soundSystem.PlayGlobalOnStation(component.Owner, component.ArmSound.GetSound());
-        }
-
-        private void NukeDisarmedAudio(NukeComponent component)
-        {
-            _soundSystem.PlayGlobalOnStation(component.Owner, component.DisarmSound.GetSound());
-            _soundSystem.StopStationEventMusic(component.Owner, StationEventMusicType.Nuke);
-        }
     }
 
     public sealed class NukeExplodedEvent : EntityEventArgs
@@ -604,18 +581,10 @@ namespace Content.Server.Nuke
 
     /// <summary>
     ///     Raised directed on the nuke when its disarm doafter is successful.
+    ///     So the game knows not to end.
     /// </summary>
     public sealed class NukeDisarmSuccessEvent : EntityEventArgs
     {
 
     }
-
-    /// <summary>
-    ///     Raised directed on the nuke when its disarm doafter is cancelled.
-    /// </summary>
-    public sealed class NukeDisarmCancelledEvent : EntityEventArgs
-    {
-
-    }
-
 }
index e3fc4c8a2d7d89c15b72e08677e1632d3cc6a016..be882f7947ac1ca3400c843861fd7359adcc8451 100644 (file)
@@ -1,4 +1,3 @@
-using System.Threading;
 using Content.Server.Nutrition.EntitySystems;
 using Content.Shared.FixedPoint;
 using JetBrains.Annotations;
@@ -46,11 +45,5 @@ namespace Content.Server.Nutrition.Components
         /// </summary>
         [DataField("forceFeedDelay")]
         public float ForceFeedDelay = 3;
-
-        /// <summary>
-        ///     Token for interrupting a do-after action (e.g., force feeding). If not null, implies component is
-        ///     currently "in use".
-        /// </summary>
-        public CancellationTokenSource? CancelToken;
     }
 }
index 511a298867b8bd15f0aa15985165582c78a8362a..5d7d325b0226fad0397fdf4dc76125813e1b0c2f 100644 (file)
@@ -1,4 +1,3 @@
-using System.Threading;
 using Content.Server.Chemistry.EntitySystems;
 using Content.Server.Nutrition.EntitySystems;
 using Content.Shared.FixedPoint;
@@ -54,12 +53,6 @@ namespace Content.Server.Nutrition.Components
         [DataField("forceFeedDelay")]
         public float ForceFeedDelay = 3;
 
-        /// <summary>
-        ///     Token for interrupting a do-after action (e.g., force feeding). If not null, implies component is
-        ///     currently "in use".
-        /// </summary>
-        public CancellationTokenSource? CancelToken;
-
         [ViewVariables]
         public int UsesRemaining
         {
index 9ab1f48dfc5c18775e734cb7fb4800596baee9e4..04c6ee7159b1a5388b1b00e952932651388d154d 100644 (file)
@@ -1,4 +1,3 @@
-using System.Threading;
 using Content.Server.Body.Components;
 using Content.Server.Body.Systems;
 using Content.Server.Chemistry.Components.SolutionManager;
@@ -9,8 +8,11 @@ using Content.Server.Nutrition.Components;
 using Content.Server.Popups;
 using Content.Shared.Administration.Logs;
 using Content.Shared.Body.Components;
+using Content.Shared.Chemistry;
+using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.Database;
+using Content.Shared.DoAfter;
 using Content.Shared.Examine;
 using Content.Shared.FixedPoint;
 using Content.Shared.IdentityManagement;
@@ -46,6 +48,7 @@ namespace Content.Server.Nutrition.EntitySystems
         [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
         [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!;
         [Dependency] private readonly SharedAudioSystem _audio = default!;
+        [Dependency] private readonly ReactiveSystem _reaction = default!;
 
         public override void Initialize()
         {
@@ -59,8 +62,7 @@ namespace Content.Server.Nutrition.EntitySystems
             SubscribeLocalEvent<DrinkComponent, GetVerbsEvent<AlternativeVerb>>(AddDrinkVerb);
             SubscribeLocalEvent<DrinkComponent, ExaminedEvent>(OnExamined);
             SubscribeLocalEvent<DrinkComponent, SolutionTransferAttemptEvent>(OnTransferAttempt);
-            SubscribeLocalEvent<BodyComponent, DrinkEvent>(OnDrink);
-            SubscribeLocalEvent<DrinkCancelledEvent>(OnDrinkCancelled);
+            SubscribeLocalEvent<DrinkComponent, DoAfterEvent<DrinkData>>(OnDoAfter);
         }
 
         public bool IsEmpty(EntityUid uid, DrinkComponent? component = null)
@@ -126,12 +128,13 @@ namespace Content.Server.Nutrition.EntitySystems
             if (args.Handled || args.Target == null || !args.CanReach)
                 return;
 
-            args.Handled = TryDrink(args.User, args.Target.Value, component);
+            args.Handled = TryDrink(args.User, args.Target.Value, component, uid);
         }
 
         private void OnUse(EntityUid uid, DrinkComponent component, UseInHandEvent args)
         {
-            if (args.Handled) return;
+            if (args.Handled)
+                return;
 
             if (!component.Opened)
             {
@@ -142,7 +145,7 @@ namespace Content.Server.Nutrition.EntitySystems
                 return;
             }
 
-            args.Handled = TryDrink(args.User, args.User, component);
+            args.Handled = TryDrink(args.User, args.User, component, uid);
         }
 
         private void HandleLand(EntityUid uid, DrinkComponent component, ref LandEvent args)
@@ -212,36 +215,30 @@ namespace Content.Server.Nutrition.EntitySystems
             }
         }
 
-        private bool TryDrink(EntityUid user, EntityUid target, DrinkComponent drink)
+        private bool TryDrink(EntityUid user, EntityUid target, DrinkComponent drink, EntityUid item)
         {
-            // cannot stack do-afters
-            if (drink.CancelToken != null)
-            {
-                return true;
-            }
-
             if (!EntityManager.HasComponent<BodyComponent>(target))
                 return false;
 
             if (!drink.Opened)
             {
                 _popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-not-open",
-                    ("owner", EntityManager.GetComponent<MetaDataComponent>(drink.Owner).EntityName)), drink.Owner, user);
+                    ("owner", EntityManager.GetComponent<MetaDataComponent>(item).EntityName)), item, user);
                 return true;
             }
 
-            if (!_solutionContainerSystem.TryGetDrainableSolution(drink.Owner, out var drinkSolution) ||
+            if (!_solutionContainerSystem.TryGetDrainableSolution(item, out var drinkSolution) ||
                 drinkSolution.Volume <= 0)
             {
                 _popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-is-empty",
-                    ("entity", EntityManager.GetComponent<MetaDataComponent>(drink.Owner).EntityName)), drink.Owner, user);
+                    ("entity", EntityManager.GetComponent<MetaDataComponent>(item).EntityName)), item, user);
                 return true;
             }
 
             if (_foodSystem.IsMouthBlocked(target, user))
                 return true;
 
-            if (!_interactionSystem.InRangeUnobstructed(user, drink.Owner, popup: true))
+            if (!_interactionSystem.InRangeUnobstructed(user, item, popup: true))
                 return true;
 
             var forceDrink = user != target;
@@ -254,20 +251,21 @@ namespace Content.Server.Nutrition.EntitySystems
                     user, target);
 
                 // logging
-                _adminLogger.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(user):user} is forcing {ToPrettyString(target):target} to drink {ToPrettyString(drink.Owner):drink} {SolutionContainerSystem.ToPrettyString(drinkSolution)}");
+                _adminLogger.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(user):user} is forcing {ToPrettyString(target):target} to drink {ToPrettyString(item):drink} {SolutionContainerSystem.ToPrettyString(drinkSolution)}");
             }
             else
             {
                 // log voluntary drinking
-                _adminLogger.Add(LogType.Ingestion, LogImpact.Low, $"{ToPrettyString(target):target} is drinking {ToPrettyString(drink.Owner):drink} {SolutionContainerSystem.ToPrettyString(drinkSolution)}");
+                _adminLogger.Add(LogType.Ingestion, LogImpact.Low, $"{ToPrettyString(target):target} is drinking {ToPrettyString(item):drink} {SolutionContainerSystem.ToPrettyString(drinkSolution)}");
             }
-
-            drink.CancelToken = new CancellationTokenSource();
             var moveBreak = user != target;
 
             var flavors = _flavorProfileSystem.GetLocalizedFlavorsMessage(user, drinkSolution);
 
-            _doAfterSystem.DoAfter(new DoAfterEventArgs(user, forceDrink ? drink.ForceFeedDelay : drink.Delay, drink.CancelToken.Token, target, drink.Owner)
+            var drinkData = new DrinkData(drinkSolution, flavors);
+
+            var doAfterEventArgs = new DoAfterEventArgs(user, forceDrink ? drink.ForceFeedDelay : drink.Delay,
+                target: target, used: item)
             {
                 BreakOnUserMove = moveBreak,
                 BreakOnDamage = true,
@@ -275,10 +273,10 @@ namespace Content.Server.Nutrition.EntitySystems
                 BreakOnTargetMove = moveBreak,
                 MovementThreshold = 0.01f,
                 DistanceThreshold = 1.0f,
-                TargetFinishedEvent = new DrinkEvent(user, drink, drinkSolution, flavors),
-                BroadcastCancelledEvent = new DrinkCancelledEvent(drink),
-                NeedHand = true,
-            });
+                NeedHand = true
+            };
+
+            _doAfterSystem.DoAfter(doAfterEventArgs, drinkData);
 
             return true;
         }
@@ -286,103 +284,93 @@ namespace Content.Server.Nutrition.EntitySystems
         /// <summary>
         ///     Raised directed at a victim when someone has force fed them a drink.
         /// </summary>
-        private void OnDrink(EntityUid uid, BodyComponent body, DrinkEvent args)
+        private void OnDoAfter(EntityUid uid, DrinkComponent component, DoAfterEvent<DrinkData> args)
         {
-            if (args.Drink.Deleted)
+            if (args.Handled || args.Cancelled || component.Deleted)
+                return;
+
+            if (!TryComp<BodyComponent>(args.Args.Target, out var body))
                 return;
 
-            args.Drink.CancelToken = null;
-            var transferAmount = FixedPoint2.Min(args.Drink.TransferAmount, args.DrinkSolution.Volume);
-            var drained = _solutionContainerSystem.Drain(args.Drink.Owner, args.DrinkSolution, transferAmount);
+            var transferAmount = FixedPoint2.Min(component.TransferAmount, args.AdditionalData.DrinkSolution.Volume);
+            var drained = _solutionContainerSystem.Drain(uid, args.AdditionalData.DrinkSolution, transferAmount);
 
-            var forceDrink = uid != args.User;
+            var forceDrink = args.Args.Target.Value != args.Args.User;
 
-            if (!_bodySystem.TryGetBodyOrganComponents<StomachComponent>(uid, out var stomachs, body))
+            if (!_bodySystem.TryGetBodyOrganComponents<StomachComponent>(args.Args.Target.Value, out var stomachs, body))
             {
-                _popupSystem.PopupEntity(
-                    forceDrink ?
-                        Loc.GetString("drink-component-try-use-drink-cannot-drink-other") :
-                        Loc.GetString("drink-component-try-use-drink-had-enough"),
-                    uid, args.User);
+                _popupSystem.PopupEntity(forceDrink ? Loc.GetString("drink-component-try-use-drink-cannot-drink-other") : Loc.GetString("drink-component-try-use-drink-had-enough"), args.Args.Target.Value, args.Args.User);
 
-                if (EntityManager.HasComponent<RefillableSolutionComponent>(uid))
+                if (HasComp<RefillableSolutionComponent>(args.Args.Target.Value))
                 {
-                    _spillableSystem.SpillAt(args.User, drained, "PuddleSmear");
+                    _spillableSystem.SpillAt(args.Args.User, drained, "PuddleSmear");
+                    args.Handled = true;
                     return;
                 }
 
-                _solutionContainerSystem.Refill(uid, args.DrinkSolution, drained);
+                _solutionContainerSystem.Refill(args.Args.Target.Value, args.AdditionalData.DrinkSolution, drained);
+                args.Handled = true;
                 return;
             }
 
-            var firstStomach = stomachs.FirstOrNull(
-                stomach => _stomachSystem.CanTransferSolution((stomach.Comp).Owner, drained));
+            var firstStomach = stomachs.FirstOrNull(stomach => _stomachSystem.CanTransferSolution(stomach.Comp.Owner, drained));
 
-            // All stomach are full or can't handle whatever solution we have.
+            //All stomachs are full or can't handle whatever solution we have.
             if (firstStomach == null)
             {
-                _popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-had-enough"),
-                    uid, uid);
+                _popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-had-enough"), args.Args.Target.Value, args.Args.Target.Value);
 
                 if (forceDrink)
                 {
-                    _popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-had-enough-other"),
-                        uid, args.User);
-                    _spillableSystem.SpillAt(uid, drained, "PuddleSmear");
+                    _popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-had-enough-other"), args.Args.Target.Value, args.Args.User);
+                    _spillableSystem.SpillAt(args.Args.Target.Value, drained, "PuddleSmear");
                 }
                 else
-                {
-                    _solutionContainerSystem.TryAddSolution(args.Drink.Owner, args.DrinkSolution, drained);
-                }
+                    _solutionContainerSystem.TryAddSolution(uid, args.AdditionalData.DrinkSolution, drained);
 
+                args.Handled = true;
                 return;
             }
 
-            var flavors = args.FlavorMessage;
+            var flavors = args.AdditionalData.FlavorMessage;
 
             if (forceDrink)
             {
-                var targetName = Identity.Entity(uid, EntityManager);
-                var userName = Identity.Entity(args.User, EntityManager);
+                var targetName = Identity.Entity(args.Args.Target.Value, EntityManager);
+                var userName = Identity.Entity(args.Args.User, EntityManager);
 
-                _popupSystem.PopupEntity(
-                    Loc.GetString("drink-component-force-feed-success", ("user", userName), ("flavors", flavors)), uid, uid);
+                _popupSystem.PopupEntity(Loc.GetString("drink-component-force-feed-success", ("user", userName), ("flavors", flavors)), args.Args.Target.Value, args.Args.Target.Value);
 
                 _popupSystem.PopupEntity(
                     Loc.GetString("drink-component-force-feed-success-user", ("target", targetName)),
-                    args.User, args.User);
+                    args.Args.User, args.Args.User);
 
                 // log successful forced drinking
-                _adminLogger.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(args.User):user} forced {ToPrettyString(uid):target} to drink {ToPrettyString(args.Drink.Owner):drink}");
+                _adminLogger.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(uid):user} forced {ToPrettyString(args.Args.User):target} to drink {ToPrettyString(component.Owner):drink}");
             }
             else
             {
                 _popupSystem.PopupEntity(
-                    Loc.GetString("drink-component-try-use-drink-success-slurp-taste", ("flavors", flavors)), args.User,
-                    args.User);
+                    Loc.GetString("drink-component-try-use-drink-success-slurp-taste", ("flavors", flavors)), args.Args.User,
+                    args.Args.User);
                 _popupSystem.PopupEntity(
-                    Loc.GetString("drink-component-try-use-drink-success-slurp"), args.User, Filter.PvsExcept(args.User), true);
+                    Loc.GetString("drink-component-try-use-drink-success-slurp"), args.Args.User, Filter.PvsExcept(args.Args.User), true);
 
                 // log successful voluntary drinking
-                _adminLogger.Add(LogType.Ingestion, LogImpact.Low, $"{ToPrettyString(args.User):target} drank {ToPrettyString(args.Drink.Owner):drink}");
+                _adminLogger.Add(LogType.Ingestion, LogImpact.Low, $"{ToPrettyString(args.Args.User):target} drank {ToPrettyString(uid):drink}");
             }
 
-            _audio.PlayPvs(_audio.GetSound(args.Drink.UseSound), uid, AudioParams.Default.WithVolume(-2f));
+            _audio.PlayPvs(_audio.GetSound(component.UseSound), args.Args.Target.Value, AudioParams.Default.WithVolume(-2f));
 
-            drained.DoEntityReaction(uid, ReactionMethod.Ingestion);
+            _reaction.DoEntityReaction(args.Args.Target.Value, args.AdditionalData.DrinkSolution, ReactionMethod.Ingestion);
+            //TODO: Grab the stomach UIDs somehow without using Owner
             _stomachSystem.TryTransferSolution(firstStomach.Value.Comp.Owner, drained, firstStomach.Value.Comp);
-        }
 
-        private static void OnDrinkCancelled(DrinkCancelledEvent args)
-        {
-            args.Drink.CancelToken = null;
+            args.Handled = true;
         }
 
         private void AddDrinkVerb(EntityUid uid, DrinkComponent component, GetVerbsEvent<AlternativeVerb> ev)
         {
-            if (component.CancelToken != null)
-                return;
-
             if (uid == ev.User ||
                 !ev.CanInteract ||
                 !ev.CanAccess ||
@@ -397,7 +385,7 @@ namespace Content.Server.Nutrition.EntitySystems
             {
                 Act = () =>
                 {
-                    TryDrink(ev.User, ev.User, component);
+                    TryDrink(ev.User, ev.User, component, uid);
                 },
                 IconTexture = "/Textures/Interface/VerbIcons/drink.svg.192dpi.png",
                 Text = Loc.GetString("drink-system-verb-drink"),
@@ -418,5 +406,11 @@ namespace Content.Server.Nutrition.EntitySystems
 
             return remainingString;
         }
+
+        private record struct DrinkData(Solution DrinkSolution, string FlavorMessage)
+        {
+            public readonly Solution DrinkSolution = DrinkSolution;
+            public readonly string FlavorMessage = FlavorMessage;
+        }
     }
 }
index 59a744cbaeeb0c6a2432723450610c078ebcf04c..0768313eb11f74456e1c1f35849add027585be02 100644 (file)
@@ -1,4 +1,3 @@
-using System.Threading;
 using Content.Server.Body.Components;
 using Content.Server.Body.Systems;
 using Content.Server.Chemistry.EntitySystems;
@@ -8,8 +7,11 @@ using Content.Server.Nutrition.Components;
 using Content.Server.Popups;
 using Content.Shared.Administration.Logs;
 using Content.Shared.Body.Components;
+using Content.Shared.Chemistry;
+using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.Database;
+using Content.Shared.DoAfter;
 using Content.Shared.FixedPoint;
 using Content.Shared.Hands.EntitySystems;
 using Content.Shared.IdentityManagement;
@@ -42,6 +44,8 @@ namespace Content.Server.Nutrition.EntitySystems
         [Dependency] private readonly InventorySystem _inventorySystem = default!;
         [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
         [Dependency] private readonly SharedHandsSystem _handsSystem = default!;
+        [Dependency] private readonly ReactiveSystem _reaction = default!;
+        [Dependency] private readonly SharedAudioSystem _audio = default!;
 
         public override void Initialize()
         {
@@ -50,8 +54,7 @@ namespace Content.Server.Nutrition.EntitySystems
             SubscribeLocalEvent<FoodComponent, UseInHandEvent>(OnUseFoodInHand);
             SubscribeLocalEvent<FoodComponent, AfterInteractEvent>(OnFeedFood);
             SubscribeLocalEvent<FoodComponent, GetVerbsEvent<AlternativeVerb>>(AddEatVerb);
-            SubscribeLocalEvent<BodyComponent, FeedEvent>(OnFeed);
-            SubscribeLocalEvent<ForceFeedCancelledEvent>(OnFeedCancelled);
+            SubscribeLocalEvent<FoodComponent, DoAfterEvent<FoodData>>(OnDoAfter);
             SubscribeLocalEvent<InventoryComponent, IngestionAttemptEvent>(OnInventoryIngestAttempt);
         }
 
@@ -63,7 +66,7 @@ namespace Content.Server.Nutrition.EntitySystems
             if (ev.Handled)
                 return;
 
-            ev.Handled = TryFeed(ev.User, ev.User, foodComponent);
+            ev.Handled = TryFeed(ev.User, ev.User, uid, foodComponent);
         }
 
         /// <summary>
@@ -74,49 +77,41 @@ namespace Content.Server.Nutrition.EntitySystems
             if (args.Handled || args.Target == null || !args.CanReach)
                 return;
 
-            args.Handled = TryFeed(args.User, args.Target.Value, foodComponent);
+            args.Handled = TryFeed(args.User, args.Target.Value, uid, foodComponent);
         }
 
-        public bool TryFeed(EntityUid user, EntityUid target, FoodComponent food)
+        public bool TryFeed(EntityUid user, EntityUid target, EntityUid food, FoodComponent foodComp)
         {
-            // if currently being used to feed, cancel that action.
-            if (food.CancelToken != null)
-            {
-                return true;
-            }
-
-            if (food.Owner == user || //Suppresses self-eating
-                EntityManager.TryGetComponent<MobStateComponent>(food.Owner, out var mobState) && _mobStateSystem.IsAlive(food.Owner, mobState)) // Suppresses eating alive mobs
+            //Suppresses self-eating
+            if (food == user || EntityManager.TryGetComponent<MobStateComponent>(food, out var mobState) && _mobStateSystem.IsAlive(food, mobState)) // Suppresses eating alive mobs
                 return false;
 
             // Target can't be fed
             if (!EntityManager.HasComponent<BodyComponent>(target))
                 return false;
 
-            if (!_solutionContainerSystem.TryGetSolution(food.Owner, food.SolutionName, out var foodSolution))
+            if (!_solutionContainerSystem.TryGetSolution(food, foodComp.SolutionName, out var foodSolution))
                 return false;
 
-            var flavors = _flavorProfileSystem.GetLocalizedFlavorsMessage(food.Owner, user, foodSolution);
+            var flavors = _flavorProfileSystem.GetLocalizedFlavorsMessage(food, user, foodSolution);
 
-            if (food.UsesRemaining <= 0)
+            if (foodComp.UsesRemaining <= 0)
             {
-                _popupSystem.PopupEntity(Loc.GetString("food-system-try-use-food-is-empty",
-                    ("entity", food.Owner)), user, user);
-                DeleteAndSpawnTrash(food, user);
+                _popupSystem.PopupEntity(Loc.GetString("food-system-try-use-food-is-empty", ("entity", food)), user, user);
+                DeleteAndSpawnTrash(foodComp, food, user);
                 return false;
             }
 
             if (IsMouthBlocked(target, user))
                 return false;
 
-            if (!TryGetRequiredUtensils(user, food, out var utensils))
+            if (!TryGetRequiredUtensils(user, foodComp, out var utensils))
                 return false;
 
-            if (!_interactionSystem.InRangeUnobstructed(user, food.Owner, popup: true))
+            if (!_interactionSystem.InRangeUnobstructed(user, food, popup: true))
                 return true;
 
             var forceFeed = user != target;
-            food.CancelToken = new CancellationTokenSource();
 
             if (forceFeed)
             {
@@ -125,17 +120,19 @@ namespace Content.Server.Nutrition.EntitySystems
                     user, target);
 
                 // logging
-                _adminLogger.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(user):user} is forcing {ToPrettyString(target):target} to eat {ToPrettyString(food.Owner):food} {SolutionContainerSystem.ToPrettyString(foodSolution)}");
+                _adminLogger.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(user):user} is forcing {ToPrettyString(target):target} to eat {ToPrettyString(food):food} {SolutionContainerSystem.ToPrettyString(foodSolution)}");
             }
             else
             {
                 // log voluntary eating
-                _adminLogger.Add(LogType.Ingestion, LogImpact.Low, $"{ToPrettyString(target):target} is eating {ToPrettyString(food.Owner):food} {SolutionContainerSystem.ToPrettyString(foodSolution)}");
+                _adminLogger.Add(LogType.Ingestion, LogImpact.Low, $"{ToPrettyString(target):target} is eating {ToPrettyString(food):food} {SolutionContainerSystem.ToPrettyString(foodSolution)}");
             }
 
             var moveBreak = user != target;
 
-            _doAfterSystem.DoAfter(new DoAfterEventArgs(user, forceFeed ? food.ForceFeedDelay : food.Delay, food.CancelToken.Token, target, food.Owner)
+            var foodData = new FoodData(foodSolution, flavors, utensils);
+
+            var doAfterEventArgs = new DoAfterEventArgs(user, forceFeed ? foodComp.ForceFeedDelay : foodComp.Delay, target: target, used: food)
             {
                 BreakOnUserMove = moveBreak,
                 BreakOnDamage = true,
@@ -143,116 +140,114 @@ namespace Content.Server.Nutrition.EntitySystems
                 BreakOnTargetMove = moveBreak,
                 MovementThreshold = 0.01f,
                 DistanceThreshold = 1.0f,
-                TargetFinishedEvent = new FeedEvent(user, food, foodSolution, flavors, utensils),
-                BroadcastCancelledEvent = new ForceFeedCancelledEvent(food),
-                NeedHand = true,
-            });
+                NeedHand = true
+            };
+
+            _doAfterSystem.DoAfter(doAfterEventArgs, foodData);
 
             return true;
 
         }
 
-        private void OnFeed(EntityUid uid, BodyComponent body, FeedEvent args)
+        private void OnDoAfter(EntityUid uid, FoodComponent component, DoAfterEvent<FoodData> args)
         {
-            if (args.Food.Deleted)
+            if (args.Cancelled || args.Handled || component.Deleted || args.Args.Target == null)
                 return;
 
-            args.Food.CancelToken = null;
-
-            if (!_bodySystem.TryGetBodyOrganComponents<StomachComponent>(uid, out var stomachs, body))
+            if (!TryComp<BodyComponent>(args.Args.Target.Value, out var body))
                 return;
 
-            var transferAmount = args.Food.TransferAmount != null
-                ? FixedPoint2.Min((FixedPoint2) args.Food.TransferAmount, args.FoodSolution.Volume)
-                : args.FoodSolution.Volume;
-
-            var split = _solutionContainerSystem.SplitSolution((args.Food).Owner, args.FoodSolution, transferAmount);
+            if (!_bodySystem.TryGetBodyOrganComponents<StomachComponent>(args.Args.Target.Value, out var stomachs, body))
+                return;
 
+            var transferAmount = component.TransferAmount != null ? FixedPoint2.Min((FixedPoint2) component.TransferAmount, args.AdditionalData.FoodSolution.Volume) : args.AdditionalData.FoodSolution.Volume;
 
-            var firstStomach = stomachs.FirstOrNull(
-                stomach => _stomachSystem.CanTransferSolution((stomach.Comp).Owner, split));
+            var split = _solutionContainerSystem.SplitSolution(uid, args.AdditionalData.FoodSolution, transferAmount);
+            //TODO: Get the stomach UID somehow without nabbing owner
+            var firstStomach = stomachs.FirstOrNull(stomach => _stomachSystem.CanTransferSolution(stomach.Comp.Owner, split));
 
-            var forceFeed = uid != args.User;
+            var forceFeed = args.Args.Target.Value != args.Args.User;
 
             // No stomach so just popup a message that they can't eat.
             if (firstStomach == null)
             {
-                _solutionContainerSystem.TryAddSolution(uid, args.FoodSolution, split);
-                _popupSystem.PopupEntity(
-                    forceFeed ?
-                        Loc.GetString("food-system-you-cannot-eat-any-more-other") :
-                        Loc.GetString("food-system-you-cannot-eat-any-more")
-                    , uid, args.User);
+                _solutionContainerSystem.TryAddSolution(uid, args.AdditionalData.FoodSolution, split);
+                _popupSystem.PopupEntity(forceFeed ? Loc.GetString("food-system-you-cannot-eat-any-more-other") : Loc.GetString("food-system-you-cannot-eat-any-more"), args.Args.Target.Value, args.Args.User);
+                args.Handled = true;
                 return;
             }
 
-            split.DoEntityReaction(uid, ReactionMethod.Ingestion);
+            _reaction.DoEntityReaction(args.Args.Target.Value, args.AdditionalData.FoodSolution, ReactionMethod.Ingestion);
             _stomachSystem.TryTransferSolution(firstStomach.Value.Comp.Owner, split, firstStomach.Value.Comp);
 
-            var flavors = args.FlavorMessage;
+            var flavors = args.AdditionalData.FlavorMessage;
 
             if (forceFeed)
             {
-                var targetName = Identity.Entity(uid, EntityManager);
-                var userName = Identity.Entity(args.User, EntityManager);
+                var targetName = Identity.Entity(args.Args.Target.Value, EntityManager);
+                var userName = Identity.Entity(args.Args.User, EntityManager);
                 _popupSystem.PopupEntity(Loc.GetString("food-system-force-feed-success", ("user", userName), ("flavors", flavors)),
                     uid, uid);
 
-                _popupSystem.PopupEntity(Loc.GetString("food-system-force-feed-success-user", ("target", targetName)),
-                    args.User, args.User);
+                _popupSystem.PopupEntity(Loc.GetString("food-system-force-feed-success-user", ("target", targetName)), args.Args.User, args.Args.User);
 
                 // log successful force feed
-                _adminLogger.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(args.User):user} forced {ToPrettyString(uid):target} to eat {ToPrettyString(args.Food.Owner):food}");
+                _adminLogger.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(uid):user} forced {ToPrettyString(args.Args.User):target} to eat {ToPrettyString(uid):food}");
             }
             else
             {
-                _popupSystem.PopupEntity(Loc.GetString(args.Food.EatMessage, ("food", args.Food.Owner), ("flavors", flavors)), args.User, args.User);
+                _popupSystem.PopupEntity(Loc.GetString(component.EatMessage, ("foodComp", uid), ("flavors", flavors)), args.Args.User, args.Args.User);
 
                 // log successful voluntary eating
-                _adminLogger.Add(LogType.Ingestion, LogImpact.Low, $"{ToPrettyString(args.User):target} ate {ToPrettyString(args.Food.Owner):food}");
+                _adminLogger.Add(LogType.Ingestion, LogImpact.Low, $"{ToPrettyString(args.Args.User):target} ate {ToPrettyString(uid):food}");
             }
 
-            SoundSystem.Play(args.Food.UseSound.GetSound(), Filter.Pvs(uid), uid, AudioParams.Default.WithVolume(-1f));
+            _audio.Play(component.UseSound, Filter.Pvs(args.Args.Target.Value), args.Args.Target.Value, true, AudioParams.Default.WithVolume(-1f));
 
             // Try to break all used utensils
-            foreach (var utensil in args.Utensils)
+            //TODO: Replace utensil owner with actual UID
+            foreach (var utensil in args.AdditionalData.Utensils)
             {
-                _utensilSystem.TryBreak((utensil).Owner, args.User);
+                _utensilSystem.TryBreak(utensil.Owner, args.Args.User);
             }
 
-            if (args.Food.UsesRemaining > 0)
+            if (component.UsesRemaining > 0)
+            {
+                args.Handled = true;
                 return;
+            }
+
+
+            if (string.IsNullOrEmpty(component.TrashPrototype))
+                EntityManager.QueueDeleteEntity(uid);
 
-            if (string.IsNullOrEmpty(args.Food.TrashPrototype))
-                EntityManager.QueueDeleteEntity(args.Food.Owner);
             else
-                DeleteAndSpawnTrash(args.Food, args.User);
+                DeleteAndSpawnTrash(component, uid, args.Args.User);
+
+            args.Handled = true;
         }
 
-        private void DeleteAndSpawnTrash(FoodComponent component, EntityUid? user = null)
+        private void DeleteAndSpawnTrash(FoodComponent component, EntityUid food, EntityUid? user = null)
         {
             //We're empty. Become trash.
-            var position = Transform(component.Owner).MapPosition;
+            var position = Transform(food).MapPosition;
             var finisher = EntityManager.SpawnEntity(component.TrashPrototype, position);
 
             // If the user is holding the item
-            if (user != null && _handsSystem.IsHolding(user.Value, component.Owner, out var hand))
+            if (user != null && _handsSystem.IsHolding(user.Value, food, out var hand))
             {
-                EntityManager.DeleteEntity((component).Owner);
+                EntityManager.DeleteEntity(food);
 
                 // Put the trash in the user's hand
                 _handsSystem.TryPickup(user.Value, finisher, hand);
                 return;
             }
 
-            EntityManager.QueueDeleteEntity(component.Owner);
+            EntityManager.QueueDeleteEntity(food);
         }
 
         private void AddEatVerb(EntityUid uid, FoodComponent component, GetVerbsEvent<AlternativeVerb> ev)
         {
-            if (component.CancelToken != null)
-                return;
-
             if (uid == ev.User ||
                 !ev.CanInteract ||
                 !ev.CanAccess ||
@@ -267,7 +262,7 @@ namespace Content.Server.Nutrition.EntitySystems
             {
                 Act = () =>
                 {
-                    TryFeed(ev.User, ev.User, component);
+                    TryFeed(ev.User, ev.User, uid, component);
                 },
                 IconTexture = "/Textures/Interface/VerbIcons/cutlery.svg.192dpi.png",
                 Text = Loc.GetString("food-system-verb-eat"),
@@ -296,7 +291,7 @@ namespace Content.Server.Nutrition.EntitySystems
                 return;
 
             if (food.UsesRemaining <= 0)
-                DeleteAndSpawnTrash(food);
+                DeleteAndSpawnTrash(food, uid);
 
             var firstStomach = stomachs.FirstOrNull(
                 stomach => _stomachSystem.CanTransferSolution(((IComponent) stomach.Comp).Owner, foodSolution));
@@ -320,7 +315,7 @@ namespace Content.Server.Nutrition.EntitySystems
             if (string.IsNullOrEmpty(food.TrashPrototype))
                 EntityManager.QueueDeleteEntity(food.Owner);
             else
-                DeleteAndSpawnTrash(food);
+                DeleteAndSpawnTrash(food, uid);
         }
 
         private bool TryGetRequiredUtensils(EntityUid user, FoodComponent component,
@@ -361,11 +356,6 @@ namespace Content.Server.Nutrition.EntitySystems
             return true;
         }
 
-        private static void OnFeedCancelled(ForceFeedCancelledEvent args)
-        {
-            args.Food.CancelToken = null;
-        }
-
         /// <summary>
         ///     Block ingestion attempts based on the equipped mask or head-wear
         /// </summary>
@@ -415,5 +405,12 @@ namespace Content.Server.Nutrition.EntitySystems
 
             return attempt.Cancelled;
         }
+
+        private record struct FoodData(Solution FoodSolution, string FlavorMessage, List<UtensilComponent> Utensils)
+        {
+            public readonly Solution FoodSolution = FoodSolution;
+            public readonly string FlavorMessage = FlavorMessage;
+            public readonly List<UtensilComponent> Utensils = Utensils;
+        }
     }
 }
index 3a3b9918c4ff0829fd8d11338f7b9b39fcafce0c..4b2ab316a13d5f8b1ec7eed06737acf46a3f78cc 100644 (file)
@@ -44,14 +44,14 @@ namespace Content.Server.Nutrition.EntitySystems
             //Prevents food usage with a wrong utensil
             if ((food.Utensil & component.Types) == 0)
             {
-                _popupSystem.PopupEntity(Loc.GetString("food-system-wrong-utensil", ("food", food.Owner), ("utensil", component.Owner)), user, user);
+                _popupSystem.PopupEntity(Loc.GetString("food-system-wrong-utensil", ("food", target), ("utensil", component.Owner)), user, user);
                 return false;
             }
 
             if (!_interactionSystem.InRangeUnobstructed(user, target, popup: true))
                 return false;
 
-            return _foodSystem.TryFeed(user, user, food);
+            return _foodSystem.TryFeed(user, user, target, food);
         }
 
         /// <summary>
index 3c902a6819925fe47367c1e34aea639aab19ad57..c9668ad9db32f3e09f4f9ddc89b97ec13645b704 100644 (file)
@@ -1,6 +1,3 @@
-using Content.Server.Nutrition.Components;
-using Content.Shared.Chemistry.Components;
-
 namespace Content.Server.Nutrition;
 
 /// <summary>
@@ -13,69 +10,3 @@ public sealed class IngestionAttemptEvent : CancellableEntityEventArgs
     /// </summary>
     public EntityUid? Blocker = null;
 }
-
-/// <summary>
-///     Raised directed at the food after a successful feed do-after.
-/// </summary>
-public sealed class FeedEvent : EntityEventArgs
-{
-    public readonly EntityUid User;
-    public readonly FoodComponent Food;
-    public readonly Solution FoodSolution;
-    public readonly string FlavorMessage;
-    public readonly List<UtensilComponent> Utensils;
-
-    public FeedEvent(EntityUid user, FoodComponent food, Solution foodSolution, string flavorMessage, List<UtensilComponent> utensils)
-    {
-        User = user;
-        Food = food;
-        FoodSolution = foodSolution;
-        FlavorMessage = flavorMessage;
-        Utensils = utensils;
-    }
-}
-
-/// <summary>
-///     Raised directed at the food after a failed force-feed do-after.
-/// </summary>
-public sealed class ForceFeedCancelledEvent : EntityEventArgs
-{
-    public readonly FoodComponent Food;
-
-    public ForceFeedCancelledEvent(FoodComponent food)
-    {
-        Food = food;
-    }
-}
-
-/// <summary>
-///     Raised directed at the drink after a successful force-drink do-after.
-/// </summary>
-public sealed class DrinkEvent : EntityEventArgs
-{
-    public readonly EntityUid User;
-    public readonly DrinkComponent Drink;
-    public readonly Solution DrinkSolution;
-    public readonly string FlavorMessage;
-
-    public DrinkEvent(EntityUid user, DrinkComponent drink, Solution drinkSolution, string flavorMessage)
-    {
-        User = user;
-        Drink = drink;
-        DrinkSolution = drinkSolution;
-        FlavorMessage = flavorMessage;
-    }
-}
-
-/// <summary>
-///     Raised directed at the food after a failed force-dink do-after.
-/// </summary>
-public sealed class DrinkCancelledEvent : EntityEventArgs
-{
-    public readonly DrinkComponent Drink;
-
-    public DrinkCancelledEvent(DrinkComponent drink)
-    {
-        Drink = drink;
-    }
-}
index 45ad5a6aca9af2bc7a53a9b721c5373e853cf4f5..8ce9d26ec024085c93686eb58cb8c2fd647860bf 100644 (file)
@@ -1,7 +1,5 @@
 using Content.Server.Popups;
 using Content.Server.Power.Components;
-using Content.Server.Tools;
-using Content.Server.Wires;
 using Content.Shared.Access.Components;
 using Content.Shared.Access.Systems;
 using Content.Shared.APC;
@@ -10,8 +8,8 @@ using Content.Shared.Emag.Systems;
 using Content.Shared.Examine;
 using Content.Shared.Interaction;
 using Content.Shared.Popups;
+using Content.Shared.Tools;
 using Content.Shared.Tools.Components;
-using Content.Shared.Wires;
 using JetBrains.Annotations;
 using Robust.Server.GameObjects;
 using Robust.Shared.Audio;
@@ -27,7 +25,7 @@ namespace Content.Server.Power.EntitySystems
         [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
         [Dependency] private readonly PopupSystem _popupSystem = default!;
         [Dependency] private readonly IGameTiming _gameTiming = default!;
-        [Dependency] private readonly ToolSystem _toolSystem = default!;
+        [Dependency] private readonly SharedToolSystem _toolSystem = default!;
         [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
 
         private const float ScrewTime = 2f;
@@ -201,10 +199,11 @@ namespace Content.Server.Power.EntitySystems
         {
             if (!EntityManager.TryGetComponent(args.Used, out ToolComponent? tool))
                 return;
-            if (_toolSystem.UseTool(args.Used, args.User, uid, 0f, ScrewTime, new[] { "Screwing" }, doAfterCompleteEvent: new ApcToolFinishedEvent(uid), toolComponent: tool))
-            {
+
+            var toolEvData = new ToolEventData(new ApcToolFinishedEvent(uid), fuel: 0f);
+
+            if (_toolSystem.UseTool(args.Used, args.User, uid, ScrewTime, new [] { "Screwing" }, toolEvData, toolComponent:tool))
                 args.Handled = true;
-            }
         }
 
         private void OnToolFinished(ApcToolFinishedEvent args)
@@ -219,13 +218,9 @@ namespace Content.Server.Power.EntitySystems
             }
 
             if (component.IsApcOpen)
-            {
                 SoundSystem.Play(component.ScrewdriverOpenSound.GetSound(), Filter.Pvs(args.Target), args.Target);
-            }
             else
-            {
                 SoundSystem.Play(component.ScrewdriverCloseSound.GetSound(), Filter.Pvs(args.Target), args.Target);
-            }
         }
 
         private void UpdatePanelAppearance(EntityUid uid, AppearanceComponent? appearance = null, ApcComponent? apc = null)
index 4d795edc8a21a5edef65e7dd2d5bdfc45d64590a..eead07f34d54613709e5a9ee1f2c26df6c268017 100644 (file)
@@ -2,9 +2,10 @@ using Content.Server.Administration.Logs;
 using Content.Server.Electrocution;
 using Content.Server.Power.Components;
 using Content.Server.Stack;
-using Content.Server.Tools;
 using Content.Shared.Database;
 using Content.Shared.Interaction;
+using Content.Shared.Tools;
+using Content.Shared.Tools.Components;
 using Robust.Shared.Map;
 
 namespace Content.Server.Power.EntitySystems;
@@ -13,7 +14,7 @@ public sealed partial class CableSystem : EntitySystem
 {
     [Dependency] private readonly IMapManager _mapManager = default!;
     [Dependency] private readonly ITileDefinitionManager _tileManager = default!;
-    [Dependency] private readonly ToolSystem _toolSystem = default!;
+    [Dependency] private readonly SharedToolSystem _toolSystem = default!;
     [Dependency] private readonly StackSystem _stack = default!;
     [Dependency] private readonly ElectrocutionSystem _electrocutionSystem = default!;
     [Dependency] private readonly IAdminLogManager _adminLogs = default!;
@@ -35,8 +36,8 @@ public sealed partial class CableSystem : EntitySystem
         if (args.Handled)
             return;
 
-        var ev = new CuttingFinishedEvent(args.User);
-        args.Handled = _toolSystem.UseTool(args.Used, args.User, uid, 0, cable.CuttingDelay, new[] { cable.CuttingQuality }, doAfterCompleteEvent: ev, doAfterEventTarget: uid);
+        var toolEvData = new ToolEventData(new CuttingFinishedEvent(args.User), targetEntity: uid);
+        args.Handled = _toolSystem.UseTool(args.Used, args.User, uid, cable.CuttingDelay, new[] { cable.CuttingQuality }, toolEvData);
     }
 
     private void OnCableCut(EntityUid uid, CableComponent cable, CuttingFinishedEvent args)
index 9193b7650fb5885fdcf8091a8b42b772135784f9..38c4e0f36aef64e9fb5b9c92eed316ed82353510 100644 (file)
@@ -4,6 +4,7 @@ using Content.Server.DoAfter;
 using Content.Server.Popups;
 using Content.Server.RCD.Components;
 using Content.Shared.Database;
+using Content.Shared.DoAfter;
 using Content.Shared.Examine;
 using Content.Shared.Interaction;
 using Content.Shared.Interaction.Events;
index 948640f470254f03e4af2247bd4a4b382de26623..6013d2660120ed681e04b95520904747be68a604 100644 (file)
@@ -4,12 +4,14 @@ using Content.Shared.Damage;
 using Content.Shared.Database;
 using Content.Shared.Interaction;
 using Content.Shared.Popups;
+using Content.Shared.Tools;
+using Content.Shared.Tools.Components;
 
 namespace Content.Server.Repairable
 {
     public sealed class RepairableSystem : EntitySystem
     {
-        [Dependency] private readonly ToolSystem _toolSystem = default!;
+        [Dependency] private readonly SharedToolSystem _toolSystem = default!;
         [Dependency] private readonly DamageableSystem _damageableSystem = default!;
         [Dependency] private readonly IAdminLogManager _adminLogger= default!;
 
@@ -30,8 +32,10 @@ namespace Content.Server.Repairable
             if (args.User == args.Target)
                 delay *= component.SelfRepairPenalty;
 
+            var toolEvData = new ToolEventData(null);
+
             // Can the tool actually repair this, does it have enough fuel?
-            if (!await _toolSystem.UseTool(args.Used, args.User, uid, component.FuelCost, delay, component.QualityNeeded))
+            if (!_toolSystem.UseTool(args.Used, args.User, uid, delay, component.QualityNeeded, toolEvData, component.FuelCost))
                 return;
 
             if (component.Damage != null)
index a41786e9b6c7a352ee1da810caf2050aae665c3e..aff5a0b0e3896cb1efdd415b91601604d00de849 100644 (file)
@@ -1,5 +1,3 @@
-using System.Threading;
-
 namespace Content.Server.Resist;
 
 [RegisterComponent]
@@ -10,9 +8,4 @@ public sealed class CanEscapeInventoryComponent : Component
     /// </summary>
     [DataField("baseResistTime")]
     public float BaseResistTime = 5f;
-
-    /// <summary>
-    /// Cancellation token used to cancel the DoAfter if the mob is removed before it's complete
-    /// </summary>
-    public CancellationTokenSource? CancelToken;
 }
index 18546e07f0ea8c1cc8c7eddb72c811c7477ed3d5..2e3fc66ea87ad544a226db72c4356ea4e2b18d11 100644 (file)
@@ -2,11 +2,11 @@ using Content.Server.DoAfter;
 using Content.Server.Contests;
 using Robust.Shared.Containers;
 using Content.Server.Popups;
-using Robust.Shared.Player;
 using Content.Shared.Storage;
 using Content.Shared.Inventory;
 using Content.Shared.Hands.EntitySystems;
 using Content.Shared.ActionBlocker;
+using Content.Shared.DoAfter;
 using Content.Shared.Movement.Events;
 using Content.Shared.Interaction.Events;
 
@@ -31,16 +31,12 @@ public sealed class EscapeInventorySystem : EntitySystem
         base.Initialize();
 
         SubscribeLocalEvent<CanEscapeInventoryComponent, MoveInputEvent>(OnRelayMovement);
-        SubscribeLocalEvent<CanEscapeInventoryComponent, EscapeDoAfterComplete>(OnEscapeComplete);
-        SubscribeLocalEvent<CanEscapeInventoryComponent, EscapeDoAfterCancel>(OnEscapeFail);
+        SubscribeLocalEvent<CanEscapeInventoryComponent, DoAfterEvent<EscapeInventoryEvent>>(OnEscape);
         SubscribeLocalEvent<CanEscapeInventoryComponent, DroppedEvent>(OnDropped);
     }
 
     private void OnRelayMovement(EntityUid uid, CanEscapeInventoryComponent component, ref MoveInputEvent args)
     {
-        if (component.CancelToken != null)
-            return;
-
         if (!_containerSystem.TryGetContainingContainer(uid, out var container) || !_actionBlockerSystem.CanInteract(uid, container.Owner))
             return;
 
@@ -69,41 +65,38 @@ public sealed class EscapeInventorySystem : EntitySystem
 
     private void AttemptEscape(EntityUid user, EntityUid container, CanEscapeInventoryComponent component, float multiplier = 1f)
     {
-        component.CancelToken = new();
-        var doAfterEventArgs = new DoAfterEventArgs(user, component.BaseResistTime * multiplier, component.CancelToken.Token, container)
+        var escapeEvent = new EscapeInventoryEvent();
+        var doAfterEventArgs = new DoAfterEventArgs(user, component.BaseResistTime * multiplier, target:container)
         {
             BreakOnTargetMove = false,
             BreakOnUserMove = false,
             BreakOnDamage = true,
             BreakOnStun = true,
-            NeedHand = false,
-            UserFinishedEvent = new EscapeDoAfterComplete(),
-            UserCancelledEvent = new EscapeDoAfterCancel(),
+            NeedHand = false
         };
 
         _popupSystem.PopupEntity(Loc.GetString("escape-inventory-component-start-resisting"), user, user);
         _popupSystem.PopupEntity(Loc.GetString("escape-inventory-component-start-resisting-target"), container, container);
-        _doAfterSystem.DoAfter(doAfterEventArgs);
+        _doAfterSystem.DoAfter(doAfterEventArgs, escapeEvent);
     }
 
-    private void OnEscapeComplete(EntityUid uid, CanEscapeInventoryComponent component, EscapeDoAfterComplete ev)
+    private void OnEscape(EntityUid uid, CanEscapeInventoryComponent component, DoAfterEvent<EscapeInventoryEvent> args)
     {
-        //Drops the mob on the tile below the container
+        if (args.Handled || args.Cancelled)
+            return;
+
         Transform(uid).AttachParentToContainerOrGrid(EntityManager);
-        component.CancelToken = null;
-    }
 
-    private void OnEscapeFail(EntityUid uid, CanEscapeInventoryComponent component, EscapeDoAfterCancel ev)
-    {
-        component.CancelToken = null;
+        args.Handled = true;
     }
 
     private void OnDropped(EntityUid uid, CanEscapeInventoryComponent component, DroppedEvent args)
     {
-        component.CancelToken?.Cancel();
+        //TODO: Enter cancel logic here
     }
 
-    private sealed class EscapeDoAfterComplete : EntityEventArgs { }
+    private sealed class EscapeInventoryEvent : EntityEventArgs
+    {
 
-    private sealed class EscapeDoAfterCancel : EntityEventArgs { }
+    }
 }
index 7c3e7ff201fb355a7770832bdacbc4ffcc063067..6cf4102d4509ea2c0ce97f8bed4ec6fc05ad7c53 100644 (file)
@@ -19,7 +19,7 @@ public sealed class ResistLockerComponent : Component
     public bool IsResisting = false;
 
     /// <summary>
-    /// Cancellation token used to cancel the DoAfter if the container is opened before it's complete
+    /// Used to cancel the DoAfter when a locker is open
     /// </summary>
-    public CancellationTokenSource? CancelToken;
+    public Shared.DoAfter.DoAfter? DoAfter;
 }
index 7230b496dc1eec46f95947a538baa05fe376e6aa..85b4176d9cf5e242c469d46b4ddb7d473858e355 100644 (file)
@@ -1,11 +1,12 @@
-using Content.Server.Storage.Components;
 using Content.Server.DoAfter;
-using Robust.Shared.Containers;
 using Content.Server.Popups;
-using Content.Shared.Movement.Events;
+using Content.Server.Storage.Components;
 using Content.Server.Storage.EntitySystems;
+using Content.Shared.DoAfter;
 using Content.Shared.Lock;
+using Content.Shared.Movement.Events;
 using Content.Shared.Popups;
+using Robust.Shared.Containers;
 
 namespace Content.Server.Resist;
 
@@ -20,9 +21,8 @@ public sealed class ResistLockerSystem : EntitySystem
     {
         base.Initialize();
         SubscribeLocalEvent<ResistLockerComponent, ContainerRelayMovementEntityEvent>(OnRelayMovement);
-        SubscribeLocalEvent<ResistLockerComponent, ResistDoAfterComplete>(OnDoAfterComplete);
-        SubscribeLocalEvent<ResistLockerComponent, ResistDoAfterCancelled>(OnDoAfterCancelled);
-        SubscribeLocalEvent<ResistLockerComponent, EntRemovedFromContainerMessage>(OnRemovedFromContainer);
+        SubscribeLocalEvent<ResistLockerComponent, DoAfterEvent>(OnDoAfter);
+        SubscribeLocalEvent<ResistLockerComponent, EntRemovedFromContainerMessage>(OnRemoved);
     }
 
     private void OnRelayMovement(EntityUid uid, ResistLockerComponent component, ref ContainerRelayMovementEntityEvent args)
@@ -44,70 +44,51 @@ public sealed class ResistLockerSystem : EntitySystem
         if (!Resolve(target, ref storageComponent, ref resistLockerComponent))
             return;
 
-        resistLockerComponent.CancelToken = new();
-        var doAfterEventArgs = new DoAfterEventArgs(user, resistLockerComponent.ResistTime, resistLockerComponent.CancelToken.Token, target)
+        var doAfterEventArgs = new DoAfterEventArgs(user, resistLockerComponent.ResistTime, target:target)
         {
             BreakOnTargetMove = false,
             BreakOnUserMove = true,
             BreakOnDamage = true,
             BreakOnStun = true,
-            NeedHand = false, //No hands 'cause we be kickin'
-            TargetFinishedEvent = new ResistDoAfterComplete(user, target),
-            TargetCancelledEvent = new ResistDoAfterCancelled(user)
+            NeedHand = false //No hands 'cause we be kickin'
         };
 
         resistLockerComponent.IsResisting = true;
         _popupSystem.PopupEntity(Loc.GetString("resist-locker-component-start-resisting"), user, user, PopupType.Large);
-        _doAfterSystem.DoAfter(doAfterEventArgs);
+        resistLockerComponent.DoAfter = _doAfterSystem.DoAfter(doAfterEventArgs);
     }
 
-    private void OnDoAfterComplete(EntityUid uid, ResistLockerComponent component, ResistDoAfterComplete ev)
+    private void OnRemoved(EntityUid uid, ResistLockerComponent component, EntRemovedFromContainerMessage args)
     {
-        component.IsResisting = false;
+        if (component.DoAfter != null)
+            _doAfterSystem.Cancel(uid, component.DoAfter);
+    }
 
-        if (TryComp<EntityStorageComponent>(uid, out var storageComponent))
+    private void OnDoAfter(EntityUid uid, ResistLockerComponent component, DoAfterEvent args)
+    {
+        if (args.Cancelled)
         {
-            if (storageComponent.IsWeldedShut)
-                storageComponent.IsWeldedShut = false;
-
-            if (TryComp<LockComponent>(ev.Target, out var lockComponent))
-                _lockSystem.Unlock(uid, ev.User, lockComponent);
-
-            component.CancelToken = null;
-            _entityStorage.TryOpenStorage(ev.User, storageComponent.Owner);
+            component.IsResisting = false;
+            _popupSystem.PopupEntity(Loc.GetString("resist-locker-component-resist-interrupted"), args.Args.User, args.Args.User, PopupType.Medium);
+            return;
         }
-    }
 
-    private void OnDoAfterCancelled(EntityUid uid, ResistLockerComponent component, ResistDoAfterCancelled ev)
-    {
-        component.IsResisting = false;
-        component.CancelToken = null;
-        _popupSystem.PopupEntity(Loc.GetString("resist-locker-component-resist-interrupted"), ev.User, ev.User, PopupType.Medium);
-    }
+        if (args.Handled || args.Args.Target == null)
+            return;
 
-    private void OnRemovedFromContainer(EntityUid uid, ResistLockerComponent component, EntRemovedFromContainerMessage message)
-    {
-        component.CancelToken?.Cancel();
-    }
+        component.IsResisting = false;
 
-    private sealed class ResistDoAfterComplete : EntityEventArgs
-    {
-        public readonly EntityUid User;
-        public readonly EntityUid Target;
-        public ResistDoAfterComplete(EntityUid userUid, EntityUid target)
+        if (TryComp<EntityStorageComponent>(uid, out var storageComponent))
         {
-            User = userUid;
-            Target = target;
-        }
-    }
+            if (storageComponent.IsWeldedShut)
+                storageComponent.IsWeldedShut = false;
 
-    private sealed class ResistDoAfterCancelled : EntityEventArgs
-    {
-        public readonly EntityUid User;
+            if (TryComp<LockComponent>(args.Args.Target.Value, out var lockComponent))
+                _lockSystem.Unlock(uid, args.Args.User, lockComponent);
 
-        public ResistDoAfterCancelled(EntityUid userUid)
-        {
-            User = userUid;
+            _entityStorage.TryOpenStorage(args.Args.User, uid);
         }
+
+        args.Handled = true;
     }
 }
index ee5d603eddfcd0fb2c3926b8b9fd3f46c93d5834..7c3b1508e7288d82dcdb6927aaa2d32c66582d7f 100644 (file)
@@ -1,6 +1,5 @@
 using Content.Shared.Popups;
 using Content.Shared.Damage;
-using Content.Server.DoAfter;
 using Content.Shared.Revenant;
 using Robust.Shared.Random;
 using Robust.Shared.Map;
@@ -19,6 +18,7 @@ using Content.Shared.Bed.Sleep;
 using System.Linq;
 using Content.Server.Maps;
 using Content.Server.Revenant.Components;
+using Content.Shared.DoAfter;
 using Content.Shared.Emag.Systems;
 using Content.Shared.FixedPoint;
 using Content.Shared.Humanoid;
@@ -46,10 +46,8 @@ public sealed partial class RevenantSystem
     private void InitializeAbilities()
     {
         SubscribeLocalEvent<RevenantComponent, InteractNoHandEvent>(OnInteract);
-        SubscribeLocalEvent<RevenantComponent, SoulSearchDoAfterComplete>(OnSoulSearchComplete);
-        SubscribeLocalEvent<RevenantComponent, SoulSearchDoAfterCancelled>(OnSoulSearchCancelled);
-        SubscribeLocalEvent<RevenantComponent, HarvestDoAfterComplete>(OnHarvestComplete);
-        SubscribeLocalEvent<RevenantComponent, HarvestDoAfterCancelled>(OnHarvestCancelled);
+        SubscribeLocalEvent<RevenantComponent, DoAfterEvent<SoulEvent>>(OnSoulSearch);
+        SubscribeLocalEvent<RevenantComponent, DoAfterEvent<HarvestEvent>>(OnHarvest);
 
         SubscribeLocalEvent<RevenantComponent, RevenantDefileActionEvent>(OnDefileAction);
         SubscribeLocalEvent<RevenantComponent, RevenantOverloadLightsActionEvent>(OnOverloadLightsAction);
@@ -86,26 +84,23 @@ public sealed partial class RevenantSystem
 
     private void BeginSoulSearchDoAfter(EntityUid uid, EntityUid target, RevenantComponent revenant)
     {
-        if (revenant.SoulSearchCancelToken != null)
-            return;
-
         _popup.PopupEntity(Loc.GetString("revenant-soul-searching", ("target", target)), uid, uid, PopupType.Medium);
-        revenant.SoulSearchCancelToken = new();
-        var searchDoAfter = new DoAfterEventArgs(uid, revenant.SoulSearchDuration, revenant.SoulSearchCancelToken.Token, target)
+        var soulSearchEvent = new SoulEvent();
+        var searchDoAfter = new DoAfterEventArgs(uid, revenant.SoulSearchDuration, target:target)
         {
             BreakOnUserMove = true,
-            DistanceThreshold = 2,
-            UserFinishedEvent = new SoulSearchDoAfterComplete(target),
-            UserCancelledEvent = new SoulSearchDoAfterCancelled(),
+            DistanceThreshold = 2
         };
-        _doAfter.DoAfter(searchDoAfter);
+        _doAfter.DoAfter(searchDoAfter, soulSearchEvent);
     }
 
-    private void OnSoulSearchComplete(EntityUid uid, RevenantComponent component, SoulSearchDoAfterComplete args)
+    private void OnSoulSearch(EntityUid uid, RevenantComponent component, DoAfterEvent<SoulEvent> args)
     {
-        if (!TryComp<EssenceComponent>(args.Target, out var essence))
+        if (args.Handled || args.Cancelled)
+            return;
+
+        if (!TryComp<EssenceComponent>(args.Args.Target, out var essence))
             return;
-        component.SoulSearchCancelToken = null;
         essence.SearchComplete = true;
 
         string message;
@@ -121,19 +116,13 @@ public sealed partial class RevenantSystem
                 message = "revenant-soul-yield-average";
                 break;
         }
-        _popup.PopupEntity(Loc.GetString(message, ("target", args.Target)), args.Target, uid, PopupType.Medium);
-    }
+        _popup.PopupEntity(Loc.GetString(message, ("target", args.Args.Target)), args.Args.Target.Value, uid, PopupType.Medium);
 
-    private void OnSoulSearchCancelled(EntityUid uid, RevenantComponent component, SoulSearchDoAfterCancelled args)
-    {
-        component.SoulSearchCancelToken = null;
+        args.Handled = true;
     }
 
     private void BeginHarvestDoAfter(EntityUid uid, EntityUid target, RevenantComponent revenant, EssenceComponent essence)
     {
-        if (revenant.HarvestCancelToken != null)
-            return;
-
         if (essence.Harvested)
         {
             _popup.PopupEntity(Loc.GetString("revenant-soul-harvested"), target, uid, PopupType.SmallCaution);
@@ -146,14 +135,13 @@ public sealed partial class RevenantSystem
             return;
         }
 
-        revenant.HarvestCancelToken = new();
-        var doAfter = new DoAfterEventArgs(uid, revenant.HarvestDebuffs.X, revenant.HarvestCancelToken.Token, target)
+        var harvestEvent = new HarvestEvent();
+
+        var doAfter = new DoAfterEventArgs(uid, revenant.HarvestDebuffs.X, target:target)
         {
             DistanceThreshold = 2,
             BreakOnUserMove = true,
-            NeedHand = false,
-            UserFinishedEvent = new HarvestDoAfterComplete(target),
-            UserCancelledEvent = new HarvestDoAfterCancelled(),
+            NeedHand = false
         };
 
         _appearance.SetData(uid, RevenantVisuals.Harvesting, true);
@@ -162,29 +150,37 @@ public sealed partial class RevenantSystem
             target, PopupType.Large);
 
         TryUseAbility(uid, revenant, 0, revenant.HarvestDebuffs);
-        _doAfter.DoAfter(doAfter);
+        _doAfter.DoAfter(doAfter, harvestEvent);
     }
 
-    private void OnHarvestComplete(EntityUid uid, RevenantComponent component, HarvestDoAfterComplete args)
+    private void OnHarvest(EntityUid uid, RevenantComponent component, DoAfterEvent<HarvestEvent> args)
     {
-        component.HarvestCancelToken = null;
+        if (args.Cancelled)
+        {
+            _appearance.SetData(uid, RevenantVisuals.Harvesting, false);
+            return;
+        }
+
+        if (args.Handled || args.Args.Target == null)
+            return;
+
         _appearance.SetData(uid, RevenantVisuals.Harvesting, false);
 
-        if (!TryComp<EssenceComponent>(args.Target, out var essence))
+        if (!TryComp<EssenceComponent>(args.Args.Target, out var essence))
             return;
 
-        _popup.PopupEntity(Loc.GetString("revenant-soul-finish-harvest", ("target", args.Target)),
-            args.Target, PopupType.LargeCaution);
+        _popup.PopupEntity(Loc.GetString("revenant-soul-finish-harvest", ("target", args.Args.Target)),
+            args.Args.Target.Value, PopupType.LargeCaution);
 
         essence.Harvested = true;
         ChangeEssenceAmount(uid, essence.EssenceAmount, component);
         _store.TryAddCurrency(new Dictionary<string, FixedPoint2>
             { {component.StolenEssenceCurrencyPrototype, essence.EssenceAmount} }, uid);
 
-        if (!HasComp<MobStateComponent>(args.Target))
+        if (!HasComp<MobStateComponent>(args.Args.Target))
             return;
 
-        if (_mobState.IsAlive(args.Target) || _mobState.IsCritical(args.Target))
+        if (_mobState.IsAlive(args.Args.Target.Value) || _mobState.IsCritical(args.Args.Target.Value))
         {
             _popup.PopupEntity(Loc.GetString("revenant-max-essence-increased"), uid, uid);
             component.EssenceRegenCap += component.MaxEssenceUpgradeAmount;
@@ -192,17 +188,13 @@ public sealed partial class RevenantSystem
 
         //KILL THEMMMM
 
-        if (!_mobThresholdSystem.TryGetThresholdForState(args.Target, MobState.Dead, out var damage))
+        if (!_mobThresholdSystem.TryGetThresholdForState(args.Args.Target.Value, MobState.Dead, out var damage))
             return;
         DamageSpecifier dspec = new();
         dspec.DamageDict.Add("Poison", damage.Value);
-        _damage.TryChangeDamage(args.Target, dspec, true, origin: uid);
-    }
+        _damage.TryChangeDamage(args.Args.Target, dspec, true, origin: uid);
 
-    private void OnHarvestCancelled(EntityUid uid, RevenantComponent component, HarvestDoAfterCancelled args)
-    {
-        component.HarvestCancelToken = null;
-        _appearance.SetData(uid, RevenantVisuals.Harvesting, false);
+        args.Handled = true;
     }
 
     private void OnDefileAction(EntityUid uid, RevenantComponent component, RevenantDefileActionEvent args)
@@ -335,4 +327,14 @@ public sealed partial class RevenantSystem
             _emag.DoEmagEffect(ent, ent); //it emags itself. spooky.
         }
     }
+
+    private sealed class SoulEvent : EntityEventArgs
+    {
+
+    }
+
+    private sealed class HarvestEvent : EntityEventArgs
+    {
+
+    }
 }
index c0e916adc594a3606c87f3f664c057bdd1e64ff6..159022b1893f433fc0dd006d62e3ac4a3d472fe3 100644 (file)
@@ -80,4 +80,9 @@ public sealed class StickyComponent : Component
     /// </summary>
     [ViewVariables(VVAccess.ReadOnly)]
     public EntityUid? StuckTo;
+
+    /// <summary>
+    /// For the DoAfter event to tell if it should stick or unstick
+    /// </summary>
+    public bool Stick;
 }
index 3d9346beddc1758aa68316fd64cf136ef17811d3..693dff6a1b9a543848c67307f7ea92bba2b1b697 100644 (file)
@@ -2,6 +2,7 @@ using Content.Server.DoAfter;
 using Content.Server.Popups;
 using Content.Server.Sticky.Components;
 using Content.Server.Sticky.Events;
+using Content.Shared.DoAfter;
 using Content.Shared.Hands.EntitySystems;
 using Content.Shared.Interaction;
 using Content.Shared.Sticky.Components;
@@ -26,8 +27,7 @@ public sealed class StickySystem : EntitySystem
     public override void Initialize()
     {
         base.Initialize();
-        SubscribeLocalEvent<StickSuccessfulEvent>(OnStickSuccessful);
-        SubscribeLocalEvent<UnstickSuccessfulEvent>(OnUnstickSuccessful);
+        SubscribeLocalEvent<StickyComponent, DoAfterEvent>(OnStickSuccessful);
         SubscribeLocalEvent<StickyComponent, AfterInteractEvent>(OnAfterInteract);
         SubscribeLocalEvent<StickyComponent, GetVerbsEvent<Verb>>(AddUnstickVerb);
     }
@@ -84,10 +84,11 @@ public sealed class StickySystem : EntitySystem
                 _popupSystem.PopupEntity(msg, user, user);
             }
 
+            component.Stick = true;
+
             // start sticking object to target
-            _doAfterSystem.DoAfter(new DoAfterEventArgs(user, delay, target: target)
+            _doAfterSystem.DoAfter(new DoAfterEventArgs(user, delay, target: target, used: uid)
             {
-                BroadcastFinishedEvent = new StickSuccessfulEvent(uid, user, target),
                 BreakOnStun = true,
                 BreakOnTargetMove = true,
                 BreakOnUserMove = true,
@@ -103,13 +104,18 @@ public sealed class StickySystem : EntitySystem
         return true;
     }
 
-    private void OnStickSuccessful(StickSuccessfulEvent ev)
+    private void OnStickSuccessful(EntityUid uid, StickyComponent component, DoAfterEvent args)
     {
-        // check if entity still has sticky component
-        if (!TryComp(ev.Uid, out StickyComponent? component))
+        if (args.Handled || args.Cancelled || args.Args.Target == null)
             return;
 
-        StickToEntity(ev.Uid, ev.Target, ev.User, component);
+        if (component.Stick)
+            StickToEntity(uid, args.Args.Target.Value, args.Args.User, component);
+
+        else
+            UnstickFromEntity(uid, args.Args.User, component);
+
+        args.Handled = true;
     }
 
     private void StartUnsticking(EntityUid uid, EntityUid user, StickyComponent? component = null)
@@ -127,10 +133,11 @@ public sealed class StickySystem : EntitySystem
                 _popupSystem.PopupEntity(msg, user, user);
             }
 
+            component.Stick = false;
+
             // start unsticking object
             _doAfterSystem.DoAfter(new DoAfterEventArgs(user, delay, target: uid)
             {
-                BroadcastFinishedEvent = new UnstickSuccessfulEvent(uid, user),
                 BreakOnStun = true,
                 BreakOnTargetMove = true,
                 BreakOnUserMove = true,
@@ -144,15 +151,6 @@ public sealed class StickySystem : EntitySystem
         }
     }
 
-    private void OnUnstickSuccessful(UnstickSuccessfulEvent ev)
-    {
-        // check if entity still has sticky component
-        if (!TryComp(ev.Uid, out StickyComponent? component))
-            return;
-
-        UnstickFromEntity(ev.Uid, ev.User, component);
-    }
-
     public void StickToEntity(EntityUid uid, EntityUid target, EntityUid user, StickyComponent? component = null)
     {
         if (!Resolve(uid, ref component))
@@ -215,30 +213,4 @@ public sealed class StickySystem : EntitySystem
         component.StuckTo = null;
         RaiseLocalEvent(uid, new EntityUnstuckEvent(target, user), true);
     }
-
-    private sealed class StickSuccessfulEvent : EntityEventArgs
-    {
-        public readonly EntityUid Uid;
-        public readonly EntityUid User;
-        public readonly EntityUid Target;
-
-        public StickSuccessfulEvent(EntityUid uid, EntityUid user, EntityUid target)
-        {
-            Uid = uid;
-            User = user;
-            Target = target;
-        }
-    }
-
-    private sealed class UnstickSuccessfulEvent : EntityEventArgs
-    {
-        public readonly EntityUid Uid;
-        public readonly EntityUid User;
-
-        public UnstickSuccessfulEvent(EntityUid uid, EntityUid user)
-        {
-            Uid = uid;
-            User = user;
-        }
-    }
 }
index 233219faec101f0d4c7d42644f649f4ae1a03912..92fc18ef39f472867e98c4ac039a0fa1e992c66e 100644 (file)
@@ -1,6 +1,4 @@
-using System.Threading;
-
-namespace Content.Server.Storage.Components;
+namespace Content.Server.Storage.Components;
 
 [RegisterComponent]
 public sealed class BluespaceLockerComponent : Component
@@ -55,8 +53,6 @@ public sealed class BluespaceLockerComponent : Component
     [DataField("pickLinksFromNonBluespaceLockers"), ViewVariables(VVAccess.ReadWrite)]
     public bool PickLinksFromNonBluespaceLockers = true;
 
-    public CancellationTokenSource? CancelToken;
-
     /// <summary>
     /// Determines if links automatically added get the source locker set as a target
     /// </summary>
index a16603469d64dbd4282eaa91b301095650e4cce8..b0b276ea2c8b0071e51bbd3c47f22a534bb726ba 100644 (file)
@@ -30,11 +30,6 @@ namespace Content.Server.Storage.Components
         [DataField("areaInsert")]
         public bool AreaInsert = false;  // "Attacking" with the storage entity causes it to insert all nearby storables after a delay
 
-        /// <summary>
-        /// Token for interrupting area insert do after.
-        /// </summary>
-        public CancellationTokenSource? CancelToken;
-
         [DataField("areaInsertRadius")]
         public int AreaInsertRadius = 1;
 
index fe368eb1860f50e11dc847e8cd24bfbb7abf0c3f..8c2e06cf48a9709d79e288dcec0eb36da95b7cdf 100644 (file)
@@ -1,5 +1,4 @@
-using System.Linq;
-using System.Threading;
+using System.Linq;
 using Content.Server.DoAfter;
 using Content.Server.Explosion.EntitySystems;
 using Content.Server.Mind.Components;
@@ -9,6 +8,7 @@ using Content.Server.Storage.Components;
 using Content.Server.Tools.Systems;
 using Content.Shared.Access.Components;
 using Content.Shared.Coordinates;
+using Content.Shared.DoAfter;
 using Content.Shared.Lock;
 using Content.Shared.Storage.Components;
 using Robust.Shared.Random;
@@ -33,7 +33,7 @@ public sealed class BluespaceLockerSystem : EntitySystem
         SubscribeLocalEvent<BluespaceLockerComponent, ComponentStartup>(OnStartup);
         SubscribeLocalEvent<BluespaceLockerComponent, StorageBeforeOpenEvent>(PreOpen);
         SubscribeLocalEvent<BluespaceLockerComponent, StorageAfterCloseEvent>(PostClose);
-        SubscribeLocalEvent<BluespaceLockerComponent, BluespaceLockerTeleportDelayComplete>(OnBluespaceLockerTeleportDelayComplete);
+        SubscribeLocalEvent<BluespaceLockerComponent, DoAfterEvent>(OnDoAfter);
     }
 
     private void OnStartup(EntityUid uid, BluespaceLockerComponent component, ComponentStartup args)
@@ -67,13 +67,6 @@ public sealed class BluespaceLockerSystem : EntitySystem
         if (!Resolve(uid, ref entityStorageComponent))
             return;
 
-        if (component.CancelToken != null)
-        {
-            component.CancelToken.Cancel();
-            component.CancelToken = null;
-            return;
-        }
-
         if (!component.BehaviorProperties.ActOnOpen)
             return;
 
@@ -265,9 +258,14 @@ public sealed class BluespaceLockerSystem : EntitySystem
         PostClose(uid, component);
     }
 
-    private void OnBluespaceLockerTeleportDelayComplete(EntityUid uid, BluespaceLockerComponent component, BluespaceLockerTeleportDelayComplete args)
+    private void OnDoAfter(EntityUid uid, BluespaceLockerComponent component, DoAfterEvent args)
     {
+        if (args.Handled || args.Cancelled)
+            return;
+
         PostClose(uid, component, false);
+
+        args.Handled = true;
     }
 
     private void PostClose(EntityUid uid, BluespaceLockerComponent component, bool doDelay = true)
@@ -278,8 +276,6 @@ public sealed class BluespaceLockerSystem : EntitySystem
         if (!Resolve(uid, ref entityStorageComponent))
             return;
 
-        component.CancelToken?.Cancel();
-
         if (!component.BehaviorProperties.ActOnClose)
             return;
 
@@ -287,13 +283,8 @@ public sealed class BluespaceLockerSystem : EntitySystem
         if (doDelay && component.BehaviorProperties.Delay > 0)
         {
             EnsureComp<DoAfterComponent>(uid);
-            component.CancelToken = new CancellationTokenSource();
 
-            _doAfterSystem.DoAfter(
-                new DoAfterEventArgs(uid, component.BehaviorProperties.Delay, component.CancelToken.Token)
-                {
-                    UserFinishedEvent = new BluespaceLockerTeleportDelayComplete()
-                });
+            _doAfterSystem.DoAfter(new DoAfterEventArgs(uid, component.BehaviorProperties.Delay));
             return;
         }
 
@@ -400,8 +391,4 @@ public sealed class BluespaceLockerSystem : EntitySystem
                 break;
         }
     }
-
-    private sealed class BluespaceLockerTeleportDelayComplete : EntityEventArgs
-    {
-    }
 }
index 806900e7a04ef9bc3af63360d6dbd3f81a2a7106..6f61fdc6e8de74e142295c13d676f19c667b2af6 100644 (file)
@@ -6,7 +6,9 @@ using Content.Shared.Verbs;
 using Content.Server.Disposal.Unit.Components;
 using Content.Server.Disposal.Unit.EntitySystems;
 using Content.Server.DoAfter;
+using Content.Shared.DoAfter;
 using Content.Shared.Placeable;
+using Content.Shared.Storage;
 using Robust.Shared.Containers;
 using Robust.Shared.Random;
 
@@ -17,6 +19,7 @@ namespace Content.Server.Storage.EntitySystems
         [Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
         [Dependency] private readonly DisposalUnitSystem _disposalUnitSystem = default!;
         [Dependency] private readonly IRobustRandom _random = default!;
+        [Dependency] private readonly SharedContainerSystem _container = default!;
 
         public override void Initialize()
         {
@@ -24,8 +27,7 @@ namespace Content.Server.Storage.EntitySystems
             SubscribeLocalEvent<DumpableComponent, AfterInteractEvent>(OnAfterInteract, after: new[]{ typeof(StorageSystem) });
             SubscribeLocalEvent<DumpableComponent, GetVerbsEvent<AlternativeVerb>>(AddDumpVerb);
             SubscribeLocalEvent<DumpableComponent, GetVerbsEvent<UtilityVerb>>(AddUtilityVerbs);
-            SubscribeLocalEvent<DumpCompletedEvent>(OnDumpCompleted);
-            SubscribeLocalEvent<DumpCancelledEvent>(OnDumpCancelled);
+            SubscribeLocalEvent<DumpableComponent, DoAfterEvent>(OnDoAfter);
         }
 
         private void OnAfterInteract(EntityUid uid, DumpableComponent component, AfterInteractEvent args)
@@ -33,16 +35,8 @@ namespace Content.Server.Storage.EntitySystems
             if (!args.CanReach)
                 return;
 
-            if (!TryComp<ServerStorageComponent>(args.Used, out var storage))
-                return;
-
-            if (storage.StoredEntities == null || storage.StoredEntities.Count == 0 || storage.CancelToken != null)
-                return;
-
             if (HasComp<DisposalUnitComponent>(args.Target) || HasComp<PlaceableSurfaceComponent>(args.Target))
-            {
-                StartDoAfter(uid, args.Target.Value, args.User, component, storage);
-            }
+                StartDoAfter(uid, args.Target.Value, args.User, component);
         }
 
         private void AddDumpVerb(EntityUid uid, DumpableComponent dumpable, GetVerbsEvent<AlternativeVerb> args)
@@ -57,7 +51,7 @@ namespace Content.Server.Storage.EntitySystems
             {
                 Act = () =>
                 {
-                    StartDoAfter(uid, null, args.User, dumpable, storage, 0.6f);
+                    StartDoAfter(uid, null, args.User, dumpable);//Had multiplier of 0.6f
                 },
                 Text = Loc.GetString("dump-verb-name"),
                 IconTexture = "/Textures/Interface/VerbIcons/drop.svg.192dpi.png",
@@ -79,7 +73,7 @@ namespace Content.Server.Storage.EntitySystems
                 {
                     Act = () =>
                     {
-                        StartDoAfter(uid, args.Target, args.User, dumpable, storage);
+                        StartDoAfter(uid, args.Target, args.User, dumpable);
                     },
                     Text = Loc.GetString("dump-disposal-verb-name", ("unit", args.Target)),
                     IconEntity = uid
@@ -93,7 +87,7 @@ namespace Content.Server.Storage.EntitySystems
                 {
                     Act = () =>
                     {
-                        StartDoAfter(uid, args.Target, args.User, dumpable, storage);
+                        StartDoAfter(uid, args.Target, args.User, dumpable);
                     },
                     Text = Loc.GetString("dump-placeable-verb-name", ("surface", args.Target)),
                     IconEntity = uid
@@ -102,21 +96,15 @@ namespace Content.Server.Storage.EntitySystems
             }
         }
 
-        public void StartDoAfter(EntityUid storageUid, EntityUid? targetUid, EntityUid userUid, DumpableComponent dumpable, ServerStorageComponent storage, float multiplier = 1)
+        public void StartDoAfter(EntityUid storageUid, EntityUid? targetUid, EntityUid userUid, DumpableComponent dumpable)
         {
-            if (dumpable.CancelToken != null)
-                return;
-
-            if (storage.StoredEntities == null)
+            if (!TryComp<SharedStorageComponent>(storageUid, out var storage) || storage.StoredEntities == null)
                 return;
 
-            float delay = storage.StoredEntities.Count * (float) dumpable.DelayPerItem.TotalSeconds * multiplier;
+            float delay = storage.StoredEntities.Count * (float) dumpable.DelayPerItem.TotalSeconds * dumpable.Multiplier;
 
-            dumpable.CancelToken = new CancellationTokenSource();
-            _doAfterSystem.DoAfter(new DoAfterEventArgs(userUid, delay, dumpable.CancelToken.Token, target: targetUid)
+            _doAfterSystem.DoAfter(new DoAfterEventArgs(userUid, delay, target: targetUid, used: storageUid)
             {
-                BroadcastFinishedEvent = new DumpCompletedEvent(dumpable, userUid, targetUid, storage.StoredEntities),
-                BroadcastCancelledEvent = new DumpCancelledEvent(dumpable.Owner),
                 BreakOnTargetMove = true,
                 BreakOnUserMove = true,
                 BreakOnStun = true,
@@ -124,71 +112,43 @@ namespace Content.Server.Storage.EntitySystems
             });
         }
 
-
-        private void OnDumpCompleted(DumpCompletedEvent args)
+        private void OnDoAfter(EntityUid uid, DumpableComponent component, DoAfterEvent args)
         {
-            args.Component.CancelToken = null;
+            if (args.Handled || args.Cancelled || !TryComp<SharedStorageComponent>(uid, out var storage) || storage.StoredEntities == null)
+                return;
 
             Queue<EntityUid> dumpQueue = new();
-            foreach (var entity in args.StoredEntities)
+            foreach (var entity in storage.StoredEntities)
             {
                 dumpQueue.Enqueue(entity);
             }
 
-            if (TryComp<DisposalUnitComponent>(args.Target, out var disposal))
-            {
-                foreach (var entity in dumpQueue)
-                {
-                    _disposalUnitSystem.DoInsertDisposalUnit(args.Target.Value, entity, args.User);
-                }
-                return;
-            }
-
             foreach (var entity in dumpQueue)
             {
                 var transform = Transform(entity);
-                transform.AttachParentToContainerOrGrid(EntityManager);
-                transform.LocalPosition = transform.LocalPosition + _random.NextVector2Box() / 2;
+                _container.AttachParentToContainerOrGrid(transform);
+                transform.LocalPosition += _random.NextVector2Box() / 2;
                 transform.LocalRotation = _random.NextAngle();
             }
 
-            if (HasComp<PlaceableSurfaceComponent>(args.Target))
+            if (args.Args.Target == null)
+                return;
+
+            if (HasComp<DisposalUnitComponent>(args.Args.Target.Value))
             {
                 foreach (var entity in dumpQueue)
                 {
-                    Transform(entity).LocalPosition = Transform(args.Target.Value).LocalPosition + _random.NextVector2Box() / 4;
+                    _disposalUnitSystem.DoInsertDisposalUnit(args.Args.Target.Value, entity, args.Args.User);
                 }
+                return;
             }
-        }
-
-        private void OnDumpCancelled(DumpCancelledEvent args)
-        {
-            if (TryComp<DumpableComponent>(args.Uid, out var dumpable))
-                dumpable.CancelToken = null;
-        }
-
-        private sealed class DumpCancelledEvent : EntityEventArgs
-        {
-            public readonly EntityUid Uid;
-            public DumpCancelledEvent(EntityUid uid)
-            {
-                Uid = uid;
-            }
-        }
-
-        private sealed class DumpCompletedEvent : EntityEventArgs
-        {
-            public DumpableComponent Component { get; }
-            public EntityUid User { get; }
-            public EntityUid? Target { get; }
-            public IReadOnlyList<EntityUid> StoredEntities { get; }
 
-            public DumpCompletedEvent(DumpableComponent component, EntityUid user, EntityUid? target, IReadOnlyList<EntityUid> storedEntities)
+            if (HasComp<PlaceableSurfaceComponent>(args.Args.Target.Value))
             {
-                Component = component;
-                User = user;
-                Target = target;
-                StoredEntities = storedEntities;
+                foreach (var entity in dumpQueue)
+                {
+                    Transform(entity).LocalPosition = Transform(args.Args.Target.Value).LocalPosition + _random.NextVector2Box() / 4;
+                }
             }
         }
     }
index 7cc632247ac31d7ab569f8ccbe6ff83ab2ff2d3b..568f5d0a5f0b0e204ab493faf5128183b4c5b111 100644 (file)
@@ -2,7 +2,6 @@ using System.Linq;
 using Content.Server.Hands.Components;
 using Content.Server.Storage.Components;
 using Content.Shared.Interaction;
-using Content.Shared.Movement;
 using Content.Shared.Storage;
 using Content.Shared.Verbs;
 using JetBrains.Annotations;
@@ -30,6 +29,7 @@ using static Content.Shared.Storage.SharedStorageComponent;
 using Content.Shared.ActionBlocker;
 using Content.Shared.CombatMode;
 using Content.Shared.Containers.ItemSlots;
+using Content.Shared.DoAfter;
 using Content.Shared.Implants.Components;
 using Content.Shared.Lock;
 using Content.Shared.Movement.Events;
@@ -75,8 +75,7 @@ namespace Content.Server.Storage.EntitySystems
             SubscribeLocalEvent<ServerStorageComponent, BoundUIClosedEvent>(OnBoundUIClosed);
             SubscribeLocalEvent<ServerStorageComponent, EntRemovedFromContainerMessage>(OnStorageItemRemoved);
 
-            SubscribeLocalEvent<ServerStorageComponent, AreaPickupCompleteEvent>(OnAreaPickupComplete);
-            SubscribeLocalEvent<ServerStorageComponent, AreaPickupCancelledEvent>(OnAreaPickupCancelled);
+            SubscribeLocalEvent<ServerStorageComponent, DoAfterEvent<StorageData>>(OnDoAfter);
 
             SubscribeLocalEvent<EntityStorageComponent, GetVerbsEvent<InteractionVerb>>(AddToggleOpenVerb);
             SubscribeLocalEvent<EntityStorageComponent, ContainerRelayMovementEntityEvent>(OnRelayMovement);
@@ -84,55 +83,6 @@ namespace Content.Server.Storage.EntitySystems
             SubscribeLocalEvent<StorageFillComponent, MapInitEvent>(OnStorageFillMapInit);
         }
 
-        private void OnAreaPickupCancelled(EntityUid uid, ServerStorageComponent component, AreaPickupCancelledEvent args)
-        {
-            component.CancelToken = null;
-        }
-
-        private void OnAreaPickupComplete(EntityUid uid, ServerStorageComponent component, AreaPickupCompleteEvent args)
-        {
-            component.CancelToken = null;
-            var successfullyInserted = new List<EntityUid>();
-            var successfullyInsertedPositions = new List<EntityCoordinates>();
-            var itemQuery = GetEntityQuery<ItemComponent>();
-            var xformQuery = GetEntityQuery<TransformComponent>();
-            xformQuery.TryGetComponent(uid, out var xform);
-
-            foreach (var entity in args.ValidStorables)
-            {
-                // Check again, situation may have changed for some entities, but we'll still pick up any that are valid
-                if (_containerSystem.IsEntityInContainer(entity)
-                    || entity == args.User
-                    || !itemQuery.HasComponent(entity))
-                    continue;
-
-                if (xform == null ||
-                    !xformQuery.TryGetComponent(entity, out var targetXform) ||
-                    targetXform.MapID != xform.MapID)
-                {
-                    continue;
-                }
-
-                var position = EntityCoordinates.FromMap(
-                    xform.ParentUid.IsValid() ? xform.ParentUid : uid,
-                    new MapCoordinates(_transform.GetWorldPosition(targetXform, xformQuery),
-                        targetXform.MapID), EntityManager);
-
-                if (PlayerInsertEntityInWorld(uid, args.User, entity, component))
-                {
-                    successfullyInserted.Add(entity);
-                    successfullyInsertedPositions.Add(position);
-                }
-            }
-
-            // If we picked up atleast one thing, play a sound and do a cool animation!
-            if (successfullyInserted.Count > 0)
-            {
-                _audio.PlayPvs(component.StorageInsertSound, uid);
-                RaiseNetworkEvent(new AnimateInsertingEntitiesEvent(uid, successfullyInserted, successfullyInsertedPositions));
-            }
-        }
-
         private void OnComponentInit(EntityUid uid, ServerStorageComponent storageComp, ComponentInit args)
         {
             base.Initialize();
@@ -147,10 +97,7 @@ namespace Content.Server.Storage.EntitySystems
 
         private void OnRelayMovement(EntityUid uid, EntityStorageComponent component, ref ContainerRelayMovementEntityEvent args)
         {
-            if (!EntityManager.HasComponent<HandsComponent>(args.Entity))
-                return;
-
-            if (_gameTiming.CurTime < component.LastInternalOpenAttempt + EntityStorageComponent.InternalOpenAttemptDelay)
+            if (!EntityManager.HasComponent<HandsComponent>(args.Entity) || _gameTiming.CurTime < component.LastInternalOpenAttempt + EntityStorageComponent.InternalOpenAttemptDelay)
                 return;
 
             component.LastInternalOpenAttempt = _gameTiming.CurTime;
@@ -163,10 +110,7 @@ namespace Content.Server.Storage.EntitySystems
 
         private void AddToggleOpenVerb(EntityUid uid, EntityStorageComponent component, GetVerbsEvent<InteractionVerb> args)
         {
-            if (!args.CanAccess || !args.CanInteract)
-                return;
-
-            if (!_entityStorage.CanOpen(args.User, args.Target, silent: true, component))
+            if (!args.CanAccess || !args.CanInteract || !_entityStorage.CanOpen(args.User, args.Target, silent: true, component))
                 return;
 
             InteractionVerb verb = new();
@@ -186,10 +130,7 @@ namespace Content.Server.Storage.EntitySystems
 
         private void AddOpenUiVerb(EntityUid uid, ServerStorageComponent component, GetVerbsEvent<ActivationVerb> args)
         {
-            if (!args.CanAccess || !args.CanInteract)
-                return;
-
-            if (TryComp<LockComponent>(uid, out var lockComponent) && lockComponent.Locked)
+            if (!args.CanAccess || !args.CanInteract || TryComp<LockComponent>(uid, out var lockComponent) && lockComponent.Locked)
                 return;
 
             // Get the session for the user
@@ -222,10 +163,7 @@ namespace Content.Server.Storage.EntitySystems
                 return;
 
             var entities = component.Storage?.ContainedEntities;
-            if (entities == null || entities.Count == 0)
-                return;
-
-            if (TryComp(uid, out LockComponent? lockComponent) && lockComponent.Locked)
+            if (entities == null || entities.Count == 0 || TryComp(uid, out LockComponent? lockComponent) && lockComponent.Locked)
                 return;
 
             // if the target is storage, add a verb to transfer storage.
@@ -249,13 +187,7 @@ namespace Content.Server.Storage.EntitySystems
         /// <returns>true if inserted, false otherwise</returns>
         private void OnInteractUsing(EntityUid uid, ServerStorageComponent storageComp, InteractUsingEvent args)
         {
-            if (args.Handled)
-                return;
-
-            if (!storageComp.ClickInsert)
-                return;
-
-            if (TryComp(uid, out LockComponent? lockComponent) && lockComponent.Locked)
+            if (args.Handled || !storageComp.ClickInsert || TryComp(uid, out LockComponent? lockComponent) && lockComponent.Locked)
                 return;
 
             Logger.DebugS(storageComp.LoggerName, $"Storage (UID {uid}) attacked by user (UID {args.User}) with entity (UID {args.Used}).");
@@ -273,10 +205,7 @@ namespace Content.Server.Storage.EntitySystems
         /// <returns></returns>
         private void OnActivate(EntityUid uid, ServerStorageComponent storageComp, ActivateInWorldEvent args)
         {
-            if (args.Handled || _combatMode.IsInCombatMode(args.User))
-                return;
-
-            if (TryComp(uid, out LockComponent? lockComponent) && lockComponent.Locked)
+            if (args.Handled || _combatMode.IsInCombatMode(args.User) || TryComp(uid, out LockComponent? lockComponent) && lockComponent.Locked)
                 return;
 
             OpenStorageUI(uid, args.User, storageComp);
@@ -300,12 +229,8 @@ namespace Content.Server.Storage.EntitySystems
         /// <returns></returns>
         private async void AfterInteract(EntityUid uid, ServerStorageComponent storageComp, AfterInteractEvent args)
         {
-            if (!args.CanReach) return;
-
-            if (storageComp.CancelToken != null)
-            {
+            if (!args.CanReach)
                 return;
-            }
 
             // Pick up all entities in a radius around the clicked location.
             // The last half of the if is because carpets exist and this is terrible
@@ -328,18 +253,16 @@ namespace Content.Server.Storage.EntitySystems
                 //If there's only one then let's be generous
                 if (validStorables.Count > 1)
                 {
-                    storageComp.CancelToken = new CancellationTokenSource();
-                    var doAfterArgs = new DoAfterEventArgs(args.User, 0.2f * validStorables.Count, storageComp.CancelToken.Token, target: uid)
+                    var storageData = new StorageData(validStorables);
+                    var doAfterArgs = new DoAfterEventArgs(args.User, 0.2f * validStorables.Count, target: uid)
                     {
                         BreakOnStun = true,
                         BreakOnDamage = true,
                         BreakOnUserMove = true,
-                        NeedHand = true,
-                        TargetCancelledEvent = new AreaPickupCancelledEvent(),
-                        TargetFinishedEvent = new AreaPickupCompleteEvent(args.User, validStorables),
+                        NeedHand = true
                     };
 
-                    _doAfterSystem.DoAfter(doAfterArgs);
+                    _doAfterSystem.DoAfter(doAfterArgs, storageData);
                 }
 
                 return;
@@ -374,6 +297,54 @@ namespace Content.Server.Storage.EntitySystems
             }
         }
 
+        private void OnDoAfter(EntityUid uid, ServerStorageComponent component, DoAfterEvent<StorageData> args)
+        {
+            if (args.Handled || args.Cancelled)
+                return;
+
+            var successfullyInserted = new List<EntityUid>();
+            var successfullyInsertedPositions = new List<EntityCoordinates>();
+            var itemQuery = GetEntityQuery<ItemComponent>();
+            var xformQuery = GetEntityQuery<TransformComponent>();
+            xformQuery.TryGetComponent(uid, out var xform);
+
+            foreach (var entity in args.AdditionalData.ValidStorables)
+            {
+                // Check again, situation may have changed for some entities, but we'll still pick up any that are valid
+                if (_containerSystem.IsEntityInContainer(entity)
+                    || entity == args.Args.User
+                    || !itemQuery.HasComponent(entity))
+                    continue;
+
+                if (xform == null ||
+                    !xformQuery.TryGetComponent(entity, out var targetXform) ||
+                    targetXform.MapID != xform.MapID)
+                {
+                    continue;
+                }
+
+                var position = EntityCoordinates.FromMap(
+                    xform.ParentUid.IsValid() ? xform.ParentUid : uid,
+                    new MapCoordinates(_transform.GetWorldPosition(targetXform, xformQuery),
+                        targetXform.MapID), EntityManager);
+
+                if (PlayerInsertEntityInWorld(uid, args.Args.User, entity, component))
+                {
+                    successfullyInserted.Add(entity);
+                    successfullyInsertedPositions.Add(position);
+                }
+            }
+
+            // If we picked up atleast one thing, play a sound and do a cool animation!
+            if (successfullyInserted.Count > 0)
+            {
+                _audio.PlayPvs(component.StorageInsertSound, uid);
+                RaiseNetworkEvent(new AnimateInsertingEntitiesEvent(uid, successfullyInserted, successfullyInsertedPositions));
+            }
+
+            args.Handled = true;
+        }
+
         private void OnDestroy(EntityUid uid, ServerStorageComponent storageComp, DestructionEventArgs args)
         {
             var storedEntities = storageComp.StoredEntities?.ToList();
@@ -404,10 +375,7 @@ namespace Content.Server.Storage.EntitySystems
                 return;
             }
 
-            if (!_actionBlockerSystem.CanInteract(player, args.InteractedItemUID))
-                return;
-
-            if (storageComp.Storage == null || !storageComp.Storage.Contains(args.InteractedItemUID))
+            if (!_actionBlockerSystem.CanInteract(player, args.InteractedItemUID) || storageComp.Storage == null || !storageComp.Storage.Contains(args.InteractedItemUID))
                 return;
 
             // Does the player have hands?
@@ -419,7 +387,7 @@ namespace Content.Server.Storage.EntitySystems
             {
                 if (_sharedHandsSystem.TryPickupAnyHand(player, args.InteractedItemUID, handsComp: hands)
                     && storageComp.StorageRemoveSound != null)
-                        SoundSystem.Play(storageComp.StorageRemoveSound.GetSound(), Filter.Pvs(uid, entityManager: EntityManager), uid, AudioParams.Default);
+                    _audio.Play(storageComp.StorageRemoveSound, Filter.Pvs(uid, entityManager: EntityManager), uid, true, AudioParams.Default);
                 return;
             }
 
@@ -457,7 +425,7 @@ namespace Content.Server.Storage.EntitySystems
                 UpdateStorageVisualization(uid, storageComp);
 
                 if (storageComp.StorageCloseSound is not null)
-                    SoundSystem.Play(storageComp.StorageCloseSound.GetSound(), Filter.Pvs(uid, entityManager: EntityManager), uid, storageComp.StorageCloseSound.Params);
+                    _audio.Play(storageComp.StorageCloseSound, Filter.Pvs(uid, entityManager: EntityManager), uid, true, storageComp.StorageCloseSound.Params);
             }
         }
 
@@ -473,10 +441,10 @@ namespace Content.Server.Storage.EntitySystems
                 return;
 
             _appearance.SetData(uid, StorageVisuals.Open, storageComp.IsOpen, appearance);
-            _appearance.SetData(uid, SharedBagOpenVisuals.BagState, storageComp.IsOpen ? SharedBagState.Open : SharedBagState.Closed, appearance);
+            _appearance.SetData(uid, SharedBagOpenVisuals.BagState, storageComp.IsOpen ? SharedBagState.Open : SharedBagState.Closed);
 
             if (HasComp<ItemCounterComponent>(uid))
-                _appearance.SetData(uid, StackVisuals.Hide, !storageComp.IsOpen, appearance);
+                _appearance.SetData(uid, StackVisuals.Hide, !storageComp.IsOpen);
         }
 
         private void RecalculateStorageUsed(ServerStorageComponent storageComp)
@@ -581,16 +549,11 @@ namespace Content.Server.Storage.EntitySystems
         /// <returns>true if the entity was inserted, false otherwise</returns>
         public bool Insert(EntityUid uid, EntityUid insertEnt, ServerStorageComponent? storageComp = null, bool playSound = true)
         {
-            if (!Resolve(uid, ref storageComp))
-                return false;
-
-            if (!CanInsert(uid, insertEnt, out _, storageComp) || storageComp.Storage?.Insert(insertEnt) == false)
+            if (!Resolve(uid, ref storageComp) || !CanInsert(uid, insertEnt, out _, storageComp) || storageComp.Storage?.Insert(insertEnt) == false)
                 return false;
 
             if (playSound && storageComp.StorageInsertSound is not null)
-            {
                 _audio.PlayPvs(storageComp.StorageInsertSound, uid);
-            }
 
             RecalculateStorageUsed(storageComp);
             UpdateStorageUI(uid, storageComp);
@@ -617,11 +580,7 @@ namespace Content.Server.Storage.EntitySystems
         /// <returns>true if inserted, false otherwise</returns>
         public bool PlayerInsertHeldEntity(EntityUid uid, EntityUid player, ServerStorageComponent? storageComp = null)
         {
-            if (!Resolve(uid, ref storageComp))
-                return false;
-
-            if (!TryComp(player, out HandsComponent? hands) ||
-                hands.ActiveHandEntity == null)
+            if (!Resolve(uid, ref storageComp) || !TryComp(player, out HandsComponent? hands) || hands.ActiveHandEntity == null)
                 return false;
 
             var toInsert = hands.ActiveHandEntity;
@@ -643,10 +602,7 @@ namespace Content.Server.Storage.EntitySystems
         /// <returns>true if inserted, false otherwise</returns>
         public bool PlayerInsertEntityInWorld(EntityUid uid, EntityUid player, EntityUid toInsert, ServerStorageComponent? storageComp = null)
         {
-            if (!Resolve(uid, ref storageComp))
-                return false;
-
-            if (!_sharedInteractionSystem.InRangeUnobstructed(player, uid, popup: storageComp.ShowPopup))
+            if (!Resolve(uid, ref storageComp) || !_sharedInteractionSystem.InRangeUnobstructed(player, uid, popup: storageComp.ShowPopup))
                 return false;
 
             if (!Insert(uid, toInsert, storageComp))
@@ -663,18 +619,17 @@ namespace Content.Server.Storage.EntitySystems
         /// <param name="entity">The entity to open the UI for</param>
         public void OpenStorageUI(EntityUid uid, EntityUid entity, ServerStorageComponent? storageComp = null)
         {
-            if (!Resolve(uid, ref storageComp))
-                return;
-
-            if (!TryComp(entity, out ActorComponent? player))
+            if (!Resolve(uid, ref storageComp) || !TryComp(entity, out ActorComponent? player))
                 return;
 
             if (storageComp.StorageOpenSound is not null)
-                SoundSystem.Play(storageComp.StorageOpenSound.GetSound(), Filter.Pvs(uid, entityManager: EntityManager), uid, storageComp.StorageOpenSound.Params);
+                _audio.Play(storageComp.StorageOpenSound, Filter.Pvs(uid, entityManager: EntityManager), uid, true, storageComp.StorageOpenSound.Params);
 
             Logger.DebugS(storageComp.LoggerName, $"Storage (UID {uid}) \"used\" by player session (UID {player.PlayerSession.AttachedEntity}).");
 
-            _uiSystem.GetUiOrNull(uid, StorageUiKey.Key)?.Open(player.PlayerSession);
+            var bui = _uiSystem.GetUiOrNull(uid, StorageUiKey.Key);
+            if (bui != null)
+                _uiSystem.OpenUi(bui, player.PlayerSession);
         }
 
         /// <summary>
@@ -683,10 +638,7 @@ namespace Content.Server.Storage.EntitySystems
         /// <param name="session"></param>
         public void CloseNestedInterfaces(EntityUid uid, IPlayerSession session, ServerStorageComponent? storageComp = null)
         {
-            if (!Resolve(uid, ref storageComp))
-                return;
-
-            if (storageComp.StoredEntities == null)
+            if (!Resolve(uid, ref storageComp) || storageComp.StoredEntities == null)
                 return;
 
             // for each containing thing
@@ -697,9 +649,7 @@ namespace Content.Server.Storage.EntitySystems
             foreach (var entity in storageComp.StoredEntities)
             {
                 if (TryComp(entity, out ServerStorageComponent? storedStorageComp))
-                {
                     DebugTools.Assert(storedStorageComp != storageComp, $"Storage component contains itself!? Entity: {uid}");
-                }
 
                 if (!TryComp(entity, out ServerUserInterfaceComponent? ui))
                     continue;
@@ -718,34 +668,22 @@ namespace Content.Server.Storage.EntitySystems
 
             var state = new StorageBoundUserInterfaceState((List<EntityUid>) storageComp.Storage.ContainedEntities, storageComp.StorageUsed, storageComp.StorageCapacityMax);
 
-            _uiSystem.GetUiOrNull(uid, StorageUiKey.Key)?.SetState(state);
+            var bui = _uiSystem.GetUiOrNull(uid, StorageUiKey.Key);
+            if (bui != null)
+                _uiSystem.SetUiState(bui, state);
         }
 
         private void Popup(EntityUid uid, EntityUid player, string message, ServerStorageComponent storageComp)
         {
-            if (!storageComp.ShowPopup) return;
+            if (!storageComp.ShowPopup)
+                return;
 
             _popupSystem.PopupEntity(Loc.GetString(message), player, player);
         }
 
-        /// <summary>
-        /// Raised on storage if it successfully completes area pickup.
-        /// </summary>
-        private sealed class AreaPickupCompleteEvent : EntityEventArgs
-        {
-            public EntityUid User;
-            public List<EntityUid> ValidStorables;
-
-            public AreaPickupCompleteEvent(EntityUid user, List<EntityUid> validStorables)
-            {
-                User = user;
-                ValidStorables = validStorables;
-            }
-        }
-
-        private sealed class AreaPickupCancelledEvent : EntityEventArgs
+        private record struct StorageData(List<EntityUid> validStorables)
         {
-
+            public List<EntityUid> ValidStorables = validStorables;
         }
     }
 }
index d70d374f69fb726ce78b143a00e5e025b96a4f4c..3501448e8be8661534e3fd3173787da8359fe832 100644 (file)
@@ -15,6 +15,7 @@ using Robust.Server.GameObjects;
 using System.Threading;
 using Content.Server.Administration.Logs;
 using Content.Shared.Database;
+using Content.Shared.DoAfter;
 using Content.Shared.Ensnaring.Components;
 using Content.Shared.Interaction;
 using Content.Shared.Strip;
@@ -56,7 +57,7 @@ namespace Content.Server.Strip
                 if (!TryComp<EnsnaringComponent>(entity, out var ensnaring))
                     continue;
 
-                _ensnaring.TryFree(component.Owner, ensnaring, user);
+                _ensnaring.TryFree(uid, entity, ensnaring, user);
                 return;
             }
         }
index 12dddf2f54c345948df8f07ef649e1e9662ad853..98a2aff1a8fa708106bbaab27b335402982f5bb9 100644 (file)
@@ -1,6 +1,6 @@
-using System.Threading;
 using Content.Server.Administration.Logs;
 using Content.Server.DoAfter;
+using Content.Shared.DoAfter;
 using Content.Shared.Database;
 using Content.Shared.Interaction.Events;
 using Content.Shared.Teleportation.Components;
@@ -24,19 +24,17 @@ public sealed class HandTeleporterSystem : EntitySystem
     {
         SubscribeLocalEvent<HandTeleporterComponent, UseInHandEvent>(OnUseInHand);
 
-        SubscribeLocalEvent<HandTeleporterComponent, HandTeleporterSuccessEvent>(OnPortalSuccess);
-        SubscribeLocalEvent<HandTeleporterComponent, HandTeleporterCancelledEvent>(OnPortalCancelled);
+        SubscribeLocalEvent<HandTeleporterComponent, DoAfterEvent>(OnDoAfter);
     }
 
-    private void OnPortalSuccess(EntityUid uid, HandTeleporterComponent component, HandTeleporterSuccessEvent args)
+    private void OnDoAfter(EntityUid uid, HandTeleporterComponent component, DoAfterEvent args)
     {
-        component.CancelToken = null;
-        HandlePortalUpdating(uid, component, args.User);
-    }
+        if (args.Cancelled || args.Handled)
+            return;
 
-    private void OnPortalCancelled(EntityUid uid, HandTeleporterComponent component, HandTeleporterCancelledEvent args)
-    {
-        component.CancelToken = null;
+        HandlePortalUpdating(uid, component, args.Args.User);
+
+        args.Handled = true;
     }
 
     private void OnUseInHand(EntityUid uid, HandTeleporterComponent component, UseInHandEvent args)
@@ -47,12 +45,6 @@ public sealed class HandTeleporterSystem : EntitySystem
         if (Deleted(component.SecondPortal))
             component.SecondPortal = null;
 
-        if (component.CancelToken != null)
-        {
-            component.CancelToken.Cancel();
-            return;
-        }
-
         if (component.FirstPortal != null && component.SecondPortal != null)
         {
             // handle removing portals immediately as opposed to a doafter
@@ -64,16 +56,12 @@ public sealed class HandTeleporterSystem : EntitySystem
             if (xform.ParentUid != xform.GridUid)
                 return;
 
-            component.CancelToken = new CancellationTokenSource();
-            var doafterArgs = new DoAfterEventArgs(args.User, component.PortalCreationDelay,
-                component.CancelToken.Token, used: uid)
+            var doafterArgs = new DoAfterEventArgs(args.User, component.PortalCreationDelay, used: uid)
             {
                 BreakOnDamage = true,
                 BreakOnStun = true,
                 BreakOnUserMove = true,
                 MovementThreshold = 0.5f,
-                UsedCancelledEvent = new HandTeleporterCancelledEvent(),
-                UsedFinishedEvent = new HandTeleporterSuccessEvent(args.User)
             };
 
             _doafter.DoAfter(doafterArgs);
index 0bb8b3c7d2a50df19b6964210044c4cf8b4d691a..b98c5c9d38f9173b9333dc1bbe0c013fe0bd4e2a 100644 (file)
@@ -3,7 +3,6 @@ using Content.Server.Buckle.Systems;
 using Content.Server.Popups;
 using Content.Server.Storage.Components;
 using Content.Server.Storage.EntitySystems;
-using Content.Server.Tools;
 using Content.Shared.Body.Components;
 using Content.Shared.Body.Part;
 using Content.Shared.Buckle.Components;
@@ -13,6 +12,7 @@ using Content.Shared.Interaction;
 using Content.Shared.Interaction.Events;
 using Content.Shared.Popups;
 using Content.Shared.Toilet;
+using Content.Shared.Tools;
 using Content.Shared.Tools.Components;
 using Robust.Shared.Audio;
 using Robust.Shared.Player;
@@ -28,7 +28,7 @@ namespace Content.Server.Toilet
         [Dependency] private readonly SharedAudioSystem _audio = default!;
         [Dependency] private readonly SecretStashSystem _secretStash = default!;
         [Dependency] private readonly PopupSystem _popupSystem = default!;
-        [Dependency] private readonly ToolSystem _toolSystem = default!;
+        [Dependency] private readonly SharedToolSystem _toolSystem = default!;
 
         public override void Initialize()
         {
@@ -102,10 +102,10 @@ namespace Content.Server.Toilet
                     return;
                 component.IsPrying = true;
 
+                var toolEvData = new ToolEventData(new ToiletPryFinished(uid));
+
                 // try to pry toilet cistern
-                if (!_toolSystem.UseTool(args.Used, args.User, uid, 0f,
-                    component.PryLidTime, component.PryingQuality,
-                    new ToiletPryFinished(uid), new ToiletPryInterrupted(uid)))
+                if (!_toolSystem.UseTool(args.Used, args.User, uid, component.PryLidTime, new [] {component.PryingQuality}, toolEvData))
                 {
                     component.IsPrying = false;
                     return;
index addfc38b76480820e057d8efa482b814a99fdab3..b1e4e18fec9774f7eb055fa9a110427550d6ef19 100644 (file)
@@ -1,5 +1,4 @@
-using System.Threading;
-using Content.Shared.Tools;
+using Content.Shared.Tools;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
 
 namespace Content.Server.Tools.Components;
@@ -18,9 +17,4 @@ public sealed class LatticeCuttingComponent : Component
 
     [DataField("vacuumDelay")]
     public float VacuumDelay = 1.75f;
-
-    /// <summary>
-    /// Used for do_afters.
-    /// </summary>
-    public CancellationTokenSource? CancelTokenSource = null;
 }
index 6a43b471d3c30459749313bff9e774de2d7f0e9c..82566e355930ce8cd0aab7e410c091f3a6a78041 100644 (file)
@@ -15,10 +15,5 @@ namespace Content.Server.Tools.Components
 
         [DataField("delay")]
         public float Delay = 1f;
-
-        /// <summary>
-        /// Used for do_afters.
-        /// </summary>
-        public CancellationTokenSource? CancelToken = null;
     }
 }
index 068af0ecebfae17680a9dcdf3937a8b9820bd39c..ced618ad024bc47daf58bd04e2153acc8c37d189 100644 (file)
@@ -3,15 +3,15 @@ using Content.Server.Tools.Components;
 using Content.Shared.Database;
 using Content.Shared.Examine;
 using Content.Shared.Interaction;
+using Content.Shared.Tools;
 using Content.Shared.Tools.Components;
-using Robust.Server.GameObjects;
 
 namespace Content.Server.Tools.Systems;
 
 public sealed class WeldableSystem : EntitySystem
 {
     [Dependency] private readonly IAdminLogManager _adminLogger = default!;
-    [Dependency] private readonly ToolSystem _toolSystem = default!;
+    [Dependency] private readonly SharedToolSystem _toolSystem = default!;
     [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
 
     public override void Initialize()
@@ -65,9 +65,8 @@ public sealed class WeldableSystem : EntitySystem
         if (!CanWeld(uid, tool, user, component))
             return false;
 
-        component.BeingWelded = _toolSystem.UseTool(tool, user, uid, component.FuelConsumption,
-            component.WeldingTime.Seconds, component.WeldingQuality,
-            new WeldFinishedEvent(user, tool), new WeldCancelledEvent(), uid);
+        var toolEvData = new ToolEventData(new WeldFinishedEvent(user, tool), targetEntity: uid);
+        component.BeingWelded = _toolSystem.UseTool(tool, user, uid, component.WeldingTime.Seconds, new[] { component.WeldingQuality }, toolEvData, fuel: component.FuelConsumption);
 
         // Log attempt
         _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(user):user} is {(component.IsWelded ? "un" : "")}welding {ToPrettyString(uid):target} at {Transform(uid).Coordinates:targetlocation}");
index 165e5708b23fcd6a033d7adba6c84b8ff7b68bb8..4a62865561f00feb3f0cb426f682d4912827b25b 100644 (file)
@@ -1,5 +1,4 @@
-using System.Threading;
-using Content.Server.Administration.Logs;
+using Content.Server.Administration.Logs;
 using Content.Server.Maps;
 using Content.Server.Tools.Components;
 using Content.Shared.Database;
@@ -19,17 +18,10 @@ public sealed partial class ToolSystem
     {
         SubscribeLocalEvent<LatticeCuttingComponent, AfterInteractEvent>(OnLatticeCuttingAfterInteract);
         SubscribeLocalEvent<LatticeCuttingComponent, LatticeCuttingCompleteEvent>(OnLatticeCutComplete);
-        SubscribeLocalEvent<LatticeCuttingComponent, LatticeCuttingCancelledEvent>(OnLatticeCutCancelled);
-    }
-
-    private void OnLatticeCutCancelled(EntityUid uid, LatticeCuttingComponent component, LatticeCuttingCancelledEvent args)
-    {
-        component.CancelTokenSource = null;
     }
 
     private void OnLatticeCutComplete(EntityUid uid, LatticeCuttingComponent component, LatticeCuttingCompleteEvent args)
     {
-        component.CancelTokenSource = null;
         var gridUid = args.Coordinates.GetGridUid(EntityManager);
         if (gridUid == null)
             return;
@@ -52,17 +44,14 @@ public sealed partial class ToolSystem
         if (args.Handled || !args.CanReach || args.Target != null)
             return;
 
-        if (TryCut(args.User, component, args.ClickLocation))
+        if (TryCut(uid, args.User, component, args.ClickLocation))
             args.Handled = true;
     }
 
-    private bool TryCut(EntityUid user, LatticeCuttingComponent component, EntityCoordinates clickLocation)
+    private bool TryCut(EntityUid toolEntity, EntityUid user, LatticeCuttingComponent component, EntityCoordinates clickLocation)
     {
-        if (component.CancelTokenSource != null)
-            return true;
-
         ToolComponent? tool = null;
-        if (component.ToolComponentNeeded && !TryComp<ToolComponent?>(component.Owner, out tool))
+        if (component.ToolComponentNeeded && !TryComp<ToolComponent?>(toolEntity, out tool))
             return false;
 
         if (!_mapManager.TryGetGrid(clickLocation.GetGridUid(EntityManager), out var mapGrid))
@@ -82,17 +71,10 @@ public sealed partial class ToolSystem
             || tile.IsBlockedTurf(true))
             return false;
 
-        var tokenSource = new CancellationTokenSource();
-        component.CancelTokenSource = tokenSource;
+        var toolEvData = new ToolEventData(new LatticeCuttingCompleteEvent(clickLocation, user), targetEntity: toolEntity);
 
-        if (!UseTool(component.Owner, user, null, 0f, component.Delay, new[] {component.QualityNeeded},
-                new LatticeCuttingCompleteEvent
-                {
-                    Coordinates = clickLocation,
-                    User = user
-                }, new LatticeCuttingCancelledEvent(), toolComponent: tool, doAfterEventTarget: component.Owner,
-                cancelToken: tokenSource.Token))
-            component.CancelTokenSource = null;
+        if (!UseTool(toolEntity, user, null, component.Delay, new[] {component.QualityNeeded}, toolEvData, toolComponent: tool))
+            return false;
 
         return true;
     }
@@ -101,10 +83,12 @@ public sealed partial class ToolSystem
     {
         public EntityCoordinates Coordinates;
         public EntityUid User;
-    }
 
-    private sealed class LatticeCuttingCancelledEvent : EntityEventArgs
-    {
+        public LatticeCuttingCompleteEvent(EntityCoordinates coordinates, EntityUid user)
+        {
+            Coordinates = coordinates;
+            User = user;
+        }
     }
 }
 
index fcb0d7e0a3afe650e3a1c77a0c19710c48b131a4..1af417a533be68a8fb07af25cd09d7603387a391 100644 (file)
@@ -19,17 +19,10 @@ public sealed partial class ToolSystem
     {
         SubscribeLocalEvent<TilePryingComponent, AfterInteractEvent>(OnTilePryingAfterInteract);
         SubscribeLocalEvent<TilePryingComponent, TilePryingCompleteEvent>(OnTilePryComplete);
-        SubscribeLocalEvent<TilePryingComponent, TilePryingCancelledEvent>(OnTilePryCancelled);
-    }
-
-    private void OnTilePryCancelled(EntityUid uid, TilePryingComponent component, TilePryingCancelledEvent args)
-    {
-        component.CancelToken = null;
     }
 
     private void OnTilePryComplete(EntityUid uid, TilePryingComponent component, TilePryingCompleteEvent args)
     {
-        component.CancelToken = null;
         var gridUid = args.Coordinates.GetGridUid(EntityManager);
         if (!_mapManager.TryGetGrid(gridUid, out var grid))
         {
@@ -45,18 +38,13 @@ public sealed partial class ToolSystem
     {
         if (args.Handled || !args.CanReach || (args.Target != null && !HasComp<PuddleComponent>(args.Target))) return;
 
-        if (TryPryTile(args.User, component, args.ClickLocation))
+        if (TryPryTile(uid, args.User, component, args.ClickLocation))
             args.Handled = true;
     }
 
-    private bool TryPryTile(EntityUid user, TilePryingComponent component, EntityCoordinates clickLocation)
+    private bool TryPryTile(EntityUid toolEntity, EntityUid user, TilePryingComponent component, EntityCoordinates clickLocation)
     {
-        if (component.CancelToken != null)
-        {
-            return true;
-        }
-
-        if (!TryComp<ToolComponent?>(component.Owner, out var tool) && component.ToolComponentNeeded)
+        if (!TryComp<ToolComponent?>(toolEntity, out var tool) && component.ToolComponentNeeded)
             return false;
 
         if (!_mapManager.TryGetGrid(clickLocation.GetGridUid(EntityManager), out var mapGrid))
@@ -74,34 +62,22 @@ public sealed partial class ToolSystem
         if (!tileDef.CanCrowbar)
             return false;
 
-        var token = new CancellationTokenSource();
-        component.CancelToken = token;
-
-        bool success = UseTool(
-            component.Owner,
-            user,
-            null,
-            0f,
-            component.Delay,
-            new [] {component.QualityNeeded},
-            new TilePryingCompleteEvent
-            {
-                Coordinates = clickLocation,
-            },
-            new TilePryingCancelledEvent(),
-            toolComponent: tool,
-            doAfterEventTarget: component.Owner,
-            cancelToken: token.Token);
-
-        if (!success)
-            component.CancelToken = null;
+        var toolEvData = new ToolEventData(new TilePryingCompleteEvent(clickLocation), targetEntity:toolEntity);
+
+        if (!UseTool(toolEntity, user, null, component.Delay, new[] { component.QualityNeeded }, toolEvData, toolComponent: tool))
+            return false;
 
         return true;
     }
 
     private sealed class TilePryingCompleteEvent : EntityEventArgs
     {
-        public EntityCoordinates Coordinates { get; init; }
+        public readonly EntityCoordinates Coordinates;
+
+        public TilePryingCompleteEvent(EntityCoordinates coordinates)
+        {
+            Coordinates = coordinates;
+        }
     }
 
     private sealed class TilePryingCancelledEvent : EntityEventArgs
index 0368c7bc29a433e3b0bb6c51546f8e15f5431cde..de37c680058dd1fa9a0db1ad621b637ed498d1ee 100644 (file)
@@ -1,17 +1,9 @@
 using Content.Server.Atmos.EntitySystems;
 using Content.Server.Chemistry.EntitySystems;
-using Content.Server.DoAfter;
 using Content.Server.Popups;
-using Content.Shared.Audio;
-using Content.Shared.Item;
 using Content.Shared.Tools;
-using Content.Shared.Tools.Components;
 using Robust.Server.GameObjects;
-using Robust.Shared.Audio;
 using Robust.Shared.Map;
-using Robust.Shared.Player;
-using System.Threading;
-using System.Threading.Tasks;
 
 namespace Content.Server.Tools
 {
@@ -20,12 +12,10 @@ namespace Content.Server.Tools
     {
         [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
         [Dependency] private readonly IMapManager _mapManager = default!;
-        [Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
         [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
         [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
         [Dependency] private readonly PopupSystem _popupSystem = default!;
         [Dependency] private readonly TransformSystem _transformSystem = default!;
-        [Dependency] private readonly SharedItemSystem _itemSystem = default!;
 
         public override void Initialize()
         {
@@ -34,220 +24,6 @@ namespace Content.Server.Tools
             InitializeTilePrying();
             InitializeLatticeCutting();
             InitializeWelders();
-
-            SubscribeLocalEvent<ToolDoAfterComplete>(OnDoAfterComplete);
-            SubscribeLocalEvent<ToolDoAfterCancelled>(OnDoAfterCancelled);
-        }
-
-        private void OnDoAfterComplete(ToolDoAfterComplete ev)
-        {
-            // Actually finish the tool use! Depending on whether that succeeds or not, either event will be broadcast.
-            if(ToolFinishUse(ev.Uid, ev.UserUid, ev.Fuel))
-            {
-                if (ev.EventTarget != null)
-                    RaiseLocalEvent(ev.EventTarget.Value, ev.CompletedEvent, false);
-                else
-                    RaiseLocalEvent(ev.CompletedEvent);
-            }
-            else if(ev.CancelledEvent != null)
-            {
-                if (ev.EventTarget != null)
-                    RaiseLocalEvent(ev.EventTarget.Value, ev.CancelledEvent, false);
-                else
-                    RaiseLocalEvent(ev.CancelledEvent);
-            }
-        }
-
-        private void OnDoAfterCancelled(ToolDoAfterCancelled ev)
-        {
-            if (ev.EventTarget != null)
-                RaiseLocalEvent(ev.EventTarget.Value, ev.Event, false);
-            else
-                RaiseLocalEvent(ev.Event);
-        }
-
-        /// <summary>
-        ///     Whether a tool entity has the specified quality or not.
-        /// </summary>
-        public bool HasQuality(EntityUid uid, string quality, ToolComponent? tool = null)
-        {
-            return Resolve(uid, ref tool, false) && tool.Qualities.Contains(quality);
-        }
-
-        /// <summary>
-        ///     Whether a tool entity has all specified qualities or not.
-        /// </summary>
-        public bool HasAllQualities(EntityUid uid, IEnumerable<string> qualities, ToolComponent? tool = null)
-        {
-            return Resolve(uid, ref tool, false) && tool.Qualities.ContainsAll(qualities);
-        }
-
-        /// <summary>
-        ///     Sync version of UseTool.
-        /// </summary>
-        /// <param name="tool">The tool entity.</param>
-        /// <param name="user">The entity using the tool.</param>
-        /// <param name="target">Optionally, a target to use the tool on.</param>
-        /// <param name="fuel">An optional amount of fuel or energy to consume-</param>
-        /// <param name="doAfterDelay">A doAfter delay in seconds.</param>
-        /// <param name="toolQualitiesNeeded">The tool qualities needed to use the tool.</param>
-        /// <param name="doAfterCompleteEvent">An event to raise once the doAfter is completed successfully.</param>
-        /// <param name="doAfterCancelledEvent">An event to raise once the doAfter is canceled.</param>
-        /// <param name="doAfterEventTarget">Where to direct the do-after events. If null, events are broadcast</param>
-        /// <param name="doAfterCheck">An optional check to perform for the doAfter.</param>
-        /// <param name="toolComponent">The tool component.</param>
-        /// <param name="cancelToken">Token to provide to do_after for cancelling</param>
-        /// <returns>Whether initially, using the tool succeeded. If there's a doAfter delay, you'll need to listen to
-        ///          the <see cref="doAfterCompleteEvent"/> and <see cref="doAfterCancelledEvent"/> being broadcast
-        ///          to see whether using the tool succeeded or not. If the <see cref="doAfterDelay"/> is zero,
-        ///          this simply returns whether using the tool succeeded or not.</returns>
-        public override bool UseTool(
-            EntityUid tool,
-            EntityUid user,
-            EntityUid? target,
-            float fuel,
-            float doAfterDelay,
-            IEnumerable<string> toolQualitiesNeeded,
-            object? doAfterCompleteEvent = null,
-            object? doAfterCancelledEvent = null,
-            EntityUid? doAfterEventTarget = null,
-            Func<bool>? doAfterCheck = null,
-            ToolComponent? toolComponent = null,
-            CancellationToken? cancelToken = null)
-        {
-            // No logging here, after all that'd mean the caller would need to check if the component is there or not.
-            if (!Resolve(tool, ref toolComponent, false))
-                return false;
-
-            var ev = new ToolUserAttemptUseEvent(user, target);
-            RaiseLocalEvent(user, ref ev);
-            if (ev.Cancelled)
-                return false;
-
-            if (!ToolStartUse(tool, user, fuel, toolQualitiesNeeded, toolComponent))
-                return false;
-
-            if (doAfterDelay > 0f)
-            {
-                var doAfterArgs = new DoAfterEventArgs(user, doAfterDelay / toolComponent.SpeedModifier, cancelToken ?? default, target)
-                {
-                    ExtraCheck = doAfterCheck,
-                    BreakOnDamage = true,
-                    BreakOnStun = true,
-                    BreakOnTargetMove = true,
-                    BreakOnUserMove = true,
-                    NeedHand = true,
-                    BroadcastFinishedEvent = doAfterCompleteEvent != null ? new ToolDoAfterComplete(doAfterCompleteEvent, doAfterCancelledEvent, tool, user, fuel, doAfterEventTarget) : null,
-                    BroadcastCancelledEvent = doAfterCancelledEvent != null ? new ToolDoAfterCancelled(doAfterCancelledEvent, doAfterEventTarget) : null,
-                };
-
-                _doAfterSystem.DoAfter(doAfterArgs);
-                return true;
-            }
-
-            return ToolFinishUse(tool, user, fuel, toolComponent);
-        }
-
-        /// <summary>
-        ///     Async version of UseTool.
-        /// </summary>
-        /// <param name="tool">The tool entity.</param>
-        /// <param name="user">The entity using the tool.</param>
-        /// <param name="target">Optionally, a target to use the tool on.</param>
-        /// <param name="fuel">An optional amount of fuel or energy to consume-</param>
-        /// <param name="doAfterDelay">A doAfter delay in seconds.</param>
-        /// <param name="toolQualitiesNeeded">The tool qualities needed to use the tool.</param>
-        /// <param name="doAfterCheck">An optional check to perform for the doAfter.</param>
-        /// <param name="toolComponent">The tool component.</param>
-        /// <returns>Whether using the tool succeeded or not.</returns>
-        public async Task<bool> UseTool(EntityUid tool, EntityUid user, EntityUid? target, float fuel,
-            float doAfterDelay, IEnumerable<string> toolQualitiesNeeded, Func<bool>? doAfterCheck = null,
-            ToolComponent? toolComponent = null)
-        {
-            // No logging here, after all that'd mean the caller would need to check if the component is there or not.
-            if (!Resolve(tool, ref toolComponent, false))
-                return false;
-
-            var ev = new ToolUserAttemptUseEvent(user, target);
-            RaiseLocalEvent(user, ref ev);
-            if (ev.Cancelled)
-                return false;
-
-            if (!ToolStartUse(tool, user, fuel, toolQualitiesNeeded, toolComponent))
-                return false;
-
-            if (doAfterDelay > 0f)
-            {
-                var doAfterArgs = new DoAfterEventArgs(user, doAfterDelay / toolComponent.SpeedModifier, default, target)
-                {
-                    ExtraCheck = doAfterCheck,
-                    BreakOnDamage = true,
-                    BreakOnStun = true,
-                    BreakOnTargetMove = true,
-                    BreakOnUserMove = true,
-                    NeedHand = true,
-                };
-
-                var result = await _doAfterSystem.WaitDoAfter(doAfterArgs);
-
-                if (result == DoAfterStatus.Cancelled)
-                    return false;
-            }
-
-            return ToolFinishUse(tool, user, fuel, toolComponent);
-        }
-
-        // This is hilariously long.
-        /// <inheritdoc cref="UseTool(Robust.Shared.GameObjects.EntityUid,Robust.Shared.GameObjects.EntityUid,System.Nullable{Robust.Shared.GameObjects.EntityUid},float,float,System.Collections.Generic.IEnumerable{string},Robust.Shared.GameObjects.EntityUid,object,object,System.Func{bool}?,Content.Shared.Tools.Components.ToolComponent?)"/>
-        public Task<bool> UseTool(EntityUid tool, EntityUid user, EntityUid? target, float fuel,
-            float doAfterDelay, string toolQualityNeeded, Func<bool>? doAfterCheck = null,
-            ToolComponent? toolComponent = null)
-        {
-            return UseTool(tool, user, target, fuel, doAfterDelay, new [] {toolQualityNeeded}, doAfterCheck, toolComponent);
-        }
-
-        private bool ToolStartUse(EntityUid tool, EntityUid user, float fuel, IEnumerable<string> toolQualitiesNeeded, ToolComponent? toolComponent = null)
-        {
-            if (!Resolve(tool, ref toolComponent))
-                return false;
-
-            if (!toolComponent.Qualities.ContainsAll(toolQualitiesNeeded))
-                return false;
-
-            var beforeAttempt = new ToolUseAttemptEvent(fuel, user);
-            RaiseLocalEvent(tool, beforeAttempt, false);
-
-            return !beforeAttempt.Cancelled;
-        }
-
-        private bool ToolFinishUse(EntityUid tool, EntityUid user, float fuel, ToolComponent? toolComponent = null)
-        {
-            if (!Resolve(tool, ref toolComponent))
-                return false;
-
-            var afterAttempt = new ToolUseFinishAttemptEvent(fuel, user);
-            RaiseLocalEvent(tool, afterAttempt, false);
-
-            if (afterAttempt.Cancelled)
-                return false;
-
-            if (toolComponent.UseSound != null)
-                PlayToolSound(tool, toolComponent);
-
-            return true;
-        }
-
-        public void PlayToolSound(EntityUid uid, ToolComponent? tool = null)
-        {
-            if (!Resolve(uid, ref tool))
-                return;
-
-            if (tool.UseSound is not {} sound)
-                return;
-
-            // Pass tool.Owner to Filter.Pvs to avoid a TryGetEntity call.
-            SoundSystem.Play(sound.GetSound(), Filter.Pvs(tool.Owner),
-                uid, AudioHelpers.WithVariation(0.175f).WithVolume(-5f));
         }
 
         public override void Update(float frameTime)
@@ -256,85 +32,5 @@ namespace Content.Server.Tools
 
             UpdateWelders(frameTime);
         }
-
-        private sealed class ToolDoAfterComplete : EntityEventArgs
-        {
-            public readonly object CompletedEvent;
-            public readonly object? CancelledEvent;
-            public readonly EntityUid Uid;
-            public readonly EntityUid UserUid;
-            public readonly float Fuel;
-            public readonly EntityUid? EventTarget;
-
-            public ToolDoAfterComplete(object completedEvent, object? cancelledEvent, EntityUid uid, EntityUid userUid, float fuel, EntityUid? eventTarget = null)
-            {
-                CompletedEvent = completedEvent;
-                Uid = uid;
-                UserUid = userUid;
-                Fuel = fuel;
-                CancelledEvent = cancelledEvent;
-                EventTarget = eventTarget;
-            }
-        }
-
-        private sealed class ToolDoAfterCancelled : EntityEventArgs
-        {
-            public readonly object Event;
-            public readonly EntityUid? EventTarget;
-
-            public ToolDoAfterCancelled(object @event, EntityUid? eventTarget = null)
-            {
-                Event = @event;
-                EventTarget = eventTarget;
-            }
-        }
-    }
-
-    /// <summary>
-    ///     Attempt event called *before* any do afters to see if the tool usage should succeed or not.
-    ///     You can change the fuel consumption by changing the Fuel property.
-    /// </summary>
-    public sealed class ToolUseAttemptEvent : CancellableEntityEventArgs
-    {
-        public float Fuel { get; set; }
-        public EntityUid User { get; }
-
-        public ToolUseAttemptEvent(float fuel, EntityUid user)
-        {
-            Fuel = fuel;
-            User = user;
-        }
-    }
-
-    /// <summary>
-    /// Event raised on the user of a tool to see if they can actually use it.
-    /// </summary>
-    [ByRefEvent]
-    public struct ToolUserAttemptUseEvent
-    {
-        public EntityUid User;
-        public EntityUid? Target;
-        public bool Cancelled = false;
-
-        public ToolUserAttemptUseEvent(EntityUid user, EntityUid? target)
-        {
-            User = user;
-            Target = target;
-        }
-    }
-
-    /// <summary>
-    ///     Attempt event called *after* any do afters to see if the tool usage should succeed or not.
-    ///     You can use this event to consume any fuel needed.
-    /// </summary>
-    public sealed class ToolUseFinishAttemptEvent : CancellableEntityEventArgs
-    {
-        public float Fuel { get; }
-        public EntityUid User { get; }
-
-        public ToolUseFinishAttemptEvent(float fuel, EntityUid user)
-        {
-            Fuel = fuel;
-        }
     }
 }
index 1825c407cd58beb70cf55a358c41830d9db2d46b..944ce5bb0c25927e9f846a6948d00be92df7d20f 100644 (file)
@@ -8,8 +8,6 @@ namespace Content.Server.VendingMachines.Restock
     [RegisterComponent]
     public sealed class VendingMachineRestockComponent : Component
     {
-        public CancellationTokenSource? CancelToken;
-
         /// <summary>
         /// The time (in seconds) that it takes to restock a machine.
         /// </summary>
index b1a95f07276f7ba0bcc9866e6bf2d5e6acf9ec58..e8528edadf82bbcc0ad514e2ca8ccde72f0b150f 100644 (file)
@@ -1,14 +1,14 @@
 using System.Linq;
-using System.Threading;
-using Robust.Server.GameObjects;
-using Robust.Shared.Audio;
-using Robust.Shared.Prototypes;
 using Content.Server.Cargo.Systems;
 using Content.Server.DoAfter;
 using Content.Server.Wires;
+using Content.Shared.DoAfter;
 using Content.Shared.Interaction;
 using Content.Shared.Popups;
 using Content.Shared.VendingMachines;
+using Robust.Server.GameObjects;
+using Robust.Shared.Audio;
+using Robust.Shared.Prototypes;
 
 namespace Content.Server.VendingMachines.Restock
 {
@@ -27,16 +27,16 @@ namespace Content.Server.VendingMachines.Restock
 
             SubscribeLocalEvent<VendingMachineRestockComponent, AfterInteractEvent>(OnAfterInteract);
             SubscribeLocalEvent<VendingMachineRestockComponent, PriceCalculationEvent>(OnPriceCalculation);
-            SubscribeLocalEvent<VendingMachineRestockComponent, RestockCancelledEvent>(OnRestockCancelled);
         }
 
         public bool TryAccessMachine(EntityUid uid,
-            VendingMachineRestockComponent component,
+            VendingMachineRestockComponent restock,
             VendingMachineComponent machineComponent,
             EntityUid user,
             EntityUid target)
         {
-            if (!TryComp<WiresComponent>(target, out var wires) || !wires.IsPanelOpen) {
+            if (!TryComp<WiresComponent>(target, out var wires) || !wires.IsPanelOpen)
+            {
                 _popupSystem.PopupCursor(Loc.GetString("vending-machine-restock-needs-panel-open",
                         ("this", uid),
                         ("user", user),
@@ -54,13 +54,9 @@ namespace Content.Server.VendingMachines.Restock
             EntityUid user,
             EntityUid target)
         {
-            if (!component.CanRestock.Contains(machineComponent.PackPrototypeId)) {
-                _popupSystem.PopupCursor(Loc.GetString("vending-machine-restock-invalid-inventory",
-                        ("this", uid),
-                        ("user", user),
-                        ("target", target)
-                        ),
-                    user);
+            if (!component.CanRestock.Contains(machineComponent.PackPrototypeId))
+            {
+                _popupSystem.PopupCursor(Loc.GetString("vending-machine-restock-invalid-inventory", ("this", uid), ("user", user), ("target", target)), user);
                 return false;
             }
 
@@ -69,7 +65,7 @@ namespace Content.Server.VendingMachines.Restock
 
         private void OnAfterInteract(EntityUid uid, VendingMachineRestockComponent component, AfterInteractEvent args)
         {
-            if (component.CancelToken != null || args.Target == null || !args.CanReach)
+            if (args.Target == null || !args.CanReach)
                 return;
 
             if (!TryComp<VendingMachineComponent>(args.Target, out var machineComponent))
@@ -81,17 +77,8 @@ namespace Content.Server.VendingMachines.Restock
             if (!TryAccessMachine(uid, component, machineComponent, args.User, args.Target.Value))
                 return;
 
-            component.CancelToken = new CancellationTokenSource();
-
-            _doAfterSystem.DoAfter(new DoAfterEventArgs(
-                    args.User,
-                    (float) component.RestockDelay.TotalSeconds,
-                    component.CancelToken.Token,
-                    args.Target,
-                    args.Used)
+            _doAfterSystem.DoAfter(new DoAfterEventArgs(args.User, (float) component.RestockDelay.TotalSeconds, target:args.Target, used:uid)
             {
-                TargetFinishedEvent = new VendingMachineRestockEvent(args.User, uid),
-                UsedCancelledEvent = new RestockCancelledEvent(),
                 BreakOnTargetMove = true,
                 BreakOnUserMove = true,
                 BreakOnStun = true,
@@ -99,18 +86,11 @@ namespace Content.Server.VendingMachines.Restock
                 NeedHand = true
             });
 
-            _popupSystem.PopupEntity(Loc.GetString("vending-machine-restock-start",
-                    ("this", uid),
-                    ("user", args.User),
-                    ("target", args.Target)
-                    ),
+            _popupSystem.PopupEntity(Loc.GetString("vending-machine-restock-start", ("this", uid), ("user", args.User), ("target", args.Target)),
                 args.User,
                 PopupType.Medium);
 
-            _audioSystem.PlayPvs(component.SoundRestockStart, component.Owner,
-                AudioParams.Default
-                .WithVolume(-2f)
-                .WithVariation(0.2f));
+            _audioSystem.PlayPvs(component.SoundRestockStart, component.Owner, AudioParams.Default.WithVolume(-2f).WithVariation(0.2f));
         }
 
         private void OnPriceCalculation(EntityUid uid, VendingMachineRestockComponent component, ref PriceCalculationEvent args)
@@ -136,13 +116,5 @@ namespace Content.Server.VendingMachines.Restock
 
             args.Price += priceSets.Max();
         }
-
-        private void OnRestockCancelled(EntityUid uid, VendingMachineRestockComponent component, RestockCancelledEvent args)
-        {
-            component.CancelToken?.Cancel();
-            component.CancelToken = null;
-        }
-
-        public readonly struct RestockCancelledEvent { }
     }
 }
index ccef2b1b4c62cb3491f01b387c640c74c31526fa..57355afcce83eb351fdbd0672c66a6fe5cc241cf 100644 (file)
@@ -10,6 +10,7 @@ using Content.Shared.Actions;
 using Content.Shared.Actions.ActionTypes;
 using Content.Shared.Damage;
 using Content.Shared.Destructible;
+using Content.Shared.DoAfter;
 using Content.Shared.Emag.Components;
 using Content.Shared.Emag.Systems;
 using Content.Shared.Popups;
@@ -55,7 +56,7 @@ namespace Content.Server.VendingMachines
 
             SubscribeLocalEvent<VendingMachineComponent, VendingMachineSelfDispenseEvent>(OnSelfDispense);
 
-            SubscribeLocalEvent<VendingMachineComponent, VendingMachineRestockEvent>(OnRestock);
+            SubscribeLocalEvent<VendingMachineComponent, DoAfterEvent>(OnDoAfter);
         }
 
         private void OnVendingPrice(EntityUid uid, VendingMachineComponent component, ref PriceCalculationEvent args)
@@ -162,29 +163,26 @@ namespace Content.Server.VendingMachines
             EjectRandom(uid, throwItem: true, forceEject: false, component);
         }
 
-        private void OnRestock(EntityUid uid, VendingMachineComponent component, VendingMachineRestockEvent args)
+        private void OnDoAfter(EntityUid uid, VendingMachineComponent component, DoAfterEvent args)
         {
-            if (!TryComp<VendingMachineRestockComponent>(args.RestockBox, out var restockComponent))
+            if (args.Handled || args.Cancelled || args.Args.Used == null)
+                return;
+
+            if (!TryComp<VendingMachineRestockComponent>(args.Args.Used, out var restockComponent))
             {
-                _sawmill.Error($"{ToPrettyString(args.User)} tried to restock {ToPrettyString(uid)} with {ToPrettyString(args.RestockBox)} which did not have a VendingMachineRestockComponent.");
+                _sawmill.Error($"{ToPrettyString(args.Args.User)} tried to restock {ToPrettyString(uid)} with {ToPrettyString(args.Args.Used.Value)} which did not have a VendingMachineRestockComponent.");
                 return;
             }
 
             TryRestockInventory(uid, component);
 
-            _popupSystem.PopupEntity(Loc.GetString("vending-machine-restock-done",
-                    ("this", args.RestockBox),
-                    ("user", args.User),
-                    ("target", uid)),
-                args.User,
-                PopupType.Medium);
+            _popupSystem.PopupEntity(Loc.GetString("vending-machine-restock-done", ("this", args.Args.Used), ("user", args.Args.User), ("target", uid)), args.Args.User, PopupType.Medium);
+
+            _audioSystem.PlayPvs(restockComponent.SoundRestockDone, uid, AudioParams.Default.WithVolume(-2f).WithVariation(0.2f));
 
-            _audioSystem.PlayPvs(restockComponent.SoundRestockDone, component.Owner,
-                AudioParams.Default
-                .WithVolume(-2f)
-                .WithVariation(0.2f));
+            Del(args.Args.Used.Value);
 
-            Del(args.RestockBox);
+            args.Handled = true;
         }
 
         /// <summary>
@@ -450,18 +448,5 @@ namespace Content.Server.VendingMachines
             UpdateVendingMachineInterfaceState(vendComponent);
             TryUpdateVisualState(uid, vendComponent);
         }
-
-    }
-
-    public sealed class VendingMachineRestockEvent : EntityEventArgs
-    {
-        public EntityUid User { get; }
-        public EntityUid RestockBox { get; }
-
-        public VendingMachineRestockEvent(EntityUid user, EntityUid restockBox)
-        {
-            User = user;
-            RestockBox = restockBox;
-        }
     }
 }
index feea5492b47fa2db84ae7d147284d27449be9350..b285a972ceaef40c136179b1562d1f33112026d5 100644 (file)
@@ -11,6 +11,7 @@ using Content.Shared.Popups;
 using Content.Shared.Verbs;
 using Robust.Shared.Player;
 using Content.Server.Actions.Events;
+using Content.Shared.DoAfter;
 using Content.Shared.Weapons.Melee.Events;
 
 
@@ -30,7 +31,7 @@ namespace Content.Server.Wieldable
             base.Initialize();
 
             SubscribeLocalEvent<WieldableComponent, UseInHandEvent>(OnUseInHand);
-            SubscribeLocalEvent<WieldableComponent, ItemWieldedEvent>(OnItemWielded);
+            SubscribeLocalEvent<WieldableComponent, DoAfterEvent>(OnDoAfter);
             SubscribeLocalEvent<WieldableComponent, ItemUnwieldedEvent>(OnItemUnwielded);
             SubscribeLocalEvent<WieldableComponent, GotUnequippedHandEvent>(OnItemLeaveHand);
             SubscribeLocalEvent<WieldableComponent, VirtualItemDeletedEvent>(OnVirtualItemDeleted);
@@ -125,18 +126,12 @@ namespace Content.Server.Wieldable
             if (ev.Cancelled)
                 return;
 
-            var doargs = new DoAfterEventArgs(
-                user,
-                component.WieldTime,
-                default,
-                used)
+            var doargs = new DoAfterEventArgs(user, component.WieldTime, used:used)
             {
                 BreakOnUserMove = false,
                 BreakOnDamage = true,
                 BreakOnStun = true,
-                BreakOnTargetMove = true,
-                TargetFinishedEvent = new ItemWieldedEvent(user),
-                UserFinishedEvent = new WieldedItemEvent(used)
+                BreakOnTargetMove = true
             };
 
             _doAfter.DoAfter(doargs);
@@ -154,18 +149,13 @@ namespace Content.Server.Wieldable
                 return;
 
             var targEv = new ItemUnwieldedEvent(user);
-            var userEv = new UnwieldedItemEvent(used);
 
             RaiseLocalEvent(used, targEv);
-            RaiseLocalEvent(user, userEv);
         }
 
-        private void OnItemWielded(EntityUid uid, WieldableComponent component, ItemWieldedEvent args)
+        private void OnDoAfter(EntityUid uid, WieldableComponent component, DoAfterEvent args)
         {
-            if (args.User == null)
-                return;
-
-            if (!CanWield(uid, component, args.User.Value) || component.Wielded)
+            if (args.Handled || args.Cancelled || !CanWield(uid, component, args.Args.User) || component.Wielded)
                 return;
 
             if (TryComp<ItemComponent>(uid, out var item))
@@ -177,19 +167,17 @@ namespace Content.Server.Wieldable
             component.Wielded = true;
 
             if (component.WieldSound != null)
-            {
                 _audioSystem.PlayPvs(component.WieldSound, uid);
-            }
 
-            for (var i = 0; i < component.FreeHandsRequired; i++)
+            for (int i = 0; i < component.FreeHandsRequired; i++)
             {
-                _virtualItemSystem.TrySpawnVirtualItemInHand(uid, args.User.Value);
+                _virtualItemSystem.TrySpawnVirtualItemInHand(uid, args.Args.User);
             }
 
-            _popupSystem.PopupEntity(Loc.GetString("wieldable-component-successful-wield",
-                ("item", uid)), args.User.Value, args.User.Value);
-            _popupSystem.PopupEntity(Loc.GetString("wieldable-component-successful-wield-other",
-                ("user", args.User.Value),("item", uid)), args.User.Value, Filter.PvsExcept(args.User.Value), true);
+            _popupSystem.PopupEntity(Loc.GetString("wieldable-component-successful-wield", ("item", uid)), args.Args.User, args.Args.User);
+            _popupSystem.PopupEntity(Loc.GetString("wieldable-component-successful-wield-other", ("user", args.Args.User),("item", uid)), args.Args.User, Filter.PvsExcept(args.Args.User), true);
+
+            args.Handled = true;
         }
 
         private void OnItemUnwielded(EntityUid uid, WieldableComponent component, ItemUnwieldedEvent args)
@@ -253,32 +241,6 @@ namespace Content.Server.Wieldable
     {
     }
 
-    /// <summary>
-    ///     Raised on the item that has been wielded.
-    /// </summary>
-    public sealed class ItemWieldedEvent : EntityEventArgs
-    {
-        public EntityUid? User;
-
-        public ItemWieldedEvent(EntityUid? user = null)
-        {
-            User = user;
-        }
-    }
-
-    /// <summary>
-    ///     Raised on the user who wielded the item.
-    /// </summary>
-    public sealed class WieldedItemEvent : EntityEventArgs
-    {
-        public EntityUid Item;
-
-        public WieldedItemEvent(EntityUid item)
-        {
-            Item = item;
-        }
-    }
-
     public sealed class BeforeUnwieldEvent : CancellableEntityEventArgs
     {
     }
@@ -301,18 +263,5 @@ namespace Content.Server.Wieldable
         }
     }
 
-    /// <summary>
-    ///     Raised on the user who unwielded the item.
-    /// </summary>
-    public sealed class UnwieldedItemEvent : EntityEventArgs
-    {
-        public EntityUid Item;
-
-        public UnwieldedItemEvent(EntityUid item)
-        {
-            Item = item;
-        }
-    }
-
     #endregion
 }
index d4890fbfa511eeba4739c12a33e5c148204fad3d..8be77f8a0edd0f1b26e690770026c9140fe6265a 100644 (file)
@@ -5,12 +5,13 @@ using Content.Server.Administration.Logs;
 using Content.Server.DoAfter;
 using Content.Server.Hands.Components;
 using Content.Server.Power.Components;
-using Content.Server.Tools;
+using Content.Shared.DoAfter;
 using Content.Shared.Database;
 using Content.Shared.Examine;
 using Content.Shared.GameTicking;
 using Content.Shared.Interaction;
 using Content.Shared.Popups;
+using Content.Shared.Tools;
 using Content.Shared.Tools.Components;
 using Content.Shared.Wires;
 using Robust.Server.GameObjects;
@@ -26,7 +27,7 @@ public sealed class WiresSystem : EntitySystem
     [Dependency] private readonly IAdminLogManager _adminLogger = default!;
     [Dependency] private readonly AppearanceSystem _appearance = default!;
     [Dependency] private readonly DoAfterSystem _doAfter = default!;
-    [Dependency] private readonly ToolSystem _toolSystem = default!;
+    [Dependency] private readonly SharedToolSystem _toolSystem = default!;
     [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
     [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
     [Dependency] private readonly SharedAudioSystem _audio = default!;
@@ -55,8 +56,7 @@ public sealed class WiresSystem : EntitySystem
         SubscribeLocalEvent<WiresComponent, MapInitEvent>(OnMapInit);
         SubscribeLocalEvent<WiresComponent, TimedWireEvent>(OnTimedWire);
         SubscribeLocalEvent<WiresComponent, PowerChangedEvent>(OnWiresPowered);
-        SubscribeLocalEvent<WiresComponent, OnWireDoAfterEvent>(OnWireDoAfter);
-        SubscribeLocalEvent<WiresComponent, OnWireDoAfterCancelEvent>(OnWireDoAfterCancel);
+        SubscribeLocalEvent<WiresComponent, DoAfterEvent<WireExtraData>>(OnDoAfter);
     }
 
     private void SetOrCreateWireLayout(EntityUid uid, WiresComponent? wires = null)
@@ -434,14 +434,20 @@ public sealed class WiresSystem : EntitySystem
         TryDoWireAction(uid, player, activeHandEntity, args.Id, args.Action, component, tool);
     }
 
-    private void OnWireDoAfter(EntityUid uid, WiresComponent component, OnWireDoAfterEvent args)
+    private void OnDoAfter(EntityUid uid, WiresComponent component, DoAfterEvent<WireExtraData> args)
     {
-        UpdateWires(args.Target, args.User, args.Tool, args.Id, args.Action, component);
-    }
+        if (args.Cancelled)
+        {
+            component.WiresQueue.Remove(args.AdditionalData.Id);
+            return;
+        }
 
-    private void OnWireDoAfterCancel(EntityUid uid, WiresComponent component, OnWireDoAfterCancelEvent args)
-    {
-        component.WiresQueue.Remove(args.Id);
+        if (args.Handled || args.Args.Target == null || args.Args.Used == null)
+            return;
+
+        UpdateWires(args.Args.Target.Value, args.Args.User, args.Args.Used.Value, args.AdditionalData.Id, args.AdditionalData.Action, component);
+
+        args.Handled = true;
     }
 
     private void OnInteractUsing(EntityUid uid, WiresComponent component, InteractUsingEvent args)
@@ -461,11 +467,9 @@ public sealed class WiresSystem : EntitySystem
         }
         else if (!component.IsScrewing && _toolSystem.HasQuality(args.Used, "Screwing", tool))
         {
-            component.IsScrewing = _toolSystem.UseTool(args.Used, args.User, uid,
-                0f, ScrewTime, new[] { "Screwing" },
-                new WireToolFinishedEvent(uid, args.User),
-                new WireToolCanceledEvent(uid),
-                toolComponent: tool);
+            var toolEvData = new ToolEventData(new WireToolFinishedEvent(uid, args.User));
+
+            component.IsScrewing = _toolSystem.UseTool(args.Used, args.User, uid, ScrewTime, new[] { "Screwing" }, toolEvData, toolComponent: tool);
             args.Handled = component.IsScrewing;
 
             // Log attempt
@@ -711,27 +715,16 @@ public sealed class WiresSystem : EntitySystem
 
         if (_toolTime > 0f)
         {
-            var args = new DoAfterEventArgs(user, _toolTime, default, used)
+            var data = new WireExtraData(action, id);
+            var args = new DoAfterEventArgs(user, _toolTime, target:used, used:toolEntity)
             {
                 NeedHand = true,
                 BreakOnStun = true,
                 BreakOnDamage = true,
-                BreakOnUserMove = true,
-                TargetFinishedEvent = new OnWireDoAfterEvent
-                {
-                    Target = used,
-                    User = user,
-                    Tool = toolEntity,
-                    Action = action,
-                    Id = id
-                },
-                TargetCancelledEvent = new OnWireDoAfterCancelEvent
-                {
-                    Id = id
-                }
+                BreakOnUserMove = true
             };
 
-            _doAfter.DoAfter(args);
+            _doAfter.DoAfter(args, data);
         }
         else
         {
@@ -739,7 +732,11 @@ public sealed class WiresSystem : EntitySystem
         }
     }
 
-
+    private record struct WireExtraData(WiresAction Action, int Id)
+    {
+        public WiresAction Action = Action;
+        public int Id = Id;
+    }
 
     private void UpdateWires(EntityUid used, EntityUid user, EntityUid toolEntity, int id, WiresAction action, WiresComponent? wires = null, ToolComponent? tool = null)
     {
@@ -931,20 +928,6 @@ public sealed class WiresSystem : EntitySystem
     }
 
     public record struct WireToolCanceledEvent(EntityUid Target);
-
-    private sealed class OnWireDoAfterEvent : EntityEventArgs
-    {
-        public EntityUid User { get; set; }
-        public EntityUid Target { get; set; }
-        public EntityUid Tool { get; set; }
-        public WiresAction Action { get; set; }
-        public int Id { get; set; }
-    }
-
-    private sealed class OnWireDoAfterCancelEvent : EntityEventArgs
-    {
-        public int Id { get; set; }
-    }
     #endregion
 }
 
index dfffa6d62f16642abc9ccd234ebe53490aa545d4..0a92b28abe674bf152bd66297eb97da2687ec084 100644 (file)
@@ -1,4 +1,3 @@
-using System.Threading;
 using Content.Shared.Construction.EntitySystems;
 using Content.Shared.Tools;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
@@ -21,8 +20,6 @@ namespace Content.Shared.Construction.Components
         [ViewVariables(VVAccess.ReadWrite)]
         [DataField("delay")]
         public float Delay = 1f;
-
-        public CancellationTokenSource? CancelToken = null;
     }
 
     public abstract class BaseAnchoredAttemptEvent : CancellableEntityEventArgs
diff --git a/Content.Shared/DoAfter/ActiveDoAfterComponent.cs b/Content.Shared/DoAfter/ActiveDoAfterComponent.cs
new file mode 100644 (file)
index 0000000..d8d71de
--- /dev/null
@@ -0,0 +1,12 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.DoAfter;
+
+/// <summary>
+///     Added to entities that are currently performing any doafters.
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+public sealed class ActiveDoAfterComponent : Component
+{
+
+}
diff --git a/Content.Shared/DoAfter/DoAfter.cs b/Content.Shared/DoAfter/DoAfter.cs
new file mode 100644 (file)
index 0000000..85d15ac
--- /dev/null
@@ -0,0 +1,82 @@
+using System.Threading.Tasks;
+using Content.Shared.Hands.Components;
+using Robust.Shared.Map;
+using Robust.Shared.Serialization;
+using Robust.Shared.Timing;
+
+namespace Content.Shared.DoAfter;
+[Serializable, NetSerializable]
+[DataDefinition]
+public sealed class DoAfter
+{
+    [NonSerialized]
+    [Obsolete]
+    public Task<DoAfterStatus> AsTask;
+
+    [NonSerialized]
+    [Obsolete("Will be obsolete for EventBus")]
+    public TaskCompletionSource<DoAfterStatus> Tcs;
+
+    //TODO: Should be merged into here
+    public readonly DoAfterEventArgs EventArgs;
+
+    //ID so the client DoAfterSystem can track
+    public byte ID;
+
+    public bool Cancelled = false;
+
+    //Cache the delay so the timer properly shows
+    public float Delay;
+
+    //Keep track of the time this DoAfter started
+    public TimeSpan StartTime;
+
+    //Keep track of the time this DoAfter was cancelled
+    public TimeSpan CancelledTime;
+
+    //How long has the do after been running?
+    public TimeSpan Elapsed = TimeSpan.Zero;
+
+    /// <summary>
+    /// Accrued time when cancelled.
+    /// </summary>
+    public TimeSpan CancelledElapsed = TimeSpan.Zero;
+
+    public EntityCoordinates UserGrid;
+    public EntityCoordinates TargetGrid;
+
+    [NonSerialized]
+    public Action<bool>? Done;
+
+#pragma warning disable RA0004
+    public DoAfterStatus Status => AsTask.IsCompletedSuccessfully ? AsTask.Result : DoAfterStatus.Running;
+#pragma warning restore RA0004
+
+    // NeedHand
+    public readonly string? ActiveHand;
+    public readonly EntityUid? ActiveItem;
+
+    public DoAfter(DoAfterEventArgs eventArgs, IEntityManager entityManager)
+    {
+        EventArgs = eventArgs;
+        StartTime = IoCManager.Resolve<IGameTiming>().CurTime;
+
+        if (eventArgs.BreakOnUserMove)
+            UserGrid = entityManager.GetComponent<TransformComponent>(eventArgs.User).Coordinates;
+
+        if (eventArgs.Target != null && eventArgs.BreakOnTargetMove)
+            // Target should never be null if the bool is set.
+            TargetGrid = entityManager.GetComponent<TransformComponent>(eventArgs.Target!.Value).Coordinates;
+
+        // 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).
+        if (eventArgs.NeedHand && entityManager.TryGetComponent(eventArgs.User, out SharedHandsComponent? handsComponent))
+        {
+            ActiveHand = handsComponent.ActiveHand?.Name;
+            ActiveItem = handsComponent.ActiveHandEntity;
+        }
+
+        Tcs = new TaskCompletionSource<DoAfterStatus>();
+        AsTask = Tcs.Task;
+    }
+}
diff --git a/Content.Shared/DoAfter/DoAfterComponent.cs b/Content.Shared/DoAfter/DoAfterComponent.cs
new file mode 100644 (file)
index 0000000..38d4622
--- /dev/null
@@ -0,0 +1,89 @@
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.DoAfter;
+
+[RegisterComponent, NetworkedComponent]
+public sealed class DoAfterComponent : Component
+{
+    [DataField("doAfters")]
+    public readonly Dictionary<byte, DoAfter> DoAfters = new();
+
+    [DataField("cancelledDoAfters")]
+    public readonly Dictionary<byte, DoAfter> CancelledDoAfters = new();
+
+    // So the client knows which one to update (and so we don't send all of the do_afters every time 1 updates)
+    // we'll just send them the index. Doesn't matter if it wraps around.
+    [DataField("runningIndex")]
+    public byte RunningIndex;
+}
+
+[Serializable, NetSerializable]
+public sealed class DoAfterComponentState : ComponentState
+{
+    public Dictionary<byte, DoAfter> DoAfters;
+
+    public DoAfterComponentState(Dictionary<byte, DoAfter> doAfters)
+    {
+        DoAfters = doAfters;
+    }
+}
+
+/// <summary>
+/// Use this event to raise your DoAfter events now.
+/// Check for cancelled, and if it is, then null the token there.
+/// </summary>
+/// TODO: Add a networked DoAfterEvent to pass in AdditionalData for the future
+[Serializable, NetSerializable]
+public sealed class DoAfterEvent : HandledEntityEventArgs
+{
+    public bool Cancelled;
+    public readonly DoAfterEventArgs Args;
+
+    public DoAfterEvent(bool cancelled, DoAfterEventArgs args)
+    {
+        Cancelled = cancelled;
+        Args = args;
+    }
+}
+
+/// <summary>
+/// Use this event to raise your DoAfter events now.
+/// Check for cancelled, and if it is, then null the token there.
+/// Can't be serialized
+/// </summary>
+/// TODO: Net/Serilization isn't supported so this needs to be networked somehow
+public sealed class DoAfterEvent<T> : HandledEntityEventArgs
+{
+    public T AdditionalData;
+    public bool Cancelled;
+    public readonly DoAfterEventArgs Args;
+
+    public DoAfterEvent(T additionalData, bool cancelled, DoAfterEventArgs args)
+    {
+        AdditionalData = additionalData;
+        Cancelled = cancelled;
+        Args = args;
+    }
+}
+
+[Serializable, NetSerializable]
+public sealed class CancelledDoAfterMessage : EntityEventArgs
+{
+    public EntityUid Uid;
+    public byte ID;
+
+    public CancelledDoAfterMessage(EntityUid uid, byte id)
+    {
+        Uid = uid;
+        ID = id;
+    }
+}
+
+[Serializable, NetSerializable]
+public enum DoAfterStatus : byte
+{
+    Running,
+    Cancelled,
+    Finished,
+}
diff --git a/Content.Shared/DoAfter/DoAfterEventArgs.cs b/Content.Shared/DoAfter/DoAfterEventArgs.cs
new file mode 100644 (file)
index 0000000..1aaa90e
--- /dev/null
@@ -0,0 +1,121 @@
+using System.Threading;
+using Content.Shared.FixedPoint;
+using Robust.Shared.Serialization;
+using Robust.Shared.Utility;
+
+namespace Content.Shared.DoAfter;
+//TODO: Merge into DoAfter
+[Serializable, NetSerializable]
+public sealed class DoAfterEventArgs
+{
+    /// <summary>
+    ///     The entity invoking do_after
+    /// </summary>
+    public EntityUid User;
+
+    /// <summary>
+    ///     How long does the do_after require to complete
+    /// </summary>
+    public float Delay;
+
+    /// <summary>
+    ///     Applicable target (if relevant)
+    /// </summary>
+    public EntityUid? Target;
+
+    /// <summary>
+    ///     Entity used by the User on the Target.
+    /// </summary>
+    public EntityUid? Used;
+
+    public bool RaiseOnUser = true;
+
+    public bool RaiseOnTarget = true;
+
+    public bool RaiseOnUsed = true;
+
+    /// <summary>
+    ///     Manually cancel the do_after so it no longer runs
+    /// </summary>
+    [NonSerialized]
+    public CancellationToken CancelToken;
+
+    // Break the chains
+    /// <summary>
+    ///     Whether we need to keep our active hand as is (i.e. can't change hand or change item).
+    ///     This also covers requiring the hand to be free (if applicable).
+    /// </summary>
+    public bool NeedHand;
+
+    /// <summary>
+    ///     If do_after stops when the user moves
+    /// </summary>
+    public bool BreakOnUserMove;
+
+    /// <summary>
+    ///     If do_after stops when the target moves (if there is a target)
+    /// </summary>
+    public bool BreakOnTargetMove;
+
+    /// <summary>
+    ///     Threshold for user and target movement
+    /// </summary>
+    public float MovementThreshold;
+
+    public bool BreakOnDamage;
+
+    /// <summary>
+    ///     Threshold for user damage
+    /// </summary>
+    public FixedPoint2? DamageThreshold;
+    public bool BreakOnStun;
+
+    /// <summary>
+    /// Should the DoAfter event broadcast?
+    /// </summary>
+    public bool Broadcast;
+
+    /// <summary>
+    ///     Threshold for distance user from the used OR target entities.
+    /// </summary>
+    public float? DistanceThreshold;
+
+    /// <summary>
+    ///     Requires a function call once at the end (like InRangeUnobstructed).
+    /// </summary>
+    /// <remarks>
+    ///     Anything that needs a pre-check should do it itself so no DoAfterState is ever sent to the client.
+    /// </remarks>
+    [NonSerialized]
+    //TODO: Replace with eventbus
+    public Func<bool>? PostCheck;
+
+    /// <summary>
+    ///     Additional conditions that need to be met. Return false to cancel.
+    /// </summary>
+    [NonSerialized]
+    //TODO Replace with eventbus
+    public Func<bool>? ExtraCheck;
+
+    public DoAfterEventArgs(
+        EntityUid user,
+        float delay,
+        CancellationToken cancelToken = default,
+        EntityUid? target = null,
+        EntityUid? used = null)
+    {
+        User = user;
+        Delay = delay;
+        CancelToken = cancelToken;
+        Target = target;
+        Used = used;
+        MovementThreshold = 0.1f;
+        DamageThreshold = 1.0;
+
+        if (Target == null)
+        {
+            DebugTools.Assert(!BreakOnTargetMove);
+            BreakOnTargetMove = false;
+        }
+    }
+}
diff --git a/Content.Shared/DoAfter/SharedDoAfterComponent.cs b/Content.Shared/DoAfter/SharedDoAfterComponent.cs
deleted file mode 100644 (file)
index 3d75916..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-using Content.Shared.FixedPoint;
-using Robust.Shared.GameStates;
-using Robust.Shared.Map;
-using Robust.Shared.Serialization;
-
-namespace Content.Shared.DoAfter
-{
-    [NetworkedComponent()]
-    public abstract class SharedDoAfterComponent : Component
-    {
-    }
-
-    [Serializable, NetSerializable]
-    public sealed class DoAfterComponentState : ComponentState
-    {
-        public List<ClientDoAfter> DoAfters { get; }
-
-        public DoAfterComponentState(List<ClientDoAfter> doAfters)
-        {
-            DoAfters = doAfters;
-        }
-    }
-
-    [Serializable, NetSerializable]
-    public sealed class CancelledDoAfterMessage : EntityEventArgs
-    {
-        public EntityUid Uid;
-        public byte ID { get; }
-
-        public CancelledDoAfterMessage(EntityUid uid, byte id)
-        {
-            Uid = uid;
-            ID = id;
-        }
-    }
-
-    // TODO: Merge this with the actual DoAfter
-    /// <summary>
-    ///     We send a trimmed-down version of the DoAfter for the client for it to use.
-    /// </summary>
-    [Serializable, NetSerializable]
-    public sealed class ClientDoAfter
-    {
-        public bool Cancelled = false;
-
-        /// <summary>
-        /// Accrued time when cancelled.
-        /// </summary>
-        public float CancelledAccumulator;
-
-        // To see what these do look at DoAfter and DoAfterEventArgs
-        public byte ID { get; }
-
-        public TimeSpan StartTime { get; }
-
-        public EntityCoordinates UserGrid { get; }
-
-        public EntityCoordinates TargetGrid { get; }
-
-        public EntityUid? Target { get; }
-
-        public float Accumulator;
-
-        public float Delay { get; }
-
-        // TODO: The other ones need predicting
-        public bool BreakOnUserMove { get; }
-
-        public bool BreakOnTargetMove { get; }
-
-        public float MovementThreshold { get; }
-
-        public FixedPoint2 DamageThreshold { get; }
-
-        public ClientDoAfter(byte id, EntityCoordinates userGrid, EntityCoordinates targetGrid, TimeSpan startTime,
-            float delay, bool breakOnUserMove, bool breakOnTargetMove, float movementThreshold, FixedPoint2 damageThreshold, EntityUid? target = null)
-        {
-            ID = id;
-            UserGrid = userGrid;
-            TargetGrid = targetGrid;
-            StartTime = startTime;
-            Delay = delay;
-            BreakOnUserMove = breakOnUserMove;
-            BreakOnTargetMove = breakOnTargetMove;
-            MovementThreshold = movementThreshold;
-            DamageThreshold = damageThreshold;
-            Target = target;
-        }
-    }
-}
diff --git a/Content.Shared/DoAfter/SharedDoAfterSystem.cs b/Content.Shared/DoAfter/SharedDoAfterSystem.cs
new file mode 100644 (file)
index 0000000..d83570d
--- /dev/null
@@ -0,0 +1,385 @@
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Content.Shared.Damage;
+using Content.Shared.Hands.Components;
+using Content.Shared.Mobs;
+using Content.Shared.Stunnable;
+using Robust.Shared.GameStates;
+using Robust.Shared.Timing;
+
+namespace Content.Shared.DoAfter;
+
+public abstract class SharedDoAfterSystem : EntitySystem
+{
+    [Dependency] protected readonly IGameTiming GameTiming = default!;
+
+    // We cache the list as to not allocate every update tick...
+    private readonly Queue<DoAfter> _pending = new();
+
+    public override void Initialize()
+    {
+        base.Initialize();
+        SubscribeLocalEvent<DoAfterComponent, DamageChangedEvent>(OnDamage);
+        SubscribeLocalEvent<DoAfterComponent, MobStateChangedEvent>(OnStateChanged);
+        SubscribeLocalEvent<DoAfterComponent, ComponentGetState>(OnDoAfterGetState);
+    }
+
+    private void Add(EntityUid entity, DoAfterComponent component, DoAfter doAfter)
+    {
+        doAfter.ID = component.RunningIndex;
+        doAfter.Delay = doAfter.EventArgs.Delay;
+        component.DoAfters.Add(component.RunningIndex, doAfter);
+        EnsureComp<ActiveDoAfterComponent>(entity);
+        component.RunningIndex++;
+        Dirty(component);
+    }
+
+    private void OnDoAfterGetState(EntityUid uid, DoAfterComponent component, ref ComponentGetState args)
+    {
+        args.State = new DoAfterComponentState(component.DoAfters);
+    }
+
+    private void Cancelled(DoAfterComponent component, DoAfter doAfter)
+    {
+        if (!component.DoAfters.TryGetValue(doAfter.ID, out var index))
+            return;
+
+        component.DoAfters.Remove(doAfter.ID);
+
+        if (component.DoAfters.Count == 0)
+            RemComp<ActiveDoAfterComponent>(component.Owner);
+
+        RaiseNetworkEvent(new CancelledDoAfterMessage(component.Owner, index.ID));
+    }
+
+    /// <summary>
+    ///     Call when the particular DoAfter is finished.
+    ///     Client should be tracking this independently.
+    /// </summary>
+    private void Finished(DoAfterComponent component, DoAfter doAfter)
+    {
+        if (!component.DoAfters.ContainsKey(doAfter.ID))
+            return;
+
+        component.DoAfters.Remove(doAfter.ID);
+
+        if (component.DoAfters.Count == 0)
+            RemComp<ActiveDoAfterComponent>(component.Owner);
+    }
+
+    private void OnStateChanged(EntityUid uid, DoAfterComponent component, MobStateChangedEvent args)
+    {
+        if (args.NewMobState != MobState.Dead || args.NewMobState != MobState.Critical)
+            return;
+
+        foreach (var (_, doAfter) in component.DoAfters)
+        {
+            Cancel(uid, doAfter, component);
+        }
+    }
+
+    /// <summary>
+    /// Cancels DoAfter if it breaks on damage and it meets the threshold
+    /// </summary>
+    /// <param name="uid">The EntityUID of the user</param>
+    /// <param name="component"></param>
+    /// <param name="args"></param>
+    private void OnDamage(EntityUid uid, DoAfterComponent component, DamageChangedEvent args)
+    {
+        if (!args.InterruptsDoAfters || !args.DamageIncreased || args.DamageDelta == null)
+            return;
+
+        foreach (var doAfter in component.DoAfters.Values)
+        {
+            if (doAfter.EventArgs.BreakOnDamage && args.DamageDelta?.Total.Float() > doAfter.EventArgs.DamageThreshold)
+                Cancel(uid, doAfter, component);
+        }
+    }
+
+    public override void Update(float frameTime)
+    {
+        base.Update(frameTime);
+
+        foreach (var (_, comp) in EntityManager.EntityQuery<ActiveDoAfterComponent, DoAfterComponent>())
+        {
+            foreach (var doAfter in comp.DoAfters.Values.ToArray())
+            {
+                Run(comp.Owner, comp, doAfter);
+
+                switch (doAfter.Status)
+                {
+                    case DoAfterStatus.Running:
+                        break;
+                    case DoAfterStatus.Cancelled:
+                        _pending.Enqueue(doAfter);
+                        break;
+                    case DoAfterStatus.Finished:
+                        _pending.Enqueue(doAfter);
+                        break;
+                    default:
+                        throw new ArgumentOutOfRangeException();
+                }
+            }
+
+            while (_pending.TryDequeue(out var doAfter))
+            {
+                if (doAfter.Status == DoAfterStatus.Cancelled)
+                {
+                    Cancelled(comp, doAfter);
+
+                    if (doAfter.Done != null)
+                        doAfter.Done(true);
+                }
+
+                if (doAfter.Status == DoAfterStatus.Finished)
+                {
+                    Finished(comp, doAfter);
+
+                    if (doAfter.Done != null)
+                        doAfter.Done(false);
+                }
+            }
+        }
+    }
+
+    /// <summary>
+    ///     Tasks that are delayed until the specified time has passed
+    ///     These can be potentially cancelled by the user moving or when other things happen.
+    /// </summary>
+    /// <param name="eventArgs"></param>
+    /// <returns></returns>
+    [Obsolete("Use the synchronous version instead, DoAfter")]
+    public async Task<DoAfterStatus> WaitDoAfter(DoAfterEventArgs eventArgs)
+    {
+        var doAfter = CreateDoAfter(eventArgs);
+
+        await doAfter.AsTask;
+
+        return doAfter.Status;
+    }
+
+    /// <summary>
+    ///     Creates a DoAfter without waiting for it to finish. You can use events with this.
+    ///     These can be potentially cancelled by the user moving or when other things happen.
+    ///     Use this when you need to send extra data with the DoAfter
+    /// </summary>
+    /// <param name="eventArgs">The DoAfterEventArgs</param>
+    /// <param name="data">The extra data sent over </param>
+    public void DoAfter<T>(DoAfterEventArgs eventArgs, T data)
+    {
+        var doAfter = CreateDoAfter(eventArgs);
+
+        doAfter.Done = cancelled => { Send(data, cancelled, eventArgs); };
+    }
+
+    /// <summary>
+    ///     Creates a DoAfter without waiting for it to finish. You can use events with this.
+    ///     These can be potentially cancelled by the user moving or when other things happen.
+    ///     Use this if you don't have any extra data to send with the DoAfter
+    /// </summary>
+    /// <param name="eventArgs">The DoAfterEventArgs</param>
+    public DoAfter DoAfter(DoAfterEventArgs eventArgs)
+    {
+        var doAfter = CreateDoAfter(eventArgs);
+
+        doAfter.Done = cancelled => { Send(cancelled, eventArgs); };
+
+        return doAfter;
+    }
+
+    private DoAfter CreateDoAfter(DoAfterEventArgs eventArgs)
+    {
+        // Setup
+        eventArgs.CancelToken = new CancellationToken();
+        var doAfter = new DoAfter(eventArgs, EntityManager);
+        // Caller's gonna be responsible for this I guess
+        var doAfterComponent = Comp<DoAfterComponent>(eventArgs.User);
+        doAfter.ID = doAfterComponent.RunningIndex;
+        doAfter.StartTime = GameTiming.CurTime;
+        Add(eventArgs.User, doAfterComponent, doAfter);
+        return doAfter;
+    }
+
+    private void Run(EntityUid entity, DoAfterComponent comp, DoAfter doAfter)
+    {
+        switch (doAfter.Status)
+        {
+            case DoAfterStatus.Running:
+                break;
+            case DoAfterStatus.Cancelled:
+            case DoAfterStatus.Finished:
+                return;
+            default:
+                throw new ArgumentOutOfRangeException();
+        }
+
+        doAfter.Elapsed = GameTiming.CurTime - doAfter.StartTime;
+
+        if (IsFinished(doAfter))
+        {
+            if (!TryPostCheck(doAfter))
+            {
+                Cancel(entity, doAfter, comp);
+            }
+            else
+            {
+                doAfter.Tcs.SetResult(DoAfterStatus.Finished);
+            }
+
+            return;
+        }
+
+        if (IsCancelled(doAfter))
+        {
+            Cancel(entity, doAfter, comp);
+        }
+    }
+
+    private bool TryPostCheck(DoAfter doAfter)
+    {
+        return doAfter.EventArgs.PostCheck?.Invoke() != false;
+    }
+
+    private bool IsFinished(DoAfter doAfter)
+    {
+        var delay = TimeSpan.FromSeconds(doAfter.EventArgs.Delay);
+
+        if (doAfter.Elapsed <= delay)
+            return false;
+
+        return true;
+    }
+
+    private bool IsCancelled(DoAfter doAfter)
+    {
+        var eventArgs = doAfter.EventArgs;
+        var xForm = GetEntityQuery<TransformComponent>();
+
+        if (!Exists(eventArgs.User) || eventArgs.Target is { } target && !Exists(target))
+            return true;
+
+        if (eventArgs.CancelToken.IsCancellationRequested)
+            return true;
+
+        //TODO: Handle Inertia in space
+        if (eventArgs.BreakOnUserMove && !xForm.GetComponent(eventArgs.User).Coordinates
+                .InRange(EntityManager, doAfter.UserGrid, eventArgs.MovementThreshold))
+            return true;
+
+        if (eventArgs.Target != null && eventArgs.BreakOnTargetMove && !xForm.GetComponent(eventArgs.Target!.Value)
+                .Coordinates.InRange(EntityManager, doAfter.TargetGrid, eventArgs.MovementThreshold))
+            return true;
+
+        if (eventArgs.ExtraCheck != null && !eventArgs.ExtraCheck.Invoke())
+            return true;
+
+        if (eventArgs.BreakOnStun && HasComp<StunnedComponent>(eventArgs.User))
+            return true;
+
+        if (eventArgs.NeedHand)
+        {
+            if (!TryComp<SharedHandsComponent>(eventArgs.User, out var handsComp))
+            {
+                //TODO: Figure out active hand and item values
+
+                // If we had a hand but no longer have it that's still a paddlin'
+                if (doAfter.ActiveHand != null)
+                    return true;
+            }
+            else
+            {
+                var currentActiveHand = handsComp.ActiveHand?.Name;
+                if (doAfter.ActiveHand != currentActiveHand)
+                    return true;
+
+                var currentItem = handsComp.ActiveHandEntity;
+                if (doAfter.ActiveItem != currentItem)
+                    return true;
+            }
+        }
+
+        if (eventArgs.DistanceThreshold != null)
+        {
+            var userXform = xForm.GetComponent(eventArgs.User);
+
+            if (eventArgs.Target != null && !eventArgs.User.Equals(eventArgs.Target))
+            {
+                //recalculate Target location in case Target has also moved
+                var targetCoords = xForm.GetComponent(eventArgs.Target.Value).Coordinates;
+                if (!userXform.Coordinates.InRange(EntityManager, targetCoords, eventArgs.DistanceThreshold.Value))
+                    return true;
+            }
+
+            if (eventArgs.Used != null)
+            {
+                var usedCoords = xForm.GetComponent(eventArgs.Used.Value).Coordinates;
+                if (!userXform.Coordinates.InRange(EntityManager, usedCoords, eventArgs.DistanceThreshold.Value))
+                    return true;
+            }
+        }
+
+        return false;
+    }
+
+    public void Cancel(EntityUid entity, DoAfter doAfter, DoAfterComponent? comp = null)
+    {
+        if (!Resolve(entity, ref comp, false))
+            return;
+
+        if (comp.CancelledDoAfters.ContainsKey(doAfter.ID))
+            return;
+
+        if (!comp.DoAfters.ContainsKey(doAfter.ID))
+            return;
+
+        doAfter.Cancelled = true;
+        doAfter.CancelledTime = GameTiming.CurTime;
+
+        var doAfterMessage = comp.DoAfters[doAfter.ID];
+        comp.CancelledDoAfters.Add(doAfter.ID, doAfterMessage);
+
+        if (doAfter.Status == DoAfterStatus.Running)
+        {
+            doAfter.Tcs.SetResult(DoAfterStatus.Cancelled);
+        }
+    }
+
+    /// <summary>
+    /// Send the DoAfter event, used where you don't need any extra data to send.
+    /// </summary>
+    /// <param name="cancelled"></param>
+    /// <param name="args"></param>
+    private void Send(bool cancelled, DoAfterEventArgs args)
+    {
+        var ev = new DoAfterEvent(cancelled, args);
+
+        RaiseDoAfterEvent(ev, args);
+    }
+
+    /// <summary>
+    /// Send the DoAfter event, used where you need extra data to send
+    /// </summary>
+    /// <param name="data"></param>
+    /// <param name="cancelled"></param>
+    /// <param name="args"></param>
+    /// <typeparam name="T"></typeparam>
+    private void Send<T>(T data, bool cancelled, DoAfterEventArgs args)
+    {
+        var ev = new DoAfterEvent<T>(data, cancelled, args);
+
+        RaiseDoAfterEvent(ev, args);
+    }
+
+    private void RaiseDoAfterEvent<TEvent>(TEvent ev, DoAfterEventArgs args) where TEvent : notnull
+    {
+        if (EntityManager.EntityExists(args.User) && args.RaiseOnUser)
+            RaiseLocalEvent(args.User, ev, args.Broadcast);
+
+        if (args.Target is { } target && EntityManager.EntityExists(target) && args.RaiseOnTarget)
+            RaiseLocalEvent(target, ev, args.Broadcast);
+
+        if (args.Used is { } used && EntityManager.EntityExists(used) && args.RaiseOnUsed)
+            RaiseLocalEvent(used, ev, args.Broadcast);
+    }
+}
index 1e69cdf263225da302be35ff125adb5744cbe773..d88dd30dd0f9d5d628c6d3fb7cfe35686e1a29e1 100644 (file)
@@ -57,7 +57,6 @@ public sealed class EnsnaringComponent : Component
     [DataField("canMoveBreakout")]
     public bool CanMoveBreakout;
 
-    public CancellationTokenSource? CancelToken;
 }
 
 /// <summary>
index a83f617e5e233a52fe81bd50a835657170448d3b..36509054e6672cd7ecd64b224daf5e1c42905ceb 100644 (file)
@@ -20,8 +20,6 @@ public sealed class MechEquipmentComponent : Component
     /// </summary>
     [ViewVariables]
     public EntityUid? EquipmentOwner;
-
-    public CancellationTokenSource? TokenSource = null;
 }
 
 /// <summary>
index 3a9c76111ca25ada10836fc9918abfe70e5f9e00..2bd456d77494e0e2d5a9bd4261ba53578c0140ed 100644 (file)
@@ -1,5 +1,4 @@
-using System.Threading;
-using Content.Shared.Body.Components;
+using Content.Shared.Body.Components;
 using Content.Shared.DragDrop;
 using Robust.Shared.Containers;
 using Robust.Shared.GameStates;
@@ -79,8 +78,6 @@ public abstract class SharedCryoPodComponent: Component
 
     public bool IsPrying { get; set; }
 
-    public CancellationTokenSource? DragDropCancelToken;
-
     [Serializable, NetSerializable]
     public enum CryoPodVisuals : byte
     {
index 502669dc5cddf4d3bb33b07f1c227e8e8533fbc4..5aadbbaeb8e0725e0f8c2752110b76d4c91d7f3e 100644 (file)
@@ -1,4 +1,5 @@
-using Content.Server.Medical.Components;
+using Content.Server.Medical.Components;
+using Content.Shared.DoAfter;
 using Content.Shared.Body.Components;
 using Content.Shared.DragDrop;
 using Content.Shared.Emag.Systems;
@@ -143,17 +144,6 @@ public abstract partial class SharedCryoPodSystem: EntitySystem
         args.Handled = true;
     }
 
-    protected void DoInsertCryoPod(EntityUid uid, SharedCryoPodComponent cryoPodComponent, DoInsertCryoPodEvent args)
-    {
-        cryoPodComponent.DragDropCancelToken = null;
-        InsertBody(uid, args.ToInsert, cryoPodComponent);
-    }
-
-    protected void DoInsertCancelCryoPod(EntityUid uid, SharedCryoPodComponent cryoPodComponent, DoInsertCancelledCryoPodEvent args)
-    {
-        cryoPodComponent.DragDropCancelToken = null;
-    }
-
     protected void OnCryoPodPryFinished(EntityUid uid, SharedCryoPodComponent cryoPodComponent, CryoPodPryFinished args)
     {
         cryoPodComponent.IsPrying = false;
@@ -167,8 +157,6 @@ public abstract partial class SharedCryoPodSystem: EntitySystem
 
     #region Event records
 
-    protected record DoInsertCryoPodEvent(EntityUid ToInsert);
-    protected record DoInsertCancelledCryoPodEvent;
     protected record CryoPodPryFinished;
     protected record CryoPodPryInterrupted;
 
index 23d4ee5091be6f2cde4b1d75dd74f0541dba78e4..772255eab85b4a6cd6dd5c43b2fd55d4b4cdcb55 100644 (file)
@@ -109,7 +109,9 @@ public sealed class EncryptionKeySystem : EntitySystem
             return;
         }
 
-        if (!_toolSystem.UseTool(args.Used, args.User, uid, 0f, 0f, component.KeysExtractionMethod, toolComponent: tool))
+        var toolEvData = new ToolEventData(null);
+
+        if(!_toolSystem.UseTool(args.Used, args.User, uid, 0f, new[] { component.KeysExtractionMethod }, toolEvData, toolComponent: tool))
             return;
 
         var contained = component.KeyContainer.ContainedEntities.ToArray();
index 60fa510bffd45f5f264172febcd89b9548cebaa4..c9e6824ab0744a228f1d6369f2113bf6ea9e01cc 100644 (file)
@@ -1,4 +1,3 @@
-using System.Threading;
 using Content.Shared.Disease;
 using Content.Shared.FixedPoint;
 using Content.Shared.Store;
@@ -67,9 +66,6 @@ public sealed class RevenantComponent : Component
     /// </summary>
     [ViewVariables(VVAccess.ReadWrite), DataField("maxEssenceUpgradeAmount")]
     public float MaxEssenceUpgradeAmount = 10;
-
-    public CancellationTokenSource? SoulSearchCancelToken;
-    public CancellationTokenSource? HarvestCancelToken;
     #endregion
 
     //In the nearby radius, causes various objects to be thrown, messed with, and containers opened
index 9632c86c651fde4c0977db957fc72243d261dd03..15e9726350776915ad4b31eacd17f9538fff7ccf 100644 (file)
@@ -16,8 +16,9 @@ namespace Content.Shared.Storage.Components
         public TimeSpan DelayPerItem = TimeSpan.FromSeconds(0.2);
 
         /// <summary>
-        /// Cancellation token for the doafter.
+        /// The multiplier modifier
         /// </summary>
-        public CancellationTokenSource? CancelToken;
+        [DataField("multiplier")]
+        public float Multiplier = 1.0f;
     }
 }
index 95e06c8aecc4ec57c279072a481791c2eb21a490..d30a913adf02401dcddbb69faea6719c44300e2b 100644 (file)
@@ -1,6 +1,4 @@
-using System.Threading;
-using Content.Shared.Audio;
-using Robust.Shared.Audio;
+using Robust.Shared.Audio;
 using Robust.Shared.GameStates;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
@@ -41,15 +39,4 @@ public sealed class HandTeleporterComponent : Component
     [DataField("portalCreationDelay")]
     public float PortalCreationDelay = 2.5f;
 
-    public CancellationTokenSource? CancelToken = null;
 }
-
-/// <summary>
-///     Raised on doafter success for creating a portal.
-/// </summary>
-public record HandTeleporterSuccessEvent(EntityUid User);
-
-/// <summary>
-///     Raised on doafter cancel for creating a portal.
-/// </summary>
-public record HandTeleporterCancelledEvent;
index 8f4b9c430379160c8ca8cc2d2267f66dd02e77ef..bae98e97126c1daedab56935d32960d822e64130 100644 (file)
@@ -20,4 +20,68 @@ namespace Content.Shared.Tools.Components
         [DataField("useSound")]
         public SoundSpecifier? UseSound { get; set; }
     }
+
+    /// <summary>
+    ///     Attempt event called *before* any do afters to see if the tool usage should succeed or not.
+    ///     You can change the fuel consumption by changing the Fuel property.
+    /// </summary>
+    public sealed class ToolUseAttemptEvent : CancellableEntityEventArgs
+    {
+        public float Fuel { get; set; }
+        public EntityUid User { get; }
+
+        public ToolUseAttemptEvent(float fuel, EntityUid user)
+        {
+            Fuel = fuel;
+            User = user;
+        }
+    }
+
+    /// <summary>
+    /// Event raised on the user of a tool to see if they can actually use it.
+    /// </summary>
+    [ByRefEvent]
+    public struct ToolUserAttemptUseEvent
+    {
+        public EntityUid User;
+        public EntityUid? Target;
+        public bool Cancelled = false;
+
+        public ToolUserAttemptUseEvent(EntityUid user, EntityUid? target)
+        {
+            User = user;
+            Target = target;
+        }
+    }
+
+    /// <summary>
+    ///     Attempt event called *after* any do afters to see if the tool usage should succeed or not.
+    ///     You can use this event to consume any fuel needed.
+    /// </summary>
+    public sealed class ToolUseFinishAttemptEvent : CancellableEntityEventArgs
+    {
+        public float Fuel { get; }
+        public EntityUid User { get; }
+
+        public ToolUseFinishAttemptEvent(float fuel, EntityUid user)
+        {
+            Fuel = fuel;
+        }
+    }
+
+    public sealed class ToolEventData
+    {
+        public readonly Object? Ev;
+        public readonly Object? CancelledEv;
+        public readonly float Fuel;
+        public readonly EntityUid? TargetEntity;
+
+        public ToolEventData(Object? ev, float fuel = 0f, Object? cancelledEv = null, EntityUid? targetEntity = null)
+        {
+            Ev = ev;
+            CancelledEv = cancelledEv;
+            Fuel = fuel;
+            TargetEntity = targetEntity;
+        }
+    }
 }
index 9cce7337ecb0d5bd85bd77536668a56364437fbd..b723395885d01a57a572ae2ef5cef9060c396e1e 100644 (file)
@@ -1,8 +1,11 @@
 using System.Linq;
-using System.Threading;
+using Content.Shared.Audio;
+using Content.Shared.DoAfter;
 using Content.Shared.Interaction;
 using Content.Shared.Tools.Components;
+using Robust.Shared.Audio;
 using Robust.Shared.GameStates;
+using Robust.Shared.Player;
 using Robust.Shared.Prototypes;
 
 namespace Content.Shared.Tools;
@@ -11,6 +14,7 @@ public abstract class SharedToolSystem : EntitySystem
 {
     [Dependency] private readonly IPrototypeManager _protoMan = default!;
     [Dependency] private readonly SharedAudioSystem _audioSystem = default!;
+    [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
 
     public override void Initialize()
     {
@@ -18,32 +22,77 @@ public abstract class SharedToolSystem : EntitySystem
         SubscribeLocalEvent<MultipleToolComponent, ActivateInWorldEvent>(OnMultipleToolActivated);
         SubscribeLocalEvent<MultipleToolComponent, ComponentGetState>(OnMultipleToolGetState);
         SubscribeLocalEvent<MultipleToolComponent, ComponentHandleState>(OnMultipleToolHandleState);
+
+        SubscribeLocalEvent<ToolComponent, DoAfterEvent<ToolEventData>>(OnDoAfter);
+
+        SubscribeLocalEvent<ToolDoAfterComplete>(OnDoAfterComplete);
+        SubscribeLocalEvent<ToolDoAfterCancelled>(OnDoAfterCancelled);
     }
 
-    public bool UseTool(EntityUid tool, EntityUid user, EntityUid? target, float fuel,
-            float doAfterDelay, string toolQualityNeeded, object? doAfterCompleteEvent = null, object? doAfterCancelledEvent = null, EntityUid? doAfterEventTarget = null,
-            Func<bool>? doAfterCheck = null, ToolComponent? toolComponent = null)
+    private void OnDoAfter(EntityUid uid, ToolComponent component, DoAfterEvent<ToolEventData> args)
     {
-        return UseTool(tool, user, target, fuel, doAfterDelay, new[] { toolQualityNeeded },
-            doAfterCompleteEvent, doAfterCancelledEvent, doAfterEventTarget, doAfterCheck, toolComponent);
+        if (args.Handled || args.Cancelled || args.AdditionalData.Ev == null)
+            return;
+
+        if (ToolFinishUse(uid, args.Args.User, args.AdditionalData.Fuel))
+        {
+            if (args.AdditionalData.TargetEntity != null)
+                RaiseLocalEvent(args.AdditionalData.TargetEntity.Value, args.AdditionalData.Ev);
+            else
+                RaiseLocalEvent(args.AdditionalData.Ev);
+
+            args.Handled = true;
+        }
+        else if (args.AdditionalData.CancelledEv != null)
+        {
+            if (args.AdditionalData.TargetEntity != null)
+                RaiseLocalEvent(args.AdditionalData.TargetEntity.Value, args.AdditionalData.CancelledEv);
+            else
+                RaiseLocalEvent(args.AdditionalData.CancelledEv);
+
+            args.Handled = true;
+        }
     }
 
-    public virtual bool UseTool(
-        EntityUid tool,
-        EntityUid user,
-        EntityUid? target,
-        float fuel,
-        float doAfterDelay,
-        IEnumerable<string> toolQualitiesNeeded,
-        object? doAfterCompleteEvent = null,
-        object? doAfterCancelledEvent = null,
-        EntityUid? doAfterEventTarget = null,
-        Func<bool>? doAfterCheck = null,
-        ToolComponent? toolComponent = null,
-        CancellationToken? cancelToken = null)
+    public bool UseTool(EntityUid tool, EntityUid user, EntityUid? target, float doAfterDelay, IEnumerable<string> toolQualitiesNeeded, ToolEventData toolEventData, float fuel = 0f, ToolComponent? toolComponent = null, Func<bool>? doAfterCheck = null)
     {
-        // predicted tools when.
-        return false;
+        // No logging here, after all that'd mean the caller would need to check if the component is there or not.
+        if (!Resolve(tool, ref toolComponent, false))
+            return false;
+
+        var ev = new ToolUserAttemptUseEvent(user, target);
+        RaiseLocalEvent(user, ref ev);
+        if (ev.Cancelled)
+            return false;
+
+        if (!ToolStartUse(tool, user, fuel, toolQualitiesNeeded, toolComponent))
+            return false;
+
+        if (doAfterDelay > 0f)
+        {
+            var doAfterArgs = new DoAfterEventArgs(user, doAfterDelay / toolComponent.SpeedModifier, target:target, used:tool)
+            {
+                ExtraCheck = doAfterCheck,
+                BreakOnDamage = true,
+                BreakOnStun = true,
+                BreakOnTargetMove = true,
+                BreakOnUserMove = true,
+                NeedHand = true
+            };
+
+            _doAfterSystem.DoAfter(doAfterArgs, toolEventData);
+            return true;
+        }
+
+        return ToolFinishUse(tool, user, fuel, toolComponent);
+    }
+
+    public bool UseTool(EntityUid tool, EntityUid user, EntityUid? target, float doAfterDelay, string toolQualityNeeded,
+        ToolEventData toolEventData, float fuel = 0, ToolComponent? toolComponent = null,
+        Func<bool>? doAfterCheck = null)
+    {
+        return UseTool(tool, user, target, doAfterDelay, new[] { toolQualityNeeded }, toolEventData, fuel,
+            toolComponent, doAfterCheck);
     }
 
     private void OnMultipleToolHandleState(EntityUid uid, MultipleToolComponent component, ref ComponentHandleState args)
@@ -116,5 +165,125 @@ public abstract class SharedToolSystem : EntitySystem
         if (_protoMan.TryIndex(current.Behavior.First(), out ToolQualityPrototype? quality))
             multiple.CurrentQualityName = Loc.GetString(quality.Name);
     }
+
+    /// <summary>
+    ///     Whether a tool entity has the specified quality or not.
+    /// </summary>
+    public bool HasQuality(EntityUid uid, string quality, ToolComponent? tool = null)
+    {
+        return Resolve(uid, ref tool, false) && tool.Qualities.Contains(quality);
+    }
+
+    /// <summary>
+    ///     Whether a tool entity has all specified qualities or not.
+    /// </summary>
+    public bool HasAllQualities(EntityUid uid, IEnumerable<string> qualities, ToolComponent? tool = null)
+    {
+        return Resolve(uid, ref tool, false) && tool.Qualities.ContainsAll(qualities);
+    }
+
+
+    private bool ToolStartUse(EntityUid tool, EntityUid user, float fuel, IEnumerable<string> toolQualitiesNeeded, ToolComponent? toolComponent = null)
+    {
+        if (!Resolve(tool, ref toolComponent))
+            return false;
+
+        if (!toolComponent.Qualities.ContainsAll(toolQualitiesNeeded))
+            return false;
+
+        var beforeAttempt = new ToolUseAttemptEvent(fuel, user);
+        RaiseLocalEvent(tool, beforeAttempt, false);
+
+        return !beforeAttempt.Cancelled;
+    }
+
+    private bool ToolFinishUse(EntityUid tool, EntityUid user, float fuel, ToolComponent? toolComponent = null)
+    {
+        if (!Resolve(tool, ref toolComponent))
+            return false;
+
+        var afterAttempt = new ToolUseFinishAttemptEvent(fuel, user);
+        RaiseLocalEvent(tool, afterAttempt, false);
+
+        if (afterAttempt.Cancelled)
+            return false;
+
+        if (toolComponent.UseSound != null)
+            PlayToolSound(tool, toolComponent);
+
+        return true;
+    }
+
+    public void PlayToolSound(EntityUid uid, ToolComponent? tool = null)
+    {
+        if (!Resolve(uid, ref tool))
+            return;
+
+        if (tool.UseSound is not {} sound)
+            return;
+
+        // Pass tool.Owner to Filter.Pvs to avoid a TryGetEntity call.
+        SoundSystem.Play(sound.GetSound(), Filter.Pvs(tool.Owner),
+            uid, AudioHelpers.WithVariation(0.175f).WithVolume(-5f));
+    }
+
+    private void OnDoAfterComplete(ToolDoAfterComplete ev)
+    {
+        // Actually finish the tool use! Depending on whether that succeeds or not, either event will be broadcast.
+        if(ToolFinishUse(ev.Uid, ev.UserUid, ev.Fuel))
+        {
+            if (ev.EventTarget != null)
+                RaiseLocalEvent(ev.EventTarget.Value, ev.CompletedEvent, false);
+            else
+                RaiseLocalEvent(ev.CompletedEvent);
+        }
+        else if(ev.CancelledEvent != null)
+        {
+            if (ev.EventTarget != null)
+                RaiseLocalEvent(ev.EventTarget.Value, ev.CancelledEvent, false);
+            else
+                RaiseLocalEvent(ev.CancelledEvent);
+        }
+    }
+
+    private void OnDoAfterCancelled(ToolDoAfterCancelled ev)
+    {
+        if (ev.EventTarget != null)
+            RaiseLocalEvent(ev.EventTarget.Value, ev.Event, false);
+        else
+            RaiseLocalEvent(ev.Event);
+    }
+
+    private sealed class ToolDoAfterComplete : EntityEventArgs
+    {
+        public readonly object CompletedEvent;
+        public readonly object? CancelledEvent;
+        public readonly EntityUid Uid;
+        public readonly EntityUid UserUid;
+        public readonly float Fuel;
+        public readonly EntityUid? EventTarget;
+
+        public ToolDoAfterComplete(object completedEvent, object? cancelledEvent, EntityUid uid, EntityUid userUid, float fuel, EntityUid? eventTarget = null)
+        {
+            CompletedEvent = completedEvent;
+            Uid = uid;
+            UserUid = userUid;
+            Fuel = fuel;
+            CancelledEvent = cancelledEvent;
+            EventTarget = eventTarget;
+        }
+    }
+
+    private sealed class ToolDoAfterCancelled : EntityEventArgs
+    {
+        public readonly object Event;
+        public readonly EntityUid? EventTarget;
+
+        public ToolDoAfterCancelled(object @event, EntityUid? eventTarget = null)
+        {
+            Event = @event;
+            EventTarget = eventTarget;
+        }
+    }
 }