diff --git a/Content.Server/Chemistry/EntitySystems/SolutionSpikableSystem.cs b/Content.Server/Chemistry/EntitySystems/SolutionSpikableSystem.cs index e27063b1b5..04aa6546a9 100644 --- a/Content.Server/Chemistry/EntitySystems/SolutionSpikableSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/SolutionSpikableSystem.cs @@ -61,35 +61,11 @@ public sealed class SolutionSpikableSystem : EntitySystem return; } - if (_solutionSystem.TryMixAndOverflow(target, - targetSolution, - sourceSolution, - targetSolution.MaxVolume, - out var overflow)) - { - if (overflow.Volume > 0) - { - RaiseLocalEvent(target, new SolutionSpikeOverflowEvent(overflow)); - } + if (!_solutionSystem.ForceAddSolution(target, targetSolution, sourceSolution)) + return; - _popupSystem.PopupEntity(Loc.GetString(spikableSource.Popup, ("spiked-entity", target), ("spike-entity", source)), user, user); - - sourceSolution.RemoveAllSolution(); - - _triggerSystem.Trigger(source, user); - } - } -} - -public sealed class SolutionSpikeOverflowEvent : HandledEntityEventArgs -{ - /// - /// The solution that's been overflowed from the spike. - /// - public Solution Overflow { get; } - - public SolutionSpikeOverflowEvent(Solution overflow) - { - Overflow = overflow; + _popupSystem.PopupEntity(Loc.GetString(spikableSource.Popup, ("spiked-entity", target), ("spike-entity", source)), user, user); + sourceSolution.RemoveAllSolution(); + _triggerSystem.Trigger(source, user); } } diff --git a/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs b/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs index 39485d60f7..a6d6a5b204 100644 --- a/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs +++ b/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs @@ -34,7 +34,7 @@ public sealed partial class PuddleSystem SubscribeLocalEvent(SplashOnMeleeHit, after: new[] { typeof(OpenableSystem) }); SubscribeLocalEvent>(AddSpillVerb); SubscribeLocalEvent(OnGotEquipped); - SubscribeLocalEvent(OnSpikeOverflow); + SubscribeLocalEvent(OnOverflow); SubscribeLocalEvent(OnDoAfter); } @@ -46,13 +46,12 @@ public sealed partial class PuddleSystem args.PushMarkup(Loc.GetString("spill-examine-spillable-weapon")); } - private void OnSpikeOverflow(EntityUid uid, SpillableComponent component, SolutionSpikeOverflowEvent args) + private void OnOverflow(EntityUid uid, SpillableComponent component, ref SolutionOverflowEvent args) { - if (!args.Handled) - { - TrySpillAt(Transform(uid).Coordinates, args.Overflow, out _); - } + if (args.Handled) + return; + TrySpillAt(Transform(uid).Coordinates, args.Overflow, out _); args.Handled = true; } diff --git a/Content.Shared/Chemistry/EntitySystems/SolutionContainerSystem.cs b/Content.Shared/Chemistry/EntitySystems/SolutionContainerSystem.cs index d4c599557b..a5c35b5e6c 100644 --- a/Content.Shared/Chemistry/EntitySystems/SolutionContainerSystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/SolutionContainerSystem.cs @@ -30,6 +30,24 @@ public sealed class SolutionChangedEvent : EntityEventArgs } } +/// +/// An event raised when more reagents are added to a (managed) solution than it can hold. +/// +[ByRefEvent] +public record struct SolutionOverflowEvent(EntityUid SolutionEnt, Solution SolutionHolder, Solution Overflow) +{ + /// The entity which contains the solution that has overflowed. + public readonly EntityUid SolutionEnt = SolutionEnt; + /// The solution that has overflowed. + public readonly Solution SolutionHolder = SolutionHolder; + /// The reagents that have overflowed the solution. + public readonly Solution Overflow = Overflow; + /// The volume by which the solution has overflowed. + public readonly FixedPoint2 OverflowVol = Overflow.Volume; + /// Whether some subscriber has taken care of the effects of the overflow. + public bool Handled = false; +} + /// /// Part of Chemistry system deal with SolutionContainers /// @@ -265,6 +283,14 @@ public sealed partial class SolutionContainerSystem : EntitySystem _chemistrySystem.FullyReactSolution(solutionHolder, uid, solutionHolder.MaxVolume, mixerComponent); } + var overflowVol = solutionHolder.Volume - solutionHolder.MaxVolume; + if (overflowVol > FixedPoint2.Zero) + { + var overflow = solutionHolder.SplitSolution(overflowVol); + var overflowEv = new SolutionOverflowEvent(uid, solutionHolder, overflow); + RaiseLocalEvent(uid, ref overflowEv); + } + UpdateAppearance(uid, solutionHolder); RaiseLocalEvent(uid, new SolutionChangedEvent(solutionHolder, solutionHolder.Name)); } @@ -417,24 +443,6 @@ public sealed partial class SolutionContainerSystem : EntitySystem return RemoveReagent(targetUid, container, new ReagentQuantity(reagentId, quantity)); } - /// - /// Adds a solution to the container, if it can fully fit. - /// - /// entity holding targetSolution - /// entity holding targetSolution - /// solution being added - /// If the solution could be added. - public bool TryAddSolution(EntityUid targetUid, Solution? targetSolution, Solution addedSolution) - { - if (targetSolution == null - || !targetSolution.CanAddSolution(addedSolution) || addedSolution.Volume == 0) - return false; - - targetSolution.AddSolution(addedSolution, _prototypeManager); - UpdateChemicals(targetUid, targetSolution, true); - return true; - } - /// /// Moves some quantity of a solution from one solution to another. /// @@ -497,32 +505,86 @@ public sealed partial class SolutionContainerSystem : EntitySystem } /// - /// Adds a solution to the container, overflowing the rest. - /// It will - /// Unlike it will ignore size limits. + /// Adds a solution to the container, if it can fully fit. /// /// entity holding targetSolution - /// The container to which we try to add. - /// solution being added - /// After addition this much will be left in targetSolution. Should be less - /// than targetSolution.TotalVolume + /// entity holding targetSolution + /// solution being added + /// If the solution could be added. + public bool TryAddSolution(EntityUid targetUid, Solution targetSolution, Solution toAdd) + { + if (toAdd.Volume == FixedPoint2.Zero) + return true; + if (toAdd.Volume > targetSolution.AvailableVolume) + return false; + + ForceAddSolution(targetUid, targetSolution, toAdd); + return true; + } + + /// + /// Adds as much of a solution to a container as can fit. + /// + /// The entity containing + /// The solution being added to. + /// The solution being added to + /// The quantity of the solution actually added. + public FixedPoint2 AddSolution(EntityUid targetUid, Solution targetSolution, Solution toAdd) + { + if (toAdd.Volume == FixedPoint2.Zero) + return FixedPoint2.Zero; + + var quantity = FixedPoint2.Max(FixedPoint2.Zero, FixedPoint2.Min(toAdd.Volume, targetSolution.AvailableVolume)); + if (quantity < toAdd.Volume) + TryTransferSolution(targetUid, targetSolution, toAdd, quantity); + else + ForceAddSolution(targetUid, targetSolution, toAdd); + + return quantity; + } + + /// + /// Adds a solution to a container and updates the container. + /// + /// The entity containing + /// The solution being added to. + /// The solution being added to + /// Whether any reagents were added to the solution. + public bool ForceAddSolution(EntityUid targetUid, Solution targetSolution, Solution toAdd) + { + if (toAdd.Volume == FixedPoint2.Zero) + return false; + + targetSolution.AddSolution(toAdd, _prototypeManager); + UpdateChemicals(targetUid, targetSolution, needsReactionsProcessing: true); + return true; + } + + /// + /// Adds a solution to the container, removing the overflow. + /// Unlike it will ignore size limits. + /// + /// The entity containing + /// The solution being added to. + /// The solution being added to + /// The combined volume above which the overflow will be returned. + /// If the combined volume is below this an empty solution is returned. /// Solution that exceeded overflowThreshold - /// + /// Whether any reagents were added to . public bool TryMixAndOverflow(EntityUid targetUid, Solution targetSolution, - Solution addedSolution, + Solution toAdd, FixedPoint2 overflowThreshold, [NotNullWhen(true)] out Solution? overflowingSolution) { - if (addedSolution.Volume == 0 || overflowThreshold > targetSolution.MaxVolume) + if (toAdd.Volume == 0 || overflowThreshold > targetSolution.MaxVolume) { overflowingSolution = null; return false; } - targetSolution.AddSolution(addedSolution, _prototypeManager); + targetSolution.AddSolution(toAdd, _prototypeManager); + overflowingSolution = targetSolution.SplitSolution(FixedPoint2.Max(FixedPoint2.Zero, targetSolution.Volume - overflowThreshold)); UpdateChemicals(targetUid, targetSolution, true); - overflowingSolution = targetSolution.SplitSolution(FixedPoint2.Max(FixedPoint2.Zero, - targetSolution.Volume - overflowThreshold)); return true; } @@ -778,4 +840,8 @@ public sealed partial class SolutionContainerSystem : EntitySystem } #endregion Thermal Energy and Temperature + + #region Event Handlers + + #endregion Event Handlers } diff --git a/Content.Shared/Chemistry/Reaction/ChemicalReactionSystem.cs b/Content.Shared/Chemistry/Reaction/ChemicalReactionSystem.cs index 4ec50bc61a..ed53b78466 100644 --- a/Content.Shared/Chemistry/Reaction/ChemicalReactionSystem.cs +++ b/Content.Shared/Chemistry/Reaction/ChemicalReactionSystem.cs @@ -262,12 +262,6 @@ namespace Content.Shared.Chemistry.Reaction if (products.Count == 0) return true; - // remove excess product - // TODO spill excess? - var excessVolume = solution.Volume - maxVolume; - if (excessVolume > 0) - solution.RemoveSolution(excessVolume); - // Add any reactions associated with the new products. This may re-add reactions that were already iterated // over previously. The new product may mean the reactions are applicable again and need to be processed. foreach (var product in products) @@ -279,16 +273,10 @@ namespace Content.Shared.Chemistry.Reaction return true; } - /// - /// Continually react a solution until no more reactions occur. - /// - public void FullyReactSolution(Solution solution, EntityUid owner) => FullyReactSolution(solution, owner, FixedPoint2.MaxValue, null); - /// /// Continually react a solution until no more reactions occur, with a volume constraint. - /// If a reaction's products would exceed the max volume, some product is deleted. /// - public void FullyReactSolution(Solution solution, EntityUid owner, FixedPoint2 maxVolume, ReactionMixerComponent? mixerComponent) + public void FullyReactSolution(Solution solution, EntityUid owner, FixedPoint2 maxVolume, ReactionMixerComponent? mixerComponent = null) { // construct the initial set of reactions to check. SortedSet reactions = new(); diff --git a/Resources/Prototypes/Recipes/Reactions/cleaning.yml b/Resources/Prototypes/Recipes/Reactions/cleaning.yml index 8ae72853be..1c68aeb2e3 100644 --- a/Resources/Prototypes/Recipes/Reactions/cleaning.yml +++ b/Resources/Prototypes/Recipes/Reactions/cleaning.yml @@ -31,4 +31,4 @@ Oxygen: amount: 1 products: - SpaceLube: 3 + SpaceLube: 5