diff --git a/Content.Server/Fluids/Components/SpillableComponent.cs b/Content.Server/Fluids/Components/SpillableComponent.cs index 6ccb3d5645..2d1f23d5c8 100644 --- a/Content.Server/Fluids/Components/SpillableComponent.cs +++ b/Content.Server/Fluids/Components/SpillableComponent.cs @@ -1,3 +1,5 @@ +using System.Threading; + namespace Content.Server.Fluids.Components; [RegisterComponent] @@ -12,4 +14,9 @@ public sealed class SpillableComponent : Component /// [DataField("spillWorn")] public bool SpillWorn = true; + + [DataField("spillDelay")] + public float? SpillDelay; + + public CancellationTokenSource? CancelToken; } diff --git a/Content.Server/Fluids/EntitySystems/SpillableSystem.cs b/Content.Server/Fluids/EntitySystems/SpillableSystem.cs index c455809f22..c3c9819724 100644 --- a/Content.Server/Fluids/EntitySystems/SpillableSystem.cs +++ b/Content.Server/Fluids/EntitySystems/SpillableSystem.cs @@ -1,8 +1,10 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Threading; using Content.Server.Administration.Logs; using Content.Server.Chemistry.EntitySystems; using Content.Server.Clothing.Components; +using Content.Server.DoAfter; using Content.Server.Fluids.Components; using Content.Server.Nutrition.Components; using Content.Server.Nutrition.EntitySystems; @@ -28,6 +30,7 @@ public sealed class SpillableSystem : EntitySystem [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly EntityLookupSystem _entityLookup = default!; [Dependency] private readonly IAdminLogManager _adminLogger= default!; + [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; public override void Initialize() { @@ -36,6 +39,8 @@ public sealed class SpillableSystem : EntitySystem SubscribeLocalEvent>(AddSpillVerb); SubscribeLocalEvent(OnGotEquipped); SubscribeLocalEvent(OnSpikeOverflow); + SubscribeLocalEvent(OnSpillFinished); + SubscribeLocalEvent(OnSpillCancelled); } private void OnSpikeOverflow(EntityUid uid, SpillableComponent component, SolutionSpikeOverflowEvent args) @@ -125,12 +130,35 @@ public sealed class SpillableSystem : EntitySystem Verb verb = new(); verb.Text = Loc.GetString("spill-target-verb-get-data-text"); // TODO VERB ICONS spill icon? pouring out a glass/beaker? - verb.Act = () => + if (component.SpillDelay == null) { - var puddleSolution = _solutionContainerSystem.SplitSolution(args.Target, - solution, solution.DrainAvailable); - SpillAt(puddleSolution, Transform(args.Target).Coordinates, "PuddleSmear"); - }; + verb.Act = () => + { + var puddleSolution = _solutionContainerSystem.SplitSolution(args.Target, + solution, solution.DrainAvailable); + SpillAt(puddleSolution, Transform(args.Target).Coordinates, "PuddleSmear"); + }; + } + else + { + verb.Act = () => + { + if (component.CancelToken == null) + { + 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) + }); + } + }; + } verb.Impact = LogImpact.Medium; // dangerous reagent reaction are logged separately. args.Verbs.Add(verb); } @@ -234,4 +262,47 @@ public sealed class SpillableSystem : EntitySystem return puddleComponent; } + + private void OnSpillFinished(EntityUid uid, SpillableComponent component, SpillFinishedEvent ev) + { + component.CancelToken = null; + + //solution gone by other means before doafter completes + if (ev.Solution == null || ev.Solution.CurrentVolume == 0) + return; + + var puddleSolution = _solutionContainerSystem.SplitSolution(uid, + ev.Solution, ev.Solution.DrainAvailable); + + 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; } + } + + private sealed class SpillCancelledEvent : EntityEventArgs + { + public EntityUid Spillable; + + public SpillCancelledEvent(EntityUid spillable) + { + Spillable = spillable; + } + } } diff --git a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml index cc2c44b2ea..8e1e63a2de 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml @@ -46,6 +46,7 @@ - ReagentId: Water Quantity: 250 # half-full at roundstart to leave room for puddles - type: Spillable + spillDelay: 3.0 - type: DrainableSolution solution: bucket - type: RefillableSolution @@ -141,6 +142,7 @@ mask: - MobMask - type: Spillable + spillDelay: 3.0 - type: SolutionContainerManager solutions: bucket: