diff --git a/Content.Server/Chemistry/EntitySystems/SolutionContainerSystem.cs b/Content.Server/Chemistry/EntitySystems/SolutionContainerSystem.cs index 1d39a7996c..2e6b8114ba 100644 --- a/Content.Server/Chemistry/EntitySystems/SolutionContainerSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/SolutionContainerSystem.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using Content.Server.Chemistry.Components.SolutionManager; +using Content.Server.Fluids.Components; using Content.Shared.Chemistry; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reaction; @@ -213,17 +214,46 @@ namespace Content.Server.Chemistry.EntitySystems /// /// Adds a solution to the container, if it can fully fit. /// - /// - /// The container to which we try to add. - /// The solution to try to add. + /// entity holding targetSolution + /// entity holding targetSolution + /// solution being added /// If the solution could be added. - public bool TryAddSolution(EntityUid targetUid, Solution? targetSolution, Solution solution) + public bool TryAddSolution(EntityUid targetUid, Solution? targetSolution, Solution addedSolution) { - if (targetSolution == null || !targetSolution.CanAddSolution(solution) || solution.TotalVolume == 0) + if (targetSolution == null + || !targetSolution.CanAddSolution(addedSolution) || targetSolution.TotalVolume == 0) return false; - targetSolution.AddSolution(solution); + addedSolution.AddSolution(targetSolution); + UpdateChemicals(targetUid, addedSolution, true); + return true; + } + + /// + /// Adds a solution to the container, overflowing the rest. + /// Unlike TryAddSolution it will ignore size limits. + /// + /// entity holding targetSolution + /// The container to which we try to add. + /// solution being added + /// After addition this much will be left in targetSolution + /// Solution that exceeded overflowThreshold + /// + public bool TryMixAndOverflow(EntityUid targetUid, Solution targetSolution, + Solution addedSolution, + FixedPoint2 overflowThreshold, + [NotNullWhen(true)] out Solution? overflowingSolution) + { + if (addedSolution.TotalVolume == 0) + { + overflowingSolution = null; + return false; + } + + targetSolution.AddSolution(addedSolution); UpdateChemicals(targetUid, targetSolution, true); + overflowingSolution = targetSolution.SplitSolution(FixedPoint2.Max(FixedPoint2.Zero, + targetSolution.CurrentVolume - overflowThreshold)); return true; } @@ -295,19 +325,6 @@ namespace Content.Server.Chemistry.EntitySystems return removedReagent; } - public void TryRemoveAllReagents(EntityUid uid, Solution solution, List removeReagents) - { - if (removeReagents.Count == 0) - return; - - foreach (var reagent in removeReagents) - { - solution.RemoveReagent(reagent.ReagentId, reagent.Quantity); - } - - UpdateChemicals(uid, solution); - } - public FixedPoint2 GetReagentQuantity(EntityUid owner, string reagentId) { var reagentQuantity = FixedPoint2.New(0); diff --git a/Content.Server/Chemistry/TileReactions/SpillIfPuddlePresentTileReaction.cs b/Content.Server/Chemistry/TileReactions/SpillIfPuddlePresentTileReaction.cs index f0a1bdc3a5..95daf37beb 100644 --- a/Content.Server/Chemistry/TileReactions/SpillIfPuddlePresentTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/SpillIfPuddlePresentTileReaction.cs @@ -1,5 +1,4 @@ -using Content.Server.Fluids.Components; -using Content.Server.Fluids.EntitySystems; +using Content.Server.Fluids.EntitySystems; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reaction; using Content.Shared.Chemistry.Reagent; @@ -20,8 +19,8 @@ namespace Content.Server.Chemistry.TileReactions var spillSystem = EntitySystem.Get(); if (reactVolume < 5 || !spillSystem.TryGetPuddle(tile, out _)) return FixedPoint2.Zero; - return spillSystem.SpillAt(tile,new Solution(reagent.ID, reactVolume), "PuddleSmear", true, false, true) != null - ? reactVolume + return spillSystem.SpillAt(tile,new Solution(reagent.ID, reactVolume), "PuddleSmear", true, false, true) != null + ? reactVolume : FixedPoint2.Zero; } } diff --git a/Content.Server/Destructible/Thresholds/Behaviors/SpillBehavior.cs b/Content.Server/Destructible/Thresholds/Behaviors/SpillBehavior.cs index dbb9a8fca6..b21e0f6654 100644 --- a/Content.Server/Destructible/Thresholds/Behaviors/SpillBehavior.cs +++ b/Content.Server/Destructible/Thresholds/Behaviors/SpillBehavior.cs @@ -37,7 +37,7 @@ namespace Content.Server.Destructible.Thresholds.Behaviors else if (Solution != null && solutionContainerSystem.TryGetSolution(owner, Solution, out var behaviorSolution)) { - spillableSystem.SpillAt(behaviorSolution, coordinates, "PuddleSmear", false); + spillableSystem.SpillAt(behaviorSolution, coordinates, "PuddleSmear"); } } } diff --git a/Content.Server/Fluids/Components/FluidSpreaderComponent.cs b/Content.Server/Fluids/Components/FluidSpreaderComponent.cs new file mode 100644 index 0000000000..27ee2c4a5e --- /dev/null +++ b/Content.Server/Fluids/Components/FluidSpreaderComponent.cs @@ -0,0 +1,14 @@ +using Content.Server.Fluids.EntitySystems; +using Content.Shared.Chemistry.Components; + +namespace Content.Server.Fluids.Components; + +[RegisterComponent] +[Friend(typeof(FluidSpreaderSystem))] +public sealed class FluidSpreaderComponent : Component +{ + [ViewVariables] + public Solution OverflownSolution = default!; + + public bool Enabled { get; set; } +} diff --git a/Content.Server/Fluids/Components/PuddleComponent.cs b/Content.Server/Fluids/Components/PuddleComponent.cs index eec596d154..b7bdab7568 100644 --- a/Content.Server/Fluids/Components/PuddleComponent.cs +++ b/Content.Server/Fluids/Components/PuddleComponent.cs @@ -48,11 +48,6 @@ namespace Content.Server.Fluids.Components [DataField("spillSound")] public SoundSpecifier SpillSound = new SoundPathSpecifier("/Audio/Effects/Fluids/splat.ogg"); - /// - /// Whether or not this puddle is currently overflowing onto its neighbors - /// - public bool Overflown; - [ViewVariables(VVAccess.ReadOnly)] public FixedPoint2 CurrentVolume => EntitySystem.Get().CurrentVolume(Owner); diff --git a/Content.Server/Fluids/EntitySystems/FluidSpreaderSystem.cs b/Content.Server/Fluids/EntitySystems/FluidSpreaderSystem.cs new file mode 100644 index 0000000000..c4907bc5d6 --- /dev/null +++ b/Content.Server/Fluids/EntitySystems/FluidSpreaderSystem.cs @@ -0,0 +1,208 @@ +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Content.Server.Chemistry.EntitySystems; +using Content.Server.Fluids.Components; +using Content.Shared.Chemistry.Components; +using Content.Shared.Directions; +using Content.Shared.FixedPoint; +using Content.Shared.Physics; +using JetBrains.Annotations; +using Robust.Shared.Map; +using Robust.Shared.Physics; +using Robust.Shared.Utility; + +namespace Content.Server.Fluids.EntitySystems; + +[UsedImplicitly] +public sealed class FluidSpreaderSystem : EntitySystem +{ + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly PuddleSystem _puddleSystem = default!; + [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; + + + private float _accumulatedTimeFrame; + private HashSet _fluidSpread = new(); + + public override void Initialize() + { + SubscribeLocalEvent((uid, component, _) => + FluidSpreaderAdd(uid, component)); + } + + public void AddOverflowingPuddle(PuddleComponent puddleComponent, Solution? solution = null) + { + var puddleSolution = solution; + if (puddleSolution == null && !_solutionContainerSystem.TryGetSolution(puddleComponent.Owner, + puddleComponent.SolutionName, + out puddleSolution)) return; + + if (puddleSolution.CurrentVolume <= puddleComponent.OverflowVolume) + return; + + var spreaderComponent = EntityManager.EnsureComponent(puddleComponent.Owner); + spreaderComponent.OverflownSolution = puddleSolution; + spreaderComponent.Enabled = true; + FluidSpreaderAdd(spreaderComponent.Owner, spreaderComponent); + } + + private void FluidSpreaderAdd(EntityUid uid, FluidSpreaderComponent component) + { + if (component.Enabled) + _fluidSpread.Add(uid); + } + + public override void Update(float frameTime) + { + _accumulatedTimeFrame += frameTime; + + if (!(_accumulatedTimeFrame >= 1.0f)) + return; + + _accumulatedTimeFrame -= 1.0f; + + base.Update(frameTime); + + var remQueue = new RemQueue(); + foreach (var uid in _fluidSpread) + { + MetaDataComponent? meta = null; + + if (Paused(uid, meta)) + continue; + + // If not paused + // it's either Deleted or will be via SpreadFluid + remQueue.Add(uid); + + if (Deleted(uid, meta)) + continue; + + SpreadFluid(uid); + } + + foreach (var removeUid in remQueue) + { + _fluidSpread.Remove(removeUid); + } + } + + private void SpreadFluid(EntityUid suid) + { + PuddleComponent? puddleComponent = null; + MetaDataComponent? metadataOriginal = null; + TransformComponent? transformOrig = null; + FluidSpreaderComponent? spreader = null; + + if (!Resolve(suid, ref puddleComponent, ref metadataOriginal, ref transformOrig, ref spreader, false)) + return; + + var prototypeName = metadataOriginal.EntityPrototype!.ID; + + var puddles = new List { puddleComponent }; + var visitedTiles = new HashSet(); + + if (!_mapManager.TryGetGrid(transformOrig.GridID, out var mapGrid)) + return; + + while (puddles.Count > 0 + && spreader.OverflownSolution.CurrentVolume > FixedPoint2.Zero) + { + var nextToExpand = new List<(Vector2i, EntityUid?)>(); + + var divided = spreader.OverflownSolution.CurrentVolume / puddles.Count; + + foreach (var puddle in puddles) + { + if (puddle.CurrentVolume >= puddle.OverflowVolume) continue; + + // -puddle.OverflowLeft is guaranteed to be >= 0 + // iff puddle.CurrentVolume >= puddle.OverflowVolume + var split = FixedPoint2.Min(divided, -puddle.OverflowLeft); + _puddleSystem.TryAddSolution( + puddle.Owner, + spreader.OverflownSolution.SplitSolution(split), + false, false, puddle); + } + + // if solution is spent do not explore + if (spreader.OverflownSolution.CurrentVolume <= FixedPoint2.Zero) + continue; + + // find edges + foreach (var puddle in puddles) + { + TransformComponent? transform = null; + + if (!Resolve(puddle.Owner, ref transform, false)) + continue; + + // prepare next set of puddles to be expanded + var puddlePos = transform.Coordinates.ToVector2i(EntityManager, _mapManager); + foreach (var direction in SharedDirectionExtensions.RandomDirections().ToArray()) + { + var newPos = puddlePos.Offset(direction); + if (visitedTiles.Contains(newPos)) + continue; + + visitedTiles.Add(newPos); + + if (CanExpand(newPos, mapGrid, out var uid)) + nextToExpand.Add((newPos, uid)); + } + } + + puddles = new List(); + + // prepare edges for next iteration + foreach (var (pos, uid) in nextToExpand) + { + if (spreader.OverflownSolution.CurrentVolume <= FixedPoint2.Zero) + continue; + + var puddleUid = uid!.Value; + var coordinate = mapGrid.GridTileToWorld(pos); + if (uid == EntityUid.Invalid) + { + puddleUid = EntityManager.SpawnEntity(prototypeName, coordinate); + } + + puddles.Add(EntityManager.GetComponent(puddleUid)); + } + } + } + + private bool CanExpand(Vector2i newPos, IMapGrid mapGrid, + [NotNullWhen(true)] out EntityUid? uid) + { + if (!mapGrid.TryGetTileRef(newPos, out var tileRef) + || tileRef.Tile.IsEmpty) + { + uid = null; + return false; + } + + foreach (var entity in mapGrid.GetAnchoredEntities(newPos)) + { + IPhysBody? physics = null; + PuddleComponent? existingPuddle = null; + + // This is an invalid location + if (Resolve(entity, ref physics, false) + && (physics.CollisionLayer & (int) CollisionGroup.Impassable) != 0) + { + uid = null; + return false; + } + + if (!Resolve(entity, ref existingPuddle, false)) + continue; + + uid = entity; + return true; + } + + uid = EntityUid.Invalid; + return true; + } +} diff --git a/Content.Server/Fluids/EntitySystems/PuddleSystem.cs b/Content.Server/Fluids/EntitySystems/PuddleSystem.cs index 44b8014494..c39a30fdf2 100644 --- a/Content.Server/Fluids/EntitySystems/PuddleSystem.cs +++ b/Content.Server/Fluids/EntitySystems/PuddleSystem.cs @@ -1,28 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; using Content.Server.Chemistry.EntitySystems; -using Content.Server.Construction.Components; using Content.Server.Fluids.Components; using Content.Shared.Chemistry.Components; -using Content.Shared.Database; -using Content.Shared.Directions; using Content.Shared.Examine; using Content.Shared.FixedPoint; using Content.Shared.Fluids; -using Content.Shared.Maps; -using Content.Shared.Physics; using Content.Shared.Slippery; -using Content.Shared.Verbs; using JetBrains.Annotations; using Robust.Shared.Audio; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Localization; -using Robust.Shared.Map; -using Robust.Shared.Maths; -using Robust.Shared.Physics; using Robust.Shared.Player; namespace Content.Server.Fluids.EntitySystems @@ -30,8 +14,8 @@ namespace Content.Server.Fluids.EntitySystems [UsedImplicitly] public sealed class PuddleSystem : EntitySystem { - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; + [Dependency] private readonly FluidSpreaderSystem _fluidSpreaderSystem = default!; public override void Initialize() { @@ -45,7 +29,7 @@ namespace Content.Server.Fluids.EntitySystems private void OnInit(EntityUid uid, PuddleComponent component, ComponentInit args) { - var solution = _solutionContainerSystem.EnsureSolution(uid, component.SolutionName); + var solution = _solutionContainerSystem.EnsureSolution(uid, component.SolutionName); solution.MaxVolume = FixedPoint2.New(1000); } @@ -111,21 +95,6 @@ namespace Content.Server.Fluids.EntitySystems QueueDel(uid); } - /// - /// Whether adding this solution to this puddle would overflow. - /// - /// Uid of owning entity - /// Puddle to which we are adding solution - /// Solution we intend to add - /// - public bool WouldOverflow(EntityUid uid, Solution solution, PuddleComponent? puddle = null) - { - if (!Resolve(uid, ref puddle)) - return false; - - return puddle.CurrentVolume + solution.TotalVolume > puddle.OverflowVolume; - } - public bool EmptyHolder(EntityUid uid, PuddleComponent? puddleComponent = null) { if (!Resolve(uid, ref puddleComponent)) @@ -147,24 +116,40 @@ namespace Content.Server.Fluids.EntitySystems : FixedPoint2.Zero; } - public bool TryAddSolution(EntityUid uid, Solution solution, + /// + /// + /// + /// Puddle to which we add + /// Solution that is added to puddleComponent + /// Play sound on overflow + /// Overflow on encountered values + /// Optional resolved PuddleComponent + /// + public bool TryAddSolution(EntityUid puddleUid, + Solution addedSolution, bool sound = true, bool checkForOverflow = true, PuddleComponent? puddleComponent = null) { - if (!Resolve(uid, ref puddleComponent)) + if (!Resolve(puddleUid, ref puddleComponent)) return false; - if (solution.TotalVolume == 0 || + if (addedSolution.TotalVolume == 0 || !_solutionContainerSystem.TryGetSolution(puddleComponent.Owner, puddleComponent.SolutionName, out var puddleSolution)) { return false; } - var result = _solutionContainerSystem - .TryAddSolution(puddleComponent.Owner, puddleSolution, solution); + .TryMixAndOverflow(puddleComponent.Owner, puddleSolution, addedSolution, puddleComponent.OverflowVolume, + out var overflowSolution); + + if (checkForOverflow && overflowSolution != null) + { + _fluidSpreaderSystem.AddOverflowingPuddle(puddleComponent, overflowSolution); + } + if (!result) { return false; @@ -172,11 +157,6 @@ namespace Content.Server.Fluids.EntitySystems RaiseLocalEvent(puddleComponent.Owner, new SolutionChangedEvent()); - if (checkForOverflow) - { - CheckOverflow(puddleComponent); - } - if (!sound) { return true; @@ -188,139 +168,18 @@ namespace Content.Server.Fluids.EntitySystems } /// - /// Will overflow this entity to neighboring entities if required + /// Whether adding this solution to this puddle would overflow. /// - private void CheckOverflow(PuddleComponent puddleComponent) + /// Uid of owning entity + /// Puddle to which we are adding solution + /// Solution we intend to add + /// + public bool WouldOverflow(EntityUid uid, Solution solution, PuddleComponent? puddle = null) { - if (puddleComponent.CurrentVolume <= puddleComponent.OverflowVolume - || puddleComponent.Overflown) - return; - - var nextPuddles = new List() { puddleComponent }; - var overflownPuddles = new List(); - - while (puddleComponent.OverflowLeft > FixedPoint2.Zero && nextPuddles.Count > 0) - { - foreach (var next in nextPuddles.ToArray()) - { - nextPuddles.Remove(next); - - next.Overflown = true; - overflownPuddles.Add(next); - - var adjacentPuddles = GetAllAdjacentOverflow(next).ToArray(); - if (puddleComponent.OverflowLeft <= FixedPoint2.Epsilon * adjacentPuddles.Length) - { - break; - } - - if (adjacentPuddles.Length == 0) - { - continue; - } - - var numberOfAdjacent = FixedPoint2.New(adjacentPuddles.Length); - var overflowSplit = puddleComponent.OverflowLeft / numberOfAdjacent; - foreach (var adjacent in adjacentPuddles) - { - var adjacentPuddle = adjacent(); - var quantity = FixedPoint2.Min(overflowSplit, adjacentPuddle.OverflowVolume); - var puddleSolution = _solutionContainerSystem.EnsureSolution(puddleComponent.Owner, - puddleComponent.SolutionName); - var spillAmount = _solutionContainerSystem.SplitSolution(puddleComponent.Owner, - puddleSolution, quantity); - - TryAddSolution(adjacentPuddle.Owner, spillAmount, false, false); - nextPuddles.Add(adjacentPuddle); - } - } - } - - foreach (var puddle in overflownPuddles) - { - puddle.Overflown = false; - } - } - - /// - /// Finds or creates adjacent puddles in random directions from this one - /// - /// Enumerable of the puddles found or to be created - private IEnumerable> GetAllAdjacentOverflow(PuddleComponent puddleComponent) - { - foreach (var direction in SharedDirectionExtensions.RandomDirections()) - { - if (TryGetAdjacentOverflow(puddleComponent, direction, out var puddle)) - { - yield return puddle; - } - } - } - - /// - /// Tries to get an adjacent coordinate to overflow to, unless it is blocked by a wall on the - /// same tile or the tile is empty - /// - /// - /// The direction to get the puddle from, respective to this one - /// The puddle that was found or is to be created, or null if there - /// is a wall in the way - /// true if a puddle was found or created, false otherwise - private bool TryGetAdjacentOverflow(PuddleComponent puddleComponent, Direction direction, - [NotNullWhen(true)] out Func? puddle) - { - puddle = default; - - // We're most likely in space, do nothing. - if (!EntityManager.GetComponent(puddleComponent.Owner).GridID.IsValid()) + if (!Resolve(uid, ref puddle)) return false; - var mapGrid = _mapManager.GetGrid(EntityManager.GetComponent(puddleComponent.Owner).GridID); - var coords = EntityManager.GetComponent(puddleComponent.Owner).Coordinates; - - if (!coords.Offset(direction).TryGetTileRef(out var tile)) - { - return false; - } - - // If space return early, let that spill go out into the void - if (tile.Value.Tile.IsEmpty) - { - return false; - } - - if (!EntityManager.GetComponent(puddleComponent.Owner).Anchored) - return false; - - foreach (var entity in mapGrid.GetInDir(coords, direction)) - { - if (EntityManager.TryGetComponent(entity, out IPhysBody? physics) && - (physics.CollisionLayer & (int)CollisionGroup.Impassable) != 0) - { - puddle = default; - return false; - } - - if (EntityManager.TryGetComponent(entity, out PuddleComponent? existingPuddle)) - { - if (existingPuddle.Overflown) - { - return false; - } - - puddle = () => existingPuddle; - } - } - - puddle ??= () => - { - var id = EntityManager.SpawnEntity( - EntityManager.GetComponent(puddleComponent.Owner).EntityPrototype?.ID, - mapGrid.DirectionToGrid(coords, direction)); - return EntityManager.GetComponent(id); - }; - - return true; + return puddle.CurrentVolume + solution.TotalVolume > puddle.OverflowVolume; } } } diff --git a/Content.Server/Fluids/EntitySystems/SpillableSystem.cs b/Content.Server/Fluids/EntitySystems/SpillableSystem.cs index b3a8ab6469..047c6ba518 100644 --- a/Content.Server/Fluids/EntitySystems/SpillableSystem.cs +++ b/Content.Server/Fluids/EntitySystems/SpillableSystem.cs @@ -3,7 +3,6 @@ using System.Linq; using Content.Server.Administration.Logs; using Content.Server.Chemistry.EntitySystems; using Content.Server.Clothing.Components; -using Content.Server.Coordinates.Helpers; using Content.Server.Fluids.Components; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; @@ -13,17 +12,13 @@ using Content.Shared.Inventory.Events; using Content.Shared.Throwing; using Content.Shared.Verbs; using JetBrains.Annotations; -using Robust.Server.GameObjects; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Localization; using Robust.Shared.Map; using Robust.Shared.Prototypes; namespace Content.Server.Fluids.EntitySystems; [UsedImplicitly] -public class SpillableSystem : EntitySystem +public sealed class SpillableSystem : EntitySystem { [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; [Dependency] private readonly PuddleSystem _puddleSystem = default!; @@ -61,7 +56,6 @@ public class SpillableSystem : EntitySystem // spill all solution on the player var drainedSolution = _solutionContainerSystem.Drain(uid, solution, solution.DrainAvailable); SpillAt(args.Equipee, drainedSolution, "PuddleSmear"); - } /// @@ -174,10 +168,13 @@ public class SpillableSystem : EntitySystem if (!noTileReact) { // First, do all tile reactions - foreach (var (reagentId, quantity) in solution.Contents) + for (var i = 0; i < solution.Contents.Count; i++) { + var (reagentId, quantity) = solution.Contents[i]; var proto = _prototypeManager.Index(reagentId); - proto.ReactionTile(tileRef, quantity); + var removed = proto.ReactionTile(tileRef, quantity); + if (removed <= FixedPoint2.Zero) continue; + solution.RemoveReagent(reagentId, removed); } } @@ -188,40 +185,34 @@ public class SpillableSystem : EntitySystem // Get normalized co-ordinate for spill location and spill it in the centre // TODO: Does SnapGrid or something else already do this? var spillGridCoords = mapGrid.GridTileToWorld(tileRef.GridIndices); - - var spillEntities = _entityLookup.GetEntitiesIntersecting(mapGrid.ParentMapId, spillGridCoords.Position).ToArray(); - foreach (var spillEntity in spillEntities) - { - if (_solutionContainerSystem.TryGetRefillableSolution(spillEntity, out var solutionContainerComponent)) - { - _solutionContainerSystem.Refill(spillEntity, solutionContainerComponent, - solution.SplitSolution(FixedPoint2.Min( - solutionContainerComponent.AvailableVolume, - solutionContainerComponent.MaxSpillRefill)) - ); - } - } + var startEntity = EntityUid.Invalid; + PuddleComponent? puddleComponent = null; if (combine) { + var spillEntities = _entityLookup.GetEntitiesIntersecting(tileRef).ToArray(); + foreach (var spillEntity in spillEntities) { - if (!EntityManager.TryGetComponent(spillEntity, out PuddleComponent? puddleComponent)) continue; + if (!EntityManager.TryGetComponent(spillEntity, out puddleComponent)) continue; if (!overflow && _puddleSystem.WouldOverflow(puddleComponent.Owner, solution, puddleComponent)) return null; - if (!_puddleSystem.TryAddSolution(puddleComponent.Owner, solution, sound)) continue; + if (!_puddleSystem.TryAddSolution(puddleComponent.Owner, solution, sound, overflow)) continue; - return puddleComponent; + startEntity = puddleComponent.Owner; + break; } } - var puddleEnt = EntityManager.SpawnEntity(prototype, spillGridCoords); - var newPuddleComponent = EntityManager.GetComponent(puddleEnt); + if (startEntity != EntityUid.Invalid) + return puddleComponent; - _puddleSystem.TryAddSolution(newPuddleComponent.Owner, solution, sound); + startEntity = EntityManager.SpawnEntity(prototype, spillGridCoords); + puddleComponent = EntityManager.EnsureComponent(startEntity); + _puddleSystem.TryAddSolution(startEntity, solution, sound, overflow); - return newPuddleComponent; + return puddleComponent; } }