From 857ddff7cb08e4c012c4df05cd59d8caa8f83850 Mon Sep 17 00:00:00 2001 From: Milon Date: Mon, 22 Dec 2025 08:59:30 +0100 Subject: [PATCH] fix solution contents duplication on spill behavior (#33231) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * I’M SCREAMING INTO THE VOID AND IT’S NOT LISTENING * review * explodes pancakes with mind * graaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa * Meteors RAAAAAAAAAAH * I'm so tired of solutions * whhop * revert --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- Content.Client/Fluids/PuddleSystem.cs | 14 +++++- .../Thresholds/Behaviors/SpillBehavior.cs | 34 +++----------- .../EntitySystems/PuddleSystem.Spillable.cs | 13 +---- .../Fluids/EntitySystems/PuddleSystem.cs | 47 ++++++++++++++----- Content.Shared/Fluids/SharedPuddleSystem.cs | 13 ++++- .../PressurizedSolutionSystem.cs | 4 +- 6 files changed, 69 insertions(+), 56 deletions(-) diff --git a/Content.Client/Fluids/PuddleSystem.cs b/Content.Client/Fluids/PuddleSystem.cs index 1ab1ded3e8e..ab96d24d85a 100644 --- a/Content.Client/Fluids/PuddleSystem.cs +++ b/Content.Client/Fluids/PuddleSystem.cs @@ -73,7 +73,19 @@ public sealed class PuddleSystem : SharedPuddleSystem // Maybe someday we'll have clientside prediction for entity spawning, but not today. // Until then, these methods do nothing on the client. /// - public override bool TrySplashSpillAt(EntityUid uid, EntityCoordinates coordinates, Solution solution, out EntityUid puddleUid, bool sound = true, EntityUid? user = null) + public override bool TrySplashSpillAt(Entity entity, EntityCoordinates coordinates, out EntityUid puddleUid, out Solution solution, bool sound = true, EntityUid? user = null) + { + puddleUid = EntityUid.Invalid; + solution = new Solution(); + return false; + } + + public override bool TrySplashSpillAt(EntityUid entity, + EntityCoordinates coordinates, + Solution spilled, + out EntityUid puddleUid, + bool sound = true, + EntityUid? user = null) { puddleUid = EntityUid.Invalid; return false; diff --git a/Content.Server/Destructible/Thresholds/Behaviors/SpillBehavior.cs b/Content.Server/Destructible/Thresholds/Behaviors/SpillBehavior.cs index d45fe707ab2..ad34f05edd0 100644 --- a/Content.Server/Destructible/Thresholds/Behaviors/SpillBehavior.cs +++ b/Content.Server/Destructible/Thresholds/Behaviors/SpillBehavior.cs @@ -1,7 +1,5 @@ using Content.Server.Fluids.EntitySystems; -using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.EntitySystems; -using Content.Shared.Fluids.Components; using JetBrains.Annotations; namespace Content.Server.Destructible.Thresholds.Behaviors; @@ -27,34 +25,14 @@ public sealed partial class SpillBehavior : IThresholdBehavior /// Optional entity that caused this behavior to trigger public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause = null) { - var solutionContainerSystem = system.EntityManager.System(); - var spillableSystem = system.EntityManager.System(); + var puddleSystem = system.EntityManager.System(); + var solutionContainer = system.EntityManager.System(); var coordinates = system.EntityManager.GetComponent(owner).Coordinates; - Solution targetSolution; - - // First try to get solution from SpillableComponent - if (system.EntityManager.TryGetComponent(owner, out SpillableComponent? spillableComponent) && - solutionContainerSystem.TryGetSolution(owner, spillableComponent.SolutionName, out var solution, out var compSolution)) - { - // If entity is drainable, drain the solution. Otherwise just split it. - // Both methods ensure the solution is properly removed. - targetSolution = system.EntityManager.HasComponent(owner) - ? solutionContainerSystem.Drain((owner, system.EntityManager.GetComponent(owner)), solution.Value, compSolution.Volume) - : compSolution.SplitSolution(compSolution.Volume); - } - // Fallback to solution specified in behavior data - else if (Solution != null && - solutionContainerSystem.TryGetSolution(owner, Solution, out var solutionEnt, out var behaviorSolution)) - { - targetSolution = system.EntityManager.HasComponent(owner) - ? solutionContainerSystem.Drain((owner, system.EntityManager.GetComponent(owner)), solutionEnt.Value, behaviorSolution.Volume) - : behaviorSolution.SplitSolution(behaviorSolution.Volume); - } - else - return; - // Spill the solution that was drained/split - spillableSystem.TrySplashSpillAt(owner, coordinates, targetSolution, out _, false, cause); + if (solutionContainer.TryGetSolution(owner, Solution, out _, out var solution)) + puddleSystem.TrySplashSpillAt(owner, coordinates, solution, out _, false, cause); + else + puddleSystem.TrySplashSpillAt(owner, coordinates, out _, out _, false, cause); } } diff --git a/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs b/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs index 76f88186f7a..2e3210f6712 100644 --- a/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs +++ b/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs @@ -32,16 +32,10 @@ public sealed partial class PuddleSystem private void SpillOnLand(Entity entity, ref LandEvent args) { - if (!_solutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var soln, out var solution)) + if (!entity.Comp.SpillWhenThrown || Openable.IsClosed(entity.Owner)) return; - if (Openable.IsClosed(entity.Owner)) - return; - - if (!entity.Comp.SpillWhenThrown) - return; - - if (args.User != null) + if (TrySplashSpillAt(entity.Owner, Transform(entity).Coordinates, out _, out var solution) && args.User != null) { // DeltaV - Beer Goggles Safe Throw if (_safeSolutionThrower.GetSafeThrow(args.User.Value)) @@ -55,9 +49,6 @@ public sealed partial class PuddleSystem AdminLogger.Add(LogType.Landed, $"{ToPrettyString(entity.Owner):entity} spilled a solution {SharedSolutionContainerSystem.ToPrettyString(solution):solution} on landing"); } - - var drainedSolution = _solutionContainerSystem.Drain(entity.Owner, soln.Value, solution.Volume); - TrySplashSpillAt(entity.Owner, Transform(entity).Coordinates, drainedSolution, out _); } private void OnDoAfter(Entity entity, ref SpillDoAfterEvent args) diff --git a/Content.Server/Fluids/EntitySystems/PuddleSystem.cs b/Content.Server/Fluids/EntitySystems/PuddleSystem.cs index 2f966354eca..c756a8e6963 100644 --- a/Content.Server/Fluids/EntitySystems/PuddleSystem.cs +++ b/Content.Server/Fluids/EntitySystems/PuddleSystem.cs @@ -369,16 +369,37 @@ public sealed partial class PuddleSystem : SharedPuddleSystem // TODO: This can be predicted once https://github.com/space-wizards/RobustToolbox/pull/5849 is merged /// - public override bool TrySplashSpillAt(EntityUid uid, + public override bool TrySplashSpillAt(Entity entity, EntityCoordinates coordinates, - Solution solution, + out EntityUid puddleUid, + out Solution spilled, + bool sound = true, + EntityUid? user = null) + { + puddleUid = EntityUid.Invalid; + spilled = new Solution(); + + if (!Resolve(entity, ref entity.Comp)) + return false; + + if (!_solutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var solution)) + return false; + + spilled = solution.Value.Comp.Solution; + + return TrySplashSpillAt(entity, coordinates, spilled, out puddleUid, sound, user); + } + + public override bool TrySplashSpillAt(EntityUid entity, + EntityCoordinates coordinates, + Solution spilled, out EntityUid puddleUid, bool sound = true, EntityUid? user = null) { puddleUid = EntityUid.Invalid; - if (solution.Volume == 0) + if (spilled.Volume == 0) return false; var targets = new List(); @@ -392,26 +413,28 @@ public sealed partial class PuddleSystem : SharedPuddleSystem var owner = ent.Owner; // between 5 and 30% - var splitAmount = solution.Volume * _random.NextFloat(0.05f, 0.30f); - var splitSolution = solution.SplitSolution(splitAmount); + var splitAmount = spilled.Volume * _random.NextFloat(0.05f, 0.30f); + var splitSolution = spilled.SplitSolution(splitAmount); if (user != null) { AdminLogger.Add(LogType.Landed, - $"{ToPrettyString(user.Value):user} threw {ToPrettyString(uid):entity} which splashed a solution {SharedSolutionContainerSystem.ToPrettyString(solution):solution} onto {ToPrettyString(owner):target}"); + $"{ToPrettyString(user.Value):user} threw {ToPrettyString(entity):entity} which splashed a solution {SharedSolutionContainerSystem.ToPrettyString(spilled):solution} onto {ToPrettyString(owner):target}"); } targets.Add(owner); Reactive.DoEntityReaction(owner, splitSolution, ReactionMethod.Touch); - Popups.PopupEntity( - Loc.GetString("spill-land-spilled-on-other", ("spillable", uid), - ("target", Identity.Entity(owner, EntityManager))), owner, PopupType.SmallCaution); + Popups.PopupEntity(Loc.GetString("spill-land-spilled-on-other", + ("spillable", entity), + ("target", Identity.Entity(owner, EntityManager))), + owner, + PopupType.SmallCaution); } - _color.RaiseEffect(solution.GetColor(_prototypeManager), targets, - Filter.Pvs(uid, entityManager: EntityManager)); + _color.RaiseEffect(spilled.GetColor(_prototypeManager), targets, + Filter.Pvs(entity, entityManager: EntityManager)); - return TrySpillAt(coordinates, solution, out puddleUid, sound); + return TrySpillAt(coordinates, spilled, out puddleUid, sound); } /// diff --git a/Content.Shared/Fluids/SharedPuddleSystem.cs b/Content.Shared/Fluids/SharedPuddleSystem.cs index e81d1c9d114..8f87734717d 100644 --- a/Content.Shared/Fluids/SharedPuddleSystem.cs +++ b/Content.Shared/Fluids/SharedPuddleSystem.cs @@ -354,6 +354,15 @@ public abstract partial class SharedPuddleSystem : EntitySystem } #region Spill + + /// + public abstract bool TrySplashSpillAt(Entity entity, + EntityCoordinates coordinates, + out EntityUid puddleUid, + out Solution spilled, + bool sound = true, + EntityUid? user = null); + // These methods are in Shared to make it easier to interact with PuddleSystem in Shared code. // Note that they always fail when run on the client, not creating a puddle and returning false. // Adding proper prediction to this system would require spawning temporary puddle entities on the @@ -367,9 +376,9 @@ public abstract partial class SharedPuddleSystem : EntitySystem /// /// On the client, this will always set to and return false. /// - public abstract bool TrySplashSpillAt(EntityUid uid, + public abstract bool TrySplashSpillAt(EntityUid entity, EntityCoordinates coordinates, - Solution solution, + Solution spilled, out EntityUid puddleUid, bool sound = true, EntityUid? user = null); diff --git a/Content.Shared/Nutrition/EntitySystems/PressurizedSolutionSystem.cs b/Content.Shared/Nutrition/EntitySystems/PressurizedSolutionSystem.cs index 82d90f4c928..9a0e3e79fa6 100644 --- a/Content.Shared/Nutrition/EntitySystems/PressurizedSolutionSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/PressurizedSolutionSystem.cs @@ -179,8 +179,8 @@ public sealed partial class PressurizedSolutionSystem : EntitySystem var solution = _solutionContainer.SplitSolution(soln.Value, interactions.Volume); // Spray the solution onto the ground and anyone nearby - if (TryComp(entity, out TransformComponent? transform)) - _puddle.TrySplashSpillAt(entity, transform.Coordinates, solution, out _, sound: false); + var coordinates = Transform(entity).Coordinates; + _puddle.TrySplashSpillAt(entity.Owner, coordinates, out _, out _, sound: false); var drinkName = Identity.Entity(entity, EntityManager);