diff --git a/Content.Server/Botany/Systems/BotanySwabSystem.cs b/Content.Server/Botany/Systems/BotanySwabSystem.cs index 09b6c38ccc..ef7cc1760b 100644 --- a/Content.Server/Botany/Systems/BotanySwabSystem.cs +++ b/Content.Server/Botany/Systems/BotanySwabSystem.cs @@ -18,7 +18,7 @@ public sealed class BotanySwabSystem : EntitySystem base.Initialize(); SubscribeLocalEvent(OnExamined); SubscribeLocalEvent(OnAfterInteract); - SubscribeLocalEvent(OnDoAfter); + SubscribeLocalEvent(OnDoAfter); } /// @@ -41,7 +41,7 @@ public sealed class BotanySwabSystem : EntitySystem /// private void OnAfterInteract(EntityUid uid, BotanySwabComponent swab, AfterInteractEvent args) { - if (args.Target == null || !args.CanReach) + if (args.Target == null || !args.CanReach || !HasComp(args.Target)) return; _doAfterSystem.DoAfter(new DoAfterEventArgs(args.User, swab.SwabDelay, target: args.Target, used: uid) @@ -57,7 +57,7 @@ public sealed class BotanySwabSystem : EntitySystem /// /// Save seed data or cross-pollenate. /// - private void OnDoAfter(DoAfterEvent args) + private void OnDoAfter(EntityUid uid, BotanySwabComponent component, DoAfterEvent args) { if (args.Cancelled || args.Handled || !TryComp(args.Args.Target, out var plant) || !TryComp(args.Args.Used, out var swab)) return; diff --git a/Content.Server/Chemistry/EntitySystems/ChemistrySystem.Injector.cs b/Content.Server/Chemistry/EntitySystems/ChemistrySystem.Injector.cs index b011f39f5d..598f470d6e 100644 --- a/Content.Server/Chemistry/EntitySystems/ChemistrySystem.Injector.cs +++ b/Content.Server/Chemistry/EntitySystems/ChemistrySystem.Injector.cs @@ -129,11 +129,18 @@ public sealed partial class ChemistrySystem private void OnInjectDoAfter(EntityUid uid, InjectorComponent component, DoAfterEvent args) { - if (args.Handled || args.Cancelled || args.Args.Target == null) + if (args.Cancelled) + { + component.IsInjecting = false; + return; + } + + if (args.Handled || args.Args.Target == null) return; UseInjector(args.Args.Target.Value, args.Args.User, uid, component); + component.IsInjecting = false; args.Handled = true; } @@ -216,12 +223,18 @@ public sealed partial class ChemistrySystem if (!_solutions.TryGetSolution(injector, InjectorComponent.SolutionName, out var solution)) return; + //If it found it's injecting + if (component.IsInjecting) + return; + var actualDelay = MathF.Max(component.Delay, 1f); // Injections take 1 second longer per additional 5u actualDelay += (float) component.TransferAmount / component.Delay - 1; - if (user != target) + var isTarget = user != target; + + if (isTarget) { // Create a pop-up for the target var userName = Identity.Entity(user, EntityManager); @@ -256,8 +269,12 @@ public sealed partial class ChemistrySystem _adminLogger.Add(LogType.Ingestion, $"{EntityManager.ToPrettyString(user):user} is attempting to inject themselves with a solution {SolutionContainerSystem.ToPrettyString(solution):solution}."); } + component.IsInjecting = true; + _doAfter.DoAfter(new DoAfterEventArgs(user, actualDelay, target:target, used:injector) { + RaiseOnTarget = isTarget, + RaiseOnUser = !isTarget, BreakOnUserMove = true, BreakOnDamage = true, BreakOnStun = true, diff --git a/Content.Server/Disease/DiseaseDiagnosisSystem.cs b/Content.Server/Disease/DiseaseDiagnosisSystem.cs index 28b4dcd7c6..98b4d6c230 100644 --- a/Content.Server/Disease/DiseaseDiagnosisSystem.cs +++ b/Content.Server/Disease/DiseaseDiagnosisSystem.cs @@ -47,7 +47,7 @@ namespace Content.Server.Disease // Private Events SubscribeLocalEvent(OnDiagnoserFinished); SubscribeLocalEvent(OnVaccinatorFinished); - SubscribeLocalEvent(OnSwabDoAfter); + SubscribeLocalEvent(OnSwabDoAfter); } private Queue AddQueue = new(); @@ -99,7 +99,7 @@ namespace Content.Server.Disease /// private void OnAfterInteract(EntityUid uid, DiseaseSwabComponent swab, AfterInteractEvent args) { - if (args.Target == null || !args.CanReach) + if (args.Target == null || !args.CanReach || !HasComp(args.Target)) return; if (swab.Used) @@ -116,8 +116,12 @@ namespace Content.Server.Disease return; } + var isTarget = args.User != args.Target; + _doAfterSystem.DoAfter(new DoAfterEventArgs(args.User, swab.SwabDelay, target: args.Target, used: uid) { + RaiseOnTarget = isTarget, + RaiseOnUser = !isTarget, BreakOnTargetMove = true, BreakOnUserMove = true, BreakOnStun = true, @@ -301,9 +305,9 @@ namespace Content.Server.Disease /// Copies a disease prototype to the swab /// after the doafter completes. /// - private void OnSwabDoAfter(DoAfterEvent args) + private void OnSwabDoAfter(EntityUid uid, DiseaseSwabComponent component, DoAfterEvent args) { - if (args.Handled || args.Cancelled || !TryComp(args.Args.Target, out var carrier) || !TryComp(args.Args.Target, out var swab)) + if (args.Handled || args.Cancelled || !TryComp(args.Args.Target, out var carrier) || !TryComp(args.Args.Used, out var swab)) return; swab.Used = true; @@ -313,7 +317,6 @@ namespace Content.Server.Disease return; swab.Disease = _random.Pick(carrier.Diseases); - } diff --git a/Content.Server/Medical/Components/HealingComponent.cs b/Content.Server/Medical/Components/HealingComponent.cs index edebf7697a..5f3120cff0 100644 --- a/Content.Server/Medical/Components/HealingComponent.cs +++ b/Content.Server/Medical/Components/HealingComponent.cs @@ -39,6 +39,12 @@ namespace Content.Server.Medical.Components [DataField("delay")] public float Delay = 3f; + /// + /// Cancel token to prevent rapid healing + /// + [DataField("cancelToken")] + public CancellationTokenSource? CancelToken; + /// /// Delay multiplier when healing yourself. /// diff --git a/Content.Server/Medical/HealingSystem.cs b/Content.Server/Medical/HealingSystem.cs index a908801605..9b4d9e77ed 100644 --- a/Content.Server/Medical/HealingSystem.cs +++ b/Content.Server/Medical/HealingSystem.cs @@ -1,3 +1,4 @@ +using System.Threading; using Content.Server.Administration.Logs; using Content.Server.Body.Systems; using Content.Server.DoAfter; @@ -40,6 +41,12 @@ public sealed class HealingSystem : EntitySystem private void OnDoAfter(EntityUid uid, DamageableComponent component, DoAfterEvent args) { + if (args.Cancelled) + { + args.AdditionalData.HealingComponent.CancelToken = null; + return; + } + if (args.Handled || args.Cancelled || _mobStateSystem.IsDead(uid) || args.Args.Used == null) return; @@ -67,6 +74,7 @@ public sealed class HealingSystem : EntitySystem if (args.AdditionalData.HealingComponent.HealingEndSound != null) _audio.PlayPvs(args.AdditionalData.HealingComponent.HealingEndSound, uid, AudioHelpers.WithVariation(0.125f, _random).WithVolume(-5f)); + args.AdditionalData.HealingComponent.CancelToken = null; args.Handled = true; } @@ -90,7 +98,7 @@ public sealed class HealingSystem : EntitySystem private bool TryHeal(EntityUid uid, EntityUid user, EntityUid target, HealingComponent component) { - if (_mobStateSystem.IsDead(target) || !TryComp(target, out var targetDamage)) + if (_mobStateSystem.IsDead(target) || !TryComp(target, out var targetDamage) || component.CancelToken != null) return false; if (targetDamage.TotalDamage == 0) @@ -108,14 +116,21 @@ public sealed class HealingSystem : EntitySystem if (component.HealingBeginSound != null) _audio.PlayPvs(component.HealingBeginSound, uid, AudioHelpers.WithVariation(0.125f, _random).WithVolume(-5f)); - var delay = user != target + var isNotSelf = user != target; + + var delay = isNotSelf ? component.Delay : component.Delay * GetScaledHealingPenalty(user, component); + component.CancelToken = new CancellationTokenSource(); + var healingData = new HealingData(component, stack); - var doAfterEventArgs = new DoAfterEventArgs(user, delay, target: target, used: uid) + var doAfterEventArgs = new DoAfterEventArgs(user, delay, cancelToken: component.CancelToken.Token,target: target, used: uid) { + //Raise the event on the target if it's not self, otherwise raise it on self. + RaiseOnTarget = isNotSelf, + RaiseOnUser = !isNotSelf, BreakOnUserMove = true, BreakOnTargetMove = true, // Didn't break on damage as they may be trying to prevent it and diff --git a/Content.Server/Nutrition/Components/DrinkComponent.cs b/Content.Server/Nutrition/Components/DrinkComponent.cs index be882f7947..14c9712fa8 100644 --- a/Content.Server/Nutrition/Components/DrinkComponent.cs +++ b/Content.Server/Nutrition/Components/DrinkComponent.cs @@ -34,6 +34,13 @@ namespace Content.Server.Nutrition.Components [DataField("burstSound")] public SoundSpecifier BurstSound = new SoundPathSpecifier("/Audio/Effects/flash_bang.ogg"); + /// + /// Is this drink being forced on someone else? + /// + /// + [DataField("forceDrink")] + public bool ForceDrink; + /// /// How long it takes to drink this yourself. /// diff --git a/Content.Server/Nutrition/Components/FoodComponent.cs b/Content.Server/Nutrition/Components/FoodComponent.cs index 5d7d325b02..c5c12e0fa6 100644 --- a/Content.Server/Nutrition/Components/FoodComponent.cs +++ b/Content.Server/Nutrition/Components/FoodComponent.cs @@ -40,6 +40,13 @@ namespace Content.Server.Nutrition.Components [DataField("eatMessage")] public string EatMessage = "food-nom"; + /// + /// Is this entity being forcefed? + /// Prevents the entity from being forced to eat multiple times if not self + /// + [DataField("forceFeed")] + public bool ForceFeed; + /// /// How long it takes to eat the food personally. /// diff --git a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs index 04c6ee7159..2c21e83c96 100644 --- a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs @@ -217,7 +217,7 @@ namespace Content.Server.Nutrition.EntitySystems private bool TryDrink(EntityUid user, EntityUid target, DrinkComponent drink, EntityUid item) { - if (!EntityManager.HasComponent(target)) + if (!EntityManager.HasComponent(target) || drink.ForceDrink) return false; if (!drink.Opened) @@ -241,9 +241,9 @@ namespace Content.Server.Nutrition.EntitySystems if (!_interactionSystem.InRangeUnobstructed(user, item, popup: true)) return true; - var forceDrink = user != target; + drink.ForceDrink = user != target; - if (forceDrink) + if (drink.ForceDrink) { var userName = Identity.Entity(user, EntityManager); @@ -264,7 +264,7 @@ namespace Content.Server.Nutrition.EntitySystems var drinkData = new DrinkData(drinkSolution, flavors); - var doAfterEventArgs = new DoAfterEventArgs(user, forceDrink ? drink.ForceFeedDelay : drink.Delay, + var doAfterEventArgs = new DoAfterEventArgs(user, drink.ForceDrink ? drink.ForceFeedDelay : drink.Delay, target: target, used: item) { BreakOnUserMove = moveBreak, @@ -286,6 +286,14 @@ namespace Content.Server.Nutrition.EntitySystems /// private void OnDoAfter(EntityUid uid, DrinkComponent component, DoAfterEvent args) { + //Special cancel if they're force feeding someone. + //Allows self to drink multiple times but prevents force feeding drinks to others rapidly. + if (args.Cancelled && component.ForceDrink) + { + component.ForceDrink = false; + return; + } + if (args.Handled || args.Cancelled || component.Deleted) return; @@ -295,11 +303,11 @@ namespace Content.Server.Nutrition.EntitySystems var transferAmount = FixedPoint2.Min(component.TransferAmount, args.AdditionalData.DrinkSolution.Volume); var drained = _solutionContainerSystem.Drain(uid, args.AdditionalData.DrinkSolution, transferAmount); - var forceDrink = args.Args.Target.Value != args.Args.User; + //var forceDrink = args.Args.Target.Value != args.Args.User; if (!_bodySystem.TryGetBodyOrganComponents(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"), args.Args.Target.Value, args.Args.User); + _popupSystem.PopupEntity(component.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 (HasComp(args.Args.Target.Value)) { @@ -320,7 +328,7 @@ namespace Content.Server.Nutrition.EntitySystems { _popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-had-enough"), args.Args.Target.Value, args.Args.Target.Value); - if (forceDrink) + if (component.ForceDrink) { _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"); @@ -334,7 +342,7 @@ namespace Content.Server.Nutrition.EntitySystems var flavors = args.AdditionalData.FlavorMessage; - if (forceDrink) + if (component.ForceDrink) { var targetName = Identity.Entity(args.Args.Target.Value, EntityManager); var userName = Identity.Entity(args.Args.User, EntityManager); @@ -366,6 +374,7 @@ namespace Content.Server.Nutrition.EntitySystems //TODO: Grab the stomach UIDs somehow without using Owner _stomachSystem.TryTransferSolution(firstStomach.Value.Comp.Owner, drained, firstStomach.Value.Comp); + component.ForceDrink = false; args.Handled = true; } diff --git a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs index 0768313eb1..2ff765f855 100644 --- a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs @@ -86,8 +86,8 @@ namespace Content.Server.Nutrition.EntitySystems if (food == user || EntityManager.TryGetComponent(food, out var mobState) && _mobStateSystem.IsAlive(food, mobState)) // Suppresses eating alive mobs return false; - // Target can't be fed - if (!EntityManager.HasComponent(target)) + // Target can't be fed or they're already forcefeeding + if (!EntityManager.HasComponent(target) || foodComp.ForceFeed) return false; if (!_solutionContainerSystem.TryGetSolution(food, foodComp.SolutionName, out var foodSolution)) @@ -111,9 +111,9 @@ namespace Content.Server.Nutrition.EntitySystems if (!_interactionSystem.InRangeUnobstructed(user, food, popup: true)) return true; - var forceFeed = user != target; + foodComp.ForceFeed = user != target; - if (forceFeed) + if (foodComp.ForceFeed) { var userName = Identity.Entity(user, EntityManager); _popupSystem.PopupEntity(Loc.GetString("food-system-force-feed", ("user", userName)), @@ -128,16 +128,16 @@ namespace Content.Server.Nutrition.EntitySystems _adminLogger.Add(LogType.Ingestion, LogImpact.Low, $"{ToPrettyString(target):target} is eating {ToPrettyString(food):food} {SolutionContainerSystem.ToPrettyString(foodSolution)}"); } - var moveBreak = user != target; - var foodData = new FoodData(foodSolution, flavors, utensils); - var doAfterEventArgs = new DoAfterEventArgs(user, forceFeed ? foodComp.ForceFeedDelay : foodComp.Delay, target: target, used: food) + var doAfterEventArgs = new DoAfterEventArgs(user, foodComp.ForceFeed ? foodComp.ForceFeedDelay : foodComp.Delay, target: target, used: food) { - BreakOnUserMove = moveBreak, + RaiseOnTarget = foodComp.ForceFeed, + RaiseOnUser = !foodComp.ForceFeed, + BreakOnUserMove = foodComp.ForceFeed, BreakOnDamage = true, BreakOnStun = true, - BreakOnTargetMove = moveBreak, + BreakOnTargetMove = foodComp.ForceFeed, MovementThreshold = 0.01f, DistanceThreshold = 1.0f, NeedHand = true @@ -151,6 +151,13 @@ namespace Content.Server.Nutrition.EntitySystems private void OnDoAfter(EntityUid uid, FoodComponent component, DoAfterEvent args) { + //Prevents the target from being force fed food but allows the user to chow down + if (args.Cancelled && component.ForceFeed) + { + component.ForceFeed = false; + return; + } + if (args.Cancelled || args.Handled || component.Deleted || args.Args.Target == null) return; @@ -166,13 +173,11 @@ namespace Content.Server.Nutrition.EntitySystems //TODO: Get the stomach UID somehow without nabbing owner var firstStomach = stomachs.FirstOrNull(stomach => _stomachSystem.CanTransferSolution(stomach.Comp.Owner, split)); - 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.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); + _popupSystem.PopupEntity(component.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; } @@ -182,7 +187,7 @@ namespace Content.Server.Nutrition.EntitySystems var flavors = args.AdditionalData.FlavorMessage; - if (forceFeed) + if (component.ForceFeed) { var targetName = Identity.Entity(args.Args.Target.Value, EntityManager); var userName = Identity.Entity(args.Args.User, EntityManager); diff --git a/Content.Server/Tools/Components/TilePryingComponent.cs b/Content.Server/Tools/Components/TilePryingComponent.cs index 82566e3559..9dee4fc004 100644 --- a/Content.Server/Tools/Components/TilePryingComponent.cs +++ b/Content.Server/Tools/Components/TilePryingComponent.cs @@ -15,5 +15,8 @@ namespace Content.Server.Tools.Components [DataField("delay")] public float Delay = 1f; + + [DataField("cancelToken")] + public CancellationTokenSource? CancelToken; } } diff --git a/Content.Server/Tools/ToolSystem.TilePrying.cs b/Content.Server/Tools/ToolSystem.TilePrying.cs index 1af417a533..ea257548d3 100644 --- a/Content.Server/Tools/ToolSystem.TilePrying.cs +++ b/Content.Server/Tools/ToolSystem.TilePrying.cs @@ -19,10 +19,20 @@ public sealed partial class ToolSystem { SubscribeLocalEvent(OnTilePryingAfterInteract); SubscribeLocalEvent(OnTilePryComplete); + SubscribeLocalEvent(OnTilePryCancelled); + } + + private void OnTilePryingAfterInteract(EntityUid uid, TilePryingComponent component, AfterInteractEvent args) + { + if (args.Handled || !args.CanReach || (args.Target != null && !HasComp(args.Target))) return; + + if (TryPryTile(uid, args.User, component, args.ClickLocation)) + args.Handled = true; } 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)) { @@ -34,17 +44,14 @@ public sealed partial class ToolSystem _tile.PryTile(tile); } - private void OnTilePryingAfterInteract(EntityUid uid, TilePryingComponent component, AfterInteractEvent args) + private void OnTilePryCancelled(EntityUid uid, TilePryingComponent component, TilePryingCancelledEvent args) { - if (args.Handled || !args.CanReach || (args.Target != null && !HasComp(args.Target))) return; - - if (TryPryTile(uid, args.User, component, args.ClickLocation)) - args.Handled = true; + component.CancelToken = null; } private bool TryPryTile(EntityUid toolEntity, EntityUid user, TilePryingComponent component, EntityCoordinates clickLocation) { - if (!TryComp(toolEntity, out var tool) && component.ToolComponentNeeded) + if (!TryComp(toolEntity, out var tool) && component.ToolComponentNeeded || component.CancelToken != null) return false; if (!_mapManager.TryGetGrid(clickLocation.GetGridUid(EntityManager), out var mapGrid)) @@ -62,9 +69,11 @@ public sealed partial class ToolSystem if (!tileDef.CanCrowbar) return false; - var toolEvData = new ToolEventData(new TilePryingCompleteEvent(clickLocation), targetEntity:toolEntity); + component.CancelToken = new CancellationTokenSource(); - if (!UseTool(toolEntity, user, null, component.Delay, new[] { component.QualityNeeded }, toolEvData, toolComponent: tool)) + var toolEvData = new ToolEventData(new TilePryingCompleteEvent(clickLocation), cancelledEv:new TilePryingCancelledEvent() ,targetEntity:toolEntity); + + if (!UseTool(toolEntity, user, null, component.Delay, new[] { component.QualityNeeded }, toolEvData, toolComponent: tool, cancelToken: component.CancelToken)) return false; return true; diff --git a/Content.Shared/Chemistry/Components/SharedInjectorComponent.cs b/Content.Shared/Chemistry/Components/SharedInjectorComponent.cs index e90126689e..7c81495f08 100644 --- a/Content.Shared/Chemistry/Components/SharedInjectorComponent.cs +++ b/Content.Shared/Chemistry/Components/SharedInjectorComponent.cs @@ -10,6 +10,12 @@ namespace Content.Shared.Chemistry.Components [NetworkedComponent, ComponentProtoName("Injector")] public abstract class SharedInjectorComponent : Component { + /// + /// Checks to see if the entity being injected + /// + [DataField("isInjecting")] + public bool IsInjecting; + /// /// Component data used for net updates. Used by client for item status ui /// diff --git a/Content.Shared/Tools/Systems/SharedToolSystem.MultipleTool.cs b/Content.Shared/Tools/Systems/SharedToolSystem.MultipleTool.cs index b723395885..3006c95b89 100644 --- a/Content.Shared/Tools/Systems/SharedToolSystem.MultipleTool.cs +++ b/Content.Shared/Tools/Systems/SharedToolSystem.MultipleTool.cs @@ -1,4 +1,5 @@ using System.Linq; +using System.Threading; using Content.Shared.Audio; using Content.Shared.DoAfter; using Content.Shared.Interaction; @@ -31,9 +32,24 @@ public abstract class SharedToolSystem : EntitySystem private void OnDoAfter(EntityUid uid, ToolComponent component, DoAfterEvent args) { - if (args.Handled || args.Cancelled || args.AdditionalData.Ev == null) + if (args.Handled || args.AdditionalData.Ev == null) return; + if (args.Cancelled) + { + 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; + } + + return; + } + if (ToolFinishUse(uid, args.Args.User, args.AdditionalData.Fuel)) { if (args.AdditionalData.TargetEntity != null) @@ -43,18 +59,9 @@ public abstract class SharedToolSystem : EntitySystem 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 bool UseTool(EntityUid tool, EntityUid user, EntityUid? target, float doAfterDelay, IEnumerable toolQualitiesNeeded, ToolEventData toolEventData, float fuel = 0f, ToolComponent? toolComponent = null, Func? doAfterCheck = null) + public bool UseTool(EntityUid tool, EntityUid user, EntityUid? target, float doAfterDelay, IEnumerable toolQualitiesNeeded, ToolEventData toolEventData, float fuel = 0f, ToolComponent? toolComponent = null, Func? doAfterCheck = null, CancellationTokenSource? 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)) @@ -70,7 +77,7 @@ public abstract class SharedToolSystem : EntitySystem if (doAfterDelay > 0f) { - var doAfterArgs = new DoAfterEventArgs(user, doAfterDelay / toolComponent.SpeedModifier, target:target, used:tool) + var doAfterArgs = new DoAfterEventArgs(user, doAfterDelay / toolComponent.SpeedModifier, cancelToken:cancelToken?.Token ?? default, target:target, used:tool) { ExtraCheck = doAfterCheck, BreakOnDamage = true,