]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Solution Entities (#21916)
authorTemporalOroboros <TemporalOroboros@gmail.com>
Fri, 29 Dec 2023 01:58:14 +0000 (17:58 -0800)
committerGitHub <noreply@github.com>
Fri, 29 Dec 2023 01:58:14 +0000 (17:58 -0800)
* Creates Content.Shared.Chemistry.Solutions
Copies Solution class to new namespace
Obsoletes old Solution class

* Switches over to the Solutions.Solution Solution

* Creates Content.Shared.Chemistry.Containers
Copies relevant components/systems to the new namespace
Obsoletes old versions

* Switches over to the Containers.XYZ namespace

* Creates SolutionSystem and obsoletes old SolutionContainerSystem methods

* Start using SolutionSystem for Solution manipulation

* EnumerateSolutions

* Move TryGetMixableSolution

* Move EnsureSolution to Server

* Create Solution Entities

* Stop using obsolete solution system methods

* Fix prototype component tests

* Add using ..Audio.Systems; back

* Wrap solution container slots in ContainerSlots

* Actually add the slot to the solution container map

* Dirty SolutionContainerComponent when ensuring solutions

* Revert namespace changes

* Remerge SolutionSystem and SolutionContainerSystem

* SolutionContainerManagerComponent refactor

* Avoid wrapping necessary code in DebugTools.Assert as it is removed when compiling for release

* Readd examine reagent sorting

* Fix errors

* Poke tests

* Fix solution names not being applied

* Fix WoolyComponent including statement

* Fix merge skew

* Fix compile errors

* Make reactions use solntities

* Reindent solution class namespace

* Field attribute changes

* AutoGenerateComponentState for SolutionContainerComponent

* SolutionContainerComponent -> ContainedSolutionComponent

* ref ReactionAttemptEvent

* Denetwork preinit solutions

* Misc 1

* Nullable TryGetSolution out vars

* Cache associated solutions

* Fix merge skew

* Use explicit regions in SharedSolutionContainerSystem.Capabilities

* Add debug assert

* Use explicit regions in SharedSolutionContainerSystem.Relay + ref SolutionContainerChangedEvent

* ContainedSolutionComponent.Name -> ContainedSolutionComponent.ContainerName

* SolutionComponent doc comments

* Implicit DataField names and property purge

* ReagentEffect DataField names

* Local variables for readability

* Sort using statements + Entity<T> event handlers

* Fix compile erros

* Fix compile errors

---------

Co-authored-by: ElectroJr <leonsfriedrich@gmail.com>
180 files changed:
Content.Client/Administration/UI/ManageSolutions/AddReagentWindow.xaml.cs
Content.Client/Administration/UI/ManageSolutions/EditSolutionsWindow.xaml.cs
Content.Client/Chemistry/Containers/EntitySystems/SolutionContainerSystem.cs [new file with mode: 0644]
Content.Client/Kitchen/UI/GrinderMenu.xaml.cs
Content.Client/Kitchen/UI/MicrowaveBoundUserInterface.cs
Content.IntegrationTests/Tests/Chemistry/SolutionSystemTests.cs
Content.IntegrationTests/Tests/Chemistry/TryAllReactionsTest.cs
Content.IntegrationTests/Tests/Fluids/AbsorbentTest.cs
Content.Server/Administration/Commands/AddReagent.cs
Content.Server/Administration/Commands/SetSolutionCapacity.cs
Content.Server/Administration/Commands/SetSolutionTemperature.cs
Content.Server/Administration/Commands/SetSolutionThermalEnergy.cs
Content.Server/Administration/Systems/AdminVerbSystem.cs
Content.Server/Administration/Toolshed/SolutionCommand.cs
Content.Server/Administration/UI/EditSolutionsEui.cs
Content.Server/Animals/Components/UdderComponent.cs
Content.Server/Animals/Components/WoolyComponent.cs
Content.Server/Animals/Systems/UdderSystem.cs
Content.Server/Animals/Systems/WoolySystem.cs
Content.Server/Anomaly/Components/ReagentProducerAnomalyComponent.cs
Content.Server/Anomaly/Effects/InjectionAnomalySystem.cs
Content.Server/Anomaly/Effects/PuddleCreateAnomalySystem.cs
Content.Server/Anomaly/Effects/ReagentProducerAnomalySystem.cs
Content.Server/Atmos/Piping/Unary/Components/GasCondenserComponent.cs
Content.Server/Atmos/Piping/Unary/EntitySystems/GasCondenserSystem.cs
Content.Server/Body/Components/BloodstreamComponent.cs
Content.Server/Body/Components/LungComponent.cs
Content.Server/Body/Components/MetabolizerComponent.cs
Content.Server/Body/Components/StomachComponent.cs
Content.Server/Body/Systems/BloodstreamSystem.cs
Content.Server/Body/Systems/LungSystem.cs
Content.Server/Body/Systems/MetabolizerSystem.cs
Content.Server/Body/Systems/RespiratorSystem.cs
Content.Server/Body/Systems/StomachSystem.cs
Content.Server/Botany/Components/PlantHolderComponent.cs
Content.Server/Botany/Systems/BotanySystem.Produce.cs
Content.Server/Botany/Systems/BotanySystem.Seed.cs
Content.Server/Botany/Systems/PlantHolderSystem.cs
Content.Server/Cargo/Systems/PricingSystem.cs
Content.Server/Chemistry/Components/HyposprayComponent.cs
Content.Server/Chemistry/Components/SolutionRegenerationComponent.cs
Content.Server/Chemistry/Containers/EntitySystems/SolutionContainerSystem.cs [new file with mode: 0644]
Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs
Content.Server/Chemistry/EntitySystems/ChemistrySystem.Injector.cs
Content.Server/Chemistry/EntitySystems/ChemistrySystem.cs
Content.Server/Chemistry/EntitySystems/ChemistrySystemHypospray.cs
Content.Server/Chemistry/EntitySystems/ChemistrySystemMixer.cs
Content.Server/Chemistry/EntitySystems/DeleteOnSolutionEmptySystem.cs
Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs
Content.Server/Chemistry/EntitySystems/RehydratableSystem.cs
Content.Server/Chemistry/EntitySystems/SolutionHeaterSystem.cs
Content.Server/Chemistry/EntitySystems/SolutionInjectOnCollideSystem.cs
Content.Server/Chemistry/EntitySystems/SolutionPurgeSystem.cs
Content.Server/Chemistry/EntitySystems/SolutionRandomFillSystem.cs
Content.Server/Chemistry/EntitySystems/SolutionRegenerationSystem.cs
Content.Server/Chemistry/EntitySystems/SolutionSpikableSystem.cs
Content.Server/Chemistry/EntitySystems/SolutionTransferSystem.cs
Content.Server/Chemistry/EntitySystems/TransformableContainerSystem.cs
Content.Server/Chemistry/EntitySystems/VaporSystem.cs
Content.Server/Chemistry/ReactionEffects/AreaReactionEffect.cs
Content.Server/Chemistry/ReactionEffects/CreateEntityReactionEffect.cs
Content.Server/Chemistry/ReactionEffects/EmpReactionEffect.cs
Content.Server/Chemistry/ReactionEffects/ExplosionReactionEffect.cs
Content.Server/Chemistry/ReactionEffects/SmokeAreaReactionEffect.cs [deleted file]
Content.Server/Chemistry/ReactionEffects/SolutionTemperatureEffects.cs
Content.Server/Chemistry/ReagentEffectConditions/BodyTemperature.cs
Content.Server/Chemistry/ReagentEffectConditions/HasTagCondition.cs
Content.Server/Chemistry/ReagentEffectConditions/MobStateCondition.cs
Content.Server/Chemistry/ReagentEffectConditions/OrganType.cs
Content.Server/Chemistry/ReagentEffectConditions/ReagentThreshold.cs
Content.Server/Chemistry/ReagentEffectConditions/SolutionTemperature.cs
Content.Server/Chemistry/ReagentEffectConditions/TotalDamage.cs
Content.Server/Chemistry/ReagentEffects/AddToSolutionReaction.cs
Content.Server/Chemistry/ReagentEffects/AdjustAlert.cs
Content.Server/Chemistry/ReagentEffects/AdjustReagent.cs
Content.Server/Chemistry/ReagentEffects/AdjustTemperature.cs
Content.Server/Chemistry/ReagentEffects/CauseZombieInfection.cs
Content.Server/Chemistry/ReagentEffects/ChemCleanBoodstream.cs
Content.Server/Chemistry/ReagentEffects/ChemHealEyeDamage.cs
Content.Server/Chemistry/ReagentEffects/ChemVomit.cs
Content.Server/Chemistry/ReagentEffects/CreateGas.cs
Content.Server/Chemistry/ReagentEffects/CureZombieInfection.cs
Content.Server/Chemistry/ReagentEffects/Drunk.cs
Content.Server/Chemistry/ReagentEffects/Electrocute.cs
Content.Server/Chemistry/ReagentEffects/Emote.cs
Content.Server/Chemistry/ReagentEffects/FlammableReaction.cs
Content.Server/Chemistry/ReagentEffects/HealthChange.cs
Content.Server/Chemistry/ReagentEffects/ModifyBleedAmount.cs
Content.Server/Chemistry/ReagentEffects/ModifyBloodLevel.cs
Content.Server/Chemistry/ReagentEffects/MovespeedModifier.cs
Content.Server/Chemistry/ReagentEffects/Oxygenate.cs
Content.Server/Chemistry/ReagentEffects/Paralyze.cs
Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustAttribute.cs
Content.Server/Chemistry/ReagentEffects/PlantMetabolism/RobustHarvest.cs
Content.Server/Chemistry/ReagentEffects/PopupMessage.cs
Content.Server/Chemistry/ReagentEffects/StatusEffects/GenericStatusEffect.cs
Content.Server/Chemistry/ReagentEffects/StatusEffects/Jitter.cs
Content.Server/Chemistry/TileReactions/CleanDecalsReaction.cs
Content.Server/Chemistry/TileReactions/CleanTileReaction.cs
Content.Server/Chemistry/TileReactions/CreateEntityTileReaction.cs
Content.Server/Cloning/CloningSystem.cs
Content.Server/Construction/Conditions/MinSolution.cs
Content.Server/Construction/Conditions/SolutionEmpty.cs
Content.Server/Destructible/DestructibleSystem.cs
Content.Server/Destructible/Thresholds/Behaviors/SolutionExplosionBehavior.cs
Content.Server/Destructible/Thresholds/Behaviors/SpillBehavior.cs
Content.Server/Explosion/EntitySystems/TriggerSystem.cs
Content.Server/Extinguisher/FireExtinguisherSystem.cs
Content.Server/Fluids/EntitySystems/AbsorbentSystem.cs
Content.Server/Fluids/EntitySystems/DrainSystem.cs
Content.Server/Fluids/EntitySystems/PuddleSystem.Evaporation.cs
Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs
Content.Server/Fluids/EntitySystems/PuddleSystem.Transfers.cs
Content.Server/Fluids/EntitySystems/PuddleSystem.cs
Content.Server/Fluids/EntitySystems/SmokeSystem.cs
Content.Server/Fluids/EntitySystems/SpraySystem.cs
Content.Server/Glue/GlueSystem.cs
Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs
Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs
Content.Server/Lube/LubeSystem.cs
Content.Server/Materials/MaterialReclaimerSystem.cs
Content.Server/Medical/CryoPodSystem.cs
Content.Server/Medical/HealingSystem.cs
Content.Server/Medical/HealthAnalyzerSystem.cs
Content.Server/Medical/VomitSystem.cs
Content.Server/NPC/HTN/PrimitiveTasks/Operators/Specific/MedibotInjectOperator.cs
Content.Server/NPC/Systems/NPCUtilitySystem.cs
Content.Server/Nutrition/EntitySystems/CreamPieSystem.cs
Content.Server/Nutrition/EntitySystems/DrinkSystem.cs
Content.Server/Nutrition/EntitySystems/FlavorProfileSystem.cs
Content.Server/Nutrition/EntitySystems/FoodSystem.cs
Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs
Content.Server/Nutrition/EntitySystems/SmokingSystem.Cigar.cs
Content.Server/Nutrition/EntitySystems/SmokingSystem.SmokingPipe.cs
Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs
Content.Server/Nutrition/EntitySystems/SmokingSystem.cs
Content.Server/Nutrition/EntitySystems/TrashOnSolutionEmptySystem.cs
Content.Server/Payload/EntitySystems/PayloadSystem.cs
Content.Server/Power/EntitySystems/RiggableSystem.cs
Content.Server/Power/Generator/ChemicalFuelGeneratorAdapterComponent.cs
Content.Server/Power/Generator/GeneratorSystem.cs
Content.Server/Stunnable/Systems/StunbatonSystem.cs
Content.Server/Tools/Components/WelderComponent.cs
Content.Server/Tools/ToolSystem.Welder.cs
Content.Server/Tools/ToolSystem.cs
Content.Server/Weapons/Melee/MeleeWeaponSystem.cs
Content.Server/Weapons/Ranged/Systems/ChemicalAmmoSystem.cs
Content.Server/Weapons/Ranged/Systems/GunSystem.Solution.cs
Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Components/ChemicalPuddleArtifactComponent.cs
Content.Shared/Administration/EditSolutionsEuiState.cs
Content.Shared/Chemistry/Components/DrainableSolutionComponent.cs
Content.Shared/Chemistry/Components/DumpableSolutionComponent.cs
Content.Shared/Chemistry/Components/FitsInDispenserComponent.cs
Content.Shared/Chemistry/Components/RefillableSolutionComponent.cs
Content.Shared/Chemistry/Components/SmokeComponent.cs
Content.Shared/Chemistry/Components/Solution.cs
Content.Shared/Chemistry/Components/SolutionComponent.cs [new file with mode: 0644]
Content.Shared/Chemistry/Components/SolutionManager/ContainedSolutionComponent.cs [new file with mode: 0644]
Content.Shared/Chemistry/Components/SolutionManager/DrawableSolutionComponent.cs
Content.Shared/Chemistry/Components/SolutionManager/ExaminableSolutionComponent.cs
Content.Shared/Chemistry/Components/SolutionManager/InjectableSolutionComponent.cs
Content.Shared/Chemistry/Components/SolutionManager/SolutionContainerManagerComponent.cs
Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerMixerSystem.cs
Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.Capabilities.cs [new file with mode: 0644]
Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.Relays.cs [new file with mode: 0644]
Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs [new file with mode: 0644]
Content.Shared/Chemistry/EntitySystems/SolutionContainerSystem.Capabilities.cs [deleted file]
Content.Shared/Chemistry/EntitySystems/SolutionContainerSystem.cs [deleted file]
Content.Shared/Chemistry/Reaction/ChemicalReactionSystem.cs
Content.Shared/Chemistry/Reaction/ReactionMixerComponent.cs
Content.Shared/Chemistry/ReactiveSystem.cs
Content.Shared/Chemistry/SharedChemMaster.cs
Content.Shared/Fluids/Components/DrainComponent.cs
Content.Shared/Fluids/Components/PuddleComponent.cs
Content.Shared/Fluids/SharedPuddleSystem.cs
Content.Shared/Kitchen/Components/SharedMicrowave.cs
Content.Shared/Kitchen/SharedReagentGrinder.cs
Content.Shared/Materials/MaterialReclaimerComponent.cs
Content.Shared/Nutrition/Events.cs
Content.Tests/Shared/Chemistry/SolutionTests.cs

index cf7c8c50585f29b82a882755b1248c6633b941a7..1ded5cebb3a1da34b22b7f6416caff9e7a6c8e3f 100644 (file)
@@ -4,9 +4,6 @@ using Robust.Client.Console;
 using Robust.Client.UserInterface.Controls;
 using Robust.Client.UserInterface.CustomControls;
 using Robust.Client.UserInterface.XAML;
-using Robust.Shared.GameObjects;
-using Robust.Shared.IoC;
-using Robust.Shared.Localization;
 using Robust.Shared.Prototypes;
 
 namespace Content.Client.Administration.UI.ManageSolutions
index d2dda0ccbdee9d1e283c7f2ef065835fd1091ad2..ecc26d60265c2fc13c4b1297763d14c785b0ef92 100644 (file)
@@ -20,7 +20,7 @@ namespace Content.Client.Administration.UI.ManageSolutions
         private NetEntity _target = NetEntity.Invalid;
         private string? _selectedSolution;
         private AddReagentWindow? _addReagentWindow;
-        private Dictionary<string, Solution>? _solutions;
+        private Dictionary<string, EntityUid>? _solutions;
 
         public EditSolutionsWindow()
         {
@@ -60,9 +60,11 @@ namespace Content.Client.Administration.UI.ManageSolutions
             if (_selectedSolution == null || _solutions == null)
                 return;
 
-            if (!_solutions.TryGetValue(_selectedSolution, out var solution))
+            if (!_solutions.TryGetValue(_selectedSolution, out var solutionId) ||
+                !_entityManager.TryGetComponent(solutionId, out SolutionComponent? solutionComp))
                 return;
 
+            var solution = solutionComp.Solution;
             UpdateVolumeBox(solution);
             UpdateThermalBox(solution);
 
@@ -198,10 +200,13 @@ namespace Content.Client.Administration.UI.ManageSolutions
         /// </summary>
         private void SetReagent(FloatSpinBox.FloatSpinBoxEventArgs args, string prototype)
         {
-            if (_solutions == null || _selectedSolution == null)
+            if (_solutions == null || _selectedSolution == null ||
+                !_solutions.TryGetValue(_selectedSolution, out var solutionId) ||
+                !_entityManager.TryGetComponent(solutionId, out SolutionComponent? solutionComp))
                 return;
 
-            var current = _solutions[_selectedSolution].GetTotalPrototypeQuantity(prototype);
+            var solution = solutionComp.Solution;
+            var current = solution.GetTotalPrototypeQuantity(prototype);
             var delta = args.Value - current.Float();
 
             if (MathF.Abs(delta) < 0.01)
@@ -275,22 +280,38 @@ namespace Content.Client.Administration.UI.ManageSolutions
         /// <summary>
         ///     Update the solution options.
         /// </summary>
-        public void UpdateSolutions(Dictionary<string, Solution>? solutions)
+        public void UpdateSolutions(List<(string, NetEntity)>? solutions)
         {
             SolutionOption.Clear();
-            _solutions = solutions;
+
+            if (solutions is { Count: > 0 })
+            {
+                if (_solutions is { Count: > 0 })
+                    _solutions.Clear();
+                else
+                    _solutions = new(solutions.Count);
+
+                foreach (var (name, netSolution) in solutions)
+                {
+                    if (_entityManager.TryGetEntity(netSolution, out var solution))
+                        _solutions.Add(name, solution.Value);
+                }
+            }
+            else
+                _solutions = null;
 
             if (_solutions == null)
                 return;
 
             int i = 0;
-            foreach (var solution in _solutions.Keys)
+            int selectedIndex = 0; // Default to the first solution if none are found.
+            foreach (var (name, _) in _solutions)
             {
-                SolutionOption.AddItem(solution, i);
-                SolutionOption.SetItemMetadata(i, solution);
+                SolutionOption.AddItem(name, i);
+                SolutionOption.SetItemMetadata(i, name);
 
-                if (solution == _selectedSolution)
-                    SolutionOption.Select(i);
+                if (name == _selectedSolution)
+                    selectedIndex = i;
 
                 i++;
             }
@@ -300,14 +321,11 @@ namespace Content.Client.Administration.UI.ManageSolutions
                 // No applicable solutions
                 Close();
                 Dispose();
+                return;
             }
 
-            if (_selectedSolution == null || !_solutions.ContainsKey(_selectedSolution))
-            {
-                // the previously selected solution is no longer valid.
-                SolutionOption.Select(0);
-                _selectedSolution = (string?) SolutionOption.SelectedMetadata;
-            }
+            SolutionOption.Select(selectedIndex);
+            _selectedSolution = (string?) SolutionOption.SelectedMetadata;
         }
     }
 }
diff --git a/Content.Client/Chemistry/Containers/EntitySystems/SolutionContainerSystem.cs b/Content.Client/Chemistry/Containers/EntitySystems/SolutionContainerSystem.cs
new file mode 100644 (file)
index 0000000..0c38d60
--- /dev/null
@@ -0,0 +1,7 @@
+using Content.Shared.Chemistry.EntitySystems;
+
+namespace Content.Client.Chemistry.Containers.EntitySystems;
+
+public sealed partial class SolutionContainerSystem : SharedSolutionContainerSystem
+{
+}
index 1822dd7fff05cddb60bc91add3392103d34e3d9f..6e4b7a76180871680cf66805155fc19c8a668b55 100644 (file)
@@ -1,11 +1,9 @@
 using Content.Client.UserInterface.Controls;
-using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.Kitchen;
 using Robust.Client.AutoGenerated;
 using Robust.Client.GameObjects;
 using Robust.Client.UserInterface.Controls;
-using Robust.Client.UserInterface.CustomControls;
 using Robust.Client.UserInterface.XAML;
 using Robust.Shared.Prototypes;
 
@@ -15,7 +13,7 @@ namespace Content.Client.Kitchen.UI
     public sealed partial class GrinderMenu : FancyWindow
     {
         private readonly IEntityManager _entityManager;
-        private readonly IPrototypeManager _prototypeManager ;
+        private readonly IPrototypeManager _prototypeManager;
         private readonly ReagentGrinderBoundUserInterface _owner;
 
         private readonly Dictionary<int, EntityUid> _chamberVisualContents = new();
@@ -122,8 +120,8 @@ namespace Content.Client.Kitchen.UI
             {
                 foreach (var (reagent, quantity) in reagents)
                 {
-                    var reagentName = _prototypeManager.TryIndex(reagent.Prototype, out ReagentPrototype? proto) 
-                        ? Loc.GetString($"{quantity} {proto.LocalizedName}") 
+                    var reagentName = _prototypeManager.TryIndex(reagent.Prototype, out ReagentPrototype? proto)
+                        ? Loc.GetString($"{quantity} {proto.LocalizedName}")
                         : "???";
                     BeakerContentBox.BoxContents.AddItem(reagentName);
                 }
index 518fbe095e19ad4ad122bab4c66a240764c29138..74579b2699485416b82907389a6d3331878cbf09 100644 (file)
@@ -1,11 +1,9 @@
-using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.Kitchen.Components;
 using JetBrains.Annotations;
 using Robust.Client.GameObjects;
 using Robust.Client.Graphics;
 using Robust.Client.UserInterface.Controls;
-using Robust.Shared.Graphics;
 
 namespace Content.Client.Kitchen.UI
 {
index 064ce46934edda34ad9228a3339d8a018c8541fe..d96a035b2dc65abc61b923bb0715e2e8f829ea80 100644 (file)
@@ -1,5 +1,5 @@
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Chemistry.Components;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.FixedPoint;
 using Robust.Shared.GameObjects;
 using Robust.Shared.Prototypes;
@@ -51,7 +51,7 @@ public sealed class SolutionSystemTests
 
         var entityManager = server.ResolveDependency<IEntityManager>();
         var protoMan = server.ResolveDependency<IPrototypeManager>();
-        var containerSystem = entityManager.EntitySysManager.GetEntitySystem<SolutionContainerSystem>();
+        var containerSystem = entityManager.System<SolutionContainerSystem>();
         var testMap = await pair.CreateTestMap();
         var coordinates = testMap.GridCoords;
 
@@ -67,11 +67,11 @@ public sealed class SolutionSystemTests
 
             beaker = entityManager.SpawnEntity("SolutionTarget", coordinates);
             Assert.That(containerSystem
-                .TryGetSolution(beaker, "beaker", out var solution));
+                .TryGetSolution(beaker, "beaker", out var solutionEnt, out var solution));
 
             solution.AddSolution(originalWater, protoMan);
             Assert.That(containerSystem
-                .TryAddSolution(beaker, solution, oilAdded));
+                .TryAddSolution(solutionEnt.Value, oilAdded));
 
             var water = solution.GetTotalPrototypeQuantity("Water");
             var oil = solution.GetTotalPrototypeQuantity("Oil");
@@ -97,7 +97,7 @@ public sealed class SolutionSystemTests
 
         var entityManager = server.ResolveDependency<IEntityManager>();
         var protoMan = server.ResolveDependency<IPrototypeManager>();
-        var containerSystem = entityManager.EntitySysManager.GetEntitySystem<SolutionContainerSystem>();
+        var containerSystem = entityManager.System<SolutionContainerSystem>();
         var coordinates = testMap.GridCoords;
 
         EntityUid beaker;
@@ -112,11 +112,11 @@ public sealed class SolutionSystemTests
 
             beaker = entityManager.SpawnEntity("SolutionTarget", coordinates);
             Assert.That(containerSystem
-                .TryGetSolution(beaker, "beaker", out var solution));
+                .TryGetSolution(beaker, "beaker", out var solutionEnt, out var solution));
 
             solution.AddSolution(originalWater, protoMan);
             Assert.That(containerSystem
-                .TryAddSolution(beaker, solution, oilAdded), Is.False);
+                .TryAddSolution(solutionEnt.Value, oilAdded), Is.False);
 
             var water = solution.GetTotalPrototypeQuantity("Water");
             var oil = solution.GetTotalPrototypeQuantity("Oil");
@@ -141,7 +141,7 @@ public sealed class SolutionSystemTests
         var entityManager = server.ResolveDependency<IEntityManager>();
         var protoMan = server.ResolveDependency<IPrototypeManager>();
         var testMap = await pair.CreateTestMap();
-        var containerSystem = entityManager.EntitySysManager.GetEntitySystem<SolutionContainerSystem>();
+        var containerSystem = entityManager.System<SolutionContainerSystem>();
         var coordinates = testMap.GridCoords;
 
         EntityUid beaker;
@@ -158,11 +158,11 @@ public sealed class SolutionSystemTests
 
             beaker = entityManager.SpawnEntity("SolutionTarget", coordinates);
             Assert.That(containerSystem
-                .TryGetSolution(beaker, "beaker", out var solution));
+                .TryGetSolution(beaker, "beaker", out var solutionEnt, out var solution));
 
             solution.AddSolution(originalWater, protoMan);
             Assert.That(containerSystem
-                .TryMixAndOverflow(beaker, solution, oilAdded, threshold, out var overflowingSolution));
+                .TryMixAndOverflow(solutionEnt.Value, oilAdded, threshold, out var overflowingSolution));
 
             Assert.Multiple(() =>
             {
@@ -194,7 +194,7 @@ public sealed class SolutionSystemTests
 
         var entityManager = server.ResolveDependency<IEntityManager>();
         var protoMan = server.ResolveDependency<IPrototypeManager>();
-        var containerSystem = entityManager.EntitySysManager.GetEntitySystem<SolutionContainerSystem>();
+        var containerSystem = entityManager.System<SolutionContainerSystem>();
         var testMap = await pair.CreateTestMap();
         var coordinates = testMap.GridCoords;
 
@@ -212,11 +212,11 @@ public sealed class SolutionSystemTests
 
             beaker = entityManager.SpawnEntity("SolutionTarget", coordinates);
             Assert.That(containerSystem
-                .TryGetSolution(beaker, "beaker", out var solution));
+                .TryGetSolution(beaker, "beaker", out var solutionEnt, out var solution));
 
             solution.AddSolution(originalWater, protoMan);
             Assert.That(containerSystem
-                .TryMixAndOverflow(beaker, solution, oilAdded, threshold, out _),
+                .TryMixAndOverflow(solutionEnt.Value, oilAdded, threshold, out _),
                 Is.False);
         });
 
index 5213747588a1d00b3106c29f2d5c15105cfb357d..ddfe7b3481e37480d734e6d2c20c0c914c82b6a2 100644 (file)
@@ -1,11 +1,11 @@
-using System.Linq;
-using Content.Shared.Chemistry.Components;
-using Content.Shared.Chemistry.EntitySystems;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Chemistry.Reaction;
+using Content.Shared.Chemistry.Components;
 using Robust.Shared.GameObjects;
 using Robust.Shared.Map;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Utility;
+using System.Linq;
 
 namespace Content.IntegrationTests.Tests.Chemistry
 {
@@ -34,8 +34,7 @@ namespace Content.IntegrationTests.Tests.Chemistry
             var prototypeManager = server.ResolveDependency<IPrototypeManager>();
             var testMap = await pair.CreateTestMap();
             var coordinates = testMap.GridCoords;
-            var solutionSystem = server.ResolveDependency<IEntitySystemManager>()
-                .GetEntitySystem<SolutionContainerSystem>();
+            var solutionContainerSystem = entityManager.System<SolutionContainerSystem>();
 
             foreach (var reactionPrototype in prototypeManager.EnumeratePrototypes<ReactionPrototype>())
             {
@@ -43,30 +42,31 @@ namespace Content.IntegrationTests.Tests.Chemistry
                 Console.WriteLine($"Testing {reactionPrototype.ID}");
 
                 EntityUid beaker = default;
-                Solution component = null;
+                Entity<SolutionComponent>? solutionEnt = default!;
+                Solution solution = null;
 
                 await server.WaitAssertion(() =>
                 {
                     beaker = entityManager.SpawnEntity("TestSolutionContainer", coordinates);
-                    Assert.That(solutionSystem
-                        .TryGetSolution(beaker, "beaker", out component));
+                    Assert.That(solutionContainerSystem
+                        .TryGetSolution(beaker, "beaker", out solutionEnt, out solution));
                     foreach (var (id, reactant) in reactionPrototype.Reactants)
                     {
 #pragma warning disable NUnit2045
-                        Assert.That(solutionSystem
-                            .TryAddReagent(beaker, component, id, reactant.Amount, out var quantity));
+                        Assert.That(solutionContainerSystem
+                            .TryAddReagent(solutionEnt.Value, id, reactant.Amount, out var quantity));
                         Assert.That(reactant.Amount, Is.EqualTo(quantity));
 #pragma warning restore NUnit2045
                     }
 
-                    solutionSystem.SetTemperature(beaker, component, reactionPrototype.MinimumTemperature);
+                    solutionContainerSystem.SetTemperature(solutionEnt.Value, reactionPrototype.MinimumTemperature);
 
                     if (reactionPrototype.MixingCategories != null)
                     {
                         var dummyEntity = entityManager.SpawnEntity(null, MapCoordinates.Nullspace);
                         var mixerComponent = entityManager.AddComponent<ReactionMixerComponent>(dummyEntity);
                         mixerComponent.ReactionTypes = reactionPrototype.MixingCategories;
-                        solutionSystem.UpdateChemicals(beaker, component, true, mixerComponent);
+                        solutionContainerSystem.UpdateChemicals(solutionEnt.Value, true, mixerComponent);
                     }
                 });
 
@@ -79,7 +79,7 @@ namespace Content.IntegrationTests.Tests.Chemistry
                     var foundProductsMap = reactionPrototype.Products
                         .Concat(reactionPrototype.Reactants.Where(x => x.Value.Catalyst).ToDictionary(x => x.Key, x => x.Value.Amount))
                         .ToDictionary(x => x, _ => false);
-                    foreach (var (reagent, quantity) in component.Contents)
+                    foreach (var (reagent, quantity) in solution.Contents)
                     {
                         Assert.That(foundProductsMap.TryFirstOrNull(x => x.Key.Key == reagent.Prototype && x.Key.Value == quantity, out var foundProduct));
                         foundProductsMap[foundProduct.Value.Key] = true;
index de5226202c584b18802d51184efa75c9c0af8f6e..87ef41fe96eda815f5690d608ea69fc779fb57e3 100644 (file)
@@ -80,7 +80,7 @@ public sealed class AbsorbentTest
 
         var entityManager = server.ResolveDependency<IEntityManager>();
         var absorbentSystem = entityManager.System<AbsorbentSystem>();
-        var solutionContainerSystem = entityManager.System<SolutionContainerSystem>();
+        var solutionContainerSystem = entityManager.System<SharedSolutionContainerSystem>();
         var prototypeManager = server.ResolveDependency<IPrototypeManager>();
 
         EntityUid user = default;
@@ -94,19 +94,19 @@ public sealed class AbsorbentTest
             refillable = entityManager.SpawnEntity(RefillableDummyId, coordinates);
 
             entityManager.TryGetComponent(absorbent, out component);
-            solutionContainerSystem.TryGetSolution(absorbent, AbsorbentComponent.SolutionName, out var absorbentSolution);
-            solutionContainerSystem.TryGetRefillableSolution(refillable, out var refillableSolution);
+            solutionContainerSystem.TryGetSolution(absorbent, AbsorbentComponent.SolutionName, out var absorbentSoln, out var absorbentSolution);
+            solutionContainerSystem.TryGetRefillableSolution(refillable, out var refillableSoln, out var refillableSolution);
 
             // Arrange
             if (testCase.InitialAbsorbentSolution.VolumeOfEvaporable > FixedPoint2.Zero)
-                solutionContainerSystem.AddSolution(absorbent, absorbentSolution, new Solution(EvaporablePrototypeId, testCase.InitialAbsorbentSolution.VolumeOfEvaporable));
+                solutionContainerSystem.AddSolution(absorbentSoln.Value, new Solution(EvaporablePrototypeId, testCase.InitialAbsorbentSolution.VolumeOfEvaporable));
             if (testCase.InitialAbsorbentSolution.VolumeOfNonEvaporable > FixedPoint2.Zero)
-                solutionContainerSystem.AddSolution(absorbent, absorbentSolution, new Solution(NonEvaporablePrototypeId, testCase.InitialAbsorbentSolution.VolumeOfNonEvaporable));
+                solutionContainerSystem.AddSolution(absorbentSoln.Value, new Solution(NonEvaporablePrototypeId, testCase.InitialAbsorbentSolution.VolumeOfNonEvaporable));
 
             if (testCase.InitialRefillableSolution.VolumeOfEvaporable > FixedPoint2.Zero)
-                solutionContainerSystem.AddSolution(refillable, refillableSolution, new Solution(EvaporablePrototypeId, testCase.InitialRefillableSolution.VolumeOfEvaporable));
+                solutionContainerSystem.AddSolution(refillableSoln.Value, new Solution(EvaporablePrototypeId, testCase.InitialRefillableSolution.VolumeOfEvaporable));
             if (testCase.InitialRefillableSolution.VolumeOfNonEvaporable > FixedPoint2.Zero)
-                solutionContainerSystem.AddSolution(refillable, refillableSolution, new Solution(NonEvaporablePrototypeId, testCase.InitialRefillableSolution.VolumeOfNonEvaporable));
+                solutionContainerSystem.AddSolution(refillableSoln.Value, new Solution(NonEvaporablePrototypeId, testCase.InitialRefillableSolution.VolumeOfNonEvaporable));
 
             // Act
             absorbentSystem.Mop(user, refillable, absorbent, component);
@@ -138,7 +138,7 @@ public sealed class AbsorbentTest
 
         var entityManager = server.ResolveDependency<IEntityManager>();
         var absorbentSystem = entityManager.System<AbsorbentSystem>();
-        var solutionContainerSystem = entityManager.System<SolutionContainerSystem>();
+        var solutionContainerSystem = entityManager.System<SharedSolutionContainerSystem>();
         var prototypeManager = server.ResolveDependency<IPrototypeManager>();
 
         EntityUid user = default;
@@ -152,18 +152,18 @@ public sealed class AbsorbentTest
             refillable = entityManager.SpawnEntity(SmallRefillableDummyId, coordinates);
 
             entityManager.TryGetComponent(absorbent, out component);
-            solutionContainerSystem.TryGetSolution(absorbent, AbsorbentComponent.SolutionName, out var absorbentSolution);
-            solutionContainerSystem.TryGetRefillableSolution(refillable, out var refillableSolution);
+            solutionContainerSystem.TryGetSolution(absorbent, AbsorbentComponent.SolutionName, out var absorbentSoln, out var absorbentSolution);
+            solutionContainerSystem.TryGetRefillableSolution(refillable, out var refillableSoln, out var refillableSolution);
 
             // Arrange
-            solutionContainerSystem.AddSolution(absorbent, absorbentSolution, new Solution(EvaporablePrototypeId, testCase.InitialAbsorbentSolution.VolumeOfEvaporable));
+            solutionContainerSystem.AddSolution(absorbentSoln.Value, new Solution(EvaporablePrototypeId, testCase.InitialAbsorbentSolution.VolumeOfEvaporable));
             if (testCase.InitialAbsorbentSolution.VolumeOfNonEvaporable > FixedPoint2.Zero)
-                solutionContainerSystem.AddSolution(absorbent, absorbentSolution, new Solution(NonEvaporablePrototypeId, testCase.InitialAbsorbentSolution.VolumeOfNonEvaporable));
+                solutionContainerSystem.AddSolution(absorbentSoln.Value, new Solution(NonEvaporablePrototypeId, testCase.InitialAbsorbentSolution.VolumeOfNonEvaporable));
 
             if (testCase.InitialRefillableSolution.VolumeOfEvaporable > FixedPoint2.Zero)
-                solutionContainerSystem.AddSolution(refillable, refillableSolution, new Solution(EvaporablePrototypeId, testCase.InitialRefillableSolution.VolumeOfEvaporable));
+                solutionContainerSystem.AddSolution(refillableSoln.Value, new Solution(EvaporablePrototypeId, testCase.InitialRefillableSolution.VolumeOfEvaporable));
             if (testCase.InitialRefillableSolution.VolumeOfNonEvaporable > FixedPoint2.Zero)
-                solutionContainerSystem.AddSolution(refillable, refillableSolution, new Solution(NonEvaporablePrototypeId, testCase.InitialRefillableSolution.VolumeOfNonEvaporable));
+                solutionContainerSystem.AddSolution(refillableSoln.Value, new Solution(NonEvaporablePrototypeId, testCase.InitialRefillableSolution.VolumeOfNonEvaporable));
 
             // Act
             absorbentSystem.Mop(user, refillable, absorbent, component);
index b75e46ad9084a67cbe0d42b90be4445d336f5be9..46e8e8c35e2fc5fec360fe49d2a6c17c0433c1ab 100644 (file)
@@ -1,10 +1,11 @@
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Administration;
 using Content.Shared.Chemistry.Components.SolutionManager;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.FixedPoint;
 using Robust.Shared.Console;
 using Robust.Shared.Prototypes;
+using System.Linq;
 
 namespace Content.Server.Administration.Commands
 {
@@ -41,13 +42,13 @@ namespace Content.Server.Administration.Commands
                 return;
             }
 
-            if (!man.Solutions.ContainsKey(args[1]))
+            var solutionContainerSystem = _entManager.System<SolutionContainerSystem>();
+            if (!solutionContainerSystem.TryGetSolution((uid.Value, man), args[1], out var solution))
             {
-                var validSolutions = string.Join(", ", man.Solutions.Keys);
+                var validSolutions = string.Join(", ", solutionContainerSystem.EnumerateSolutions((uid.Value, man)).Select(s => s.Name));
                 shell.WriteLine($"Entity does not have a \"{args[1]}\" solution. Valid solutions are:\n{validSolutions}");
                 return;
             }
-            var solution = man.Solutions[args[1]];
 
             if (!_protomanager.HasIndex<ReagentPrototype>(args[2]))
             {
@@ -63,9 +64,9 @@ namespace Content.Server.Administration.Commands
             var quantity = FixedPoint2.New(MathF.Abs(quantityFloat));
 
             if (quantityFloat > 0)
-                _entManager.System<SolutionContainerSystem>().TryAddReagent(uid.Value, solution, args[2], quantity, out _);
+                solutionContainerSystem.TryAddReagent(solution.Value, args[2], quantity, out _);
             else
-                _entManager.System<SolutionContainerSystem>().RemoveReagent(uid.Value, solution, args[2], quantity);
+                solutionContainerSystem.RemoveReagent(solution.Value, args[2], quantity);
         }
     }
 }
index e1e644f84f4cb16fd45b69abb40e3ed528ea1446..19900d488bebd826c20d8a420ae11a534acced37 100644 (file)
@@ -1,8 +1,9 @@
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Administration;
 using Content.Shared.Chemistry.Components.SolutionManager;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.FixedPoint;
 using Robust.Shared.Console;
+using System.Linq;
 
 namespace Content.Server.Administration.Commands
 {
@@ -35,13 +36,13 @@ namespace Content.Server.Administration.Commands
                 return;
             }
 
-            if (!man.Solutions.ContainsKey(args[1]))
+            var solutionContainerSystem = _entManager.System<SolutionContainerSystem>();
+            if (!solutionContainerSystem.TryGetSolution((uid.Value, man), args[1], out var solution))
             {
-                var validSolutions = string.Join(", ", man.Solutions.Keys);
+                var validSolutions = string.Join(", ", solutionContainerSystem.EnumerateSolutions((uid.Value, man)).Select(s => s.Name));
                 shell.WriteLine($"Entity does not have a \"{args[1]}\" solution. Valid solutions are:\n{validSolutions}");
                 return;
             }
-            var solution = man.Solutions[args[1]];
 
             if (!float.TryParse(args[2], out var quantityFloat))
             {
@@ -49,14 +50,14 @@ namespace Content.Server.Administration.Commands
                 return;
             }
 
-            if(quantityFloat < 0.0f)
+            if (quantityFloat < 0.0f)
             {
                 shell.WriteLine($"Cannot set the maximum volume of a solution to a negative number.");
                 return;
             }
 
             var quantity = FixedPoint2.New(quantityFloat);
-            _entManager.System<SolutionContainerSystem>().SetCapacity(uid.Value, solution, quantity);
+            solutionContainerSystem.SetCapacity(solution.Value, quantity);
         }
     }
 }
index e8f58a50cfd3d2c8c810943f6843fb13786132a9..5ebd39880b7d7c5abf861e845e7bea896d4397f3 100644 (file)
@@ -1,7 +1,8 @@
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Administration;
 using Content.Shared.Chemistry.Components.SolutionManager;
-using Content.Shared.Chemistry.EntitySystems;
 using Robust.Shared.Console;
+using System.Linq;
 
 namespace Content.Server.Administration.Commands
 {
@@ -34,13 +35,13 @@ namespace Content.Server.Administration.Commands
                 return;
             }
 
-            if (!man.Solutions.ContainsKey(args[1]))
+            var solutionContainerSystem = _entManager.System<SolutionContainerSystem>();
+            if (!solutionContainerSystem.TryGetSolution((uid.Value, man), args[1], out var solution))
             {
-                var validSolutions = string.Join(", ", man.Solutions.Keys);
+                var validSolutions = string.Join(", ", solutionContainerSystem.EnumerateSolutions((uid.Value, man)).Select(s => s.Name));
                 shell.WriteLine($"Entity does not have a \"{args[1]}\" solution. Valid solutions are:\n{validSolutions}");
                 return;
             }
-            var solution = man.Solutions[args[1]];
 
             if (!float.TryParse(args[2], out var quantity))
             {
@@ -54,7 +55,7 @@ namespace Content.Server.Administration.Commands
                 return;
             }
 
-            _entManager.System<SolutionContainerSystem>().SetTemperature(uid.Value, solution, quantity);
+            solutionContainerSystem.SetTemperature(solution.Value, quantity);
         }
     }
 }
index c2b7f9d25c2e469f43a5537ae5f027aef94b595e..6442f1141a040f7969f632dcbe7692b377d31750 100644 (file)
@@ -1,7 +1,8 @@
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Administration;
 using Content.Shared.Chemistry.Components.SolutionManager;
-using Content.Shared.Chemistry.EntitySystems;
 using Robust.Shared.Console;
+using System.Linq;
 
 namespace Content.Server.Administration.Commands
 {
@@ -34,13 +35,13 @@ namespace Content.Server.Administration.Commands
                 return;
             }
 
-            if (!man.Solutions.ContainsKey(args[1]))
+            var solutionContainerSystem = _entManager.System<SolutionContainerSystem>();
+            if (!solutionContainerSystem.TryGetSolution((uid.Value, man), args[1], out var solutionEnt, out var solution))
             {
-                var validSolutions = string.Join(", ", man.Solutions.Keys);
+                var validSolutions = string.Join(", ", solutionContainerSystem.EnumerateSolutions((uid.Value, man)).Select(s => s.Name));
                 shell.WriteLine($"Entity does not have a \"{args[1]}\" solution. Valid solutions are:\n{validSolutions}");
                 return;
             }
-            var solution = man.Solutions[args[1]];
 
             if (!float.TryParse(args[2], out var quantity))
             {
@@ -50,19 +51,19 @@ namespace Content.Server.Administration.Commands
 
             if (solution.GetHeatCapacity(null) <= 0.0f)
             {
-                if(quantity != 0.0f)
+                if (quantity != 0.0f)
                 {
                     shell.WriteLine($"Cannot set the thermal energy of a solution with 0 heat capacity to a non-zero number.");
                     return;
                 }
             }
-            else if(quantity <= 0.0f)
+            else if (quantity <= 0.0f)
             {
                 shell.WriteLine($"Cannot set the thermal energy of a solution with heat capacity to a non-positive number.");
                 return;
             }
 
-            _entManager.System<SolutionContainerSystem>().SetThermalEnergy(uid.Value, solution, quantity);
+            solutionContainerSystem.SetThermalEnergy(solutionEnt.Value, quantity);
         }
     }
 }
index 26370045652f1f959b52205c020259cde0ad2867..cfd8311d8e8349703fae67ec0ff6a8a32562015a 100644 (file)
@@ -1,4 +1,3 @@
-using System.Linq;
 using Content.Server.Administration.Logs;
 using Content.Server.Administration.Managers;
 using Content.Server.Administration.UI;
@@ -34,6 +33,7 @@ using Robust.Shared.Prototypes;
 using Robust.Shared.Timing;
 using Robust.Shared.Toolshed;
 using Robust.Shared.Utility;
+using System.Linq;
 using static Content.Shared.Configurable.ConfigurationComponent;
 
 namespace Content.Server.Administration.Systems
@@ -71,7 +71,7 @@ namespace Content.Server.Administration.Systems
         {
             SubscribeLocalEvent<GetVerbsEvent<Verb>>(GetVerbs);
             SubscribeLocalEvent<RoundRestartCleanupEvent>(Reset);
-            SubscribeLocalEvent<SolutionContainerManagerComponent, SolutionChangedEvent>(OnSolutionChanged);
+            SubscribeLocalEvent<SolutionContainerManagerComponent, SolutionContainerChangedEvent>(OnSolutionChanged);
         }
 
         private void GetVerbs(GetVerbsEvent<Verb> ev)
@@ -470,11 +470,11 @@ namespace Content.Server.Administration.Systems
         }
 
         #region SolutionsEui
-        private void OnSolutionChanged(EntityUid uid, SolutionContainerManagerComponent component, SolutionChangedEvent args)
+        private void OnSolutionChanged(Entity<SolutionContainerManagerComponent> entity, ref SolutionContainerChangedEvent args)
         {
             foreach (var eui in _openSolutionUis.Values)
             {
-                if (eui.Target == uid)
+                if (eui.Target == entity.Owner)
                     eui.StateDirty();
             }
         }
index 1edf958c95e6562a909ae32706e27db6432b4317..e81e9aa323531982ba10c689ce4b98c97703ec8a 100644 (file)
@@ -1,12 +1,12 @@
-using System.Linq;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Administration;
 using Content.Shared.Chemistry.Components;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.FixedPoint;
 using Robust.Shared.Toolshed;
 using Robust.Shared.Toolshed.Syntax;
 using Robust.Shared.Toolshed.TypeParsers;
+using System.Linq;
 
 namespace Content.Server.Administration.Toolshed;
 
@@ -24,10 +24,8 @@ public sealed class SolutionCommand : ToolshedCommand
     {
         _solutionContainer ??= GetSys<SolutionContainerSystem>();
 
-        _solutionContainer.TryGetSolution(input, name.Evaluate(ctx)!, out var solution);
-
-        if (solution is not null)
-            return new SolutionRef(input, solution);
+        if (_solutionContainer.TryGetSolution(input, name.Evaluate(ctx)!, out var solution))
+            return new SolutionRef(solution.Value);
 
         return null;
     }
@@ -55,11 +53,11 @@ public sealed class SolutionCommand : ToolshedCommand
         var amount = amountRef.Evaluate(ctx);
         if (amount > 0)
         {
-            _solutionContainer.TryAddReagent(input.Owner, input.Solution, name.Value.ID, amount, out _);
+            _solutionContainer.TryAddReagent(input.Solution, name.Value.ID, amount, out _);
         }
         else if (amount < 0)
         {
-            _solutionContainer.RemoveReagent(input.Owner, input.Solution, name.Value.ID, -amount);
+            _solutionContainer.RemoveReagent(input.Solution, name.Value.ID, -amount);
         }
 
         return input;
@@ -75,10 +73,10 @@ public sealed class SolutionCommand : ToolshedCommand
         => input.Select(x => AdjReagent(ctx, x, name, amountRef));
 }
 
-public readonly record struct SolutionRef(EntityUid Owner, Solution Solution)
+public readonly record struct SolutionRef(Entity<SolutionComponent> Solution)
 {
     public override string ToString()
     {
-        return $"{Owner} {Solution}";
+        return $"{Solution.Owner} {Solution.Comp.Solution}";
     }
 }
index 5136b347a2e45a5ebf4902d7c6ebca71b9091838..228463d5ce89ae92cdfec426a701b04ae6722eaf 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Server.Administration.Systems;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.EUI;
 using Content.Shared.Administration;
 using Content.Shared.Chemistry.Components.SolutionManager;
@@ -14,11 +15,13 @@ namespace Content.Server.Administration.UI
     public sealed class EditSolutionsEui : BaseEui
     {
         [Dependency] private readonly IEntityManager _entityManager = default!;
+        private readonly SolutionContainerSystem _solutionContainerSystem = default!;
         public readonly EntityUid Target;
 
         public EditSolutionsEui(EntityUid entity)
         {
             IoCManager.InjectDependencies(this);
+            _solutionContainerSystem = _entityManager.System<SolutionContainerSystem>();
             Target = entity;
         }
 
@@ -36,8 +39,23 @@ namespace Content.Server.Administration.UI
 
         public override EuiStateBase GetNewState()
         {
-            var solutions = _entityManager.GetComponentOrNull<SolutionContainerManagerComponent>(Target)?.Solutions;
-            return new EditSolutionsEuiState(_entityManager.GetNetEntity(Target), solutions);
+            List<(string Name, NetEntity Solution)>? netSolutions;
+
+            if (_entityManager.TryGetComponent(Target, out SolutionContainerManagerComponent? container) && container.Containers.Count > 0)
+            {
+                netSolutions = new();
+                foreach (var (name, solution) in _solutionContainerSystem.EnumerateSolutions((Target, container)))
+                {
+                    if (name is null || !_entityManager.TryGetNetEntity(solution, out var netSolution))
+                        continue;
+
+                    netSolutions.Add((name, netSolution.Value));
+                }
+            }
+            else
+                netSolutions = null;
+
+            return new EditSolutionsEuiState(_entityManager.GetNetEntity(Target), netSolutions);
         }
     }
 }
index 3895a8ba24f10ccc959906d0b8b3efee013526b7..620f4572a7128735c765b97db85226f78dcf0bd4 100644 (file)
@@ -1,10 +1,10 @@
 using Content.Server.Animals.Systems;
+using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.FixedPoint;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
 
-
 namespace Content.Server.Animals.Components
 
 /// <summary>
@@ -21,10 +21,16 @@ namespace Content.Server.Animals.Components
         public ProtoId<ReagentPrototype> ReagentId = "Milk";
 
         /// <summary>
-        ///     The solution to add reagent to.
+        ///     The name of <see cref="Solution"/>.
         /// </summary>
         [DataField, ViewVariables(VVAccess.ReadOnly)]
-        public string Solution = "udder";
+        public string SolutionName = "udder";
+
+        /// <summary>
+        ///     The solution to add reagent to.
+        /// </summary>
+        [DataField]
+        public Entity<SolutionComponent>? Solution = null;
 
         /// <summary>
         ///     The amount of reagent to be generated on update.
index e700fd76f66a9c26861a17ee87e3803d94e53e76..c09c6f5e089e2e1ff70b577a4870cc8799edd971 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Server.Animals.Systems;
+using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.FixedPoint;
 using Robust.Shared.Prototypes;
@@ -20,10 +21,16 @@ public sealed partial class WoolyComponent : Component
     public ProtoId<ReagentPrototype> ReagentId = "Fiber";
 
     /// <summary>
-    ///     The solution to add reagent to.
+    ///     The name of <see cref="Solution"/>.
     /// </summary>
     [DataField, ViewVariables(VVAccess.ReadOnly)]
-    public string Solution = "wool";
+    public string SolutionName = "wool";
+
+    /// <summary>
+    ///     The solution to add reagent to.
+    /// </summary>
+    [DataField]
+    public Entity<SolutionComponent>? Solution;
 
     /// <summary>
     ///     The amount of reagent to be generated on update.
index c07ba12dd5b195ad7c251511939fe99b9fbf5d3e..ef43c2c89d0e8d7de39fd93a6d58c39dea290948 100644 (file)
@@ -1,7 +1,7 @@
 using Content.Server.Animals.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Popups;
 using Content.Shared.Chemistry.Components;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.DoAfter;
 using Content.Shared.IdentityManagement;
 using Content.Shared.Mobs.Systems;
@@ -61,11 +61,11 @@ internal sealed class UdderSystem : EntitySystem
                 _hunger.ModifyHunger(uid, -udder.HungerUsage, hunger);
             }
 
-            if (!_solutionContainerSystem.TryGetSolution(uid, udder.Solution, out var solution))
+            if (!_solutionContainerSystem.ResolveSolution(uid, udder.SolutionName, ref udder.Solution))
                 continue;
 
             //TODO: toxins from bloodstream !?
-            _solutionContainerSystem.TryAddReagent(uid, solution, udder.ReagentId, udder.QuantityPerUpdate, out _);
+            _solutionContainerSystem.TryAddReagent(udder.Solution.Value, udder.ReagentId, udder.QuantityPerUpdate, out _);
         }
     }
 
@@ -85,47 +85,50 @@ internal sealed class UdderSystem : EntitySystem
         _doAfterSystem.TryStartDoAfter(doargs);
     }
 
-    private void OnDoAfter(EntityUid uid, UdderComponent component, MilkingDoAfterEvent args)
+    private void OnDoAfter(Entity<UdderComponent> entity, ref MilkingDoAfterEvent args)
     {
         if (args.Cancelled || args.Handled || args.Args.Used == null)
             return;
 
-        if (!_solutionContainerSystem.TryGetSolution(uid, component.Solution, out var solution))
+        if (!_solutionContainerSystem.ResolveSolution(entity.Owner, entity.Comp.SolutionName, ref entity.Comp.Solution, out var solution))
             return;
 
-        if (!_solutionContainerSystem.TryGetRefillableSolution(args.Args.Used.Value, out var targetSolution))
+        if (!_solutionContainerSystem.TryGetRefillableSolution(args.Args.Used.Value, out var targetSoln, out var targetSolution))
             return;
 
         args.Handled = true;
         var quantity = solution.Volume;
         if (quantity == 0)
         {
-            _popupSystem.PopupEntity(Loc.GetString("udder-system-dry"), uid, args.Args.User);
+            _popupSystem.PopupEntity(Loc.GetString("udder-system-dry"), entity.Owner, args.Args.User);
             return;
         }
 
         if (quantity > targetSolution.AvailableVolume)
             quantity = targetSolution.AvailableVolume;
 
-        var split = _solutionContainerSystem.SplitSolution(uid, solution, quantity);
-        _solutionContainerSystem.TryAddSolution(args.Args.Used.Value, targetSolution, split);
+        var split = _solutionContainerSystem.SplitSolution(entity.Comp.Solution.Value, quantity);
+        _solutionContainerSystem.TryAddSolution(targetSoln.Value, split);
 
-        _popupSystem.PopupEntity(Loc.GetString("udder-system-success", ("amount", quantity), ("target", Identity.Entity(args.Args.Used.Value, EntityManager))), uid,
+        _popupSystem.PopupEntity(Loc.GetString("udder-system-success", ("amount", quantity), ("target", Identity.Entity(args.Args.Used.Value, EntityManager))), entity.Owner,
             args.Args.User, PopupType.Medium);
     }
 
-    private void AddMilkVerb(EntityUid uid, UdderComponent component, GetVerbsEvent<AlternativeVerb> args)
+    private void AddMilkVerb(Entity<UdderComponent> entity, ref GetVerbsEvent<AlternativeVerb> args)
     {
         if (args.Using == null ||
              !args.CanInteract ||
              !EntityManager.HasComponent<RefillableSolutionComponent>(args.Using.Value))
             return;
 
+        var uid = entity.Owner;
+        var user = args.User;
+        var used = args.Using.Value;
         AlternativeVerb verb = new()
         {
             Act = () =>
             {
-                AttemptMilk(uid, args.User, args.Using.Value);
+                AttemptMilk(uid, user, used);
             },
             Text = Loc.GetString("udder-system-verb-milk"),
             Priority = 2
index 6cf8d8c88f303ee18b8cb186ff315e7a73cda8d6..cbe959fb560b1949c03f790f0dc38ef8f9d38d2d 100644 (file)
@@ -1,6 +1,6 @@
 using Content.Server.Animals.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Nutrition;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Mobs.Systems;
 using Content.Shared.Nutrition.Components;
 using Content.Shared.Nutrition.EntitySystems;
@@ -52,10 +52,10 @@ public sealed class WoolySystem : EntitySystem
                 _hunger.ModifyHunger(uid, -wooly.HungerUsage, hunger);
             }
 
-            if (!_solutionContainer.TryGetSolution(uid, wooly.Solution, out var solution))
+            if (!_solutionContainer.ResolveSolution(uid, wooly.SolutionName, ref wooly.Solution))
                 continue;
 
-            _solutionContainer.TryAddReagent(uid, solution, wooly.ReagentId, wooly.Quantity, out _);
+            _solutionContainer.TryAddReagent(wooly.Solution.Value, wooly.ReagentId, wooly.Quantity, out _);
         }
     }
 
index aa860c171350870c9198a6ea618bc6d44ec5614c..49c62aec81d6f642192c897213d03859c24cf11b 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Server.Anomaly.Effects;
+using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Reagent;
 using Robust.Shared.Audio;
 using Robust.Shared.Prototypes;
@@ -85,10 +86,17 @@ public sealed partial class ReagentProducerAnomalyComponent : Component
     /// </summary>
     [DataField, ViewVariables(VVAccess.ReadWrite)]
     public ProtoId<ReagentPrototype> ProducingReagent = "Water";
+
     /// <summary>
     /// Solution name where the substance is generated
     /// </summary>
     [ViewVariables(VVAccess.ReadWrite)]
     [DataField("solution")]
-    public string Solution = "default";
+    public string SolutionName = "default";
+
+    /// <summary>
+    /// Solution where the substance is generated
+    /// </summary>
+    [DataField("solutionRef")]
+    public Entity<SolutionComponent>? Solution = null;
 }
index 05f1c5c61c64f56ccd882f8539773704f7ac47d0..2bef32e322816733c67427feb91bc25e1f2f70dc 100644 (file)
@@ -1,8 +1,8 @@
-using System.Linq;
 using Content.Server.Anomaly.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Anomaly.Components;
 using Content.Shared.Chemistry.Components.SolutionManager;
-using Content.Shared.Chemistry.EntitySystems;
+using System.Linq;
 
 namespace Content.Server.Anomaly.Effects;
 /// <summary>
@@ -27,41 +27,40 @@ public sealed class InjectionAnomalySystem : EntitySystem
         _injectableQuery = GetEntityQuery<InjectableSolutionComponent>();
     }
 
-    private void OnPulse(EntityUid uid, InjectionAnomalyComponent component, ref AnomalyPulseEvent args)
+    private void OnPulse(Entity<InjectionAnomalyComponent> entity, ref AnomalyPulseEvent args)
     {
-        PulseScalableEffect(uid, component, component.InjectRadius, component.MaxSolutionInjection * args.Severity);
+        PulseScalableEffect(entity, entity.Comp.InjectRadius, entity.Comp.MaxSolutionInjection * args.Severity);
     }
 
-    private void OnSupercritical(EntityUid uid, InjectionAnomalyComponent component, ref AnomalySupercriticalEvent args)
+    private void OnSupercritical(Entity<InjectionAnomalyComponent> entity, ref AnomalySupercriticalEvent args)
     {
-        PulseScalableEffect(uid, component, component.SuperCriticalInjectRadius, component.SuperCriticalSolutionInjection);
+        PulseScalableEffect(entity, entity.Comp.SuperCriticalInjectRadius, entity.Comp.SuperCriticalSolutionInjection);
     }
 
-    private void PulseScalableEffect(EntityUid uid, InjectionAnomalyComponent component, float injectRadius, float maxInject)
+    private void PulseScalableEffect(Entity<InjectionAnomalyComponent> entity, float injectRadius, float maxInject)
     {
-        if (!_solutionContainer.TryGetSolution(uid, component.Solution, out var sol))
+        if (!_solutionContainer.TryGetSolution(entity.Owner, entity.Comp.Solution, out _, out var sol))
             return;
+
         //We get all the entity in the radius into which the reagent will be injected.
         var xformQuery = GetEntityQuery<TransformComponent>();
-        var xform = xformQuery.GetComponent(uid);
+        var xform = xformQuery.GetComponent(entity);
         var allEnts = _lookup.GetEntitiesInRange<InjectableSolutionComponent>(xform.MapPosition, injectRadius)
             .Select(x => x.Owner).ToList();
 
         //for each matching entity found
         foreach (var ent in allEnts)
         {
-            if (!_solutionContainer.TryGetInjectableSolution(ent, out var injectable))
+            if (!_solutionContainer.TryGetInjectableSolution(ent, out var injectable, out _))
                 continue;
 
             if (_injectableQuery.TryGetComponent(ent, out var injEnt))
             {
-                var buffer = sol;
-                _solutionContainer.TryTransferSolution(ent, injectable, buffer, maxInject);
+                _solutionContainer.TryTransferSolution(injectable.Value, sol, maxInject);
                 //Spawn Effect
                 var uidXform = Transform(ent);
-                Spawn(component.VisualEffectPrototype, uidXform.Coordinates);
+                Spawn(entity.Comp.VisualEffectPrototype, uidXform.Coordinates);
             }
         }
     }
-
 }
index 9855e66cfe33c462304a04ee9e31f110bf8f022e..90177bae8e3e4e8e84358356f60929f1e25d5edd 100644 (file)
@@ -1,7 +1,7 @@
 using Content.Server.Anomaly.Components;
-using Content.Shared.Anomaly.Components;
-using Content.Shared.Chemistry.EntitySystems;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Fluids.EntitySystems;
+using Content.Shared.Anomaly.Components;
 
 namespace Content.Server.Anomaly.Effects;
 
@@ -19,21 +19,21 @@ public sealed class PuddleCreateAnomalySystem : EntitySystem
         SubscribeLocalEvent<PuddleCreateAnomalyComponent, AnomalySupercriticalEvent>(OnSupercritical, before: new[] { typeof(InjectionAnomalySystem) });
     }
 
-    private void OnPulse(EntityUid uid, PuddleCreateAnomalyComponent component, ref AnomalyPulseEvent args)
+    private void OnPulse(Entity<PuddleCreateAnomalyComponent> entity, ref AnomalyPulseEvent args)
     {
-        if (!_solutionContainer.TryGetSolution(uid, component.Solution, out var sol))
+        if (!_solutionContainer.TryGetSolution(entity.Owner, entity.Comp.Solution, out var sol, out _))
             return;
 
-        var xform = Transform(uid);
-        var puddleSol = _solutionContainer.SplitSolution(uid, sol, component.MaxPuddleSize * args.Severity);
-        _puddle.TrySplashSpillAt(uid, xform.Coordinates, puddleSol, out _);
+        var xform = Transform(entity.Owner);
+        var puddleSol = _solutionContainer.SplitSolution(sol.Value, entity.Comp.MaxPuddleSize * args.Severity);
+        _puddle.TrySplashSpillAt(entity.Owner, xform.Coordinates, puddleSol, out _);
     }
-    private void OnSupercritical(EntityUid uid, PuddleCreateAnomalyComponent component, ref AnomalySupercriticalEvent args)
+    private void OnSupercritical(Entity<PuddleCreateAnomalyComponent> entity, ref AnomalySupercriticalEvent args)
     {
-        if (!_solutionContainer.TryGetSolution(uid, component.Solution, out var sol))
+        if (!_solutionContainer.TryGetSolution(entity.Owner, entity.Comp.Solution, out _, out var sol))
             return;
-        var buffer = sol;
-        var xform = Transform(uid);
-        _puddle.TrySpillAt(xform.Coordinates, buffer, out _);
+
+        var xform = Transform(entity.Owner);
+        _puddle.TrySpillAt(xform.Coordinates, sol, out _);
     }
 }
index 487d20f43f01a91a1f211f31e14354a917f9e575..0e49c5ee56b03c57a2c56dd76250d5df6dafd9cc 100644 (file)
@@ -1,12 +1,12 @@
 using Content.Server.Anomaly.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Anomaly.Components;
-using Robust.Shared.Random;
 using Content.Shared.Chemistry.Components;
-using Content.Shared.Chemistry.EntitySystems;
-using Robust.Shared.Prototypes;
 using Content.Shared.Sprite;
 using Robust.Server.GameObjects;
 using Robust.Shared.Audio.Systems;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Random;
 
 namespace Content.Server.Anomaly.Effects;
 
@@ -42,17 +42,17 @@ public sealed class ReagentProducerAnomalySystem : EntitySystem
         SubscribeLocalEvent<ReagentProducerAnomalyComponent, MapInitEvent>(OnMapInit);
     }
 
-    private void OnPulse(EntityUid uid, ReagentProducerAnomalyComponent component, ref AnomalyPulseEvent args)
+    private void OnPulse(Entity<ReagentProducerAnomalyComponent> entity, ref AnomalyPulseEvent args)
     {
         if (_random.NextFloat(0.0f, 1.0f) > args.Stability)
-            ChangeReagent(uid, component, args.Severity);
+            ChangeReagent(entity, args.Severity);
     }
 
-    private void ChangeReagent(EntityUid uid, ReagentProducerAnomalyComponent component, float severity)
+    private void ChangeReagent(Entity<ReagentProducerAnomalyComponent> entity, float severity)
     {
-        var reagent = GetRandomReagentType(uid, component, severity);
-        component.ProducingReagent = reagent;
-        _audio.PlayPvs(component.ChangeSound, uid);
+        var reagent = GetRandomReagentType(entity, severity);
+        entity.Comp.ProducingReagent = reagent;
+        _audio.PlayPvs(entity.Comp.ChangeSound, entity);
     }
 
     //reagent realtime generation
@@ -68,7 +68,7 @@ public sealed class ReagentProducerAnomalySystem : EntitySystem
             if (component.AccumulatedFrametime < component.UpdateInterval)
                 continue;
 
-            if (!_solutionContainer.TryGetSolution(uid, component.Solution, out var producerSol))
+            if (!_solutionContainer.ResolveSolution(uid, component.SolutionName, ref component.Solution, out var producerSolution))
                 continue;
 
             Solution newSol = new();
@@ -76,7 +76,7 @@ public sealed class ReagentProducerAnomalySystem : EntitySystem
             if (anomaly.Severity >= 0.97) reagentProducingAmount *= component.SupercriticalReagentProducingModifier;
 
             newSol.AddReagent(component.ProducingReagent, reagentProducingAmount);
-            _solutionContainer.TryAddSolution(uid, producerSol, newSol); //TO DO - the container is not fully filled.
+            _solutionContainer.TryAddSolution(component.Solution.Value, newSol); //TO DO - the container is not fully filled. 
 
             component.AccumulatedFrametime = 0;
 
@@ -87,7 +87,7 @@ public sealed class ReagentProducerAnomalySystem : EntitySystem
             // and nothing worked out for me. So for now it will be like this.
             if (component.NeedRecolor)
             {
-                var color = producerSol.GetColor(_prototypeManager);
+                var color = producerSolution.GetColor(_prototypeManager);
                 _light.SetColor(uid, color);
                 if (TryComp<RandomSpriteComponent>(uid, out var randomSprite))
                 {
@@ -103,9 +103,9 @@ public sealed class ReagentProducerAnomalySystem : EntitySystem
         }
     }
 
-    private void OnMapInit(EntityUid uid, ReagentProducerAnomalyComponent component, MapInitEvent args)
+    private void OnMapInit(Entity<ReagentProducerAnomalyComponent> entity, ref MapInitEvent args)
     {
-        ChangeReagent(uid, component, 0.1f); //MapInit Reagent 100% change
+        ChangeReagent(entity, 0.1f); //MapInit Reagent 100% change
     }
 
     // returns a random reagent based on a system of random weights.
@@ -117,33 +117,33 @@ public sealed class ReagentProducerAnomalySystem : EntitySystem
     // After that, a random reagent in the selected category is selected.
     //
     // Such a system is made to control the danger and interest of the anomaly more.
-    private string GetRandomReagentType(EntityUid uid, ReagentProducerAnomalyComponent component, float severity)
+    private string GetRandomReagentType(Entity<ReagentProducerAnomalyComponent> entity, float severity)
     {
         //Category Weight Randomization
-        var currentWeightDangerous = MathHelper.Lerp(component.WeightSpreadDangerous.X, component.WeightSpreadDangerous.Y, severity);
-        var currentWeightFun = MathHelper.Lerp(component.WeightSpreadFun.X, component.WeightSpreadFun.Y, severity);
-        var currentWeightUseful = MathHelper.Lerp(component.WeightSpreadUseful.X, component.WeightSpreadUseful.Y, severity);
+        var currentWeightDangerous = MathHelper.Lerp(entity.Comp.WeightSpreadDangerous.X, entity.Comp.WeightSpreadDangerous.Y, severity);
+        var currentWeightFun = MathHelper.Lerp(entity.Comp.WeightSpreadFun.X, entity.Comp.WeightSpreadFun.Y, severity);
+        var currentWeightUseful = MathHelper.Lerp(entity.Comp.WeightSpreadUseful.X, entity.Comp.WeightSpreadUseful.Y, severity);
 
         var sumWeight = currentWeightDangerous + currentWeightFun + currentWeightUseful;
         var rnd = _random.NextFloat(0f, sumWeight);
         //Dangerous
-        if (rnd <= currentWeightDangerous && component.DangerousChemicals.Count > 0)
+        if (rnd <= currentWeightDangerous && entity.Comp.DangerousChemicals.Count > 0)
         {
-            var reagent = _random.Pick(component.DangerousChemicals);
+            var reagent = _random.Pick(entity.Comp.DangerousChemicals);
             return reagent;
         }
         else rnd -= currentWeightDangerous;
         //Fun
-        if (rnd <= currentWeightFun && component.FunChemicals.Count > 0)
+        if (rnd <= currentWeightFun && entity.Comp.FunChemicals.Count > 0)
         {
-            var reagent = _random.Pick(component.FunChemicals);
+            var reagent = _random.Pick(entity.Comp.FunChemicals);
             return reagent;
         }
         else rnd -= currentWeightFun;
         //Useful
-        if (rnd <= currentWeightUseful && component.UsefulChemicals.Count > 0)
+        if (rnd <= currentWeightUseful && entity.Comp.UsefulChemicals.Count > 0)
         {
-            var reagent = _random.Pick(component.UsefulChemicals);
+            var reagent = _random.Pick(entity.Comp.UsefulChemicals);
             return reagent;
         }
         //We should never end up here.
index 1db0b524bb6569c5e264e152f16286484225d75b..c25c010708f987c61fbaf9049a86200d8cae3728 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Server.Atmos.Piping.Unary.EntitySystems;
+using Content.Shared.Chemistry.Components;
 
 namespace Content.Server.Atmos.Piping.Unary.Components;
 
@@ -21,6 +22,12 @@ public sealed partial class GasCondenserComponent : Component
     [DataField]
     public string SolutionId = "tank";
 
+    /// <summary>
+    /// The solution that gases are condensed into.
+    /// </summary>
+    [DataField]
+    public Entity<SolutionComponent>? Solution = null;
+
     /// <summary>
     /// For a condenser, how many U of reagents are given per each mole of gas.
     /// </summary>
index f9ed614d9cf2cdce913a3ef226cfdb07883ec908..491bf60062734968cf530435b5ee3f8a96053baa 100644 (file)
@@ -5,10 +5,11 @@ using Content.Server.NodeContainer;
 using Content.Server.NodeContainer.EntitySystems;
 using Content.Server.NodeContainer.Nodes;
 using Content.Server.Power.Components;
-using Content.Shared.Atmos;
-using JetBrains.Annotations;
 using Content.Server.Power.EntitySystems;
+using Content.Shared.Atmos;
 using Content.Shared.Chemistry.EntitySystems;
+using Content.Shared.FixedPoint;
+using JetBrains.Annotations;
 
 namespace Content.Server.Atmos.Piping.Unary.EntitySystems;
 
@@ -18,7 +19,7 @@ public sealed class GasCondenserSystem : EntitySystem
     [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
     [Dependency] private readonly PowerReceiverSystem _power = default!;
     [Dependency] private readonly NodeContainerSystem _nodeContainer = default!;
-    [Dependency] private readonly SolutionContainerSystem _solution = default!;
+    [Dependency] private readonly SharedSolutionContainerSystem _solution = default!;
 
     public override void Initialize()
     {
@@ -27,12 +28,12 @@ public sealed class GasCondenserSystem : EntitySystem
         SubscribeLocalEvent<GasCondenserComponent, AtmosDeviceUpdateEvent>(OnCondenserUpdated);
     }
 
-    private void OnCondenserUpdated(EntityUid uid, GasCondenserComponent component, ref AtmosDeviceUpdateEvent args)
+    private void OnCondenserUpdated(Entity<GasCondenserComponent> entity, ref AtmosDeviceUpdateEvent args)
     {
-        if (!(_power.IsPowered(uid) && TryComp<ApcPowerReceiverComponent>(uid, out var receiver))
-            || !TryComp<NodeContainerComponent>(uid, out var nodeContainer)
-            || !_nodeContainer.TryGetNode(nodeContainer, component.Inlet, out PipeNode? inlet)
-            || !_solution.TryGetSolution(uid, component.SolutionId, out var solution))
+        if (!(_power.IsPowered(entity) && TryComp<ApcPowerReceiverComponent>(entity, out var receiver))
+            || !TryComp<NodeContainerComponent>(entity, out var nodeContainer)
+            || !_nodeContainer.TryGetNode(nodeContainer, entity.Comp.Inlet, out PipeNode? inlet)
+            || !_solution.ResolveSolution(entity.Owner, entity.Comp.SolutionId, ref entity.Comp.Solution, out var solution))
         {
             return;
         }
@@ -48,18 +49,21 @@ public sealed class GasCondenserSystem : EntitySystem
             if (moles <= 0)
                 continue;
 
-            if (_atmosphereSystem.GetGas(i).Reagent is not {} gasReagent)
+            if (_atmosphereSystem.GetGas(i).Reagent is not { } gasReagent)
                 continue;
 
-            var moleToReagentMultiplier = component.MolesToReagentMultiplier;
-            var amount = moles * moleToReagentMultiplier;
-
-            if (_solution.TryAddReagent(uid, solution, gasReagent, amount, out var remaining))
+            var moleToReagentMultiplier = entity.Comp.MolesToReagentMultiplier;
+            var amount = FixedPoint2.Min(FixedPoint2.New(moles * moleToReagentMultiplier), solution.AvailableVolume);
+            if (amount <= 0)
                 continue;
 
+            solution.AddReagent(gasReagent, amount);
+
             // if we have leftover reagent, then convert it back to moles and put it back in the mixture.
-            inlet.Air.AdjustMoles(i, remaining.Float() / moleToReagentMultiplier);
+            inlet.Air.AdjustMoles(i, moles - (amount.Float() / moleToReagentMultiplier));
         }
+
+        _solution.UpdateChemicals(entity.Comp.Solution.Value);
     }
 
     public float NumberOfMolesToConvert(ApcPowerReceiverComponent comp, GasMixture mix, float dt)
index 33db12157e9d5bc09a6fc33a49200fa8371a9c70..3ee8fe4d170ed538779e9b0a40b65d0eca9c244f 100644 (file)
@@ -34,52 +34,52 @@ namespace Content.Server.Body.Components
         /// <summary>
         ///     How much should bleeding should be reduced every update interval?
         /// </summary>
-        [DataField("bleedReductionAmount")]
+        [DataField]
         public float BleedReductionAmount = 1.0f;
 
         /// <summary>
         ///     How high can <see cref="BleedAmount"/> go?
         /// </summary>
-        [DataField("maxBleedAmount")]
+        [DataField]
         public float MaxBleedAmount = 10.0f;
 
         /// <summary>
         ///     What percentage of current blood is necessary to avoid dealing blood loss damage?
         /// </summary>
-        [DataField("bloodlossThreshold")]
+        [DataField]
         public float BloodlossThreshold = 0.9f;
 
         /// <summary>
         ///     The base bloodloss damage to be incurred if below <see cref="BloodlossThreshold"/>
         ///     The default values are defined per mob/species in YML.
         /// </summary>
-        [DataField("bloodlossDamage", required: true)]
+        [DataField(required: true)]
         public DamageSpecifier BloodlossDamage = new();
 
         /// <summary>
         ///     The base bloodloss damage to be healed if above <see cref="BloodlossThreshold"/>
         ///     The default values are defined per mob/species in YML.
         /// </summary>
-        [DataField("bloodlossHealDamage", required: true)]
+        [DataField(required: true)]
         public DamageSpecifier BloodlossHealDamage = new();
 
         /// <summary>
         ///     How frequently should this bloodstream update, in seconds?
         /// </summary>
-        [DataField("updateInterval")]
+        [DataField]
         public float UpdateInterval = 3.0f;
 
         // TODO shouldn't be hardcoded, should just use some organ simulation like bone marrow or smth.
         /// <summary>
         ///     How much reagent of blood should be restored each update interval?
         /// </summary>
-        [DataField("bloodRefreshAmount")]
+        [DataField]
         public float BloodRefreshAmount = 1.0f;
 
         /// <summary>
         ///     How much blood needs to be in the temporary solution in order to create a puddle?
         /// </summary>
-        [DataField("bleedPuddleThreshold")]
+        [DataField]
         public FixedPoint2 BleedPuddleThreshold = 1.0f;
 
         /// <summary>
@@ -89,19 +89,19 @@ namespace Content.Server.Body.Components
         /// <remarks>
         ///     For example, piercing damage is increased while poison damage is nullified entirely.
         /// </remarks>
-        [DataField("damageBleedModifiers", customTypeSerializer:typeof(PrototypeIdSerializer<DamageModifierSetPrototype>))]
+        [DataField(customTypeSerializer:typeof(PrototypeIdSerializer<DamageModifierSetPrototype>))]
         public string DamageBleedModifiers = "BloodlossHuman";
 
         /// <summary>
         ///     The sound to be played when a weapon instantly deals blood loss damage.
         /// </summary>
-        [DataField("instantBloodSound")]
+        [DataField]
         public SoundSpecifier InstantBloodSound = new SoundCollectionSpecifier("blood");
 
         /// <summary>
         ///     The sound to be played when some damage actually heals bleeding rather than starting it.
         /// </summary>
-        [DataField("bloodHealedSound")]
+        [DataField]
         public SoundSpecifier BloodHealedSound = new SoundPathSpecifier("/Audio/Effects/lightburn.ogg");
 
         // TODO probably damage bleed thresholds.
@@ -109,14 +109,14 @@ namespace Content.Server.Body.Components
         /// <summary>
         ///     Max volume of internal chemical solution storage
         /// </summary>
-        [DataField("chemicalMaxVolume")]
+        [DataField]
         public FixedPoint2 ChemicalMaxVolume = FixedPoint2.New(250);
 
         /// <summary>
         ///     Max volume of internal blood storage,
         ///     and starting level of blood.
         /// </summary>
-        [DataField("bloodMaxVolume")]
+        [DataField]
         public FixedPoint2 BloodMaxVolume = FixedPoint2.New(300);
 
         /// <summary>
@@ -125,29 +125,40 @@ namespace Content.Server.Body.Components
         /// <remarks>
         ///     Slime-people might use slime as their blood or something like that.
         /// </remarks>
-        [DataField("bloodReagent")]
+        [DataField]
         public string BloodReagent = "Blood";
 
+        /// <summary>Name/Key that <see cref="BloodSolution"/> is indexed by.</summary>
+        [DataField]
+        public string BloodSolutionName = DefaultBloodSolutionName;
+
+        /// <summary>Name/Key that <see cref="ChemicalSolution"/> is indexed by.</summary>
+        [DataField]
+        public string ChemicalSolutionName = DefaultChemicalsSolutionName;
+
+        /// <summary>Name/Key that <see cref="TemporarySolution"/> is indexed by.</summary>
+        [DataField]
+        public string BloodTemporarySolutionName = DefaultBloodTemporarySolutionName;
+
         /// <summary>
-        ///     Internal solution for reagent storage
+        ///     Internal solution for blood storage
         /// </summary>
-        [ViewVariables(VVAccess.ReadWrite)]
-        [Access(typeof(BloodstreamSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends
-        public Solution ChemicalSolution = default!;
+        [DataField]
+        public Entity<SolutionComponent>? BloodSolution = null;
 
         /// <summary>
-        ///     Internal solution for blood storage
+        ///     Internal solution for reagent storage
         /// </summary>
-        [ViewVariables(VVAccess.ReadWrite)]
-        public Solution BloodSolution = default!;
+        [DataField]
+        public Entity<SolutionComponent>? ChemicalSolution = null;
 
         /// <summary>
         ///     Temporary blood solution.
         ///     When blood is lost, it goes to this solution, and when this
         ///     solution hits a certain cap, the blood is actually spilled as a puddle.
         /// </summary>
-        [ViewVariables(VVAccess.ReadWrite)]
-        public Solution BloodTemporarySolution = default!;
+        [DataField]
+        public Entity<SolutionComponent>? TemporarySolution = null;
 
         /// <summary>
         /// Variable that stores the amount of status time added by having a low blood level.
index 434286a5c6dd48583f68e73b907e49f9d9e5bdfb..1d997a995024f43367ad36d313bee8826abe0d4c 100644 (file)
@@ -8,7 +8,7 @@ namespace Content.Server.Body.Components;
 [RegisterComponent, Access(typeof(LungSystem))]
 public sealed partial class LungComponent : Component
 {
-    [DataField("air")]
+    [DataField]
     [Access(typeof(LungSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends
     public GasMixture Air { get; set; } = new()
     {
@@ -16,7 +16,15 @@ public sealed partial class LungComponent : Component
         Temperature = Atmospherics.NormalBodyTemperature
     };
 
-    [ViewVariables]
-    [Access(typeof(LungSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends
-    public Solution LungSolution = default!;
+    /// <summary>
+    /// The name/key of the solution on this entity which these lungs act on.
+    /// </summary>
+    [DataField]
+    public string SolutionName = LungSystem.LungSolutionName;
+
+    /// <summary>
+    /// The solution on this entity that these lungs act on.
+    /// </summary>
+    [DataField]
+    public Entity<SolutionComponent>? Solution = null;
 }
index 21a0be25d2b68bea2923f68e71947e9aea1d4d3c..a8c82f3d369cd43a2bd98b185ff381493100d201 100644 (file)
@@ -18,7 +18,7 @@ namespace Content.Server.Body.Components
         ///     How often to metabolize reagents, in seconds.
         /// </summary>
         /// <returns></returns>
-        [DataField("updateFrequency")]
+        [DataField]
         public float UpdateFrequency = 1.0f;
 
         /// <summary>
@@ -33,13 +33,13 @@ namespace Content.Server.Body.Components
         /// <remarks>
         ///     Most things will use the parent entity (bloodstream).
         /// </remarks>
-        [DataField("solutionOnBody")]
+        [DataField]
         public bool SolutionOnBody = true;
 
         /// <summary>
         ///     List of metabolizer types that this organ is. ex. Human, Slime, Felinid, w/e.
         /// </summary>
-        [DataField("metabolizerTypes", customTypeSerializer:typeof(PrototypeIdHashSetSerializer<MetabolizerTypePrototype>))]
+        [DataField(customTypeSerializer:typeof(PrototypeIdHashSetSerializer<MetabolizerTypePrototype>))]
         [Access(typeof(MetabolizerSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends
         public HashSet<string>? MetabolizerTypes = null;
 
@@ -47,7 +47,7 @@ namespace Content.Server.Body.Components
         ///     Should this metabolizer remove chemicals that have no metabolisms defined?
         ///     As a stop-gap, basically.
         /// </summary>
-        [DataField("removeEmpty")]
+        [DataField]
         public bool RemoveEmpty = false;
 
         /// <summary>
@@ -72,7 +72,7 @@ namespace Content.Server.Body.Components
     [DataDefinition]
     public sealed partial class MetabolismGroupEntry
     {
-        [DataField("id", required: true, customTypeSerializer:typeof(PrototypeIdSerializer<MetabolismGroupPrototype>))]
+        [DataField(required: true, customTypeSerializer:typeof(PrototypeIdSerializer<MetabolismGroupPrototype>))]
         public string Id = default!;
 
         [DataField("rateModifier")]
index 30f97915252c32a37c2de848d43c625c2a4472f8..fe93468f74eed9a5c73a0fb04a27721798cc3ce8 100644 (file)
@@ -2,7 +2,6 @@
 using Content.Server.Nutrition.EntitySystems;
 using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Reagent;
-using Content.Shared.FixedPoint;
 using Content.Shared.Whitelist;
 
 namespace Content.Server.Body.Components
@@ -15,26 +14,32 @@ namespace Content.Server.Body.Components
         /// <summary>
         ///     How fast should this component update, in seconds?
         /// </summary>
-        [DataField("updateInterval")]
+        [DataField]
         public float UpdateInterval = 1.0f;
 
+        /// <summary>
+        ///     The solution inside of this stomach this transfers reagents to the body.
+        /// </summary>
+        [DataField]
+        public Entity<SolutionComponent>? Solution = null;
+
         /// <summary>
         ///     What solution should this stomach push reagents into, on the body?
         /// </summary>
-        [DataField("bodySolutionName")]
+        [DataField]
         public string BodySolutionName = BloodstreamComponent.DefaultChemicalsSolutionName;
 
         /// <summary>
         ///     Time in seconds between reagents being ingested and them being
         ///     transferred to <see cref="BloodstreamComponent"/>
         /// </summary>
-        [DataField("digestionDelay")]
+        [DataField]
         public float DigestionDelay = 20;
 
         /// <summary>
         ///     A whitelist for what special-digestible-required foods this stomach is capable of eating.
         /// </summary>
-        [DataField("specialDigestible")]
+        [DataField]
         public EntityWhitelist? SpecialDigestible = null;
 
         /// <summary>
index cb83625615bce55cc1ce503e916099728777e422..21dc2f4526c65e74f99789305895dfeabe18a86a 100644 (file)
@@ -1,6 +1,8 @@
 using Content.Server.Body.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Chemistry.ReactionEffects;
 using Content.Server.Fluids.EntitySystems;
+using Content.Server.Forensics;
 using Content.Server.HealthExaminable;
 using Content.Server.Popups;
 using Content.Shared.Alert;
@@ -16,13 +18,9 @@ using Content.Shared.Mobs.Systems;
 using Content.Shared.Popups;
 using Content.Shared.Rejuvenate;
 using Content.Shared.Speech.EntitySystems;
-using Robust.Server.GameObjects;
+using Robust.Server.Audio;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Random;
-using Content.Shared.Speech.EntitySystems;
-using Robust.Server.Audio;
-using Robust.Shared.GameObjects;
-using Content.Server.Forensics;
 
 namespace Content.Server.Body.Systems;
 
@@ -51,14 +49,13 @@ public sealed class BloodstreamSystem : EntitySystem
         SubscribeLocalEvent<BloodstreamComponent, BeingGibbedEvent>(OnBeingGibbed);
         SubscribeLocalEvent<BloodstreamComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
         SubscribeLocalEvent<BloodstreamComponent, ReactionAttemptEvent>(OnReactionAttempt);
+        SubscribeLocalEvent<BloodstreamComponent, SolutionRelayEvent<ReactionAttemptEvent>>(OnReactionAttempt);
         SubscribeLocalEvent<BloodstreamComponent, RejuvenateEvent>(OnRejuvenate);
     }
 
-    private void OnReactionAttempt(EntityUid uid, BloodstreamComponent component, ReactionAttemptEvent args)
+    private void OnReactionAttempt(Entity<BloodstreamComponent> entity, ref ReactionAttemptEvent args)
     {
-        if (args.Solution.Name != BloodstreamComponent.DefaultBloodSolutionName
-            && args.Solution.Name != BloodstreamComponent.DefaultChemicalsSolutionName
-            && args.Solution.Name != BloodstreamComponent.DefaultBloodTemporarySolutionName)
+        if (args.Cancelled)
             return;
 
         foreach (var effect in args.Reaction.Effects)
@@ -67,7 +64,7 @@ public sealed class BloodstreamSystem : EntitySystem
             {
                 case CreateEntityReactionEffect: // Prevent entities from spawning in the bloodstream
                 case AreaReactionEffect: // No spontaneous smoke or foam leaking out of blood vessels.
-                    args.Cancel();
+                    args.Cancelled = true;
                     return;
             }
         }
@@ -81,6 +78,16 @@ public sealed class BloodstreamSystem : EntitySystem
         // Having cheese-clots form in your veins can't be good for you.
     }
 
+    private void OnReactionAttempt(Entity<BloodstreamComponent> entity, ref SolutionRelayEvent<ReactionAttemptEvent> args)
+    {
+        if (args.Name != entity.Comp.BloodSolutionName
+            && args.Name != entity.Comp.ChemicalSolutionName
+            && args.Name != entity.Comp.BloodTemporarySolutionName)
+            return;
+
+        OnReactionAttempt(entity, ref args.Event);
+    }
+
     public override void Update(float frameTime)
     {
         base.Update(frameTime);
@@ -95,8 +102,11 @@ public sealed class BloodstreamSystem : EntitySystem
 
             bloodstream.AccumulatedFrametime -= bloodstream.UpdateInterval;
 
+            if (!_solutionContainerSystem.ResolveSolution(uid, bloodstream.BloodSolutionName, ref bloodstream.BloodSolution, out var bloodSolution))
+                continue;
+
             // Adds blood to their blood level if it is below the maximum; Blood regeneration. Must be alive.
-            if (bloodstream.BloodSolution.Volume < bloodstream.BloodSolution.MaxVolume && !_mobStateSystem.IsDead(uid))
+            if (bloodSolution.Volume < bloodSolution.MaxVolume && !_mobStateSystem.IsDead(uid))
             {
                 TryModifyBloodLevel(uid, bloodstream.BloodRefreshAmount, bloodstream);
             }
@@ -143,19 +153,18 @@ public sealed class BloodstreamSystem : EntitySystem
         }
     }
 
-    private void OnComponentInit(EntityUid uid, BloodstreamComponent component, ComponentInit args)
+    private void OnComponentInit(Entity<BloodstreamComponent> entity, ref ComponentInit args)
     {
-        component.ChemicalSolution = _solutionContainerSystem.EnsureSolution(uid, BloodstreamComponent.DefaultChemicalsSolutionName);
-        component.BloodSolution = _solutionContainerSystem.EnsureSolution(uid, BloodstreamComponent.DefaultBloodSolutionName);
-        component.BloodTemporarySolution = _solutionContainerSystem.EnsureSolution(uid, BloodstreamComponent.DefaultBloodTemporarySolutionName);
+        var chemicalSolution = _solutionContainerSystem.EnsureSolution(entity.Owner, entity.Comp.ChemicalSolutionName);
+        var bloodSolution = _solutionContainerSystem.EnsureSolution(entity.Owner, entity.Comp.BloodSolutionName);
+        var tempSolution = _solutionContainerSystem.EnsureSolution(entity.Owner, entity.Comp.BloodTemporarySolutionName);
 
-        component.ChemicalSolution.MaxVolume = component.ChemicalMaxVolume;
-        component.BloodSolution.MaxVolume = component.BloodMaxVolume;
-        component.BloodTemporarySolution.MaxVolume = component.BleedPuddleThreshold * 4; // give some leeway, for chemstream as well
+        chemicalSolution.MaxVolume = entity.Comp.ChemicalMaxVolume;
+        bloodSolution.MaxVolume = entity.Comp.BloodMaxVolume;
+        tempSolution.MaxVolume = entity.Comp.BleedPuddleThreshold * 4; // give some leeway, for chemstream as well
 
         // Fill blood solution with BLOOD
-        _solutionContainerSystem.TryAddReagent(uid, component.BloodSolution, component.BloodReagent,
-            component.BloodMaxVolume, out _);
+        bloodSolution.AddReagent(entity.Comp.BloodReagent, entity.Comp.BloodMaxVolume - bloodSolution.Volume);
     }
 
     private void OnDamageChanged(EntityUid uid, BloodstreamComponent component, DamageChangedEvent args)
@@ -250,34 +259,45 @@ public sealed class BloodstreamSystem : EntitySystem
             component.AccumulatedFrametime = component.UpdateInterval;
     }
 
-    private void OnRejuvenate(EntityUid uid, BloodstreamComponent component, RejuvenateEvent args)
+    private void OnRejuvenate(Entity<BloodstreamComponent> entity, ref RejuvenateEvent args)
     {
-        TryModifyBleedAmount(uid, -component.BleedAmount, component);
-        TryModifyBloodLevel(uid, component.BloodSolution.AvailableVolume, component);
-        _solutionContainerSystem.RemoveAllSolution(uid, component.ChemicalSolution);
+        TryModifyBleedAmount(entity.Owner, -entity.Comp.BleedAmount, entity.Comp);
+
+        if (_solutionContainerSystem.ResolveSolution(entity.Owner, entity.Comp.BloodSolutionName, ref entity.Comp.BloodSolution, out var bloodSolution))
+            TryModifyBloodLevel(entity.Owner, bloodSolution.AvailableVolume, entity.Comp);
+
+        if (_solutionContainerSystem.ResolveSolution(entity.Owner, entity.Comp.ChemicalSolutionName, ref entity.Comp.ChemicalSolution))
+            _solutionContainerSystem.RemoveAllSolution(entity.Comp.ChemicalSolution.Value);
     }
 
     /// <summary>
     ///     Attempt to transfer provided solution to internal solution.
     /// </summary>
-    public bool TryAddToChemicals(EntityUid uid, Solution solution, BloodstreamComponent? component=null)
+    public bool TryAddToChemicals(EntityUid uid, Solution solution, BloodstreamComponent? component = null)
     {
         if (!Resolve(uid, ref component, false))
             return false;
 
-        return _solutionContainerSystem.TryAddSolution(uid, component.ChemicalSolution, solution);
+        if (!_solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution))
+            return false;
+
+        return _solutionContainerSystem.TryAddSolution(component.ChemicalSolution.Value, solution);
     }
 
-    public bool FlushChemicals(EntityUid uid, string excludedReagentID, FixedPoint2 quantity, BloodstreamComponent? component = null) {
+    public bool FlushChemicals(EntityUid uid, string excludedReagentID, FixedPoint2 quantity, BloodstreamComponent? component = null)
+    {
         if (!Resolve(uid, ref component, false))
             return false;
 
-        for (var i = component.ChemicalSolution.Contents.Count - 1; i >= 0; i--)
+        if (!_solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution, out var chemSolution))
+            return false;
+
+        for (var i = chemSolution.Contents.Count - 1; i >= 0; i--)
         {
-            var (reagentId, _) = component.ChemicalSolution.Contents[i];
+            var (reagentId, _) = chemSolution.Contents[i];
             if (reagentId.Prototype != excludedReagentID)
             {
-                _solutionContainerSystem.RemoveReagent(uid, component.ChemicalSolution, reagentId, quantity);
+                _solutionContainerSystem.RemoveReagent(component.ChemicalSolution.Value, reagentId, quantity);
             }
         }
 
@@ -289,7 +309,10 @@ public sealed class BloodstreamSystem : EntitySystem
         if (!Resolve(uid, ref component))
             return 0.0f;
 
-        return component.BloodSolution.FillFraction;
+        if (!_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution, out var bloodSolution))
+            return 0.0f;
+
+        return bloodSolution.FillFraction;
     }
 
     public void SetBloodLossThreshold(EntityUid uid, float threshold, BloodstreamComponent? comp = null)
@@ -308,28 +331,41 @@ public sealed class BloodstreamSystem : EntitySystem
         if (!Resolve(uid, ref component, false))
             return false;
 
+        if (!_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution))
+            return false;
+
         if (amount >= 0)
-            return _solutionContainerSystem.TryAddReagent(uid, component.BloodSolution, component.BloodReagent, amount, out _);
+            return _solutionContainerSystem.TryAddReagent(component.BloodSolution.Value, component.BloodReagent, amount, out _);
 
         // Removal is more involved,
         // since we also wanna handle moving it to the temporary solution
         // and then spilling it if necessary.
-        var newSol = component.BloodSolution.SplitSolution(-amount);
-        component.BloodTemporarySolution.AddSolution(newSol, _prototypeManager);
+        var newSol = _solutionContainerSystem.SplitSolution(component.BloodSolution.Value, -amount);
+
+        if (!_solutionContainerSystem.ResolveSolution(uid, component.BloodTemporarySolutionName, ref component.TemporarySolution, out var tempSolution))
+            return true;
 
-        if (component.BloodTemporarySolution.Volume > component.BleedPuddleThreshold)
+        tempSolution.AddSolution(newSol, _prototypeManager);
+
+        if (tempSolution.Volume > component.BleedPuddleThreshold)
         {
             // Pass some of the chemstream into the spilled blood.
-            var temp = component.ChemicalSolution.SplitSolution(component.BloodTemporarySolution.Volume / 10);
-            component.BloodTemporarySolution.AddSolution(temp, _prototypeManager);
-            if (_puddleSystem.TrySpillAt(uid, component.BloodTemporarySolution, out var puddleUid, false))
+            if (_solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution))
+            {
+                var temp = _solutionContainerSystem.SplitSolution(component.ChemicalSolution.Value, tempSolution.Volume / 10);
+                tempSolution.AddSolution(temp, _prototypeManager);
+            }
+
+            if (_puddleSystem.TrySpillAt(uid, tempSolution, out var puddleUid, false))
             {
                 _forensicsSystem.TransferDna(puddleUid, uid, false);
             }
 
-            component.BloodTemporarySolution.RemoveAllSolution();
+            tempSolution.RemoveAllSolution();
         }
 
+        _solutionContainerSystem.UpdateChemicals(component.TemporarySolution.Value);
+
         return true;
     }
 
@@ -363,16 +399,28 @@ public sealed class BloodstreamSystem : EntitySystem
         if (!Resolve(uid, ref component))
             return;
 
-        var max = component.BloodSolution.MaxVolume + component.BloodTemporarySolution.MaxVolume +
-                  component.ChemicalSolution.MaxVolume;
-        var tempSol = new Solution() { MaxVolume = max };
+        var tempSol = new Solution();
 
-        tempSol.AddSolution(component.BloodSolution, _prototypeManager);
-        component.BloodSolution.RemoveAllSolution();
-        tempSol.AddSolution(component.BloodTemporarySolution, _prototypeManager);
-        component.BloodTemporarySolution.RemoveAllSolution();
-        tempSol.AddSolution(component.ChemicalSolution, _prototypeManager);
-        component.ChemicalSolution.RemoveAllSolution();
+        if (_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution, out var bloodSolution))
+        {
+            tempSol.MaxVolume += bloodSolution.MaxVolume;
+            tempSol.AddSolution(bloodSolution, _prototypeManager);
+            _solutionContainerSystem.RemoveAllSolution(component.BloodSolution.Value);
+        }
+
+        if (_solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution, out var chemSolution))
+        {
+            tempSol.MaxVolume += chemSolution.MaxVolume;
+            tempSol.AddSolution(chemSolution, _prototypeManager);
+            _solutionContainerSystem.RemoveAllSolution(component.ChemicalSolution.Value);
+        }
+
+        if (_solutionContainerSystem.ResolveSolution(uid, component.BloodTemporarySolutionName, ref component.TemporarySolution, out var tempSolution))
+        {
+            tempSol.MaxVolume += tempSolution.MaxVolume;
+            tempSol.AddSolution(tempSolution, _prototypeManager);
+            _solutionContainerSystem.RemoveAllSolution(component.TemporarySolution.Value);
+        }
 
         if (_puddleSystem.TrySpillAt(uid, tempSol, out var puddleUid))
         {
@@ -388,13 +436,20 @@ public sealed class BloodstreamSystem : EntitySystem
         if (!Resolve(uid, ref component, false))
             return;
 
-        if(reagent == component.BloodReagent)
+        if (reagent == component.BloodReagent)
             return;
 
-        var currentVolume = component.BloodSolution.Volume;
+        if (!_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution, out var bloodSolution))
+        {
+            component.BloodReagent = reagent;
+            return;
+        }
+
+        var currentVolume = bloodSolution.RemoveReagent(component.BloodReagent, bloodSolution.Volume);
 
         component.BloodReagent = reagent;
-        component.BloodSolution.RemoveAllSolution();
-        _solutionContainerSystem.TryAddReagent(uid, component.BloodSolution, component.BloodReagent, currentVolume, out _);
+
+        if (currentVolume > 0)
+            _solutionContainerSystem.TryAddReagent(component.BloodSolution.Value, component.BloodReagent, currentVolume, out _);
     }
 }
index 3aef2486e774178e5e9d4e159c77889dac1f9312..b5bac507391a8c5aa22957cfd9cd86e62bec7baf 100644 (file)
@@ -1,8 +1,8 @@
 using Content.Server.Atmos.Components;
 using Content.Server.Atmos.EntitySystems;
 using Content.Server.Body.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Atmos;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Clothing;
 using Content.Shared.Inventory.Events;
 
@@ -44,11 +44,11 @@ public sealed class LungSystem : EntitySystem
         }
     }
 
-    private void OnComponentInit(EntityUid uid, LungComponent component, ComponentInit args)
+    private void OnComponentInit(Entity<LungComponent> entity, ref ComponentInit args)
     {
-        component.LungSolution = _solutionContainerSystem.EnsureSolution(uid, LungSolutionName);
-        component.LungSolution.MaxVolume = 100.0f;
-        component.LungSolution.CanReact = false; // No dexalin lungs
+        var solution = _solutionContainerSystem.EnsureSolution(entity.Owner, entity.Comp.SolutionName);
+        solution.MaxVolume = 100.0f;
+        solution.CanReact = false; // No dexalin lungs
     }
 
     private void OnMaskToggled(Entity<BreathToolComponent> ent, ref ItemMaskToggledEvent args)
@@ -71,6 +71,9 @@ public sealed class LungSystem : EntitySystem
 
     public void GasToReagent(EntityUid uid, LungComponent lung)
     {
+        if (!_solutionContainerSystem.ResolveSolution(uid, lung.SolutionName, ref lung.Solution, out var solution))
+            return;
+
         foreach (var gas in Enum.GetValues<Gas>())
         {
             var i = (int) gas;
@@ -81,11 +84,13 @@ public sealed class LungSystem : EntitySystem
             if (reagent == null) continue;
 
             var amount = moles * Atmospherics.BreathMolesToReagentMultiplier;
-            _solutionContainerSystem.TryAddReagent(uid, lung.LungSolution, reagent, amount, out _);
+            solution.AddReagent(reagent, amount);
 
             // We don't remove the gas from the lung mix,
             // that's the responsibility of whatever gas is being metabolized.
             // Most things will just want to exhale again.
         }
+
+        _solutionContainerSystem.UpdateChemicals(lung.Solution.Value);
     }
 }
index c8851b49acac05448a1e2274eab39eb501b03859..d05e6c7f79649467b842d21b6fabaab602b93f94 100644 (file)
@@ -1,9 +1,9 @@
 using Content.Server.Body.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Administration.Logs;
 using Content.Shared.Body.Organ;
 using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Components.SolutionManager;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.Database;
 using Content.Shared.FixedPoint;
@@ -37,15 +37,15 @@ namespace Content.Server.Body.Systems
             SubscribeLocalEvent<MetabolizerComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
         }
 
-        private void OnMetabolizerInit(EntityUid uid, MetabolizerComponent component, ComponentInit args)
+        private void OnMetabolizerInit(Entity<MetabolizerComponent> entity, ref ComponentInit args)
         {
-            if (!component.SolutionOnBody)
+            if (!entity.Comp.SolutionOnBody)
             {
-                _solutionContainerSystem.EnsureSolution(uid, component.SolutionName);
+                _solutionContainerSystem.EnsureSolution(entity.Owner, entity.Comp.SolutionName);
             }
-            else if (_organQuery.CompOrNull(uid)?.Body is { } body)
+            else if (_organQuery.CompOrNull(entity)?.Body is { } body)
             {
-                _solutionContainerSystem.EnsureSolution(body, component.SolutionName);
+                _solutionContainerSystem.EnsureSolution(body, entity.Comp.SolutionName);
             }
         }
 
@@ -95,6 +95,7 @@ namespace Content.Server.Body.Systems
 
             // First step is get the solution we actually care about
             Solution? solution = null;
+            Entity<SolutionComponent>? soln = default!;
             EntityUid? solutionEntityUid = null;
 
             SolutionContainerManagerComponent? manager = null;
@@ -106,7 +107,7 @@ namespace Content.Server.Body.Systems
                     if (!_solutionQuery.Resolve(body, ref manager, false))
                         return;
 
-                    _solutionContainerSystem.TryGetSolution(body, meta.SolutionName, out solution, manager);
+                    _solutionContainerSystem.TryGetSolution((body, manager), meta.SolutionName, out soln, out solution);
                     solutionEntityUid = body;
                 }
             }
@@ -115,11 +116,11 @@ namespace Content.Server.Body.Systems
                 if (!_solutionQuery.Resolve(uid, ref manager, false))
                     return;
 
-                _solutionContainerSystem.TryGetSolution(uid, meta.SolutionName, out solution, manager);
+                _solutionContainerSystem.TryGetSolution((uid, manager), meta.SolutionName, out soln, out solution);
                 solutionEntityUid = uid;
             }
 
-            if (solutionEntityUid == null || solution == null || solution.Contents.Count == 0)
+            if (solutionEntityUid == null || soln is null || solution is null || solution.Contents.Count == 0)
                 return;
 
             // randomize the reagent list so we don't have any weird quirks
@@ -138,8 +139,7 @@ namespace Content.Server.Body.Systems
                 {
                     if (meta.RemoveEmpty)
                     {
-                        _solutionContainerSystem.RemoveReagent(solutionEntityUid.Value, solution, reagent,
-                            FixedPoint2.New(1));
+                        solution.RemoveReagent(reagent, FixedPoint2.New(1));
                     }
 
                     continue;
@@ -198,12 +198,14 @@ namespace Content.Server.Body.Systems
                 // remove a certain amount of reagent
                 if (mostToRemove > FixedPoint2.Zero)
                 {
-                    _solutionContainerSystem.RemoveReagent(solutionEntityUid.Value, solution, reagent, mostToRemove);
+                    solution.RemoveReagent(reagent, mostToRemove);
 
                     // We have processed a reagant, so count it towards the cap
                     reagents += 1;
                 }
             }
+
+            _solutionContainerSystem.UpdateChemicals(soln.Value);
         }
     }
 
index 3fa8f59a1d4f55f47b9d61f64cc1ac4277abe870..b814422181c054191ad3701ef6211913dbdbc723 100644 (file)
@@ -2,6 +2,7 @@ using Content.Server.Administration.Logs;
 using Content.Server.Atmos;
 using Content.Server.Atmos.EntitySystems;
 using Content.Server.Body.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Popups;
 using Content.Shared.Alert;
 using Content.Shared.Atmos;
@@ -26,6 +27,7 @@ namespace Content.Server.Body.Systems
         [Dependency] private readonly LungSystem _lungSystem = default!;
         [Dependency] private readonly PopupSystem _popupSystem = default!;
         [Dependency] private readonly MobStateSystem _mobState = default!;
+        [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
 
         public override void Initialize()
         {
@@ -144,7 +146,9 @@ namespace Content.Server.Body.Systems
             {
                 _atmosSys.Merge(outGas, lung.Air);
                 lung.Air.Clear();
-                lung.LungSolution.RemoveAllSolution();
+
+                if (_solutionContainerSystem.ResolveSolution(lung.Owner, lung.SolutionName, ref lung.Solution))
+                    _solutionContainerSystem.RemoveAllSolution(lung.Solution.Value);
             }
 
             _atmosSys.Merge(ev.Gas, outGas);
index 5a3f6cf31a9aecb2aef553ea368ebd44431a030e..4c11244c379ee195236a005da005301baf55c861 100644 (file)
@@ -1,8 +1,8 @@
 using Content.Server.Body.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Body.Organ;
 using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Components.SolutionManager;
-using Content.Shared.Chemistry.EntitySystems;
 using Robust.Shared.Utility;
 
 namespace Content.Server.Body.Systems
@@ -31,8 +31,7 @@ namespace Content.Server.Body.Systems
                 stomach.AccumulatedFrameTime -= stomach.UpdateInterval;
 
                 // Get our solutions
-                if (!_solutionContainerSystem.TryGetSolution(uid, DefaultSolutionName,
-                        out var stomachSolution, sol))
+                if (!_solutionContainerSystem.ResolveSolution((uid, sol), DefaultSolutionName, ref stomach.Solution, out var stomachSolution))
                     continue;
 
                 if (organ.Body is not { } body || !_solutionContainerSystem.TryGetSolution(body, stomach.BodySolutionName, out var bodySolution))
@@ -51,7 +50,7 @@ namespace Content.Server.Body.Systems
                             if (reagent.Quantity > delta.ReagentQuantity.Quantity)
                                 reagent = new(reagent.Reagent, delta.ReagentQuantity.Quantity);
 
-                            _solutionContainerSystem.RemoveReagent(uid, stomachSolution, reagent);
+                            stomachSolution.RemoveReagent(reagent);
                             transferSolution.AddReagent(reagent);
                         }
 
@@ -64,8 +63,10 @@ namespace Content.Server.Body.Systems
                     stomach.ReagentDeltas.Remove(item);
                 }
 
+                _solutionContainerSystem.UpdateChemicals(stomach.Solution.Value);
+
                 // Transfer everything to the body solution!
-                _solutionContainerSystem.TryAddSolution(body, bodySolution, transferSolution);
+                _solutionContainerSystem.TryAddSolution(bodySolution.Value, transferSolution);
             }
         }
 
@@ -86,12 +87,13 @@ namespace Content.Server.Body.Systems
         }
 
         public bool CanTransferSolution(EntityUid uid, Solution solution,
+            StomachComponent? stomach = null,
             SolutionContainerManagerComponent? solutions = null)
         {
-            if (!Resolve(uid, ref solutions, false))
+            if (!Resolve(uid, ref stomach, ref solutions, false))
                 return false;
 
-            if (!_solutionContainerSystem.TryGetSolution(uid, DefaultSolutionName, out var stomachSolution, solutions))
+            if (!_solutionContainerSystem.ResolveSolution((uid, solutions), DefaultSolutionName, ref stomach.Solution, out var stomachSolution))
                 return false;
 
             // TODO: For now no partial transfers. Potentially change by design
@@ -108,11 +110,11 @@ namespace Content.Server.Body.Systems
             if (!Resolve(uid, ref stomach, ref solutions, false))
                 return false;
 
-            if (!_solutionContainerSystem.TryGetSolution(uid, DefaultSolutionName, out var stomachSolution, solutions)
-                || !CanTransferSolution(uid, solution, solutions))
+            if (!_solutionContainerSystem.ResolveSolution((uid, solutions), DefaultSolutionName, ref stomach.Solution)
+                || !CanTransferSolution(uid, solution, stomach, solutions))
                 return false;
 
-            _solutionContainerSystem.TryAddSolution(uid, stomachSolution, solution);
+            _solutionContainerSystem.TryAddSolution(stomach.Solution.Value, solution);
             // Add each reagent to ReagentDeltas. Used to track how long each reagent has been in the stomach
             foreach (var reagent in solution.Contents)
             {
index 9825167ca248378779c1e5cecfae8b9d5b40cbd4..809af737ac666fe241816c396f6eb414da16ef37 100644 (file)
@@ -1,3 +1,4 @@
+using Content.Shared.Chemistry.Components;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
 
 namespace Content.Server.Botany.Components;
@@ -90,4 +91,7 @@ public sealed partial class PlantHolderComponent : Component
 
     [ViewVariables(VVAccess.ReadWrite), DataField("solution")]
     public string SoilSolutionName = "soil";
+
+    [DataField]
+    public Entity<SolutionComponent>? SoilSolution = null;
 }
index 3cfb21f3c46e7206cdc1ce2a6db963084608279a..788c09821b21915670b3bf08fc31b8e428306cc9 100644 (file)
@@ -1,6 +1,5 @@
 using Content.Server.Botany.Components;
 using Content.Shared.FixedPoint;
-using Robust.Server.GameObjects;
 
 namespace Content.Server.Botany.Systems;
 
@@ -11,7 +10,7 @@ public sealed partial class BotanySystem
         if (!TryGetSeed(produce, out var seed))
             return;
 
-        var solutionContainer = _solutionContainerSystem.EnsureSolution(uid, produce.SolutionName);
+        var solutionContainer = _solutionContainerSystem.EnsureSolution(uid, produce.SolutionName, FixedPoint2.Zero, out _);
 
         solutionContainer.RemoveAllSolution();
         foreach (var (chem, quantity) in seed.Chemicals)
index 02fb1060f3168d54b45c7a63fa401d87a6db9d31..1fb6952e913aebcc0f3928df4612c7c12c88bcd9 100644 (file)
@@ -1,10 +1,8 @@
-using System.Diagnostics.CodeAnalysis;
-using System.Linq;
 using Content.Server.Botany.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Kitchen.Components;
 using Content.Server.Popups;
 using Content.Shared.Botany;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Examine;
 using Content.Shared.Hands.EntitySystems;
 using Content.Shared.Physics;
@@ -20,6 +18,8 @@ using Robust.Shared.Physics.Components;
 using Robust.Shared.Physics.Systems;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Random;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
 
 namespace Content.Server.Botany.Systems;
 
index 027ca9b03845337804e76343e26a4061552cfcf5..96fde08118d17d77f41636b533bac34da8af1096 100644 (file)
@@ -1,12 +1,12 @@
 using Content.Server.Atmos;
 using Content.Server.Atmos.EntitySystems;
 using Content.Server.Botany.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Fluids.Components;
 using Content.Server.Ghost.Roles.Components;
 using Content.Server.Kitchen.Components;
 using Content.Server.Popups;
 using Content.Shared.Botany;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.Coordinates.Helpers;
 using Content.Shared.Examine;
@@ -38,7 +38,7 @@ public sealed class PlantHolderSystem : EntitySystem
     [Dependency] private readonly PopupSystem _popup = default!;
     [Dependency] private readonly IGameTiming _gameTiming = default!;
     [Dependency] private readonly SharedPointLightSystem _pointLight = default!;
-    [Dependency] private readonly SolutionContainerSystem _solutionSystem = default!;
+    [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
     [Dependency] private readonly TagSystem _tagSystem = default!;
     [Dependency] private readonly RandomHelperSystem _randomHelper = default!;
     [Dependency] private readonly IRobustRandom _random = default!;
@@ -70,11 +70,13 @@ public sealed class PlantHolderSystem : EntitySystem
         }
     }
 
-    private void OnExamine(EntityUid uid, PlantHolderComponent component, ExaminedEvent args)
+    private void OnExamine(Entity<PlantHolderComponent> entity, ref ExaminedEvent args)
     {
         if (!args.IsInDetailsRange)
             return;
 
+        var (_, component) = entity;
+
         if (component.Seed == null)
         {
             args.PushMarkup(Loc.GetString("plant-holder-component-nothing-planted-message"));
@@ -131,8 +133,10 @@ public sealed class PlantHolderSystem : EntitySystem
         }
     }
 
-    private void OnInteractUsing(EntityUid uid, PlantHolderComponent component, InteractUsingEvent args)
+    private void OnInteractUsing(Entity<PlantHolderComponent> entity, ref InteractUsingEvent args)
     {
+        var (uid, component) = entity;
+
         if (TryComp(args.Used, out SeedComponent? seeds))
         {
             if (component.Seed == null)
@@ -203,8 +207,8 @@ public sealed class PlantHolderSystem : EntitySystem
             return;
         }
 
-        if (_solutionSystem.TryGetDrainableSolution(args.Used, out var solution)
-            && _solutionSystem.TryGetSolution(uid, component.SoilSolutionName, out var targetSolution)
+        if (_solutionContainerSystem.TryGetDrainableSolution(args.Used, out var solution, out _)
+            && _solutionContainerSystem.ResolveSolution(uid, component.SoilSolutionName, ref component.SoilSolution)
             && TryComp(args.Used, out SprayComponent? spray))
         {
             var amount = FixedPoint2.New(1);
@@ -214,7 +218,7 @@ public sealed class PlantHolderSystem : EntitySystem
 
             _audio.PlayPvs(spray.SpraySound, args.Used, AudioParams.Default.WithVariation(0.125f));
 
-            var split = _solutionSystem.Drain(solutionEntity, solution, amount);
+            var split = _solutionContainerSystem.Drain(solutionEntity, solution.Value, amount);
 
             if (split.Volume == 0)
             {
@@ -227,7 +231,7 @@ public sealed class PlantHolderSystem : EntitySystem
                 ("owner", uid),
                 ("amount", split.Volume)), args.User, PopupType.Medium);
 
-            _solutionSystem.TryAddSolution(targetEntity, targetSolution, split);
+            _solutionContainerSystem.TryAddSolution(component.SoilSolution.Value, split);
 
             ForceUpdateByExternalCause(uid, component);
 
@@ -290,16 +294,15 @@ public sealed class PlantHolderSystem : EntitySystem
                 ("usingItem", args.Used),
                 ("owner", uid)), uid, Filter.PvsExcept(args.User), true);
 
-            if (_solutionSystem.TryGetSolution(args.Used, produce.SolutionName, out var solution2))
+            if (_solutionContainerSystem.TryGetSolution(args.Used, produce.SolutionName, out var soln2, out var solution2))
             {
-                if (_solutionSystem.TryGetSolution(uid, component.SoilSolutionName, out var solution1))
+                if (_solutionContainerSystem.ResolveSolution(uid, component.SoilSolutionName, ref component.SoilSolution, out var solution1))
                 {
                     // We try to fit as much of the composted plant's contained solution into the hydroponics tray as we can,
                     // since the plant will be consumed anyway.
 
                     var fillAmount = FixedPoint2.Min(solution2.Volume, solution1.AvailableVolume);
-                    _solutionSystem.TryAddSolution(uid, solution1,
-                        _solutionSystem.SplitSolution(args.Used, solution2, fillAmount));
+                    _solutionContainerSystem.TryAddSolution(component.SoilSolution.Value, _solutionContainerSystem.SplitSolution(soln2.Value, fillAmount));
 
                     ForceUpdateByExternalCause(uid, component);
                 }
@@ -314,9 +317,9 @@ public sealed class PlantHolderSystem : EntitySystem
         }
     }
 
-    private void OnInteractHand(EntityUid uid, PlantHolderComponent component, InteractHandEvent args)
+    private void OnInteractHand(Entity<PlantHolderComponent> entity, ref InteractHandEvent args)
     {
-        DoHarvest(uid, args.User, component);
+        DoHarvest(entity, args.User, entity.Comp);
     }
 
     public void WeedInvasion()
@@ -819,13 +822,13 @@ public sealed class PlantHolderSystem : EntitySystem
         if (!Resolve(uid, ref component))
             return;
 
-        if (!_solutionSystem.TryGetSolution(uid, component.SoilSolutionName, out var solution))
+        if (!_solutionContainerSystem.ResolveSolution(uid, component.SoilSolutionName, ref component.SoilSolution, out var solution))
             return;
 
         if (solution.Volume > 0 && component.MutationLevel < 25)
         {
             var amt = FixedPoint2.New(1);
-            foreach (var entry in _solutionSystem.RemoveEachReagent(uid, solution, amt))
+            foreach (var entry in _solutionContainerSystem.RemoveEachReagent(component.SoilSolution.Value, amt))
             {
                 var reagentProto = _prototype.Index<ReagentPrototype>(entry.Reagent.Prototype);
                 reagentProto.ReactionPlant(uid, entry, solution);
index 5bbe1e5a6d21e1c383a1a6707498d6e3798e2214..6fb36c960834c31b3ef9125fc560ed1f98c9a2e3 100644 (file)
@@ -1,7 +1,7 @@
-using System.Linq;
-using Content.Server.Administration;
+using Content.Server.Administration;
 using Content.Server.Body.Systems;
 using Content.Server.Cargo.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Administration;
 using Content.Shared.Body.Components;
 using Content.Shared.Chemistry.Components.SolutionManager;
@@ -16,6 +16,7 @@ using Robust.Shared.Map;
 using Robust.Shared.Map.Components;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Utility;
+using System.Linq;
 
 namespace Content.Server.Cargo.Systems;
 
@@ -30,6 +31,7 @@ public sealed class PricingSystem : EntitySystem
     [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
     [Dependency] private readonly BodySystem _bodySystem = default!;
     [Dependency] private readonly MobStateSystem _mobStateSystem = default!;
+    [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
 
     /// <inheritdoc/>
     public override void Initialize()
@@ -106,12 +108,16 @@ public sealed class PricingSystem : EntitySystem
         args.Price += (component.Price - partPenalty) * (_mobStateSystem.IsAlive(uid, state) ? 1.0 : component.DeathPenalty);
     }
 
-    private double GetSolutionPrice(SolutionContainerManagerComponent component)
+    private double GetSolutionPrice(Entity<SolutionContainerManagerComponent> entity)
     {
+        if (Comp<MetaDataComponent>(entity).EntityLifeStage < EntityLifeStage.MapInitialized)
+            return GetSolutionPrice(entity.Comp);
+
         var price = 0.0;
 
-        foreach (var solution in component.Solutions.Values)
+        foreach (var (_, soln) in _solutionContainerSystem.EnumerateSolutions((entity.Owner, entity.Comp)))
         {
+            var solution = soln.Comp.Solution;
             foreach (var (reagent, quantity) in solution.Contents)
             {
                 if (!_prototypeManager.TryIndex<ReagentPrototype>(reagent.Prototype, out var reagentProto))
@@ -125,6 +131,25 @@ public sealed class PricingSystem : EntitySystem
         return price;
     }
 
+    private double GetSolutionPrice(SolutionContainerManagerComponent component)
+    {
+        var price = 0.0;
+
+        foreach (var (_, prototype) in _solutionContainerSystem.EnumerateSolutions(component))
+        {
+            foreach (var (reagent, quantity) in prototype.Contents)
+            {
+                if (!_prototypeManager.TryIndex<ReagentPrototype>(reagent.Prototype, out var reagentProto))
+                    continue;
+
+                // TODO check ReagentData for price information?
+                price += (float) quantity * reagentProto.PricePerUnit;
+            }
+        }
+
+        return price;
+    }
+
     private double GetMaterialPrice(PhysicalCompositionComponent component)
     {
         double price = 0;
@@ -257,7 +282,7 @@ public sealed class PricingSystem : EntitySystem
 
         if (TryComp<SolutionContainerManagerComponent>(uid, out var solComp))
         {
-            price += GetSolutionPrice(solComp);
+            price += GetSolutionPrice((uid, solComp));
         }
 
         return price;
index 76bb0e8d418a05d965b2be95e4aa8e84540ef756..2a80cec801b89405f914162cb9f0666acbaddbee 100644 (file)
@@ -1,4 +1,3 @@
-using Content.Server.Chemistry.EntitySystems;
 using Content.Shared.Chemistry.Components;
 using Content.Shared.FixedPoint;
 using Robust.Shared.Audio;
index 1fc5772d4e687a80004219cf4b5ad98809b22faa..7fe2dacb6790392411befbb4f2039914c23f9b8e 100644 (file)
@@ -1,8 +1,6 @@
 using Content.Server.Chemistry.EntitySystems;
 using Content.Shared.Chemistry.Components;
-using Content.Shared.FixedPoint;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
 
 namespace Content.Server.Chemistry.Components;
 
@@ -17,7 +15,13 @@ public sealed partial class SolutionRegenerationComponent : Component
     /// The name of the solution to add to.
     /// </summary>
     [DataField("solution", required: true), ViewVariables(VVAccess.ReadWrite)]
-    public string Solution = string.Empty;
+    public string SolutionName = string.Empty;
+
+    /// <summary>
+    /// The solution to add reagents to.
+    /// </summary>
+    [DataField("solutionRef")]
+    public Entity<SolutionComponent>? Solution = null;
 
     /// <summary>
     /// The reagent(s) to be regenerated in the solution.
diff --git a/Content.Server/Chemistry/Containers/EntitySystems/SolutionContainerSystem.cs b/Content.Server/Chemistry/Containers/EntitySystems/SolutionContainerSystem.cs
new file mode 100644 (file)
index 0000000..5ccccdb
--- /dev/null
@@ -0,0 +1,185 @@
+using Content.Shared.Chemistry.Components;
+using Content.Shared.Chemistry.Components.SolutionManager;
+using Content.Shared.Chemistry.EntitySystems;
+using Content.Shared.FixedPoint;
+using Robust.Shared.Containers;
+using Robust.Shared.Map;
+using Robust.Shared.Network;
+using Robust.Shared.Utility;
+using System.Numerics;
+
+namespace Content.Server.Chemistry.Containers.EntitySystems;
+
+public sealed partial class SolutionContainerSystem : SharedSolutionContainerSystem
+{
+    [Dependency] private readonly INetManager _netManager = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<SolutionContainerManagerComponent, MapInitEvent>(OnMapInit);
+        SubscribeLocalEvent<SolutionContainerManagerComponent, ComponentShutdown>(OnComponentShutdown);
+        SubscribeLocalEvent<ContainedSolutionComponent, ComponentShutdown>(OnComponentShutdown);
+    }
+
+
+    public Solution EnsureSolution(Entity<MetaDataComponent?> entity, string name)
+        => EnsureSolution(entity, name, out _);
+
+    public Solution EnsureSolution(Entity<MetaDataComponent?> entity, string name, out bool existed)
+        => EnsureSolution(entity, name, FixedPoint2.Zero, out existed);
+
+    public Solution EnsureSolution(Entity<MetaDataComponent?> entity, string name, FixedPoint2 minVol, out bool existed)
+        => EnsureSolution(entity, name, minVol, null, out existed);
+
+    public Solution EnsureSolution(Entity<MetaDataComponent?> entity, string name, FixedPoint2 minVol, Solution? prototype, out bool existed)
+    {
+        var (uid, meta) = entity;
+        DebugTools.Assert(Resolve(uid, ref meta), $"Attempted to ensure solution on invalid entity {ToPrettyString(entity.Owner)}");
+
+        var manager = EnsureComp<SolutionContainerManagerComponent>(uid);
+        if (meta.EntityLifeStage >= EntityLifeStage.MapInitialized)
+            return EnsureSolutionEntity((uid, manager), name, minVol, prototype, out existed).Comp.Solution;
+        else
+            return EnsureSolutionPrototype((uid, manager), name, minVol, prototype, out existed);
+    }
+
+    public Entity<SolutionComponent> EnsureSolutionEntity(Entity<SolutionContainerManagerComponent?> entity, string name, FixedPoint2 minVol, Solution? prototype, out bool existed)
+    {
+        existed = true;
+
+        var (uid, container) = entity;
+
+        var solutionSlot = ContainerSystem.EnsureContainer<ContainerSlot>(uid, $"solution@{name}", out existed);
+        if (!Resolve(uid, ref container, logMissing: false))
+        {
+            existed = false;
+            container = AddComp<SolutionContainerManagerComponent>(uid);
+            container.Containers.Add(name);
+        }
+        else if (!existed)
+        {
+            container.Containers.Add(name);
+            Dirty(uid, container);
+        }
+
+        var needsInit = false;
+        SolutionComponent solutionComp;
+        if (solutionSlot.ContainedEntity is not { } solutionId)
+        {
+            prototype ??= new() { MaxVolume = minVol };
+            prototype.Name = name;
+            (solutionId, solutionComp, _) = SpawnSolutionUninitialized(solutionSlot, name, minVol, prototype);
+            existed = false;
+            needsInit = true;
+            Dirty(uid, container);
+        }
+        else
+        {
+            solutionComp = Comp<SolutionComponent>(solutionId);
+            DebugTools.Assert(TryComp(solutionId, out ContainedSolutionComponent? relation) && relation.Container == uid && relation.ContainerName == name);
+            DebugTools.Assert(solutionComp.Solution.Name == name);
+
+            var solution = solutionComp.Solution;
+            solution.MaxVolume = FixedPoint2.Max(solution.MaxVolume, minVol);
+
+            // Depending on MapInitEvent order some systems can ensure solution empty solutions and conflict with the prototype solutions.
+            // We want the reagents from the prototype to exist even if something else already created the solution.
+            if (prototype is { Volume.Value: > 0 })
+                solution.AddSolution(prototype, PrototypeManager);
+
+            Dirty(solutionId, solutionComp);
+        }
+
+        if (needsInit)
+            EntityManager.InitializeAndStartEntity(solutionId, Transform(solutionId).MapID);
+
+        return (solutionId, solutionComp);
+    }
+
+    private Solution EnsureSolutionPrototype(Entity<SolutionContainerManagerComponent?> entity, string name, FixedPoint2 minVol, Solution? prototype, out bool existed)
+    {
+        existed = true;
+
+        var (uid, container) = entity;
+        if (!Resolve(uid, ref container, logMissing: false))
+        {
+            container = AddComp<SolutionContainerManagerComponent>(uid);
+            existed = false;
+        }
+
+        if (container.Solutions is null)
+            container.Solutions = new(SolutionContainerManagerComponent.DefaultCapacity);
+
+        if (!container.Solutions.TryGetValue(name, out var solution))
+        {
+            solution = prototype ?? new() { Name = name, MaxVolume = minVol };
+            container.Solutions.Add(name, solution);
+            existed = false;
+        }
+        else
+            solution.MaxVolume = FixedPoint2.Max(solution.MaxVolume, minVol);
+
+        Dirty(uid, container);
+        return solution;
+    }
+
+
+    private Entity<SolutionComponent, ContainedSolutionComponent> SpawnSolutionUninitialized(ContainerSlot container, string name, FixedPoint2 minVol, Solution prototype)
+    {
+        var coords = new EntityCoordinates(container.Owner, Vector2.Zero);
+        var uid = EntityManager.CreateEntityUninitialized(null, coords, null);
+
+        var solution = new SolutionComponent() { Solution = prototype };
+        AddComp(uid, solution);
+
+        var relation = new ContainedSolutionComponent() { Container = container.Owner, ContainerName = name };
+        AddComp(uid, relation);
+
+        ContainerSystem.Insert(uid, container, force: true);
+
+        return (uid, solution, relation);
+    }
+
+
+    #region Event Handlers
+
+    private void OnMapInit(Entity<SolutionContainerManagerComponent> entity, ref MapInitEvent args)
+    {
+        if (entity.Comp.Solutions is not { } prototypes)
+            return;
+
+        foreach (var (name, prototype) in prototypes)
+        {
+            EnsureSolutionEntity((entity.Owner, entity.Comp), name, prototype.MaxVolume, prototype, out _);
+        }
+
+        entity.Comp.Solutions = null;
+        Dirty(entity);
+    }
+
+    private void OnComponentShutdown(Entity<SolutionContainerManagerComponent> entity, ref ComponentShutdown args)
+    {
+        foreach (var name in entity.Comp.Containers)
+        {
+            if (ContainerSystem.TryGetContainer(entity, $"solution@{name}", out var solutionContainer))
+                ContainerSystem.ShutdownContainer(solutionContainer);
+        }
+        entity.Comp.Containers.Clear();
+    }
+
+    private void OnComponentShutdown(Entity<ContainedSolutionComponent> entity, ref ComponentShutdown args)
+    {
+        if (TryComp(entity.Comp.Container, out SolutionContainerManagerComponent? container))
+        {
+            container.Containers.Remove(entity.Comp.ContainerName);
+            Dirty(entity.Comp.Container, container);
+        }
+
+        if (ContainerSystem.TryGetContainer(entity, $"solution@{entity.Comp.ContainerName}", out var solutionContainer))
+            ContainerSystem.ShutdownContainer(solutionContainer);
+    }
+
+    #endregion Event Handlers
+}
index cbb84be83c33b6392ff4b7c05ad9bc67f117cec7..2cc0f3d055e147b5e294d447984ec6b6640b6ea9 100644 (file)
@@ -1,6 +1,5 @@
-using System.Diagnostics.CodeAnalysis;
-using System.Linq;
 using Content.Server.Chemistry.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Labels;
 using Content.Server.Popups;
 using Content.Server.Storage.EntitySystems;
@@ -19,6 +18,8 @@ using Robust.Server.GameObjects;
 using Robust.Shared.Audio;
 using Robust.Shared.Containers;
 using Robust.Shared.Prototypes;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
 
 namespace Content.Server.Chemistry.EntitySystems
 {
@@ -47,7 +48,7 @@ namespace Content.Server.Chemistry.EntitySystems
             base.Initialize();
 
             SubscribeLocalEvent<ChemMasterComponent, ComponentStartup>(SubscribeUpdateUiState);
-            SubscribeLocalEvent<ChemMasterComponent, SolutionChangedEvent>(SubscribeUpdateUiState);
+            SubscribeLocalEvent<ChemMasterComponent, SolutionContainerChangedEvent>(SubscribeUpdateUiState);
             SubscribeLocalEvent<ChemMasterComponent, EntInsertedIntoContainerMessage>(SubscribeUpdateUiState);
             SubscribeLocalEvent<ChemMasterComponent, EntRemovedFromContainerMessage>(SubscribeUpdateUiState);
             SubscribeLocalEvent<ChemMasterComponent, BoundUIOpenedEvent>(SubscribeUpdateUiState);
@@ -67,7 +68,7 @@ namespace Content.Server.Chemistry.EntitySystems
         private void UpdateUiState(Entity<ChemMasterComponent> ent, bool updateLabel = false)
         {
             var (owner, chemMaster) = ent;
-            if (!_solutionContainerSystem.TryGetSolution(owner, SharedChemMaster.BufferSolutionName, out var bufferSolution))
+            if (!_solutionContainerSystem.TryGetSolution(owner, SharedChemMaster.BufferSolutionName, out _, out var bufferSolution))
                 return;
             var inputContainer = _itemSlotsSystem.GetItemOrNull(owner, SharedChemMaster.InputSlotName);
             var outputContainer = _itemSlotsSystem.GetItemOrNull(owner, SharedChemMaster.OutputSlotName);
@@ -130,8 +131,8 @@ namespace Content.Server.Chemistry.EntitySystems
         {
             var container = _itemSlotsSystem.GetItemOrNull(chemMaster, SharedChemMaster.InputSlotName);
             if (container is null ||
-                !_solutionContainerSystem.TryGetFitsInDispenser(container.Value, out var containerSolution) ||
-                !_solutionContainerSystem.TryGetSolution(chemMaster, SharedChemMaster.BufferSolutionName, out var bufferSolution))
+                !_solutionContainerSystem.TryGetFitsInDispenser(container.Value, out var containerSoln, out var containerSolution) ||
+                !_solutionContainerSystem.TryGetSolution(chemMaster.Owner, SharedChemMaster.BufferSolutionName, out _, out var bufferSolution))
             {
                 return;
             }
@@ -140,12 +141,12 @@ namespace Content.Server.Chemistry.EntitySystems
             {
                 amount = FixedPoint2.Min(amount, containerSolution.AvailableVolume);
                 amount = bufferSolution.RemoveReagent(id, amount);
-                _solutionContainerSystem.TryAddReagent(container.Value, containerSolution, id, amount, out var _);
+                _solutionContainerSystem.TryAddReagent(containerSoln.Value, id, amount, out var _);
             }
             else // Container to buffer
             {
                 amount = FixedPoint2.Min(amount, containerSolution.GetReagentQuantity(id));
-                _solutionContainerSystem.RemoveReagent(container.Value, containerSolution, id, amount);
+                _solutionContainerSystem.RemoveReagent(containerSoln.Value, id, amount);
                 bufferSolution.AddReagent(id, amount);
             }
 
@@ -156,7 +157,7 @@ namespace Content.Server.Chemistry.EntitySystems
         {
             if (fromBuffer)
             {
-                if (_solutionContainerSystem.TryGetSolution(chemMaster, SharedChemMaster.BufferSolutionName, out var bufferSolution))
+                if (_solutionContainerSystem.TryGetSolution(chemMaster.Owner, SharedChemMaster.BufferSolutionName, out _, out var bufferSolution))
                     bufferSolution.RemoveReagent(id, amount);
                 else
                     return;
@@ -165,9 +166,9 @@ namespace Content.Server.Chemistry.EntitySystems
             {
                 var container = _itemSlotsSystem.GetItemOrNull(chemMaster, SharedChemMaster.InputSlotName);
                 if (container is not null &&
-                    _solutionContainerSystem.TryGetFitsInDispenser(container.Value, out var containerSolution))
+                    _solutionContainerSystem.TryGetFitsInDispenser(container.Value, out var containerSolution, out _))
                 {
-                    _solutionContainerSystem.RemoveReagent(container.Value, containerSolution, id, amount);
+                    _solutionContainerSystem.RemoveReagent(containerSolution.Value, id, amount);
                 }
                 else
                     return;
@@ -210,10 +211,8 @@ namespace Content.Server.Chemistry.EntitySystems
                 _storageSystem.Insert(container, item, out _, user: user, storage);
                 _labelSystem.Label(item, message.Label);
 
-                var itemSolution = _solutionContainerSystem.EnsureSolution(item, SharedChemMaster.PillSolutionName);
-
-                _solutionContainerSystem.TryAddSolution(
-                    item, itemSolution, withdrawal.SplitSolution(message.Dosage));
+                var itemSolution = _solutionContainerSystem.EnsureSolutionEntity(item, SharedChemMaster.PillSolutionName, message.Dosage, null, out _);
+                _solutionContainerSystem.TryAddSolution(itemSolution, withdrawal.SplitSolution(message.Dosage));
 
                 var pill = EnsureComp<PillComponent>(item);
                 pill.PillType = chemMaster.Comp.PillType;
@@ -223,13 +222,13 @@ namespace Content.Server.Chemistry.EntitySystems
                 {
                     // Log pill creation by a user
                     _adminLogger.Add(LogType.Action, LogImpact.Low,
-                        $"{ToPrettyString(user.Value):user} printed {ToPrettyString(item):pill} {SolutionContainerSystem.ToPrettyString(itemSolution)}");
+                        $"{ToPrettyString(user.Value):user} printed {ToPrettyString(item):pill} {SolutionContainerSystem.ToPrettyString(itemSolution.Comp.Solution)}");
                 }
                 else
                 {
                     // Log pill creation by magic? This should never happen... right?
                     _adminLogger.Add(LogType.Action, LogImpact.Low,
-                        $"Unknown printed {ToPrettyString(item):pill} {SolutionContainerSystem.ToPrettyString(itemSolution)}");
+                        $"Unknown printed {ToPrettyString(item):pill} {SolutionContainerSystem.ToPrettyString(itemSolution.Comp.Solution)}");
                 }
             }
 
@@ -242,8 +241,7 @@ namespace Content.Server.Chemistry.EntitySystems
             var user = message.Session.AttachedEntity;
             var maybeContainer = _itemSlotsSystem.GetItemOrNull(chemMaster, SharedChemMaster.OutputSlotName);
             if (maybeContainer is not { Valid: true } container
-                || !_solutionContainerSystem.TryGetSolution(
-                    container, SharedChemMaster.BottleSolutionName, out var solution))
+                || !_solutionContainerSystem.TryGetSolution(container, SharedChemMaster.BottleSolutionName, out var soln, out var solution))
             {
                 return; // output can't fit reagents
             }
@@ -260,8 +258,7 @@ namespace Content.Server.Chemistry.EntitySystems
                 return;
 
             _labelSystem.Label(container, message.Label);
-            _solutionContainerSystem.TryAddSolution(
-                container, solution, withdrawal);
+            _solutionContainerSystem.TryAddSolution(soln.Value, withdrawal);
 
             if (user.HasValue)
             {
@@ -287,8 +284,7 @@ namespace Content.Server.Chemistry.EntitySystems
         {
             outputSolution = null;
 
-            if (!_solutionContainerSystem.TryGetSolution(
-                    chemMaster, SharedChemMaster.BufferSolutionName, out var solution))
+            if (!_solutionContainerSystem.TryGetSolution(chemMaster.Owner, SharedChemMaster.BufferSolutionName, out _, out var solution))
             {
                 return false;
             }
@@ -323,7 +319,7 @@ namespace Content.Server.Chemistry.EntitySystems
                 return null;
 
             if (!TryComp(container, out FitsInDispenserComponent? fits)
-                || !_solutionContainerSystem.TryGetSolution(container.Value, fits.Solution, out var solution))
+                || !_solutionContainerSystem.TryGetSolution(container.Value, fits.Solution, out _, out var solution))
             {
                 return null;
             }
@@ -339,7 +335,7 @@ namespace Content.Server.Chemistry.EntitySystems
             var name = Name(container.Value);
             {
                 if (_solutionContainerSystem.TryGetSolution(
-                        container.Value, SharedChemMaster.BottleSolutionName, out var solution))
+                        container.Value, SharedChemMaster.BottleSolutionName, out _, out var solution))
                 {
                     return BuildContainerInfo(name, solution);
                 }
@@ -350,7 +346,7 @@ namespace Content.Server.Chemistry.EntitySystems
 
             var pills = storage.Container.ContainedEntities.Select((Func<EntityUid, (string, FixedPoint2 quantity)>) (pill =>
             {
-                _solutionContainerSystem.TryGetSolution(pill, SharedChemMaster.PillSolutionName, out var solution);
+                _solutionContainerSystem.TryGetSolution(pill, SharedChemMaster.PillSolutionName, out _, out var solution);
                 var quantity = solution?.Volume ?? FixedPoint2.Zero;
                 return (Name(pill), quantity);
             })).ToList();
index c618094d1b0220fde8914b873725d03aecfcd3cc..5f7427eacc68cacbec4dd9383d604b987df135bb 100644 (file)
@@ -1,21 +1,22 @@
 using Content.Server.Body.Components;
 using Content.Server.Chemistry.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Components.SolutionManager;
 using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.Database;
+using Content.Shared.DoAfter;
 using Content.Shared.FixedPoint;
+using Content.Shared.Forensics;
 using Content.Shared.IdentityManagement;
 using Content.Shared.Interaction;
 using Content.Shared.Interaction.Events;
-using Robust.Shared.GameStates;
-using Content.Shared.DoAfter;
 using Content.Shared.Mobs.Components;
-using Content.Shared.Verbs;
 using Content.Shared.Stacks;
+using Content.Shared.Verbs;
+using Robust.Shared.GameStates;
 using Robust.Shared.Player;
-using Content.Shared.Forensics;
 
 namespace Content.Server.Chemistry.EntitySystems;
 
@@ -25,11 +26,11 @@ public sealed partial class ChemistrySystem
     /// <summary>
     ///     Default transfer amounts for the set-transfer verb.
     /// </summary>
-    public static readonly List<int> TransferAmounts = new() {1, 5, 10, 15};
+    public static readonly List<int> TransferAmounts = new() { 1, 5, 10, 15 };
     private void InitializeInjector()
     {
         SubscribeLocalEvent<InjectorComponent, GetVerbsEvent<AlternativeVerb>>(AddSetTransferVerbs);
-        SubscribeLocalEvent<InjectorComponent, SolutionChangedEvent>(OnSolutionChange);
+        SubscribeLocalEvent<InjectorComponent, SolutionContainerChangedEvent>(OnSolutionChange);
         SubscribeLocalEvent<InjectorComponent, InjectorDoAfterEvent>(OnInjectDoAfter);
         SubscribeLocalEvent<InjectorComponent, ComponentStartup>(OnInjectorStartup);
         SubscribeLocalEvent<InjectorComponent, UseInHandEvent>(OnInjectorUse);
@@ -37,7 +38,7 @@ public sealed partial class ChemistrySystem
         SubscribeLocalEvent<InjectorComponent, ComponentGetState>(OnInjectorGetState);
     }
 
-    private void AddSetTransferVerbs(EntityUid uid, InjectorComponent component, GetVerbsEvent<AlternativeVerb> args)
+    private void AddSetTransferVerbs(Entity<InjectorComponent> entity, ref GetVerbsEvent<AlternativeVerb> args)
     {
         if (!args.CanAccess || !args.CanInteract || args.Hands == null)
             return;
@@ -45,11 +46,14 @@ public sealed partial class ChemistrySystem
         if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor))
             return;
 
+        var (uid, component) = entity;
+
         // Add specific transfer verbs according to the container's size
         var priority = 0;
+        var user = args.User;
         foreach (var amount in TransferAmounts)
         {
-            if ( amount < component.MinimumTransferAmount.Int() || amount > component.MaximumTransferAmount.Int())
+            if (amount < component.MinimumTransferAmount.Int() || amount > component.MaximumTransferAmount.Int())
                 continue;
 
             AlternativeVerb verb = new();
@@ -58,7 +62,7 @@ public sealed partial class ChemistrySystem
             verb.Act = () =>
             {
                 component.TransferAmount = FixedPoint2.New(amount);
-                _popup.PopupEntity(Loc.GetString("comp-solution-transfer-set-amount", ("amount", amount)), args.User, args.User);
+                _popup.PopupEntity(Loc.GetString("comp-solution-transfer-set-amount", ("amount", amount)), user, user);
             };
 
             // we want to sort by size, not alphabetically by the verb text.
@@ -69,22 +73,22 @@ public sealed partial class ChemistrySystem
         }
     }
 
-    private void UseInjector(EntityUid target, EntityUid user, EntityUid injector, InjectorComponent component)
+    private void UseInjector(Entity<InjectorComponent> injector, EntityUid target, EntityUid user)
     {
         // Handle injecting/drawing for solutions
-        if (component.ToggleState == SharedInjectorComponent.InjectorToggleMode.Inject)
+        if (injector.Comp.ToggleState == SharedInjectorComponent.InjectorToggleMode.Inject)
         {
-            if (_solutions.TryGetInjectableSolution(target, out var injectableSolution))
+            if (_solutionContainers.TryGetInjectableSolution(target, out var injectableSolution, out _))
             {
-                TryInject(component, injector, target, injectableSolution, user, false);
+                TryInject(injector, target, injectableSolution.Value, user, false);
             }
-            else if (_solutions.TryGetRefillableSolution(target, out var refillableSolution))
+            else if (_solutionContainers.TryGetRefillableSolution(target, out var refillableSolution, out _))
             {
-                TryInject(component, injector, target, refillableSolution, user, true);
+                TryInject(injector, target, refillableSolution.Value, user, true);
             }
             else if (TryComp<BloodstreamComponent>(target, out var bloodstream))
             {
-                TryInjectIntoBloodstream(component, injector, target, bloodstream, user);
+                TryInjectIntoBloodstream(injector, (target, bloodstream), user);
             }
             else
             {
@@ -92,111 +96,112 @@ public sealed partial class ChemistrySystem
                     ("target", Identity.Entity(target, EntityManager))), injector, user);
             }
         }
-        else if (component.ToggleState == SharedInjectorComponent.InjectorToggleMode.Draw)
+        else if (injector.Comp.ToggleState == SharedInjectorComponent.InjectorToggleMode.Draw)
         {
             // Draw from a bloodstream, if the target has that
-            if (TryComp<BloodstreamComponent>(target, out var stream))
+            if (TryComp<BloodstreamComponent>(target, out var stream) &&
+                _solutionContainers.ResolveSolution(target, stream.BloodSolutionName, ref stream.BloodSolution))
             {
-                TryDraw(component, injector, target, stream.BloodSolution, user, stream);
+                TryDraw(injector, (target, stream), stream.BloodSolution.Value, user);
                 return;
             }
 
             // Draw from an object (food, beaker, etc)
-            if (_solutions.TryGetDrawableSolution(target, out var drawableSolution))
+            if (_solutionContainers.TryGetDrawableSolution(target, out var drawableSolution, out _))
             {
-                TryDraw(component, injector, target, drawableSolution, user);
+                TryDraw(injector, target, drawableSolution.Value, user);
             }
             else
             {
                 _popup.PopupEntity(Loc.GetString("injector-component-cannot-draw-message",
-                    ("target", Identity.Entity(target, EntityManager))), injector, user);
+                    ("target", Identity.Entity(target, EntityManager))), injector.Owner, user);
             }
         }
     }
 
-    private void OnSolutionChange(EntityUid uid, InjectorComponent component, SolutionChangedEvent args)
+    private void OnSolutionChange(Entity<InjectorComponent> entity, ref SolutionContainerChangedEvent args)
     {
-        Dirty(component);
+        Dirty(entity);
     }
 
-    private void OnInjectorGetState(EntityUid uid, InjectorComponent component, ref ComponentGetState args)
+    private void OnInjectorGetState(Entity<InjectorComponent> entity, ref ComponentGetState args)
     {
-        _solutions.TryGetSolution(uid, InjectorComponent.SolutionName, out var solution);
+        _solutionContainers.TryGetSolution(entity.Owner, InjectorComponent.SolutionName, out _, out var solution);
 
         var currentVolume = solution?.Volume ?? FixedPoint2.Zero;
         var maxVolume = solution?.MaxVolume ?? FixedPoint2.Zero;
 
-        args.State = new SharedInjectorComponent.InjectorComponentState(currentVolume, maxVolume, component.ToggleState);
+        args.State = new SharedInjectorComponent.InjectorComponentState(currentVolume, maxVolume, entity.Comp.ToggleState);
     }
 
-    private void OnInjectDoAfter(EntityUid uid, InjectorComponent component, DoAfterEvent args)
+    private void OnInjectDoAfter(Entity<InjectorComponent> entity, ref InjectorDoAfterEvent args)
     {
         if (args.Cancelled || args.Handled || args.Args.Target == null)
             return;
 
-        UseInjector(args.Args.Target.Value, args.Args.User, uid, component);
+        UseInjector(entity, args.Args.Target.Value, args.Args.User);
         args.Handled = true;
     }
 
-    private void OnInjectorAfterInteract(EntityUid uid, InjectorComponent component, AfterInteractEvent args)
+    private void OnInjectorAfterInteract(Entity<InjectorComponent> entity, ref AfterInteractEvent args)
     {
         if (args.Handled || !args.CanReach)
             return;
 
         //Make sure we have the attacking entity
-        if (args.Target is not { Valid: true } target || !HasComp<SolutionContainerManagerComponent>(uid))
+        if (args.Target is not { Valid: true } target || !HasComp<SolutionContainerManagerComponent>(entity))
             return;
 
         // Is the target a mob? If yes, use a do-after to give them time to respond.
         if (HasComp<MobStateComponent>(target) || HasComp<BloodstreamComponent>(target))
         {
             // Are use using an injector capible of targeting a mob?
-            if (component.IgnoreMobs)
+            if (entity.Comp.IgnoreMobs)
                 return;
 
-            InjectDoAfter(component, args.User, target, uid);
+            InjectDoAfter(entity, target, args.User);
             args.Handled = true;
             return;
         }
 
-        UseInjector(target, args.User, uid, component);
+        UseInjector(entity, target, args.User);
         args.Handled = true;
     }
 
-    private void OnInjectorStartup(EntityUid uid, InjectorComponent component, ComponentStartup args)
+    private void OnInjectorStartup(Entity<InjectorComponent> entity, ref ComponentStartup args)
     {
         // ???? why ?????
-        Dirty(component);
+        Dirty(entity);
     }
 
-    private void OnInjectorUse(EntityUid uid, InjectorComponent component, UseInHandEvent args)
+    private void OnInjectorUse(Entity<InjectorComponent> entity, ref UseInHandEvent args)
     {
         if (args.Handled)
             return;
 
-        Toggle(component, args.User, uid);
+        Toggle(entity, args.User);
         args.Handled = true;
     }
 
     /// <summary>
     /// Toggle between draw/inject state if applicable
     /// </summary>
-    private void Toggle(InjectorComponent component, EntityUid user, EntityUid injector)
+    private void Toggle(Entity<InjectorComponent> injector, EntityUid user)
     {
-        if (component.InjectOnly)
+        if (injector.Comp.InjectOnly)
         {
             return;
         }
 
         string msg;
-        switch (component.ToggleState)
+        switch (injector.Comp.ToggleState)
         {
             case SharedInjectorComponent.InjectorToggleMode.Inject:
-                component.ToggleState = SharedInjectorComponent.InjectorToggleMode.Draw;
+                injector.Comp.ToggleState = SharedInjectorComponent.InjectorToggleMode.Draw;
                 msg = "injector-component-drawing-text";
                 break;
             case SharedInjectorComponent.InjectorToggleMode.Draw:
-                component.ToggleState = SharedInjectorComponent.InjectorToggleMode.Inject;
+                injector.Comp.ToggleState = SharedInjectorComponent.InjectorToggleMode.Inject;
                 msg = "injector-component-injecting-text";
                 break;
             default:
@@ -209,18 +214,18 @@ public sealed partial class ChemistrySystem
     /// <summary>
     /// Send informative pop-up messages and wait for a do-after to complete.
     /// </summary>
-    private void InjectDoAfter(InjectorComponent component, EntityUid user, EntityUid target, EntityUid injector)
+    private void InjectDoAfter(Entity<InjectorComponent> injector, EntityUid target, EntityUid user)
     {
         // Create a pop-up for the user
         _popup.PopupEntity(Loc.GetString("injector-component-injecting-user"), target, user);
 
-        if (!_solutions.TryGetSolution(injector, InjectorComponent.SolutionName, out var solution))
+        if (!_solutionContainers.TryGetSolution(injector.Owner, InjectorComponent.SolutionName, out _, out var solution))
             return;
 
-        var actualDelay = MathF.Max(component.Delay, 1f);
+        var actualDelay = MathF.Max(injector.Comp.Delay, 1f);
 
         // Injections take 0.5 seconds longer per additional 5u
-        actualDelay += (float) component.TransferAmount / component.Delay - 0.5f;
+        actualDelay += (float) injector.Comp.TransferAmount / injector.Comp.Delay - 0.5f;
 
         var isTarget = user != target;
 
@@ -244,7 +249,7 @@ public sealed partial class ChemistrySystem
             }
 
             // Add an admin log, using the "force feed" log type. It's not quite feeding, but the effect is the same.
-            if (component.ToggleState == SharedInjectorComponent.InjectorToggleMode.Inject)
+            if (injector.Comp.ToggleState == SharedInjectorComponent.InjectorToggleMode.Inject)
             {
                 _adminLogger.Add(LogType.ForceFeed,
                     $"{EntityManager.ToPrettyString(user):user} is attempting to inject {EntityManager.ToPrettyString(target):target} with a solution {SolutionContainerSystem.ToPrettyString(solution):solution}");
@@ -255,11 +260,11 @@ public sealed partial class ChemistrySystem
             // Self-injections take half as long.
             actualDelay /= 2;
 
-            if (component.ToggleState == SharedInjectorComponent.InjectorToggleMode.Inject)
+            if (injector.Comp.ToggleState == SharedInjectorComponent.InjectorToggleMode.Inject)
                 _adminLogger.Add(LogType.Ingestion, $"{EntityManager.ToPrettyString(user):user} is attempting to inject themselves with a solution {SolutionContainerSystem.ToPrettyString(solution):solution}.");
         }
 
-        _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, user, actualDelay, new InjectorDoAfterEvent(), injector, target: target, used: injector)
+        _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, user, actualDelay, new InjectorDoAfterEvent(), injector.Owner, target: target, used: injector.Owner)
         {
             BreakOnUserMove = true,
             BreakOnDamage = true,
@@ -268,77 +273,80 @@ public sealed partial class ChemistrySystem
         });
     }
 
-    private void TryInjectIntoBloodstream(InjectorComponent component, EntityUid injector, EntityUid target, BloodstreamComponent targetBloodstream, EntityUid user)
+    private void TryInjectIntoBloodstream(Entity<InjectorComponent> injector, Entity<BloodstreamComponent> target, EntityUid user)
     {
         // Get transfer amount. May be smaller than _transferAmount if not enough room
-        var realTransferAmount = FixedPoint2.Min(component.TransferAmount, targetBloodstream.ChemicalSolution.AvailableVolume);
+        if (!_solutionContainers.ResolveSolution(target.Owner, target.Comp.ChemicalSolutionName, ref target.Comp.ChemicalSolution, out var chemSolution))
+        {
+            _popup.PopupEntity(Loc.GetString("injector-component-cannot-inject-message", ("target", Identity.Entity(target, EntityManager))), injector.Owner, user);
+            return;
+        }
 
+        var realTransferAmount = FixedPoint2.Min(injector.Comp.TransferAmount, chemSolution.AvailableVolume);
         if (realTransferAmount <= 0)
         {
-            _popup.PopupEntity(Loc.GetString("injector-component-cannot-inject-message", ("target", Identity.Entity(target, EntityManager))), injector, user);
+            _popup.PopupEntity(Loc.GetString("injector-component-cannot-inject-message", ("target", Identity.Entity(target, EntityManager))), injector.Owner, user);
             return;
         }
 
         // Move units from attackSolution to targetSolution
-        var removedSolution = _solutions.SplitSolution(user, targetBloodstream.ChemicalSolution, realTransferAmount);
+        var removedSolution = _solutionContainers.SplitSolution(target.Comp.ChemicalSolution.Value, realTransferAmount);
 
-        _blood.TryAddToChemicals(target, removedSolution, targetBloodstream);
+        _blood.TryAddToChemicals(target, removedSolution, target.Comp);
 
         _reactiveSystem.DoEntityReaction(target, removedSolution, ReactionMethod.Injection);
 
         _popup.PopupEntity(Loc.GetString("injector-component-inject-success-message",
                 ("amount", removedSolution.Volume),
-                ("target", Identity.Entity(target, EntityManager))), injector, user);
+                ("target", Identity.Entity(target, EntityManager))), injector.Owner, user);
 
-        Dirty(component);
-        AfterInject(component, injector, target);
+        Dirty(injector);
+        AfterInject(injector, target);
     }
 
-    private void TryInject(InjectorComponent component, EntityUid injector, EntityUid targetEntity, Solution targetSolution, EntityUid user, bool asRefill)
+    private void TryInject(Entity<InjectorComponent> injector, EntityUid targetEntity, Entity<SolutionComponent> targetSolution, EntityUid user, bool asRefill)
     {
-        if (!_solutions.TryGetSolution(injector, InjectorComponent.SolutionName, out var solution)
-            || solution.Volume == 0)
+        if (!_solutionContainers.TryGetSolution(injector.Owner, InjectorComponent.SolutionName, out var soln, out var solution) || solution.Volume == 0)
             return;
 
         // Get transfer amount. May be smaller than _transferAmount if not enough room
-        var realTransferAmount = FixedPoint2.Min(component.TransferAmount, targetSolution.AvailableVolume);
+        var realTransferAmount = FixedPoint2.Min(injector.Comp.TransferAmount, targetSolution.Comp.Solution.AvailableVolume);
 
         if (realTransferAmount <= 0)
         {
             _popup.PopupEntity(Loc.GetString("injector-component-target-already-full-message", ("target", Identity.Entity(targetEntity, EntityManager))),
-                injector, user);
+                injector.Owner, user);
             return;
         }
 
         // Move units from attackSolution to targetSolution
         Solution removedSolution;
         if (TryComp<StackComponent>(targetEntity, out var stack))
-            removedSolution = _solutions.SplitStackSolution(injector, solution, realTransferAmount, stack.Count);
+            removedSolution = _solutionContainers.SplitStackSolution(soln.Value, realTransferAmount, stack.Count);
         else
-          removedSolution = _solutions.SplitSolution(injector, solution, realTransferAmount);
+            removedSolution = _solutionContainers.SplitSolution(soln.Value, realTransferAmount);
 
         _reactiveSystem.DoEntityReaction(targetEntity, removedSolution, ReactionMethod.Injection);
 
         if (!asRefill)
-            _solutions.Inject(targetEntity, targetSolution, removedSolution);
+            _solutionContainers.Inject(targetEntity, targetSolution, removedSolution);
         else
-            _solutions.Refill(targetEntity, targetSolution, removedSolution);
+            _solutionContainers.Refill(targetEntity, targetSolution, removedSolution);
 
         _popup.PopupEntity(Loc.GetString("injector-component-transfer-success-message",
                 ("amount", removedSolution.Volume),
-                ("target", Identity.Entity(targetEntity, EntityManager))), injector, user);
+                ("target", Identity.Entity(targetEntity, EntityManager))), injector.Owner, user);
 
-        Dirty(component);
-        AfterInject(component, injector, targetEntity);
+        Dirty(injector);
+        AfterInject(injector, targetEntity);
     }
 
-    private void AfterInject(InjectorComponent component, EntityUid injector, EntityUid target)
+    private void AfterInject(Entity<InjectorComponent> injector, EntityUid target)
     {
         // Automatically set syringe to draw after completely draining it.
-        if (_solutions.TryGetSolution(injector, InjectorComponent.SolutionName, out var solution)
-            && solution.Volume == 0)
+        if (_solutionContainers.TryGetSolution(injector.Owner, InjectorComponent.SolutionName, out _, out var solution) && solution.Volume == 0)
         {
-            component.ToggleState = SharedInjectorComponent.InjectorToggleMode.Draw;
+            injector.Comp.ToggleState = SharedInjectorComponent.InjectorToggleMode.Draw;
         }
 
         // Leave some DNA from the injectee on it
@@ -346,13 +354,12 @@ public sealed partial class ChemistrySystem
         RaiseLocalEvent(target, ref ev);
     }
 
-    private void AfterDraw(InjectorComponent component, EntityUid injector, EntityUid target)
+    private void AfterDraw(Entity<InjectorComponent> injector, EntityUid target)
     {
         // Automatically set syringe to inject after completely filling it.
-        if (_solutions.TryGetSolution(injector, InjectorComponent.SolutionName, out var solution)
-            && solution.AvailableVolume == 0)
+        if (_solutionContainers.TryGetSolution(injector.Owner, InjectorComponent.SolutionName, out _, out var solution) && solution.AvailableVolume == 0)
         {
-            component.ToggleState = SharedInjectorComponent.InjectorToggleMode.Inject;
+            injector.Comp.ToggleState = SharedInjectorComponent.InjectorToggleMode.Inject;
         }
 
         // Leave some DNA from the drawee on it
@@ -360,70 +367,68 @@ public sealed partial class ChemistrySystem
         RaiseLocalEvent(target, ref ev);
     }
 
-    private void TryDraw(InjectorComponent component, EntityUid injector, EntityUid targetEntity, Solution targetSolution, EntityUid user, BloodstreamComponent? stream = null)
+    private void TryDraw(Entity<InjectorComponent> injector, Entity<BloodstreamComponent?> target, Entity<SolutionComponent> targetSolution, EntityUid user)
     {
-        if (!_solutions.TryGetSolution(injector, InjectorComponent.SolutionName, out var solution)
-            || solution.AvailableVolume == 0)
+        if (!_solutionContainers.TryGetSolution(injector.Owner, InjectorComponent.SolutionName, out var soln, out var solution) || solution.AvailableVolume == 0)
         {
             return;
         }
 
         // Get transfer amount. May be smaller than _transferAmount if not enough room, also make sure there's room in the injector
-        var realTransferAmount = FixedPoint2.Min(component.TransferAmount, targetSolution.Volume, solution.AvailableVolume);
+        var realTransferAmount = FixedPoint2.Min(injector.Comp.TransferAmount, targetSolution.Comp.Solution.Volume, solution.AvailableVolume);
 
         if (realTransferAmount <= 0)
         {
-            _popup.PopupEntity(Loc.GetString("injector-component-target-is-empty-message", ("target", Identity.Entity(targetEntity, EntityManager))),
-                injector, user);
+            _popup.PopupEntity(Loc.GetString("injector-component-target-is-empty-message", ("target", Identity.Entity(target, EntityManager))),
+                injector.Owner, user);
             return;
         }
 
         // We have some snowflaked behavior for streams.
-        if (stream != null)
+        if (target.Comp != null)
         {
-            DrawFromBlood(user, injector, targetEntity, component, solution, stream, realTransferAmount);
+            DrawFromBlood(injector, (target.Owner, target.Comp), soln.Value, realTransferAmount, user);
             return;
         }
 
         // Move units from attackSolution to targetSolution
-        var removedSolution = _solutions.Draw(targetEntity, targetSolution, realTransferAmount);
+        var removedSolution = _solutionContainers.Draw(target.Owner, targetSolution, realTransferAmount);
 
-        if (!_solutions.TryAddSolution(injector, solution, removedSolution))
+        if (!_solutionContainers.TryAddSolution(soln.Value, removedSolution))
         {
             return;
         }
 
         _popup.PopupEntity(Loc.GetString("injector-component-draw-success-message",
                 ("amount", removedSolution.Volume),
-                ("target", Identity.Entity(targetEntity, EntityManager))), injector, user);
+                ("target", Identity.Entity(target, EntityManager))), injector.Owner, user);
 
-        Dirty(component);
-        AfterDraw(component, injector, targetEntity);
+        Dirty(injector);
+        AfterDraw(injector, target);
     }
 
-    private void DrawFromBlood(EntityUid user, EntityUid injector, EntityUid target, InjectorComponent component, Solution injectorSolution, BloodstreamComponent stream, FixedPoint2 transferAmount)
+    private void DrawFromBlood(Entity<InjectorComponent> injector, Entity<BloodstreamComponent> target, Entity<SolutionComponent> injectorSolution, FixedPoint2 transferAmount, EntityUid user)
     {
         var drawAmount = (float) transferAmount;
-        var bloodAmount = drawAmount;
-        var chemAmount = 0f;
-        if (stream.ChemicalSolution.Volume > 0f) // If they have stuff in their chem stream, we'll draw some of that
+
+        if (_solutionContainers.ResolveSolution(target.Owner, target.Comp.ChemicalSolutionName, ref target.Comp.ChemicalSolution))
         {
-            bloodAmount = drawAmount * 0.85f;
-            chemAmount = drawAmount * 0.15f;
+            var chemTemp = _solutionContainers.SplitSolution(target.Comp.ChemicalSolution.Value, drawAmount * 0.15f);
+            _solutionContainers.TryAddSolution(injectorSolution, chemTemp);
+            drawAmount -= (float) chemTemp.Volume;
         }
 
-        var bloodTemp = stream.BloodSolution.SplitSolution(bloodAmount);
-        var chemTemp = stream.ChemicalSolution.SplitSolution(chemAmount);
-
-        _solutions.TryAddSolution(injector, injectorSolution, bloodTemp);
-        _solutions.TryAddSolution(injector, injectorSolution, chemTemp);
+        if (_solutionContainers.ResolveSolution(target.Owner, target.Comp.BloodSolutionName, ref target.Comp.BloodSolution))
+        {
+            var bloodTemp = _solutionContainers.SplitSolution(target.Comp.BloodSolution.Value, drawAmount);
+            _solutionContainers.TryAddSolution(injectorSolution, bloodTemp);
+        }
 
         _popup.PopupEntity(Loc.GetString("injector-component-draw-success-message",
                 ("amount", transferAmount),
-                ("target", Identity.Entity(target, EntityManager))), injector, user);
+                ("target", Identity.Entity(target, EntityManager))), injector.Owner, user);
 
-        Dirty(component);
-        AfterDraw(component, injector, target);
+        Dirty(injector);
+        AfterDraw(injector, target);
     }
-
 }
index 759d403acee0ed3b54efbfedddfe4711400b5147..a2b4f399e2d869d83373f0ee14a84ea311008d00 100644 (file)
@@ -1,13 +1,12 @@
 using Content.Server.Administration.Logs;
 using Content.Server.Body.Systems;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Interaction;
 using Content.Server.Popups;
-using Content.Shared.CombatMode;
 using Content.Shared.Chemistry;
-using Content.Shared.Chemistry.EntitySystems;
+using Content.Shared.CombatMode;
 using Content.Shared.DoAfter;
 using Content.Shared.Mobs.Systems;
-using Robust.Shared.Audio;
 using Robust.Shared.Audio.Systems;
 
 namespace Content.Server.Chemistry.EntitySystems;
@@ -24,7 +23,7 @@ public sealed partial class ChemistrySystem : EntitySystem
     [Dependency] private readonly SharedAudioSystem _audio = default!;
     [Dependency] private readonly MobStateSystem _mobState = default!;
     [Dependency] private readonly SharedCombatModeSystem _combat = default!;
-    [Dependency] private readonly SolutionContainerSystem _solutions = default!;
+    [Dependency] private readonly SolutionContainerSystem _solutionContainers = default!;
 
     public override void Initialize()
     {
index cb4feb806401863e422c00675bd7d2779d5adb7f..9c1b5130a22d71ce051e677d88b7cc314884369e 100644 (file)
@@ -1,20 +1,21 @@
-using System.Linq;
-using System.Diagnostics.CodeAnalysis;
 using Content.Server.Chemistry.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Components.SolutionManager;
 using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.Database;
 using Content.Shared.FixedPoint;
+using Content.Shared.Forensics;
 using Content.Shared.IdentityManagement;
 using Content.Shared.Interaction;
 using Content.Shared.Interaction.Events;
 using Content.Shared.Mobs.Components;
-using Content.Shared.Weapons.Melee.Events;
 using Content.Shared.Timing;
+using Content.Shared.Weapons.Melee.Events;
 using Robust.Shared.GameStates;
-using Content.Shared.Forensics;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
 
 namespace Content.Server.Chemistry.EntitySystems
 {
@@ -26,33 +27,33 @@ namespace Content.Server.Chemistry.EntitySystems
         {
             SubscribeLocalEvent<HyposprayComponent, AfterInteractEvent>(OnAfterInteract);
             SubscribeLocalEvent<HyposprayComponent, MeleeHitEvent>(OnAttack);
-            SubscribeLocalEvent<HyposprayComponent, SolutionChangedEvent>(OnSolutionChange);
+            SubscribeLocalEvent<HyposprayComponent, SolutionContainerChangedEvent>(OnSolutionChange);
             SubscribeLocalEvent<HyposprayComponent, UseInHandEvent>(OnUseInHand);
             SubscribeLocalEvent<HyposprayComponent, ComponentGetState>(OnHypoGetState);
         }
 
-        private void OnHypoGetState(EntityUid uid, HyposprayComponent component, ref ComponentGetState args)
+        private void OnHypoGetState(Entity<HyposprayComponent> entity, ref ComponentGetState args)
         {
-            args.State = _solutions.TryGetSolution(uid, component.SolutionName, out var solution)
+            args.State = _solutionContainers.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out _, out var solution)
                 ? new HyposprayComponentState(solution.Volume, solution.MaxVolume)
                 : new HyposprayComponentState(FixedPoint2.Zero, FixedPoint2.Zero);
         }
 
-        private void OnUseInHand(EntityUid uid, HyposprayComponent component, UseInHandEvent args)
+        private void OnUseInHand(Entity<HyposprayComponent> entity, ref UseInHandEvent args)
         {
             if (args.Handled)
                 return;
 
-            TryDoInject(uid, args.User, args.User);
+            TryDoInject(entity, args.User, args.User);
             args.Handled = true;
         }
 
-        private void OnSolutionChange(EntityUid uid, HyposprayComponent component, SolutionChangedEvent args)
+        private void OnSolutionChange(Entity<HyposprayComponent> entity, ref SolutionContainerChangedEvent args)
         {
-            Dirty(component);
+            Dirty(entity);
         }
 
-        public void OnAfterInteract(EntityUid uid, HyposprayComponent component, AfterInteractEvent args)
+        public void OnAfterInteract(Entity<HyposprayComponent> entity, ref AfterInteractEvent args)
         {
             if (!args.CanReach)
                 return;
@@ -60,21 +61,20 @@ namespace Content.Server.Chemistry.EntitySystems
             var target = args.Target;
             var user = args.User;
 
-            TryDoInject(uid, target, user);
+            TryDoInject(entity, target, user);
         }
 
-        public void OnAttack(EntityUid uid, HyposprayComponent component, MeleeHitEvent args)
+        public void OnAttack(Entity<HyposprayComponent> entity, ref MeleeHitEvent args)
         {
             if (!args.HitEntities.Any())
                 return;
 
-            TryDoInject(uid, args.HitEntities.First(), args.User);
+            TryDoInject(entity, args.HitEntities.First(), args.User);
         }
 
-        public bool TryDoInject(EntityUid uid, EntityUid? target, EntityUid user, HyposprayComponent? component=null)
+        public bool TryDoInject(Entity<HyposprayComponent> hypo, EntityUid? target, EntityUid user)
         {
-            if (!Resolve(uid, ref component))
-                return false;
+            var (uid, component) = hypo;
 
             if (!EligibleEntity(target, _entMan, component))
                 return false;
@@ -92,15 +92,13 @@ namespace Content.Server.Chemistry.EntitySystems
                 target = user;
             }
 
-            _solutions.TryGetSolution(uid, component.SolutionName, out var hypoSpraySolution);
-
-            if (hypoSpraySolution == null || hypoSpraySolution.Volume == 0)
+            if (!_solutionContainers.TryGetSolution(uid, component.SolutionName, out var hypoSpraySoln, out var hypoSpraySolution) || hypoSpraySolution.Volume == 0)
             {
                 _popup.PopupCursor(Loc.GetString("hypospray-component-empty-message"), user);
                 return true;
             }
 
-            if (!_solutions.TryGetInjectableSolution(target.Value, out var targetSolution))
+            if (!_solutionContainers.TryGetInjectableSolution(target.Value, out var targetSoln, out var targetSolution))
             {
                 _popup.PopupCursor(Loc.GetString("hypospray-cant-inject", ("target", Identity.Entity(target.Value, _entMan))), user);
                 return false;
@@ -127,17 +125,17 @@ namespace Content.Server.Chemistry.EntitySystems
 
             if (realTransferAmount <= 0)
             {
-                _popup.PopupCursor(Loc.GetString("hypospray-component-transfer-already-full-message",("owner", target)), user);
+                _popup.PopupCursor(Loc.GetString("hypospray-component-transfer-already-full-message", ("owner", target)), user);
                 return true;
             }
 
             // Move units from attackSolution to targetSolution
-            var removedSolution = _solutions.SplitSolution(uid, hypoSpraySolution, realTransferAmount);
+            var removedSolution = _solutionContainers.SplitSolution(hypoSpraySoln.Value, realTransferAmount);
 
             if (!targetSolution.CanAddSolution(removedSolution))
                 return true;
             _reactiveSystem.DoEntityReaction(target.Value, removedSolution, ReactionMethod.Injection);
-            _solutions.TryAddSolution(target.Value, targetSolution, removedSolution);
+            _solutionContainers.TryAddSolution(targetSoln.Value, removedSolution);
 
             var ev = new TransferDnaEvent { Donor = target.Value, Recipient = uid };
             RaiseLocalEvent(target.Value, ref ev);
index 207282421dc63d8b7bf27788300b7aceea101d8a..0230671ec986eee1f6706f90302fd3f342b07d95 100644 (file)
@@ -1,10 +1,6 @@
-using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components;
-using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Reaction;
 using Content.Shared.IdentityManagement;
 using Content.Shared.Interaction;
-using Content.Shared.Interaction.Events;
-using Robust.Shared.Player;
 
 namespace Content.Server.Chemistry.EntitySystems;
 
@@ -15,27 +11,26 @@ public sealed partial class ChemistrySystem
         SubscribeLocalEvent<ReactionMixerComponent, AfterInteractEvent>(OnAfterInteract);
     }
 
-    private void OnAfterInteract(EntityUid uid, ReactionMixerComponent component, AfterInteractEvent args)
+    private void OnAfterInteract(Entity<ReactionMixerComponent> entity, ref AfterInteractEvent args)
     {
         if (!args.Target.HasValue || !args.CanReach)
             return;
 
-        var mixAttemptEvent = new MixingAttemptEvent(uid);
-        RaiseLocalEvent(uid, ref mixAttemptEvent);
-        if(mixAttemptEvent.Cancelled)
+        var mixAttemptEvent = new MixingAttemptEvent(entity);
+        RaiseLocalEvent(entity, ref mixAttemptEvent);
+        if (mixAttemptEvent.Cancelled)
         {
             return;
         }
 
-        Solution? solution = null;
-        if (!_solutions.TryGetMixableSolution(args.Target.Value, out solution))
-              return;
+        if (!_solutionContainers.TryGetMixableSolution(args.Target.Value, out var solution))
+            return;
 
-        _popup.PopupEntity(Loc.GetString(component.MixMessage, ("mixed", Identity.Entity(args.Target.Value, EntityManager)), ("mixer", Identity.Entity(uid, EntityManager))), args.User, args.User);
+        _popup.PopupEntity(Loc.GetString(entity.Comp.MixMessage, ("mixed", Identity.Entity(args.Target.Value, EntityManager)), ("mixer", Identity.Entity(entity.Owner, EntityManager))), args.User, args.User);
 
-        _solutions.UpdateChemicals(args.Target.Value, solution, true, component);
+        _solutionContainers.UpdateChemicals(solution.Value, true, entity.Comp);
 
-        var afterMixingEvent = new AfterMixingEvent(uid, args.Target.Value);
-        RaiseLocalEvent(uid, afterMixingEvent);
+        var afterMixingEvent = new AfterMixingEvent(entity, args.Target.Value);
+        RaiseLocalEvent(entity, afterMixingEvent);
     }
 }
index fd7933e66af69cd2314410a907f4bbaf531b8fbd..cfc6ba988aa428e77ab62aad934b867928eec80f 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Server.Chemistry.Components.DeleteOnSolutionEmptyComponent;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Chemistry.Components.SolutionManager;
 using Content.Shared.Chemistry.EntitySystems;
 
@@ -12,27 +13,27 @@ namespace Content.Server.Chemistry.EntitySystems.DeleteOnSolutionEmptySystem
         {
             base.Initialize();
             SubscribeLocalEvent<DeleteOnSolutionEmptyComponent, ComponentStartup>(OnStartup);
-            SubscribeLocalEvent<DeleteOnSolutionEmptyComponent, SolutionChangedEvent>(OnSolutionChange);
+            SubscribeLocalEvent<DeleteOnSolutionEmptyComponent, SolutionContainerChangedEvent>(OnSolutionChange);
         }
 
-        public void OnStartup(EntityUid uid, DeleteOnSolutionEmptyComponent component, ComponentStartup args)
+        public void OnStartup(Entity<DeleteOnSolutionEmptyComponent> entity, ref ComponentStartup args)
         {
-            CheckSolutions(uid, component);
+            CheckSolutions(entity);
         }
 
-        public void OnSolutionChange(EntityUid uid, DeleteOnSolutionEmptyComponent component, SolutionChangedEvent args)
+        public void OnSolutionChange(Entity<DeleteOnSolutionEmptyComponent> entity, ref SolutionContainerChangedEvent args)
         {
-            CheckSolutions(uid, component);
+            CheckSolutions(entity);
         }
 
-        public void CheckSolutions(EntityUid uid, DeleteOnSolutionEmptyComponent component)
+        public void CheckSolutions(Entity<DeleteOnSolutionEmptyComponent> entity)
         {
-            if (!EntityManager.HasComponent<SolutionContainerManagerComponent>(uid))
+            if (!TryComp(entity, out SolutionContainerManagerComponent? solutions))
                 return;
 
-            if (_solutionContainerSystem.TryGetSolution(uid, component.Solution, out var solution))
+            if (_solutionContainerSystem.TryGetSolution((entity.Owner, solutions), entity.Comp.Solution, out _, out var solution))
                 if (solution.Volume <= 0)
-                    EntityManager.QueueDeleteEntity(uid);
+                    EntityManager.QueueDeleteEntity(entity);
         }
     }
 }
index 3768ee1051b9e767e3df2fd1936c73ac583ae9eb..aeb141fe35b8b365a0788bac55f1b95850011801 100644 (file)
@@ -1,6 +1,6 @@
-using System.Linq;
 using Content.Server.Administration.Logs;
 using Content.Server.Chemistry.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Chemistry;
 using Content.Shared.Chemistry.Dispenser;
 using Content.Shared.Chemistry.EntitySystems;
@@ -15,6 +15,7 @@ using Robust.Server.GameObjects;
 using Robust.Shared.Audio;
 using Robust.Shared.Containers;
 using Robust.Shared.Prototypes;
+using System.Linq;
 
 namespace Content.Server.Chemistry.EntitySystems
 {
@@ -36,7 +37,7 @@ namespace Content.Server.Chemistry.EntitySystems
             base.Initialize();
 
             SubscribeLocalEvent<ReagentDispenserComponent, ComponentStartup>(SubscribeUpdateUiState);
-            SubscribeLocalEvent<ReagentDispenserComponent, SolutionChangedEvent>(SubscribeUpdateUiState);
+            SubscribeLocalEvent<ReagentDispenserComponent, SolutionContainerChangedEvent>(SubscribeUpdateUiState);
             SubscribeLocalEvent<ReagentDispenserComponent, EntInsertedIntoContainerMessage>(SubscribeUpdateUiState);
             SubscribeLocalEvent<ReagentDispenserComponent, EntRemovedFromContainerMessage>(SubscribeUpdateUiState);
             SubscribeLocalEvent<ReagentDispenserComponent, BoundUIOpenedEvent>(SubscribeUpdateUiState);
@@ -68,7 +69,7 @@ namespace Content.Server.Chemistry.EntitySystems
             if (container is not { Valid: true })
                 return null;
 
-            if (_solutionContainerSystem.TryGetFitsInDispenser(container.Value, out var solution))
+            if (_solutionContainerSystem.TryGetFitsInDispenser(container.Value, out _, out var solution))
             {
                 return new ContainerInfo(Name(container.Value), solution.Volume, solution.MaxVolume)
                 {
@@ -122,10 +123,10 @@ namespace Content.Server.Chemistry.EntitySystems
                 return;
 
             var outputContainer = _itemSlotsSystem.GetItemOrNull(reagentDispenser, SharedReagentDispenser.OutputSlotName);
-            if (outputContainer is not {Valid: true} || !_solutionContainerSystem.TryGetFitsInDispenser(outputContainer.Value, out var solution))
+            if (outputContainer is not { Valid: true } || !_solutionContainerSystem.TryGetFitsInDispenser(outputContainer.Value, out var solution, out _))
                 return;
 
-            if (_solutionContainerSystem.TryAddReagent(outputContainer.Value, solution, message.ReagentId, (int)reagentDispenser.Comp.DispenseAmount, out var dispensedAmount)
+            if (_solutionContainerSystem.TryAddReagent(solution.Value, message.ReagentId, (int) reagentDispenser.Comp.DispenseAmount, out var dispensedAmount)
                 && message.Session.AttachedEntity is not null)
             {
                 _adminLogger.Add(LogType.ChemicalReaction, LogImpact.Medium,
@@ -139,10 +140,10 @@ namespace Content.Server.Chemistry.EntitySystems
         private void OnClearContainerSolutionMessage(Entity<ReagentDispenserComponent> reagentDispenser, ref ReagentDispenserClearContainerSolutionMessage message)
         {
             var outputContainer = _itemSlotsSystem.GetItemOrNull(reagentDispenser, SharedReagentDispenser.OutputSlotName);
-            if (outputContainer is not {Valid: true} || !_solutionContainerSystem.TryGetFitsInDispenser(outputContainer.Value, out var solution))
+            if (outputContainer is not { Valid: true } || !_solutionContainerSystem.TryGetFitsInDispenser(outputContainer.Value, out var solution, out _))
                 return;
 
-            _solutionContainerSystem.RemoveAllSolution(outputContainer.Value, solution);
+            _solutionContainerSystem.RemoveAllSolution(solution.Value);
             UpdateUiState(reagentDispenser);
             ClickSound(reagentDispenser);
         }
index b75aa311e336a062872a3e13ee605a8b7f707bc1..e1cdfd2066092b4176ceaa58fd3511b8870780fa 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Server.Chemistry.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.FixedPoint;
 using Content.Shared.Popups;
@@ -16,21 +17,23 @@ public sealed class RehydratableSystem : EntitySystem
     {
         base.Initialize();
 
-        SubscribeLocalEvent<RehydratableComponent, SolutionChangedEvent>(OnSolutionChange);
+        SubscribeLocalEvent<RehydratableComponent, SolutionContainerChangedEvent>(OnSolutionChange);
     }
 
-    private void OnSolutionChange(EntityUid uid, RehydratableComponent comp, SolutionChangedEvent args)
+    private void OnSolutionChange(Entity<RehydratableComponent> entity, ref SolutionContainerChangedEvent args)
     {
-        var quantity = _solutions.GetTotalPrototypeQuantity(uid, comp.CatalystPrototype);
-        if (quantity != FixedPoint2.Zero && quantity >= comp.CatalystMinimum)
+        var quantity = _solutions.GetTotalPrototypeQuantity(entity, entity.Comp.CatalystPrototype);
+        if (quantity != FixedPoint2.Zero && quantity >= entity.Comp.CatalystMinimum)
         {
-            Expand(uid, comp);
+            Expand(entity);
         }
     }
 
     // Try not to make this public if you can help it.
-    private void Expand(EntityUid uid, RehydratableComponent comp)
+    private void Expand(Entity<RehydratableComponent> entity)
     {
+        var (uid, comp) = entity;
+
         _popups.PopupEntity(Loc.GetString("rehydratable-component-expands-message", ("owner", uid)), uid);
 
         var randomMob = _random.Pick(comp.PossibleSpawns);
index 3e839bee50e4c4df16a8fadea8c109795af8cf90..1ef589ab5cb0dd26f30be27de50ddac442e8a820 100644 (file)
@@ -1,10 +1,10 @@
 using Content.Server.Chemistry.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Construction;
 using Content.Server.Power.Components;
 using Content.Server.Power.EntitySystems;
 using Content.Shared.Chemistry;
 using Content.Shared.Chemistry.Components.SolutionManager;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Placeable;
 
 namespace Content.Server.Chemistry.EntitySystems;
@@ -13,7 +13,7 @@ public sealed class SolutionHeaterSystem : EntitySystem
 {
     [Dependency] private readonly PowerReceiverSystem _powerReceiver = default!;
     [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
-    [Dependency] private readonly SolutionContainerSystem _solution = default!;
+    [Dependency] private readonly SolutionContainerSystem _solutionContainer = default!;
 
     /// <inheritdoc/>
     public override void Initialize()
@@ -51,41 +51,41 @@ public sealed class SolutionHeaterSystem : EntitySystem
         RemComp<ActiveSolutionHeaterComponent>(uid);
     }
 
-    private void OnPowerChanged(EntityUid uid, SolutionHeaterComponent component, ref PowerChangedEvent args)
+    private void OnPowerChanged(Entity<SolutionHeaterComponent> entity, ref PowerChangedEvent args)
     {
-        var placer = Comp<ItemPlacerComponent>(uid);
+        var placer = Comp<ItemPlacerComponent>(entity);
         if (args.Powered && placer.PlacedEntities.Count > 0)
         {
-            TurnOn(uid);
+            TurnOn(entity);
         }
         else
         {
-            TurnOff(uid);
+            TurnOff(entity);
         }
     }
 
-    private void OnRefreshParts(EntityUid uid, SolutionHeaterComponent component, RefreshPartsEvent args)
+    private void OnRefreshParts(Entity<SolutionHeaterComponent> entity, ref RefreshPartsEvent args)
     {
-        var heatRating = args.PartRatings[component.MachinePartHeatMultiplier] - 1;
+        var heatRating = args.PartRatings[entity.Comp.MachinePartHeatMultiplier] - 1;
 
-        component.HeatPerSecond = component.BaseHeatPerSecond * MathF.Pow(component.PartRatingHeatMultiplier, heatRating);
+        entity.Comp.HeatPerSecond = entity.Comp.BaseHeatPerSecond * MathF.Pow(entity.Comp.PartRatingHeatMultiplier, heatRating);
     }
 
-    private void OnUpgradeExamine(EntityUid uid, SolutionHeaterComponent component, UpgradeExamineEvent args)
+    private void OnUpgradeExamine(Entity<SolutionHeaterComponent> entity, ref UpgradeExamineEvent args)
     {
-        args.AddPercentageUpgrade("solution-heater-upgrade-heat", component.HeatPerSecond / component.BaseHeatPerSecond);
+        args.AddPercentageUpgrade("solution-heater-upgrade-heat", entity.Comp.HeatPerSecond / entity.Comp.BaseHeatPerSecond);
     }
 
-    private void OnItemPlaced(EntityUid uid, SolutionHeaterComponent comp, ref ItemPlacedEvent args)
+    private void OnItemPlaced(Entity<SolutionHeaterComponent> entity, ref ItemPlacedEvent args)
     {
-        TryTurnOn(uid);
+        TryTurnOn(entity);
     }
 
-    private void OnItemRemoved(EntityUid uid, SolutionHeaterComponent component, ref ItemRemovedEvent args)
+    private void OnItemRemoved(Entity<SolutionHeaterComponent> entity, ref ItemRemovedEvent args)
     {
-        var placer = Comp<ItemPlacerComponent>(uid);
+        var placer = Comp<ItemPlacerComponent>(entity);
         if (placer.PlacedEntities.Count == 0) // Last entity was removed
-            TurnOff(uid);
+            TurnOff(entity);
     }
 
     public override void Update(float frameTime)
@@ -97,13 +97,13 @@ public sealed class SolutionHeaterSystem : EntitySystem
         {
             foreach (var heatingEntity in placer.PlacedEntities)
             {
-                if (!TryComp<SolutionContainerManagerComponent>(heatingEntity, out var solution))
+                if (!TryComp<SolutionContainerManagerComponent>(heatingEntity, out var container))
                     continue;
 
                 var energy = heater.HeatPerSecond * frameTime;
-                foreach (var s in solution.Solutions.Values)
+                foreach (var (_, soln) in _solutionContainer.EnumerateSolutions((heatingEntity, container)))
                 {
-                    _solution.AddThermalEnergy(heatingEntity, s, energy);
+                    _solutionContainer.AddThermalEnergy(soln, energy);
                 }
             }
         }
index e053ce972047819c57b13013dde1fb3294f4f6bc..f881240a09cfce3772721fe1ff96bf209534a45c 100644 (file)
@@ -1,19 +1,17 @@
 using Content.Server.Body.Components;
 using Content.Server.Body.Systems;
 using Content.Server.Chemistry.Components;
-using Content.Shared.Chemistry.EntitySystems;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Inventory;
 using JetBrains.Annotations;
 using Robust.Shared.Physics.Events;
-using Robust.Shared.Prototypes;
 
 namespace Content.Server.Chemistry.EntitySystems
 {
     [UsedImplicitly]
     internal sealed class SolutionInjectOnCollideSystem : EntitySystem
     {
-        [Dependency] private readonly IPrototypeManager _protoManager = default!;
-        [Dependency] private readonly SolutionContainerSystem _solutionsSystem = default!;
+        [Dependency] private readonly SolutionContainerSystem _solutionContainersSystem = default!;
         [Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!;
         [Dependency] private readonly InventorySystem _inventorySystem = default!;
 
@@ -30,7 +28,7 @@ namespace Content.Server.Chemistry.EntitySystems
 
             if (!args.OtherBody.Hard ||
                 !EntityManager.TryGetComponent<BloodstreamComponent>(target, out var bloodstream) ||
-                !_solutionsSystem.TryGetInjectableSolution(ent, out var solution))
+                !_solutionContainersSystem.TryGetInjectableSolution(ent.Owner, out var solution, out _))
             {
                 return;
             }
@@ -44,7 +42,7 @@ namespace Content.Server.Chemistry.EntitySystems
                     return;
             }
 
-            var solRemoved = solution.SplitSolution(component.TransferAmount);
+            var solRemoved = _solutionContainersSystem.SplitSolution(solution.Value, component.TransferAmount);
             var solRemovedVol = solRemoved.Volume;
 
             var solToInject = solRemoved.SplitSolution(solRemovedVol * component.TransferEfficiency);
index c918fa929c5459a531ea9f3897f165db08f00235..e6e003a7f5f4e7c1573aa50c7b6125fb68c997bb 100644 (file)
@@ -1,7 +1,6 @@
 using Content.Server.Chemistry.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Chemistry.Components.SolutionManager;
-using Content.Shared.Chemistry.EntitySystems;
-using Content.Shared.FixedPoint;
 using Robust.Shared.Timing;
 
 namespace Content.Server.Chemistry.EntitySystems;
@@ -30,13 +29,13 @@ public sealed class SolutionPurgeSystem : EntitySystem
 
             // timer ignores if it's empty, it's just a fixed cycle
             purge.NextPurgeTime += purge.Duration;
-            if (_solutionContainer.TryGetSolution(uid, purge.Solution, out var solution, manager))
-                _solutionContainer.SplitSolutionWithout(uid, solution, purge.Quantity, purge.Preserve.ToArray());
+            if (_solutionContainer.TryGetSolution((uid, manager), purge.Solution, out var solution))
+                _solutionContainer.SplitSolutionWithout(solution.Value, purge.Quantity, purge.Preserve.ToArray());
         }
     }
 
-    private void OnUnpaused(EntityUid uid, SolutionPurgeComponent comp, ref EntityUnpausedEvent args)
+    private void OnUnpaused(Entity<SolutionPurgeComponent> entity, ref EntityUnpausedEvent args)
     {
-        comp.NextPurgeTime += args.PausedTime;
+        entity.Comp.NextPurgeTime += args.PausedTime;
     }
 }
index c2b8efac6fe363a088babde70dfe20ab6a429920..6685fb485fe4626124d70cc83666675fcb2cbd64 100644 (file)
@@ -1,5 +1,5 @@
 using Content.Server.Chemistry.Components;
-using Content.Shared.Chemistry.EntitySystems;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.Random;
 using Content.Shared.Random.Helpers;
@@ -21,13 +21,12 @@ public sealed class SolutionRandomFillSystem : EntitySystem
         SubscribeLocalEvent<RandomFillSolutionComponent, MapInitEvent>(OnRandomSolutionFillMapInit);
     }
 
-    private void OnRandomSolutionFillMapInit(EntityUid uid, RandomFillSolutionComponent component, MapInitEvent args)
+    private void OnRandomSolutionFillMapInit(Entity<RandomFillSolutionComponent> entity, ref MapInitEvent args)
     {
-        if (component.WeightedRandomId == null)
+        if (entity.Comp.WeightedRandomId == null)
             return;
 
-        var target = _solutionsSystem.EnsureSolution(uid, component.Solution);
-        var pick = _proto.Index<WeightedRandomFillSolutionPrototype>(component.WeightedRandomId).Pick(_random);
+        var pick = _proto.Index<WeightedRandomFillSolutionPrototype>(entity.Comp.WeightedRandomId).Pick(_random);
 
         var reagent = pick.reagent;
         var quantity = pick.quantity;
@@ -38,6 +37,7 @@ public sealed class SolutionRandomFillSystem : EntitySystem
             return;
         }
 
-        target.AddReagent(reagent, quantity);
+        var target = _solutionsSystem.EnsureSolutionEntity(entity.Owner, entity.Comp.Solution, pick.quantity, null, out _);
+        _solutionsSystem.TryAddReagent(target, reagent, quantity, out _);
     }
 }
index 95908dd703185bfebfe6ba734d15910aa332f41d..37458846e333f92ae155af14f90643f96df2f776 100644 (file)
@@ -1,7 +1,7 @@
 using Content.Server.Chemistry.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Components.SolutionManager;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.FixedPoint;
 using Robust.Shared.Timing;
 
@@ -31,7 +31,7 @@ public sealed class SolutionRegenerationSystem : EntitySystem
 
             // timer ignores if its full, it's just a fixed cycle
             regen.NextRegenTime = _timing.CurTime + regen.Duration;
-            if (_solutionContainer.TryGetSolution(uid, regen.Solution, out var solution, manager))
+            if (_solutionContainer.ResolveSolution((uid, manager), regen.SolutionName, ref regen.Solution, out var solution))
             {
                 var amount = FixedPoint2.Min(solution.AvailableVolume, regen.Generated.Volume);
                 if (amount <= FixedPoint2.Zero)
@@ -48,13 +48,13 @@ public sealed class SolutionRegenerationSystem : EntitySystem
                     generated = regen.Generated.Clone().SplitSolution(amount);
                 }
 
-                _solutionContainer.TryAddSolution(uid, solution, generated);
+                _solutionContainer.TryAddSolution(regen.Solution.Value, generated);
             }
         }
     }
 
-    private void OnUnpaused(EntityUid uid, SolutionRegenerationComponent comp, ref EntityUnpausedEvent args)
+    private void OnUnpaused(Entity<SolutionRegenerationComponent> entity, ref EntityUnpausedEvent args)
     {
-        comp.NextRegenTime += args.PausedTime;
+        entity.Comp.NextRegenTime += args.PausedTime;
     }
 }
index 04aa6546a90fcc8cd013c7ae145cc5b282fdcfb3..7518b45a8bcd7207b5667f86871c2f5d68e8622d 100644 (file)
@@ -1,12 +1,10 @@
 using Content.Server.Chemistry.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Explosion.EntitySystems;
 using Content.Server.Popups;
 using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Components.SolutionManager;
-using Content.Shared.Chemistry.EntitySystems;
-using Content.Shared.FixedPoint;
 using Content.Shared.Interaction;
-using Robust.Shared.Player;
 
 namespace Content.Server.Chemistry.EntitySystems;
 
@@ -21,7 +19,7 @@ namespace Content.Server.Chemistry.EntitySystems;
 /// </summary>
 public sealed class SolutionSpikableSystem : EntitySystem
 {
-    [Dependency] private readonly SolutionContainerSystem _solutionSystem = default!;
+    [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
     [Dependency] private readonly TriggerSystem _triggerSystem = default!;
     [Dependency] private readonly PopupSystem _popupSystem = default!;
 
@@ -30,9 +28,9 @@ public sealed class SolutionSpikableSystem : EntitySystem
         SubscribeLocalEvent<RefillableSolutionComponent, InteractUsingEvent>(OnInteractUsing);
     }
 
-    private void OnInteractUsing(EntityUid uid, RefillableSolutionComponent target, InteractUsingEvent args)
+    private void OnInteractUsing(Entity<RefillableSolutionComponent> entity, ref InteractUsingEvent args)
     {
-        TrySpike(args.Used, args.Target, args.User, target);
+        TrySpike(args.Used, args.Target, args.User, entity.Comp);
     }
 
     /// <summary>
@@ -49,8 +47,8 @@ public sealed class SolutionSpikableSystem : EntitySystem
     {
         if (!Resolve(source, ref spikableSource, ref managerSource, false)
             || !Resolve(target, ref spikableTarget, ref managerTarget, false)
-            || !_solutionSystem.TryGetRefillableSolution(target, out var targetSolution, managerTarget, spikableTarget)
-            || !managerSource.Solutions.TryGetValue(spikableSource.SourceSolution, out var sourceSolution))
+            || !_solutionContainerSystem.TryGetRefillableSolution((target, spikableTarget, managerTarget), out var targetSoln, out var targetSolution)
+            || !_solutionContainerSystem.TryGetSolution((source, managerSource), spikableSource.SourceSolution, out _, out var sourceSolution))
         {
             return;
         }
@@ -61,7 +59,7 @@ public sealed class SolutionSpikableSystem : EntitySystem
             return;
         }
 
-        if (!_solutionSystem.ForceAddSolution(target, targetSolution, sourceSolution))
+        if (!_solutionContainerSystem.ForceAddSolution(targetSoln.Value, sourceSolution))
             return;
 
         _popupSystem.PopupEntity(Loc.GetString(spikableSource.Popup, ("spiked-entity", target), ("spike-entity", source)), user, user);
index d2666417cf8fc9954250cc3789d9cdfe479db00c..1ed5cec8dd83adb40e9c326e510b5c477faf4bb6 100644 (file)
@@ -1,14 +1,14 @@
 using Content.Server.Administration.Logs;
-using Content.Shared.Verbs;
-using JetBrains.Annotations;
-using Robust.Server.GameObjects;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Chemistry;
 using Content.Shared.Chemistry.Components;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Database;
 using Content.Shared.FixedPoint;
 using Content.Shared.Interaction;
 using Content.Shared.Popups;
+using Content.Shared.Verbs;
+using JetBrains.Annotations;
+using Robust.Server.GameObjects;
 using Robust.Shared.Player;
 
 namespace Content.Server.Chemistry.EntitySystems
@@ -24,7 +24,7 @@ namespace Content.Server.Chemistry.EntitySystems
         /// <summary>
         ///     Default transfer amounts for the set-transfer verb.
         /// </summary>
-        public static readonly List<int> DefaultTransferAmounts = new() { 1, 5, 10, 25, 50, 100, 250, 500, 1000};
+        public static readonly List<int> DefaultTransferAmounts = new() { 1, 5, 10, 25, 50, 100, 250, 500, 1000 };
 
         public override void Initialize()
         {
@@ -35,17 +35,19 @@ namespace Content.Server.Chemistry.EntitySystems
             SubscribeLocalEvent<SolutionTransferComponent, TransferAmountSetValueMessage>(OnTransferAmountSetValueMessage);
         }
 
-        private void OnTransferAmountSetValueMessage(EntityUid uid, SolutionTransferComponent solutionTransfer, TransferAmountSetValueMessage message)
+        private void OnTransferAmountSetValueMessage(Entity<SolutionTransferComponent> entity, ref TransferAmountSetValueMessage message)
         {
-            var newTransferAmount = FixedPoint2.Clamp(message.Value, solutionTransfer.MinimumTransferAmount, solutionTransfer.MaximumTransferAmount);
-            solutionTransfer.TransferAmount = newTransferAmount;
+            var newTransferAmount = FixedPoint2.Clamp(message.Value, entity.Comp.MinimumTransferAmount, entity.Comp.MaximumTransferAmount);
+            entity.Comp.TransferAmount = newTransferAmount;
 
-            if (message.Session.AttachedEntity is {Valid: true} user)
-                _popupSystem.PopupEntity(Loc.GetString("comp-solution-transfer-set-amount", ("amount", newTransferAmount)), uid, user);
+            if (message.Session.AttachedEntity is { Valid: true } user)
+                _popupSystem.PopupEntity(Loc.GetString("comp-solution-transfer-set-amount", ("amount", newTransferAmount)), entity.Owner, user);
         }
 
-        private void AddSetTransferVerbs(EntityUid uid, SolutionTransferComponent component, GetVerbsEvent<AlternativeVerb> args)
+        private void AddSetTransferVerbs(Entity<SolutionTransferComponent> entity, ref GetVerbsEvent<AlternativeVerb> args)
         {
+            var (uid, component) = entity;
+
             if (!args.CanAccess || !args.CanInteract || !component.CanChangeTransferAmount || args.Hands == null)
                 return;
 
@@ -56,15 +58,16 @@ namespace Content.Server.Chemistry.EntitySystems
             AlternativeVerb custom = new();
             custom.Text = Loc.GetString("comp-solution-transfer-verb-custom-amount");
             custom.Category = VerbCategory.SetTransferAmount;
-            custom.Act = () => _userInterfaceSystem.TryOpen(args.Target, TransferAmountUiKey.Key, actor.PlayerSession);
+            custom.Act = () => _userInterfaceSystem.TryOpen(uid, TransferAmountUiKey.Key, actor.PlayerSession);
             custom.Priority = 1;
             args.Verbs.Add(custom);
 
             // Add specific transfer verbs according to the container's size
             var priority = 0;
+            var user = args.User;
             foreach (var amount in DefaultTransferAmounts)
             {
-                if ( amount < component.MinimumTransferAmount.Int() || amount > component.MaximumTransferAmount.Int())
+                if (amount < component.MinimumTransferAmount.Int() || amount > component.MaximumTransferAmount.Int())
                     continue;
 
                 AlternativeVerb verb = new();
@@ -73,7 +76,7 @@ namespace Content.Server.Chemistry.EntitySystems
                 verb.Act = () =>
                 {
                     component.TransferAmount = FixedPoint2.New(amount);
-                    _popupSystem.PopupEntity(Loc.GetString("comp-solution-transfer-set-amount", ("amount", amount)), uid, args.User);
+                    _popupSystem.PopupEntity(Loc.GetString("comp-solution-transfer-set-amount", ("amount", amount)), uid, user);
                 };
 
                 // we want to sort by size, not alphabetically by the verb text.
@@ -84,18 +87,19 @@ namespace Content.Server.Chemistry.EntitySystems
             }
         }
 
-        private void OnAfterInteract(EntityUid uid, SolutionTransferComponent component, AfterInteractEvent args)
+        private void OnAfterInteract(Entity<SolutionTransferComponent> entity, ref AfterInteractEvent args)
         {
             if (!args.CanReach || args.Target == null)
                 return;
 
             var target = args.Target!.Value;
+            var (uid, component) = entity;
 
             //Special case for reagent tanks, because normally clicking another container will give solution, not take it.
-            if (component.CanReceive  && !EntityManager.HasComponent<RefillableSolutionComponent>(target) // target must not be refillable (e.g. Reagent Tanks)
-                                      && _solutionContainerSystem.TryGetDrainableSolution(target, out var targetDrain) // target must be drainable
-                                      && EntityManager.TryGetComponent(uid, out RefillableSolutionComponent? refillComp)
-                                      && _solutionContainerSystem.TryGetRefillableSolution(uid, out var ownerRefill, refillable: refillComp))
+            if (component.CanReceive && !EntityManager.HasComponent<RefillableSolutionComponent>(target) // target must not be refillable (e.g. Reagent Tanks)
+                                     && _solutionContainerSystem.TryGetDrainableSolution(target, out var targetSoln, out _) // target must be drainable
+                                     && EntityManager.TryGetComponent(uid, out RefillableSolutionComponent? refillComp)
+                                     && _solutionContainerSystem.TryGetRefillableSolution((uid, refillComp, null), out var ownerSoln, out var ownerRefill))
 
             {
 
@@ -106,7 +110,7 @@ namespace Content.Server.Chemistry.EntitySystems
                     transferAmount = FixedPoint2.Min(transferAmount, (FixedPoint2) refill.MaxRefill); // if the receiver has a smaller transfer limit, use that instead
                 }
 
-                var transferred = Transfer(args.User, target, targetDrain, uid, ownerRefill, transferAmount);
+                var transferred = Transfer(args.User, target, targetSoln.Value, uid, ownerSoln.Value, transferAmount);
                 if (transferred > 0)
                 {
                     var toTheBrim = ownerRefill.AvailableVolume == 0;
@@ -122,8 +126,8 @@ namespace Content.Server.Chemistry.EntitySystems
             }
 
             // if target is refillable, and owner is drainable
-            if (component.CanSend && _solutionContainerSystem.TryGetRefillableSolution(target, out var targetRefill)
-                                  && _solutionContainerSystem.TryGetDrainableSolution(uid, out var ownerDrain))
+            if (component.CanSend && _solutionContainerSystem.TryGetRefillableSolution(target, out targetSoln, out var targetRefill)
+                                  && _solutionContainerSystem.TryGetDrainableSolution(uid, out ownerSoln, out var ownerDrain))
             {
                 var transferAmount = component.TransferAmount;
 
@@ -132,7 +136,7 @@ namespace Content.Server.Chemistry.EntitySystems
                     transferAmount = FixedPoint2.Min(transferAmount, (FixedPoint2) refill.MaxRefill);
                 }
 
-                var transferred = Transfer(args.User, uid, ownerDrain, target, targetRefill, transferAmount);
+                var transferred = Transfer(args.User, uid, ownerSoln.Value, target, targetSoln.Value, transferAmount);
 
                 if (transferred > 0)
                 {
@@ -150,9 +154,9 @@ namespace Content.Server.Chemistry.EntitySystems
         /// <returns>The actual amount transferred.</returns>
         public FixedPoint2 Transfer(EntityUid user,
             EntityUid sourceEntity,
-            Solution source,
+            Entity<SolutionComponent> source,
             EntityUid targetEntity,
-            Solution target,
+            Entity<SolutionComponent> target,
             FixedPoint2 amount)
         {
             var transferAttempt = new SolutionTransferAttemptEvent(sourceEntity, targetEntity);
@@ -165,7 +169,8 @@ namespace Content.Server.Chemistry.EntitySystems
                 return FixedPoint2.Zero;
             }
 
-            if (source.Volume == 0)
+            var sourceSolution = source.Comp.Solution;
+            if (sourceSolution.Volume == 0)
             {
                 _popupSystem.PopupEntity(Loc.GetString("comp-solution-transfer-is-empty", ("target", sourceEntity)), sourceEntity, user);
                 return FixedPoint2.Zero;
@@ -179,19 +184,20 @@ namespace Content.Server.Chemistry.EntitySystems
                 return FixedPoint2.Zero;
             }
 
-            if (target.AvailableVolume == 0)
+            var targetSolution = target.Comp.Solution;
+            if (targetSolution.AvailableVolume == 0)
             {
                 _popupSystem.PopupEntity(Loc.GetString("comp-solution-transfer-is-full", ("target", targetEntity)), targetEntity, user);
                 return FixedPoint2.Zero;
             }
 
-            var actualAmount = FixedPoint2.Min(amount, FixedPoint2.Min(source.Volume, target.AvailableVolume));
+            var actualAmount = FixedPoint2.Min(amount, FixedPoint2.Min(sourceSolution.Volume, targetSolution.AvailableVolume));
 
             var solution = _solutionContainerSystem.Drain(sourceEntity, source, actualAmount);
             _solutionContainerSystem.Refill(targetEntity, target, solution);
 
             _adminLogger.Add(LogType.Action, LogImpact.Medium,
-                $"{EntityManager.ToPrettyString(user):player} transferred {string.Join(", ", solution.Contents)} to {EntityManager.ToPrettyString(targetEntity):entity}, which now contains {string.Join(", ", target.Contents)}");
+                $"{EntityManager.ToPrettyString(user):player} transferred {string.Join(", ", solution.Contents)} to {EntityManager.ToPrettyString(targetEntity):entity}, which now contains {SolutionContainerSystem.ToPrettyString(targetSolution)}");
 
             return actualAmount;
         }
index ac1cc1edd5f82ba7e7f7136b7fc11d0c0d40514e..94a3fe2186168adea915fa25b23922465b163e74 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Server.Chemistry.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Chemistry.Reagent;
 using Robust.Shared.Prototypes;
@@ -16,38 +17,38 @@ public sealed class TransformableContainerSystem : EntitySystem
         base.Initialize();
 
         SubscribeLocalEvent<TransformableContainerComponent, MapInitEvent>(OnMapInit);
-        SubscribeLocalEvent<TransformableContainerComponent, SolutionChangedEvent>(OnSolutionChange);
+        SubscribeLocalEvent<TransformableContainerComponent, SolutionContainerChangedEvent>(OnSolutionChange);
     }
 
-    private void OnMapInit(EntityUid uid, TransformableContainerComponent component, MapInitEvent args)
+    private void OnMapInit(Entity<TransformableContainerComponent> entity, ref MapInitEvent args)
     {
-        var meta = MetaData(uid);
-        if (string.IsNullOrEmpty(component.InitialName))
+        var meta = MetaData(entity.Owner);
+        if (string.IsNullOrEmpty(entity.Comp.InitialName))
         {
-            component.InitialName = meta.EntityName;
+            entity.Comp.InitialName = meta.EntityName;
         }
-        if (string.IsNullOrEmpty(component.InitialDescription))
+        if (string.IsNullOrEmpty(entity.Comp.InitialDescription))
         {
-            component.InitialDescription = meta.EntityDescription;
+            entity.Comp.InitialDescription = meta.EntityDescription;
         }
     }
 
-    private void OnSolutionChange(EntityUid owner, TransformableContainerComponent component,
-        SolutionChangedEvent args)
+    private void OnSolutionChange(Entity<TransformableContainerComponent> entity, ref SolutionContainerChangedEvent args)
     {
-        if (!_solutionsSystem.TryGetFitsInDispenser(owner, out var solution))
+        if (!_solutionsSystem.TryGetFitsInDispenser(entity.Owner, out _, out var solution))
             return;
+
         //Transform container into initial state when emptied
-        if (component.CurrentReagent != null && solution.Contents.Count == 0)
+        if (entity.Comp.CurrentReagent != null && solution.Contents.Count == 0)
         {
-            CancelTransformation(owner, component);
+            CancelTransformation(entity);
         }
 
         //the biggest reagent in the solution decides the appearance
         var reagentId = solution.GetPrimaryReagentId();
 
         //If biggest reagent didn't changed - don't change anything at all
-        if (component.CurrentReagent != null && component.CurrentReagent.ID == reagentId?.Prototype)
+        if (entity.Comp.CurrentReagent != null && entity.Comp.CurrentReagent.ID == reagentId?.Prototype)
         {
             return;
         }
@@ -56,29 +57,29 @@ public sealed class TransformableContainerSystem : EntitySystem
         if (!string.IsNullOrWhiteSpace(reagentId?.Prototype)
             && _prototypeManager.TryIndex(reagentId.Value.Prototype, out ReagentPrototype? proto))
         {
-            var metadata = MetaData(owner);
+            var metadata = MetaData(entity.Owner);
             var val = Loc.GetString("transformable-container-component-glass", ("name", proto.LocalizedName));
-            _metadataSystem.SetEntityName(owner, val, metadata);
-            _metadataSystem.SetEntityDescription(owner, proto.LocalizedDescription, metadata);
-            component.CurrentReagent = proto;
-            component.Transformed = true;
+            _metadataSystem.SetEntityName(entity.Owner, val, metadata);
+            _metadataSystem.SetEntityDescription(entity.Owner, proto.LocalizedDescription, metadata);
+            entity.Comp.CurrentReagent = proto;
+            entity.Comp.Transformed = true;
         }
     }
 
-    private void CancelTransformation(EntityUid owner, TransformableContainerComponent component)
+    private void CancelTransformation(Entity<TransformableContainerComponent> entity)
     {
-        component.CurrentReagent = null;
-        component.Transformed = false;
+        entity.Comp.CurrentReagent = null;
+        entity.Comp.Transformed = false;
 
-        var metadata = MetaData(owner);
+        var metadata = MetaData(entity);
 
-        if (!string.IsNullOrEmpty(component.InitialName))
+        if (!string.IsNullOrEmpty(entity.Comp.InitialName))
         {
-            _metadataSystem.SetEntityName(owner, component.InitialName, metadata);
+            _metadataSystem.SetEntityName(entity.Owner, entity.Comp.InitialName, metadata);
         }
-        if (!string.IsNullOrEmpty(component.InitialDescription))
+        if (!string.IsNullOrEmpty(entity.Comp.InitialDescription))
         {
-            _metadataSystem.SetEntityDescription(owner, component.InitialDescription, metadata);
+            _metadataSystem.SetEntityDescription(entity.Owner, entity.Comp.InitialDescription, metadata);
         }
     }
 }
index 02788d5a47d38f9659a8bc4aad808d4397fed2ee..2c23b8f0390074f37df0834abac813caad0c35a3 100644 (file)
@@ -1,9 +1,8 @@
-using System.Numerics;
 using Content.Server.Chemistry.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Chemistry;
 using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Components.SolutionManager;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.FixedPoint;
 using Content.Shared.Physics;
@@ -16,6 +15,7 @@ using Robust.Shared.Physics.Events;
 using Robust.Shared.Physics.Systems;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Spawners;
+using System.Numerics;
 
 namespace Content.Server.Chemistry.EntitySystems
 {
@@ -40,19 +40,20 @@ namespace Content.Server.Chemistry.EntitySystems
             SubscribeLocalEvent<VaporComponent, StartCollideEvent>(HandleCollide);
         }
 
-        private void HandleCollide(EntityUid uid, VaporComponent component, ref StartCollideEvent args)
+        private void HandleCollide(Entity<VaporComponent> entity, ref StartCollideEvent args)
         {
-            if (!EntityManager.TryGetComponent(uid, out SolutionContainerManagerComponent? contents)) return;
+            if (!EntityManager.TryGetComponent(entity.Owner, out SolutionContainerManagerComponent? contents)) return;
 
-            foreach (var value in contents.Solutions.Values)
+            foreach (var (_, soln) in _solutionContainerSystem.EnumerateSolutions((entity.Owner, contents)))
             {
-                _reactive.DoEntityReaction(args.OtherEntity, value, ReactionMethod.Touch);
+                var solution = soln.Comp.Solution;
+                _reactive.DoEntityReaction(args.OtherEntity, solution, ReactionMethod.Touch);
             }
 
             // Check for collision with a impassable object (e.g. wall) and stop
             if ((args.OtherFixture.CollisionLayer & (int) CollisionGroup.Impassable) != 0 && args.OtherFixture.Hard)
             {
-                EntityManager.QueueDeleteEntity(uid);
+                EntityManager.QueueDeleteEntity(entity);
             }
         }
 
@@ -83,28 +84,27 @@ namespace Content.Server.Chemistry.EntitySystems
                 return false;
             }
 
-            if (!_solutionContainerSystem.TryGetSolution(vapor, VaporComponent.SolutionName,
-                out var vaporSolution))
+            if (!_solutionContainerSystem.TryGetSolution(vapor.Owner, VaporComponent.SolutionName, out var vaporSolution))
             {
                 return false;
             }
 
-            return _solutionContainerSystem.TryAddSolution(vapor, vaporSolution, solution);
+            return _solutionContainerSystem.TryAddSolution(vaporSolution.Value, solution);
         }
 
         public override void Update(float frameTime)
         {
             var query = EntityQueryEnumerator<VaporComponent, SolutionContainerManagerComponent, TransformComponent>();
-            while (query.MoveNext(out var uid, out var vaporComp, out var solution, out var xform))
+            while (query.MoveNext(out var uid, out var vaporComp, out var container, out var xform))
             {
-                foreach (var (_, value) in solution.Solutions)
+                foreach (var (_, soln) in _solutionContainerSystem.EnumerateSolutions((uid, container)))
                 {
-                    Update(frameTime, (uid, vaporComp), value, xform);
+                    Update(frameTime, (uid, vaporComp), soln, xform);
                 }
             }
         }
 
-        private void Update(float frameTime, Entity<VaporComponent> ent, Solution contents, TransformComponent xform)
+        private void Update(float frameTime, Entity<VaporComponent> ent, Entity<SolutionComponent> soln, TransformComponent xform)
         {
             var (entity, vapor) = ent;
             if (!vapor.Active)
@@ -112,6 +112,7 @@ namespace Content.Server.Chemistry.EntitySystems
 
             vapor.ReactTimer += frameTime;
 
+            var contents = soln.Comp.Solution;
             if (vapor.ReactTimer >= ReactTime && TryComp(xform.GridUid, out MapGridComponent? gridComp))
             {
                 vapor.ReactTimer = 0;
@@ -131,7 +132,7 @@ namespace Content.Server.Chemistry.EntitySystems
                         reaction = reagentQuantity.Quantity;
                     }
 
-                    _solutionContainerSystem.RemoveReagent(entity, contents, reagentQuantity.Reagent, reaction);
+                    _solutionContainerSystem.RemoveReagent(soln, reagentQuantity.Reagent, reaction);
                 }
             }
 
index fc807069f78ebbf3792e672b5f08cfd3b802bf63..024558f8de34959f4dd0f4b67594cff8c047aa13 100644 (file)
@@ -1,6 +1,5 @@
 using Content.Server.Fluids.EntitySystems;
 using Content.Shared.Audio;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.Coordinates.Helpers;
 using Content.Shared.Database;
@@ -30,7 +29,7 @@ namespace Content.Server.Chemistry.ReactionEffects
         /// <summary>
         /// How many units of reaction for 1 smoke entity.
         /// </summary>
-        [DataField("overflowThreshold")] public FixedPoint2 OverflowThreshold = FixedPoint2.New(2.5);
+        [DataField] public FixedPoint2 OverflowThreshold = FixedPoint2.New(2.5);
 
         /// <summary>
         /// The entity prototype that will be spawned as the effect.
@@ -56,7 +55,7 @@ namespace Content.Server.Chemistry.ReactionEffects
                 return;
 
             var spreadAmount = (int) Math.Max(0, Math.Ceiling((args.Quantity / OverflowThreshold).Float()));
-            var splitSolution = args.EntityManager.System<SolutionContainerSystem>().SplitSolution(args.SolutionEntity, args.Source, args.Source.Volume);
+            var splitSolution = args.Source.SplitSolution(args.Source.Volume);
             var transform = args.EntityManager.GetComponent<TransformComponent>(args.SolutionEntity);
             var mapManager = IoCManager.Resolve<IMapManager>();
 
index 53c4f676a83e5b975d8974fcaa1729eded067fcf..f8c0378452bf1b450be7dc6264e321dab7638014 100644 (file)
@@ -11,13 +11,13 @@ public sealed partial class CreateEntityReactionEffect : ReagentEffect
     /// <summary>
     ///     What entity to create.
     /// </summary>
-    [DataField("entity", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
+    [DataField(required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
     public string Entity = default!;
 
     /// <summary>
     ///     How many entities to create per unit reaction.
     /// </summary>
-    [DataField("number")]
+    [DataField]
     public uint Number = 1;
 
     protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
index 2efdff93af2eead4b0f0d753b21fa3cb1e9d36d1..b6714ca28d0f4c6265c3bf8e058c744a9348a8a3 100644 (file)
@@ -23,7 +23,7 @@ public sealed partial class EmpReactionEffect : ReagentEffect
     /// <summary>
     ///     How much energy will be drain from sources
     /// </summary>
-    [DataField("energyConsumption")]
+    [DataField]
     public float EnergyConsumption = 12500;
 
     /// <summary>
index 41a389ee2f138d50bcaf124cf7afeed5280e52de..8a558a3ffdc787385a5bdbbd567653ed3fb01c85 100644 (file)
@@ -1,10 +1,10 @@
-using System.Text.Json.Serialization;
 using Content.Server.Explosion.EntitySystems;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.Database;
 using Content.Shared.Explosion;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+using System.Text.Json.Serialization;
 
 namespace Content.Server.Chemistry.ReactionEffects
 {
@@ -14,7 +14,7 @@ namespace Content.Server.Chemistry.ReactionEffects
         /// <summary>
         ///     The type of explosion. Determines damage types and tile break chance scaling.
         /// </summary>
-        [DataField("explosionType", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<ExplosionPrototype>))]
+        [DataField(required: true, customTypeSerializer: typeof(PrototypeIdSerializer<ExplosionPrototype>))]
         [JsonIgnore]
         public string ExplosionType = default!;
 
@@ -22,14 +22,14 @@ namespace Content.Server.Chemistry.ReactionEffects
         ///     The max intensity the explosion can have at a given tile. Places an upper limit of damage and tile break
         ///     chance.
         /// </summary>
-        [DataField("maxIntensity")]
+        [DataField]
         [JsonIgnore]
         public float MaxIntensity = 5;
 
         /// <summary>
         ///     How quickly intensity drops off as you move away from the epicenter
         /// </summary>
-        [DataField("intensitySlope")]
+        [DataField]
         [JsonIgnore]
         public float IntensitySlope = 1;
 
@@ -40,14 +40,14 @@ namespace Content.Server.Chemistry.ReactionEffects
         /// <remarks>
         ///     A slope of 1 and MaxTotalIntensity of 100 corresponds to a radius of around 4.5 tiles.
         /// </remarks>
-        [DataField("maxTotalIntensity")]
+        [DataField]
         [JsonIgnore]
         public float MaxTotalIntensity = 100;
 
         /// <summary>
         ///     The intensity of the explosion per unit reaction.
         /// </summary>
-        [DataField("intensityPerUnit")]
+        [DataField]
         [JsonIgnore]
         public float IntensityPerUnit = 1;
 
diff --git a/Content.Server/Chemistry/ReactionEffects/SmokeAreaReactionEffect.cs b/Content.Server/Chemistry/ReactionEffects/SmokeAreaReactionEffect.cs
deleted file mode 100644 (file)
index 5f28270..0000000
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
index 6117132831c8b82a32342b496d9fc7cacd48aaa3..ec58754883d6c6a9b688398a47823998e680432b 100644 (file)
@@ -1,7 +1,5 @@
-using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Reagent;
 using Robust.Shared.Prototypes;
-using static Robust.Shared.Physics.DynamicTree;
 
 namespace Content.Server.Chemistry.ReactionEffects
 {
@@ -121,5 +119,3 @@ namespace Content.Server.Chemistry.ReactionEffects
     }
 
 }
-
-
index fbb1894813171804a922d52df0cc833c276409f6..9c47bb58ab8b08f7a1979eb06a3edfd7231d4af5 100644 (file)
@@ -10,10 +10,10 @@ namespace Content.Server.Chemistry.ReagentEffectConditions
     /// </summary>
     public sealed partial class Temperature : ReagentEffectCondition
     {
-        [DataField("min")]
+        [DataField]
         public float Min = 0;
 
-        [DataField("max")]
+        [DataField]
         public float Max = float.PositiveInfinity;
         public override bool Condition(ReagentEffectArgs args)
         {
index e2f4ca0968d5e87142369436c002c74fa967f1f1..52d4d00eb3f9bb59624f3d6311631ad636709725 100644 (file)
@@ -9,10 +9,10 @@ namespace Content.Server.Chemistry.ReagentEffectConditions;
 [UsedImplicitly]
 public sealed partial class HasTag : ReagentEffectCondition
 {
-    [DataField("tag", customTypeSerializer: typeof(PrototypeIdSerializer<TagPrototype>))]
+    [DataField(customTypeSerializer: typeof(PrototypeIdSerializer<TagPrototype>))]
     public string Tag = default!;
 
-    [DataField("invert")]
+    [DataField]
     public bool Invert = false;
 
     public override bool Condition(ReagentEffectArgs args)
index 4585cc6d8a03153864569c0c6582cabe2cf5add6..377eb7d9065640f682f8e70ab3b616350f28a02d 100644 (file)
@@ -7,16 +7,14 @@ namespace Content.Server.Chemistry.ReagentEffectConditions
 {
     public sealed partial class MobStateCondition : ReagentEffectCondition
     {
-
-
-        [DataField("mobstate")]
-        public MobState mobstate = MobState.Alive;
+        [DataField]
+        public MobState Mobstate = MobState.Alive;
 
         public override bool Condition(ReagentEffectArgs args)
         {
             if (args.EntityManager.TryGetComponent(args.SolutionEntity, out MobStateComponent? mobState))
             {
-                if (mobState.CurrentState == mobstate)
+                if (mobState.CurrentState == Mobstate)
                     return true;
             }
 
@@ -25,7 +23,7 @@ namespace Content.Server.Chemistry.ReagentEffectConditions
 
         public override string GuidebookExplanation(IPrototypeManager prototype)
         {
-            return Loc.GetString("reagent-effect-condition-guidebook-mob-state-condition", ("state", mobstate));
+            return Loc.GetString("reagent-effect-condition-guidebook-mob-state-condition", ("state", Mobstate));
         }
     }
 }
index a8e9e3077ba604327ccbb2c6a3cf9c8942320b0f..3b7bffb9cff8a057398c2316c17b649916981e34 100644 (file)
@@ -11,13 +11,13 @@ namespace Content.Server.Chemistry.ReagentEffectConditions
     /// </summary>
     public sealed partial class OrganType : ReagentEffectCondition
     {
-        [DataField("type", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<MetabolizerTypePrototype>))]
+        [DataField(required: true, customTypeSerializer: typeof(PrototypeIdSerializer<MetabolizerTypePrototype>))]
         public string Type = default!;
 
         /// <summary>
         ///     Does this condition pass when the organ has the type, or when it doesn't have the type?
         /// </summary>
-        [DataField("shouldHave")]
+        [DataField]
         public bool ShouldHave = true;
 
         public override bool Condition(ReagentEffectArgs args)
index 203656be738c5ee6d22dc8c1484e6142a076b1ec..664569d7be4b376961484fc846ca3f9f5e8a971b 100644 (file)
@@ -13,14 +13,14 @@ namespace Content.Server.Chemistry.ReagentEffectConditions
     /// </summary>
     public sealed partial class ReagentThreshold : ReagentEffectCondition
     {
-        [DataField("min")]
+        [DataField]
         public FixedPoint2 Min = FixedPoint2.Zero;
 
-        [DataField("max")]
+        [DataField]
         public FixedPoint2 Max = FixedPoint2.MaxValue;
 
         // TODO use ReagentId
-        [DataField("reagent")]
+        [DataField]
         public string? Reagent;
 
         public override bool Condition(ReagentEffectArgs args)
index 974f2fb81aa5baa0f57c75a8bf0bd7d4d3cb8837..4387f2ba93be8a50d4069f7435b6931b08616d5f 100644 (file)
@@ -9,10 +9,10 @@ namespace Content.Server.Chemistry.ReagentEffectConditions
     /// </summary>
     public sealed partial class SolutionTemperature : ReagentEffectCondition
     {
-        [DataField("min")]
+        [DataField]
         public float Min = 0.0f;
 
-        [DataField("max")]
+        [DataField]
         public float Max = float.PositiveInfinity;
         public override bool Condition(ReagentEffectArgs args)
         {
index 81f6475449d599088cec925ee880483c025ea631..49630ef9ab04c094cd992737659ac85f6190b7de 100644 (file)
@@ -7,10 +7,10 @@ namespace Content.Server.Chemistry.ReagentEffectConditions
 {
     public sealed partial class TotalDamage : ReagentEffectCondition
     {
-        [DataField("max")]
+        [DataField]
         public FixedPoint2 Max = FixedPoint2.MaxValue;
 
-        [DataField("min")]
+        [DataField]
         public FixedPoint2 Min = FixedPoint2.Zero;
 
         public override bool Condition(ReagentEffectArgs args)
index d150854e1eacdf91303c33be5068db9cbd0ab230..6a43739b0ee2dacbc0970af91c24557c07d8dd96 100644 (file)
@@ -1,4 +1,4 @@
-using Content.Shared.Chemistry.EntitySystems;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Chemistry.Reagent;
 using JetBrains.Annotations;
 using Robust.Shared.Prototypes;
@@ -17,12 +17,11 @@ namespace Content.Server.Chemistry.ReagentEffects
                 return;
 
             // TODO see if this is correct
-            if (!EntitySystem.Get<SolutionContainerSystem>()
-                    .TryGetSolution(args.SolutionEntity, _solution, out var solutionContainer))
+            var solutionContainerSystem = args.EntityManager.System<SolutionContainerSystem>();
+            if (!solutionContainerSystem.TryGetSolution(args.SolutionEntity, _solution, out var solutionContainer))
                 return;
 
-            if (EntitySystem.Get<SolutionContainerSystem>()
-                .TryAddReagent(args.SolutionEntity, solutionContainer, args.Reagent.ID, args.Quantity, out var accepted))
+            if (solutionContainerSystem.TryAddReagent(solutionContainer.Value, args.Reagent.ID, args.Quantity, out var accepted))
                 args.Source?.RemoveReagent(args.Reagent.ID, accepted);
         }
 
index ee384a2834b4f986e7edee21de92c3057d259180..cf3d71405bf18da7eefbf72def6d7c7beeb8b02c 100644 (file)
@@ -10,13 +10,13 @@ public sealed partial class AdjustAlert : ReagentEffect
     [DataField("alertType", required: true)]
     public AlertType Type;
 
-    [DataField("clear")]
+    [DataField]
     public bool Clear;
 
-    [DataField("cooldown")]
+    [DataField]
     public bool Cooldown;
 
-    [DataField("time")]
+    [DataField]
     public float Time;
 
     //JUSTIFICATION: This just changes some visuals, doesn't need to be documented.
index 5a216b9753e2a3256b888186f346f230b33adf39..e70626f1d3e940c3382f92c6eab5a1e8eaca4102 100644 (file)
@@ -1,5 +1,4 @@
 using Content.Shared.Body.Prototypes;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.FixedPoint;
 using JetBrains.Annotations;
@@ -14,7 +13,7 @@ namespace Content.Server.Chemistry.ReagentEffects
         /// <summary>
         ///     The reagent ID to remove. Only one of this and <see cref="Group"/> should be active.
         /// </summary>
-        [DataField("reagent", customTypeSerializer:typeof(PrototypeIdSerializer<ReagentPrototype>))]
+        [DataField(customTypeSerializer: typeof(PrototypeIdSerializer<ReagentPrototype>))]
         public string? Reagent = null;
         // TODO use ReagentId
 
@@ -22,41 +21,39 @@ namespace Content.Server.Chemistry.ReagentEffects
         ///     The metabolism group to remove, if the reagent satisfies any.
         ///     Only one of this and <see cref="Reagent"/> should be active.
         /// </summary>
-        [DataField("group", customTypeSerializer:typeof(PrototypeIdSerializer<MetabolismGroupPrototype>))]
+        [DataField(customTypeSerializer: typeof(PrototypeIdSerializer<MetabolismGroupPrototype>))]
         public string? Group = null;
 
-        [DataField("amount", required: true)]
+        [DataField(required: true)]
         public FixedPoint2 Amount = default!;
 
         public override void Effect(ReagentEffectArgs args)
         {
-            if (args.Source != null)
-            {
-                var solutionSys = args.EntityManager.EntitySysManager.GetEntitySystem<SolutionContainerSystem>();
-                var amount = Amount;
+            if (args.Source == null)
+                return;
 
-                amount *= args.Scale;
+            var amount = Amount;
+            amount *= args.Scale;
 
-                if (Reagent != null)
-                {
-                    if (amount < 0 && args.Source.ContainsPrototype(Reagent))
-                        solutionSys.RemoveReagent(args.SolutionEntity, args.Source, Reagent, -amount);
-                    if (amount > 0)
-                        solutionSys.TryAddReagent(args.SolutionEntity, args.Source, Reagent, amount, out _);
-                }
-                else if (Group != null)
+            if (Reagent != null)
+            {
+                if (amount < 0 && args.Source.ContainsPrototype(Reagent))
+                    args.Source.RemoveReagent(Reagent, -amount);
+                if (amount > 0)
+                    args.Source.AddReagent(Reagent, amount);
+            }
+            else if (Group != null)
+            {
+                var prototypeMan = IoCManager.Resolve<IPrototypeManager>();
+                foreach (var quant in args.Source.Contents.ToArray())
                 {
-                    var prototypeMan = IoCManager.Resolve<IPrototypeManager>();
-                    foreach (var quant in args.Source.Contents.ToArray())
+                    var proto = prototypeMan.Index<ReagentPrototype>(quant.Reagent.Prototype);
+                    if (proto.Metabolisms != null && proto.Metabolisms.ContainsKey(Group))
                     {
-                        var proto = prototypeMan.Index<ReagentPrototype>(quant.Reagent.Prototype);
-                        if (proto.Metabolisms != null && proto.Metabolisms.ContainsKey(Group))
-                        {
-                            if (amount < 0)
-                                solutionSys.RemoveReagent(args.SolutionEntity, args.Source, quant.Reagent, amount);
-                            if (amount > 0)
-                                solutionSys.TryAddReagent(args.SolutionEntity, args.Source, quant.Reagent, amount, out _);
-                        }
+                        if (amount < 0)
+                            args.Source.RemoveReagent(quant.Reagent, amount);
+                        if (amount > 0)
+                            args.Source.AddReagent(quant.Reagent, amount);
                     }
                 }
             }
index 66ae5ba1db029e5a86be3026a9d0fc58b188a22a..9b97572067805764798d0f9367c2d5825270f684 100644 (file)
@@ -7,7 +7,7 @@ namespace Content.Server.Chemistry.ReagentEffects
 {
     public sealed partial class AdjustTemperature : ReagentEffect
     {
-        [DataField("amount")]
+        [DataField]
         public float Amount;
 
         protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
index ed3098dbdcd83faceea4ae0fc0a62547a1f8a85e..029b1495002b6263946e78df502f25de57e834dc 100644 (file)
@@ -1,9 +1,7 @@
+using Content.Server.Zombies;
 using Content.Shared.Chemistry.Reagent;
-using Robust.Shared.Prototypes;
-
 using Robust.Shared.Configuration;
-using Content.Server.Zombies;
-
+using Robust.Shared.Prototypes;
 
 namespace Content.Server.Chemistry.ReagentEffects;
 
index f0deb29f87f1dbb95465bd8e8d795e344b7274a0..402b30a069b0acced8004a4e2cbdc2d9f5f1e314 100644 (file)
@@ -1,6 +1,6 @@
+using Content.Server.Body.Systems;
 using Content.Shared.Chemistry.Reagent;
 using JetBrains.Annotations;
-using Content.Server.Body.Systems;
 using Robust.Shared.Prototypes;
 
 namespace Content.Server.Chemistry.ReactionEffects
@@ -11,7 +11,7 @@ namespace Content.Server.Chemistry.ReactionEffects
     [UsedImplicitly]
     public sealed partial class ChemCleanBloodstream : ReagentEffect
     {
-        [DataField("cleanseRate")]
+        [DataField]
         public float CleanseRate = 3.0f;
 
         protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
index 9f074bca52302a39f7c373d9b61622082828c8c2..206b8ed04dccc5c7a158e23cf62863d68cc945b0 100644 (file)
@@ -15,7 +15,7 @@ namespace Content.Server.Chemistry.ReagentEffects
         /// <summary>
         /// How much eye damage to add.
         /// </summary>
-        [DataField("amount")]
+        [DataField]
         public int Amount = -1;
 
         protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
index 630d00124c3f6cd760ac92072816ea89db8ea15d..851c0adf5f25320ad3c6daed5a956738c2796527 100644 (file)
@@ -1,5 +1,5 @@
-using Content.Shared.Chemistry.Reagent;
 using Content.Server.Medical;
+using Content.Shared.Chemistry.Reagent;
 using JetBrains.Annotations;
 using Robust.Shared.Prototypes;
 
@@ -12,10 +12,10 @@ namespace Content.Server.Chemistry.ReagentEffects
     public sealed partial class ChemVomit : ReagentEffect
     {
         /// How many units of thirst to add each time we vomit
-        [DataField("thirstAmount")]
+        [DataField]
         public float ThirstAmount = -8f;
         /// How many units of hunger to add each time we vomit
-        [DataField("hungerAmount")]
+        [DataField]
         public float HungerAmount = -8f;
 
         protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
index ca9ecd67d18987db2e3b35d0136fcd560ae7c221..c1da3c48db32d04e648d330c7c6ee87cd7071375 100644 (file)
@@ -8,13 +8,13 @@ namespace Content.Server.Chemistry.ReagentEffects;
 
 public sealed partial class CreateGas : ReagentEffect
 {
-    [DataField("gas", required: true)]
+    [DataField(required: true)]
     public Gas Gas = default!;
 
     /// <summary>
     ///     For each unit consumed, how many moles of gas should be created?
     /// </summary>
-    [DataField("multiplier")]
+    [DataField]
     public float Multiplier = 3f;
 
     public override bool ShouldLog => true;
index 4e1733e60aa01dabe5c9a528de8e6cf631fdf852..d56fc1153101dde58f1dd0a9604c4079e1a6bdc7 100644 (file)
@@ -1,14 +1,13 @@
+using Content.Server.Zombies;
 using Content.Shared.Chemistry.Reagent;
-using Robust.Shared.Prototypes;
 using Robust.Shared.Configuration;
-using Content.Server.Zombies;
-
+using Robust.Shared.Prototypes;
 
 namespace Content.Server.Chemistry.ReagentEffects;
 
 public sealed partial class CureZombieInfection : ReagentEffect
 {
-    [DataField("innoculate")]
+    [DataField]
     public bool Innoculate;
 
     protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
index 8053c8100fb582341e43521bea1ee1c8b7943d5f..dbce995ca2e6798c0486c109ea2915738f5b5f38 100644 (file)
@@ -9,13 +9,13 @@ public sealed partial class Drunk : ReagentEffect
     /// <summary>
     ///     BoozePower is how long each metabolism cycle will make the drunk effect last for.
     /// </summary>
-    [DataField("boozePower")]
+    [DataField]
     public float BoozePower = 3f;
 
     /// <summary>
     ///     Whether speech should be slurred.
     /// </summary>
-    [DataField("slurSpeech")]
+    [DataField]
     public bool SlurSpeech = true;
 
     protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
index 85819d36eab3f86a74fa7b88c6ac7a89b5691598..272a0fce42a9a30bbb8fe8477cb1cd7ac16efc8f 100644 (file)
@@ -6,14 +6,14 @@ namespace Content.Server.Chemistry.ReagentEffects;
 
 public sealed partial class Electrocute : ReagentEffect
 {
-    [DataField("electrocuteTime")] public int ElectrocuteTime = 2;
+    [DataField] public int ElectrocuteTime = 2;
 
-    [DataField("electrocuteDamageScale")] public int ElectrocuteDamageScale = 5;
+    [DataField] public int ElectrocuteDamageScale = 5;
 
     /// <remarks>
     ///     true - refresh electrocute time,  false - accumulate electrocute time
     /// </remarks>
-    [DataField("refresh")] public bool Refresh = true;
+    [DataField] public bool Refresh = true;
 
     protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
         => Loc.GetString("reagent-effect-guidebook-electrocute", ("chance", Probability), ("time", ElectrocuteTime));
index 45da55ea1aa0fb106002adbf48fc2ffb2a2eeba6..a4d49e4ad1cfe20530eed5a65354a1ceca1c154e 100644 (file)
@@ -16,7 +16,7 @@ public sealed partial class Emote : ReagentEffect
     [DataField("emote", customTypeSerializer: typeof(PrototypeIdSerializer<EmotePrototype>))]
     public string? EmoteId;
 
-    [DataField("showInChat")]
+    [DataField]
     public bool ShowInChat;
 
     // JUSTIFICATION: Emoting is flavor, so same reason popup messages are not in here.
index 26bd4c8b7ea12b69b470fa92baba3bbb98560bed..5b36967f6a8e9ffebb3a6b3fdd842798ec25503a 100644 (file)
@@ -10,7 +10,7 @@ namespace Content.Server.Chemistry.ReagentEffects
     [UsedImplicitly]
     public sealed partial class FlammableReaction : ReagentEffect
     {
-        [DataField("multiplier")]
+        [DataField]
         public float Multiplier = 0.05f;
 
         public override bool ShouldLog => true;
index 6d22db742f45078545d3b8666eefb160114e18c2..a0c976bdc9d5cac886f4bbba7cbe600f68eae6b8 100644 (file)
@@ -1,5 +1,3 @@
-using System.Linq;
-using System.Text.Json.Serialization;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.Damage;
 using Content.Shared.Damage.Prototypes;
@@ -7,6 +5,8 @@ using Content.Shared.FixedPoint;
 using Content.Shared.Localizations;
 using JetBrains.Annotations;
 using Robust.Shared.Prototypes;
+using System.Linq;
+using System.Text.Json.Serialization;
 
 namespace Content.Server.Chemistry.ReagentEffects
 {
@@ -19,19 +19,19 @@ namespace Content.Server.Chemistry.ReagentEffects
         /// <summary>
         /// Damage to apply every metabolism cycle. Damage Ignores resistances.
         /// </summary>
+        [DataField(required: true)]
         [JsonPropertyName("damage")]
-        [DataField("damage", required: true)]
         public DamageSpecifier Damage = default!;
 
         /// <summary>
         ///     Should this effect scale the damage by the amount of chemical in the solution?
         ///     Useful for touch reactions, like styptic powder or acid.
         /// </summary>
+        [DataField]
         [JsonPropertyName("scaleByQuantity")]
-        [DataField("scaleByQuantity")]
         public bool ScaleByQuantity;
 
-        [DataField("ignoreResistances")]
+        [DataField]
         [JsonPropertyName("ignoreResistances")]
         public bool IgnoreResistances = true;
 
index bb990214f1a8ee3c9bb4410a0876fb48ca14ef91..7b966ea4788a79e648969ad65e95bdd966452aff 100644 (file)
@@ -7,10 +7,10 @@ namespace Content.Server.Chemistry.ReagentEffects;
 
 public sealed partial class ModifyBleedAmount : ReagentEffect
 {
-    [DataField("scaled")]
+    [DataField]
     public bool Scaled = false;
 
-    [DataField("amount")]
+    [DataField]
     public float Amount = -1.0f;
 
     protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
index 42fb14809f5f572e489f608338cbaabb8e3167b3..748aa71083ebb12af105fa60ddf0087860583d34 100644 (file)
@@ -8,10 +8,10 @@ namespace Content.Server.Chemistry.ReagentEffects;
 
 public sealed partial class ModifyBloodLevel : ReagentEffect
 {
-    [DataField("scaled")]
+    [DataField]
     public bool Scaled = false;
 
-    [DataField("amount")]
+    [DataField]
     public FixedPoint2 Amount = 1.0f;
 
     protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
index 948726bb0626cb2da107bb7647d9333889342180..0301742c5afb3d5749399d09df5eaccd3137aea2 100644 (file)
@@ -15,19 +15,19 @@ namespace Content.Server.Chemistry.ReagentEffects
         /// <summary>
         /// How much the entities' walk speed is multiplied by.
         /// </summary>
-        [DataField("walkSpeedModifier")]
+        [DataField]
         public float WalkSpeedModifier { get; set; } = 1;
 
         /// <summary>
         /// How much the entities' run speed is multiplied by.
         /// </summary>
-        [DataField("sprintSpeedModifier")]
+        [DataField]
         public float SprintSpeedModifier { get; set; } = 1;
 
         /// <summary>
         /// How long the modifier applies (in seconds) when metabolized.
         /// </summary>
-        [DataField("statusLifetime")]
+        [DataField]
         public float StatusLifetime = 2f;
 
         protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
index 28356bbd3077487865ea9f1d445304d7b500a1c4..34a2454139eec9d2091122d60cf4567f15d6509b 100644 (file)
@@ -7,7 +7,7 @@ namespace Content.Server.Chemistry.ReagentEffects;
 
 public sealed partial class Oxygenate : ReagentEffect
 {
-    [DataField("factor")]
+    [DataField]
     public float Factor = 1f;
 
     // JUSTIFICATION: This is internal magic that players never directly interact with.
index 262d942fa9534e8f3695935d4573110f5baa9029..077d1abf2c2e72bd4c7ee7a4fcbdb539fe69de8f 100644 (file)
@@ -1,17 +1,17 @@
-using Content.Shared.Chemistry.Reagent;
 using Content.Server.Stunnable;
+using Content.Shared.Chemistry.Reagent;
 using Robust.Shared.Prototypes;
 
 namespace Content.Server.Chemistry.ReagentEffects;
 
 public sealed partial class Paralyze : ReagentEffect
 {
-    [DataField("paralyzeTime")] public double ParalyzeTime = 2;
+    [DataField] public double ParalyzeTime = 2;
 
     /// <remarks>
     ///     true - refresh paralyze time,  false - accumulate paralyze time
     /// </remarks>
-    [DataField("refresh")] public bool Refresh = true;
+    [DataField] public bool Refresh = true;
 
     protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
         => Loc.GetString("reagent-effect-guidebook-paralyze",
index a2d35c8264398dc64574c2e9a38cb160e69c6b89..f43b4828f932848fcb9b6a6a09850b080c722563 100644 (file)
@@ -1,16 +1,19 @@
-using System.Diagnostics.CodeAnalysis;
-using Content.Server.Botany.Components;
+using Content.Server.Botany.Components;
 using Content.Shared.Chemistry.Reagent;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Random;
+using System.Diagnostics.CodeAnalysis;
 
 namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
 {
     [ImplicitDataDefinitionForInheritors]
     public abstract partial class PlantAdjustAttribute : ReagentEffect
     {
-        [DataField("amount")] public float Amount { get; protected set; } = 1;
-        [DataField("prob")] public float Prob { get; protected set; } = 1; // = (80);
+        [DataField]
+        public float Amount { get; protected set; } = 1;
+
+        [DataField]
+        public float Prob { get; protected set; } = 1; // = (80);
 
         /// <summary>
         ///     Checks if the plant holder can metabolize the reagent or not. Checks if it has an alive plant by default.
index bcd5a01170e5e4ce550827716062dbb55271c8cd..990a5a5003f39064ca17e38ecff58eef2ce3ba70 100644 (file)
@@ -11,13 +11,13 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
     [DataDefinition]
     public sealed partial class RobustHarvest : ReagentEffect
     {
-        [DataField("potencyLimit")]
+        [DataField]
         public int PotencyLimit = 50;
 
-        [DataField("potencyIncrease")]
+        [DataField]
         public int PotencyIncrease = 3;
 
-        [DataField("potencySeedlessThreshold")]
+        [DataField]
         public int PotencySeedlessThreshold = 30;
 
         public override void Effect(ReagentEffectArgs args)
index 660a539e8ad02826abe026733f0fd24c2bfebd8f..8278e95c1da9f10d640b3dcaf3ab19719eda50f2 100644 (file)
@@ -8,13 +8,13 @@ namespace Content.Server.Chemistry.ReagentEffects
 {
     public sealed partial class PopupMessage : ReagentEffect
     {
-        [DataField("messages", required: true)]
+        [DataField(required: true)]
         public string[] Messages = default!;
 
-        [DataField("type")]
+        [DataField]
         public PopupRecipients Type = PopupRecipients.Local;
 
-        [DataField("visualType")]
+        [DataField]
         public PopupType VisualType = PopupType.Small;
 
         // JUSTIFICATION: This is purely cosmetic.
index 06608df4bbfc1601dcfd3e25fd1fadc8309fc1a2..66454b25fd6bee893bfd44c5bd87f5b2c368133b 100644 (file)
@@ -17,25 +17,25 @@ namespace Content.Server.Chemistry.ReagentEffects.StatusEffects
     [UsedImplicitly]
     public sealed partial class GenericStatusEffect : ReagentEffect
     {
-        [DataField("key", required: true)]
+        [DataField(required: true)]
         public string Key = default!;
 
-        [DataField("component")]
+        [DataField]
         public string Component = String.Empty;
 
-        [DataField("time")]
+        [DataField]
         public float Time = 2.0f;
 
         /// <remarks>
         ///     true - refresh status effect time,  false - accumulate status effect time
         /// </remarks>
-        [DataField("refresh")]
+        [DataField]
         public bool Refresh = true;
 
         /// <summary>
         ///     Should this effect add the status effect, remove time from it, or set its cooldown?
         /// </summary>
-        [DataField("type")]
+        [DataField]
         public StatusEffectMetabolismType Type = StatusEffectMetabolismType.Add;
 
         public override void Effect(ReagentEffectArgs args)
index 7ceb91a0d17242dd55c35c46a77a9884010d6139..7ee70957b7ad3f662db172088bb92dfde7915219 100644 (file)
@@ -11,19 +11,19 @@ namespace Content.Server.Chemistry.ReagentEffects.StatusEffects
     /// </summary>
     public sealed partial class Jitter : ReagentEffect
     {
-        [DataField("amplitude")]
+        [DataField]
         public float Amplitude = 10.0f;
 
-        [DataField("frequency")]
+        [DataField]
         public float Frequency = 4.0f;
 
-        [DataField("time")]
+        [DataField]
         public float Time = 2.0f;
 
         /// <remarks>
         ///     true - refresh jitter time,  false - accumulate jitter time
         /// </remarks>
-        [DataField("refresh")]
+        [DataField]
         public bool Refresh = true;
 
         public override void Effect(ReagentEffectArgs args)
index c23956760a88ad4b42327767c4a927a366d670ca..a20f54728ee49ce21005156052ed4d2dd7725eaf 100644 (file)
@@ -1,4 +1,3 @@
-using System.Numerics;
 using Content.Server.Decals;
 using Content.Shared.Chemistry.Reaction;
 using Content.Shared.Chemistry.Reagent;
@@ -6,6 +5,7 @@ using Content.Shared.Decals;
 using Content.Shared.FixedPoint;
 using Robust.Shared.Map;
 using Robust.Shared.Map.Components;
+using System.Numerics;
 
 namespace Content.Server.Chemistry.TileReactions;
 
@@ -18,7 +18,7 @@ public sealed partial class CleanDecalsReaction : ITileReaction
     /// <summary>
     /// For every cleaned decal we lose this much reagent.
     /// </summary>
-    [DataField("cleanCost")]
+    [DataField]
     public FixedPoint2 CleanCost { get; private set; } = FixedPoint2.New(0.25f);
 
     public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume)
index 4e2f52d3bcc39ad39fdf7a8fcc27285166eafb9a..9d358de07ee9205d681cf9f7964f6db69bd8866c 100644 (file)
@@ -1,12 +1,12 @@
-using System.Linq;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Chemistry.Components;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Chemistry.Reaction;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.FixedPoint;
 using Content.Shared.Fluids.Components;
 using Robust.Shared.Map;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+using System.Linq;
 
 namespace Content.Server.Chemistry.TileReactions;
 
@@ -28,7 +28,7 @@ public sealed partial class CleanTileReaction : ITileReaction
     /// <summary>
     /// What reagent to replace the tile conents with.
     /// </summary>
-    [DataField("reagent", customTypeSerializer:typeof(PrototypeIdSerializer<ReagentPrototype>))]
+    [DataField("reagent", customTypeSerializer: typeof(PrototypeIdSerializer<ReagentPrototype>))]
     public string ReplacementReagent = "Water";
 
     FixedPoint2 ITileReaction.TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume)
@@ -43,17 +43,16 @@ public sealed partial class CleanTileReaction : ITileReaction
         foreach (var entity in entities)
         {
             if (!puddleQuery.TryGetComponent(entity, out var puddle) ||
-                !solutionContainerSystem.TryGetSolution(entity, puddle.SolutionName, out var puddleSolution))
+                !solutionContainerSystem.TryGetSolution(entity, puddle.SolutionName, out var puddleSolution, out _))
             {
                 continue;
             }
 
-            var purgeable =
-                solutionContainerSystem.SplitSolutionWithout(entity, puddleSolution, purgeAmount, ReplacementReagent, reagent.ID);
+            var purgeable = solutionContainerSystem.SplitSolutionWithout(puddleSolution.Value, purgeAmount, ReplacementReagent, reagent.ID);
 
             purgeAmount -= purgeable.Volume;
 
-            solutionContainerSystem.TryAddSolution(entity, puddleSolution, new Solution(ReplacementReagent, purgeable.Volume));
+            solutionContainerSystem.TryAddSolution(puddleSolution.Value, new Solution(ReplacementReagent, purgeable.Volume));
 
             if (purgeable.Volume <= FixedPoint2.Zero)
                 break;
index 3b1494cc2f06b358ad23918f8d98375cff8bec14..9cb1ba201d5b9cefbb3da1943f9baf01a45cc563 100644 (file)
@@ -1,5 +1,4 @@
-using System.Numerics;
-using Content.Shared.Chemistry.Reaction;
+using Content.Shared.Chemistry.Reaction;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.FixedPoint;
 using Content.Shared.Maps;
@@ -8,22 +7,23 @@ using Robust.Shared.Map;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Random;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+using System.Numerics;
 
 namespace Content.Server.Chemistry.TileReactions;
 
 [DataDefinition]
 public sealed partial class CreateEntityTileReaction : ITileReaction
 {
-    [DataField("entity", required: true, customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
+    [DataField(required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
     public string Entity = default!;
 
-    [DataField("usage")]
+    [DataField]
     public FixedPoint2 Usage = FixedPoint2.New(1);
 
     /// <summary>
     ///     How many of the whitelisted entity can fit on one tile?
     /// </summary>
-    [DataField("maxOnTile")]
+    [DataField]
     public int MaxOnTile = 1;
 
     /// <summary>
@@ -32,7 +32,7 @@ public sealed partial class CreateEntityTileReaction : ITileReaction
     [DataField("maxOnTileWhitelist")]
     public EntityWhitelist? Whitelist;
 
-    [DataField("randomOffsetMax")]
+    [DataField]
     public float RandomOffsetMax = 0.0f;
 
     public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume)
index 6156a88c38d804c79c7785dfcf11efd0d876083d..95b0bd731052640cbface544d10fcc8375410f71 100644 (file)
@@ -29,7 +29,6 @@ using Content.Shared.Roles.Jobs;
 using Robust.Server.Containers;
 using Robust.Server.GameObjects;
 using Robust.Server.Player;
-using Robust.Shared.Audio;
 using Robust.Shared.Audio.Systems;
 using Robust.Shared.Configuration;
 using Robust.Shared.Containers;
index d70e84761d6631b4f59e3bf717991437b65cb8bb..f98d639b7b775e19fb1ae4594c8bbc5841d64463 100644 (file)
@@ -1,4 +1,4 @@
-using Content.Shared.Chemistry.EntitySystems;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.Construction;
 using Content.Shared.Examine;
@@ -34,7 +34,7 @@ public sealed partial class MinSolution : IGraphCondition
     public bool Condition(EntityUid uid, IEntityManager entMan)
     {
         var containerSys = entMan.System<SolutionContainerSystem>();
-        if (!containerSys.TryGetSolution(uid, Solution, out var solution))
+        if (!containerSys.TryGetSolution(uid, Solution, out _, out var solution))
             return false;
 
         solution.TryGetReagentQuantity(Reagent, out var quantity);
@@ -47,7 +47,7 @@ public sealed partial class MinSolution : IGraphCondition
         var uid = args.Examined;
 
         var containerSys = entMan.System<SolutionContainerSystem>();
-        if (!containerSys.TryGetSolution(uid, Solution, out var solution))
+        if (!containerSys.TryGetSolution(uid, Solution, out _, out var solution))
             return false;
 
         solution.TryGetReagentQuantity(Reagent, out var quantity);
index d3cbd7356e5a8f5d87aea225b2c58d02c3bb78f0..9235a187ebdf1a16c4dad25880bc38ec9a57bacb 100644 (file)
@@ -1,4 +1,4 @@
-using Content.Shared.Chemistry.EntitySystems;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Construction;
 using Content.Shared.Examine;
 
@@ -19,7 +19,7 @@ public sealed partial class SolutionEmpty : IGraphCondition
     public bool Condition(EntityUid uid, IEntityManager entMan)
     {
         var containerSys = entMan.System<SolutionContainerSystem>();
-        if (!containerSys.TryGetSolution(uid, Solution, out var solution))
+        if (!containerSys.TryGetSolution(uid, Solution, out _, out var solution))
             return false;
 
         return solution.Volume == 0;
@@ -31,7 +31,7 @@ public sealed partial class SolutionEmpty : IGraphCondition
         var uid = args.Examined;
 
         var containerSys = entMan.System<SolutionContainerSystem>();
-        if (!containerSys.TryGetSolution(uid, Solution, out var solution))
+        if (!containerSys.TryGetSolution(uid, Solution, out _, out var solution))
             return false;
 
         // already empty so dont show examine
index 7e43e72007821e317bdba4ce20ca45d3002c85b7..16c54fd3b02af0d39518858647cd65483ea83d1e 100644 (file)
@@ -1,7 +1,7 @@
-using System.Linq;
 using Content.Server.Administration.Logs;
 using Content.Server.Atmos.EntitySystems;
 using Content.Server.Body.Systems;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Construction;
 using Content.Server.Destructible.Thresholds;
 using Content.Server.Destructible.Thresholds.Behaviors;
@@ -9,7 +9,6 @@ using Content.Server.Destructible.Thresholds.Triggers;
 using Content.Server.Explosion.EntitySystems;
 using Content.Server.Fluids.EntitySystems;
 using Content.Server.Stack;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Damage;
 using Content.Shared.Database;
 using Content.Shared.Destructible;
@@ -20,6 +19,7 @@ using Robust.Server.GameObjects;
 using Robust.Shared.Containers;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Random;
+using System.Linq;
 
 namespace Content.Server.Destructible
 {
index 97ea392450c6222b252bf6e3044289e08f5eb727..5166aaccabba4137685b39c963696f4d544d34ac 100644 (file)
@@ -10,12 +10,12 @@ namespace Content.Server.Destructible.Thresholds.Behaviors
     [DataDefinition]
     public sealed partial class SolutionExplosionBehavior : IThresholdBehavior
     {
-        [DataField("solution", required: true)]
+        [DataField(required: true)]
         public string Solution = default!;
 
         public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause = null)
         {
-            if (system.SolutionContainerSystem.TryGetSolution(owner, Solution, out var explodingSolution)
+            if (system.SolutionContainerSystem.TryGetSolution(owner, Solution, out _, out var explodingSolution)
                 && system.EntityManager.TryGetComponent(owner, out ExplosiveComponent? explosiveComponent))
             {
                 // Don't explode if there's no solution
index 66f42857cfbda2fa4b980e5506bad8ee94346cda..9e22510307171336958eea05f28794ed6b06d037 100644 (file)
@@ -1,6 +1,6 @@
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Fluids.Components;
 using Content.Server.Fluids.EntitySystems;
-using Content.Shared.Chemistry.EntitySystems;
 using JetBrains.Annotations;
 
 namespace Content.Server.Destructible.Thresholds.Behaviors
@@ -9,7 +9,7 @@ namespace Content.Server.Destructible.Thresholds.Behaviors
     [DataDefinition]
     public sealed partial class SpillBehavior : IThresholdBehavior
     {
-        [DataField("solution")]
+        [DataField]
         public string? Solution;
 
         /// <summary>
@@ -28,13 +28,12 @@ namespace Content.Server.Destructible.Thresholds.Behaviors
             var coordinates = system.EntityManager.GetComponent<TransformComponent>(owner).Coordinates;
 
             if (system.EntityManager.TryGetComponent(owner, out SpillableComponent? spillableComponent) &&
-                solutionContainerSystem.TryGetSolution(owner, spillableComponent.SolutionName,
-                    out var compSolution))
+                solutionContainerSystem.TryGetSolution(owner, spillableComponent.SolutionName, out _, out var compSolution))
             {
                 spillableSystem.TrySplashSpillAt(owner, coordinates, compSolution, out _, false, user: cause);
             }
             else if (Solution != null &&
-                     solutionContainerSystem.TryGetSolution(owner, Solution, out var behaviorSolution))
+                     solutionContainerSystem.TryGetSolution(owner, Solution, out _, out var behaviorSolution))
             {
                 spillableSystem.TrySplashSpillAt(owner, coordinates, behaviorSolution, out _, user: cause);
             }
index 323dae493aa748e429a5db7cf2da4f7c47f7da10..5bb60abdc98b4c90d68b902851e260b22ca3b4c2 100644 (file)
@@ -1,30 +1,30 @@
-using System.Linq;
 using Content.Server.Administration.Logs;
 using Content.Server.Body.Systems;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Explosion.Components;
 using Content.Server.Flash;
 using Content.Server.Flash.Components;
 using Content.Server.Radio.EntitySystems;
+using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Components.SolutionManager;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Database;
 using Content.Shared.Implants.Components;
 using Content.Shared.Interaction;
+using Content.Shared.Mobs;
+using Content.Shared.Mobs.Components;
 using Content.Shared.Payload.Components;
-using Robust.Shared.Prototypes;
 using Content.Shared.Radio;
 using Content.Shared.Slippery;
 using Content.Shared.StepTrigger.Systems;
 using Content.Shared.Trigger;
+using Content.Shared.Weapons.Ranged.Events;
 using JetBrains.Annotations;
 using Robust.Shared.Audio;
+using Robust.Shared.Audio.Systems;
 using Robust.Shared.Containers;
 using Robust.Shared.Physics.Events;
 using Robust.Shared.Physics.Systems;
-using Content.Shared.Mobs;
-using Content.Shared.Mobs.Components;
-using Content.Shared.Weapons.Ranged.Events;
-using Robust.Shared.Audio.Systems;
+using Robust.Shared.Prototypes;
 using Robust.Shared.Random;
 
 namespace Content.Server.Explosion.EntitySystems
@@ -65,6 +65,7 @@ namespace Content.Server.Explosion.EntitySystems
         [Dependency] private readonly RadioSystem _radioSystem = default!;
         [Dependency] private readonly IRobustRandom _random = default!;
         [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+        [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
 
         public override void Initialize()
         {
@@ -233,7 +234,7 @@ namespace Content.Server.Explosion.EntitySystems
             comp.TimeRemaining += amount;
         }
 
-        public void HandleTimerTrigger(EntityUid uid, EntityUid? user, float delay , float beepInterval, float? initialBeepDelay, SoundSpecifier? beepSound)
+        public void HandleTimerTrigger(EntityUid uid, EntityUid? user, float delay, float beepInterval, float? initialBeepDelay, SoundSpecifier? beepSound)
         {
             if (delay <= 0)
             {
@@ -253,12 +254,18 @@ namespace Content.Server.Explosion.EntitySystems
                     TryComp(container.ContainedEntities[0], out ChemicalPayloadComponent? chemicalPayloadComponent))
                 {
                     // If a beaker is missing, the entity won't explode, so no reason to log it
-                    if (!TryComp(chemicalPayloadComponent?.BeakerSlotA.Item, out SolutionContainerManagerComponent? beakerA) ||
-                        !TryComp(chemicalPayloadComponent?.BeakerSlotB.Item, out SolutionContainerManagerComponent? beakerB))
+                    if (chemicalPayloadComponent?.BeakerSlotA.Item is not { } beakerA ||
+                        chemicalPayloadComponent?.BeakerSlotB.Item is not { } beakerB ||
+                        !TryComp(beakerA, out SolutionContainerManagerComponent? containerA) ||
+                        !TryComp(beakerB, out SolutionContainerManagerComponent? containerB) ||
+                        !TryComp(beakerA, out FitsInDispenserComponent? fitsA) ||
+                        !TryComp(beakerB, out FitsInDispenserComponent? fitsB) ||
+                        !_solutionContainerSystem.TryGetSolution((beakerA, containerA), fitsA.Solution, out _, out var solutionA) ||
+                        !_solutionContainerSystem.TryGetSolution((beakerB, containerB), fitsB.Solution, out _, out var solutionB))
                         return;
 
                     _adminLogger.Add(LogType.Trigger,
-                        $"{ToPrettyString(user.Value):user} started a {delay} second timer trigger on entity {ToPrettyString(uid):timer}, which contains [{string.Join(", ", beakerA.Solutions.Values.First())}] in one beaker and [{string.Join(", ", beakerB.Solutions.Values.First())}] in the other.");
+                        $"{ToPrettyString(user.Value):user} started a {delay} second timer trigger on entity {ToPrettyString(uid):timer}, which contains {SolutionContainerSystem.ToPrettyString(solutionA)} in one beaker and {SolutionContainerSystem.ToPrettyString(solutionB)} in the other.");
                 }
                 else
                 {
index 30895deb4c3c2df2cb2e648213897122fcae2acd..5adf067d9b5ff12773147994f32c2b19586d2ce1 100644 (file)
@@ -1,8 +1,7 @@
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Fluids.EntitySystems;
 using Content.Server.Popups;
-using Content.Shared.Audio;
 using Content.Shared.Chemistry.Components;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Extinguisher;
 using Content.Shared.FixedPoint;
 using Content.Shared.Interaction;
@@ -10,7 +9,6 @@ using Content.Shared.Interaction.Events;
 using Content.Shared.Verbs;
 using Robust.Shared.Audio;
 using Robust.Shared.Audio.Systems;
-using Robust.Shared.Player;
 
 namespace Content.Server.Extinguisher;
 
@@ -32,25 +30,25 @@ public sealed class FireExtinguisherSystem : EntitySystem
         SubscribeLocalEvent<FireExtinguisherComponent, SprayAttemptEvent>(OnSprayAttempt);
     }
 
-    private void OnFireExtinguisherInit(EntityUid uid, FireExtinguisherComponent component, ComponentInit args)
+    private void OnFireExtinguisherInit(Entity<FireExtinguisherComponent> entity, ref ComponentInit args)
     {
-        if (component.HasSafety)
+        if (entity.Comp.HasSafety)
         {
-            UpdateAppearance(uid, component);
+            UpdateAppearance((entity.Owner, entity.Comp));
         }
     }
 
-    private void OnUseInHand(EntityUid uid, FireExtinguisherComponent component, UseInHandEvent args)
+    private void OnUseInHand(Entity<FireExtinguisherComponent> entity, ref UseInHandEvent args)
     {
         if (args.Handled)
             return;
 
-        ToggleSafety(uid, args.User, component);
+        ToggleSafety((entity.Owner, entity.Comp), args.User);
 
         args.Handled = true;
     }
 
-    private void OnAfterInteract(EntityUid uid, FireExtinguisherComponent component, AfterInteractEvent args)
+    private void OnAfterInteract(Entity<FireExtinguisherComponent> entity, ref AfterInteractEvent args)
     {
         if (args.Target == null || !args.CanReach)
         {
@@ -60,24 +58,23 @@ public sealed class FireExtinguisherSystem : EntitySystem
         if (args.Handled)
             return;
 
-        if (component.HasSafety && component.Safety)
+        if (entity.Comp.HasSafety && entity.Comp.Safety)
         {
-            _popupSystem.PopupEntity(Loc.GetString("fire-extinguisher-component-safety-on-message"), uid,
-                args.User);
+            _popupSystem.PopupEntity(Loc.GetString("fire-extinguisher-component-safety-on-message"), entity.Owner, args.User);
             return;
         }
 
-        if (args.Target is not {Valid: true} target ||
-            !_solutionContainerSystem.TryGetDrainableSolution(target, out var targetSolution) ||
-            !_solutionContainerSystem.TryGetRefillableSolution(uid, out var container))
+        if (args.Target is not { Valid: true } target ||
+            !_solutionContainerSystem.TryGetDrainableSolution(target, out var targetSoln, out var targetSolution) ||
+            !_solutionContainerSystem.TryGetRefillableSolution(entity.Owner, out var containerSoln, out var containerSolution))
         {
             return;
         }
 
         args.Handled = true;
 
-        var transfer = container.AvailableVolume;
-        if (TryComp<SolutionTransferComponent>(uid, out var solTrans))
+        var transfer = containerSolution.AvailableVolume;
+        if (TryComp<SolutionTransferComponent>(entity.Owner, out var solTrans))
         {
             transfer = solTrans.TransferAmount;
         }
@@ -85,59 +82,57 @@ public sealed class FireExtinguisherSystem : EntitySystem
 
         if (transfer > 0)
         {
-            var drained = _solutionContainerSystem.Drain(target, targetSolution, transfer);
-            _solutionContainerSystem.TryAddSolution(uid, container, drained);
+            var drained = _solutionContainerSystem.Drain(target, targetSoln.Value, transfer);
+            _solutionContainerSystem.TryAddSolution(containerSoln.Value, drained);
 
-            _audio.PlayPvs(component.RefillSound, uid);
-            _popupSystem.PopupEntity(Loc.GetString("fire-extinguisher-component-after-interact-refilled-message", ("owner", uid)),
-                uid, args.Target.Value);
+            _audio.PlayPvs(entity.Comp.RefillSound, entity.Owner);
+            _popupSystem.PopupEntity(Loc.GetString("fire-extinguisher-component-after-interact-refilled-message", ("owner", entity.Owner)),
+                entity.Owner, args.Target.Value);
         }
     }
 
-    private void OnGetInteractionVerbs(EntityUid uid, FireExtinguisherComponent component, GetVerbsEvent<InteractionVerb> args)
+    private void OnGetInteractionVerbs(Entity<FireExtinguisherComponent> entity, ref GetVerbsEvent<InteractionVerb> args)
     {
         if (!args.CanInteract)
             return;
 
+        var user = args.User;
         var verb = new InteractionVerb
         {
-            Act = () => ToggleSafety(uid, args.User, component),
+            Act = () => ToggleSafety((entity.Owner, entity.Comp), user),
             Text = Loc.GetString("fire-extinguisher-component-verb-text"),
         };
 
         args.Verbs.Add(verb);
     }
 
-    private void OnSprayAttempt(EntityUid uid, FireExtinguisherComponent component, SprayAttemptEvent args)
+    private void OnSprayAttempt(Entity<FireExtinguisherComponent> entity, ref SprayAttemptEvent args)
     {
-        if (component.HasSafety && component.Safety)
+        if (entity.Comp.HasSafety && entity.Comp.Safety)
         {
-            _popupSystem.PopupEntity(Loc.GetString("fire-extinguisher-component-safety-on-message"), uid,
-                args.User);
+            _popupSystem.PopupEntity(Loc.GetString("fire-extinguisher-component-safety-on-message"), entity, args.User);
             args.Cancel();
         }
     }
 
-    private void UpdateAppearance(EntityUid uid, FireExtinguisherComponent comp,
-        AppearanceComponent? appearance=null)
+    private void UpdateAppearance(Entity<FireExtinguisherComponent, AppearanceComponent?> entity)
     {
-        if (!Resolve(uid, ref appearance, false))
+        if (!Resolve(entity, ref entity.Comp2, false))
             return;
 
-        if (comp.HasSafety)
+        if (entity.Comp1.HasSafety)
         {
-            _appearance.SetData(uid, FireExtinguisherVisuals.Safety, comp.Safety, appearance);
+            _appearance.SetData(entity, FireExtinguisherVisuals.Safety, entity.Comp1.Safety, entity.Comp2);
         }
     }
 
-    public void ToggleSafety(EntityUid uid, EntityUid user,
-        FireExtinguisherComponent? extinguisher = null)
+    public void ToggleSafety(Entity<FireExtinguisherComponent?> extinguisher, EntityUid user)
     {
-        if (!Resolve(uid, ref extinguisher))
+        if (!Resolve(extinguisher, ref extinguisher.Comp))
             return;
 
-        extinguisher.Safety = !extinguisher.Safety;
-        _audio.PlayPvs(extinguisher.SafetySound, uid, AudioParams.Default.WithVariation(0.125f).WithVolume(-4f));
-        UpdateAppearance(uid, extinguisher);
+        extinguisher.Comp.Safety = !extinguisher.Comp.Safety;
+        _audio.PlayPvs(extinguisher.Comp.SafetySound, extinguisher, AudioParams.Default.WithVariation(0.125f).WithVolume(-4f));
+        UpdateAppearance((extinguisher.Owner, extinguisher.Comp));
     }
 }
index a2c89d4e42d4c89b19cafaeb5815d8e42539f2d9..3fd2ca00e22ca42830e750d4cff8922e96a58afd 100644 (file)
@@ -1,3 +1,4 @@
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Popups;
 using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.EntitySystems;
@@ -24,7 +25,7 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
     [Dependency] private readonly PuddleSystem _puddleSystem = default!;
     [Dependency] private readonly SharedMeleeWeaponSystem _melee = default!;
     [Dependency] private readonly SharedTransformSystem _transform = default!;
-    [Dependency] private readonly SolutionContainerSystem _solutionSystem = default!;
+    [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
     [Dependency] private readonly UseDelaySystem _useDelay = default!;
 
     public override void Initialize()
@@ -33,7 +34,7 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
         SubscribeLocalEvent<AbsorbentComponent, ComponentInit>(OnAbsorbentInit);
         SubscribeLocalEvent<AbsorbentComponent, AfterInteractEvent>(OnAfterInteract);
         SubscribeLocalEvent<AbsorbentComponent, InteractNoHandEvent>(OnInteractNoHand);
-        SubscribeLocalEvent<AbsorbentComponent, SolutionChangedEvent>(OnAbsorbentSolutionChange);
+        SubscribeLocalEvent<AbsorbentComponent, SolutionContainerChangedEvent>(OnAbsorbentSolutionChange);
     }
 
     private void OnAbsorbentInit(EntityUid uid, AbsorbentComponent component, ComponentInit args)
@@ -42,14 +43,14 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
         UpdateAbsorbent(uid, component);
     }
 
-    private void OnAbsorbentSolutionChange(EntityUid uid, AbsorbentComponent component, SolutionChangedEvent args)
+    private void OnAbsorbentSolutionChange(EntityUid uid, AbsorbentComponent component, ref SolutionContainerChangedEvent args)
     {
         UpdateAbsorbent(uid, component);
     }
 
     private void UpdateAbsorbent(EntityUid uid, AbsorbentComponent component)
     {
-        if (!_solutionSystem.TryGetSolution(uid, AbsorbentComponent.SolutionName, out var solution))
+        if (!_solutionContainerSystem.TryGetSolution(uid, AbsorbentComponent.SolutionName, out _, out var solution))
             return;
 
         var oldProgress = component.Progress.ShallowClone();
@@ -102,17 +103,17 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
 
     public void Mop(EntityUid user, EntityUid target, EntityUid used, AbsorbentComponent component)
     {
-        if (!_solutionSystem.TryGetSolution(used, AbsorbentComponent.SolutionName, out var absorbentSolution))
+        if (!_solutionContainerSystem.TryGetSolution(used, AbsorbentComponent.SolutionName, out var absorberSoln))
             return;
 
         if (_useDelay.ActiveDelay(used))
             return;
 
         // If it's a puddle try to grab from
-        if (!TryPuddleInteract(user, used, target, component, absorbentSolution))
+        if (!TryPuddleInteract(user, used, target, component, absorberSoln.Value))
         {
             // If it's refillable try to transfer
-            if (!TryRefillableInteract(user, used, target, component, absorbentSolution))
+            if (!TryRefillableInteract(user, used, target, component, absorberSoln.Value))
                 return;
         }
     }
@@ -120,24 +121,24 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
     /// <summary>
     ///     Logic for an absorbing entity interacting with a refillable.
     /// </summary>
-    private bool TryRefillableInteract(EntityUid user, EntityUid used, EntityUid target, AbsorbentComponent component, Solution absorbentSolution)
+    private bool TryRefillableInteract(EntityUid user, EntityUid used, EntityUid target, AbsorbentComponent component, Entity<SolutionComponent> absorbentSoln)
     {
         if (!TryComp(target, out RefillableSolutionComponent? refillable))
             return false;
 
-        if (!_solutionSystem.TryGetRefillableSolution(target, out var refillableSolution, refillable: refillable))
+        if (!_solutionContainerSystem.TryGetRefillableSolution((target, refillable, null), out var refillableSoln, out var refillableSolution))
             return false;
 
         if (refillableSolution.Volume <= 0)
         {
             // Target empty - only transfer absorbent contents into refillable
-            if (!TryTransferFromAbsorbentToRefillable(user, used, target, component, absorbentSolution, refillableSolution))
+            if (!TryTransferFromAbsorbentToRefillable(user, used, target, component, absorbentSoln, refillableSoln.Value))
                 return false;
         }
         else
         {
             // Target non-empty - do a two-way transfer
-            if (!TryTwoWayAbsorbentRefillableTransfer(user, used, target, component, absorbentSolution, refillableSolution))
+            if (!TryTwoWayAbsorbentRefillableTransfer(user, used, target, component, absorbentSoln, refillableSoln.Value))
                 return false;
         }
 
@@ -154,15 +155,17 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
         EntityUid used,
         EntityUid target,
         AbsorbentComponent component,
-        Solution absorbentSolution,
-        Solution refillableSolution)
+        Entity<SolutionComponent> absorbentSoln,
+        Entity<SolutionComponent> refillableSoln)
     {
+        var absorbentSolution = absorbentSoln.Comp.Solution;
         if (absorbentSolution.Volume <= 0)
         {
             _popups.PopupEntity(Loc.GetString("mopping-system-target-container-empty", ("target", target)), user, user);
             return false;
         }
 
+        var refillableSolution = refillableSoln.Comp.Solution;
         var transferAmount = component.PickupAmount < refillableSolution.AvailableVolume ?
             component.PickupAmount :
             refillableSolution.AvailableVolume;
@@ -174,17 +177,15 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
         }
 
         // Prioritize transferring non-evaporatives if absorbent has any
-        var contaminants = absorbentSolution.SplitSolutionWithout(transferAmount, PuddleSystem.EvaporationReagents);
+        var contaminants = _solutionContainerSystem.SplitSolutionWithout(absorbentSoln, transferAmount, PuddleSystem.EvaporationReagents);
         if (contaminants.Volume > 0)
         {
-            _solutionSystem.UpdateChemicals(used, absorbentSolution, true);
-            _solutionSystem.TryAddSolution(target, refillableSolution, contaminants);
+            _solutionContainerSystem.TryAddSolution(refillableSoln, contaminants);
         }
         else
         {
-            var evaporatives = absorbentSolution.SplitSolution(transferAmount);
-            _solutionSystem.UpdateChemicals(used, absorbentSolution, true);
-            _solutionSystem.TryAddSolution(target, refillableSolution, evaporatives);
+            var evaporatives = _solutionContainerSystem.SplitSolution(absorbentSoln, transferAmount);
+            _solutionContainerSystem.TryAddSolution(refillableSoln, evaporatives);
         }
 
         return true;
@@ -198,12 +199,12 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
         EntityUid used,
         EntityUid target,
         AbsorbentComponent component,
-        Solution absorbentSolution,
-        Solution refillableSolution)
+        Entity<SolutionComponent> absorbentSoln,
+        Entity<SolutionComponent> refillableSoln)
     {
-        var contaminantsFromAbsorbent = absorbentSolution.SplitSolutionWithout(component.PickupAmount, PuddleSystem.EvaporationReagents);
-        _solutionSystem.UpdateChemicals(used, absorbentSolution, true);
+        var contaminantsFromAbsorbent = _solutionContainerSystem.SplitSolutionWithout(absorbentSoln, component.PickupAmount, PuddleSystem.EvaporationReagents);
 
+        var absorbentSolution = absorbentSoln.Comp.Solution;
         if (contaminantsFromAbsorbent.Volume == FixedPoint2.Zero && absorbentSolution.AvailableVolume == FixedPoint2.Zero)
         {
             // Nothing to transfer to refillable and no room to absorb anything extra
@@ -217,8 +218,9 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
             component.PickupAmount :
             absorbentSolution.AvailableVolume;
 
+        var refillableSolution = refillableSoln.Comp.Solution;
         var waterFromRefillable = refillableSolution.SplitSolutionWithOnly(waterPulled, PuddleSystem.EvaporationReagents);
-        _solutionSystem.UpdateChemicals(target, refillableSolution);
+        _solutionContainerSystem.UpdateChemicals(refillableSoln);
 
         if (waterFromRefillable.Volume == FixedPoint2.Zero && contaminantsFromAbsorbent.Volume == FixedPoint2.Zero)
         {
@@ -234,7 +236,7 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
         if (waterFromRefillable.Volume > FixedPoint2.Zero)
         {
             // transfer water to absorbent
-            _solutionSystem.TryAddSolution(used, absorbentSolution, waterFromRefillable);
+            _solutionContainerSystem.TryAddSolution(absorbentSoln, waterFromRefillable);
             anyTransferOccurred = true;
         }
 
@@ -248,12 +250,12 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
             {
                 // transfer as much contaminants to refillable as will fit
                 var contaminantsForRefillable = contaminantsFromAbsorbent.SplitSolution(refillableSolution.AvailableVolume);
-                _solutionSystem.TryAddSolution(target, refillableSolution, contaminantsForRefillable);
+                _solutionContainerSystem.TryAddSolution(refillableSoln, contaminantsForRefillable);
                 anyTransferOccurred = true;
             }
 
             // absorb everything that did not fit in the refillable back by the absorbent
-            _solutionSystem.TryAddSolution(used, absorbentSolution, contaminantsFromAbsorbent);
+            _solutionContainerSystem.TryAddSolution(absorbentSoln, contaminantsFromAbsorbent);
         }
 
         return anyTransferOccurred;
@@ -262,23 +264,24 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
     /// <summary>
     ///     Logic for an absorbing entity interacting with a puddle.
     /// </summary>
-    private bool TryPuddleInteract(EntityUid user, EntityUid used, EntityUid target, AbsorbentComponent absorber, Solution absorberSoln)
+    private bool TryPuddleInteract(EntityUid user, EntityUid used, EntityUid target, AbsorbentComponent absorber, Entity<SolutionComponent> absorberSoln)
     {
         if (!TryComp(target, out PuddleComponent? puddle))
             return false;
 
-        if (!_solutionSystem.TryGetSolution(target, puddle.SolutionName, out var puddleSoln) || puddleSoln.Volume <= 0)
+        if (!_solutionContainerSystem.ResolveSolution(target, puddle.SolutionName, ref puddle.Solution, out var puddleSolution) || puddleSolution.Volume <= 0)
             return false;
 
         // Check if the puddle has any non-evaporative reagents
-        if (_puddleSystem.CanFullyEvaporate(puddleSoln))
+        if (_puddleSystem.CanFullyEvaporate(puddleSolution))
         {
             _popups.PopupEntity(Loc.GetString("mopping-system-puddle-evaporate", ("target", target)), user, user);
             return true;
         }
 
         // Check if we have any evaporative reagents on our absorber to transfer
-        var available = absorberSoln.GetTotalPrototypeQuantity(PuddleSystem.EvaporationReagents);
+        var absorberSolution = absorberSoln.Comp.Solution;
+        var available = absorberSolution.GetTotalPrototypeQuantity(PuddleSystem.EvaporationReagents);
 
         // No material
         if (available == FixedPoint2.Zero)
@@ -290,8 +293,8 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
         var transferMax = absorber.PickupAmount;
         var transferAmount = available > transferMax ? transferMax : available;
 
-        var puddleSplit = puddleSoln.SplitSolutionWithout(transferAmount, PuddleSystem.EvaporationReagents);
-        var absorberSplit = absorberSoln.SplitSolutionWithOnly(puddleSplit.Volume, PuddleSystem.EvaporationReagents);
+        var puddleSplit = puddleSolution.SplitSolutionWithout(transferAmount, PuddleSystem.EvaporationReagents);
+        var absorberSplit = absorberSolution.SplitSolutionWithOnly(puddleSplit.Volume, PuddleSystem.EvaporationReagents);
 
         // Do tile reactions first
         var coordinates = Transform(target).Coordinates;
@@ -300,11 +303,9 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
             _puddleSystem.DoTileReactions(mapGrid.GetTileRef(coordinates), absorberSplit);
         }
 
-        puddleSoln.AddSolution(absorberSplit, _prototype);
-        absorberSoln.AddSolution(puddleSplit, _prototype);
+        _solutionContainerSystem.AddSolution(puddle.Solution.Value, absorberSplit);
+        _solutionContainerSystem.AddSolution(absorberSoln, puddleSplit);
 
-        _solutionSystem.UpdateChemicals(used, absorberSoln);
-        _solutionSystem.UpdateChemicals(target, puddleSoln);
         _audio.PlayPvs(absorber.PickupSound, target);
         _useDelay.BeginDelay(used);
 
index 505ce71261fb00fb1e5ad025b9ee1740ce70c470..726ed0b13c50648adb2b482b75c4c8484099122f 100644 (file)
@@ -1,9 +1,9 @@
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.DoAfter;
 using Content.Server.Fluids.Components;
 using Content.Server.Popups;
 using Content.Shared.Audio;
 using Content.Shared.Chemistry.Components.SolutionManager;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Database;
 using Content.Shared.DoAfter;
 using Content.Shared.Examine;
@@ -13,10 +13,9 @@ using Content.Shared.Fluids.Components;
 using Content.Shared.Interaction;
 using Content.Shared.Tag;
 using Content.Shared.Verbs;
-using Content.Shared.Fluids.Components;
-using Robust.Shared.Audio;
 using Robust.Shared.Audio.Systems;
 using Robust.Shared.Collections;
+using Robust.Shared.Prototypes;
 using Robust.Shared.Random;
 using Robust.Shared.Utility;
 
@@ -25,7 +24,7 @@ namespace Content.Server.Fluids.EntitySystems;
 public sealed class DrainSystem : SharedDrainSystem
 {
     [Dependency] private readonly EntityLookupSystem _lookup = default!;
-    [Dependency] private readonly SolutionContainerSystem _solutionSystem = default!;
+    [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
     [Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!;
     [Dependency] private readonly SharedAudioSystem _audioSystem = default!;
     [Dependency] private readonly PopupSystem _popupSystem = default!;
@@ -33,6 +32,7 @@ public sealed class DrainSystem : SharedDrainSystem
     [Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
     [Dependency] private readonly PuddleSystem _puddleSystem = default!;
     [Dependency] private readonly IRobustRandom _random = default!;
+    [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
 
     public override void Initialize()
     {
@@ -43,7 +43,7 @@ public sealed class DrainSystem : SharedDrainSystem
         SubscribeLocalEvent<DrainComponent, DrainDoAfterEvent>(OnDoAfter);
     }
 
-    private void AddEmptyVerb(EntityUid uid, DrainComponent component, GetVerbsEvent<Verb> args)
+    private void AddEmptyVerb(Entity<DrainComponent> entity, ref GetVerbsEvent<Verb> args)
     {
         if (!args.CanAccess || !args.CanInteract || args.Using == null)
             return;
@@ -52,12 +52,14 @@ public sealed class DrainSystem : SharedDrainSystem
             !TryComp(args.Target, out DrainComponent? drain))
             return;
 
+        var used = args.Using.Value;
+        var target = args.Target;
         Verb verb = new()
         {
-            Text = Loc.GetString("drain-component-empty-verb-inhand", ("object", Name(args.Using.Value))),
+            Text = Loc.GetString("drain-component-empty-verb-inhand", ("object", Name(used))),
             Act = () =>
             {
-                Empty(args.Using.Value, spillable, args.Target, drain);
+                Empty(used, spillable, target, drain);
             },
             Impact = LogImpact.Low,
             Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/eject.svg.192dpi.png"))
@@ -69,8 +71,7 @@ public sealed class DrainSystem : SharedDrainSystem
     private void Empty(EntityUid container, SpillableComponent spillable, EntityUid target, DrainComponent drain)
     {
         // Find the solution in the container that is emptied
-        if (!_solutionSystem.TryGetDrainableSolution(container, out var containerSolution) ||
-            containerSolution.Volume == FixedPoint2.Zero)
+        if (!_solutionContainerSystem.TryGetDrainableSolution(container, out var containerSoln, out var containerSolution) || containerSolution.Volume == FixedPoint2.Zero)
         {
             _popupSystem.PopupEntity(
                 Loc.GetString("drain-component-empty-verb-using-is-empty-message", ("object", container)),
@@ -79,17 +80,17 @@ public sealed class DrainSystem : SharedDrainSystem
         }
 
         // try to find the drain's solution
-        if (!_solutionSystem.TryGetSolution(target, DrainComponent.SolutionName, out var drainSolution))
+        if (!_solutionContainerSystem.ResolveSolution(target, DrainComponent.SolutionName, ref drain.Solution, out var drainSolution))
         {
             return;
         }
 
         // Try to transfer as much solution as possible to the drain
 
-        var transferSolution = _solutionSystem.SplitSolution(container, containerSolution,
+        var transferSolution = _solutionContainerSystem.SplitSolution(containerSoln.Value,
             FixedPoint2.Min(containerSolution.Volume, drainSolution.AvailableVolume));
 
-        _solutionSystem.TryAddSolution(target, drainSolution, transferSolution);
+        _solutionContainerSystem.TryAddSolution(drain.Solution.Value, transferSolution);
 
         _audioSystem.PlayPvs(drain.ManualDrainSound, target);
         _ambientSoundSystem.SetAmbience(target, true);
@@ -111,7 +112,7 @@ public sealed class DrainSystem : SharedDrainSystem
         var managerQuery = GetEntityQuery<SolutionContainerManagerComponent>();
         var xformQuery = GetEntityQuery<TransformComponent>();
         var puddleQuery = GetEntityQuery<PuddleComponent>();
-        var puddles = new ValueList<(EntityUid Entity, string Solution)>();
+        var puddles = new ValueList<(Entity<PuddleComponent> Entity, string Solution)>();
 
         var query = EntityQueryEnumerator<DrainComponent>();
         while (query.MoveNext(out var uid, out var drain))
@@ -134,9 +135,7 @@ public sealed class DrainSystem : SharedDrainSystem
                 continue;
 
             // Best to do this one every second rather than once every tick...
-            _solutionSystem.TryGetSolution(uid, DrainComponent.SolutionName, out var drainSolution, manager);
-
-            if (drainSolution is null)
+            if (!_solutionContainerSystem.ResolveSolution((uid, manager), DrainComponent.SolutionName, ref drain.Solution, out var drainSolution))
                 continue;
 
             if (drainSolution.AvailableVolume <= 0)
@@ -146,7 +145,7 @@ public sealed class DrainSystem : SharedDrainSystem
             }
 
             // Remove a bit from the buffer
-            _solutionSystem.SplitSolution(uid, drainSolution, (drain.UnitsDestroyedPerSecond * drain.DrainFrequency));
+            _solutionContainerSystem.SplitSolution(drain.Solution.Value, (drain.UnitsDestroyedPerSecond * drain.DrainFrequency));
 
             // This will ensure that UnitsPerSecond is per second...
             var amount = drain.UnitsPerSecond * drain.DrainFrequency;
@@ -162,7 +161,7 @@ public sealed class DrainSystem : SharedDrainSystem
                 // and these are placed by mappers and not buildable/movable so shouldnt really be a problem...
                 if (puddleQuery.TryGetComponent(entity, out var puddle))
                 {
-                    puddles.Add((entity, puddle.SolutionName));
+                    puddles.Add(((entity, puddle), puddle.SolutionName));
                 }
             }
 
@@ -180,7 +179,7 @@ public sealed class DrainSystem : SharedDrainSystem
             {
                 // Queue the solution deletion if it's empty. EvaporationSystem might also do this
                 // but queuedelete should be pretty safe.
-                if (!_solutionSystem.TryGetSolution(puddle, solution, out var puddleSolution))
+                if (!_solutionContainerSystem.ResolveSolution(puddle.Owner, solution, ref puddle.Comp.Solution, out var puddleSolution))
                 {
                     EntityManager.QueueDeleteEntity(puddle);
                     continue;
@@ -190,24 +189,26 @@ public sealed class DrainSystem : SharedDrainSystem
                 // the drain component's units per second adjusted for # of puddles
                 // the puddle's remaining volume (making it cleanly zero)
                 // the drain's remaining volume in its buffer.
-                var transferSolution = _solutionSystem.SplitSolution(puddle, puddleSolution,
+                var transferSolution = _solutionContainerSystem.SplitSolution(puddle.Comp.Solution.Value,
                     FixedPoint2.Min(FixedPoint2.New(amount), puddleSolution.Volume, drainSolution.AvailableVolume));
 
-                _solutionSystem.TryAddSolution(uid, drainSolution, transferSolution);
+                drainSolution.AddSolution(transferSolution, _prototypeManager);
 
                 if (puddleSolution.Volume <= 0)
                 {
                     QueueDel(puddle);
                 }
             }
+
+            _solutionContainerSystem.UpdateChemicals(drain.Solution.Value);
         }
     }
 
-    private void OnExamined(EntityUid uid, DrainComponent component, ExaminedEvent args)
+    private void OnExamined(Entity<DrainComponent> entity, ref ExaminedEvent args)
     {
         if (!args.IsInDetailsRange ||
-            !HasComp<SolutionContainerManagerComponent>(uid) ||
-            !_solutionSystem.TryGetSolution(uid, DrainComponent.SolutionName, out var drainSolution))
+            !HasComp<SolutionContainerManagerComponent>(entity) ||
+            !_solutionContainerSystem.ResolveSolution(entity.Owner, DrainComponent.SolutionName, ref entity.Comp.Solution, out var drainSolution))
         {
             return;
         }
@@ -218,11 +219,11 @@ public sealed class DrainSystem : SharedDrainSystem
         args.Message.AddMarkup($"\n\n{text}");
     }
 
-    private void OnInteract(EntityUid uid, DrainComponent component, InteractEvent args)
+    private void OnInteract(Entity<DrainComponent> entity, ref AfterInteractUsingEvent args)
     {
         if (!args.CanReach || args.Target == null ||
             !_tagSystem.HasTag(args.Used, DrainComponent.PlungerTag) ||
-            !_solutionSystem.TryGetSolution(args.Target.Value, DrainComponent.SolutionName, out var drainSolution))
+            !_solutionContainerSystem.ResolveSolution(args.Target.Value, DrainComponent.SolutionName, ref entity.Comp.Solution, out var drainSolution))
         {
             return;
         }
@@ -233,10 +234,10 @@ public sealed class DrainSystem : SharedDrainSystem
             return;
         }
 
-        _audioSystem.PlayPvs(component.PlungerSound, uid);
+        _audioSystem.PlayPvs(entity.Comp.PlungerSound, entity);
 
 
-        var doAfterArgs = new DoAfterArgs(EntityManager, args.User, component.UnclogDuration, new DrainDoAfterEvent(),uid, args.Target, args.Used)
+        var doAfterArgs = new DoAfterArgs(EntityManager, args.User, entity.Comp.UnclogDuration, new DrainDoAfterEvent(), entity, args.Target, args.Used)
         {
             BreakOnTargetMove = true,
             BreakOnUserMove = true,
@@ -247,27 +248,26 @@ public sealed class DrainSystem : SharedDrainSystem
         _doAfterSystem.TryStartDoAfter(doAfterArgs);
     }
 
-    private void OnDoAfter(EntityUid uid, DrainComponent component, DoAfterEvent args)
+    private void OnDoAfter(Entity<DrainComponent> entity, ref DrainDoAfterEvent args)
     {
         if (args.Target == null)
             return;
 
-        if (!_random.Prob(component.UnclogProbability))
+        if (!_random.Prob(entity.Comp.UnclogProbability))
         {
             _popupSystem.PopupEntity(Loc.GetString("drain-component-unclog-fail", ("object", args.Target.Value)), args.Target.Value);
             return;
         }
 
 
-        if (!_solutionSystem.TryGetSolution(args.Target.Value, DrainComponent.SolutionName,
-                out var drainSolution))
+        if (!_solutionContainerSystem.ResolveSolution(args.Target.Value, DrainComponent.SolutionName, ref entity.Comp.Solution))
         {
             return;
         }
 
 
-        _solutionSystem.RemoveAllSolution(args.Target.Value, drainSolution);
-        _audioSystem.PlayPvs(component.UnclogSound, args.Target.Value);
+        _solutionContainerSystem.RemoveAllSolution(entity.Comp.Solution.Value);
+        _audioSystem.PlayPvs(entity.Comp.UnclogSound, args.Target.Value);
         _popupSystem.PopupEntity(Loc.GetString("drain-component-unclog-success", ("object", args.Target.Value)), args.Target.Value);
     }
 }
index d0df5e05050894c981a52e25a619172253bcf3f4..f3489ba7c6b2bf5c0c7f84b2eb674892650d5ba1 100644 (file)
@@ -18,9 +18,9 @@ public sealed partial class PuddleSystem
 
     public static string[] EvaporationReagents = new[] { Water, SoapyWater };
 
-    private void OnEvaporationMapInit(EntityUid uid, EvaporationComponent component, MapInitEvent args)
+    private void OnEvaporationMapInit(Entity<EvaporationComponent> entity, ref MapInitEvent args)
     {
-        component.NextTick = _timing.CurTime + EvaporationCooldown;
+        entity.Comp.NextTick = _timing.CurTime + EvaporationCooldown;
     }
 
     private void UpdateEvaporation(EntityUid uid, Solution solution)
@@ -52,7 +52,7 @@ public sealed partial class PuddleSystem
 
             evaporation.NextTick += EvaporationCooldown;
 
-            if (!_solutionContainerSystem.TryGetSolution(uid, puddle.SolutionName, out var puddleSolution))
+            if (!_solutionContainerSystem.ResolveSolution(uid, puddle.SolutionName, ref puddle.Solution, out var puddleSolution))
                 continue;
 
             var reagentTick = evaporation.EvaporationAmount * EvaporationCooldown.TotalSeconds;
index e1681d87311f31d15c20a8e3842dbd20f4e0d9e4..083d15c22e5aba9b62dcde891335d92e23c4972a 100644 (file)
@@ -1,4 +1,4 @@
-using Content.Server.Chemistry.EntitySystems;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Fluids.Components;
 using Content.Server.Nutrition.EntitySystems;
 using Content.Shared.Chemistry.Components;
@@ -36,29 +36,29 @@ public sealed partial class PuddleSystem
         SubscribeLocalEvent<SpillableComponent, MeleeHitEvent>(SplashOnMeleeHit, after: new[] { typeof(OpenableSystem) });
         SubscribeLocalEvent<SpillableComponent, GetVerbsEvent<Verb>>(AddSpillVerb);
         SubscribeLocalEvent<SpillableComponent, GotEquippedEvent>(OnGotEquipped);
-        SubscribeLocalEvent<SpillableComponent, SolutionOverflowEvent>(OnOverflow);
+        SubscribeLocalEvent<SpillableComponent, SolutionContainerOverflowEvent>(OnOverflow);
         SubscribeLocalEvent<SpillableComponent, SpillDoAfterEvent>(OnDoAfter);
         SubscribeLocalEvent<SpillableComponent, AttemptPacifiedThrowEvent>(OnAttemptPacifiedThrow);
     }
 
-    private void OnExamined(EntityUid uid, SpillableComponent component, ExaminedEvent args)
+    private void OnExamined(Entity<SpillableComponent> entity, ref ExaminedEvent args)
     {
         args.PushMarkup(Loc.GetString("spill-examine-is-spillable"));
 
-        if (HasComp<MeleeWeaponComponent>(uid))
+        if (HasComp<MeleeWeaponComponent>(entity))
             args.PushMarkup(Loc.GetString("spill-examine-spillable-weapon"));
     }
 
-    private void OnOverflow(EntityUid uid, SpillableComponent component, ref SolutionOverflowEvent args)
+    private void OnOverflow(Entity<SpillableComponent> entity, ref SolutionContainerOverflowEvent args)
     {
         if (args.Handled)
             return;
 
-        TrySpillAt(Transform(uid).Coordinates, args.Overflow, out _);
+        TrySpillAt(Transform(entity).Coordinates, args.Overflow, out _);
         args.Handled = true;
     }
 
-    private void SplashOnMeleeHit(EntityUid uid, SpillableComponent component, MeleeHitEvent args)
+    private void SplashOnMeleeHit(Entity<SpillableComponent> entity, ref MeleeHitEvent args)
     {
         if (args.Handled)
             return;
@@ -68,20 +68,20 @@ public sealed partial class PuddleSystem
         // If this also has solution transfer, then assume the transfer amount is how much we want to spill.
         // Otherwise let's say they want to spill a quarter of its max volume.
 
-        if (!_solutionContainerSystem.TryGetDrainableSolution(uid, out var solution))
+        if (!_solutionContainerSystem.TryGetDrainableSolution(entity.Owner, out var soln, out var solution))
             return;
 
         var hitCount = args.HitEntities.Count;
 
         var totalSplit = FixedPoint2.Min(solution.MaxVolume * 0.25, solution.Volume);
-        if (TryComp<SolutionTransferComponent>(uid, out var transfer))
+        if (TryComp<SolutionTransferComponent>(entity, out var transfer))
         {
             totalSplit = FixedPoint2.Min(transfer.TransferAmount, solution.Volume);
         }
 
         // a little lame, but reagent quantity is not very balanced and we don't want people
         // spilling like 100u of reagent on someone at once!
-        totalSplit = FixedPoint2.Min(totalSplit, component.MaxMeleeSpillAmount);
+        totalSplit = FixedPoint2.Min(totalSplit, entity.Comp.MaxMeleeSpillAmount);
 
         if (totalSplit == 0)
             return;
@@ -95,29 +95,29 @@ public sealed partial class PuddleSystem
                 continue;
             }
 
-            var splitSolution = _solutionContainerSystem.SplitSolution(uid, solution, totalSplit / hitCount);
+            var splitSolution = _solutionContainerSystem.SplitSolution(soln.Value, totalSplit / hitCount);
 
-            _adminLogger.Add(LogType.MeleeHit, $"{ToPrettyString(args.User)} splashed {SolutionContainerSystem.ToPrettyString(splitSolution):solution} from {ToPrettyString(uid):entity} onto {ToPrettyString(hit):target}");
+            _adminLogger.Add(LogType.MeleeHit, $"{ToPrettyString(args.User)} splashed {SolutionContainerSystem.ToPrettyString(splitSolution):solution} from {ToPrettyString(entity.Owner):entity} onto {ToPrettyString(hit):target}");
             _reactive.DoEntityReaction(hit, splitSolution, ReactionMethod.Touch);
 
             _popups.PopupEntity(
-                Loc.GetString("spill-melee-hit-attacker", ("amount", totalSplit / hitCount), ("spillable", uid),
+                Loc.GetString("spill-melee-hit-attacker", ("amount", totalSplit / hitCount), ("spillable", entity.Owner),
                     ("target", Identity.Entity(hit, EntityManager))),
                 hit, args.User);
 
             _popups.PopupEntity(
-                Loc.GetString("spill-melee-hit-others", ("attacker", args.User), ("spillable", uid),
+                Loc.GetString("spill-melee-hit-others", ("attacker", args.User), ("spillable", entity.Owner),
                     ("target", Identity.Entity(hit, EntityManager))),
                 hit, Filter.PvsExcept(args.User), true, PopupType.SmallCaution);
         }
     }
 
-    private void OnGotEquipped(EntityUid uid, SpillableComponent component, GotEquippedEvent args)
+    private void OnGotEquipped(Entity<SpillableComponent> entity, ref GotEquippedEvent args)
     {
-        if (!component.SpillWorn)
+        if (!entity.Comp.SpillWorn)
             return;
 
-        if (!TryComp(uid, out ClothingComponent? clothing))
+        if (!TryComp(entity, out ClothingComponent? clothing))
             return;
 
         // check if entity was actually used as clothing
@@ -126,33 +126,33 @@ public sealed partial class PuddleSystem
         if (!isCorrectSlot)
             return;
 
-        if (!_solutionContainerSystem.TryGetSolution(uid, component.SolutionName, out var solution))
+        if (!_solutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var soln, out var solution))
             return;
 
         if (solution.Volume == 0)
             return;
 
         // spill all solution on the player
-        var drainedSolution = _solutionContainerSystem.Drain(uid, solution, solution.Volume);
-        TrySplashSpillAt(uid, Transform(args.Equipee).Coordinates, drainedSolution, out _);
+        var drainedSolution = _solutionContainerSystem.Drain(entity.Owner, soln.Value, solution.Volume);
+        TrySplashSpillAt(entity.Owner, Transform(args.Equipee).Coordinates, drainedSolution, out _);
     }
 
-    private void SpillOnLand(EntityUid uid, SpillableComponent component, ref LandEvent args)
+    private void SpillOnLand(Entity<SpillableComponent> entity, ref LandEvent args)
     {
-        if (!_solutionContainerSystem.TryGetSolution(uid, component.SolutionName, out var solution))
+        if (!_solutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var soln, out var solution))
             return;
 
-        if (_openable.IsClosed(uid))
+        if (_openable.IsClosed(entity.Owner))
             return;
 
         if (args.User != null)
         {
             _adminLogger.Add(LogType.Landed,
-                $"{ToPrettyString(uid):entity} spilled a solution {SolutionContainerSystem.ToPrettyString(solution):solution} on landing");
+                $"{ToPrettyString(entity.Owner):entity} spilled a solution {SolutionContainerSystem.ToPrettyString(solution):solution} on landing");
         }
 
-        var drainedSolution = _solutionContainerSystem.Drain(uid, solution, solution.Volume);
-        TrySplashSpillAt(uid, Transform(uid).Coordinates, drainedSolution, out _);
+        var drainedSolution = _solutionContainerSystem.Drain(entity.Owner, soln.Value, solution.Volume);
+        TrySplashSpillAt(entity.Owner, Transform(entity).Coordinates, drainedSolution, out _);
     }
 
     /// <summary>
@@ -165,18 +165,18 @@ public sealed partial class PuddleSystem
             return;
 
         // Don’t care about empty containers.
-        if (!_solutionContainerSystem.TryGetSolution(ent, ent.Comp.SolutionName, out var solution))
+        if (!_solutionContainerSystem.TryGetSolution(ent.Owner, ent.Comp.SolutionName, out _, out var solution) || solution.Volume <= 0)
             return;
 
         args.Cancel("pacified-cannot-throw-spill");
     }
 
-    private void AddSpillVerb(EntityUid uid, SpillableComponent component, GetVerbsEvent<Verb> args)
+    private void AddSpillVerb(Entity<SpillableComponent> entity, ref GetVerbsEvent<Verb> args)
     {
         if (!args.CanAccess || !args.CanInteract)
             return;
 
-        if (!_solutionContainerSystem.TryGetSolution(args.Target, component.SolutionName, out var solution))
+        if (!_solutionContainerSystem.TryGetSolution(args.Target, entity.Comp.SolutionName, out var soln, out var solution))
             return;
 
         if (_openable.IsClosed(args.Target))
@@ -195,20 +195,21 @@ public sealed partial class PuddleSystem
         };
 
         // TODO VERB ICONS spill icon? pouring out a glass/beaker?
-        if (component.SpillDelay == null)
+        if (entity.Comp.SpillDelay == null)
         {
+            var target = args.Target;
             verb.Act = () =>
             {
-                var puddleSolution = _solutionContainerSystem.SplitSolution(args.Target,
-                    solution, solution.Volume);
-                TrySpillAt(Transform(args.Target).Coordinates, puddleSolution, out _);
+                var puddleSolution = _solutionContainerSystem.SplitSolution(soln.Value, solution.Volume);
+                TrySpillAt(Transform(target).Coordinates, puddleSolution, out _);
             };
         }
         else
         {
+            var user = args.User;
             verb.Act = () =>
             {
-                _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, component.SpillDelay ?? 0, new SpillDoAfterEvent(), uid, target: uid)
+                _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, user, entity.Comp.SpillDelay ?? 0, new SpillDoAfterEvent(), entity.Owner, target: entity.Owner)
                 {
                     BreakOnTargetMove = true,
                     BreakOnUserMove = true,
@@ -222,17 +223,17 @@ public sealed partial class PuddleSystem
         args.Verbs.Add(verb);
     }
 
-    private void OnDoAfter(EntityUid uid, SpillableComponent component, DoAfterEvent args)
+    private void OnDoAfter(Entity<SpillableComponent> entity, ref SpillDoAfterEvent args)
     {
         if (args.Handled || args.Cancelled || args.Args.Target == null)
             return;
 
         //solution gone by other means before doafter completes
-        if (!_solutionContainerSystem.TryGetDrainableSolution(uid, out var solution) || solution.Volume == 0)
+        if (!_solutionContainerSystem.TryGetDrainableSolution(entity.Owner, out var soln, out var solution) || solution.Volume == 0)
             return;
 
-        var puddleSolution = _solutionContainerSystem.SplitSolution(uid, solution, solution.Volume);
-        TrySpillAt(Transform(uid).Coordinates, puddleSolution, out _);
+        var puddleSolution = _solutionContainerSystem.SplitSolution(soln.Value, solution.Volume);
+        TrySpillAt(Transform(entity).Coordinates, puddleSolution, out _);
         args.Handled = true;
     }
 }
index ba73adf389ff5f60c222161f5fcd01044f48904e..04bbf55c581c7ad801b1ccea237416fb50107cfc 100644 (file)
@@ -1,9 +1,7 @@
-using Content.Server.Fluids.Components;
 using Content.Shared.Chemistry.Components;
 using Content.Shared.DragDrop;
 using Content.Shared.FixedPoint;
 using Content.Shared.Fluids;
-using Content.Shared.Fluids.Components;
 
 namespace Content.Server.Fluids.EntitySystems;
 
@@ -14,33 +12,30 @@ public sealed partial class PuddleSystem
         SubscribeLocalEvent<RefillableSolutionComponent, DragDropDraggedEvent>(OnRefillableDragged);
     }
 
-    private void OnRefillableDragged(EntityUid uid, RefillableSolutionComponent component, ref DragDropDraggedEvent args)
+    private void OnRefillableDragged(Entity<RefillableSolutionComponent> entity, ref DragDropDraggedEvent args)
     {
-        _solutionContainerSystem.TryGetSolution(uid, component.Solution, out var solution);
-
-        if (solution?.Volume == FixedPoint2.Zero)
+        if (!_solutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.Solution, out var soln, out var solution) || solution.Volume == FixedPoint2.Zero)
         {
-            _popups.PopupEntity(Loc.GetString("mopping-system-empty", ("used", uid)), uid, args.User);
+            _popups.PopupEntity(Loc.GetString("mopping-system-empty", ("used", entity.Owner)), entity, args.User);
             return;
         }
 
         // Dump reagents into DumpableSolution
         if (TryComp<DumpableSolutionComponent>(args.Target, out var dump))
         {
-            _solutionContainerSystem.TryGetDumpableSolution(args.Target, out var dumpableSolution, dump);
-            if (dumpableSolution == null || solution == null)
+            if (!_solutionContainerSystem.TryGetDumpableSolution((args.Target, dump, null), out var dumpableSoln, out var dumpableSolution))
                 return;
 
             bool success = true;
             if (dump.Unlimited)
             {
-                var split = _solutionContainerSystem.SplitSolution(uid, solution, solution.Volume);
+                var split = _solutionContainerSystem.SplitSolution(soln.Value, solution.Volume);
                 dumpableSolution.AddSolution(split, _prototypeManager);
             }
             else
             {
-                var split = _solutionContainerSystem.SplitSolution(uid, solution, dumpableSolution.AvailableVolume);
-                success = _solutionContainerSystem.TryAddSolution(args.Target, dumpableSolution, split);
+                var split = _solutionContainerSystem.SplitSolution(soln.Value, dumpableSolution.AvailableVolume);
+                success = _solutionContainerSystem.TryAddSolution(dumpableSoln.Value, split);
             }
 
             if (success)
@@ -55,25 +50,21 @@ public sealed partial class PuddleSystem
             return;
         }
 
-        TryComp<DrainableSolutionComponent>(args.Target, out var drainable);
-
-        _solutionContainerSystem.TryGetDrainableSolution(args.Target, out var drainableSolution, drainable);
-
         // Take reagents from target
-        if (drainable != null)
+        if (!TryComp<DrainableSolutionComponent>(args.Target, out var drainable))
         {
-            if (drainableSolution == null || solution == null)
+            if (!_solutionContainerSystem.TryGetDrainableSolution((args.Target, drainable, null), out var drainableSolution, out _))
                 return;
 
-            var split = _solutionContainerSystem.SplitSolution(args.Target, drainableSolution, solution.AvailableVolume);
+            var split = _solutionContainerSystem.SplitSolution(drainableSolution.Value, solution.AvailableVolume);
 
-            if (_solutionContainerSystem.TryAddSolution(uid, solution, split))
+            if (_solutionContainerSystem.TryAddSolution(soln.Value, split))
             {
-                _audio.PlayPvs(AbsorbentComponent.DefaultTransferSound, uid);
+                _audio.PlayPvs(AbsorbentComponent.DefaultTransferSound, entity);
             }
             else
             {
-                _popups.PopupEntity(Loc.GetString("mopping-system-full", ("used", uid)), uid, args.User);
+                _popups.PopupEntity(Loc.GetString("mopping-system-full", ("used", entity.Owner)), entity, args.User);
             }
         }
     }
index 05d0809bb2b153045d94b438f3f275e622e3bea4..844f43a5e4e1d46d3136ea05668a03b14bd1d41a 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Server.Administration.Logs;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.DoAfter;
 using Content.Server.Fluids.Components;
 using Content.Server.Spreader;
@@ -22,19 +23,13 @@ using Content.Shared.Popups;
 using Content.Shared.Slippery;
 using Content.Shared.StepTrigger.Components;
 using Content.Shared.StepTrigger.Systems;
-using Robust.Server.GameObjects;
-using Robust.Shared.Audio;
+using Robust.Server.Audio;
 using Robust.Shared.Map;
 using Robust.Shared.Map.Components;
 using Robust.Shared.Player;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Random;
 using Robust.Shared.Timing;
-using Content.Shared.Movement.Components;
-using Content.Shared.Movement.Systems;
-using Content.Shared.Maps;
-using Content.Shared.Effects;
-using Robust.Server.Audio;
 
 namespace Content.Server.Fluids.EntitySystems;
 
@@ -91,7 +86,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
         // Shouldn't need re-anchoring.
         SubscribeLocalEvent<PuddleComponent, AnchorStateChangedEvent>(OnAnchorChanged);
         SubscribeLocalEvent<PuddleComponent, ExaminedEvent>(HandlePuddleExamined);
-        SubscribeLocalEvent<PuddleComponent, SolutionChangedEvent>(OnSolutionUpdate);
+        SubscribeLocalEvent<PuddleComponent, SolutionContainerChangedEvent>(OnSolutionUpdate);
         SubscribeLocalEvent<PuddleComponent, ComponentInit>(OnPuddleInit);
         SubscribeLocalEvent<PuddleComponent, SpreadNeighborsEvent>(OnPuddleSpread);
         SubscribeLocalEvent<PuddleComponent, SlipEvent>(OnPuddleSlip);
@@ -102,13 +97,13 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
         InitializeTransfers();
     }
 
-    private void OnPuddleSpread(EntityUid uid, PuddleComponent component, ref SpreadNeighborsEvent args)
+    private void OnPuddleSpread(Entity<PuddleComponent> entity, ref SpreadNeighborsEvent args)
     {
-        var overflow = GetOverflowSolution(uid, component);
+        var overflow = GetOverflowSolution(entity.Owner, entity.Comp);
 
         if (overflow.Volume == FixedPoint2.Zero)
         {
-            RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
+            RemCompDeferred<ActiveEdgeSpreaderComponent>(entity);
             return;
         }
 
@@ -125,7 +120,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
             foreach (var neighbor in args.Neighbors)
             {
                 if (!puddleQuery.TryGetComponent(neighbor, out var puddle) ||
-                    !_solutionContainerSystem.TryGetSolution(neighbor, puddle.SolutionName, out var neighborSolution) ||
+                    !_solutionContainerSystem.ResolveSolution(neighbor, puddle.SolutionName, ref puddle.Solution, out var neighborSolution) ||
                     CanFullyEvaporate(neighborSolution))
                 {
                     continue;
@@ -138,7 +133,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
 
                 var split = overflow.SplitSolution(remaining);
 
-                if (!_solutionContainerSystem.TryAddSolution(neighbor, neighborSolution, split))
+                if (!_solutionContainerSystem.TryAddSolution(puddle.Solution.Value, split))
                     continue;
 
                 args.Updates--;
@@ -150,7 +145,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
 
             if (overflow.Volume == FixedPoint2.Zero)
             {
-                RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
+                RemCompDeferred<ActiveEdgeSpreaderComponent>(entity);
                 return;
             }
         }
@@ -173,7 +168,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
                     break;
             }
 
-            RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
+            RemCompDeferred<ActiveEdgeSpreaderComponent>(entity);
             return;
         }
 
@@ -186,7 +181,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
             {
                 // Overflow to neighbours (unless it's pure water)
                 if (!puddleQuery.TryGetComponent(neighbor, out var puddle) ||
-                    !_solutionContainerSystem.TryGetSolution(neighbor, puddle.SolutionName, out var neighborSolution) ||
+                    !_solutionContainerSystem.ResolveSolution(neighbor, puddle.SolutionName, ref puddle.Solution, out var neighborSolution) ||
                     CanFullyEvaporate(neighborSolution))
                 {
                     continue;
@@ -194,7 +189,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
 
                 var split = overflow.SplitSolution(spillPerNeighbor);
 
-                if (!_solutionContainerSystem.TryAddSolution(neighbor, neighborSolution, split))
+                if (!_solutionContainerSystem.TryAddSolution(puddle.Solution.Value, split))
                     continue;
 
                 EnsureComp<ActiveEdgeSpreaderComponent>(neighbor);
@@ -206,13 +201,13 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
         }
 
         // Add the remainder back
-        if (_solutionContainerSystem.TryGetSolution(uid, component.SolutionName, out var puddleSolution))
+        if (_solutionContainerSystem.ResolveSolution(entity.Owner, entity.Comp.SolutionName, ref entity.Comp.Solution))
         {
-            _solutionContainerSystem.TryAddSolution(uid, puddleSolution, overflow);
+            _solutionContainerSystem.TryAddSolution(entity.Comp.Solution.Value, overflow);
         }
     }
 
-    private void OnPuddleSlip(EntityUid uid, PuddleComponent component, ref SlipEvent args)
+    private void OnPuddleSlip(Entity<PuddleComponent> entity, ref SlipEvent args)
     {
         // Reactive entities have a chance to get a touch reaction from slipping on a puddle
         // (i.e. it is implied they fell face first onto it or something)
@@ -224,14 +219,14 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
         if (!_random.Prob(0.5f))
             return;
 
-        if (!_solutionContainerSystem.TryGetSolution(uid, component.SolutionName, out var solution))
+        if (!_solutionContainerSystem.ResolveSolution(entity.Owner, entity.Comp.SolutionName, ref entity.Comp.Solution, out var solution))
             return;
 
-        _popups.PopupEntity(Loc.GetString("puddle-component-slipped-touch-reaction", ("puddle", uid)),
+        _popups.PopupEntity(Loc.GetString("puddle-component-slipped-touch-reaction", ("puddle", entity.Owner)),
             args.Slipped, args.Slipped, PopupType.SmallCaution);
 
         // Take 15% of the puddle solution
-        var splitSol = _solutionContainerSystem.SplitSolution(uid, solution, solution.Volume * 0.15f);
+        var splitSol = _solutionContainerSystem.SplitSolution(entity.Comp.Solution.Value, solution.Volume * 0.15f);
         _reactive.DoEntityReaction(args.Slipped, splitSol, ReactionMethod.Touch);
     }
 
@@ -248,27 +243,27 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
         TickEvaporation();
     }
 
-    private void OnPuddleInit(EntityUid uid, PuddleComponent component, ComponentInit args)
+    private void OnPuddleInit(Entity<PuddleComponent> entity, ref ComponentInit args)
     {
-        _solutionContainerSystem.EnsureSolution(uid, component.SolutionName, FixedPoint2.New(PuddleVolume), out _);
+        _solutionContainerSystem.EnsureSolution(entity.Owner, entity.Comp.SolutionName, FixedPoint2.New(PuddleVolume), out _);
     }
 
-    private void OnSolutionUpdate(EntityUid uid, PuddleComponent component, SolutionChangedEvent args)
+    private void OnSolutionUpdate(Entity<PuddleComponent> entity, ref SolutionContainerChangedEvent args)
     {
-        if (args.Solution.Name != component.SolutionName)
+        if (args.SolutionId != entity.Comp.SolutionName)
             return;
 
         if (args.Solution.Volume <= 0)
         {
-            _deletionQueue.Add(uid);
+            _deletionQueue.Add(entity);
             return;
         }
 
-        _deletionQueue.Remove(uid);
-        UpdateSlip(uid, component, args.Solution);
-        UpdateSlow(uid, args.Solution);
-        UpdateEvaporation(uid, args.Solution);
-        UpdateAppearance(uid, component);
+        _deletionQueue.Remove(entity);
+        UpdateSlip(entity, entity.Comp, args.Solution);
+        UpdateSlow(entity, args.Solution);
+        UpdateEvaporation(entity, args.Solution);
+        UpdateAppearance(entity, entity.Comp);
     }
 
     private void UpdateAppearance(EntityUid uid, PuddleComponent? puddleComponent = null, AppearanceComponent? appearance = null)
@@ -281,7 +276,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
         var volume = FixedPoint2.Zero;
         Color color = Color.White;
 
-        if (_solutionContainerSystem.TryGetSolution(uid, puddleComponent.SolutionName, out var solution))
+        if (_solutionContainerSystem.ResolveSolution(uid, puddleComponent.SolutionName, ref puddleComponent.Solution, out var solution))
         {
             volume = solution.Volume / puddleComponent.OverflowVolume;
 
@@ -364,39 +359,31 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
         }
     }
 
-    private void HandlePuddleExamined(EntityUid uid, PuddleComponent component, ExaminedEvent args)
+    private void HandlePuddleExamined(Entity<PuddleComponent> entity, ref ExaminedEvent args)
     {
-        if (TryComp<StepTriggerComponent>(uid, out var slippery) && slippery.Active)
+        if (TryComp<StepTriggerComponent>(entity, out var slippery) && slippery.Active)
         {
             args.PushMarkup(Loc.GetString("puddle-component-examine-is-slipper-text"));
         }
 
-        if (HasComp<EvaporationComponent>(uid))
+        if (HasComp<EvaporationComponent>(entity) &&
+            _solutionContainerSystem.ResolveSolution(entity.Owner, entity.Comp.SolutionName, ref entity.Comp.Solution, out var solution))
         {
-            if (_solutionContainerSystem.TryGetSolution(uid, component.SolutionName, out var solution) &&
-                CanFullyEvaporate(solution))
-            {
+            if (CanFullyEvaporate(solution))
                 args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating"));
-            }
-            else if (solution?.GetTotalPrototypeQuantity(EvaporationReagents) > FixedPoint2.Zero)
-            {
+            else if (solution.GetTotalPrototypeQuantity(EvaporationReagents) > FixedPoint2.Zero)
                 args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating-partial"));
-            }
             else
-            {
                 args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating-no"));
-            }
         }
         else
-        {
             args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating-no"));
-        }
     }
 
-    private void OnAnchorChanged(EntityUid uid, PuddleComponent puddle, ref AnchorStateChangedEvent args)
+    private void OnAnchorChanged(Entity<PuddleComponent> entity, ref AnchorStateChangedEvent args)
     {
         if (!args.Anchored)
-            QueueDel(uid);
+            QueueDel(entity);
     }
 
     /// <summary>
@@ -407,8 +394,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
         if (!Resolve(uid, ref puddleComponent))
             return FixedPoint2.Zero;
 
-        return _solutionContainerSystem.TryGetSolution(uid, puddleComponent.SolutionName,
-            out var solution)
+        return _solutionContainerSystem.ResolveSolution(uid, puddleComponent.SolutionName, ref puddleComponent.Solution, out var solution)
             ? solution.Volume
             : FixedPoint2.Zero;
     }
@@ -432,14 +418,12 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
             return false;
 
         if (addedSolution.Volume == 0 ||
-            !_solutionContainerSystem.TryGetSolution(puddleUid, puddleComponent.SolutionName,
-                out var solution))
+            !_solutionContainerSystem.ResolveSolution(puddleUid, puddleComponent.SolutionName, ref puddleComponent.Solution))
         {
             return false;
         }
 
-        solution.AddSolution(addedSolution, _prototypeManager);
-        _solutionContainerSystem.UpdateChemicals(puddleUid, solution, true);
+        _solutionContainerSystem.AddSolution(puddleComponent.Solution.Value, addedSolution);
 
         if (checkForOverflow && IsOverflowing(puddleUid, puddleComponent))
         {
@@ -482,15 +466,14 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
     /// </summary>
     public Solution GetOverflowSolution(EntityUid uid, PuddleComponent? puddle = null)
     {
-        if (!Resolve(uid, ref puddle) || !_solutionContainerSystem.TryGetSolution(uid, puddle.SolutionName,
-                out var solution))
+        if (!Resolve(uid, ref puddle) || !_solutionContainerSystem.ResolveSolution(uid, puddle.SolutionName, ref puddle.Solution))
         {
             return new Solution(0);
         }
 
         // TODO: This is going to fail with struct solutions.
         var remaining = puddle.OverflowVolume;
-        var split = _solutionContainerSystem.SplitSolution(uid, solution, CurrentVolume(uid, puddle) - remaining);
+        var split = _solutionContainerSystem.SplitSolution(puddle.Solution.Value, CurrentVolume(uid, puddle) - remaining);
         return split;
     }
 
index 5459dacf0b912cf5ba26d07f616f7496e7314621..795655068f742a2d670e8272517ec131b6ab0faa 100644 (file)
@@ -1,7 +1,7 @@
-using System.Linq;
 using Content.Server.Administration.Logs;
 using Content.Server.Body.Components;
 using Content.Server.Body.Systems;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Chemistry.ReactionEffects;
 using Content.Server.Spreader;
 using Content.Shared.Chemistry;
@@ -21,6 +21,8 @@ using Robust.Shared.Physics.Systems;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Random;
 using Robust.Shared.Timing;
+using System.Linq;
+
 using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent;
 
 namespace Content.Server.Fluids.EntitySystems;
@@ -42,7 +44,7 @@ public sealed class SmokeSystem : EntitySystem
     [Dependency] private readonly ReactiveSystem _reactive = default!;
     [Dependency] private readonly SharedBroadphaseSystem _broadphase = default!;
     [Dependency] private readonly SharedPhysicsSystem _physics = default!;
-    [Dependency] private readonly SolutionContainerSystem _solutionSystem = default!;
+    [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
     [Dependency] private readonly TransformSystem _transform = default!;
 
     private EntityQuery<SmokeComponent> _smokeQuery;
@@ -59,6 +61,7 @@ public sealed class SmokeSystem : EntitySystem
         SubscribeLocalEvent<SmokeComponent, StartCollideEvent>(OnStartCollide);
         SubscribeLocalEvent<SmokeComponent, EndCollideEvent>(OnEndCollide);
         SubscribeLocalEvent<SmokeComponent, ReactionAttemptEvent>(OnReactionAttempt);
+        SubscribeLocalEvent<SmokeComponent, SolutionRelayEvent<ReactionAttemptEvent>>(OnReactionAttempt);
         SubscribeLocalEvent<SmokeComponent, SpreadNeighborsEvent>(OnSmokeSpread);
         SubscribeLocalEvent<SmokeAffectedComponent, EntityUnpausedEvent>(OnAffectedUnpaused);
     }
@@ -80,33 +83,33 @@ public sealed class SmokeSystem : EntitySystem
         }
     }
 
-    private void OnStartCollide(EntityUid uid, SmokeComponent component, ref StartCollideEvent args)
+    private void OnStartCollide(Entity<SmokeComponent> entity, ref StartCollideEvent args)
     {
         if (_smokeAffectedQuery.HasComponent(args.OtherEntity))
             return;
 
         var smokeAffected = AddComp<SmokeAffectedComponent>(args.OtherEntity);
-        smokeAffected.SmokeEntity = uid;
+        smokeAffected.SmokeEntity = entity;
         smokeAffected.NextSecond = _timing.CurTime + TimeSpan.FromSeconds(1);
     }
 
-    private void OnEndCollide(EntityUid uid, SmokeComponent component, ref EndCollideEvent args)
+    private void OnEndCollide(Entity<SmokeComponent> entity, ref EndCollideEvent args)
     {
         // if we are already in smoke, make sure the thing we are exiting is the current smoke we are in.
         if (_smokeAffectedQuery.TryGetComponent(args.OtherEntity, out var smokeAffectedComponent))
         {
-            if (smokeAffectedComponent.SmokeEntity != uid)
+            if (smokeAffectedComponent.SmokeEntity != entity.Owner)
                 return;
         }
 
-        var exists = Exists(uid);
+        var exists = Exists(entity);
 
         if (!TryComp<PhysicsComponent>(args.OtherEntity, out var body))
             return;
 
         foreach (var ent in _physics.GetContactingEntities(args.OtherEntity, body))
         {
-            if (exists && ent == uid)
+            if (exists && ent == entity.Owner)
                 continue;
 
             if (!_smokeQuery.HasComponent(ent))
@@ -121,51 +124,51 @@ public sealed class SmokeSystem : EntitySystem
             RemComp(args.OtherEntity, smokeAffectedComponent);
     }
 
-    private void OnAffectedUnpaused(EntityUid uid, SmokeAffectedComponent component, ref EntityUnpausedEvent args)
+    private void OnAffectedUnpaused(Entity<SmokeAffectedComponent> entity, ref EntityUnpausedEvent args)
     {
-        component.NextSecond += args.PausedTime;
+        entity.Comp.NextSecond += args.PausedTime;
     }
 
-    private void OnSmokeSpread(EntityUid uid, SmokeComponent component, ref SpreadNeighborsEvent args)
+    private void OnSmokeSpread(Entity<SmokeComponent> entity, ref SpreadNeighborsEvent args)
     {
-        if (component.SpreadAmount == 0 || !_solutionSystem.TryGetSolution(uid, SmokeComponent.SolutionName, out var solution))
+        if (entity.Comp.SpreadAmount == 0 || !_solutionContainerSystem.ResolveSolution(entity.Owner, SmokeComponent.SolutionName, ref entity.Comp.Solution, out var solution))
         {
-            RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
+            RemCompDeferred<ActiveEdgeSpreaderComponent>(entity);
             return;
         }
 
-        if (Prototype(uid) is not { } prototype)
+        if (Prototype(entity) is not { } prototype)
         {
-            RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
+            RemCompDeferred<ActiveEdgeSpreaderComponent>(entity);
             return;
         }
 
         if (!args.NeighborFreeTiles.Any())
             return;
 
-        TryComp<TimedDespawnComponent>(uid, out var timer);
+        TryComp<TimedDespawnComponent>(entity, out var timer);
 
         // wtf is the logic behind any of this.
-        var smokePerSpread = component.SpreadAmount / Math.Max(1, args.NeighborFreeTiles.Count);
+        var smokePerSpread = entity.Comp.SpreadAmount / Math.Max(1, args.NeighborFreeTiles.Count);
         foreach (var neighbor in args.NeighborFreeTiles)
         {
             var coords = neighbor.Grid.GridTileToLocal(neighbor.Tile);
             var ent = Spawn(prototype.ID, coords);
             var spreadAmount = Math.Max(0, smokePerSpread);
-            component.SpreadAmount -= args.NeighborFreeTiles.Count();
+            entity.Comp.SpreadAmount -= args.NeighborFreeTiles.Count();
 
-            StartSmoke(ent, solution.Clone(), timer?.Lifetime ?? component.Duration, spreadAmount);
+            StartSmoke(ent, solution.Clone(), timer?.Lifetime ?? entity.Comp.Duration, spreadAmount);
 
-            if (component.SpreadAmount == 0)
+            if (entity.Comp.SpreadAmount == 0)
             {
-                RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
+                RemCompDeferred<ActiveEdgeSpreaderComponent>(entity);
                 break;
             }
         }
 
         args.Updates--;
 
-        if (args.NeighborFreeTiles.Count > 0 || args.Neighbors.Count == 0 || component.SpreadAmount < 1)
+        if (args.NeighborFreeTiles.Count > 0 || args.Neighbors.Count == 0 || entity.Comp.SpreadAmount < 1)
             return;
 
         // We have no more neighbours to spread to. So instead we will randomly distribute our volume to neighbouring smoke tiles.
@@ -179,21 +182,21 @@ public sealed class SmokeSystem : EntitySystem
                 continue;
 
             smoke.SpreadAmount++;
-            component.SpreadAmount--;
+            entity.Comp.SpreadAmount--;
             EnsureComp<ActiveEdgeSpreaderComponent>(neighbor);
 
-            if (component.SpreadAmount == 0)
+            if (entity.Comp.SpreadAmount == 0)
             {
-                RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
+                RemCompDeferred<ActiveEdgeSpreaderComponent>(entity);
                 break;
             }
         }
 
     }
 
-    private void OnReactionAttempt(EntityUid uid, SmokeComponent component, ReactionAttemptEvent args)
+    private void OnReactionAttempt(Entity<SmokeComponent> entity, ref ReactionAttemptEvent args)
     {
-        if (args.Solution.Name != SmokeComponent.SolutionName)
+        if (args.Cancelled)
             return;
 
         // Prevent smoke/foam fork bombs (smoke creating more smoke).
@@ -201,12 +204,18 @@ public sealed class SmokeSystem : EntitySystem
         {
             if (effect is AreaReactionEffect)
             {
-                args.Cancel();
+                args.Cancelled = true;
                 return;
             }
         }
     }
 
+    private void OnReactionAttempt(Entity<SmokeComponent> entity, ref SolutionRelayEvent<ReactionAttemptEvent> args)
+    {
+        if (args.Name == SmokeComponent.SolutionName)
+            OnReactionAttempt(entity, ref args.Event);
+    }
+
     /// <summary>
     /// Sets up a smoke component for spreading.
     /// </summary>
@@ -245,14 +254,14 @@ public sealed class SmokeSystem : EntitySystem
         if (!Resolve(smokeUid, ref component))
             return;
 
-        if (!_solutionSystem.TryGetSolution(smokeUid, SmokeComponent.SolutionName, out var solution) ||
+        if (!_solutionContainerSystem.ResolveSolution(smokeUid, SmokeComponent.SolutionName, ref component.Solution, out var solution) ||
             solution.Contents.Count == 0)
         {
             return;
         }
 
         ReactWithEntity(entity, smokeUid, solution, component);
-        UpdateVisuals(smokeUid);
+        UpdateVisuals((smokeUid, component));
     }
 
     private void ReactWithEntity(EntityUid entity, EntityUid smokeUid, Solution solution, SmokeComponent? component = null)
@@ -263,11 +272,14 @@ public sealed class SmokeSystem : EntitySystem
         if (!TryComp<BloodstreamComponent>(entity, out var bloodstream))
             return;
 
-        var blockIngestion =  _internals.AreInternalsWorking(entity);
+        if (!_solutionContainerSystem.ResolveSolution(entity, bloodstream.ChemicalSolutionName, ref bloodstream.ChemicalSolution, out var chemSolution) || chemSolution.AvailableVolume <= 0)
+            return;
+
+        var blockIngestion = _internals.AreInternalsWorking(entity);
 
         var cloneSolution = solution.Clone();
         var availableTransfer = FixedPoint2.Min(cloneSolution.Volume, component.TransferRate);
-        var transferAmount = FixedPoint2.Min(availableTransfer, bloodstream.ChemicalSolution.AvailableVolume);
+        var transferAmount = FixedPoint2.Min(availableTransfer, chemSolution.AvailableVolume);
         var transferSolution = cloneSolution.SplitSolution(transferAmount);
 
         foreach (var reagentQuantity in transferSolution.Contents.ToArray())
@@ -296,7 +308,7 @@ public sealed class SmokeSystem : EntitySystem
         if (!Resolve(uid, ref component, ref xform))
             return;
 
-        if (!_solutionSystem.TryGetSolution(uid, SmokeComponent.SolutionName, out var solution) || !solution.Any())
+        if (!_solutionContainerSystem.ResolveSolution(uid, SmokeComponent.SolutionName, ref component.Solution, out var solution) || !solution.Any())
             return;
 
         if (!_mapManager.TryGetGrid(xform.GridUid, out var mapGrid))
@@ -317,29 +329,30 @@ public sealed class SmokeSystem : EntitySystem
     /// <summary>
     /// Adds the specified solution to the relevant smoke solution.
     /// </summary>
-    private void TryAddSolution(EntityUid uid, Solution solution)
+    private void TryAddSolution(Entity<SmokeComponent?> smoke, Solution solution)
     {
         if (solution.Volume == FixedPoint2.Zero)
             return;
 
-        if (!_solutionSystem.TryGetSolution(uid, SmokeComponent.SolutionName, out var solutionArea))
+        if (!Resolve(smoke, ref smoke.Comp))
             return;
 
-        var addSolution =
-            solution.SplitSolution(FixedPoint2.Min(solution.Volume, solutionArea.AvailableVolume));
+        if (!_solutionContainerSystem.ResolveSolution(smoke.Owner, SmokeComponent.SolutionName, ref smoke.Comp.Solution, out var solutionArea))
+            return;
 
-        _solutionSystem.TryAddSolution(uid, solutionArea, addSolution);
+        var addSolution = solution.SplitSolution(FixedPoint2.Min(solution.Volume, solutionArea.AvailableVolume));
+        _solutionContainerSystem.TryAddSolution(smoke.Comp.Solution.Value, addSolution);
 
-        UpdateVisuals(uid);
+        UpdateVisuals(smoke);
     }
 
-    private void UpdateVisuals(EntityUid uid)
+    private void UpdateVisuals(Entity<SmokeComponent?, AppearanceComponent?> smoke)
     {
-        if (!TryComp(uid, out AppearanceComponent? appearance) ||
-            !_solutionSystem.TryGetSolution(uid, SmokeComponent.SolutionName, out var solution))
+        if (!Resolve(smoke, ref smoke.Comp1, ref smoke.Comp2) ||
+            !_solutionContainerSystem.ResolveSolution(smoke.Owner, SmokeComponent.SolutionName, ref smoke.Comp1.Solution, out var solution))
             return;
 
         var color = solution.GetColor(_prototype);
-        _appearance.SetData(uid, SmokeVisuals.Color, color, appearance);
+        _appearance.SetData(smoke.Owner, SmokeVisuals.Color, color, smoke.Comp2);
     }
 }
index 1b87b3ef246d692c5063294b4878939ba7b4563b..5acd2c91181a5918f84a1dfda617d2d27497816a 100644 (file)
@@ -1,22 +1,21 @@
-using System.Numerics;
 using Content.Server.Chemistry.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Chemistry.EntitySystems;
 using Content.Server.Cooldown;
 using Content.Server.Extinguisher;
 using Content.Server.Fluids.Components;
 using Content.Server.Gravity;
 using Content.Server.Popups;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Cooldown;
 using Content.Shared.FixedPoint;
 using Content.Shared.Interaction;
 using Content.Shared.Vapor;
 using Robust.Server.GameObjects;
-using Robust.Shared.Audio;
 using Robust.Shared.Audio.Systems;
 using Robust.Shared.Physics.Components;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Timing;
+using System.Numerics;
 
 namespace Content.Server.Fluids.EntitySystems;
 
@@ -40,23 +39,23 @@ public sealed class SpraySystem : EntitySystem
         SubscribeLocalEvent<SprayComponent, AfterInteractEvent>(OnAfterInteract, after: new[] { typeof(FireExtinguisherSystem) });
     }
 
-    private void OnAfterInteract(EntityUid uid, SprayComponent component, AfterInteractEvent args)
+    private void OnAfterInteract(Entity<SprayComponent> entity, ref AfterInteractEvent args)
     {
         if (args.Handled)
             return;
 
         args.Handled = true;
 
-        if (!_solutionContainer.TryGetSolution(uid, SprayComponent.SolutionName, out var solution))
+        if (!_solutionContainer.TryGetSolution(entity.Owner, SprayComponent.SolutionName, out var soln, out var solution))
             return;
 
         var ev = new SprayAttemptEvent(args.User);
-        RaiseLocalEvent(uid, ev);
+        RaiseLocalEvent(entity, ev);
         if (ev.Cancelled)
             return;
 
         var curTime = _gameTiming.CurTime;
-        if (TryComp<ItemCooldownComponent>(uid, out var cooldown)
+        if (TryComp<ItemCooldownComponent>(entity, out var cooldown)
             && curTime < cooldown.CooldownEnd)
         {
             return;
@@ -64,8 +63,7 @@ public sealed class SpraySystem : EntitySystem
 
         if (solution.Volume <= 0)
         {
-            _popupSystem.PopupEntity(Loc.GetString("spray-component-is-empty-message"), uid,
-                args.User);
+            _popupSystem.PopupEntity(Loc.GetString("spray-component-is-empty-message"), entity.Owner, args.User);
             return;
         }
 
@@ -82,9 +80,9 @@ public sealed class SpraySystem : EntitySystem
         var diffNorm = diffPos.Normalized();
         var diffLength = diffPos.Length();
 
-        if (diffLength > component.SprayDistance)
+        if (diffLength > entity.Comp.SprayDistance)
         {
-            diffLength = component.SprayDistance;
+            diffLength = entity.Comp.SprayDistance;
         }
 
         var diffAngle = diffNorm.ToAngle();
@@ -93,8 +91,8 @@ public sealed class SpraySystem : EntitySystem
         var threeQuarters = diffNorm * 0.75f;
         var quarter = diffNorm * 0.25f;
 
-        var amount = Math.Max(Math.Min((solution.Volume / component.TransferAmount).Int(), component.VaporAmount), 1);
-        var spread = component.VaporSpread / amount;
+        var amount = Math.Max(Math.Min((solution.Volume / entity.Comp.TransferAmount).Int(), entity.Comp.VaporAmount), 1);
+        var spread = entity.Comp.VaporSpread / amount;
         // TODO: Just use usedelay homie.
         var cooldownTime = 0f;
 
@@ -108,18 +106,18 @@ public sealed class SpraySystem : EntitySystem
                 .Offset((diffNorm + rotation.ToVec()).Normalized() * diffLength + quarter);
 
             var distance = (target.Position - userMapPos.Position).Length();
-            if (distance > component.SprayDistance)
-                target = userMapPos.Offset(diffNorm * component.SprayDistance);
+            if (distance > entity.Comp.SprayDistance)
+                target = userMapPos.Offset(diffNorm * entity.Comp.SprayDistance);
 
-            var adjustedSolutionAmount = component.TransferAmount / component.VaporAmount;
-            var newSolution = _solutionContainer.SplitSolution(uid, solution, adjustedSolutionAmount);
+            var adjustedSolutionAmount = entity.Comp.TransferAmount / entity.Comp.VaporAmount;
+            var newSolution = _solutionContainer.SplitSolution(soln.Value, adjustedSolutionAmount);
 
             if (newSolution.Volume <= FixedPoint2.Zero)
                 break;
 
             // Spawn the vapor cloud onto the grid/map the user is present on. Offset the start position based on how far the target destination is.
             var vaporPos = userMapPos.Offset(distance < 1 ? quarter : threeQuarters);
-            var vapor = Spawn(component.SprayedPrototype, vaporPos);
+            var vapor = Spawn(entity.Comp.SprayedPrototype, vaporPos);
             var vaporXform = xformQuery.GetComponent(vapor);
 
             _transform.SetWorldRotation(vaporXform, rotation);
@@ -137,22 +135,21 @@ public sealed class SpraySystem : EntitySystem
 
             // impulse direction is defined in world-coordinates, not local coordinates
             var impulseDirection = rotation.ToVec();
-            var time = diffLength / component.SprayVelocity;
+            var time = diffLength / entity.Comp.SprayVelocity;
             cooldownTime = MathF.Max(time, cooldownTime);
 
-            _vapor.Start(ent, vaporXform, impulseDirection * diffLength, component.SprayVelocity, target, time, args.User);
+            _vapor.Start(ent, vaporXform, impulseDirection * diffLength, entity.Comp.SprayVelocity, target, time, args.User);
 
             if (TryComp<PhysicsComponent>(args.User, out var body))
             {
                 if (_gravity.IsWeightless(args.User, body))
-                    _physics.ApplyLinearImpulse(args.User, -impulseDirection.Normalized() * component.PushbackAmount, body: body);
+                    _physics.ApplyLinearImpulse(args.User, -impulseDirection.Normalized() * entity.Comp.PushbackAmount, body: body);
             }
         }
 
-        _audio.PlayPvs(component.SpraySound, uid, component.SpraySound.Params.WithVariation(0.125f));
+        _audio.PlayPvs(entity.Comp.SpraySound, entity, entity.Comp.SpraySound.Params.WithVariation(0.125f));
 
-        RaiseLocalEvent(uid,
-            new RefreshItemCooldownEvent(curTime, curTime + TimeSpan.FromSeconds(cooldownTime)), true);
+        RaiseLocalEvent(entity, new RefreshItemCooldownEvent(curTime, curTime + TimeSpan.FromSeconds(cooldownTime)), true);
     }
 }
 
index 58eed00c492bfef408523fc66cf4fd0ef9950f7f..2b7f0060bc4f6a2299ac3d73799e2b28a78ef8e4 100644 (file)
@@ -1,16 +1,15 @@
 using Content.Server.Administration.Logs;
-using Content.Shared.Popups;
-using Content.Shared.Item;
-using Content.Shared.Glue;
-using Content.Shared.Interaction;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Nutrition.EntitySystems;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Database;
+using Content.Shared.Glue;
 using Content.Shared.Hands;
-using Robust.Shared.Timing;
+using Content.Shared.Interaction;
 using Content.Shared.Interaction.Components;
-using Robust.Shared.Audio;
+using Content.Shared.Item;
+using Content.Shared.Popups;
 using Robust.Shared.Audio.Systems;
+using Robust.Shared.Timing;
 
 namespace Content.Server.Glue;
 
@@ -33,7 +32,7 @@ public sealed class GlueSystem : SharedGlueSystem
     }
 
     // When glue bottle is used on item it will apply the glued and unremoveable components.
-    private void OnInteract(EntityUid uid, GlueComponent component, AfterInteractEvent args)
+    private void OnInteract(Entity<GlueComponent> entity, ref AfterInteractEvent args)
     {
         if (args.Handled)
             return;
@@ -41,10 +40,10 @@ public sealed class GlueSystem : SharedGlueSystem
         if (!args.CanReach || args.Target is not { Valid: true } target)
             return;
 
-        if (TryGlue(uid, component, target, args.User))
+        if (TryGlue(entity, target, args.User))
         {
             args.Handled = true;
-            _audio.PlayPvs(component.Squeeze, uid);
+            _audio.PlayPvs(entity.Comp.Squeeze, entity);
             _popup.PopupEntity(Loc.GetString("glue-success", ("target", target)), args.User, args.User, PopupType.Medium);
         }
         else
@@ -53,7 +52,7 @@ public sealed class GlueSystem : SharedGlueSystem
         }
     }
 
-    private bool TryGlue(EntityUid uid, GlueComponent component, EntityUid target, EntityUid actor)
+    private bool TryGlue(Entity<GlueComponent> glue, EntityUid target, EntityUid actor)
     {
         // if item is glued then don't apply glue again so it can be removed for reasonable time
         if (HasComp<GluedComponent>(target) || !HasComp<ItemComponent>(target))
@@ -61,13 +60,13 @@ public sealed class GlueSystem : SharedGlueSystem
             return false;
         }
 
-        if (HasComp<ItemComponent>(target) && _solutionContainer.TryGetSolution(uid, component.Solution, out var solution))
+        if (HasComp<ItemComponent>(target) && _solutionContainer.TryGetSolution(glue.Owner, glue.Comp.Solution, out _, out var solution))
         {
-            var quantity = solution.RemoveReagent(component.Reagent, component.ConsumptionUnit);
+            var quantity = solution.RemoveReagent(glue.Comp.Reagent, glue.Comp.ConsumptionUnit);
             if (quantity > 0)
             {
-                EnsureComp<GluedComponent>(target).Duration = quantity.Double() * component.DurationPerUnit;
-                _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(actor):actor} glued {ToPrettyString(target):subject} with {ToPrettyString(uid):tool}");
+                EnsureComp<GluedComponent>(target).Duration = quantity.Double() * glue.Comp.DurationPerUnit;
+                _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(actor):actor} glued {ToPrettyString(target):subject} with {ToPrettyString(glue.Owner):tool}");
                 return true;
             }
         }
@@ -90,17 +89,17 @@ public sealed class GlueSystem : SharedGlueSystem
         }
     }
 
-    private void OnGluedInit(EntityUid uid, GluedComponent component, ComponentInit args)
+    private void OnGluedInit(Entity<GluedComponent> entity, ref ComponentInit args)
     {
-        var meta = MetaData(uid);
+        var meta = MetaData(entity);
         var name = meta.EntityName;
-        component.BeforeGluedEntityName = meta.EntityName;
-        _metaData.SetEntityName(uid, Loc.GetString("glued-name-prefix", ("target", name)));
+        entity.Comp.BeforeGluedEntityName = meta.EntityName;
+        _metaData.SetEntityName(entity.Owner, Loc.GetString("glued-name-prefix", ("target", name)));
     }
 
-    private void OnHandPickUp(EntityUid uid, GluedComponent component, GotEquippedHandEvent args)
+    private void OnHandPickUp(Entity<GluedComponent> entity, ref GotEquippedHandEvent args)
     {
-        EnsureComp<UnremoveableComponent>(uid);
-        component.Until = _timing.CurTime + component.Duration;
+        EnsureComp<UnremoveableComponent>(entity);
+        entity.Comp.Until = _timing.CurTime + entity.Comp.Duration;
     }
 }
index e4159f89ef8f808cf163b5846b902d0fcffc1f14..c7e1824c40b3b1c2ab6c520633a5b145682aab08 100644 (file)
@@ -1,9 +1,8 @@
-using System.Linq;
 using Content.Server.Body.Systems;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Construction;
 using Content.Server.DeviceLinking.Events;
 using Content.Server.DeviceLinking.Systems;
-using Content.Server.DeviceNetwork;
 using Content.Server.Hands.Systems;
 using Content.Server.Kitchen.Components;
 using Content.Server.Power.Components;
@@ -31,6 +30,7 @@ using Robust.Shared.Audio;
 using Robust.Shared.Audio.Systems;
 using Robust.Shared.Containers;
 using Robust.Shared.Player;
+using System.Linq;
 
 namespace Content.Server.Kitchen.EntitySystems
 {
@@ -57,7 +57,7 @@ namespace Content.Server.Kitchen.EntitySystems
 
             SubscribeLocalEvent<MicrowaveComponent, ComponentInit>(OnInit);
             SubscribeLocalEvent<MicrowaveComponent, MapInitEvent>(OnMapInit);
-            SubscribeLocalEvent<MicrowaveComponent, SolutionChangedEvent>(OnSolutionChange);
+            SubscribeLocalEvent<MicrowaveComponent, SolutionContainerChangedEvent>(OnSolutionChange);
             SubscribeLocalEvent<MicrowaveComponent, InteractUsingEvent>(OnInteractUsing, after: new[] { typeof(AnchorableSystem) });
             SubscribeLocalEvent<MicrowaveComponent, BreakageEventArgs>(OnBreak);
             SubscribeLocalEvent<MicrowaveComponent, PowerChangedEvent>(OnPowerChanged);
@@ -76,21 +76,21 @@ namespace Content.Server.Kitchen.EntitySystems
             SubscribeLocalEvent<ActiveMicrowaveComponent, ComponentShutdown>(OnCookStop);
         }
 
-        private void OnCookStart(EntityUid uid, ActiveMicrowaveComponent component, ComponentStartup args)
+        private void OnCookStart(Entity<ActiveMicrowaveComponent> ent, ref ComponentStartup args)
         {
-            if (!TryComp<MicrowaveComponent>(uid, out var microwaveComponent))
+            if (!TryComp<MicrowaveComponent>(ent, out var microwaveComponent))
                 return;
-            SetAppearance(uid, MicrowaveVisualState.Cooking, microwaveComponent);
+            SetAppearance(ent.Owner, MicrowaveVisualState.Cooking, microwaveComponent);
 
             microwaveComponent.PlayingStream =
-                _audio.PlayPvs(microwaveComponent.LoopingSound, uid, AudioParams.Default.WithLoop(true).WithMaxDistance(5)).Value.Entity;
+                _audio.PlayPvs(microwaveComponent.LoopingSound, ent, AudioParams.Default.WithLoop(true).WithMaxDistance(5)).Value.Entity;
         }
 
-        private void OnCookStop(EntityUid uid, ActiveMicrowaveComponent component, ComponentShutdown args)
+        private void OnCookStop(Entity<ActiveMicrowaveComponent> ent, ref ComponentShutdown args)
         {
-            if (!TryComp<MicrowaveComponent>(uid, out var microwaveComponent))
+            if (!TryComp<MicrowaveComponent>(ent, out var microwaveComponent))
                 return;
-            SetAppearance(uid, MicrowaveVisualState.Idle, microwaveComponent);
+            SetAppearance(ent.Owner, MicrowaveVisualState.Idle, microwaveComponent);
 
             microwaveComponent.PlayingStream = _audio.Stop(microwaveComponent.PlayingStream);
         }
@@ -111,12 +111,13 @@ namespace Content.Server.Kitchen.EntitySystems
 
                 if (!TryComp<SolutionContainerManagerComponent>(entity, out var solutions))
                     continue;
-                foreach (var (_, solution) in solutions.Solutions)
+                foreach (var (_, soln) in _solutionContainer.EnumerateSolutions((entity, solutions)))
                 {
+                    var solution = soln.Comp.Solution;
                     if (solution.Temperature > component.TemperatureUpperThreshold)
                         continue;
 
-                    _solutionContainer.AddThermalEnergy(entity, solution, heatToAdd);
+                    _solutionContainer.AddThermalEnergy(soln, heatToAdd);
                 }
             }
         }
@@ -134,8 +135,9 @@ namespace Content.Server.Kitchen.EntitySystems
                     continue;
 
                 // go over every solution
-                foreach (var (_, solution) in solMan.Solutions)
+                foreach (var (_, soln) in _solutionContainer.EnumerateSolutions((item, solMan)))
                 {
+                    var solution = soln.Comp.Solution;
                     foreach (var (reagent, _) in recipe.IngredientsReagents)
                     {
                         // removed everything
@@ -154,7 +156,7 @@ namespace Content.Server.Kitchen.EntitySystems
                             totalReagentsToRemove[reagent] -= quant;
                         }
 
-                        _solutionContainer.RemoveReagent(item, solution, reagent, quant);
+                        _solutionContainer.RemoveReagent(soln, reagent, quant);
                     }
                 }
             }
@@ -193,7 +195,7 @@ namespace Content.Server.Kitchen.EntitySystems
             _deviceLink.EnsureSinkPorts(ent, ent.Comp.OnPort);
         }
 
-        private void OnSuicide(EntityUid uid, MicrowaveComponent component, SuicideEvent args)
+        private void OnSuicide(Entity<MicrowaveComponent> ent, ref SuicideEvent args)
         {
             if (args.Handled)
                 return;
@@ -208,7 +210,7 @@ namespace Content.Server.Kitchen.EntitySystems
 
                 foreach (var part in headSlots)
                 {
-                    _container.Insert(part.Id, component.Storage);
+                    _container.Insert(part.Id, ent.Comp.Storage);
                     headCount++;
                 }
             }
@@ -224,73 +226,73 @@ namespace Content.Server.Kitchen.EntitySystems
             _popupSystem.PopupEntity(othersMessage, victim, Filter.PvsExcept(victim), true);
             _popupSystem.PopupEntity(selfMessage, victim, victim);
 
-            _audio.PlayPvs(component.ClickSound, uid, AudioParams.Default.WithVolume(-2));
-            component.CurrentCookTimerTime = 10;
-            Wzhzhzh(uid, component, args.Victim);
-            UpdateUserInterfaceState(uid, component);
+            _audio.PlayPvs(ent.Comp.ClickSound, ent.Owner, AudioParams.Default.WithVolume(-2));
+            ent.Comp.CurrentCookTimerTime = 10;
+            Wzhzhzh(ent.Owner, ent.Comp, args.Victim);
+            UpdateUserInterfaceState(ent.Owner, ent.Comp);
         }
 
-        private void OnSolutionChange(EntityUid uid, MicrowaveComponent component, SolutionChangedEvent args)
+        private void OnSolutionChange(Entity<MicrowaveComponent> ent, ref SolutionContainerChangedEvent args)
         {
-            UpdateUserInterfaceState(uid, component);
+            UpdateUserInterfaceState(ent, ent.Comp);
         }
 
-        private void OnInteractUsing(EntityUid uid, MicrowaveComponent component, InteractUsingEvent args)
+        private void OnInteractUsing(Entity<MicrowaveComponent> ent, ref InteractUsingEvent args)
         {
             if (args.Handled)
                 return;
-            if (!(TryComp<ApcPowerReceiverComponent>(uid, out var apc) && apc.Powered))
+            if (!(TryComp<ApcPowerReceiverComponent>(ent, out var apc) && apc.Powered))
             {
-                _popupSystem.PopupEntity(Loc.GetString("microwave-component-interact-using-no-power"), uid, args.User);
+                _popupSystem.PopupEntity(Loc.GetString("microwave-component-interact-using-no-power"), ent, args.User);
                 return;
             }
 
-            if (component.Broken)
+            if (ent.Comp.Broken)
             {
-                _popupSystem.PopupEntity(Loc.GetString("microwave-component-interact-using-broken"), uid, args.User);
+                _popupSystem.PopupEntity(Loc.GetString("microwave-component-interact-using-broken"), ent, args.User);
                 return;
             }
 
             if (!HasComp<ItemComponent>(args.Used))
             {
-                _popupSystem.PopupEntity(Loc.GetString("microwave-component-interact-using-transfer-fail"), uid, args.User);
+                _popupSystem.PopupEntity(Loc.GetString("microwave-component-interact-using-transfer-fail"), ent, args.User);
                 return;
             }
 
             args.Handled = true;
-            _handsSystem.TryDropIntoContainer(args.User, args.Used, component.Storage);
-            UpdateUserInterfaceState(uid, component);
+            _handsSystem.TryDropIntoContainer(args.User, args.Used, ent.Comp.Storage);
+            UpdateUserInterfaceState(ent, ent.Comp);
         }
 
-        private void OnBreak(EntityUid uid, MicrowaveComponent component, BreakageEventArgs args)
+        private void OnBreak(Entity<MicrowaveComponent> ent, ref BreakageEventArgs args)
         {
-            component.Broken = true;
-            SetAppearance(uid, MicrowaveVisualState.Broken, component);
-            RemComp<ActiveMicrowaveComponent>(uid);
-            _sharedContainer.EmptyContainer(component.Storage);
-            UpdateUserInterfaceState(uid, component);
+            ent.Comp.Broken = true;
+            SetAppearance(ent, MicrowaveVisualState.Broken, ent.Comp);
+            RemComp<ActiveMicrowaveComponent>(ent);
+            _sharedContainer.EmptyContainer(ent.Comp.Storage);
+            UpdateUserInterfaceState(ent, ent.Comp);
         }
 
-        private void OnPowerChanged(EntityUid uid, MicrowaveComponent component, ref PowerChangedEvent args)
+        private void OnPowerChanged(Entity<MicrowaveComponent> ent, ref PowerChangedEvent args)
         {
             if (!args.Powered)
             {
-                SetAppearance(uid, MicrowaveVisualState.Idle, component);
-                RemComp<ActiveMicrowaveComponent>(uid);
-                _sharedContainer.EmptyContainer(component.Storage);
+                SetAppearance(ent, MicrowaveVisualState.Idle, ent.Comp);
+                RemComp<ActiveMicrowaveComponent>(ent);
+                _sharedContainer.EmptyContainer(ent.Comp.Storage);
             }
-            UpdateUserInterfaceState(uid, component);
+            UpdateUserInterfaceState(ent, ent.Comp);
         }
 
-        private void OnRefreshParts(EntityUid uid, MicrowaveComponent component, RefreshPartsEvent args)
+        private void OnRefreshParts(Entity<MicrowaveComponent> ent, ref RefreshPartsEvent args)
         {
-            var cookRating = args.PartRatings[component.MachinePartCookTimeMultiplier];
-            component.CookTimeMultiplier = MathF.Pow(component.CookTimeScalingConstant, cookRating - 1);
+            var cookRating = args.PartRatings[ent.Comp.MachinePartCookTimeMultiplier];
+            ent.Comp.CookTimeMultiplier = MathF.Pow(ent.Comp.CookTimeScalingConstant, cookRating - 1);
         }
 
-        private void OnUpgradeExamine(EntityUid uid, MicrowaveComponent component, UpgradeExamineEvent args)
+        private void OnUpgradeExamine(Entity<MicrowaveComponent> ent, ref UpgradeExamineEvent args)
         {
-            args.AddPercentageUpgrade("microwave-component-upgrade-cook-time", component.CookTimeMultiplier);
+            args.AddPercentageUpgrade("microwave-component-upgrade-cook-time", ent.Comp.CookTimeMultiplier);
         }
 
         private void OnSignalReceived(Entity<MicrowaveComponent> ent, ref SignalReceivedEvent args)
@@ -391,8 +393,9 @@ namespace Content.Server.Kitchen.EntitySystems
                 if (!TryComp<SolutionContainerManagerComponent>(item, out var solMan))
                     continue;
 
-                foreach (var (_, solution) in solMan.Solutions)
+                foreach (var (_, soln) in _solutionContainer.EnumerateSolutions((item, solMan)))
                 {
+                    var solution = soln.Comp.Solution;
                     foreach (var (reagent, quantity) in solution.Contents)
                     {
                         if (reagentDict.ContainsKey(reagent.Prototype))
@@ -489,38 +492,38 @@ namespace Content.Server.Kitchen.EntitySystems
         }
 
         #region ui
-        private void OnEjectMessage(EntityUid uid, MicrowaveComponent component, MicrowaveEjectMessage args)
+        private void OnEjectMessage(Entity<MicrowaveComponent> ent, ref MicrowaveEjectMessage args)
         {
-            if (!HasContents(component) || HasComp<ActiveMicrowaveComponent>(uid))
+            if (!HasContents(ent.Comp) || HasComp<ActiveMicrowaveComponent>(ent))
                 return;
 
-            _sharedContainer.EmptyContainer(component.Storage);
-            _audio.PlayPvs(component.ClickSound, uid, AudioParams.Default.WithVolume(-2));
-            UpdateUserInterfaceState(uid, component);
+            _sharedContainer.EmptyContainer(ent.Comp.Storage);
+            _audio.PlayPvs(ent.Comp.ClickSound, ent, AudioParams.Default.WithVolume(-2));
+            UpdateUserInterfaceState(ent, ent.Comp);
         }
 
-        private void OnEjectIndex(EntityUid uid, MicrowaveComponent component, MicrowaveEjectSolidIndexedMessage args)
+        private void OnEjectIndex(Entity<MicrowaveComponent> ent, ref MicrowaveEjectSolidIndexedMessage args)
         {
-            if (!HasContents(component) || HasComp<ActiveMicrowaveComponent>(uid))
+            if (!HasContents(ent.Comp) || HasComp<ActiveMicrowaveComponent>(ent))
                 return;
 
-            _sharedContainer.Remove(EntityManager.GetEntity(args.EntityID), component.Storage);
-            UpdateUserInterfaceState(uid, component);
+            _sharedContainer.Remove(EntityManager.GetEntity(args.EntityID), ent.Comp.Storage);
+            UpdateUserInterfaceState(ent, ent.Comp);
         }
 
-        private void OnSelectTime(EntityUid uid, MicrowaveComponent comp, MicrowaveSelectCookTimeMessage args)
+        private void OnSelectTime(Entity<MicrowaveComponent> ent, ref MicrowaveSelectCookTimeMessage args)
         {
-            if (!HasContents(comp) || HasComp<ActiveMicrowaveComponent>(uid) || !(TryComp<ApcPowerReceiverComponent>(uid, out var apc) && apc.Powered))
+            if (!HasContents(ent.Comp) || HasComp<ActiveMicrowaveComponent>(ent) || !(TryComp<ApcPowerReceiverComponent>(ent, out var apc) && apc.Powered))
                 return;
 
             // some validation to prevent trollage
-            if (args.NewCookTime % 5 != 0 || args.NewCookTime > comp.MaxCookTime)
+            if (args.NewCookTime % 5 != 0 || args.NewCookTime > ent.Comp.MaxCookTime)
                 return;
 
-            comp.CurrentCookTimeButtonIndex = args.ButtonIndex;
-            comp.CurrentCookTimerTime = args.NewCookTime;
-            _audio.PlayPvs(comp.ClickSound, uid, AudioParams.Default.WithVolume(-2));
-            UpdateUserInterfaceState(uid, comp);
+            ent.Comp.CurrentCookTimeButtonIndex = args.ButtonIndex;
+            ent.Comp.CurrentCookTimerTime = args.NewCookTime;
+            _audio.PlayPvs(ent.Comp.ClickSound, ent, AudioParams.Default.WithVolume(-2));
+            UpdateUserInterfaceState(ent, ent.Comp);
         }
         #endregion
     }
index ba6795ad9f52af4b4ba24780d4bc17eac62fc6cc..cd9a82a0727cfea645a50059ae90f431f9a8ac18 100644 (file)
@@ -1,11 +1,10 @@
-using System.Linq;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Construction;
 using Content.Server.Kitchen.Components;
 using Content.Server.Power.Components;
 using Content.Server.Power.EntitySystems;
 using Content.Server.Stack;
 using Content.Shared.Chemistry.Components;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Containers.ItemSlots;
 using Content.Shared.FixedPoint;
 using Content.Shared.Interaction;
@@ -19,6 +18,7 @@ using Robust.Shared.Audio;
 using Robust.Shared.Audio.Systems;
 using Robust.Shared.Containers;
 using Robust.Shared.Timing;
+using System.Linq;
 
 namespace Content.Server.Kitchen.EntitySystems
 {
@@ -26,7 +26,7 @@ namespace Content.Server.Kitchen.EntitySystems
     internal sealed class ReagentGrinderSystem : EntitySystem
     {
         [Dependency] private readonly IGameTiming _timing = default!;
-        [Dependency] private readonly SolutionContainerSystem _solutionsSystem = default!;
+        [Dependency] private readonly SolutionContainerSystem _solutionContainersSystem = default!;
         [Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!;
         [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
         [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
@@ -70,7 +70,7 @@ namespace Content.Server.Kitchen.EntitySystems
 
                 var inputContainer = _containerSystem.EnsureContainer<Container>(uid, SharedReagentGrinder.InputContainerId);
                 var outputContainer = _itemSlotsSystem.GetItemOrNull(uid, SharedReagentGrinder.BeakerSlotId);
-                if (outputContainer is null || !_solutionsSystem.TryGetFitsInDispenser(outputContainer.Value, out var containerSolution))
+                if (outputContainer is null || !_solutionContainersSystem.TryGetFitsInDispenser(outputContainer.Value, out var containerSoln, out var containerSolution))
                     continue;
 
                 foreach (var item in inputContainer.ContainedEntities.ToList())
@@ -108,7 +108,7 @@ namespace Content.Server.Kitchen.EntitySystems
                         QueueDel(item);
                     }
 
-                    _solutionsSystem.TryAddSolution(outputContainer.Value, containerSolution, solution);
+                    _solutionContainersSystem.TryAddSolution(containerSoln.Value, solution);
                 }
 
                 _userInterfaceSystem.TrySendUiMessage(uid, ReagentGrinderUiKey.Key,
@@ -118,9 +118,9 @@ namespace Content.Server.Kitchen.EntitySystems
             }
         }
 
-        private void OnEntRemoveAttempt(EntityUid uid, ReagentGrinderComponent reagentGrinder, ContainerIsRemovingAttemptEvent args)
+        private void OnEntRemoveAttempt(Entity<ReagentGrinderComponent> entity, ref ContainerIsRemovingAttemptEvent args)
         {
-            if (HasComp<ActiveReagentGrinderComponent>(uid))
+            if (HasComp<ActiveReagentGrinderComponent>(entity))
                 args.Cancel();
         }
 
@@ -132,17 +132,17 @@ namespace Content.Server.Kitchen.EntitySystems
             _appearanceSystem.SetData(uid, ReagentGrinderVisualState.BeakerAttached, outputContainer.HasValue);
         }
 
-        private void OnInteractUsing(EntityUid uid, ReagentGrinderComponent reagentGrinder, InteractUsingEvent args)
+        private void OnInteractUsing(Entity<ReagentGrinderComponent> entity, ref InteractUsingEvent args)
         {
             var heldEnt = args.Used;
-            var inputContainer = _containerSystem.EnsureContainer<Container>(uid, SharedReagentGrinder.InputContainerId);
+            var inputContainer = _containerSystem.EnsureContainer<Container>(entity.Owner, SharedReagentGrinder.InputContainerId);
 
             if (!HasComp<ExtractableComponent>(heldEnt))
             {
                 if (!HasComp<FitsInDispenserComponent>(heldEnt))
                 {
                     // This is ugly but we can't use whitelistFailPopup because there are 2 containers with different whitelists.
-                    _popupSystem.PopupEntity(Loc.GetString("reagent-grinder-component-cannot-put-entity-message"), uid, args.User);
+                    _popupSystem.PopupEntity(Loc.GetString("reagent-grinder-component-cannot-put-entity-message"), entity.Owner, args.User);
                 }
 
                 // Entity did NOT pass the whitelist for grind/juice.
@@ -156,7 +156,7 @@ namespace Content.Server.Kitchen.EntitySystems
 
             // Cap the chamber. Don't want someone putting in 500 entities and ejecting them all at once.
             // Maybe I should have done that for the microwave too?
-            if (inputContainer.ContainedEntities.Count >= reagentGrinder.StorageMaxEntities)
+            if (inputContainer.ContainedEntities.Count >= entity.Comp.StorageMaxEntities)
                 return;
 
             if (!_containerSystem.Insert(heldEnt, inputContainer))
@@ -168,19 +168,19 @@ namespace Content.Server.Kitchen.EntitySystems
         /// <remarks>
         /// Gotta be efficient, you know? you're saving a whole extra second here and everything.
         /// </remarks>
-        private void OnRefreshParts(EntityUid uid, ReagentGrinderComponent component, RefreshPartsEvent args)
+        private void OnRefreshParts(Entity<ReagentGrinderComponent> entity, ref RefreshPartsEvent args)
         {
-            var ratingWorkTime = args.PartRatings[component.MachinePartWorkTime];
-            var ratingStorage = args.PartRatings[component.MachinePartStorageMax];
+            var ratingWorkTime = args.PartRatings[entity.Comp.MachinePartWorkTime];
+            var ratingStorage = args.PartRatings[entity.Comp.MachinePartStorageMax];
 
-            component.WorkTimeMultiplier = MathF.Pow(component.PartRatingWorkTimerMulitplier, ratingWorkTime - 1);
-            component.StorageMaxEntities = component.BaseStorageMaxEntities + (int) (component.StoragePerPartRating * (ratingStorage - 1));
+            entity.Comp.WorkTimeMultiplier = MathF.Pow(entity.Comp.PartRatingWorkTimerMulitplier, ratingWorkTime - 1);
+            entity.Comp.StorageMaxEntities = entity.Comp.BaseStorageMaxEntities + (int) (entity.Comp.StoragePerPartRating * (ratingStorage - 1));
         }
 
-        private void OnUpgradeExamine(EntityUid uid, ReagentGrinderComponent component, UpgradeExamineEvent args)
+        private void OnUpgradeExamine(Entity<ReagentGrinderComponent> entity, ref UpgradeExamineEvent args)
         {
-            args.AddPercentageUpgrade("reagent-grinder-component-upgrade-work-time", component.WorkTimeMultiplier);
-            args.AddNumberUpgrade("reagent-grinder-component-upgrade-storage", component.StorageMaxEntities - component.BaseStorageMaxEntities);
+            args.AddPercentageUpgrade("reagent-grinder-component-upgrade-work-time", entity.Comp.WorkTimeMultiplier);
+            args.AddNumberUpgrade("reagent-grinder-component-upgrade-storage", entity.Comp.StorageMaxEntities - entity.Comp.BaseStorageMaxEntities);
         }
 
         private void UpdateUiState(EntityUid uid)
@@ -193,7 +193,7 @@ namespace Content.Server.Kitchen.EntitySystems
             var canGrind = false;
 
             if (outputContainer is not null
-                && _solutionsSystem.TryGetFitsInDispenser(outputContainer.Value, out containerSolution)
+                && _solutionContainersSystem.TryGetFitsInDispenser(outputContainer.Value, out _, out containerSolution)
                 && inputContainer.ContainedEntities.Count > 0)
             {
                 canGrind = inputContainer.ContainedEntities.All(CanGrind);
@@ -212,43 +212,43 @@ namespace Content.Server.Kitchen.EntitySystems
             _userInterfaceSystem.TrySetUiState(uid, ReagentGrinderUiKey.Key, state);
         }
 
-        private void OnStartMessage(EntityUid uid, ReagentGrinderComponent reagentGrinder, ReagentGrinderStartMessage message)
+        private void OnStartMessage(Entity<ReagentGrinderComponent> entity, ref ReagentGrinderStartMessage message)
         {
-            if (!this.IsPowered(uid, EntityManager) || HasComp<ActiveReagentGrinderComponent>(uid))
+            if (!this.IsPowered(entity.Owner, EntityManager) || HasComp<ActiveReagentGrinderComponent>(entity))
                 return;
 
-            DoWork(uid, reagentGrinder, message.Program);
+            DoWork(entity.Owner, entity.Comp, message.Program);
         }
 
-        private void OnEjectChamberAllMessage(EntityUid uid, ReagentGrinderComponent reagentGrinder, ReagentGrinderEjectChamberAllMessage message)
+        private void OnEjectChamberAllMessage(Entity<ReagentGrinderComponent> entity, ref ReagentGrinderEjectChamberAllMessage message)
         {
-            var inputContainer = _containerSystem.EnsureContainer<Container>(uid, SharedReagentGrinder.InputContainerId);
+            var inputContainer = _containerSystem.EnsureContainer<Container>(entity.Owner, SharedReagentGrinder.InputContainerId);
 
-            if (HasComp<ActiveReagentGrinderComponent>(uid) || inputContainer.ContainedEntities.Count <= 0)
+            if (HasComp<ActiveReagentGrinderComponent>(entity) || inputContainer.ContainedEntities.Count <= 0)
                 return;
 
-            ClickSound(uid, reagentGrinder);
-            foreach (var entity in inputContainer.ContainedEntities.ToList())
+            ClickSound(entity);
+            foreach (var toEject in inputContainer.ContainedEntities.ToList())
             {
-                _containerSystem.Remove(entity, inputContainer);
-                _randomHelper.RandomOffset(entity, 0.4f);
+                _containerSystem.Remove(toEject, inputContainer);
+                _randomHelper.RandomOffset(toEject, 0.4f);
             }
-            UpdateUiState(uid);
+            UpdateUiState(entity);
         }
 
-        private void OnEjectChamberContentMessage(EntityUid uid, ReagentGrinderComponent reagentGrinder, ReagentGrinderEjectChamberContentMessage message)
+        private void OnEjectChamberContentMessage(Entity<ReagentGrinderComponent> entity, ref ReagentGrinderEjectChamberContentMessage message)
         {
-            if (HasComp<ActiveReagentGrinderComponent>(uid))
+            if (HasComp<ActiveReagentGrinderComponent>(entity))
                 return;
 
-            var inputContainer = _containerSystem.EnsureContainer<Container>(uid, SharedReagentGrinder.InputContainerId);
+            var inputContainer = _containerSystem.EnsureContainer<Container>(entity.Owner, SharedReagentGrinder.InputContainerId);
             var ent = GetEntity(message.EntityId);
 
             if (_containerSystem.Remove(ent, inputContainer))
             {
                 _randomHelper.RandomOffset(ent, 0.4f);
-                ClickSound(uid, reagentGrinder);
-                UpdateUiState(uid);
+                ClickSound(entity);
+                UpdateUiState(entity);
             }
         }
 
@@ -290,16 +290,16 @@ namespace Content.Server.Kitchen.EntitySystems
                 new ReagentGrinderWorkStartedMessage(program));
         }
 
-        private void ClickSound(EntityUid uid, ReagentGrinderComponent reagentGrinder)
+        private void ClickSound(Entity<ReagentGrinderComponent> reagentGrinder)
         {
-            _audioSystem.PlayPvs(reagentGrinder.ClickSound, uid, AudioParams.Default.WithVolume(-2f));
+            _audioSystem.PlayPvs(reagentGrinder.Comp.ClickSound, reagentGrinder.Owner, AudioParams.Default.WithVolume(-2f));
         }
 
         private Solution? GetGrindSolution(EntityUid uid)
         {
             if (TryComp<ExtractableComponent>(uid, out var extractable)
                 && extractable.GrindableSolution is not null
-                && _solutionsSystem.TryGetSolution(uid, extractable.GrindableSolution, out var solution))
+                && _solutionContainersSystem.TryGetSolution(uid, extractable.GrindableSolution, out _, out var solution))
             {
                 return solution;
             }
@@ -311,7 +311,7 @@ namespace Content.Server.Kitchen.EntitySystems
         {
             var solutionName = CompOrNull<ExtractableComponent>(uid)?.GrindableSolution;
 
-            return solutionName is not null && _solutionsSystem.TryGetSolution(uid, solutionName, out _);
+            return solutionName is not null && _solutionContainersSystem.TryGetSolution(uid, solutionName, out _, out _);
         }
 
         private bool CanJuice(EntityUid uid)
index 86921d222ce50aab42926ad9bf5e4c41f4ef3d73..d777ae9406d3bb34d0b52f4e902358c5d3f2b1c9 100644 (file)
@@ -1,6 +1,6 @@
 using Content.Server.Administration.Logs;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Nutrition.EntitySystems;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Database;
 using Content.Shared.IdentityManagement;
 using Content.Shared.Interaction;
@@ -28,7 +28,7 @@ public sealed class LubeSystem : EntitySystem
         SubscribeLocalEvent<LubeComponent, AfterInteractEvent>(OnInteract, after: new[] { typeof(OpenableSystem) });
     }
 
-    private void OnInteract(EntityUid uid, LubeComponent component, AfterInteractEvent args)
+    private void OnInteract(Entity<LubeComponent> entity, ref AfterInteractEvent args)
     {
         if (args.Handled)
             return;
@@ -36,10 +36,10 @@ public sealed class LubeSystem : EntitySystem
         if (!args.CanReach || args.Target is not { Valid: true } target)
             return;
 
-        if (TryLube(uid, component, target, args.User))
+        if (TryLube(entity, target, args.User))
         {
             args.Handled = true;
-            _audio.PlayPvs(component.Squeeze, uid);
+            _audio.PlayPvs(entity.Comp.Squeeze, entity);
             _popup.PopupEntity(Loc.GetString("lube-success", ("target", Identity.Entity(target, EntityManager))), args.User, args.User, PopupType.Medium);
         }
         else
@@ -48,22 +48,22 @@ public sealed class LubeSystem : EntitySystem
         }
     }
 
-    private bool TryLube(EntityUid uid, LubeComponent component, EntityUid target, EntityUid actor)
+    private bool TryLube(Entity<LubeComponent> lube, EntityUid target, EntityUid actor)
     {
         if (HasComp<LubedComponent>(target) || !HasComp<ItemComponent>(target))
         {
             return false;
         }
 
-        if (HasComp<ItemComponent>(target) && _solutionContainer.TryGetSolution(uid, component.Solution, out var solution))
+        if (HasComp<ItemComponent>(target) && _solutionContainer.TryGetSolution(lube.Owner, lube.Comp.Solution, out _, out var solution))
         {
-            var quantity = solution.RemoveReagent(component.Reagent, component.Consumption);
+            var quantity = solution.RemoveReagent(lube.Comp.Reagent, lube.Comp.Consumption);
             if (quantity > 0)
             {
                 var lubed = EnsureComp<LubedComponent>(target);
-                lubed.SlipsLeft = _random.Next(component.MinSlips * quantity.Int(), component.MaxSlips * quantity.Int());
-                lubed.SlipStrength = component.SlipStrength;
-                _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(actor):actor} lubed {ToPrettyString(target):subject} with {ToPrettyString(uid):tool}");
+                lubed.SlipsLeft = _random.Next(lube.Comp.MinSlips * quantity.Int(), lube.Comp.MaxSlips * quantity.Int());
+                lubed.SlipStrength = lube.Comp.SlipStrength;
+                _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(actor):actor} lubed {ToPrettyString(target):subject} with {ToPrettyString(lube.Owner):tool}");
                 return true;
             }
         }
index d6a94ec6d24da7c8b9aefc1757d84d5770be91f6..ff05a72ef09e4a90779242c3e2756bcdcc0e2bb7 100644 (file)
@@ -1,4 +1,4 @@
-using System.Linq;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Chemistry.EntitySystems;
 using Content.Server.Construction;
 using Content.Server.Fluids.EntitySystems;
@@ -11,7 +11,6 @@ using Content.Server.Wires;
 using Content.Shared.Body.Systems;
 using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Components.SolutionManager;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.IdentityManagement;
 using Content.Shared.Interaction;
 using Content.Shared.Interaction.Events;
@@ -20,6 +19,7 @@ using Content.Shared.Mind;
 using Robust.Server.GameObjects;
 using Robust.Shared.Player;
 using Robust.Shared.Utility;
+using System.Linq;
 
 namespace Content.Server.Materials;
 
@@ -51,40 +51,40 @@ public sealed class MaterialReclaimerSystem : SharedMaterialReclaimerSystem
         SubscribeLocalEvent<MaterialReclaimerComponent, SuicideEvent>(OnSuicide);
         SubscribeLocalEvent<ActiveMaterialReclaimerComponent, PowerChangedEvent>(OnActivePowerChanged);
     }
-    private void OnStartup(EntityUid uid, MaterialReclaimerComponent component, ComponentStartup args)
+    private void OnStartup(Entity<MaterialReclaimerComponent> entity, ref ComponentStartup args)
     {
-        component.OutputSolution = _solutionContainer.EnsureSolution(uid, component.SolutionContainerId);
+        _solutionContainer.EnsureSolution(entity.Owner, entity.Comp.SolutionContainerId);
     }
 
-    private void OnUpgradeExamine(EntityUid uid, MaterialReclaimerComponent component, UpgradeExamineEvent args)
+    private void OnUpgradeExamine(Entity<MaterialReclaimerComponent> entity, ref UpgradeExamineEvent args)
     {
-        args.AddPercentageUpgrade(Loc.GetString("material-reclaimer-upgrade-process-rate"), component.MaterialProcessRate / component.BaseMaterialProcessRate);
+        args.AddPercentageUpgrade(Loc.GetString("material-reclaimer-upgrade-process-rate"), entity.Comp.MaterialProcessRate / entity.Comp.BaseMaterialProcessRate);
     }
 
-    private void OnRefreshParts(EntityUid uid, MaterialReclaimerComponent component, RefreshPartsEvent args)
+    private void OnRefreshParts(Entity<MaterialReclaimerComponent> entity, ref RefreshPartsEvent args)
     {
-        var rating = args.PartRatings[component.MachinePartProcessRate] - 1;
-        component.MaterialProcessRate = component.BaseMaterialProcessRate * MathF.Pow(component.PartRatingProcessRateMultiplier, rating);
-        Dirty(component);
+        var rating = args.PartRatings[entity.Comp.MachinePartProcessRate] - 1;
+        entity.Comp.MaterialProcessRate = entity.Comp.BaseMaterialProcessRate * MathF.Pow(entity.Comp.PartRatingProcessRateMultiplier, rating);
+        Dirty(entity);
     }
 
-    private void OnPowerChanged(EntityUid uid, MaterialReclaimerComponent component, ref PowerChangedEvent args)
+    private void OnPowerChanged(Entity<MaterialReclaimerComponent> entity, ref PowerChangedEvent args)
     {
-        AmbientSound.SetAmbience(uid, component.Enabled && args.Powered);
-        component.Powered = args.Powered;
-        Dirty(component);
+        AmbientSound.SetAmbience(entity.Owner, entity.Comp.Enabled && args.Powered);
+        entity.Comp.Powered = args.Powered;
+        Dirty(entity);
     }
 
-    private void OnInteractUsing(EntityUid uid, MaterialReclaimerComponent component, InteractUsingEvent args)
+    private void OnInteractUsing(Entity<MaterialReclaimerComponent> entity, ref InteractUsingEvent args)
     {
         if (args.Handled)
             return;
 
         // if we're trying to get a solution out of the reclaimer, don't destroy it
-        if (component.OutputSolution.Contents.Any())
+        if (_solutionContainer.TryGetSolution(entity.Owner, entity.Comp.SolutionContainerId, out _, out var outputSolution) && outputSolution.Contents.Any())
         {
             if (TryComp<SolutionContainerManagerComponent>(args.Used, out var managerComponent) &&
-                managerComponent.Solutions.Any(s => s.Value.AvailableVolume > 0))
+                _solutionContainer.EnumerateSolutions((args.Used, managerComponent)).Any(s => s.Solution.Comp.Solution.AvailableVolume > 0))
             {
                 if (_openable.IsClosed(args.Used))
                     return;
@@ -95,10 +95,10 @@ public sealed class MaterialReclaimerSystem : SharedMaterialReclaimerSystem
             }
         }
 
-        args.Handled = TryStartProcessItem(uid, args.Used, component, args.User);
+        args.Handled = TryStartProcessItem(entity.Owner, args.Used, entity.Comp, args.User);
     }
 
-    private void OnSuicide(EntityUid uid, MaterialReclaimerComponent component, SuicideEvent args)
+    private void OnSuicide(Entity<MaterialReclaimerComponent> entity, ref SuicideEvent args)
     {
         if (args.Handled)
             return;
@@ -109,9 +109,9 @@ public sealed class MaterialReclaimerSystem : SharedMaterialReclaimerSystem
             _mind.TryGetMind(actor.PlayerSession, out var mindId, out var mind))
         {
             _ticker.OnGhostAttempt(mindId, false, mind: mind);
-            if (mind.OwnedEntity is { Valid: true } entity)
+            if (mind.OwnedEntity is { Valid: true } suicider)
             {
-                _popup.PopupEntity(Loc.GetString("recycler-component-suicide-message"), entity);
+                _popup.PopupEntity(Loc.GetString("recycler-component-suicide-message"), suicider);
             }
         }
 
@@ -120,13 +120,13 @@ public sealed class MaterialReclaimerSystem : SharedMaterialReclaimerSystem
             Filter.PvsExcept(victim, entityManager: EntityManager), true);
 
         _body.GibBody(victim, true);
-        _appearance.SetData(uid, RecyclerVisuals.Bloody, true);
+        _appearance.SetData(entity.Owner, RecyclerVisuals.Bloody, true);
     }
 
-    private void OnActivePowerChanged(EntityUid uid, ActiveMaterialReclaimerComponent component, ref PowerChangedEvent args)
+    private void OnActivePowerChanged(Entity<ActiveMaterialReclaimerComponent> entity, ref PowerChangedEvent args)
     {
         if (!args.Powered)
-            TryFinishProcessItem(uid, null, component);
+            TryFinishProcessItem(entity, null, entity.Comp);
     }
 
     /// <inheritdoc/>
@@ -224,6 +224,8 @@ public sealed class MaterialReclaimerSystem : SharedMaterialReclaimerSystem
     {
         if (!Resolve(reclaimer, ref reclaimerComponent, ref xform))
             return;
+        if (!_solutionContainer.TryGetSolution(reclaimer, reclaimerComponent.SolutionContainerId, out var outputSolution))
+            return;
 
         efficiency *= reclaimerComponent.Efficiency;
 
@@ -241,8 +243,9 @@ public sealed class MaterialReclaimerSystem : SharedMaterialReclaimerSystem
         // if the item we inserted has reagents, add it in.
         if (TryComp<SolutionContainerManagerComponent>(item, out var solutionContainer))
         {
-            foreach (var solution in solutionContainer.Solutions.Values)
+            foreach (var (_, soln) in _solutionContainer.EnumerateSolutions((item, solutionContainer)))
             {
+                var solution = soln.Comp.Solution;
                 foreach (var quantity in solution.Contents)
                 {
                     totalChemicals.AddReagent(quantity.Reagent.Prototype, quantity.Quantity * efficiency, false);
@@ -250,7 +253,7 @@ public sealed class MaterialReclaimerSystem : SharedMaterialReclaimerSystem
             }
         }
 
-        _solutionContainer.TryTransferSolution(reclaimer, reclaimerComponent.OutputSolution, totalChemicals, totalChemicals.Volume);
+        _solutionContainer.TryTransferSolution(outputSolution.Value, totalChemicals, totalChemicals.Volume);
         if (totalChemicals.Volume > 0)
         {
             _puddle.TrySpillAt(reclaimer, totalChemicals, out _, sound, transformComponent: xform);
index b083c480f6ce68b794fdd9ac9d9486c65c06af2f..19cb62f8365f0dcf3ad2579ca4d9b63486d151f6 100644 (file)
@@ -5,6 +5,7 @@ using Content.Server.Atmos.Piping.Components;
 using Content.Server.Atmos.Piping.Unary.EntitySystems;
 using Content.Server.Body.Components;
 using Content.Server.Body.Systems;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Medical.Components;
 using Content.Server.NodeContainer;
 using Content.Server.NodeContainer.EntitySystems;
@@ -16,7 +17,6 @@ using Content.Server.UserInterface;
 using Content.Shared.Chemistry;
 using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Components.SolutionManager;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.Climbing.Systems;
 using Content.Shared.Containers.ItemSlots;
@@ -28,7 +28,6 @@ using Content.Shared.Examine;
 using Content.Shared.Interaction;
 using Content.Shared.Medical.Cryogenics;
 using Content.Shared.MedicalScanner;
-using Content.Shared.Tools;
 using Content.Shared.Verbs;
 using Robust.Server.GameObjects;
 using Robust.Shared.Timing;
@@ -36,7 +35,7 @@ using SharedToolSystem = Content.Shared.Tools.Systems.SharedToolSystem;
 
 namespace Content.Server.Medical;
 
-public sealed partial class CryoPodSystem: SharedCryoPodSystem
+public sealed partial class CryoPodSystem : SharedCryoPodSystem
 {
     [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
     [Dependency] private readonly GasCanisterSystem _gasCanisterSystem = default!;
@@ -105,14 +104,15 @@ public sealed partial class CryoPodSystem: SharedCryoPodSystem
                 && fitsInDispenserQuery.TryGetComponent(container, out var fitsInDispenserComponent)
                 && solutionContainerManagerQuery.TryGetComponent(container,
                     out var solutionContainerManagerComponent)
-                && _solutionContainerSystem.TryGetFitsInDispenser(container.Value, out var containerSolution, dispenserFits: fitsInDispenserComponent, solutionManager: solutionContainerManagerComponent))
+                && _solutionContainerSystem.TryGetFitsInDispenser((container.Value, fitsInDispenserComponent, solutionContainerManagerComponent),
+                    out var containerSolution, out _))
             {
                 if (!bloodStreamQuery.TryGetComponent(patient, out var bloodstream))
                 {
                     continue;
                 }
 
-                var solutionToInject = _solutionContainerSystem.SplitSolution(container.Value, containerSolution, cryoPod.BeakerTransferAmount);
+                var solutionToInject = _solutionContainerSystem.SplitSolution(containerSolution.Value, cryoPod.BeakerTransferAmount);
                 _bloodstreamSystem.TryAddToChemicals(patient.Value, solutionToInject, bloodstream);
                 _reactiveSystem.DoEntityReaction(patient.Value, solutionToInject, ReactionMethod.Injection);
             }
@@ -123,7 +123,7 @@ public sealed partial class CryoPodSystem: SharedCryoPodSystem
     {
         if (!Resolve(uid, ref cryoPodComponent))
             return null;
-        if (cryoPodComponent.BodyContainer.ContainedEntity is not {Valid: true} contained)
+        if (cryoPodComponent.BodyContainer.ContainedEntity is not { Valid: true } contained)
             return null;
         base.EjectBody(uid, cryoPodComponent);
         _climbSystem.ForciblySetClimbing(contained, uid);
@@ -132,12 +132,12 @@ public sealed partial class CryoPodSystem: SharedCryoPodSystem
 
     #region Interaction
 
-    private void HandleDragDropOn(EntityUid uid, CryoPodComponent cryoPodComponent, ref DragDropTargetEvent args)
+    private void HandleDragDropOn(Entity<CryoPodComponent> entity, ref DragDropTargetEvent args)
     {
-        if (cryoPodComponent.BodyContainer.ContainedEntity != null)
+        if (entity.Comp.BodyContainer.ContainedEntity != null)
             return;
 
-        var doAfterArgs = new DoAfterArgs(EntityManager, args.User, cryoPodComponent.EntryDelay, new CryoPodDragFinished(), uid, target: args.Dragged, used: uid)
+        var doAfterArgs = new DoAfterArgs(EntityManager, args.User, entity.Comp.EntryDelay, new CryoPodDragFinished(), entity, target: args.Dragged, used: entity)
         {
             BreakOnDamage = true,
             BreakOnTargetMove = true,
@@ -148,61 +148,65 @@ public sealed partial class CryoPodSystem: SharedCryoPodSystem
         args.Handled = true;
     }
 
-    private void OnDragFinished(EntityUid uid, CryoPodComponent component, CryoPodDragFinished args)
+    private void OnDragFinished(Entity<CryoPodComponent> entity, ref CryoPodDragFinished args)
     {
         if (args.Cancelled || args.Handled || args.Args.Target == null)
             return;
 
-        if (InsertBody(uid, args.Args.Target.Value, component))
+        if (InsertBody(entity.Owner, args.Args.Target.Value, entity.Comp))
         {
-            if (!TryComp(uid, out CryoPodAirComponent? cryoPodAir))
+            if (!TryComp(entity.Owner, out CryoPodAirComponent? cryoPodAir))
                 _adminLogger.Add(LogType.Action, LogImpact.Medium,
-                    $"{ToPrettyString(args.User)} inserted {ToPrettyString(args.Args.Target.Value)} into {ToPrettyString(uid)}");
+                    $"{ToPrettyString(args.User)} inserted {ToPrettyString(args.Args.Target.Value)} into {ToPrettyString(entity.Owner)}");
 
             _adminLogger.Add(LogType.Action, LogImpact.Medium,
-                $"{ToPrettyString(args.User)} inserted {ToPrettyString(args.Args.Target.Value)} into {ToPrettyString(uid)} which contains gas: {cryoPodAir!.Air.ToPrettyString():gasMix}");
+                $"{ToPrettyString(args.User)} inserted {ToPrettyString(args.Args.Target.Value)} into {ToPrettyString(entity.Owner)} which contains gas: {cryoPodAir!.Air.ToPrettyString():gasMix}");
         }
         args.Handled = true;
     }
 
-    private void OnActivateUIAttempt(EntityUid uid, CryoPodComponent cryoPodComponent, ActivatableUIOpenAttemptEvent args)
+    private void OnActivateUIAttempt(Entity<CryoPodComponent> entity, ref ActivatableUIOpenAttemptEvent args)
     {
         if (args.Cancelled)
         {
             return;
         }
 
-        var containedEntity = cryoPodComponent.BodyContainer.ContainedEntity;
-        if (containedEntity == null || containedEntity == args.User || !HasComp<ActiveCryoPodComponent>(uid))
+        var containedEntity = entity.Comp.BodyContainer.ContainedEntity;
+        if (containedEntity == null || containedEntity == args.User || !HasComp<ActiveCryoPodComponent>(entity))
         {
             args.Cancel();
         }
     }
 
-    private void OnActivateUI(EntityUid uid, CryoPodComponent cryoPodComponent, AfterActivatableUIOpenEvent args)
+    private void OnActivateUI(Entity<CryoPodComponent> entity, ref AfterActivatableUIOpenEvent args)
     {
-        TryComp<TemperatureComponent>(cryoPodComponent.BodyContainer.ContainedEntity, out var temp);
-        TryComp<BloodstreamComponent>(cryoPodComponent.BodyContainer.ContainedEntity, out var bloodstream);
+        TryComp<TemperatureComponent>(entity.Comp.BodyContainer.ContainedEntity, out var temp);
+        TryComp<BloodstreamComponent>(entity.Comp.BodyContainer.ContainedEntity, out var bloodstream);
 
         _userInterfaceSystem.TrySendUiMessage(
-            uid,
+            entity.Owner,
             HealthAnalyzerUiKey.Key,
-            new HealthAnalyzerScannedUserMessage(GetNetEntity(cryoPodComponent.BodyContainer.ContainedEntity),
-            temp?.CurrentTemperature ?? 0, bloodstream != null ? bloodstream.BloodSolution.FillFraction : 0));
+            new HealthAnalyzerScannedUserMessage(GetNetEntity(entity.Comp.BodyContainer.ContainedEntity),
+            temp?.CurrentTemperature ?? 0,
+            (bloodstream != null && _solutionContainerSystem.ResolveSolution(entity.Owner, bloodstream.BloodSolutionName, ref bloodstream.BloodSolution, out var bloodSolution))
+                ? bloodSolution.FillFraction
+                : 0
+        ));
     }
 
-    private void OnInteractUsing(EntityUid uid, CryoPodComponent cryoPodComponent, InteractUsingEvent args)
+    private void OnInteractUsing(Entity<CryoPodComponent> entity, ref InteractUsingEvent args)
     {
-        if (args.Handled || !cryoPodComponent.Locked || cryoPodComponent.BodyContainer.ContainedEntity == null)
+        if (args.Handled || !entity.Comp.Locked || entity.Comp.BodyContainer.ContainedEntity == null)
             return;
 
-        args.Handled = _toolSystem.UseTool(args.Used, args.User, uid, cryoPodComponent.PryDelay, "Prying", new CryoPodPryFinished());
+        args.Handled = _toolSystem.UseTool(args.Used, args.User, entity.Owner, entity.Comp.PryDelay, "Prying", new CryoPodPryFinished());
     }
 
-    private void OnExamined(EntityUid uid, CryoPodComponent component, ExaminedEvent args)
+    private void OnExamined(Entity<CryoPodComponent> entity, ref ExaminedEvent args)
     {
-        var container = _itemSlotsSystem.GetItemOrNull(uid, component.SolutionContainerName);
-        if (args.IsInDetailsRange && container != null && _solutionContainerSystem.TryGetFitsInDispenser(container.Value, out var containerSolution))
+        var container = _itemSlotsSystem.GetItemOrNull(entity.Owner, entity.Comp.SolutionContainerName);
+        if (args.IsInDetailsRange && container != null && _solutionContainerSystem.TryGetFitsInDispenser(container.Value, out _, out var containerSolution))
         {
             args.PushMarkup(Loc.GetString("cryo-pod-examine", ("beaker", Name(container.Value))));
             if (containerSolution.Volume == 0)
@@ -212,64 +216,63 @@ public sealed partial class CryoPodSystem: SharedCryoPodSystem
         }
     }
 
-    private void OnPowerChanged(EntityUid uid, CryoPodComponent component, ref PowerChangedEvent args)
+    private void OnPowerChanged(Entity<CryoPodComponent> entity, ref PowerChangedEvent args)
     {
         // Needed to avoid adding/removing components on a deleted entity
-        if (Terminating(uid))
+        if (Terminating(entity))
         {
             return;
         }
 
         if (args.Powered)
         {
-            EnsureComp<ActiveCryoPodComponent>(uid);
+            EnsureComp<ActiveCryoPodComponent>(entity);
         }
         else
         {
-            RemComp<ActiveCryoPodComponent>(uid);
-            _uiSystem.TryCloseAll(uid, HealthAnalyzerUiKey.Key);
+            RemComp<ActiveCryoPodComponent>(entity);
+            _uiSystem.TryCloseAll(entity.Owner, HealthAnalyzerUiKey.Key);
         }
-        UpdateAppearance(uid, component);
+        UpdateAppearance(entity.Owner, entity.Comp);
     }
 
     #endregion
 
     #region Atmos handler
 
-    private void OnCryoPodUpdateAtmosphere(EntityUid uid, CryoPodComponent cryoPod, ref AtmosDeviceUpdateEvent args)
+    private void OnCryoPodUpdateAtmosphere(Entity<CryoPodComponent> entity, ref AtmosDeviceUpdateEvent args)
     {
-        if (!TryComp(uid, out NodeContainerComponent? nodeContainer))
+        if (!TryComp(entity, out NodeContainerComponent? nodeContainer))
             return;
 
-        if (!_nodeContainer.TryGetNode(nodeContainer, cryoPod.PortName, out PortablePipeNode? portNode))
+        if (!_nodeContainer.TryGetNode(nodeContainer, entity.Comp.PortName, out PortablePipeNode? portNode))
             return;
 
-        if (!TryComp(uid, out CryoPodAirComponent? cryoPodAir))
+        if (!TryComp(entity, out CryoPodAirComponent? cryoPodAir))
             return;
 
         _atmosphereSystem.React(cryoPodAir.Air, portNode);
 
-        if (portNode.NodeGroup is PipeNet {NodeCount: > 1} net)
+        if (portNode.NodeGroup is PipeNet { NodeCount: > 1 } net)
         {
             _gasCanisterSystem.MixContainerWithPipeNet(cryoPodAir.Air, net.Air);
         }
     }
 
-    private void OnGasAnalyzed(EntityUid uid, CryoPodComponent component, GasAnalyzerScanEvent args)
+    private void OnGasAnalyzed(Entity<CryoPodComponent> entity, ref GasAnalyzerScanEvent args)
     {
-        if (!TryComp(uid, out CryoPodAirComponent? cryoPodAir))
+        if (!TryComp(entity, out CryoPodAirComponent? cryoPodAir))
             return;
 
-        var gasMixDict = new Dictionary<string, GasMixture?> { { Name(uid), cryoPodAir.Air } };
+        var gasMixDict = new Dictionary<string, GasMixture?> { { Name(entity.Owner), cryoPodAir.Air } };
         // If it's connected to a port, include the port side
-        if (TryComp(uid, out NodeContainerComponent? nodeContainer))
+        if (TryComp(entity, out NodeContainerComponent? nodeContainer))
         {
-            if (_nodeContainer.TryGetNode(nodeContainer, component.PortName, out PipeNode? port))
-                gasMixDict.Add(component.PortName, port.Air);
+            if (_nodeContainer.TryGetNode(nodeContainer, entity.Comp.PortName, out PipeNode? port))
+                gasMixDict.Add(entity.Comp.PortName, port.Air);
         }
         args.GasMixtures = gasMixDict;
     }
 
-
     #endregion
 }
index 57f66f2378ab49029c9820615df542d39051174f..38f14ea9dbe5ff0f81cfcb7301a8aa6caf1fe326 100644 (file)
@@ -1,7 +1,9 @@
 using Content.Server.Administration.Logs;
 using Content.Server.Body.Components;
 using Content.Server.Body.Systems;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Medical.Components;
+using Content.Server.Popups;
 using Content.Server.Stack;
 using Content.Shared.Audio;
 using Content.Shared.Damage;
@@ -15,8 +17,6 @@ using Content.Shared.Mobs;
 using Content.Shared.Mobs.Components;
 using Content.Shared.Mobs.Systems;
 using Content.Shared.Stacks;
-using Content.Server.Popups;
-using Robust.Shared.Audio;
 using Robust.Shared.Audio.Systems;
 using Robust.Shared.Random;
 
@@ -34,6 +34,7 @@ public sealed class HealingSystem : EntitySystem
     [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
     [Dependency] private readonly MobThresholdSystem _mobThresholdSystem = default!;
     [Dependency] private readonly PopupSystem _popupSystem = default!;
+    [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
 
     public override void Initialize()
     {
@@ -43,7 +44,7 @@ public sealed class HealingSystem : EntitySystem
         SubscribeLocalEvent<DamageableComponent, HealingDoAfterEvent>(OnDoAfter);
     }
 
-    private void OnDoAfter(EntityUid uid, DamageableComponent component, HealingDoAfterEvent args)
+    private void OnDoAfter(Entity<DamageableComponent> entity, ref HealingDoAfterEvent args)
     {
         var dontRepeat = false;
 
@@ -54,8 +55,8 @@ public sealed class HealingSystem : EntitySystem
             return;
 
         if (healing.DamageContainers is not null &&
-            component.DamageContainerID is not null &&
-            !healing.DamageContainers.Contains(component.DamageContainerID))
+            entity.Comp.DamageContainerID is not null &&
+            !healing.DamageContainers.Contains(entity.Comp.DamageContainerID))
         {
             return;
         }
@@ -63,22 +64,22 @@ public sealed class HealingSystem : EntitySystem
         // Heal some bloodloss damage.
         if (healing.BloodlossModifier != 0)
         {
-            if (!TryComp<BloodstreamComponent>(uid, out var bloodstream))
+            if (!TryComp<BloodstreamComponent>(entity, out var bloodstream))
                 return;
             var isBleeding = bloodstream.BleedAmount > 0;
-            _bloodstreamSystem.TryModifyBleedAmount(uid, healing.BloodlossModifier);
+            _bloodstreamSystem.TryModifyBleedAmount(entity.Owner, healing.BloodlossModifier);
             if (isBleeding != bloodstream.BleedAmount > 0)
             {
                 dontRepeat = true;
-                _popupSystem.PopupEntity(Loc.GetString("medical-item-stop-bleeding"), uid, args.User);
+                _popupSystem.PopupEntity(Loc.GetString("medical-item-stop-bleeding"), entity, args.User);
             }
         }
 
         // Restores missing blood
         if (healing.ModifyBloodLevel != 0)
-            _bloodstreamSystem.TryModifyBloodLevel(uid, healing.ModifyBloodLevel);
+            _bloodstreamSystem.TryModifyBloodLevel(entity.Owner, healing.ModifyBloodLevel);
 
-        var healed = _damageable.TryChangeDamage(uid, healing.Damage, true, origin: args.Args.User);
+        var healed = _damageable.TryChangeDamage(entity.Owner, healing.Damage, true, origin: args.Args.User);
 
         if (healed == null && healing.BloodlossModifier != 0)
             return;
@@ -99,10 +100,10 @@ public sealed class HealingSystem : EntitySystem
             QueueDel(args.Used.Value);
         }
 
-        if (uid != args.User)
+        if (entity.Owner != args.User)
         {
             _adminLogger.Add(LogType.Healed,
-                $"{EntityManager.ToPrettyString(args.User):user} healed {EntityManager.ToPrettyString(uid):target} for {total:damage} damage");
+                $"{EntityManager.ToPrettyString(args.User):user} healed {EntityManager.ToPrettyString(entity.Owner):target} for {total:damage} damage");
         }
         else
         {
@@ -110,12 +111,12 @@ public sealed class HealingSystem : EntitySystem
                 $"{EntityManager.ToPrettyString(args.User):user} healed themselves for {total:damage} damage");
         }
 
-        _audio.PlayPvs(healing.HealingEndSound, uid, AudioHelpers.WithVariation(0.125f, _random).WithVolume(-5f));
+        _audio.PlayPvs(healing.HealingEndSound, entity.Owner, AudioHelpers.WithVariation(0.125f, _random).WithVolume(-5f));
 
         // Logic to determine the whether or not to repeat the healing action
-        args.Repeat = (HasDamage(component, healing) && !dontRepeat);
+        args.Repeat = (HasDamage(entity.Comp, healing) && !dontRepeat);
         if (!args.Repeat && !dontRepeat)
-            _popupSystem.PopupEntity(Loc.GetString("medical-item-finished-using", ("item", args.Used)), uid, args.User);
+            _popupSystem.PopupEntity(Loc.GetString("medical-item-finished-using", ("item", args.Used)), entity.Owner, args.User);
         args.Handled = true;
     }
 
@@ -134,21 +135,21 @@ public sealed class HealingSystem : EntitySystem
         return false;
     }
 
-    private void OnHealingUse(EntityUid uid, HealingComponent component, UseInHandEvent args)
+    private void OnHealingUse(Entity<HealingComponent> entity, ref UseInHandEvent args)
     {
         if (args.Handled)
             return;
 
-        if (TryHeal(uid, args.User, args.User, component))
+        if (TryHeal(entity, args.User, args.User, entity.Comp))
             args.Handled = true;
     }
 
-    private void OnHealingAfterInteract(EntityUid uid, HealingComponent component, AfterInteractEvent args)
+    private void OnHealingAfterInteract(Entity<HealingComponent> entity, ref AfterInteractEvent args)
     {
         if (args.Handled || !args.CanReach || args.Target == null)
             return;
 
-        if (TryHeal(uid, args.User, args.Target.Value, component))
+        if (TryHeal(entity, args.User, args.Target.Value, entity.Comp))
             args.Handled = true;
     }
 
@@ -170,10 +171,14 @@ public sealed class HealingSystem : EntitySystem
         if (TryComp<StackComponent>(uid, out var stack) && stack.Count < 1)
             return false;
 
-        if (!TryComp<BloodstreamComponent>(target, out var bloodstream))
-            return false;
+        var anythingToDo =
+            HasDamage(targetDamage, component) ||
+            component.ModifyBloodLevel > 0 // Special case if healing item can restore lost blood...
+                && TryComp<BloodstreamComponent>(target, out var bloodstream)
+                && _solutionContainerSystem.ResolveSolution(target, bloodstream.BloodSolutionName, ref bloodstream.BloodSolution, out var bloodSolution)
+                && bloodSolution.Volume < bloodSolution.MaxVolume; // ...and there is lost blood to restore.
 
-        if (!HasDamage(targetDamage, component) && !(bloodstream.BloodSolution.Volume < bloodstream.BloodSolution.MaxVolume && component.ModifyBloodLevel > 0))
+        if (!anythingToDo)
         {
             _popupSystem.PopupEntity(Loc.GetString("medical-item-cant-use", ("item", uid)), uid, user);
             return false;
index fe3944b21ee35b2a95220b404ffbb8e850129b14..d9559a96264f18ff6e1061693f1ae17574be6955 100644 (file)
@@ -1,14 +1,14 @@
+using Content.Server.Body.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Medical.Components;
 using Content.Server.PowerCell;
+using Content.Server.Temperature.Components;
 using Content.Shared.Damage;
 using Content.Shared.DoAfter;
 using Content.Shared.Interaction;
 using Content.Shared.MedicalScanner;
 using Content.Shared.Mobs.Components;
 using Robust.Server.GameObjects;
-using Content.Server.Temperature.Components;
-using Content.Server.Body.Components;
-using Robust.Shared.Audio;
 using Robust.Shared.Audio.Systems;
 using Robust.Shared.Player;
 
@@ -19,6 +19,7 @@ namespace Content.Server.Medical
         [Dependency] private readonly PowerCellSystem _cell = default!;
         [Dependency] private readonly SharedAudioSystem _audio = default!;
         [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
+        [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
         [Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
 
         public override void Initialize()
@@ -28,14 +29,14 @@ namespace Content.Server.Medical
             SubscribeLocalEvent<HealthAnalyzerComponent, HealthAnalyzerDoAfterEvent>(OnDoAfter);
         }
 
-        private void OnAfterInteract(EntityUid uid, HealthAnalyzerComponent healthAnalyzer, AfterInteractEvent args)
+        private void OnAfterInteract(Entity<HealthAnalyzerComponent> entity, ref AfterInteractEvent args)
         {
-            if (args.Target == null || !args.CanReach || !HasComp<MobStateComponent>(args.Target) || !_cell.HasActivatableCharge(uid, user: args.User))
+            if (args.Target == null || !args.CanReach || !HasComp<MobStateComponent>(args.Target) || !_cell.HasActivatableCharge(entity.Owner, user: args.User))
                 return;
 
-            _audio.PlayPvs(healthAnalyzer.ScanningBeginSound, uid);
+            _audio.PlayPvs(entity.Comp.ScanningBeginSound, entity);
 
-            _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, healthAnalyzer.ScanDelay, new HealthAnalyzerDoAfterEvent(), uid, target: args.Target, used: uid)
+            _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, TimeSpan.FromSeconds(entity.Comp.ScanDelay), new HealthAnalyzerDoAfterEvent(), entity.Owner, target: args.Target, used: entity.Owner)
             {
                 BreakOnTargetMove = true,
                 BreakOnUserMove = true,
@@ -43,14 +44,14 @@ namespace Content.Server.Medical
             });
         }
 
-        private void OnDoAfter(EntityUid uid, HealthAnalyzerComponent component, DoAfterEvent args)
+        private void OnDoAfter(Entity<HealthAnalyzerComponent> entity, ref HealthAnalyzerDoAfterEvent args)
         {
-            if (args.Handled || args.Cancelled || args.Args.Target == null || !_cell.TryUseActivatableCharge(uid, user: args.User))
+            if (args.Handled || args.Cancelled || args.Target == null || !_cell.TryUseActivatableCharge(entity.Owner, user: args.User))
                 return;
 
-            _audio.PlayPvs(component.ScanningEndSound, args.Args.User);
+            _audio.PlayPvs(entity.Comp.ScanningEndSound, args.User);
 
-            UpdateScannedUser(uid, args.Args.User, args.Args.Target.Value, component);
+            UpdateScannedUser(entity, args.User, args.Target.Value, entity.Comp);
             args.Handled = true;
         }
 
@@ -73,13 +74,26 @@ namespace Content.Server.Medical
             if (!HasComp<DamageableComponent>(target))
                 return;
 
-            TryComp<TemperatureComponent>(target, out var temp);
-            TryComp<BloodstreamComponent>(target, out var bloodstream);
+            float bodyTemperature;
+            if (TryComp<TemperatureComponent>(target, out var temp))
+                bodyTemperature = temp.CurrentTemperature;
+            else
+                bodyTemperature = float.NaN;
+
+            float bloodAmount;
+            if (TryComp<BloodstreamComponent>(target, out var bloodstream) &&
+                _solutionContainerSystem.ResolveSolution(target.Value, bloodstream.BloodSolutionName, ref bloodstream.BloodSolution, out var bloodSolution))
+                bloodAmount = bloodSolution.FillFraction;
+            else
+                bloodAmount = float.NaN;
 
             OpenUserInterface(user, uid);
 
-            _uiSystem.SendUiMessage(ui, new HealthAnalyzerScannedUserMessage(GetNetEntity(target), temp != null ? temp.CurrentTemperature : float.NaN,
-                bloodstream != null ? bloodstream.BloodSolution.FillFraction : float.NaN));
+            _uiSystem.SendUiMessage(ui, new HealthAnalyzerScannedUserMessage(
+                GetNetEntity(target),
+                bodyTemperature,
+                bloodAmount
+            ));
         }
     }
 }
index fedb6d2357bb9b2223ad50bbb44e471867c54da5..8c3b15aed33aba89a87c70f006cf5eb364e93cea 100644 (file)
@@ -1,17 +1,16 @@
 using Content.Server.Body.Components;
 using Content.Server.Body.Systems;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Fluids.EntitySystems;
 using Content.Server.Forensics;
 using Content.Server.Popups;
 using Content.Server.Stunnable;
 using Content.Shared.Chemistry.Components;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.IdentityManagement;
 using Content.Shared.Nutrition.Components;
 using Content.Shared.Nutrition.EntitySystems;
 using Content.Shared.StatusEffect;
 using Robust.Server.Audio;
-using Robust.Server.GameObjects;
 using Robust.Shared.Audio;
 using Robust.Shared.Prototypes;
 
@@ -59,29 +58,32 @@ namespace Content.Server.Medical
             // Empty the stomach out into it
             foreach (var stomach in stomachList)
             {
-                if (_solutionContainer.TryGetSolution(stomach.Comp.Owner, StomachSystem.DefaultSolutionName,
-                        out var sol))
+                if (_solutionContainer.ResolveSolution(stomach.Comp.Owner, StomachSystem.DefaultSolutionName, ref stomach.Comp.Solution, out var sol))
                 {
                     solution.AddSolution(sol, _proto);
                     sol.RemoveAllSolution();
-                    _solutionContainer.UpdateChemicals(stomach.Comp.Owner, sol);
+                    _solutionContainer.UpdateChemicals(stomach.Comp.Solution.Value);
                 }
             }
             // Adds a tiny amount of the chem stream from earlier along with vomit
             if (TryComp<BloodstreamComponent>(uid, out var bloodStream))
             {
-                var chemMultiplier = 0.1;
-                var vomitMultiplier = 0.9;
+                const float chemMultiplier = 0.1f;
 
-                // Makes a vomit solution the size of 90% of the chemicals removed from the chemstream
-                var vomitAmount = new Solution("Vomit", solutionSize * vomitMultiplier);
+                var vomitAmount = solutionSize;
 
                 // Takes 10% of the chemicals removed from the chem stream
-                var vomitChemstreamAmount = _solutionContainer.SplitSolution(uid, bloodStream.ChemicalSolution, solutionSize * chemMultiplier);
+                if (_solutionContainer.ResolveSolution(uid, bloodStream.ChemicalSolutionName, ref bloodStream.ChemicalSolution))
+                {
+                    var vomitChemstreamAmount = _solutionContainer.SplitSolution(bloodStream.ChemicalSolution.Value, vomitAmount);
+                    vomitChemstreamAmount.ScaleSolution(chemMultiplier);
+                    solution.AddSolution(vomitChemstreamAmount, _proto);
 
-                _solutionContainer.SplitSolution(uid, bloodStream.ChemicalSolution, solutionSize * vomitMultiplier);
-                solution.AddSolution(vomitAmount, _proto);
-                solution.AddSolution(vomitChemstreamAmount, _proto);
+                    vomitAmount -= (float) vomitChemstreamAmount.Volume;
+                }
+
+                // Makes a vomit solution the size of 90% of the chemicals removed from the chemstream
+                solution.AddReagent("Vomit", vomitAmount); // TODO: Dehardcode vomit prototype
             }
 
             if (_puddle.TrySpillAt(uid, solution, out var puddle, false))
index 7b66cff1f8aee4a6154eca528208424a9352d88d..01cfbd113d9a551a6a15aa38aea4844c9d0df8df 100644 (file)
@@ -1,15 +1,13 @@
 using Content.Server.Chat.Systems;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.NPC.Components;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Damage;
 using Content.Shared.Emag.Components;
 using Content.Shared.Interaction;
 using Content.Shared.Mobs.Components;
 using Content.Shared.Popups;
 using Content.Shared.Silicons.Bots;
-using Robust.Shared.Audio;
 using Robust.Shared.Audio.Systems;
-using Robust.Shared.Player;
 
 namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Specific;
 
@@ -21,7 +19,7 @@ public sealed partial class MedibotInjectOperator : HTNOperator
     private SharedAudioSystem _audio = default!;
     private SharedInteractionSystem _interaction = default!;
     private SharedPopupSystem _popup = default!;
-    private SolutionContainerSystem _solution = default!;
+    private SolutionContainerSystem _solutionContainer = default!;
 
     /// <summary>
     /// Target entity to inject.
@@ -37,7 +35,7 @@ public sealed partial class MedibotInjectOperator : HTNOperator
         _audio = sysManager.GetEntitySystem<SharedAudioSystem>();
         _interaction = sysManager.GetEntitySystem<SharedInteractionSystem>();
         _popup = sysManager.GetEntitySystem<SharedPopupSystem>();
-        _solution = sysManager.GetEntitySystem<SolutionContainerSystem>();
+        _solutionContainer = sysManager.GetEntitySystem<SolutionContainerSystem>();
     }
 
     public override void TaskShutdown(NPCBlackboard blackboard, HTNOperatorStatus status)
@@ -61,7 +59,7 @@ public sealed partial class MedibotInjectOperator : HTNOperator
         if (!_entMan.TryGetComponent<DamageableComponent>(target, out var damage))
             return HTNOperatorStatus.Failed;
 
-        if (!_solution.TryGetInjectableSolution(target, out var injectable))
+        if (!_solutionContainer.TryGetInjectableSolution(target, out var injectable, out _))
             return HTNOperatorStatus.Failed;
 
         if (!_interaction.InRangeUnobstructed(owner, target))
@@ -81,7 +79,7 @@ public sealed partial class MedibotInjectOperator : HTNOperator
             return HTNOperatorStatus.Failed;
 
         _entMan.EnsureComponent<NPCRecentlyInjectedComponent>(target);
-        _solution.TryAddReagent(target, injectable, treatment.Reagent, treatment.Quantity, out _);
+        _solutionContainer.TryAddReagent(injectable.Value, treatment.Reagent, treatment.Quantity, out _);
         _popup.PopupEntity(Loc.GetString("hypospray-component-feel-prick-message"), target, target);
         _audio.PlayPvs(botComp.InjectSound, target);
         _chat.TrySendInGameICMessage(owner, Loc.GetString("medibot-finish-inject"), InGameICChatType.Speak, hideChat: true, hideLog: true);
index 641b8c20e10765caffa074c2c7a4b467638d51b6..76d0ebe83a4cca3044472f1376d374e1ac6881c0 100644 (file)
@@ -1,4 +1,4 @@
-using System.Linq;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Fluids.EntitySystems;
 using Content.Server.NPC.Queries;
 using Content.Server.NPC.Queries.Considerations;
@@ -7,7 +7,6 @@ using Content.Server.NPC.Queries.Queries;
 using Content.Server.Nutrition.Components;
 using Content.Server.Nutrition.EntitySystems;
 using Content.Server.Storage.Components;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Examine;
 using Content.Shared.Fluids.Components;
 using Content.Shared.Hands.Components;
@@ -20,9 +19,9 @@ using Content.Shared.Weapons.Ranged.Components;
 using Content.Shared.Weapons.Ranged.Events;
 using Microsoft.Extensions.ObjectPool;
 using Robust.Server.Containers;
-using Robust.Shared.Collections;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Utility;
+using System.Linq;
 
 namespace Content.Server.NPC.Systems;
 
@@ -492,7 +491,7 @@ public sealed class NPCUtilitySystem : EntitySystem
                 foreach (var ent in entities)
                 {
                     if (!_puddleQuery.TryGetComponent(ent, out var puddleComp) ||
-                        !_solutions.TryGetSolution(ent, puddleComp.SolutionName, out var sol) ||
+                        !_solutions.TryGetSolution(ent, puddleComp.SolutionName, out _, out var sol) ||
                         _puddle.CanFullyEvaporate(sol))
                     {
                         _entityList.Add(ent);
index eebe7f98d868714b57238f450f7b8d45ad977da4..45d868cdbf6a9616ad0302e6d38fa96fcfdc8314 100644 (file)
@@ -1,9 +1,9 @@
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Explosion.Components;
 using Content.Server.Explosion.EntitySystems;
 using Content.Server.Fluids.EntitySystems;
 using Content.Server.Nutrition.Components;
 using Content.Server.Popups;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Containers.ItemSlots;
 using Content.Shared.Interaction;
 using Content.Shared.Nutrition.Components;
@@ -11,7 +11,6 @@ using Content.Shared.Nutrition.EntitySystems;
 using Content.Shared.Rejuvenate;
 using Content.Shared.Throwing;
 using JetBrains.Annotations;
-using Robust.Server.GameObjects;
 using Robust.Shared.Audio;
 using Robust.Shared.Audio.Systems;
 using Robust.Shared.Player;
@@ -42,7 +41,7 @@ namespace Content.Server.Nutrition.EntitySystems
 
             if (EntityManager.TryGetComponent(uid, out FoodComponent? foodComp))
             {
-                if (_solutions.TryGetSolution(uid, foodComp.Solution, out var solution))
+                if (_solutions.TryGetSolution(uid, foodComp.Solution, out _, out var solution))
                 {
                     _puddle.TrySpillAt(uid, solution, out _, false);
                 }
@@ -56,9 +55,9 @@ namespace Content.Server.Nutrition.EntitySystems
             EntityManager.QueueDeleteEntity(uid);
         }
 
-        private void OnInteractUsing(EntityUid uid, CreamPieComponent component, InteractUsingEvent args)
+        private void OnInteractUsing(Entity<CreamPieComponent> entity, ref InteractUsingEvent args)
         {
-            ActivatePayload(uid);
+            ActivatePayload(entity);
         }
 
         private void ActivatePayload(EntityUid uid)
@@ -89,12 +88,12 @@ namespace Content.Server.Nutrition.EntitySystems
             {
                 otherPlayers.RemovePlayer(actor.PlayerSession);
             }
-            _popup.PopupEntity(Loc.GetString("cream-pied-component-on-hit-by-message-others", ("owner", uid),("thrower", args.Thrown)), uid, otherPlayers, false);
+            _popup.PopupEntity(Loc.GetString("cream-pied-component-on-hit-by-message-others", ("owner", uid), ("thrower", args.Thrown)), uid, otherPlayers, false);
         }
 
-        private void OnRejuvenate(EntityUid uid, CreamPiedComponent component, RejuvenateEvent args)
+        private void OnRejuvenate(Entity<CreamPiedComponent> entity, ref RejuvenateEvent args)
         {
-            SetCreamPied(uid, component, false);
+            SetCreamPied(entity, entity.Comp, false);
         }
     }
 }
index e41c834c80ae2e85db5e8eeaad023c53c8b904a9..b32f5a7f6358b7ae9531c218e9559df44a34339b 100644 (file)
@@ -1,6 +1,6 @@
 using Content.Server.Body.Components;
 using Content.Server.Body.Systems;
-using Content.Server.Chemistry.EntitySystems;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Chemistry.ReagentEffects;
 using Content.Server.Fluids.EntitySystems;
 using Content.Server.Forensics;
@@ -61,7 +61,7 @@ public sealed class DrinkSystem : EntitySystem
         base.Initialize();
 
         // TODO add InteractNoHandEvent for entities like mice.
-        SubscribeLocalEvent<DrinkComponent, SolutionChangedEvent>(OnSolutionChange);
+        SubscribeLocalEvent<DrinkComponent, SolutionContainerChangedEvent>(OnSolutionChange);
         SubscribeLocalEvent<DrinkComponent, ComponentInit>(OnDrinkInit);
         // run before inventory so for bucket it always tries to drink before equipping (when empty)
         // run after openable so its always open -> drink
@@ -80,7 +80,7 @@ public sealed class DrinkSystem : EntitySystem
         if (!Resolve(uid, ref component))
             return FixedPoint2.Zero;
 
-        if (!_solutionContainer.TryGetSolution(uid, component.Solution, out var sol))
+        if (!_solutionContainer.TryGetSolution(uid, component.Solution, out _, out var sol))
             return FixedPoint2.Zero;
 
         return sol.Volume;
@@ -102,7 +102,7 @@ public sealed class DrinkSystem : EntitySystem
         if (!Resolve(uid, ref comp))
             return 0f;
 
-        if (!_solutionContainer.TryGetSolution(uid, comp.Solution, out var solution))
+        if (!_solutionContainer.TryGetSolution(uid, comp.Solution, out _, out var solution))
             return 0f;
 
         var total = 0f;
@@ -128,31 +128,31 @@ public sealed class DrinkSystem : EntitySystem
         return total;
     }
 
-    private void OnExamined(EntityUid uid, DrinkComponent component, ExaminedEvent args)
+    private void OnExamined(Entity<DrinkComponent> entity, ref ExaminedEvent args)
     {
-        var hasOpenable = TryComp<OpenableComponent>(uid, out var openable);
-        if (_openable.IsClosed(uid, null, openable) || !args.IsInDetailsRange || !component.Examinable)
+        var hasOpenable = TryComp<OpenableComponent>(entity, out var openable);
+        if (_openable.IsClosed(entity.Owner, null, openable) || !args.IsInDetailsRange || !entity.Comp.Examinable)
             return;
 
         // put Empty / Xu after Opened, or start a new line
         args.Message.AddMarkup(hasOpenable ? " - " : "\n");
 
-        var empty = IsEmpty(uid, component);
+        var empty = IsEmpty(entity, entity.Comp);
         if (empty)
         {
             args.Message.AddMarkup(Loc.GetString("drink-component-on-examine-is-empty"));
             return;
         }
 
-        if (TryComp<ExaminableSolutionComponent>(uid, out var comp))
+        if (TryComp<ExaminableSolutionComponent>(entity, out var comp))
         {
             //provide exact measurement for beakers
-            args.Message.AddMarkup(Loc.GetString("drink-component-on-examine-exact-volume", ("amount", DrinkVolume(uid, component))));
+            args.Message.AddMarkup(Loc.GetString("drink-component-on-examine-exact-volume", ("amount", DrinkVolume(entity, entity.Comp))));
         }
         else
         {
             //general approximation
-            var remainingString = (int) _solutionContainer.PercentFull(uid) switch
+            var remainingString = (int) _solutionContainer.PercentFull(entity) switch
             {
                 100 => "drink-component-on-examine-is-full",
                 > 66 => "drink-component-on-examine-is-mostly-full",
@@ -163,65 +163,65 @@ public sealed class DrinkSystem : EntitySystem
         }
     }
 
-    private void AfterInteract(EntityUid uid, DrinkComponent component, AfterInteractEvent args)
+    private void AfterInteract(Entity<DrinkComponent> entity, ref AfterInteractEvent args)
     {
         if (args.Handled || args.Target == null || !args.CanReach)
             return;
 
-        args.Handled = TryDrink(args.User, args.Target.Value, component, uid);
+        args.Handled = TryDrink(args.User, args.Target.Value, entity.Comp, entity);
     }
 
-    private void OnUse(EntityUid uid, DrinkComponent component, UseInHandEvent args)
+    private void OnUse(Entity<DrinkComponent> entity, ref UseInHandEvent args)
     {
         if (args.Handled)
             return;
 
-        args.Handled = TryDrink(args.User, args.User, component, uid);
+        args.Handled = TryDrink(args.User, args.User, entity.Comp, entity);
     }
 
-    private void OnPressurizedDrinkLand(EntityUid uid, PressurizedDrinkComponent comp, ref LandEvent args)
+    private void OnPressurizedDrinkLand(Entity<PressurizedDrinkComponent> entity, ref LandEvent args)
     {
-        if (!TryComp<DrinkComponent>(uid, out var drink) || !TryComp<OpenableComponent>(uid, out var openable))
+        if (!TryComp<DrinkComponent>(entity, out var drink) || !TryComp<OpenableComponent>(entity, out var openable))
             return;
 
         if (!openable.Opened &&
-            _random.Prob(comp.BurstChance) &&
-            _solutionContainer.TryGetSolution(uid, drink.Solution, out var interactions))
+            _random.Prob(entity.Comp.BurstChance) &&
+            _solutionContainer.TryGetSolution(entity.Owner, drink.Solution, out var soln, out var interactions))
         {
             // using SetOpen instead of TryOpen to not play 2 sounds
-            _openable.SetOpen(uid, true, openable);
+            _openable.SetOpen(entity, true, openable);
 
-            var solution = _solutionContainer.SplitSolution(uid, interactions, interactions.Volume);
-            _puddle.TrySpillAt(uid, solution, out _);
+            var solution = _solutionContainer.SplitSolution(soln.Value, interactions.Volume);
+            _puddle.TrySpillAt(entity, solution, out _);
 
-            _audio.PlayPvs(comp.BurstSound, uid);
+            _audio.PlayPvs(entity.Comp.BurstSound, entity);
         }
     }
 
-    private void OnDrinkInit(EntityUid uid, DrinkComponent component, ComponentInit args)
+    private void OnDrinkInit(Entity<DrinkComponent> entity, ref ComponentInit args)
     {
-        if (TryComp<DrainableSolutionComponent>(uid, out var existingDrainable))
+        if (TryComp<DrainableSolutionComponent>(entity, out var existingDrainable))
         {
             // Beakers have Drink component but they should use the existing Drainable
-            component.Solution = existingDrainable.Solution;
+            entity.Comp.Solution = existingDrainable.Solution;
         }
         else
         {
-            _solutionContainer.EnsureSolution(uid, component.Solution);
+            _solutionContainer.EnsureSolution(entity.Owner, entity.Comp.Solution);
         }
 
-        UpdateAppearance(uid, component);
+        UpdateAppearance(entity, entity.Comp);
 
-        if (TryComp(uid, out RefillableSolutionComponent? refillComp))
-            refillComp.Solution = component.Solution;
+        if (TryComp(entity, out RefillableSolutionComponent? refillComp))
+            refillComp.Solution = entity.Comp.Solution;
 
-        if (TryComp(uid, out DrainableSolutionComponent? drainComp))
-            drainComp.Solution = component.Solution;
+        if (TryComp(entity, out DrainableSolutionComponent? drainComp))
+            drainComp.Solution = entity.Comp.Solution;
     }
 
-    private void OnSolutionChange(EntityUid uid, DrinkComponent component, SolutionChangedEvent args)
+    private void OnSolutionChange(Entity<DrinkComponent> entity, ref SolutionContainerChangedEvent args)
     {
-        UpdateAppearance(uid, component);
+        UpdateAppearance(entity, entity.Comp);
     }
 
     public void UpdateAppearance(EntityUid uid, DrinkComponent component)
@@ -244,8 +244,7 @@ public sealed class DrinkSystem : EntitySystem
         if (_openable.IsClosed(item, user))
             return true;
 
-        if (!_solutionContainer.TryGetSolution(item, drink.Solution, out var drinkSolution) ||
-            drinkSolution.Volume <= 0)
+        if (!_solutionContainer.TryGetSolution(item, drink.Solution, out _, out var drinkSolution) || drinkSolution.Volume <= 0)
         {
             if (drink.IgnoreEmpty)
                 return false;
@@ -254,9 +253,6 @@ public sealed class DrinkSystem : EntitySystem
             return true;
         }
 
-        if (drinkSolution.Name == null)
-            return false;
-
         if (_food.IsMouthBlocked(target, user))
             return true;
 
@@ -285,7 +281,7 @@ public sealed class DrinkSystem : EntitySystem
         var doAfterEventArgs = new DoAfterArgs(EntityManager,
             user,
             forceDrink ? drink.ForceFeedDelay : drink.Delay,
-            new ConsumeDoAfterEvent(drinkSolution.Name, flavors),
+            new ConsumeDoAfterEvent(drink.Solution, flavors),
             eventTarget: item,
             target: target,
             used: item)
@@ -307,15 +303,15 @@ public sealed class DrinkSystem : EntitySystem
     /// <summary>
     ///     Raised directed at a victim when someone has force fed them a drink.
     /// </summary>
-    private void OnDoAfter(EntityUid uid, DrinkComponent component, ConsumeDoAfterEvent args)
+    private void OnDoAfter(Entity<DrinkComponent> entity, ref ConsumeDoAfterEvent args)
     {
-        if (args.Handled || args.Cancelled || component.Deleted)
+        if (args.Handled || args.Cancelled || entity.Comp.Deleted)
             return;
 
         if (!TryComp<BodyComponent>(args.Target, out var body))
             return;
 
-        if (!_solutionContainer.TryGetSolution(args.Used, args.Solution, out var solution))
+        if (args.Used is null || !_solutionContainer.TryGetSolution(args.Used.Value, args.Solution, out var soln, out var solution))
             return;
 
         // TODO this should really be checked every tick.
@@ -326,8 +322,8 @@ public sealed class DrinkSystem : EntitySystem
         if (!_interaction.InRangeUnobstructed(args.User, args.Target.Value))
             return;
 
-        var transferAmount = FixedPoint2.Min(component.TransferAmount, solution.Volume);
-        var drained = _solutionContainer.SplitSolution(uid, solution, transferAmount);
+        var transferAmount = FixedPoint2.Min(entity.Comp.TransferAmount, solution.Volume);
+        var drained = _solutionContainer.SplitSolution(soln.Value, transferAmount);
         var forceDrink = args.User != args.Target;
 
         args.Handled = true;
@@ -344,11 +340,11 @@ public sealed class DrinkSystem : EntitySystem
                 return;
             }
 
-            _solutionContainer.Refill(args.Target.Value, solution, drained);
+            _solutionContainer.Refill(args.Target.Value, soln.Value, drained);
             return;
         }
 
-        var firstStomach = stomachs.FirstOrNull(stomach => _stomach.CanTransferSolution(stomach.Comp.Owner, drained));
+        var firstStomach = stomachs.FirstOrNull(stomach => _stomach.CanTransferSolution(stomach.Comp.Owner, drained, stomach.Comp));
 
         //All stomachs are full or can't handle whatever solution we have.
         if (firstStomach == null)
@@ -361,7 +357,7 @@ public sealed class DrinkSystem : EntitySystem
                 _puddle.TrySpillAt(args.Target.Value, drained, out _);
             }
             else
-                _solutionContainer.TryAddSolution(uid, solution, drained);
+                _solutionContainer.TryAddSolution(soln.Value, drained);
 
             return;
         }
@@ -380,7 +376,7 @@ public sealed class DrinkSystem : EntitySystem
                 args.User, args.User);
 
             // log successful forced drinking
-            _adminLogger.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(uid):user} forced {ToPrettyString(args.User):target} to drink {ToPrettyString(uid):drink}");
+            _adminLogger.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(entity.Owner):user} forced {ToPrettyString(args.User):target} to drink {ToPrettyString(entity.Owner):drink}");
         }
         else
         {
@@ -391,24 +387,24 @@ public sealed class DrinkSystem : EntitySystem
                 Loc.GetString("drink-component-try-use-drink-success-slurp"), args.User, Filter.PvsExcept(args.User), true);
 
             // log successful voluntary drinking
-            _adminLogger.Add(LogType.Ingestion, LogImpact.Low, $"{ToPrettyString(args.User):target} drank {ToPrettyString(uid):drink}");
+            _adminLogger.Add(LogType.Ingestion, LogImpact.Low, $"{ToPrettyString(args.User):target} drank {ToPrettyString(entity.Owner):drink}");
         }
 
-        _audio.PlayPvs(component.UseSound, args.Target.Value, AudioParams.Default.WithVolume(-2f));
+        _audio.PlayPvs(entity.Comp.UseSound, args.Target.Value, AudioParams.Default.WithVolume(-2f));
 
         _reaction.DoEntityReaction(args.Target.Value, solution, ReactionMethod.Ingestion);
         //TODO: Grab the stomach UIDs somehow without using Owner
         _stomach.TryTransferSolution(firstStomach.Value.Comp.Owner, drained, firstStomach.Value.Comp);
 
-        _forensics.TransferDna(uid, args.Target.Value);
+        _forensics.TransferDna(entity, args.Target.Value);
 
         if (!forceDrink && solution.Volume > 0)
             args.Repeat = true;
     }
 
-    private void AddDrinkVerb(EntityUid uid, DrinkComponent component, GetVerbsEvent<AlternativeVerb> ev)
+    private void AddDrinkVerb(Entity<DrinkComponent> entity, ref GetVerbsEvent<AlternativeVerb> ev)
     {
-        if (uid == ev.User ||
+        if (entity.Owner == ev.User ||
             !ev.CanInteract ||
             !ev.CanAccess ||
             !TryComp<BodyComponent>(ev.User, out var body) ||
@@ -416,16 +412,17 @@ public sealed class DrinkSystem : EntitySystem
             return;
 
         // no drinking from living drinks, have to kill them first.
-        if (_mobState.IsAlive(uid))
+        if (_mobState.IsAlive(entity))
             return;
 
+        var user = ev.User;
         AlternativeVerb verb = new()
         {
             Act = () =>
             {
-                TryDrink(ev.User, ev.User, component, uid);
+                TryDrink(user, user, entity.Comp, entity);
             },
-            Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/drink.svg.192dpi.png")),
+            Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/drink.svg.192dpi.png")),
             Text = Loc.GetString("drink-system-verb-drink"),
             Priority = 2
         };
index e01f199ae1096bebe3a87193250c1d5a1dbba916..4a18696cea1aa184fd846717a9d3247ce86336a5 100644 (file)
@@ -1,13 +1,10 @@
-using System.Linq;
-using System.Text;
 using Content.Server.Nutrition.Components;
 using Content.Shared.CCVar;
 using Content.Shared.Chemistry.Components;
-using Content.Shared.Chemistry.Reagent;
 using Content.Shared.Nutrition;
-using Microsoft.VisualBasic;
 using Robust.Shared.Configuration;
 using Robust.Shared.Prototypes;
+using System.Linq;
 
 namespace Content.Server.Nutrition.EntitySystems;
 
index b18c77fffb72a757f297e85807da3636ee62ea69..95a40a9409664e26807ed2d3ffae58ea6c7e157a 100644 (file)
@@ -1,6 +1,6 @@
-using System.Linq;
 using Content.Server.Body.Components;
 using Content.Server.Body.Systems;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Inventory;
 using Content.Server.Nutrition.Components;
 using Content.Server.Popups;
@@ -9,7 +9,6 @@ using Content.Shared.Administration.Logs;
 using Content.Shared.Body.Components;
 using Content.Shared.Body.Organ;
 using Content.Shared.Chemistry;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.Database;
 using Content.Shared.DoAfter;
@@ -27,11 +26,9 @@ using Content.Shared.Stacks;
 using Content.Shared.Storage;
 using Content.Shared.Verbs;
 using Robust.Shared.Audio;
-using Robust.Shared.Player;
-using Robust.Shared.Utility;
-using Content.Shared.Tag;
-using Content.Shared.Storage;
 using Robust.Shared.Audio.Systems;
+using Robust.Shared.Utility;
+using System.Linq;
 
 namespace Content.Server.Nutrition.EntitySystems;
 
@@ -75,24 +72,24 @@ public sealed class FoodSystem : EntitySystem
     /// <summary>
     /// Eat item
     /// </summary>
-    private void OnUseFoodInHand(EntityUid uid, FoodComponent foodComponent, UseInHandEvent ev)
+    private void OnUseFoodInHand(Entity<FoodComponent> entity, ref UseInHandEvent ev)
     {
         if (ev.Handled)
             return;
 
-        var result = TryFeed(ev.User, ev.User, uid, foodComponent);
+        var result = TryFeed(ev.User, ev.User, entity, entity.Comp);
         ev.Handled = result.Handled;
     }
 
     /// <summary>
     /// Feed someone else
     /// </summary>
-    private void OnFeedFood(EntityUid uid, FoodComponent foodComponent, AfterInteractEvent args)
+    private void OnFeedFood(Entity<FoodComponent> entity, ref AfterInteractEvent args)
     {
         if (args.Handled || args.Target == null || !args.CanReach)
             return;
 
-        var result = TryFeed(args.User, args.Target.Value, uid, foodComponent);
+        var result = TryFeed(args.User, args.Target.Value, entity, entity.Comp);
         args.Handled = result.Handled;
     }
 
@@ -112,7 +109,7 @@ public sealed class FoodSystem : EntitySystem
         if (_openable.IsClosed(food, user))
             return (false, true);
 
-        if (!_solutionContainer.TryGetSolution(food, foodComp.Solution, out var foodSolution) || foodSolution.Name == null)
+        if (!_solutionContainer.TryGetSolution(food, foodComp.Solution, out _, out var foodSolution))
             return (false, false);
 
         if (!_body.TryGetBodyOrganComponents<StomachComponent>(target, out var stomachs, body))
@@ -177,7 +174,7 @@ public sealed class FoodSystem : EntitySystem
         var doAfterArgs = new DoAfterArgs(EntityManager,
             user,
             forceFeed ? foodComp.ForceFeedDelay : foodComp.Delay,
-            new ConsumeDoAfterEvent(foodSolution.Name, flavors),
+            new ConsumeDoAfterEvent(foodComp.Solution, flavors),
             eventTarget: food,
             target: target,
             used: food)
@@ -196,9 +193,9 @@ public sealed class FoodSystem : EntitySystem
         return (true, true);
     }
 
-    private void OnDoAfter(EntityUid uid, FoodComponent component, ConsumeDoAfterEvent args)
+    private void OnDoAfter(Entity<FoodComponent> entity, ref ConsumeDoAfterEvent args)
     {
-        if (args.Cancelled || args.Handled || component.Deleted || args.Target == null)
+        if (args.Cancelled || args.Handled || entity.Comp.Deleted || args.Target == null)
             return;
 
         if (!TryComp<BodyComponent>(args.Target.Value, out var body))
@@ -207,10 +204,10 @@ public sealed class FoodSystem : EntitySystem
         if (!_body.TryGetBodyOrganComponents<StomachComponent>(args.Target.Value, out var stomachs, body))
             return;
 
-        if (!_solutionContainer.TryGetSolution(args.Used, args.Solution, out var solution))
+        if (args.Used is null || !_solutionContainer.TryGetSolution(args.Used.Value, args.Solution, out var soln, out var solution))
             return;
 
-        if (!TryGetRequiredUtensils(args.User, component, out var utensils))
+        if (!TryGetRequiredUtensils(args.User, entity.Comp, out var utensils))
             return;
 
         // TODO this should really be checked every tick.
@@ -224,9 +221,9 @@ public sealed class FoodSystem : EntitySystem
         var forceFeed = args.User != args.Target;
 
         args.Handled = true;
-        var transferAmount = component.TransferAmount != null ? FixedPoint2.Min((FixedPoint2) component.TransferAmount, solution.Volume) : solution.Volume;
+        var transferAmount = entity.Comp.TransferAmount != null ? FixedPoint2.Min((FixedPoint2) entity.Comp.TransferAmount, solution.Volume) : solution.Volume;
 
-        var split = _solutionContainer.SplitSolution(uid, solution, transferAmount);
+        var split = _solutionContainer.SplitSolution(soln.Value, transferAmount);
 
         //TODO: Get the stomach UID somehow without nabbing owner
         // Get the stomach with the highest available solution volume
@@ -235,11 +232,10 @@ public sealed class FoodSystem : EntitySystem
         foreach (var (stomach, _) in stomachs)
         {
             var owner = stomach.Owner;
-            if (!_stomach.CanTransferSolution(owner, split))
+            if (!_stomach.CanTransferSolution(owner, split, stomach))
                 continue;
 
-            if (!_solutionContainer.TryGetSolution(owner, StomachSystem.DefaultSolutionName,
-                    out var stomachSol))
+            if (!_solutionContainer.ResolveSolution(owner, StomachSystem.DefaultSolutionName, ref stomach.Solution, out var stomachSol))
                 continue;
 
             if (stomachSol.AvailableVolume <= highestAvailable)
@@ -252,7 +248,7 @@ public sealed class FoodSystem : EntitySystem
         // No stomach so just popup a message that they can't eat.
         if (stomachToUse == null)
         {
-            _solutionContainer.TryAddSolution(uid, solution, split);
+            _solutionContainer.TryAddSolution(soln.Value, split);
             _popup.PopupEntity(forceFeed ? Loc.GetString("food-system-you-cannot-eat-any-more-other") : Loc.GetString("food-system-you-cannot-eat-any-more"), args.Target.Value, args.User);
             return;
         }
@@ -266,23 +262,22 @@ public sealed class FoodSystem : EntitySystem
         {
             var targetName = Identity.Entity(args.Target.Value, EntityManager);
             var userName = Identity.Entity(args.User, EntityManager);
-            _popup.PopupEntity(Loc.GetString("food-system-force-feed-success", ("user", userName), ("flavors", flavors)),
-                uid, uid);
+            _popup.PopupEntity(Loc.GetString("food-system-force-feed-success", ("user", userName), ("flavors", flavors)), entity.Owner, entity.Owner);
 
             _popup.PopupEntity(Loc.GetString("food-system-force-feed-success-user", ("target", targetName)), args.User, args.User);
 
             // log successful force feed
-            _adminLogger.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(uid):user} forced {ToPrettyString(args.User):target} to eat {ToPrettyString(uid):food}");
+            _adminLogger.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(entity.Owner):user} forced {ToPrettyString(args.User):target} to eat {ToPrettyString(entity.Owner):food}");
         }
         else
         {
-            _popup.PopupEntity(Loc.GetString(component.EatMessage, ("food", uid), ("flavors", flavors)), args.User, args.User);
+            _popup.PopupEntity(Loc.GetString(entity.Comp.EatMessage, ("food", entity.Owner), ("flavors", flavors)), args.User, args.User);
 
             // log successful voluntary eating
-            _adminLogger.Add(LogType.Ingestion, LogImpact.Low, $"{ToPrettyString(args.User):target} ate {ToPrettyString(uid):food}");
+            _adminLogger.Add(LogType.Ingestion, LogImpact.Low, $"{ToPrettyString(args.User):target} ate {ToPrettyString(entity.Owner):food}");
         }
 
-        _audio.PlayPvs(component.UseSound, args.Target.Value, AudioParams.Default.WithVolume(-1f));
+        _audio.PlayPvs(entity.Comp.UseSound, args.Target.Value, AudioParams.Default.WithVolume(-1f));
 
         // Try to break all used utensils
         foreach (var utensil in utensils)
@@ -292,24 +287,24 @@ public sealed class FoodSystem : EntitySystem
 
         args.Repeat = !forceFeed;
 
-        if (TryComp<StackComponent>(uid, out var stack))
+        if (TryComp<StackComponent>(entity, out var stack))
         {
             //Not deleting whole stack piece will make troubles with grinding object
             if (stack.Count > 1)
             {
-                _stack.SetCount(uid, stack.Count - 1);
-                _solutionContainer.TryAddSolution(uid, solution, split);
+                _stack.SetCount(entity.Owner, stack.Count - 1);
+                _solutionContainer.TryAddSolution(soln.Value, split);
                 return;
             }
         }
-        else if (GetUsesRemaining(uid, component) > 0)
+        else if (GetUsesRemaining(entity.Owner, entity.Comp) > 0)
         {
             return;
         }
 
         // don't try to repeat if its being deleted
         args.Repeat = false;
-        DeleteAndSpawnTrash(component, uid, args.User);
+        DeleteAndSpawnTrash(entity.Comp, entity.Owner, args.User);
     }
 
     public void DeleteAndSpawnTrash(FoodComponent component, EntityUid food, EntityUid user)
@@ -345,9 +340,9 @@ public sealed class FoodSystem : EntitySystem
         QueueDel(food);
     }
 
-    private void AddEatVerb(EntityUid uid, FoodComponent component, GetVerbsEvent<AlternativeVerb> ev)
+    private void AddEatVerb(Entity<FoodComponent> entity, ref GetVerbsEvent<AlternativeVerb> ev)
     {
-        if (uid == ev.User ||
+        if (entity.Owner == ev.User ||
             !ev.CanInteract ||
             !ev.CanAccess ||
             !TryComp<BodyComponent>(ev.User, out var body) ||
@@ -355,20 +350,21 @@ public sealed class FoodSystem : EntitySystem
             return;
 
         // have to kill mouse before eating it
-        if (_mobState.IsAlive(uid) && component.RequireDead)
+        if (_mobState.IsAlive(entity) && entity.Comp.RequireDead)
             return;
 
         // only give moths eat verb for clothes since it would just fail otherwise
-        if (!IsDigestibleBy(uid, component, stomachs))
+        if (!IsDigestibleBy(entity, entity.Comp, stomachs))
             return;
 
+        var user = ev.User;
         AlternativeVerb verb = new()
         {
             Act = () =>
             {
-                TryFeed(ev.User, ev.User, uid, component);
+                TryFeed(user, user, entity, entity.Comp);
             },
-            Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/cutlery.svg.192dpi.png")),
+            Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/cutlery.svg.192dpi.png")),
             Text = Loc.GetString("food-system-verb-eat"),
             Priority = -1
         };
@@ -416,7 +412,7 @@ public sealed class FoodSystem : EntitySystem
         }
 
         if (component.RequiresSpecialDigestion)
-                return false;
+            return false;
 
         return digestible;
     }
@@ -462,14 +458,14 @@ public sealed class FoodSystem : EntitySystem
     /// <summary>
     ///     Block ingestion attempts based on the equipped mask or head-wear
     /// </summary>
-    private void OnInventoryIngestAttempt(EntityUid uid, InventoryComponent component, IngestionAttemptEvent args)
+    private void OnInventoryIngestAttempt(Entity<InventoryComponent> entity, ref IngestionAttemptEvent args)
     {
         if (args.Cancelled)
             return;
 
         IngestionBlockerComponent? blocker;
 
-        if (_inventory.TryGetSlotEntity(uid, "mask", out var maskUid) &&
+        if (_inventory.TryGetSlotEntity(entity.Owner, "mask", out var maskUid) &&
             TryComp(maskUid, out blocker) &&
             blocker.Enabled)
         {
@@ -478,7 +474,7 @@ public sealed class FoodSystem : EntitySystem
             return;
         }
 
-        if (_inventory.TryGetSlotEntity(uid, "head", out var headUid) &&
+        if (_inventory.TryGetSlotEntity(entity.Owner, "head", out var headUid) &&
             TryComp(headUid, out blocker) &&
             blocker.Enabled)
         {
@@ -516,7 +512,7 @@ public sealed class FoodSystem : EntitySystem
         if (!Resolve(uid, ref comp))
             return 0;
 
-        if (!_solutionContainer.TryGetSolution(uid, comp.Solution, out var solution) || solution.Volume == 0)
+        if (!_solutionContainer.TryGetSolution(uid, comp.Solution, out _, out var solution) || solution.Volume == 0)
             return 0;
 
         // eat all in 1 go, so non empty is 1 bite
index 56f5d9adeb24be379228b842c769ede02240171b..064d316beff5b08082ff814a13e57c8f8434c5e3 100644 (file)
@@ -1,7 +1,6 @@
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Nutrition.Components;
 using Content.Shared.Chemistry.Components;
-using Content.Shared.Chemistry.Components.SolutionManager;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Examine;
 using Content.Shared.FixedPoint;
 using Content.Shared.Hands.EntitySystems;
@@ -9,7 +8,6 @@ using Content.Shared.Interaction;
 using Robust.Shared.Audio;
 using Robust.Shared.Audio.Systems;
 using Robust.Shared.Containers;
-using Robust.Shared.Player;
 
 namespace Content.Server.Nutrition.EntitySystems
 {
@@ -29,12 +27,12 @@ namespace Content.Server.Nutrition.EntitySystems
             SubscribeLocalEvent<SliceableFoodComponent, ComponentStartup>(OnComponentStartup);
         }
 
-        private void OnInteractUsing(EntityUid uid, SliceableFoodComponent component, InteractUsingEvent args)
+        private void OnInteractUsing(Entity<SliceableFoodComponent> entity, ref InteractUsingEvent args)
         {
             if (args.Handled)
                 return;
 
-            if (TrySliceFood(uid, args.User, args.Used, component))
+            if (TrySliceFood(entity, args.User, args.Used, entity.Comp))
                 args.Handled = true;
         }
 
@@ -47,7 +45,7 @@ namespace Content.Server.Nutrition.EntitySystems
                 return false;
             }
 
-            if (!_solutionContainerSystem.TryGetSolution(uid, food.Solution, out var solution))
+            if (!_solutionContainerSystem.TryGetSolution(uid, food.Solution, out var soln, out var solution))
             {
                 return false;
             }
@@ -59,8 +57,7 @@ namespace Content.Server.Nutrition.EntitySystems
 
             var sliceUid = Slice(uid, user, component, transform);
 
-            var lostSolution = _solutionContainerSystem.SplitSolution(uid, solution,
-                solution.Volume / FixedPoint2.New(component.Count));
+            var lostSolution = _solutionContainerSystem.SplitSolution(soln.Value, solution.Volume / FixedPoint2.New(component.Count));
 
             // Fill new slice
             FillSlice(sliceUid, lostSolution);
@@ -137,27 +134,26 @@ namespace Content.Server.Nutrition.EntitySystems
         {
             // Replace all reagents on prototype not just copying poisons (example: slices of eaten pizza should have less nutrition)
             if (TryComp<FoodComponent>(sliceUid, out var sliceFoodComp) &&
-                _solutionContainerSystem.TryGetSolution(sliceUid, sliceFoodComp.Solution, out var itsSolution))
+                _solutionContainerSystem.TryGetSolution(sliceUid, sliceFoodComp.Solution, out var itsSoln, out var itsSolution))
             {
-                _solutionContainerSystem.RemoveAllSolution(sliceUid, itsSolution);
+                _solutionContainerSystem.RemoveAllSolution(itsSoln.Value);
 
                 var lostSolutionPart = solution.SplitSolution(itsSolution.AvailableVolume);
-                _solutionContainerSystem.TryAddSolution(sliceUid, itsSolution, lostSolutionPart);
+                _solutionContainerSystem.TryAddSolution(itsSoln.Value, lostSolutionPart);
             }
         }
 
-        private void OnComponentStartup(EntityUid uid, SliceableFoodComponent component, ComponentStartup args)
+        private void OnComponentStartup(Entity<SliceableFoodComponent> entity, ref ComponentStartup args)
         {
-            component.Count = component.TotalCount;
-            var foodComp = EnsureComp<FoodComponent>(uid);
+            entity.Comp.Count = entity.Comp.TotalCount;
 
-            EnsureComp<SolutionContainerManagerComponent>(uid);
-            _solutionContainerSystem.EnsureSolution(uid, foodComp.Solution);
+            var foodComp = EnsureComp<FoodComponent>(entity);
+            _solutionContainerSystem.EnsureSolution(entity.Owner, foodComp.Solution);
         }
 
-        private void OnExamined(EntityUid uid, SliceableFoodComponent component, ExaminedEvent args)
+        private void OnExamined(Entity<SliceableFoodComponent> entity, ref ExaminedEvent args)
         {
-            args.PushMarkup(Loc.GetString("sliceable-food-component-on-examine-remaining-slices-text", ("remainingCount", component.Count)));
+            args.PushMarkup(Loc.GetString("sliceable-food-component-on-examine-remaining-slices-text", ("remainingCount", entity.Comp.Count)));
         }
     }
 }
index 99a96fd2b331105ff070d9b751ef8e9ad074edb3..4e672444d18181a0abc04766baf3f18a224b1e7b 100644 (file)
@@ -1,6 +1,6 @@
 using Content.Server.Nutrition.Components;
-using Content.Shared.Nutrition.Components;
 using Content.Shared.Interaction;
+using Content.Shared.Nutrition.Components;
 using Content.Shared.Smoking;
 using Content.Shared.Temperature;
 
@@ -16,27 +16,27 @@ namespace Content.Server.Nutrition.EntitySystems
             SubscribeLocalEvent<CigarComponent, AfterInteractEvent>(OnCigarAfterInteract);
         }
 
-        private void OnCigarActivatedEvent(EntityUid uid, CigarComponent component, ActivateInWorldEvent args)
+        private void OnCigarActivatedEvent(Entity<CigarComponent> entity, ref ActivateInWorldEvent args)
         {
             if (args.Handled)
                 return;
 
-            if (!EntityManager.TryGetComponent(uid, out SmokableComponent? smokable))
+            if (!EntityManager.TryGetComponent(entity, out SmokableComponent? smokable))
                 return;
 
             if (smokable.State != SmokableState.Lit)
                 return;
 
-            SetSmokableState(uid, SmokableState.Burnt, smokable);
+            SetSmokableState(entity, SmokableState.Burnt, smokable);
             args.Handled = true;
         }
 
-        private void OnCigarInteractUsingEvent(EntityUid uid, CigarComponent component, InteractUsingEvent args)
+        private void OnCigarInteractUsingEvent(Entity<CigarComponent> entity, ref InteractUsingEvent args)
         {
             if (args.Handled)
                 return;
 
-            if (!EntityManager.TryGetComponent(uid, out SmokableComponent? smokable))
+            if (!EntityManager.TryGetComponent(entity, out SmokableComponent? smokable))
                 return;
 
             if (smokable.State != SmokableState.Unlit)
@@ -48,16 +48,16 @@ namespace Content.Server.Nutrition.EntitySystems
             if (!isHotEvent.IsHot)
                 return;
 
-            SetSmokableState(uid, SmokableState.Lit, smokable);
+            SetSmokableState(entity, SmokableState.Lit, smokable);
             args.Handled = true;
         }
 
-        public void OnCigarAfterInteract(EntityUid uid, CigarComponent component, AfterInteractEvent args)
+        public void OnCigarAfterInteract(Entity<CigarComponent> entity, ref AfterInteractEvent args)
         {
             var targetEntity = args.Target;
             if (targetEntity == null ||
                 !args.CanReach ||
-                !EntityManager.TryGetComponent(uid, out SmokableComponent? smokable) ||
+                !EntityManager.TryGetComponent(entity, out SmokableComponent? smokable) ||
                 smokable.State == SmokableState.Lit)
                 return;
 
@@ -67,13 +67,13 @@ namespace Content.Server.Nutrition.EntitySystems
             if (!isHotEvent.IsHot)
                 return;
 
-            SetSmokableState(uid, SmokableState.Lit, smokable);
+            SetSmokableState(entity, SmokableState.Lit, smokable);
             args.Handled = true;
         }
 
-        private void OnCigarSolutionEmptyEvent(EntityUid uid, CigarComponent component, SmokableSolutionEmptyEvent args)
+        private void OnCigarSolutionEmptyEvent(Entity<CigarComponent> entity, ref SmokableSolutionEmptyEvent args)
         {
-            SetSmokableState(uid, SmokableState.Burnt);
+            SetSmokableState(entity, SmokableState.Burnt);
         }
     }
 }
index 772847e8df2abc8a799fac2f7cd2f36ff541d34d..3950c73eb43e240d966bfe6beb5051dc5e4679d8 100644 (file)
@@ -1,8 +1,8 @@
 using Content.Server.Nutrition.Components;
-using Content.Shared.Nutrition.Components;
 using Content.Shared.Chemistry.Components.SolutionManager;
 using Content.Shared.Containers.ItemSlots;
 using Content.Shared.Interaction;
+using Content.Shared.Nutrition.Components;
 using Content.Shared.Smoking;
 using Content.Shared.Temperature;
 
@@ -20,17 +20,17 @@ namespace Content.Server.Nutrition.EntitySystems
             SubscribeLocalEvent<SmokingPipeComponent, ComponentInit>(OnComponentInit);
         }
 
-        public void OnComponentInit(EntityUid uid, SmokingPipeComponent pipe, ComponentInit args)
+        public void OnComponentInit(Entity<SmokingPipeComponent> entity, ref ComponentInit args)
         {
-            _itemSlotsSystem.AddItemSlot(uid, SmokingPipeComponent.BowlSlotId, pipe.BowlSlot);
+            _itemSlotsSystem.AddItemSlot(entity, SmokingPipeComponent.BowlSlotId, entity.Comp.BowlSlot);
         }
 
-        private void OnPipeInteractUsingEvent(EntityUid uid, SmokingPipeComponent component, InteractUsingEvent args)
+        private void OnPipeInteractUsingEvent(Entity<SmokingPipeComponent> entity, ref InteractUsingEvent args)
         {
             if (args.Handled)
                 return;
 
-            if (!EntityManager.TryGetComponent(uid, out SmokableComponent? smokable))
+            if (!EntityManager.TryGetComponent(entity, out SmokableComponent? smokable))
                 return;
 
             if (smokable.State != SmokableState.Unlit)
@@ -42,17 +42,17 @@ namespace Content.Server.Nutrition.EntitySystems
             if (!isHotEvent.IsHot)
                 return;
 
-            if (TryTransferReagents(component, smokable))
-                SetSmokableState(uid, SmokableState.Lit, smokable);
+            if (TryTransferReagents(entity.Comp, smokable))
+                SetSmokableState(entity, SmokableState.Lit, smokable);
             args.Handled = true;
         }
 
-        public void OnPipeAfterInteract(EntityUid uid, SmokingPipeComponent component, AfterInteractEvent args)
+        public void OnPipeAfterInteract(Entity<SmokingPipeComponent> entity, ref AfterInteractEvent args)
         {
             var targetEntity = args.Target;
             if (targetEntity == null ||
                 !args.CanReach ||
-                !EntityManager.TryGetComponent(uid, out SmokableComponent? smokable) ||
+                !EntityManager.TryGetComponent(entity, out SmokableComponent? smokable) ||
                 smokable.State == SmokableState.Lit)
                 return;
 
@@ -62,15 +62,15 @@ namespace Content.Server.Nutrition.EntitySystems
             if (!isHotEvent.IsHot)
                 return;
 
-            if(TryTransferReagents(component, smokable))
-                SetSmokableState(uid, SmokableState.Lit, smokable);
+            if (TryTransferReagents(entity.Comp, smokable))
+                SetSmokableState(entity, SmokableState.Lit, smokable);
             args.Handled = true;
         }
 
-        private void OnPipeSolutionEmptyEvent(EntityUid uid, SmokingPipeComponent component, SmokableSolutionEmptyEvent args)
+        private void OnPipeSolutionEmptyEvent(Entity<SmokingPipeComponent> entity, ref SmokableSolutionEmptyEvent args)
         {
-            _itemSlotsSystem.SetLock(component.Owner, component.BowlSlot, false);
-            SetSmokableState(uid, SmokableState.Unlit);
+            _itemSlotsSystem.SetLock(entity, entity.Comp.BowlSlot, false);
+            SetSmokableState(entity, SmokableState.Unlit);
         }
 
         // Convert smokable item into reagents to be smoked
@@ -82,12 +82,13 @@ namespace Content.Server.Nutrition.EntitySystems
             EntityUid contents = component.BowlSlot.Item.Value;
 
             if (!TryComp<SolutionContainerManagerComponent>(contents, out var reagents) ||
-                !_solutionContainerSystem.TryGetSolution(smokable.Owner, smokable.Solution, out var pipeSolution))
+                !_solutionContainerSystem.TryGetSolution(smokable.Owner, smokable.Solution, out var pipeSolution, out _))
                 return false;
 
-            foreach (var reagentSolution in reagents.Solutions)
+            foreach (var (_, soln) in _solutionContainerSystem.EnumerateSolutions((contents, reagents)))
             {
-                _solutionContainerSystem.TryAddSolution(smokable.Owner, pipeSolution, reagentSolution.Value);
+                var reagentSolution = soln.Comp.Solution;
+                _solutionContainerSystem.TryAddSolution(pipeSolution.Value, reagentSolution);
             }
 
             EntityManager.DeleteEntity(contents);
index 4dd9aff9ac8bb4393b82d484eb1fac2f2f4e1ed9..fe0d1d0c1686f99bf808e42b62d73f8ec255f56f 100644 (file)
@@ -1,18 +1,18 @@
-using Content.Server.Nutrition.Components;
+using Content.Server.Atmos;
+using Content.Server.Atmos.EntitySystems;
 using Content.Server.Body.Components;
-using Content.Shared.Interaction;
 using Content.Server.DoAfter;
-using System.Threading;
 using Content.Server.Explosion.EntitySystems;
-using Content.Shared.Damage;
+using Content.Server.Nutrition.Components;
 using Content.Server.Popups;
-using Content.Shared.IdentityManagement;
+using Content.Shared.Damage;
 using Content.Shared.DoAfter;
-using Content.Shared.Emag.Systems;
 using Content.Shared.Emag.Components;
+using Content.Shared.Emag.Systems;
+using Content.Shared.IdentityManagement;
+using Content.Shared.Interaction;
 using Content.Shared.Nutrition;
-using Content.Server.Atmos.EntitySystems;
-using Content.Server.Atmos;
+using System.Threading;
 
 /// <summary>
 /// System for vapes
@@ -35,16 +35,14 @@ namespace Content.Server.Nutrition.EntitySystems
             SubscribeLocalEvent<VapeComponent, GotEmaggedEvent>(OnEmagged);
         }
 
-        private void OnVapeInteraction(EntityUid uid, VapeComponent comp, AfterInteractEvent args)
+        private void OnVapeInteraction(Entity<VapeComponent> entity, ref AfterInteractEvent args)
         {
-            _solutionContainerSystem.TryGetRefillableSolution(uid, out var solution);
-
-            var delay = comp.Delay;
+            var delay = entity.Comp.Delay;
             var forced = true;
             var exploded = false;
 
             if (!args.CanReach
-                || solution == null
+                || !_solutionContainerSystem.TryGetRefillableSolution(entity.Owner, out _, out var solution)
                 || !HasComp<BloodstreamComponent>(args.Target)
                 || _foodSystem.IsMouthBlocked(args.Target.Value, args.User))
             {
@@ -61,14 +59,14 @@ namespace Content.Server.Nutrition.EntitySystems
 
             if (args.Target == args.User)
             {
-                delay = comp.UserDelay;
+                delay = entity.Comp.UserDelay;
                 forced = false;
             }
 
-            if (comp.ExplodeOnUse || HasComp<EmaggedComponent>(uid))
+            if (entity.Comp.ExplodeOnUse || HasComp<EmaggedComponent>(entity.Owner))
             {
-                _explosionSystem.QueueExplosion(uid, "Default", comp.ExplosionIntensity, 0.5f, 3, canCreateVacuum: false);
-                EntityManager.DeleteEntity(uid);
+                _explosionSystem.QueueExplosion(entity.Owner, "Default", entity.Comp.ExplosionIntensity, 0.5f, 3, canCreateVacuum: false);
+                EntityManager.DeleteEntity(entity);
                 exploded = true;
             }
             else
@@ -80,11 +78,11 @@ namespace Content.Server.Nutrition.EntitySystems
                 // just re-use the existing RiggableSystem.
                 foreach (var name in solution.Contents)
                 {
-                    if (name.Reagent.Prototype != comp.SolutionNeeded)
+                    if (name.Reagent.Prototype != entity.Comp.SolutionNeeded)
                     {
                         exploded = true;
-                        _explosionSystem.QueueExplosion(uid, "Default", comp.ExplosionIntensity, 0.5f, 3, canCreateVacuum: false);
-                        EntityManager.DeleteEntity(uid);
+                        _explosionSystem.QueueExplosion(entity.Owner, "Default", entity.Comp.ExplosionIntensity, 0.5f, 3, canCreateVacuum: false);
+                        EntityManager.DeleteEntity(entity);
                         break;
                     }
                 }
@@ -113,7 +111,7 @@ namespace Content.Server.Nutrition.EntitySystems
             if (!exploded)
             {
                 var vapeDoAfterEvent = new VapeDoAfterEvent(solution, forced);
-                _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, delay, vapeDoAfterEvent, uid, target: args.Target, used: uid)
+                _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, delay, vapeDoAfterEvent, entity.Owner, target: args.Target, used: entity.Owner)
                 {
                     BreakOnTargetMove = true,
                     BreakOnUserMove = false,
@@ -121,9 +119,9 @@ namespace Content.Server.Nutrition.EntitySystems
                 });
             }
             args.Handled = true;
-               }
+        }
 
-        private void OnVapeDoAfter(EntityUid uid, VapeComponent comp, VapeDoAfterEvent args)
+        private void OnVapeDoAfter(Entity<VapeComponent> entity, ref VapeDoAfterEvent args)
         {
             if (args.Handled
             || args.Args.Target == null)
@@ -136,10 +134,10 @@ namespace Content.Server.Nutrition.EntitySystems
             }
 
             //Smoking kills(your lungs, but there is no organ damage yet)
-            _damageableSystem.TryChangeDamage(args.Args.Target.Value, comp.Damage, true);
+            _damageableSystem.TryChangeDamage(args.Args.Target.Value, entity.Comp.Damage, true);
 
-            var merger = new GasMixture(1) { Temperature = args.Solution.Temperature};
-            merger.SetMoles(comp.GasType, args.Solution.Volume.Value / comp.ReductionFactor);
+            var merger = new GasMixture(1) { Temperature = args.Solution.Temperature };
+            merger.SetMoles(entity.Comp.GasType, args.Solution.Volume.Value / entity.Comp.ReductionFactor);
 
             _atmosphereSystem.Merge(environment, merger);
 
@@ -165,9 +163,9 @@ namespace Content.Server.Nutrition.EntitySystems
                     args.Args.Target.Value);
             }
         }
-        private void OnEmagged(EntityUid uid, VapeComponent component, ref GotEmaggedEvent args)
+        private void OnEmagged(Entity<VapeComponent> entity, ref GotEmaggedEvent args)
         {
             args.Handled = true;
         }
-       }
+    }
 }
index 63a558238b061df1bf4c567c83cb41dbe58f7fab..a33c944c99482e690e4696908c69155723fcdf2f 100644 (file)
@@ -1,22 +1,22 @@
 using Content.Server.Atmos.EntitySystems;
 using Content.Server.Body.Components;
 using Content.Server.Body.Systems;
-using Content.Shared.Nutrition.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
+using Content.Server.Forensics;
 using Content.Shared.Chemistry;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.Clothing.Components;
 using Content.Shared.Clothing.EntitySystems;
 using Content.Shared.FixedPoint;
 using Content.Shared.Inventory;
+using Content.Shared.Inventory.Events;
 using Content.Shared.Item;
+using Content.Shared.Nutrition.Components;
 using Content.Shared.Smoking;
 using Content.Shared.Temperature;
 using Robust.Server.GameObjects;
 using Robust.Shared.Containers;
 using System.Linq;
-using Content.Shared.Inventory.Events;
-using Content.Server.Forensics;
 
 namespace Content.Server.Nutrition.EntitySystems
 {
@@ -79,21 +79,21 @@ namespace Content.Server.Nutrition.EntitySystems
                 _active.Remove(uid);
         }
 
-        private void OnSmokableIsHotEvent(EntityUid uid, SmokableComponent component, IsHotEvent args)
+        private void OnSmokableIsHotEvent(Entity<SmokableComponent> entity, ref IsHotEvent args)
         {
-            args.IsHot = component.State == SmokableState.Lit;
+            args.IsHot = entity.Comp.State == SmokableState.Lit;
         }
 
-        private void OnSmokableShutdownEvent(EntityUid uid, SmokableComponent component, ComponentShutdown args)
+        private void OnSmokableShutdownEvent(Entity<SmokableComponent> entity, ref ComponentShutdown args)
         {
-            _active.Remove(uid);
+            _active.Remove(entity);
         }
 
-        private void OnSmokeableEquipEvent(EntityUid uid, SmokableComponent component, GotEquippedEvent args)
+        private void OnSmokeableEquipEvent(Entity<SmokableComponent> entity, ref GotEquippedEvent args)
         {
             if (args.Slot == "mask")
             {
-                _forensics.TransferDna(uid, args.Equipee, false);
+                _forensics.TransferDna(entity.Owner, args.Equipee, false);
             }
         }
 
@@ -113,7 +113,7 @@ namespace Content.Server.Nutrition.EntitySystems
                     continue;
                 }
 
-                if (!_solutionContainerSystem.TryGetSolution(uid, smokable.Solution, out var solution))
+                if (!_solutionContainerSystem.TryGetSolution(uid, smokable.Solution, out var soln, out var solution))
                 {
                     _active.Remove(uid);
                     continue;
@@ -123,14 +123,14 @@ namespace Content.Server.Nutrition.EntitySystems
                 {
                     var transform = Transform(uid);
 
-                    if (transform.GridUid is {} gridUid)
+                    if (transform.GridUid is { } gridUid)
                     {
                         var position = _transformSystem.GetGridOrMapTilePosition(uid, transform);
                         _atmos.HotspotExpose(gridUid, position, smokable.ExposeTemperature, smokable.ExposeVolume, uid, true);
                     }
                 }
 
-                var inhaledSolution = _solutionContainerSystem.SplitSolution(uid, solution, smokable.InhaleAmount * _timer);
+                var inhaledSolution = _solutionContainerSystem.SplitSolution(soln.Value, smokable.InhaleAmount * _timer);
 
                 if (solution.Volume == FixedPoint2.Zero)
                 {
index 22360b6492014586833ab4b7c7d55125aff6ce16..249c6b523913dc778609a7bbbc2946f45ae31817 100644 (file)
@@ -1,3 +1,4 @@
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Nutrition.Components;
 using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Components.SolutionManager;
@@ -15,37 +16,37 @@ namespace Content.Server.Nutrition.EntitySystems
         {
             base.Initialize();
             SubscribeLocalEvent<TrashOnSolutionEmptyComponent, ComponentStartup>(OnStartup);
-            SubscribeLocalEvent<TrashOnSolutionEmptyComponent, SolutionChangedEvent>(OnSolutionChange);
+            SubscribeLocalEvent<TrashOnSolutionEmptyComponent, SolutionContainerChangedEvent>(OnSolutionChange);
         }
 
-        public void OnStartup(EntityUid uid, TrashOnSolutionEmptyComponent component, ComponentStartup args)
+        public void OnStartup(Entity<TrashOnSolutionEmptyComponent> entity, ref ComponentStartup args)
         {
-            CheckSolutions(component);
+            CheckSolutions(entity);
         }
 
-        public void OnSolutionChange(EntityUid uid, TrashOnSolutionEmptyComponent component, SolutionChangedEvent args)
+        public void OnSolutionChange(Entity<TrashOnSolutionEmptyComponent> entity, ref SolutionContainerChangedEvent args)
         {
-            CheckSolutions(component);
+            CheckSolutions(entity);
         }
 
-        public void CheckSolutions(TrashOnSolutionEmptyComponent component)
+        public void CheckSolutions(Entity<TrashOnSolutionEmptyComponent> entity)
         {
-            if (!EntityManager.HasComponent<SolutionContainerManagerComponent>((component).Owner))
+            if (!EntityManager.HasComponent<SolutionContainerManagerComponent>(entity))
                 return;
 
-            if (_solutionContainerSystem.TryGetSolution(component.Owner, component.Solution, out var solution))
-                UpdateTags(component, solution);
+            if (_solutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.Solution, out _, out var solution))
+                UpdateTags(entity, solution);
         }
 
-        public void UpdateTags(TrashOnSolutionEmptyComponent component, Solution solution)
+        public void UpdateTags(Entity<TrashOnSolutionEmptyComponent> entity, Solution solution)
         {
             if (solution.Volume <= 0)
             {
-                _tagSystem.AddTag(component.Owner, "Trash");
+                _tagSystem.AddTag(entity.Owner, "Trash");
                 return;
             }
-            if (_tagSystem.HasTag(component.Owner, "Trash"))
-                _tagSystem.RemoveTag(component.Owner, "Trash");
+            if (_tagSystem.HasTag(entity.Owner, "Trash"))
+                _tagSystem.RemoveTag(entity.Owner, "Trash");
         }
     }
 }
index f57024087d595cd0a97c572e28c9122465a9d456..78a2de22d1c17678a177fd3250463d6051be1d15 100644 (file)
@@ -1,8 +1,7 @@
-using System.Linq;
 using Content.Server.Administration.Logs;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Explosion.EntitySystems;
 using Content.Shared.Chemistry.Components;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Database;
 using Content.Shared.Examine;
 using Content.Shared.Payload.Components;
@@ -10,14 +9,15 @@ using Content.Shared.Tag;
 using Robust.Shared.Containers;
 using Robust.Shared.Serialization.Manager;
 using Robust.Shared.Utility;
+using System.Linq;
 
 namespace Content.Server.Payload.EntitySystems;
 
 public sealed class PayloadSystem : EntitySystem
 {
     [Dependency] private readonly TagSystem _tagSystem = default!;
-    [Dependency] private readonly SolutionContainerSystem _solutionSystem = default!;
-    [Dependency] private readonly IAdminLogManager _adminLogger= default!;
+    [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
+    [Dependency] private readonly IAdminLogManager _adminLogger = default!;
     [Dependency] private readonly IComponentFactory _componentFactory = default!;
     [Dependency] private readonly ISerializationManager _serializationManager = default!;
 
@@ -33,7 +33,7 @@ public sealed class PayloadSystem : EntitySystem
         SubscribeLocalEvent<ChemicalPayloadComponent, TriggerEvent>(HandleChemicalPayloadTrigger);
     }
 
-    public IEnumerable<EntityUid> GetAllPayloads(EntityUid uid, ContainerManagerComponent? contMan=null)
+    public IEnumerable<EntityUid> GetAllPayloads(EntityUid uid, ContainerManagerComponent? contMan = null)
     {
         if (!Resolve(uid, ref contMan, false))
             yield break;
@@ -98,7 +98,7 @@ public sealed class PayloadSystem : EntitySystem
 
             var temp = (object) component;
             _serializationManager.CopyTo(data.Component, ref temp);
-            EntityManager.AddComponent(uid, (Component)temp!);
+            EntityManager.AddComponent(uid, (Component) temp!);
 
             trigger.GrantedComponents.Add(registration.Type);
         }
@@ -137,14 +137,14 @@ public sealed class PayloadSystem : EntitySystem
         }
     }
 
-    private void HandleChemicalPayloadTrigger(EntityUid uid, ChemicalPayloadComponent component, TriggerEvent args)
+    private void HandleChemicalPayloadTrigger(Entity<ChemicalPayloadComponent> entity, ref TriggerEvent args)
     {
-        if (component.BeakerSlotA.Item is not EntityUid beakerA
-            || component.BeakerSlotB.Item is not EntityUid beakerB
+        if (entity.Comp.BeakerSlotA.Item is not EntityUid beakerA
+            || entity.Comp.BeakerSlotB.Item is not EntityUid beakerB
             || !TryComp(beakerA, out FitsInDispenserComponent? compA)
             || !TryComp(beakerB, out FitsInDispenserComponent? compB)
-            || !_solutionSystem.TryGetSolution(beakerA, compA.Solution, out var solutionA)
-            || !_solutionSystem.TryGetSolution(beakerB, compB.Solution, out var solutionB)
+            || !_solutionContainerSystem.TryGetSolution(beakerA, compA.Solution, out var solnA, out var solutionA)
+            || !_solutionContainerSystem.TryGetSolution(beakerB, compB.Solution, out var solnB, out var solutionB)
             || solutionA.Volume == 0
             || solutionB.Volume == 0)
         {
@@ -155,17 +155,17 @@ public sealed class PayloadSystem : EntitySystem
         var solStringB = SolutionContainerSystem.ToPrettyString(solutionB);
 
         _adminLogger.Add(LogType.ChemicalReaction,
-            $"Chemical bomb payload {ToPrettyString(uid):payload} at {Transform(uid).MapPosition:location} is combining two solutions: {solStringA:solutionA} and {solStringB:solutionB}");
+            $"Chemical bomb payload {ToPrettyString(entity.Owner):payload} at {Transform(entity.Owner).MapPosition:location} is combining two solutions: {solStringA:solutionA} and {solStringB:solutionB}");
 
         solutionA.MaxVolume += solutionB.MaxVolume;
-        _solutionSystem.TryAddSolution(beakerA, solutionA, solutionB);
-        _solutionSystem.RemoveAllSolution(beakerB, solutionB);
+        _solutionContainerSystem.TryAddSolution(solnA.Value, solutionB);
+        _solutionContainerSystem.RemoveAllSolution(solnB.Value);
 
         // The grenade might be a dud. Redistribute solution:
-        var tmpSol = _solutionSystem.SplitSolution(beakerA, solutionA, solutionA.Volume * solutionB.MaxVolume / solutionA.MaxVolume);
-        _solutionSystem.TryAddSolution(beakerB, solutionB, tmpSol);
+        var tmpSol = _solutionContainerSystem.SplitSolution(solnA.Value, solutionA.Volume * solutionB.MaxVolume / solutionA.MaxVolume);
+        _solutionContainerSystem.TryAddSolution(solnB.Value, tmpSol);
         solutionA.MaxVolume -= solutionB.MaxVolume;
-        _solutionSystem.UpdateChemicals(beakerA, solutionA, false);
+        _solutionContainerSystem.UpdateChemicals(solnA.Value);
 
         args.Handled = true;
     }
index 995aeb7700014ee17c84d8f89dda94bab2069567..26eaca80fe7734b64bfcab4f6bd343e996972208 100644 (file)
@@ -13,7 +13,6 @@ namespace Content.Server.Power.EntitySystems;
 /// </summary>
 public sealed class RiggableSystem : EntitySystem
 {
-    [Dependency] private readonly SolutionContainerSystem _solutionsSystem = default!;
     [Dependency] private readonly ExplosionSystem _explosionSystem = default!;
     [Dependency] private readonly IAdminLogManager _adminLogger = default!;
 
@@ -22,17 +21,17 @@ public sealed class RiggableSystem : EntitySystem
         base.Initialize();
         SubscribeLocalEvent<RiggableComponent, RejuvenateEvent>(OnRejuvenate);
         SubscribeLocalEvent<RiggableComponent, BeingMicrowavedEvent>(OnMicrowaved);
-        SubscribeLocalEvent<RiggableComponent, SolutionChangedEvent>(OnSolutionChanged);
+        SubscribeLocalEvent<RiggableComponent, SolutionContainerChangedEvent>(OnSolutionChanged);
     }
 
-    private void OnRejuvenate(EntityUid uid, RiggableComponent component, RejuvenateEvent args)
+    private void OnRejuvenate(Entity<RiggableComponent> entity, ref RejuvenateEvent args)
     {
-        component.IsRigged = false;
+        entity.Comp.IsRigged = false;
     }
 
-    private void OnMicrowaved(EntityUid uid, RiggableComponent component, BeingMicrowavedEvent args)
+    private void OnMicrowaved(Entity<RiggableComponent> entity, ref BeingMicrowavedEvent args)
     {
-        if (TryComp<BatteryComponent>(uid, out var batteryComponent))
+        if (TryComp<BatteryComponent>(entity, out var batteryComponent))
         {
             if (batteryComponent.CurrentCharge == 0)
                 return;
@@ -41,21 +40,21 @@ public sealed class RiggableSystem : EntitySystem
         args.Handled = true;
 
         // What the fuck are you doing???
-        Explode(uid, batteryComponent, args.User);
+        Explode(entity.Owner, batteryComponent, args.User);
     }
 
-    private void OnSolutionChanged(EntityUid uid, RiggableComponent component, SolutionChangedEvent args)
+    private void OnSolutionChanged(Entity<RiggableComponent> entity, ref SolutionContainerChangedEvent args)
     {
-        if (args.SolutionId != component.Solution)
+        if (args.SolutionId != entity.Comp.Solution)
             return;
 
-        var wasRigged = component.IsRigged;
-        var quantity = args.Solution.GetReagentQuantity(component.RequiredQuantity.Reagent);
-        component.IsRigged = quantity >= component.RequiredQuantity.Quantity;
+        var wasRigged = entity.Comp.IsRigged;
+        var quantity = args.Solution.GetReagentQuantity(entity.Comp.RequiredQuantity.Reagent);
+        entity.Comp.IsRigged = quantity >= entity.Comp.RequiredQuantity.Quantity;
 
-        if (component.IsRigged && !wasRigged)
+        if (entity.Comp.IsRigged && !wasRigged)
         {
-            _adminLogger.Add(LogType.Explosion, LogImpact.Medium, $"{ToPrettyString(uid)} has been rigged up to explode when used.");
+            _adminLogger.Add(LogType.Explosion, LogImpact.Medium, $"{ToPrettyString(entity.Owner)} has been rigged up to explode when used.");
         }
     }
 
index abaf1c4d948eac8d0f1d9fae411b8698d8607456..20d714933254dabcb1231e55336ec9dd1c028eef 100644 (file)
@@ -1,5 +1,5 @@
-using Content.Shared.Chemistry.Components.SolutionManager;
-using Content.Shared.Chemistry.EntitySystems;
+using Content.Shared.Chemistry.Components;
+using Content.Shared.Chemistry.Components.SolutionManager;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.FixedPoint;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
@@ -20,11 +20,17 @@ public sealed partial class ChemicalFuelGeneratorAdapterComponent : Component
     public string Reagent = "WeldingFuel";
 
     /// <summary>
-    /// The solution on the <see cref="SolutionContainerManagerComponent"/> to use.
+    /// The name of <see cref="Solution"/>.
     /// </summary>
     [DataField("solution")]
     [ViewVariables(VVAccess.ReadWrite)]
-    public string Solution = "tank";
+    public string SolutionName = "tank";
+
+    /// <summary>
+    /// The solution on the <see cref="SolutionContainerManagerComponent"/> to use.
+    /// </summary>
+    [DataField("solutionRef")]
+    public Entity<SolutionComponent>? Solution = null;
 
     /// <summary>
     /// Value to multiply reagent amount by to get fuel amount.
index 7d88061f6223581b41403366733522e38b8bedd6..a23b0b8eed23647233a32523e37ab4ffecc0dc11 100644 (file)
@@ -1,10 +1,10 @@
 using Content.Server.Audio;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Fluids.EntitySystems;
 using Content.Server.Materials;
 using Content.Server.Popups;
 using Content.Server.Power.Components;
 using Content.Server.Power.EntitySystems;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.FixedPoint;
 using Content.Shared.Popups;
 using Content.Shared.Power.Generator;
@@ -65,23 +65,23 @@ public sealed class GeneratorSystem : SharedGeneratorSystem
         _materialStorage.EjectAllMaterial(uid);
     }
 
-    private void ChemicalEmpty(EntityUid uid, ChemicalFuelGeneratorAdapterComponent component, GeneratorEmpty args)
+    private void ChemicalEmpty(Entity<ChemicalFuelGeneratorAdapterComponent> entity, ref GeneratorEmpty args)
     {
-        if (!_solutionContainer.TryGetSolution(uid, component.Solution, out var solution))
+        if (!_solutionContainer.ResolveSolution(entity.Owner, entity.Comp.SolutionName, ref entity.Comp.Solution, out var solution))
             return;
 
-        var spillSolution = _solutionContainer.SplitSolution(uid, solution, solution.Volume);
-        _puddle.TrySpillAt(uid, spillSolution, out _);
+        var spillSolution = _solutionContainer.SplitSolution(entity.Comp.Solution.Value, solution.Volume);
+        _puddle.TrySpillAt(entity.Owner, spillSolution, out _);
     }
 
-    private void ChemicalGetClogged(EntityUid uid, ChemicalFuelGeneratorAdapterComponent component, ref GeneratorGetCloggedEvent args)
+    private void ChemicalGetClogged(Entity<ChemicalFuelGeneratorAdapterComponent> entity, ref GeneratorGetCloggedEvent args)
     {
-        if (!_solutionContainer.TryGetSolution(uid, component.Solution, out var solution))
+        if (!_solutionContainer.ResolveSolution(entity.Owner, entity.Comp.SolutionName, ref entity.Comp.Solution, out var solution))
             return;
 
         foreach (var reagentQuantity in solution)
         {
-            if (reagentQuantity.Reagent.Prototype != component.Reagent)
+            if (reagentQuantity.Reagent.Prototype != entity.Comp.Reagent)
             {
                 args.Clogged = true;
                 return;
@@ -89,32 +89,29 @@ public sealed class GeneratorSystem : SharedGeneratorSystem
         }
     }
 
-    private void ChemicalUseFuel(EntityUid uid, ChemicalFuelGeneratorAdapterComponent component, GeneratorUseFuel args)
+    private void ChemicalUseFuel(Entity<ChemicalFuelGeneratorAdapterComponent> entity, ref GeneratorUseFuel args)
     {
-        if (!_solutionContainer.TryGetSolution(uid, component.Solution, out var solution))
+        if (!_solutionContainer.ResolveSolution(entity.Owner, entity.Comp.SolutionName, ref entity.Comp.Solution, out var solution))
             return;
 
-        var availableReagent = solution.GetTotalPrototypeQuantity(component.Reagent).Value;
+        var availableReagent = solution.GetTotalPrototypeQuantity(entity.Comp.Reagent).Value;
         var toRemove = RemoveFractionalFuel(
-            ref component.FractionalReagent,
+            ref entity.Comp.FractionalReagent,
             args.FuelUsed,
-            component.Multiplier * FixedPoint2.Epsilon.Float(),
+            entity.Comp.Multiplier * FixedPoint2.Epsilon.Float(),
             availableReagent);
 
-        solution.RemoveReagent(component.Reagent, FixedPoint2.FromCents(toRemove));
+        _solutionContainer.RemoveReagent(entity.Comp.Solution.Value, entity.Comp.Reagent, FixedPoint2.FromCents(toRemove));
     }
 
-    private void ChemicalGetFuel(
-        EntityUid uid,
-        ChemicalFuelGeneratorAdapterComponent component,
-        ref GeneratorGetFuelEvent args)
+    private void ChemicalGetFuel(Entity<ChemicalFuelGeneratorAdapterComponent> entity, ref GeneratorGetFuelEvent args)
     {
-        if (!_solutionContainer.TryGetSolution(uid, component.Solution, out var solution))
+        if (!_solutionContainer.ResolveSolution(entity.Owner, entity.Comp.SolutionName, ref entity.Comp.Solution, out var solution))
             return;
 
-        var availableReagent = solution.GetTotalPrototypeQuantity(component.Reagent).Float();
-        var reagent = component.FractionalReagent * FixedPoint2.Epsilon.Float() + availableReagent;
-        args.Fuel = reagent * component.Multiplier;
+        var availableReagent = solution.GetTotalPrototypeQuantity(entity.Comp.Reagent).Float();
+        var reagent = entity.Comp.FractionalReagent * FixedPoint2.Epsilon.Float() + availableReagent;
+        args.Fuel = reagent * entity.Comp.Multiplier;
     }
 
     private void SolidUseFuel(EntityUid uid, SolidFuelGeneratorAdapterComponent component, GeneratorUseFuel args)
index 2ed30d8942882463acc6585419db982759b9f15b..8064434031427ed7bc8d1b0afedbe96e65e2df82 100644 (file)
@@ -25,49 +25,50 @@ namespace Content.Server.Stunnable.Systems
             base.Initialize();
 
             SubscribeLocalEvent<BatteryComponent, ExaminedEvent>(OnExamined);
-            SubscribeLocalEvent<StunbatonComponent, SolutionChangedEvent>(OnSolutionChange);
+            SubscribeLocalEvent<StunbatonComponent, SolutionContainerChangedEvent>(OnSolutionChange);
             SubscribeLocalEvent<StunbatonComponent, StaminaDamageOnHitAttemptEvent>(OnStaminaHitAttempt);
             SubscribeLocalEvent<StunbatonComponent, ItemToggleActivateAttemptEvent>(TryTurnOn);
             SubscribeLocalEvent<StunbatonComponent, ItemToggleDoneEvent>(ToggleDone);
         }
 
-        private void OnStaminaHitAttempt(EntityUid uid, StunbatonComponent component, ref StaminaDamageOnHitAttemptEvent args)
+        private void OnStaminaHitAttempt(Entity<StunbatonComponent> entity, ref StaminaDamageOnHitAttemptEvent args)
         {
-            if (!_itemToggle.IsActivated(uid) ||
-            !TryComp<BatteryComponent>(uid, out var battery) || !_battery.TryUseCharge(uid, component.EnergyPerUse, battery))
+            if (!_itemToggle.IsActivated(entity.Owner) ||
+            !TryComp<BatteryComponent>(entity.Owner, out var battery) || !_battery.TryUseCharge(entity.Owner, entity.Comp.EnergyPerUse, battery))
             {
                 args.Cancelled = true;
                 return;
             }
 
-            if (battery.CurrentCharge < component.EnergyPerUse)
+            if (battery.CurrentCharge < entity.Comp.EnergyPerUse)
             {
-                _itemToggle.Toggle(uid, predicted: false);
+                _itemToggle.Toggle(entity.Owner, predicted: false);
             }
         }
 
-        private void OnExamined(EntityUid uid, BatteryComponent battery, ExaminedEvent args)
+        private void OnExamined(Entity<BatteryComponent> entity, ref ExaminedEvent args)
         {
-            var onMsg = _itemToggle.IsActivated(uid)
+            var onMsg = _itemToggle.IsActivated(entity.Owner)
             ? Loc.GetString("comp-stunbaton-examined-on")
             : Loc.GetString("comp-stunbaton-examined-off");
             args.PushMarkup(onMsg);
 
             var chargeMessage = Loc.GetString("stunbaton-component-on-examine-charge",
-                ("charge", (int) (battery.CurrentCharge / battery.MaxCharge * 100)));
+                ("charge", (int) (entity.Comp.CurrentCharge / entity.Comp.MaxCharge * 100)));
             args.PushMarkup(chargeMessage);
         }
 
-        private void ToggleDone(EntityUid uid, StunbatonComponent comp, ref ItemToggleDoneEvent args)
+        private void ToggleDone(Entity<StunbatonComponent> entity, ref ItemToggleDoneEvent args)
         {
-            if (!TryComp<ItemComponent>(uid, out var item))
+            if (!TryComp<ItemComponent>(entity, out var item))
                 return;
-            _item.SetHeldPrefix(uid, args.Activated ? "on" : "off", item);
+
+            _item.SetHeldPrefix(entity.Owner, args.Activated ? "on" : "off", item);
         }
 
-        private void TryTurnOn(EntityUid uid, StunbatonComponent comp, ref ItemToggleActivateAttemptEvent args)
+        private void TryTurnOn(Entity<StunbatonComponent> entity, ref ItemToggleActivateAttemptEvent args)
         {
-            if (!TryComp<BatteryComponent>(uid, out var battery) || battery.CurrentCharge < comp.EnergyPerUse)
+            if (!TryComp<BatteryComponent>(entity, out var battery) || battery.CurrentCharge < entity.Comp.EnergyPerUse)
             {
                 args.Cancelled = true;
                 if (args.User != null)
@@ -77,22 +78,22 @@ namespace Content.Server.Stunnable.Systems
                 return;
             }
 
-            if (TryComp<RiggableComponent>(uid, out var rig) && rig.IsRigged)
+            if (TryComp<RiggableComponent>(entity, out var rig) && rig.IsRigged)
             {
-                _riggableSystem.Explode(uid, battery, args.User);
+                _riggableSystem.Explode(entity.Owner, battery, args.User);
             }
         }
 
         // https://github.com/space-wizards/space-station-14/pull/17288#discussion_r1241213341
-        private void OnSolutionChange(EntityUid uid, StunbatonComponent component, SolutionChangedEvent args)
+        private void OnSolutionChange(Entity<StunbatonComponent> entity, ref SolutionContainerChangedEvent args)
         {
             // Explode if baton is activated and rigged.
-            if (!TryComp<RiggableComponent>(uid, out var riggable) ||
-                !TryComp<BatteryComponent>(uid, out var battery))
+            if (!TryComp<RiggableComponent>(entity, out var riggable) ||
+                !TryComp<BatteryComponent>(entity, out var battery))
                 return;
 
-            if (_itemToggle.IsActivated(uid) && riggable.IsRigged)
-                _riggableSystem.Explode(uid, battery);
+            if (_itemToggle.IsActivated(entity.Owner) && riggable.IsRigged)
+                _riggableSystem.Explode(entity.Owner, battery);
         }
 
         private void SendPowerPulse(EntityUid target, EntityUid? user, EntityUid used)
index a620fa2ef4717c1a2da94bba18ee28cc495cf9f9..b0db2c58e88ed97d8f30141d07e455367dac326b 100644 (file)
@@ -1,3 +1,4 @@
+using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.FixedPoint;
 using Content.Shared.Tools.Components;
@@ -9,35 +10,41 @@ namespace Content.Server.Tools.Components
     [RegisterComponent]
     public sealed partial class WelderComponent : SharedWelderComponent
     {
+        /// <summary>
+        ///     Name of <see cref="FuelSolution"/>.
+        /// </summary>
+        [DataField("fuelSolution"), ViewVariables(VVAccess.ReadWrite)]
+        public string FuelSolutionName = "Welder";
+
         /// <summary>
         ///     Solution on the entity that contains the fuel.
         /// </summary>
-        [DataField, ViewVariables(VVAccess.ReadWrite)]
-        public string FuelSolution { get; private set; } = "Welder";
+        [DataField("fuelSolutionRef")]
+        public Entity<SolutionComponent>? FuelSolution = null;
 
         /// <summary>
         ///     Reagent that will be used as fuel for welding.
         /// </summary>
         [DataField, ViewVariables(VVAccess.ReadWrite)]
-        public ProtoId<ReagentPrototype> FuelReagent { get; private set; } = "WeldingFuel";
+        public ProtoId<ReagentPrototype> FuelReagent = "WeldingFuel";
 
         /// <summary>
         ///     Fuel consumption per second while the welder is active.
         /// </summary>
         [DataField, ViewVariables(VVAccess.ReadWrite)]
-        public FixedPoint2 FuelConsumption { get; private set; } = FixedPoint2.New(2.0f);
+        public FixedPoint2 FuelConsumption = FixedPoint2.New(2.0f);
 
         /// <summary>
         ///     A fuel amount to be consumed when the welder goes from being unlit to being lit.
         /// </summary>
         [DataField, ViewVariables(VVAccess.ReadWrite)]
-        public FixedPoint2 FuelLitCost { get; private set; } = FixedPoint2.New(0.5f);
+        public FixedPoint2 FuelLitCost = FixedPoint2.New(0.5f);
 
         /// <summary>
         ///     Sound played when refilling the welder.
         /// </summary>
         [DataField]
-        public SoundSpecifier WelderRefill { get; private set; } = new SoundPathSpecifier("/Audio/Effects/refill.ogg");
+        public SoundSpecifier WelderRefill = new SoundPathSpecifier("/Audio/Effects/refill.ogg");
 
         /// <summary>
         ///     Whether the item is safe to refill while lit without exploding the tank.
index f4eca32a3da6f3c14b2083b3a47c80e9fe791c90..88037cedb45fdba90d7852be3faf5c9d51e5befd 100644 (file)
@@ -1,4 +1,3 @@
-using System.Linq;
 using Content.Server.Chemistry.Components;
 using Content.Server.IgnitionSource;
 using Content.Server.Tools.Components;
@@ -12,6 +11,7 @@ using Content.Shared.Item;
 using Content.Shared.Item.ItemToggle;
 using Content.Shared.Tools.Components;
 using Robust.Shared.GameStates;
+using System.Linq;
 
 namespace Content.Server.Tools
 {
@@ -38,66 +38,67 @@ namespace Content.Server.Tools
         public (FixedPoint2 fuel, FixedPoint2 capacity) GetWelderFuelAndCapacity(EntityUid uid, WelderComponent? welder = null, SolutionContainerManagerComponent? solutionContainer = null)
         {
             if (!Resolve(uid, ref welder, ref solutionContainer)
-                || !_solutionContainer.TryGetSolution(uid, welder.FuelSolution, out var fuelSolution, solutionContainer))
+                || !_solutionContainer.ResolveSolution((uid, solutionContainer), welder.FuelSolutionName, ref welder.FuelSolution, out var fuelSolution))
                 return (FixedPoint2.Zero, FixedPoint2.Zero);
 
-            return (_solutionContainer.GetTotalPrototypeQuantity(uid, welder.FuelReagent), fuelSolution.MaxVolume);
+            return (fuelSolution.GetTotalPrototypeQuantity(welder.FuelReagent), fuelSolution.MaxVolume);
         }
 
-        public void TryTurnOn(EntityUid uid, WelderComponent welder, ref ItemToggleActivateAttemptEvent args)
+        public void TryTurnOn(Entity<WelderComponent> entity, ref ItemToggleActivateAttemptEvent args)
         {
-            if (!_solutionContainer.TryGetSolution(uid, welder.FuelSolution, out var solution) ||
-                !TryComp<TransformComponent>(uid, out var transform))
+            if (!_solutionContainer.ResolveSolution(entity.Owner, entity.Comp.FuelSolutionName, ref entity.Comp.FuelSolution, out var solution) ||
+                !TryComp<TransformComponent>(entity, out var transform))
             {
                 args.Cancelled = true;
                 return;
             }
-            var fuel = solution.GetTotalPrototypeQuantity(welder.FuelReagent);
+
+            var fuel = solution.GetTotalPrototypeQuantity(entity.Comp.FuelReagent);
 
             // Not enough fuel to lit welder.
-            if (fuel == FixedPoint2.Zero || fuel < welder.FuelLitCost)
+            if (fuel == FixedPoint2.Zero || fuel < entity.Comp.FuelLitCost)
             {
                 if (args.User != null)
                 {
-                    _popup.PopupEntity(Loc.GetString("welder-component-no-fuel-message"), uid, (EntityUid) args.User);
+                    _popup.PopupEntity(Loc.GetString("welder-component-no-fuel-message"), entity, (EntityUid) args.User);
                 }
                 args.Cancelled = true;
                 return;
             }
 
-            solution.RemoveReagent(welder.FuelReagent, welder.FuelLitCost);
+            _solutionContainer.RemoveReagent(entity.Comp.FuelSolution.Value, entity.Comp.FuelReagent, entity.Comp.FuelLitCost);
 
             // Logging
-            _adminLogger.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(args.User):user} toggled {ToPrettyString(uid):welder} on");
+            _adminLogger.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(args.User):user} toggled {ToPrettyString(entity.Owner):welder} on");
 
-            _ignitionSource.SetIgnited(uid);
+            _ignitionSource.SetIgnited(entity.Owner);
 
             if (transform.GridUid is { } gridUid)
             {
-                var position = _transformSystem.GetGridOrMapTilePosition(uid, transform);
-                _atmosphereSystem.HotspotExpose(gridUid, position, 700, 50, uid, true);
+                var position = _transformSystem.GetGridOrMapTilePosition(entity.Owner, transform);
+                _atmosphereSystem.HotspotExpose(gridUid, position, 700, 50, entity.Owner, true);
             }
 
-            Dirty(uid, welder);
+            Dirty(entity);
 
-            _activeWelders.Add(uid);
+            _activeWelders.Add(entity);
         }
 
-        public void TurnOff(EntityUid uid, WelderComponent welder, ref ItemToggleDeactivateAttemptEvent args)
+        public void TurnOff(Entity<WelderComponent> entity, ref ItemToggleDeactivateAttemptEvent args)
         {
             // Logging
-            _adminLogger.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(args.User):user} toggled {ToPrettyString(uid):welder} off");
+            _adminLogger.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(args.User):user} toggled {ToPrettyString(entity.Owner):welder} off");
 
-            _ignitionSource.SetIgnited(uid, false);
+            _ignitionSource.SetIgnited(entity.Owner, false);
 
-            Dirty(uid, welder);
+            Dirty(entity);
 
-            _activeWelders.Remove(uid);
+            _activeWelders.Remove(entity);
         }
 
-        private void OnWelderExamine(EntityUid uid, WelderComponent welder, ExaminedEvent args)
+        private void OnWelderExamine(Entity<WelderComponent> entity, ref ExaminedEvent args)
         {
-            if (_itemToggle.IsActivated(uid))
+            if (_itemToggle.IsActivated(entity.Owner))
             {
                 args.PushMarkup(Loc.GetString("welder-component-on-examine-welder-lit-message"));
             }
@@ -108,7 +109,7 @@ namespace Content.Server.Tools
 
             if (args.IsInDetailsRange)
             {
-                var (fuel, capacity) = GetWelderFuelAndCapacity(uid, welder);
+                var (fuel, capacity) = GetWelderFuelAndCapacity(entity.Owner, entity.Comp);
 
                 args.PushMarkup(Loc.GetString("welder-component-on-examine-detailed-message",
                     ("colorName", fuel < capacity / FixedPoint2.New(4f) ? "darkorange" : "orange"),
@@ -118,7 +119,7 @@ namespace Content.Server.Tools
             }
         }
 
-        private void OnWelderAfterInteract(EntityUid uid, WelderComponent welder, AfterInteractEvent args)
+        private void OnWelderAfterInteract(Entity<WelderComponent> entity, ref AfterInteractEvent args)
         {
             if (args.Handled)
                 return;
@@ -128,49 +129,49 @@ namespace Content.Server.Tools
 
             if (TryComp(target, out ReagentTankComponent? tank)
                 && tank.TankType == ReagentTankType.Fuel
-                && _solutionContainer.TryGetDrainableSolution(target, out var targetSolution)
-                && _solutionContainer.TryGetSolution(uid, welder.FuelSolution, out var welderSolution))
+                && _solutionContainer.TryGetDrainableSolution(target, out var targetSoln, out var targetSolution)
+                && _solutionContainer.ResolveSolution(entity.Owner, entity.Comp.FuelSolutionName, ref entity.Comp.FuelSolution, out var welderSolution))
             {
                 var trans = FixedPoint2.Min(welderSolution.AvailableVolume, targetSolution.Volume);
                 if (trans > 0)
                 {
-                    var drained = _solutionContainer.Drain(target, targetSolution, trans);
-                    _solutionContainer.TryAddSolution(uid, welderSolution, drained);
-                    _audio.PlayPvs(welder.WelderRefill, uid);
-                    _popup.PopupEntity(Loc.GetString("welder-component-after-interact-refueled-message"), uid, args.User);
+                    var drained = _solutionContainer.Drain(target, targetSoln.Value, trans);
+                    _solutionContainer.TryAddSolution(entity.Comp.FuelSolution.Value, drained);
+                    _audio.PlayPvs(entity.Comp.WelderRefill, entity);
+                    _popup.PopupEntity(Loc.GetString("welder-component-after-interact-refueled-message"), entity, args.User);
                 }
                 else if (welderSolution.AvailableVolume <= 0)
                 {
-                    _popup.PopupEntity(Loc.GetString("welder-component-already-full"), uid, args.User);
+                    _popup.PopupEntity(Loc.GetString("welder-component-already-full"), entity, args.User);
                 }
                 else
                 {
-                    _popup.PopupEntity(Loc.GetString("welder-component-no-fuel-in-tank", ("owner", args.Target)), uid, args.User);
+                    _popup.PopupEntity(Loc.GetString("welder-component-no-fuel-in-tank", ("owner", args.Target)), entity, args.User);
                 }
             }
 
             args.Handled = true;
         }
 
-        private void OnWelderToolUseAttempt(EntityUid uid, WelderComponent welder, DoAfterAttemptEvent<ToolDoAfterEvent> args)
+        private void OnWelderToolUseAttempt(Entity<WelderComponent> entity, ref DoAfterAttemptEvent<ToolDoAfterEvent> args)
         {
             var user = args.DoAfter.Args.User;
 
-            if (!_itemToggle.IsActivated(uid))
+            if (!_itemToggle.IsActivated(entity.Owner))
             {
-                _popup.PopupEntity(Loc.GetString("welder-component-welder-not-lit-message"), uid, user);
+                _popup.PopupEntity(Loc.GetString("welder-component-welder-not-lit-message"), entity, user);
                 args.Cancel();
             }
         }
 
-        private void OnWelderShutdown(EntityUid uid, WelderComponent welder, ComponentShutdown args)
+        private void OnWelderShutdown(Entity<WelderComponent> entity, ref ComponentShutdown args)
         {
-            _activeWelders.Remove(uid);
+            _activeWelders.Remove(entity);
         }
 
-        private void OnWelderGetState(EntityUid uid, WelderComponent welder, ref ComponentGetState args)
+        private void OnWelderGetState(Entity<WelderComponent> entity, ref ComponentGetState args)
         {
-            var (fuel, capacity) = GetWelderFuelAndCapacity(uid, welder);
+            var (fuel, capacity) = GetWelderFuelAndCapacity(entity.Owner, entity.Comp);
             args.State = new WelderComponentState(capacity.Float(), fuel.Float());
         }
 
@@ -188,10 +189,10 @@ namespace Content.Server.Tools
                     || !TryComp(tool, out SolutionContainerManagerComponent? solutionContainer))
                     continue;
 
-                if (!_solutionContainer.TryGetSolution(tool, welder.FuelSolution, out var solution, solutionContainer))
+                if (!_solutionContainer.ResolveSolution((tool, solutionContainer), welder.FuelSolutionName, ref welder.FuelSolution, out var solution))
                     continue;
 
-                solution.RemoveReagent(welder.FuelReagent, welder.FuelConsumption * _welderTimer);
+                _solutionContainer.RemoveReagent(welder.FuelSolution.Value, welder.FuelReagent, welder.FuelConsumption * _welderTimer);
 
                 if (solution.GetTotalPrototypeQuantity(welder.FuelReagent) <= FixedPoint2.Zero)
                 {
index 7366bfce40341da044dc537d5bbb9bc91ff6ef40..6153b579a1bc8e3440ff3eef33c3a2ee2f994ce3 100644 (file)
@@ -1,13 +1,11 @@
 using Content.Server.Atmos.EntitySystems;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Popups;
 using Content.Server.Tools.Components;
-using Content.Shared.Chemistry.EntitySystems;
-using Content.Shared.Maps;
-using Content.Shared.Tools;
 using Robust.Server.GameObjects;
-using Robust.Shared.Audio;
 using Robust.Shared.Audio.Systems;
 using Robust.Shared.Map;
+
 using SharedToolSystem = Content.Shared.Tools.Systems.SharedToolSystem;
 
 namespace Content.Server.Tools
index 0e085cdfe00120710afd282f9a64567c7c59e3d4..1c672796c697466f936191c034ac32f7ed043281 100644 (file)
@@ -1,15 +1,13 @@
-using System.Linq;
-using System.Numerics;
 using Content.Server.Body.Components;
 using Content.Server.Body.Systems;
 using Content.Server.Chat.Systems;
 using Content.Server.Chemistry.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.CombatMode.Disarm;
 using Content.Server.Contests;
 using Content.Server.Movement.Systems;
 using Content.Shared.Actions.Events;
 using Content.Shared.Administration.Components;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.CombatMode;
 using Content.Shared.Damage.Events;
 using Content.Shared.Damage.Systems;
@@ -29,6 +27,8 @@ using Robust.Shared.Audio;
 using Robust.Shared.Map;
 using Robust.Shared.Player;
 using Robust.Shared.Random;
+using System.Linq;
+using System.Numerics;
 
 namespace Content.Server.Weapons.Melee;
 
@@ -250,11 +250,11 @@ public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem
 
     }
 
-    private void OnChemicalInjectorHit(EntityUid owner, MeleeChemicalInjectorComponent comp, MeleeHitEvent args)
+    private void OnChemicalInjectorHit(Entity<MeleeChemicalInjectorComponent> entity, ref MeleeHitEvent args)
     {
         if (!args.IsHit ||
             !args.HitEntities.Any() ||
-            !_solutions.TryGetSolution(owner, comp.Solution, out var solutionContainer))
+            !_solutions.TryGetSolution(entity.Owner, entity.Comp.Solution, out var solutionContainer))
         {
             return;
         }
@@ -262,28 +262,28 @@ public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem
         var hitBloodstreams = new List<(EntityUid Entity, BloodstreamComponent Component)>();
         var bloodQuery = GetEntityQuery<BloodstreamComponent>();
 
-        foreach (var entity in args.HitEntities)
+        foreach (var hit in args.HitEntities)
         {
-            if (Deleted(entity))
+            if (Deleted(hit))
                 continue;
 
             // prevent deathnettles injecting through hardsuits
-            if (!comp.PierceArmor && _inventory.TryGetSlotEntity(entity, "outerClothing", out var suit) && _tag.HasTag(suit.Value, "Hardsuit"))
+            if (!entity.Comp.PierceArmor && _inventory.TryGetSlotEntity(hit, "outerClothing", out var suit) && _tag.HasTag(suit.Value, "Hardsuit"))
             {
-                PopupSystem.PopupEntity(Loc.GetString("melee-inject-failed-hardsuit", ("weapon", owner)), args.User, args.User, PopupType.SmallCaution);
+                PopupSystem.PopupEntity(Loc.GetString("melee-inject-failed-hardsuit", ("weapon", entity.Owner)), args.User, args.User, PopupType.SmallCaution);
                 continue;
             }
 
-            if (bloodQuery.TryGetComponent(entity, out var bloodstream))
-                hitBloodstreams.Add((entity, bloodstream));
+            if (bloodQuery.TryGetComponent(hit, out var bloodstream))
+                hitBloodstreams.Add((hit, bloodstream));
         }
 
         if (!hitBloodstreams.Any())
             return;
 
-        var removedSolution = solutionContainer.SplitSolution(comp.TransferAmount * hitBloodstreams.Count);
+        var removedSolution = _solutions.SplitSolution(solutionContainer.Value, entity.Comp.TransferAmount * hitBloodstreams.Count);
         var removedVol = removedSolution.Volume;
-        var solutionToInject = removedSolution.SplitSolution(removedVol * comp.TransferEfficiency);
+        var solutionToInject = removedSolution.SplitSolution(removedVol * entity.Comp.TransferEfficiency);
         var volPerBloodstream = solutionToInject.Volume * (1 / hitBloodstreams.Count);
 
         foreach (var (ent, bloodstream) in hitBloodstreams)
index 3d8601cc8e7dca09a0db2cd4cadd607d009b5405..e896e2b3734c24a586a950f31287101db97bc163 100644 (file)
@@ -1,34 +1,34 @@
-using System.Linq;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Server.Weapons.Ranged.Components;
 using Content.Shared.Chemistry.Components;
-using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Weapons.Ranged.Events;
+using System.Linq;
 
 namespace Content.Server.Weapons.Ranged.Systems
 {
     public sealed class ChemicalAmmoSystem : EntitySystem
     {
-        [Dependency] private readonly SolutionContainerSystem _solutionSystem = default!;
+        [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
 
         public override void Initialize()
         {
             SubscribeLocalEvent<ChemicalAmmoComponent, AmmoShotEvent>(OnFire);
         }
 
-        private void OnFire(EntityUid uid, ChemicalAmmoComponent component, AmmoShotEvent args)
+        private void OnFire(Entity<ChemicalAmmoComponent> entity, ref AmmoShotEvent args)
         {
-            if (!_solutionSystem.TryGetSolution(uid, component.SolutionName, out var ammoSolution))
+            if (!_solutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var ammoSoln, out var ammoSolution))
                 return;
 
             var projectiles = args.FiredProjectiles;
 
-            var projectileSolutionContainers = new List<(EntityUid, Solution)>();
+            var projectileSolutionContainers = new List<(EntityUid, Entity<SolutionComponent>)>();
             foreach (var projectile in projectiles)
             {
-                if (_solutionSystem
-                    .TryGetSolution(projectile, component.SolutionName, out var projectileSolutionContainer))
+                if (_solutionContainerSystem
+                    .TryGetSolution(projectile, entity.Comp.SolutionName, out var projectileSoln, out _))
                 {
-                    projectileSolutionContainers.Add((uid, projectileSolutionContainer));
+                    projectileSolutionContainers.Add((projectile, projectileSoln.Value));
                 }
             }
 
@@ -37,13 +37,13 @@ namespace Content.Server.Weapons.Ranged.Systems
 
             var solutionPerProjectile = ammoSolution.Volume * (1 / projectileSolutionContainers.Count);
 
-            foreach (var (projectileUid, projectileSolution) in projectileSolutionContainers)
+            foreach (var (_, projectileSolution) in projectileSolutionContainers)
             {
-                var solutionToTransfer = _solutionSystem.SplitSolution(uid, ammoSolution, solutionPerProjectile);
-                _solutionSystem.TryAddSolution(projectileUid, projectileSolution, solutionToTransfer);
+                var solutionToTransfer = _solutionContainerSystem.SplitSolution(ammoSoln.Value, solutionPerProjectile);
+                _solutionContainerSystem.TryAddSolution(projectileSolution, solutionToTransfer);
             }
 
-            _solutionSystem.RemoveAllSolution(uid, ammoSolution);
+            _solutionContainerSystem.RemoveAllSolution(ammoSoln.Value);
         }
     }
 }
index 734a01602c8bc4207f5b457adf359c661ba81360..7ad4b15ffc70b4e19bf7a142dafd565b1f98170a 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Server.Chemistry.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.FixedPoint;
@@ -18,29 +19,29 @@ public sealed partial class GunSystem
         base.InitializeSolution();
 
         SubscribeLocalEvent<SolutionAmmoProviderComponent, MapInitEvent>(OnSolutionMapInit);
-        SubscribeLocalEvent<SolutionAmmoProviderComponent, SolutionChangedEvent>(OnSolutionChanged);
+        SubscribeLocalEvent<SolutionAmmoProviderComponent, SolutionContainerChangedEvent>(OnSolutionChanged);
     }
 
-    private void OnSolutionMapInit(EntityUid uid, SolutionAmmoProviderComponent component, MapInitEvent args)
+    private void OnSolutionMapInit(Entity<SolutionAmmoProviderComponent> entity, ref MapInitEvent args)
     {
-        UpdateSolutionShots(uid, component);
+        UpdateSolutionShots(entity.Owner, entity.Comp);
     }
 
-    private void OnSolutionChanged(EntityUid uid, SolutionAmmoProviderComponent component, SolutionChangedEvent args)
+    private void OnSolutionChanged(Entity<SolutionAmmoProviderComponent> entity, ref SolutionContainerChangedEvent args)
     {
-        if (args.Solution.Name == component.SolutionId)
-            UpdateSolutionShots(uid, component, args.Solution);
+        if (args.Solution.Name == entity.Comp.SolutionId)
+            UpdateSolutionShots(entity.Owner, entity.Comp, args.Solution);
     }
 
     protected override void UpdateSolutionShots(EntityUid uid, SolutionAmmoProviderComponent component, Solution? solution = null)
     {
         var shots = 0;
         var maxShots = 0;
-        if (solution == null && !_solutionContainer.TryGetSolution(uid, component.SolutionId, out solution))
+        if (solution == null && !_solutionContainer.TryGetSolution(uid, component.SolutionId, out _, out solution))
         {
             component.Shots = shots;
             component.MaxShots = maxShots;
-            Dirty(component);
+            Dirty(uid, component);
             return;
         }
 
@@ -49,7 +50,7 @@ public sealed partial class GunSystem
 
         component.Shots = shots;
         component.MaxShots = maxShots;
-        Dirty(component);
+        Dirty(uid, component);
 
         UpdateSolutionAppearance(uid, component);
     }
@@ -58,10 +59,10 @@ public sealed partial class GunSystem
     {
         var (ent, shootable) = base.GetSolutionShot(uid, component, position);
 
-        if (!_solutionContainer.TryGetSolution(uid, component.SolutionId, out var solution))
+        if (!_solutionContainer.TryGetSolution(uid, component.SolutionId, out var solution, out _))
             return (ent, shootable);
 
-        var newSolution = _solutionContainer.SplitSolution(uid, solution, component.FireCost);
+        var newSolution = _solutionContainer.SplitSolution(solution.Value, component.FireCost);
 
         if (newSolution.Volume <= FixedPoint2.Zero)
             return (ent, shootable);
@@ -73,9 +74,9 @@ public sealed partial class GunSystem
         }
 
         // Add the solution to the vapor and actually send the thing
-        if (_solutionContainer.TryGetSolution(ent, VaporComponent.SolutionName, out var vaporSolution))
+        if (_solutionContainer.TryGetSolution(ent, VaporComponent.SolutionName, out var vaporSolution, out _))
         {
-            _solutionContainer.TryAddSolution(ent, vaporSolution, newSolution);
+            _solutionContainer.TryAddSolution(vaporSolution.Value, newSolution);
         }
         return (ent, shootable);
     }
index 1e40807f6eafe0e645638230e891bb53ac1bb965..3065b1c4172346314bb03fcea270c69bbeb94fb7 100644 (file)
@@ -1,8 +1,6 @@
 using Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Systems;
 using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Reagent;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
 
 namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
index 782c4b84f2fbb0489f9889db3741af2b1eb429f4..12fae34962103db66265d30abe3d11802d39922c 100644 (file)
@@ -1,6 +1,5 @@
 using Content.Shared.Eui;
 using Robust.Shared.Serialization;
-using Content.Shared.Chemistry.Components;
 
 namespace Content.Shared.Administration
 {
@@ -8,9 +7,9 @@ namespace Content.Shared.Administration
     public sealed class EditSolutionsEuiState : EuiStateBase
     {
         public readonly NetEntity Target;
-        public readonly Dictionary<string, Solution>? Solutions;
+        public readonly List<(string, NetEntity)>? Solutions;
 
-        public EditSolutionsEuiState(NetEntity target, Dictionary<string, Solution>? solutions)
+        public EditSolutionsEuiState(NetEntity target, List<(string, NetEntity)>? solutions)
         {
             Target = target;
             Solutions = solutions;
index 4cdff566f6cddf41b4c9efa131f3b2e35da441e2..0a6ace0943bd7d823c672fa3cda35315bae94554 100644 (file)
@@ -12,7 +12,6 @@ public sealed partial class DrainableSolutionComponent : Component
     /// <summary>
     /// Solution name that can be drained.
     /// </summary>
-    [ViewVariables(VVAccess.ReadWrite)]
-    [DataField("solution")]
-    public string Solution { get; set; } = "default";
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public string Solution = "default";
 }
index ad9e1f19a6c17b323313cd8d6fc70a04b7adac7a..43fbe137b66cf547e9ee149d9f5f410ca166a24f 100644 (file)
@@ -12,14 +12,12 @@ public sealed partial class DumpableSolutionComponent : Component
     /// <summary>
     /// Solution name that can be dumped into.
     /// </summary>
-    [ViewVariables(VVAccess.ReadWrite)]
-    [DataField("solution")]
-    public string Solution { get; set; } = "default";
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public string Solution = "default";
 
     /// <summary>
     /// Whether the solution can be dumped into infinitely.
     /// </summary>
-    [ViewVariables(VVAccess.ReadWrite)]
-    [DataField("unlimited")]
-    public bool Unlimited { get; set; } = false;
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public bool Unlimited = false;
 }
index a479dcdbe8bb130c71dbef71c28f74e40d73fb3d..f295dcc5bd2ae5fede0d6335cf34b728884cf8ff 100644 (file)
@@ -1,23 +1,21 @@
 using Robust.Shared.GameStates;
 
-namespace Content.Shared.Chemistry.Components
+namespace Content.Shared.Chemistry.Components;
+
+/// <summary>
+/// Allows the entity with this component to be placed in a <c>SharedReagentDispenserComponent</c>.
+/// <para>Otherwise it's considered to be too large or the improper shape to fit.</para>
+/// <para>Allows us to have obscenely large containers that are harder to abuse in chem dispensers
+/// since they can't be placed directly in them.</para>
+/// <see cref="Dispenser.SharedReagentDispenserComponent"/>
+/// </summary>
+[RegisterComponent]
+[NetworkedComponent] // only needed for white-lists. Client doesn't actually need Solution data;
+public sealed partial class FitsInDispenserComponent : Component
 {
     /// <summary>
-    /// Allows the entity with this component to be placed in a <c>SharedReagentDispenserComponent</c>.
-    /// <para>Otherwise it's considered to be too large or the improper shape to fit.</para>
-    /// <para>Allows us to have obscenely large containers that are harder to abuse in chem dispensers
-    /// since they can't be placed directly in them.</para>
-    /// <see cref="Dispenser.SharedReagentDispenserComponent"/>
+    /// Solution name that will interact with ReagentDispenserComponent.
     /// </summary>
-    [RegisterComponent]
-    [NetworkedComponent] // only needed for white-lists. Client doesn't actually need Solution data;
-    public sealed partial class FitsInDispenserComponent : Component
-    {
-        /// <summary>
-        /// Solution name that will interact with ReagentDispenserComponent.
-        /// </summary>
-        [ViewVariables(VVAccess.ReadWrite)]
-        [DataField("solution")]
-        public string Solution { get; set; } = "default";
-    }
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public string Solution = "default";
 }
index cb5ab7679d72275e3b31aaf91546f68f50786252..245b7398a7e090f91ec2fd032f7ae6e9551adbf6 100644 (file)
@@ -14,14 +14,12 @@ public sealed partial class RefillableSolutionComponent : Component
     /// <summary>
     /// Solution name that can added to easily.
     /// </summary>
-    [ViewVariables(VVAccess.ReadWrite)]
-    [DataField("solution")]
-    public string Solution { get; set; } = "default";
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public string Solution = "default";
 
     /// <summary>
     /// The maximum amount that can be transferred to the solution at once
     /// </summary>
-    [DataField("maxRefill")]
-    [ViewVariables(VVAccess.ReadWrite)]
-    public FixedPoint2? MaxRefill { get; set; } = null;
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public FixedPoint2? MaxRefill = null;
 }
index 9d88fcac945c12519c6edcbb30c323a788d7866b..f07a77c1ac6673423891b361822bfee574bb47cf 100644 (file)
@@ -13,6 +13,12 @@ public sealed partial class SmokeComponent : Component
 {
     public const string SolutionName = "solutionArea";
 
+    /// <summary>
+    /// The solution on the entity with touch and ingestion reactions.
+    /// </summary>
+    [DataField]
+    public Entity<SolutionComponent>? Solution = null;
+
     /// <summary>
     /// The max amount of tiles this smoke cloud can spread to.
     /// </summary>
index ca597710d48a2058694944828fe57ff2eecaad3e..8598e1ad99f6fcf7010702f9dc189d9a86a664a7 100644 (file)
@@ -71,6 +71,7 @@ namespace Content.Shared.Chemistry.Components
         /// <summary>
         ///     The name of this solution, if it is contained in some <see cref="SolutionContainerManagerComponent"/>
         /// </summary>
+        [DataField]
         public string? Name;
 
         /// <summary>
@@ -100,7 +101,7 @@ namespace Content.Shared.Chemistry.Components
             foreach (var (reagent, quantity) in Contents)
             {
                 _heatCapacity += (float) quantity *
-                                 protoMan.Index<ReagentPrototype>(reagent.Prototype).SpecificHeat;
+                                    protoMan.Index<ReagentPrototype>(reagent.Prototype).SpecificHeat;
             }
         }
 
@@ -158,10 +159,12 @@ namespace Content.Shared.Chemistry.Components
 
         public Solution(Solution solution)
         {
+            Contents = solution.Contents.ShallowClone();
             Volume = solution.Volume;
+            MaxVolume = solution.MaxVolume;
+            Temperature = solution.Temperature;
             _heatCapacity = solution._heatCapacity;
             _heatCapacityDirty = solution._heatCapacityDirty;
-            Contents = solution.Contents.ShallowClone();
             ValidateSolution();
         }
 
@@ -174,7 +177,7 @@ namespace Content.Shared.Chemistry.Components
         public void ValidateSolution()
         {
             // sandbox forbids: [Conditional("DEBUG")]
-#if DEBUG
+    #if DEBUG
             // Correct volume
             DebugTools.Assert(Contents.Select(x => x.Quantity).Sum() == Volume);
 
@@ -192,7 +195,7 @@ namespace Content.Shared.Chemistry.Components
                 UpdateHeatCapacity(null);
                 DebugTools.Assert(MathHelper.CloseTo(_heatCapacity, cur));
             }
-#endif
+    #endif
         }
 
         void ISerializationHooks.AfterDeserialization()
diff --git a/Content.Shared/Chemistry/Components/SolutionComponent.cs b/Content.Shared/Chemistry/Components/SolutionComponent.cs
new file mode 100644 (file)
index 0000000..687a545
--- /dev/null
@@ -0,0 +1,22 @@
+using Content.Shared.Chemistry.Components.SolutionManager;
+using Content.Shared.Materials;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Chemistry.Components;
+
+/// <summary>
+/// <para>Holds the composition of an entity made from reagents and its reagent temperature.</para>
+/// <para>If the entity is used to represent a collection of reagents inside of a container such as a beaker, syringe, bloodstream, food, or similar the entity is tracked by a <see cref="SolutionContainerManagerComponent"/> on the container and has a <see cref="ContainedSolutionComponent"/> tracking which container it's in.</para>
+/// </summary>
+/// <remarks>
+/// <para>Once reagents and materials have been merged this component should be depricated in favor of using a combination of <see cref="PhysicalCompositionComponent"/> and <see cref="Content.Server.Temperature.Components.TemperatureComponent"/>. May require minor reworks to both.</para>
+/// </remarks>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class SolutionComponent : Component
+{
+    /// <summary>
+    /// <para>The reagents the entity is composed of and their temperature.</para>
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public Solution Solution = new();
+}
diff --git a/Content.Shared/Chemistry/Components/SolutionManager/ContainedSolutionComponent.cs b/Content.Shared/Chemistry/Components/SolutionManager/ContainedSolutionComponent.cs
new file mode 100644 (file)
index 0000000..d648a9f
--- /dev/null
@@ -0,0 +1,31 @@
+using Content.Shared.Chemistry.EntitySystems;
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Chemistry.Components.SolutionManager;
+
+/// <summary>
+/// Component used to relate a solution to its container.
+/// </summary>
+/// <remarks>
+/// When containers are finally ECS'd have this attach to the container entity.
+/// The <see cref="Solution.MaxVolume"/> field should then be extracted out into this component.
+/// Solution entities would just become an apporpriately composed entity hanging out in the container.
+/// Will probably require entities in components being given a relation to associate themselves with their container.
+/// </remarks>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+[Access(typeof(SharedSolutionContainerSystem))]
+public sealed partial class ContainedSolutionComponent : Component
+{
+    /// <summary>
+    /// The entity that the solution is contained in.
+    /// </summary>
+    [DataField(required: true), AutoNetworkedField]
+    public EntityUid Container;
+
+    /// <summary>
+    /// The name/key of the container the solution is located in.
+    /// </summary>
+    [DataField(required: true), AutoNetworkedField]
+    public string ContainerName = default!;
+}
index f9cd6e8e8e7e950eeb2c9488b28ae78c368bb661..a949ae72e2997ad512b8a8e1b2d77817bca16f59 100644 (file)
@@ -9,7 +9,6 @@ public sealed partial class DrawableSolutionComponent : Component
     /// <summary>
     /// Solution name that can be removed with syringes.
     /// </summary>
-    [ViewVariables(VVAccess.ReadWrite)]
-    [DataField("solution")]
-    public string Solution { get; set; } = "default";
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public string Solution = "default";
 }
index b12a5bd6e4eeab06ae074de6fe0af4138ec1d88b..76e7967db2695470fe342bc0fa064e45b806f532 100644 (file)
@@ -3,7 +3,6 @@
 [RegisterComponent]
 public sealed partial class ExaminableSolutionComponent : Component
 {
-    [ViewVariables(VVAccess.ReadWrite)]
-    [DataField("solution")]
-    public string Solution { get; set; } = "default";
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public string Solution = "default";
 }
index a696244956ef46a335ff49e540b170f2fc3213d6..97266764dcc33a4b56f9758dea55ba004fc7bbba 100644 (file)
@@ -10,7 +10,6 @@ public sealed partial class InjectableSolutionComponent : Component
     /// <summary>
     /// Solution name which can be added with syringes.
     /// </summary>
-    [ViewVariables(VVAccess.ReadWrite)]
-    [DataField("solution")]
-    public string Solution { get; set; } = "default";
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public string Solution = "default";
 }
index 9dfa4a71fd4323d3afbb6b6bb86fb46bc6bcd77a..fe62555f0dca4f56ecf6278a4339fc86a868c5de 100644 (file)
@@ -1,12 +1,36 @@
 using Content.Shared.Chemistry.EntitySystems;
+using Robust.Shared.Containers;
+using Robust.Shared.GameStates;
 
 namespace Content.Shared.Chemistry.Components.SolutionManager;
 
-[RegisterComponent]
-[Access(typeof(SolutionContainerSystem))]
+/// <summary>
+/// <para>A map of the solution entities contained within this entity.</para>
+/// <para>Every solution entity this maps should have a <see cref="SolutionComponent"/> to track its state and a <see cref="ContainedSolutionComponent"/> to track its container.</para>
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+[Access(typeof(SharedSolutionContainerSystem))]
 public sealed partial class SolutionContainerManagerComponent : Component
 {
-    [DataField("solutions")]
-    [Access(typeof(SolutionContainerSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends
-    public Dictionary<string, Solution> Solutions = new();
+    /// <summary>
+    /// The default amount of space that will be allocated for solutions in solution containers.
+    /// Most solution containers will only contain 1-2 solutions.
+    /// </summary>
+    public const int DefaultCapacity = 2;
+
+    /// <summary>
+    /// The names of each solution container attached to this entity.
+    /// Actually accessing them must be done via <see cref="ContainerManagerComponent"/>.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public HashSet<string> Containers = new(DefaultCapacity);
+
+    /// <summary>
+    /// The set of solutions to load onto this entity during mapinit.
+    /// </summary>
+    /// <remarks>
+    /// Should be null after mapinit.
+    /// </remarks>
+    [DataField(serverOnly: true)] // Needs to be serverOnly or these will get loaded on the client and never cleared. Can be reworked when entity spawning is predicted.
+    public Dictionary<string, Solution>? Solutions = null;
 }
index 14c4560dd8ded5f6de5e01dff23693926d97365d..87957066125eff356f67eec288bd097b14823c8c 100644 (file)
@@ -20,7 +20,7 @@ public abstract class SharedSolutionContainerMixerSystem : EntitySystem
     [Dependency] private readonly SharedAudioSystem _audio = default!;
     [Dependency] private readonly SharedContainerSystem _container = default!;
     [Dependency] private readonly SharedPopupSystem _popup = default!;
-    [Dependency] private readonly SolutionContainerSystem _solution = default!;
+    [Dependency] private readonly SharedSolutionContainerSystem _solution = default!;
 
     /// <inheritdoc/>
     public override void Initialize()
@@ -98,10 +98,10 @@ public abstract class SharedSolutionContainerMixerSystem : EntitySystem
 
         foreach (var ent in container.ContainedEntities)
         {
-            if (!_solution.TryGetFitsInDispenser(ent, out var solution))
+            if (!_solution.TryGetFitsInDispenser(ent, out var soln, out _))
                 continue;
 
-            _solution.UpdateChemicals(ent, solution, true, reactionMixer);
+            _solution.UpdateChemicals(soln.Value, true, reactionMixer);
         }
     }
 
diff --git a/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.Capabilities.cs b/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.Capabilities.cs
new file mode 100644 (file)
index 0000000..0d4912a
--- /dev/null
@@ -0,0 +1,183 @@
+using Content.Shared.Chemistry.Components;
+using Content.Shared.Chemistry.Components.SolutionManager;
+using Content.Shared.Chemistry.Reaction;
+using Content.Shared.FixedPoint;
+using Robust.Shared.Utility;
+using System.Diagnostics.CodeAnalysis;
+using System.Text;
+
+namespace Content.Shared.Chemistry.EntitySystems;
+
+public abstract partial class SharedSolutionContainerSystem
+{
+    #region Solution Accessors
+
+    public bool TryGetRefillableSolution(Entity<RefillableSolutionComponent?, SolutionContainerManagerComponent?> entity, [NotNullWhen(true)] out Entity<SolutionComponent>? soln, [NotNullWhen(true)] out Solution? solution)
+    {
+        if (!Resolve(entity, ref entity.Comp1, logMissing: false))
+        {
+            (soln, solution) = (default!, null);
+            return false;
+        }
+
+        return TryGetSolution((entity.Owner, entity.Comp2), entity.Comp1.Solution, out soln, out solution);
+    }
+
+    public bool TryGetDrainableSolution(Entity<DrainableSolutionComponent?, SolutionContainerManagerComponent?> entity, [NotNullWhen(true)] out Entity<SolutionComponent>? soln, [NotNullWhen(true)] out Solution? solution)
+    {
+        if (!Resolve(entity, ref entity.Comp1, logMissing: false))
+        {
+            (soln, solution) = (default!, null);
+            return false;
+        }
+
+        return TryGetSolution((entity.Owner, entity.Comp2), entity.Comp1.Solution, out soln, out solution);
+    }
+
+    public bool TryGetDumpableSolution(Entity<DumpableSolutionComponent?, SolutionContainerManagerComponent?> entity, [NotNullWhen(true)] out Entity<SolutionComponent>? soln, [NotNullWhen(true)] out Solution? solution)
+    {
+        if (!Resolve(entity, ref entity.Comp1, logMissing: false))
+        {
+            (soln, solution) = (default!, null);
+            return false;
+        }
+
+        return TryGetSolution((entity.Owner, entity.Comp2), entity.Comp1.Solution, out soln, out solution);
+    }
+
+    public bool TryGetDrawableSolution(Entity<DrawableSolutionComponent?, SolutionContainerManagerComponent?> entity, [NotNullWhen(true)] out Entity<SolutionComponent>? soln, [NotNullWhen(true)] out Solution? solution)
+    {
+        if (!Resolve(entity, ref entity.Comp1, logMissing: false))
+        {
+            (soln, solution) = (default!, null);
+            return false;
+        }
+
+        return TryGetSolution((entity.Owner, entity.Comp2), entity.Comp1.Solution, out soln, out solution);
+    }
+
+    public bool TryGetInjectableSolution(Entity<InjectableSolutionComponent?, SolutionContainerManagerComponent?> entity, [NotNullWhen(true)] out Entity<SolutionComponent>? soln, [NotNullWhen(true)] out Solution? solution)
+    {
+        if (!Resolve(entity, ref entity.Comp1, logMissing: false))
+        {
+            (soln, solution) = (default!, null);
+            return false;
+        }
+
+        return TryGetSolution((entity.Owner, entity.Comp2), entity.Comp1.Solution, out soln, out solution);
+    }
+
+    public bool TryGetFitsInDispenser(Entity<FitsInDispenserComponent?, SolutionContainerManagerComponent?> entity, [NotNullWhen(true)] out Entity<SolutionComponent>? soln, [NotNullWhen(true)] out Solution? solution)
+    {
+        if (!Resolve(entity, ref entity.Comp1, logMissing: false))
+        {
+            (soln, solution) = (default!, null);
+            return false;
+        }
+
+        return TryGetSolution((entity.Owner, entity.Comp2), entity.Comp1.Solution, out soln, out solution);
+    }
+
+    public bool TryGetMixableSolution(Entity<SolutionContainerManagerComponent?> container, [NotNullWhen(true)] out Entity<SolutionComponent>? solution)
+    {
+        var getMixableSolutionAttempt = new GetMixableSolutionAttemptEvent(container);
+        RaiseLocalEvent(container, ref getMixableSolutionAttempt);
+        if (getMixableSolutionAttempt.MixedSolution != null)
+        {
+            solution = getMixableSolutionAttempt.MixedSolution;
+            return true;
+        }
+
+        if (!Resolve(container, ref container.Comp, false))
+        {
+            solution = default!;
+            return false;
+        }
+
+        var tryGetSolution = EnumerateSolutions(container).FirstOrNull(x => x.Solution.Comp.Solution.CanMix);
+        if (tryGetSolution.HasValue)
+        {
+            solution = tryGetSolution.Value.Solution;
+            return true;
+        }
+
+        solution = default!;
+        return false;
+    }
+
+    #endregion Solution Accessors
+
+    #region Solution Modifiers
+
+    public void Refill(Entity<RefillableSolutionComponent?> entity, Entity<SolutionComponent> soln, Solution refill)
+    {
+        if (!Resolve(entity, ref entity.Comp, logMissing: false))
+            return;
+
+        AddSolution(soln, refill);
+    }
+
+    public void Inject(Entity<InjectableSolutionComponent?> entity, Entity<SolutionComponent> soln, Solution inject)
+    {
+        if (!Resolve(entity, ref entity.Comp, logMissing: false))
+            return;
+
+        AddSolution(soln, inject);
+    }
+
+    public Solution Drain(Entity<DrainableSolutionComponent?> entity, Entity<SolutionComponent> soln, FixedPoint2 quantity)
+    {
+        if (!Resolve(entity, ref entity.Comp, logMissing: false))
+            return new();
+
+        return SplitSolution(soln, quantity);
+    }
+
+    public Solution Draw(Entity<DrawableSolutionComponent?> entity, Entity<SolutionComponent> soln, FixedPoint2 quantity)
+    {
+        if (!Resolve(entity, ref entity.Comp, logMissing: false))
+            return new();
+
+        return SplitSolution(soln, quantity);
+    }
+
+    #endregion Solution Modifiers
+
+    public float PercentFull(EntityUid uid)
+    {
+        if (!TryGetDrainableSolution(uid, out _, out var solution) || solution.MaxVolume.Equals(FixedPoint2.Zero))
+            return 0;
+
+        return solution.FillFraction * 100;
+    }
+
+    #region Static Methods
+
+    public static string ToPrettyString(Solution solution)
+    {
+        var sb = new StringBuilder();
+        if (solution.Name == null)
+            sb.Append("[");
+        else
+            sb.Append($"{solution.Name}:[");
+        var first = true;
+        foreach (var (id, quantity) in solution.Contents)
+        {
+            if (first)
+            {
+                first = false;
+            }
+            else
+            {
+                sb.Append(", ");
+            }
+
+            sb.AppendFormat("{0}: {1}u", id, quantity);
+        }
+
+        sb.Append(']');
+        return sb.ToString();
+    }
+
+    #endregion Static Methods
+}
diff --git a/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.Relays.cs b/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.Relays.cs
new file mode 100644 (file)
index 0000000..e6e96fa
--- /dev/null
@@ -0,0 +1,148 @@
+using Content.Shared.Chemistry.Components;
+using Content.Shared.Chemistry.Components.SolutionManager;
+using Content.Shared.Chemistry.Reaction;
+using Content.Shared.FixedPoint;
+
+namespace Content.Shared.Chemistry.EntitySystems;
+
+#region Events
+
+/// <summary>
+/// This event alerts system that the solution was changed
+/// </summary>
+[ByRefEvent]
+public record struct SolutionContainerChangedEvent
+{
+    public readonly Solution Solution;
+    public readonly string SolutionId;
+
+    public SolutionContainerChangedEvent(Solution solution, string solutionId)
+    {
+        SolutionId = solutionId;
+        Solution = solution;
+    }
+}
+
+/// <summary>
+/// An event raised when more reagents are added to a (managed) solution than it can hold.
+/// </summary>
+[ByRefEvent]
+public record struct SolutionContainerOverflowEvent(EntityUid SolutionEnt, Solution SolutionHolder, Solution Overflow)
+{
+    /// <summary>The entity which contains the solution that has overflowed.</summary>
+    public readonly EntityUid SolutionEnt = SolutionEnt;
+    /// <summary>The solution that has overflowed.</summary>
+    public readonly Solution SolutionHolder = SolutionHolder;
+    /// <summary>The reagents that have overflowed the solution.</summary>
+    public readonly Solution Overflow = Overflow;
+    /// <summary>The volume by which the solution has overflowed.</summary>
+    public readonly FixedPoint2 OverflowVol = Overflow.Volume;
+    /// <summary>Whether some subscriber has taken care of the effects of the overflow.</summary>
+    public bool Handled = false;
+}
+
+/// <summary>
+/// Ref event used to relay events raised on solution entities to their containers.
+/// </summary>
+/// <typeparam name="TEvent"></typeparam>
+/// <param name="Event">The event that is being relayed.</param>
+/// <param name="ContainerEnt">The container entity that the event is being relayed to.</param>
+/// <param name="Name">The name of the solution entity that the event is being relayed from.</param>
+[ByRefEvent]
+public record struct SolutionRelayEvent<TEvent>(TEvent Event, EntityUid ContainerEnt, string Name)
+{
+    public readonly EntityUid ContainerEnt = ContainerEnt;
+    public readonly string Name = Name;
+    public TEvent Event = Event;
+}
+
+/// <summary>
+/// Ref event used to relay events raised on solution containers to their contained solutions.
+/// </summary>
+/// <typeparam name="TEvent"></typeparam>
+/// <param name="Event">The event that is being relayed.</param>
+/// <param name="SolutionEnt">The solution entity that the event is being relayed to.</param>
+/// <param name="Name">The name of the solution entity that the event is being relayed to.</param>
+[ByRefEvent]
+public record struct SolutionContainerRelayEvent<TEvent>(TEvent Event, Entity<SolutionComponent> SolutionEnt, string Name)
+{
+    public readonly Entity<SolutionComponent> SolutionEnt = SolutionEnt;
+    public readonly string Name = Name;
+    public TEvent Event = Event;
+}
+
+#endregion Events
+
+public abstract partial class SharedSolutionContainerSystem
+{
+    protected void InitializeRelays()
+    {
+        SubscribeLocalEvent<ContainedSolutionComponent, SolutionChangedEvent>(OnSolutionChanged);
+        SubscribeLocalEvent<ContainedSolutionComponent, SolutionOverflowEvent>(OnSolutionOverflow);
+        SubscribeLocalEvent<ContainedSolutionComponent, ReactionAttemptEvent>(RelaySolutionRefEvent);
+    }
+
+    #region Event Handlers
+
+    protected virtual void OnSolutionChanged(Entity<ContainedSolutionComponent> entity, ref SolutionChangedEvent args)
+    {
+        var (solutionId, solutionComp) = args.Solution;
+        var solution = solutionComp.Solution;
+
+        UpdateAppearance(entity.Comp.Container, (solutionId, solutionComp, entity.Comp));
+
+        var relayEvent = new SolutionContainerChangedEvent(solution, entity.Comp.ContainerName);
+        RaiseLocalEvent(entity.Comp.Container, ref relayEvent);
+    }
+
+    protected virtual void OnSolutionOverflow(Entity<ContainedSolutionComponent> entity, ref SolutionOverflowEvent args)
+    {
+        var solution = args.Solution.Comp.Solution;
+        var overflow = solution.SplitSolution(args.Overflow);
+        var relayEv = new SolutionContainerOverflowEvent(entity.Owner, solution, overflow)
+        {
+            Handled = args.Handled,
+        };
+
+        RaiseLocalEvent(entity.Comp.Container, ref relayEv);
+        args.Handled = relayEv.Handled;
+    }
+
+    #region Relay Event Handlers
+
+    private void RelaySolutionValEvent<TEvent>(EntityUid uid, ContainedSolutionComponent comp, TEvent @event)
+    {
+        var relayEvent = new SolutionRelayEvent<TEvent>(@event, uid, comp.ContainerName);
+        RaiseLocalEvent(comp.Container, ref relayEvent);
+    }
+
+    private void RelaySolutionRefEvent<TEvent>(Entity<ContainedSolutionComponent> entity, ref TEvent @event)
+    {
+        var relayEvent = new SolutionRelayEvent<TEvent>(@event, entity.Owner, entity.Comp.ContainerName);
+        RaiseLocalEvent(entity.Comp.Container, ref relayEvent);
+        @event = relayEvent.Event;
+    }
+
+    private void RelaySolutionContainerEvent<TEvent>(EntityUid uid, SolutionContainerManagerComponent comp, TEvent @event)
+    {
+        foreach (var (name, soln) in EnumerateSolutions((uid, comp)))
+        {
+            var relayEvent = new SolutionContainerRelayEvent<TEvent>(@event, soln, name!);
+            RaiseLocalEvent(soln, ref relayEvent);
+        }
+    }
+
+    private void RelaySolutionContainerEvent<TEvent>(Entity<SolutionContainerManagerComponent> entity, ref TEvent @event)
+    {
+        foreach (var (name, soln) in EnumerateSolutions((entity.Owner, entity.Comp)))
+        {
+            var relayEvent = new SolutionContainerRelayEvent<TEvent>(@event, soln, name!);
+            RaiseLocalEvent(soln, ref relayEvent);
+            @event = relayEvent.Event;
+        }
+    }
+
+    #endregion Relay Event Handlers
+
+    #endregion Event Handlers
+}
diff --git a/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs b/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs
new file mode 100644 (file)
index 0000000..392b83f
--- /dev/null
@@ -0,0 +1,857 @@
+using Content.Shared.Chemistry.Components;
+using Content.Shared.Chemistry.Components.SolutionManager;
+using Content.Shared.Chemistry.Reaction;
+using Content.Shared.Chemistry.Reagent;
+using Content.Shared.Examine;
+using Content.Shared.FixedPoint;
+using Content.Shared.Verbs;
+using JetBrains.Annotations;
+using Robust.Shared.Containers;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Utility;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using Dependency = Robust.Shared.IoC.DependencyAttribute;
+
+namespace Content.Shared.Chemistry.EntitySystems;
+
+/// <summary>
+/// The event raised whenever a solution entity is modified.
+/// </summary>
+/// <remarks>
+/// Raised after chemcial reactions and <see cref="SolutionOverflowEvent"/> are handled.
+/// </remarks>
+/// <param name="Solution">The solution entity that has been modified.</param>
+[ByRefEvent]
+public readonly partial record struct SolutionChangedEvent(Entity<SolutionComponent> Solution);
+
+/// <summary>
+/// The event raised whenever a solution entity is filled past its capacity.
+/// </summary>
+/// <param name="Solution">The solution entity that has been overfilled.</param>
+/// <param name="Overflow">The amount by which the solution entity has been overfilled.</param>
+[ByRefEvent]
+public partial record struct SolutionOverflowEvent(Entity<SolutionComponent> Solution, FixedPoint2 Overflow)
+{
+    /// <summary>The solution entity that has been overfilled.</summary>
+    public readonly Entity<SolutionComponent> Solution = Solution;
+    /// <summary>The amount by which the solution entity has been overfilled.</summary>
+    public readonly FixedPoint2 Overflow = Overflow;
+    /// <summary>Whether any of the event handlers for this event have handled overflow behaviour.</summary>
+    public bool Handled = false;
+}
+
+/// <summary>
+/// Part of Chemistry system deal with SolutionContainers
+/// </summary>
+[UsedImplicitly]
+public abstract partial class SharedSolutionContainerSystem : EntitySystem
+{
+    [Dependency] protected readonly IPrototypeManager PrototypeManager = default!;
+    [Dependency] protected readonly ChemicalReactionSystem ChemicalReactionSystem = default!;
+    [Dependency] protected readonly ExamineSystemShared ExamineSystem = default!;
+    [Dependency] protected readonly SharedAppearanceSystem AppearanceSystem = default!;
+    [Dependency] protected readonly SharedContainerSystem ContainerSystem = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        InitializeRelays();
+
+        SubscribeLocalEvent<SolutionComponent, ComponentInit>(OnComponentInit);
+        SubscribeLocalEvent<SolutionComponent, ComponentStartup>(OnComponentStartup);
+        SubscribeLocalEvent<SolutionComponent, ComponentShutdown>(OnComponentShutdown);
+
+        SubscribeLocalEvent<SolutionContainerManagerComponent, ComponentInit>(OnComponentInit);
+
+        SubscribeLocalEvent<ExaminableSolutionComponent, ExaminedEvent>(OnExamineSolution);
+        SubscribeLocalEvent<ExaminableSolutionComponent, GetVerbsEvent<ExamineVerb>>(OnSolutionExaminableVerb);
+    }
+
+
+    /// <summary>
+    /// Attempts to resolve a solution associated with an entity.
+    /// </summary>
+    /// <param name="container">The entity that holdes the container the solution entity is in.</param>
+    /// <param name="name">The name of the solution entities container.</param>
+    /// <param name="entity">A reference to a solution entity to load the associated solution entity into. Will be unchanged if not null.</param>
+    /// <param name="solution">Returns the solution state of the solution entity.</param>
+    /// <returns>Whether the solution was successfully resolved.</returns>
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public bool ResolveSolution(Entity<SolutionContainerManagerComponent?> container, string? name, [NotNullWhen(true)] ref Entity<SolutionComponent>? entity, [NotNullWhen(true)] out Solution? solution)
+    {
+        if (!ResolveSolution(container, name, ref entity))
+        {
+            solution = null;
+            return false;
+        }
+
+        solution = entity.Value.Comp.Solution;
+        return true;
+    }
+
+    /// <inheritdoc cref="ResolveSolution"/>
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public bool ResolveSolution(Entity<SolutionContainerManagerComponent?> container, string? name, [NotNullWhen(true)] ref Entity<SolutionComponent>? entity)
+    {
+        if (entity is not null)
+        {
+            DebugTools.Assert(TryGetSolution(container, name, out var debugEnt)
+                              && debugEnt.Value.Owner == entity.Value.Owner);
+            return true;
+        }
+
+        return TryGetSolution(container, name, out entity);
+    }
+
+    /// <summary>
+    /// Attempts to fetch a solution entity associated with an entity.
+    /// </summary>
+    /// <remarks>
+    /// If the solution entity will be frequently accessed please use the equivalent <see cref="ResolveSolution"/> method and cache the result.
+    /// </remarks>
+    /// <param name="container">The entity the solution entity should be associated with.</param>
+    /// <param name="name">The name of the solution entity to fetch.</param>
+    /// <param name="entity">Returns the solution entity that was fetched.</param>
+    /// <param name="solution">Returns the solution state of the solution entity that was fetched.</param>
+    /// <returns></returns>
+    public bool TryGetSolution(Entity<SolutionContainerManagerComponent?> container, string? name, [NotNullWhen(true)] out Entity<SolutionComponent>? entity, [NotNullWhen(true)] out Solution? solution)
+    {
+        if (!TryGetSolution(container, name, out entity))
+        {
+            solution = null;
+            return false;
+        }
+
+        solution = entity.Value.Comp.Solution;
+        return true;
+    }
+
+    /// <inheritdoc cref="TryGetSolution"/>
+    public bool TryGetSolution(Entity<SolutionContainerManagerComponent?> container, string? name, [NotNullWhen(true)] out Entity<SolutionComponent>? entity)
+    {
+        EntityUid uid;
+        if (name is null)
+            uid = container;
+        else if (
+            ContainerSystem.TryGetContainer(container, $"solution@{name}", out var solutionContainer) &&
+            solutionContainer is ContainerSlot solutionSlot &&
+            solutionSlot.ContainedEntity is { } containedSolution
+        )
+            uid = containedSolution;
+        else
+        {
+            entity = null;
+            return false;
+        }
+
+        if (!TryComp(uid, out SolutionComponent? comp))
+        {
+            entity = null;
+            return false;
+        }
+
+        entity = (uid, comp);
+        return true;
+    }
+
+    public IEnumerable<(string? Name, Entity<SolutionComponent> Solution)> EnumerateSolutions(Entity<SolutionContainerManagerComponent?> container, bool includeSelf = true)
+    {
+        if (includeSelf && TryComp(container, out SolutionComponent? solutionComp))
+            yield return (null, (container.Owner, solutionComp));
+
+        if (!Resolve(container, ref container.Comp, logMissing: false))
+            yield break;
+
+        foreach (var name in container.Comp.Containers)
+        {
+            if (ContainerSystem.GetContainer(container, $"solution@{name}") is ContainerSlot slot && slot.ContainedEntity is { } solutionId)
+                yield return (name, (solutionId, Comp<SolutionComponent>(solutionId)));
+        }
+    }
+
+    public IEnumerable<(string Name, Solution Solution)> EnumerateSolutions(SolutionContainerManagerComponent container)
+    {
+        if (container.Solutions is not { Count: > 0 } solutions)
+            yield break;
+
+        foreach (var (name, solution) in solutions)
+        {
+            yield return (name, solution);
+        }
+    }
+
+
+    protected void UpdateAppearance(Entity<AppearanceComponent?> container, Entity<SolutionComponent, ContainedSolutionComponent> soln)
+    {
+        var (uid, appearanceComponent) = container;
+        if (!HasComp<SolutionContainerVisualsComponent>(uid) || !Resolve(uid, ref appearanceComponent, logMissing: false))
+            return;
+
+        var (_, comp, relation) = soln;
+        var solution = comp.Solution;
+
+        AppearanceSystem.SetData(uid, SolutionContainerVisuals.FillFraction, solution.FillFraction, appearanceComponent);
+        AppearanceSystem.SetData(uid, SolutionContainerVisuals.Color, solution.GetColor(PrototypeManager), appearanceComponent);
+        AppearanceSystem.SetData(uid, SolutionContainerVisuals.SolutionName, relation.ContainerName, appearanceComponent);
+
+        if (solution.GetPrimaryReagentId() is { } reagent)
+            AppearanceSystem.SetData(uid, SolutionContainerVisuals.BaseOverride, reagent.ToString(), appearanceComponent);
+        else
+            AppearanceSystem.SetData(uid, SolutionContainerVisuals.BaseOverride, string.Empty, appearanceComponent);
+    }
+
+
+    public FixedPoint2 GetTotalPrototypeQuantity(EntityUid owner, string reagentId)
+    {
+        var reagentQuantity = FixedPoint2.New(0);
+        if (EntityManager.EntityExists(owner)
+            && EntityManager.TryGetComponent(owner, out SolutionContainerManagerComponent? managerComponent))
+        {
+            foreach (var (_, soln) in EnumerateSolutions((owner, managerComponent)))
+            {
+                var solution = soln.Comp.Solution;
+                reagentQuantity += solution.GetTotalPrototypeQuantity(reagentId);
+            }
+        }
+
+        return reagentQuantity;
+    }
+
+
+    /// <summary>
+    /// Dirties a solution entity that has been modified and prompts updates to chemical reactions and overflow state.
+    /// Should be invoked whenever a solution entity is modified.
+    /// </summary>
+    /// <remarks>
+    /// 90% of this system is ensuring that this proc is invoked whenever a solution entity is changed. The other 10% <i>is</i> this proc.
+    /// </remarks>
+    /// <param name="soln"></param>
+    /// <param name="needsReactionsProcessing"></param>
+    /// <param name="mixerComponent"></param>
+    public void UpdateChemicals(Entity<SolutionComponent> soln, bool needsReactionsProcessing = true, ReactionMixerComponent? mixerComponent = null)
+    {
+        Dirty(soln);
+
+        var (uid, comp) = soln;
+        var solution = comp.Solution;
+
+        // Process reactions
+        if (needsReactionsProcessing && solution.CanReact)
+            ChemicalReactionSystem.FullyReactSolution(soln, mixerComponent);
+
+        var overflow = solution.Volume - solution.MaxVolume;
+        if (overflow > FixedPoint2.Zero)
+        {
+            var overflowEv = new SolutionOverflowEvent(soln, overflow);
+            RaiseLocalEvent(uid, ref overflowEv);
+        }
+
+        UpdateAppearance((uid, comp, null));
+
+        var changedEv = new SolutionChangedEvent(soln);
+        RaiseLocalEvent(uid, ref changedEv);
+    }
+
+    public void UpdateAppearance(Entity<SolutionComponent, AppearanceComponent?> soln)
+    {
+        var (uid, comp, appearanceComponent) = soln;
+        var solution = comp.Solution;
+
+        if (!EntityManager.EntityExists(uid) || !Resolve(uid, ref appearanceComponent, false))
+            return;
+
+        AppearanceSystem.SetData(uid, SolutionContainerVisuals.FillFraction, solution.FillFraction, appearanceComponent);
+        AppearanceSystem.SetData(uid, SolutionContainerVisuals.Color, solution.GetColor(PrototypeManager), appearanceComponent);
+
+        if (solution.GetPrimaryReagentId() is { } reagent)
+            AppearanceSystem.SetData(uid, SolutionContainerVisuals.BaseOverride, reagent.ToString(), appearanceComponent);
+        else
+            AppearanceSystem.SetData(uid, SolutionContainerVisuals.BaseOverride, string.Empty, appearanceComponent);
+    }
+
+    /// <summary>
+    ///     Removes part of the solution in the container.
+    /// </summary>
+    /// <param name="targetUid"></param>
+    /// <param name="solutionHolder"></param>
+    /// <param name="quantity">the volume of solution to remove.</param>
+    /// <returns>The solution that was removed.</returns>
+    public Solution SplitSolution(Entity<SolutionComponent> soln, FixedPoint2 quantity)
+    {
+        var (uid, comp) = soln;
+        var solution = comp.Solution;
+
+        var splitSol = solution.SplitSolution(quantity);
+        UpdateChemicals(soln);
+        return splitSol;
+    }
+
+    public Solution SplitStackSolution(Entity<SolutionComponent> soln, FixedPoint2 quantity, int stackCount)
+    {
+        var (uid, comp) = soln;
+        var solution = comp.Solution;
+
+        var splitSol = solution.SplitSolution(quantity / stackCount);
+        solution.SplitSolution(quantity - splitSol.Volume);
+        UpdateChemicals(soln);
+        return splitSol;
+    }
+
+    /// <summary>
+    /// Splits a solution without the specified reagent(s).
+    /// </summary>
+    public Solution SplitSolutionWithout(Entity<SolutionComponent> soln, FixedPoint2 quantity, params string[] reagents)
+    {
+        var (uid, comp) = soln;
+        var solution = comp.Solution;
+
+        var splitSol = solution.SplitSolutionWithout(quantity, reagents);
+        UpdateChemicals(soln);
+        return splitSol;
+    }
+
+    public void RemoveAllSolution(Entity<SolutionComponent> soln)
+    {
+        var (uid, comp) = soln;
+        var solution = comp.Solution;
+
+        if (solution.Volume == 0)
+            return;
+
+        solution.RemoveAllSolution();
+        UpdateChemicals(soln);
+    }
+
+    /// <summary>
+    ///     Sets the capacity (maximum volume) of a solution to a new value.
+    /// </summary>
+    /// <param name="targetUid">The entity containing the solution.</param>
+    /// <param name="targetSolution">The solution to set the capacity of.</param>
+    /// <param name="capacity">The value to set the capacity of the solution to.</param>
+    public void SetCapacity(Entity<SolutionComponent> soln, FixedPoint2 capacity)
+    {
+        var (uid, comp) = soln;
+        var solution = comp.Solution;
+
+        if (solution.MaxVolume == capacity)
+            return;
+
+        solution.MaxVolume = capacity;
+        UpdateChemicals(soln);
+    }
+
+    /// <summary>
+    ///     Adds reagent of an Id to the container.
+    /// </summary>
+    /// <param name="targetUid"></param>
+    /// <param name="targetSolution">Container to which we are adding reagent</param>
+    /// <param name="reagentQuantity">The reagent to add.</param>
+    /// <param name="acceptedQuantity">The amount of reagent successfully added.</param>
+    /// <returns>If all the reagent could be added.</returns>
+    public bool TryAddReagent(Entity<SolutionComponent> soln, ReagentQuantity reagentQuantity, out FixedPoint2 acceptedQuantity, float? temperature = null)
+    {
+        var (uid, comp) = soln;
+        var solution = comp.Solution;
+
+        acceptedQuantity = solution.AvailableVolume > reagentQuantity.Quantity
+            ? reagentQuantity.Quantity
+            : solution.AvailableVolume;
+
+        if (acceptedQuantity <= 0)
+            return reagentQuantity.Quantity == 0;
+
+        if (temperature == null)
+        {
+            solution.AddReagent(reagentQuantity.Reagent, acceptedQuantity);
+        }
+        else
+        {
+            var proto = PrototypeManager.Index<ReagentPrototype>(reagentQuantity.Reagent.Prototype);
+            solution.AddReagent(proto, acceptedQuantity, temperature.Value, PrototypeManager);
+        }
+
+        UpdateChemicals(soln);
+        return acceptedQuantity == reagentQuantity.Quantity;
+    }
+
+    /// <summary>
+    ///     Adds reagent of an Id to the container.
+    /// </summary>
+    /// <param name="targetUid"></param>
+    /// <param name="targetSolution">Container to which we are adding reagent</param>
+    /// <param name="prototype">The Id of the reagent to add.</param>
+    /// <param name="quantity">The amount of reagent to add.</param>
+    /// <returns>If all the reagent could be added.</returns>
+    [PublicAPI]
+    public bool TryAddReagent(Entity<SolutionComponent> soln, string prototype, FixedPoint2 quantity, float? temperature = null, ReagentData? data = null)
+        => TryAddReagent(soln, new ReagentQuantity(prototype, quantity, data), out _, temperature);
+
+    /// <summary>
+    ///     Adds reagent of an Id to the container.
+    /// </summary>
+    /// <param name="targetUid"></param>
+    /// <param name="targetSolution">Container to which we are adding reagent</param>
+    /// <param name="prototype">The Id of the reagent to add.</param>
+    /// <param name="quantity">The amount of reagent to add.</param>
+    /// <param name="acceptedQuantity">The amount of reagent successfully added.</param>
+    /// <returns>If all the reagent could be added.</returns>
+    public bool TryAddReagent(Entity<SolutionComponent> soln, string prototype, FixedPoint2 quantity, out FixedPoint2 acceptedQuantity, float? temperature = null, ReagentData? data = null)
+    {
+        var reagent = new ReagentQuantity(prototype, quantity, data);
+        return TryAddReagent(soln, reagent, out acceptedQuantity, temperature);
+    }
+
+    /// <summary>
+    ///     Adds reagent of an Id to the container.
+    /// </summary>
+    /// <param name="targetUid"></param>
+    /// <param name="targetSolution">Container to which we are adding reagent</param>
+    /// <param name="reagentId">The reagent to add.</param>
+    /// <param name="quantity">The amount of reagent to add.</param>
+    /// <param name="acceptedQuantity">The amount of reagent successfully added.</param>
+    /// <returns>If all the reagent could be added.</returns>
+    public bool TryAddReagent(Entity<SolutionComponent> soln, ReagentId reagentId, FixedPoint2 quantity, out FixedPoint2 acceptedQuantity, float? temperature = null)
+    {
+        var quant = new ReagentQuantity(reagentId, quantity);
+        return TryAddReagent(soln, quant, out acceptedQuantity, temperature);
+    }
+
+    /// <summary>
+    ///     Removes reagent from a container.
+    /// </summary>
+    /// <param name="targetUid"></param>
+    /// <param name="container">Solution container from which we are removing reagent</param>
+    /// <param name="reagentQuantity">The reagent to remove.</param>
+    /// <returns>If the reagent to remove was found in the container.</returns>
+    public bool RemoveReagent(Entity<SolutionComponent> soln, ReagentQuantity reagentQuantity)
+    {
+        var (uid, comp) = soln;
+        var solution = comp.Solution;
+
+        var quant = solution.RemoveReagent(reagentQuantity);
+        if (quant <= FixedPoint2.Zero)
+            return false;
+
+        UpdateChemicals(soln);
+        return true;
+    }
+
+    /// <summary>
+    ///     Removes reagent from a container.
+    /// </summary>
+    /// <param name="targetUid"></param>
+    /// <param name="container">Solution container from which we are removing reagent</param>
+    /// <param name="prototype">The Id of the reagent to remove.</param>
+    /// <param name="quantity">The amount of reagent to remove.</param>
+    /// <returns>If the reagent to remove was found in the container.</returns>
+    public bool RemoveReagent(Entity<SolutionComponent> soln, string prototype, FixedPoint2 quantity, ReagentData? data = null)
+    {
+        return RemoveReagent(soln, new ReagentQuantity(prototype, quantity, data));
+    }
+
+    /// <summary>
+    ///     Removes reagent from a container.
+    /// </summary>
+    /// <param name="targetUid"></param>
+    /// <param name="container">Solution container from which we are removing reagent</param>
+    /// <param name="reagentId">The reagent to remove.</param>
+    /// <param name="quantity">The amount of reagent to remove.</param>
+    /// <returns>If the reagent to remove was found in the container.</returns>
+    public bool RemoveReagent(Entity<SolutionComponent> soln, ReagentId reagentId, FixedPoint2 quantity)
+    {
+        return RemoveReagent(soln, new ReagentQuantity(reagentId, quantity));
+    }
+
+    /// <summary>
+    ///     Moves some quantity of a solution from one solution to another.
+    /// </summary>
+    /// <param name="sourceUid">entity holding the source solution</param>
+    /// <param name="targetUid">entity holding the target solution</param>
+    /// <param name="source">source solution</param>
+    /// <param name="target">target solution</param>
+    /// <param name="quantity">quantity of solution to move from source to target. If this is a negative number, the source & target roles are reversed.</param>
+    public bool TryTransferSolution(Entity<SolutionComponent> soln, Solution source, FixedPoint2 quantity)
+    {
+        var (uid, comp) = soln;
+        var solution = comp.Solution;
+
+        if (quantity < 0)
+            throw new InvalidOperationException("Quantity must be positive");
+
+        quantity = FixedPoint2.Min(quantity, solution.AvailableVolume, source.Volume);
+        if (quantity == 0)
+            return false;
+
+        // TODO This should be made into a function that directly transfers reagents.
+        // Currently this is quite inefficient.
+        solution.AddSolution(source.SplitSolution(quantity), PrototypeManager);
+
+        UpdateChemicals(soln);
+        return true;
+    }
+
+    /// <summary>
+    ///     Adds a solution to the container, if it can fully fit.
+    /// </summary>
+    /// <param name="targetUid">entity holding targetSolution</param>
+    ///  <param name="targetSolution">entity holding targetSolution</param>
+    /// <param name="toAdd">solution being added</param>
+    /// <returns>If the solution could be added.</returns>
+    public bool TryAddSolution(Entity<SolutionComponent> soln, Solution toAdd)
+    {
+        var (uid, comp) = soln;
+        var solution = comp.Solution;
+
+        if (toAdd.Volume == FixedPoint2.Zero)
+            return true;
+        if (toAdd.Volume > solution.AvailableVolume)
+            return false;
+
+        ForceAddSolution(soln, toAdd);
+        return true;
+    }
+
+    /// <summary>
+    ///     Adds as much of a solution to a container as can fit.
+    /// </summary>
+    /// <param name="targetUid">The entity containing <paramref cref="targetSolution"/></param>
+    /// <param name="targetSolution">The solution being added to.</param>
+    /// <param name="toAdd">The solution being added to <paramref cref="targetSolution"/></param>
+    /// <returns>The quantity of the solution actually added.</returns>
+    public FixedPoint2 AddSolution(Entity<SolutionComponent> soln, Solution toAdd)
+    {
+        var (uid, comp) = soln;
+        var solution = comp.Solution;
+
+        if (toAdd.Volume == FixedPoint2.Zero)
+            return FixedPoint2.Zero;
+
+        var quantity = FixedPoint2.Max(FixedPoint2.Zero, FixedPoint2.Min(toAdd.Volume, solution.AvailableVolume));
+        if (quantity < toAdd.Volume)
+            TryTransferSolution(soln, toAdd, quantity);
+        else
+            ForceAddSolution(soln, toAdd);
+
+        return quantity;
+    }
+
+    /// <summary>
+    ///     Adds a solution to a container and updates the container.
+    /// </summary>
+    /// <param name="targetUid">The entity containing <paramref cref="targetSolution"/></param>
+    /// <param name="targetSolution">The solution being added to.</param>
+    /// <param name="toAdd">The solution being added to <paramref cref="targetSolution"/></param>
+    /// <returns>Whether any reagents were added to the solution.</returns>
+    public bool ForceAddSolution(Entity<SolutionComponent> soln, Solution toAdd)
+    {
+        var (uid, comp) = soln;
+        var solution = comp.Solution;
+
+        if (toAdd.Volume == FixedPoint2.Zero)
+            return false;
+
+        solution.AddSolution(toAdd, PrototypeManager);
+        UpdateChemicals(soln);
+        return true;
+    }
+
+    /// <summary>
+    ///     Adds a solution to the container, removing the overflow.
+    ///     Unlike <see cref="TryAddSolution"/> it will ignore size limits.
+    /// </summary>
+    /// <param name="targetUid">The entity containing <paramref cref="targetSolution"/></param>
+    /// <param name="targetSolution">The solution being added to.</param>
+    /// <param name="toAdd">The solution being added to <paramref cref="targetSolution"/></param>
+    /// <param name="overflowThreshold">The combined volume above which the overflow will be returned.
+    /// If the combined volume is below this an empty solution is returned.</param>
+    /// <param name="overflowingSolution">Solution that exceeded overflowThreshold</param>
+    /// <returns>Whether any reagents were added to <paramref cref="targetSolution"/>.</returns>
+    public bool TryMixAndOverflow(Entity<SolutionComponent> soln, Solution toAdd, FixedPoint2 overflowThreshold, [MaybeNullWhen(false)] out Solution overflowingSolution)
+    {
+        var (uid, comp) = soln;
+        var solution = comp.Solution;
+
+        if (toAdd.Volume == 0 || overflowThreshold > solution.MaxVolume)
+        {
+            overflowingSolution = null;
+            return false;
+        }
+
+        solution.AddSolution(toAdd, PrototypeManager);
+        overflowingSolution = solution.SplitSolution(FixedPoint2.Max(FixedPoint2.Zero, solution.Volume - overflowThreshold));
+        UpdateChemicals(soln);
+        return true;
+    }
+
+    /// <summary>
+    ///     Removes an amount from all reagents in a solution, adding it to a new solution.
+    /// </summary>
+    /// <param name="uid">The entity containing the solution.</param>
+    /// <param name="solution">The solution to remove reagents from.</param>
+    /// <param name="quantity">The amount to remove from every reagent in the solution.</param>
+    /// <returns>A new solution containing every removed reagent from the original solution.</returns>
+    public Solution RemoveEachReagent(Entity<SolutionComponent> soln, FixedPoint2 quantity)
+    {
+        var (uid, comp) = soln;
+        var solution = comp.Solution;
+
+        if (quantity <= 0)
+            return new Solution();
+
+        var removedSolution = new Solution();
+
+        // RemoveReagent does a RemoveSwap, meaning we don't have to copy the list if we iterate it backwards.
+        for (var i = solution.Contents.Count - 1; i >= 0; i--)
+        {
+            var (reagent, _) = solution.Contents[i];
+            var removedQuantity = solution.RemoveReagent(reagent, quantity);
+            removedSolution.AddReagent(reagent, removedQuantity);
+        }
+
+        UpdateChemicals(soln);
+        return removedSolution;
+    }
+
+    // Thermal energy and temperature management.
+
+    #region Thermal Energy and Temperature
+
+    /// <summary>
+    ///     Sets the temperature of a solution to a new value and then checks for reaction processing.
+    /// </summary>
+    /// <param name="owner">The entity in which the solution is located.</param>
+    /// <param name="solution">The solution to set the temperature of.</param>
+    /// <param name="temperature">The new value to set the temperature to.</param>
+    public void SetTemperature(Entity<SolutionComponent> soln, float temperature)
+    {
+        var (_, comp) = soln;
+        var solution = comp.Solution;
+
+        if (temperature == solution.Temperature)
+            return;
+
+        solution.Temperature = temperature;
+        UpdateChemicals(soln);
+    }
+
+    /// <summary>
+    ///     Sets the thermal energy of a solution to a new value and then checks for reaction processing.
+    /// </summary>
+    /// <param name="owner">The entity in which the solution is located.</param>
+    /// <param name="solution">The solution to set the thermal energy of.</param>
+    /// <param name="thermalEnergy">The new value to set the thermal energy to.</param>
+    public void SetThermalEnergy(Entity<SolutionComponent> soln, float thermalEnergy)
+    {
+        var (_, comp) = soln;
+        var solution = comp.Solution;
+
+        var heatCap = solution.GetHeatCapacity(PrototypeManager);
+        solution.Temperature = heatCap == 0 ? 0 : thermalEnergy / heatCap;
+        UpdateChemicals(soln);
+    }
+
+    /// <summary>
+    ///     Adds some thermal energy to a solution and then checks for reaction processing.
+    /// </summary>
+    /// <param name="owner">The entity in which the solution is located.</param>
+    /// <param name="solution">The solution to set the thermal energy of.</param>
+    /// <param name="thermalEnergy">The new value to set the thermal energy to.</param>
+    public void AddThermalEnergy(Entity<SolutionComponent> soln, float thermalEnergy)
+    {
+        var (_, comp) = soln;
+        var solution = comp.Solution;
+
+        if (thermalEnergy == 0.0f)
+            return;
+
+        var heatCap = solution.GetHeatCapacity(PrototypeManager);
+        solution.Temperature += heatCap == 0 ? 0 : thermalEnergy / heatCap;
+        UpdateChemicals(soln);
+    }
+
+    #endregion Thermal Energy and Temperature
+
+    #region Event Handlers
+
+    private void OnComponentInit(Entity<SolutionComponent> entity, ref ComponentInit args)
+    {
+        entity.Comp.Solution.ValidateSolution();
+    }
+
+    private void OnComponentStartup(Entity<SolutionComponent> entity, ref ComponentStartup args)
+    {
+        UpdateChemicals(entity);
+    }
+
+    private void OnComponentShutdown(Entity<SolutionComponent> entity, ref ComponentShutdown args)
+    {
+        RemoveAllSolution(entity);
+    }
+
+    private void OnComponentInit(Entity<SolutionContainerManagerComponent> entity, ref ComponentInit args)
+    {
+        if (entity.Comp.Containers is not { Count: > 0 } containers)
+            return;
+
+        var containerManager = EnsureComp<ContainerManagerComponent>(entity);
+        foreach (var name in containers)
+        {
+            // The actual solution entity should be directly held within the corresponding slot.
+            ContainerSystem.EnsureContainer<ContainerSlot>(entity.Owner, $"solution@{name}", containerManager);
+        }
+    }
+
+    private void OnExamineSolution(Entity<ExaminableSolutionComponent> entity, ref ExaminedEvent args)
+    {
+        if (!TryGetSolution(entity.Owner, entity.Comp.Solution, out _, out var solution))
+        {
+            return;
+        }
+
+        var primaryReagent = solution.GetPrimaryReagentId();
+
+        if (string.IsNullOrEmpty(primaryReagent?.Prototype))
+        {
+            args.PushText(Loc.GetString("shared-solution-container-component-on-examine-empty-container"));
+            return;
+        }
+
+        if (!PrototypeManager.TryIndex(primaryReagent.Value.Prototype, out ReagentPrototype? primary))
+        {
+            Log.Error($"{nameof(Solution)} could not find the prototype associated with {primaryReagent}.");
+            return;
+        }
+
+        var colorHex = solution.GetColor(PrototypeManager)
+            .ToHexNoAlpha(); //TODO: If the chem has a dark color, the examine text becomes black on a black background, which is unreadable.
+        var messageString = "shared-solution-container-component-on-examine-main-text";
+
+        args.PushMarkup(Loc.GetString(messageString,
+            ("color", colorHex),
+            ("wordedAmount", Loc.GetString(solution.Contents.Count == 1
+                ? "shared-solution-container-component-on-examine-worded-amount-one-reagent"
+                : "shared-solution-container-component-on-examine-worded-amount-multiple-reagents")),
+            ("desc", primary.LocalizedPhysicalDescription)));
+
+        var reagentPrototypes = solution.GetReagentPrototypes(PrototypeManager);
+
+        // Sort the reagents by amount, descending then alphabetically
+        var sortedReagentPrototypes = reagentPrototypes
+            .OrderByDescending(pair => pair.Value.Value)
+            .ThenBy(pair => pair.Key.LocalizedName);
+
+        // Add descriptions of immediately recognizable reagents, like water or beer
+        var recognized = new List<ReagentPrototype>();
+        foreach (var keyValuePair in sortedReagentPrototypes)
+        {
+            var proto = keyValuePair.Key;
+            if (!proto.Recognizable)
+            {
+                continue;
+            }
+
+            recognized.Add(proto);
+        }
+
+        // Skip if there's nothing recognizable
+        if (recognized.Count == 0)
+            return;
+
+        var msg = new StringBuilder();
+        foreach (var reagent in recognized)
+        {
+            string part;
+            if (reagent == recognized[0])
+            {
+                part = "examinable-solution-recognized-first";
+            }
+            else if (reagent == recognized[^1])
+            {
+                // this loc specifically  requires space to be appended, fluent doesnt support whitespace
+                msg.Append(' ');
+                part = "examinable-solution-recognized-last";
+            }
+            else
+            {
+                part = "examinable-solution-recognized-next";
+            }
+
+            msg.Append(Loc.GetString(part, ("color", reagent.SubstanceColor.ToHexNoAlpha()),
+                ("chemical", reagent.LocalizedName)));
+        }
+
+        args.PushMarkup(Loc.GetString("examinable-solution-has-recognizable-chemicals", ("recognizedString", msg.ToString())));
+    }
+
+    private void OnSolutionExaminableVerb(Entity<ExaminableSolutionComponent> entity, ref GetVerbsEvent<ExamineVerb> args)
+    {
+        if (!args.CanInteract || !args.CanAccess)
+            return;
+
+        var scanEvent = new SolutionScanEvent();
+        RaiseLocalEvent(args.User, scanEvent);
+        if (!scanEvent.CanScan)
+        {
+            return;
+        }
+
+        if (!TryGetSolution(args.Target, entity.Comp.Solution, out _, out var solutionHolder))
+        {
+            return;
+        }
+
+        var target = args.Target;
+        var user = args.User;
+        var verb = new ExamineVerb()
+        {
+            Act = () =>
+            {
+                var markup = GetSolutionExamine(solutionHolder);
+                ExamineSystem.SendExamineTooltip(user, target, markup, false, false);
+            },
+            Text = Loc.GetString("scannable-solution-verb-text"),
+            Message = Loc.GetString("scannable-solution-verb-message"),
+            Category = VerbCategory.Examine,
+            Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/drink.svg.192dpi.png")),
+        };
+
+        args.Verbs.Add(verb);
+    }
+
+    private FormattedMessage GetSolutionExamine(Solution solution)
+    {
+        var msg = new FormattedMessage();
+
+        if (solution.Volume == 0)
+        {
+            msg.AddMarkup(Loc.GetString("scannable-solution-empty-container"));
+            return msg;
+        }
+
+        msg.AddMarkup(Loc.GetString("scannable-solution-main-text"));
+
+        var reagentPrototypes = solution.GetReagentPrototypes(PrototypeManager);
+
+        // Sort the reagents by amount, descending then alphabetically
+        var sortedReagentPrototypes = reagentPrototypes
+            .OrderByDescending(pair => pair.Value.Value)
+            .ThenBy(pair => pair.Key.LocalizedName);
+
+        foreach (var (proto, quantity) in sortedReagentPrototypes)
+        {
+            msg.PushNewline();
+            msg.AddMarkup(Loc.GetString("scannable-solution-chemical"
+                , ("type", proto.LocalizedName)
+                , ("color", proto.SubstanceColor.ToHexNoAlpha())
+                , ("amount", quantity)));
+        }
+
+        return msg;
+    }
+
+    #endregion Event Handlers
+}
diff --git a/Content.Shared/Chemistry/EntitySystems/SolutionContainerSystem.Capabilities.cs b/Content.Shared/Chemistry/EntitySystems/SolutionContainerSystem.Capabilities.cs
deleted file mode 100644 (file)
index 3647ddb..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-using System.Diagnostics.CodeAnalysis;
-using System.Text;
-using Content.Shared.Chemistry.Components;
-using Content.Shared.Chemistry.Components.SolutionManager;
-using Content.Shared.FixedPoint;
-
-namespace Content.Shared.Chemistry.EntitySystems;
-
-public sealed partial class SolutionContainerSystem
-{
-    public void Refill(EntityUid targetUid, Solution targetSolution, Solution addedSolution,
-        RefillableSolutionComponent? refillableSolution = null)
-    {
-        if (!Resolve(targetUid, ref refillableSolution, false))
-            return;
-
-        TryAddSolution(targetUid, targetSolution, addedSolution);
-    }
-
-    public void Inject(EntityUid targetUid, Solution targetSolution, Solution addedSolution,
-        InjectableSolutionComponent? injectableSolution = null)
-    {
-        if (!Resolve(targetUid, ref injectableSolution, false))
-            return;
-
-        TryAddSolution(targetUid, targetSolution, addedSolution);
-    }
-
-    public Solution Draw(EntityUid targetUid, Solution solution, FixedPoint2 amount,
-        DrawableSolutionComponent? drawableSolution = null)
-    {
-        if (!Resolve(targetUid, ref drawableSolution, false))
-            return new Solution();
-
-        return SplitSolution(targetUid, solution, amount);
-    }
-
-    public Solution Drain(EntityUid targetUid, Solution targetSolution, FixedPoint2 amount,
-        DrainableSolutionComponent? drainableSolution = null)
-    {
-        if (!Resolve(targetUid, ref drainableSolution, false))
-            return new Solution();
-
-        return SplitSolution(targetUid, targetSolution, amount);
-    }
-
-    public bool TryGetInjectableSolution(EntityUid targetUid,
-        [NotNullWhen(true)] out Solution? solution,
-        InjectableSolutionComponent? injectable = null,
-        SolutionContainerManagerComponent? manager = null
-    )
-    {
-        if (!Resolve(targetUid, ref manager, ref injectable, false)
-            || !manager.Solutions.TryGetValue(injectable.Solution, out solution))
-        {
-            solution = null;
-            return false;
-        }
-
-        return true;
-    }
-
-    public bool TryGetRefillableSolution(EntityUid targetUid,
-        [NotNullWhen(true)] out Solution? solution,
-        SolutionContainerManagerComponent? solutionManager = null,
-        RefillableSolutionComponent? refillable = null)
-    {
-        if (!Resolve(targetUid, ref solutionManager, ref refillable, false)
-            || !solutionManager.Solutions.TryGetValue(refillable.Solution, out var refillableSolution))
-        {
-            solution = null;
-            return false;
-        }
-
-        solution = refillableSolution;
-        return true;
-    }
-
-    public bool TryGetDrainableSolution(EntityUid uid,
-        [NotNullWhen(true)] out Solution? solution,
-        DrainableSolutionComponent? drainable = null,
-        SolutionContainerManagerComponent? manager = null)
-    {
-        if (!Resolve(uid, ref drainable, ref manager, false)
-            || !manager.Solutions.TryGetValue(drainable.Solution, out solution))
-        {
-            solution = null;
-            return false;
-        }
-
-        return true;
-    }
-
-    public bool TryGetDumpableSolution(EntityUid uid,
-        [NotNullWhen(true)] out Solution? solution,
-        DumpableSolutionComponent? dumpable = null,
-        SolutionContainerManagerComponent? manager = null)
-    {
-        if (!Resolve(uid, ref dumpable, ref manager, false)
-            || !manager.Solutions.TryGetValue(dumpable.Solution, out solution))
-        {
-            solution = null;
-            return false;
-        }
-
-        return true;
-    }
-
-    public bool TryGetDrawableSolution(EntityUid uid,
-        [NotNullWhen(true)] out Solution? solution,
-        DrawableSolutionComponent? drawable = null,
-        SolutionContainerManagerComponent? manager = null)
-    {
-        if (!Resolve(uid, ref drawable, ref manager, false)
-            || !manager.Solutions.TryGetValue(drawable.Solution, out solution))
-        {
-            solution = null;
-            return false;
-        }
-
-        return true;
-    }
-
-    public FixedPoint2 DrainAvailable(EntityUid uid)
-    {
-        return !TryGetDrainableSolution(uid, out var solution)
-            ? FixedPoint2.Zero
-            : solution.Volume;
-    }
-
-    public float PercentFull(EntityUid uid)
-    {
-        if (!TryGetDrainableSolution(uid, out var solution) || solution.MaxVolume.Equals(FixedPoint2.Zero))
-            return 0;
-
-        return solution.FillFraction * 100;
-    }
-
-    public bool TryGetFitsInDispenser(EntityUid owner,
-        [NotNullWhen(true)] out Solution? solution,
-        FitsInDispenserComponent? dispenserFits = null,
-        SolutionContainerManagerComponent? solutionManager = null)
-    {
-        if (!Resolve(owner, ref dispenserFits, ref solutionManager, false)
-            || !solutionManager.Solutions.TryGetValue(dispenserFits.Solution, out solution))
-        {
-            solution = null;
-            return false;
-        }
-
-        return true;
-    }
-
-    public static string ToPrettyString(Solution solution)
-    {
-        var sb = new StringBuilder();
-        if (solution.Name == null)
-            sb.Append("[");
-        else
-            sb.Append($"{solution.Name}:[");
-        var first = true;
-        foreach (var (id, quantity) in solution.Contents)
-        {
-            if (first)
-            {
-                first = false;
-            }
-            else
-            {
-                sb.Append(", ");
-            }
-
-            sb.AppendFormat("{0}: {1}u", id, quantity);
-        }
-
-        sb.Append(']');
-        return sb.ToString();
-    }
-}
diff --git a/Content.Shared/Chemistry/EntitySystems/SolutionContainerSystem.cs b/Content.Shared/Chemistry/EntitySystems/SolutionContainerSystem.cs
deleted file mode 100644 (file)
index f8fcbf9..0000000
+++ /dev/null
@@ -1,871 +0,0 @@
-using System.Diagnostics.CodeAnalysis;
-using System.Linq;
-using System.Text;
-using Content.Shared.Chemistry.Components;
-using Content.Shared.Chemistry.Components.SolutionManager;
-using Content.Shared.Chemistry.Reaction;
-using Content.Shared.Chemistry.Reagent;
-using Content.Shared.Examine;
-using Content.Shared.FixedPoint;
-using Content.Shared.Verbs;
-using JetBrains.Annotations;
-using Robust.Shared.Audio;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Utility;
-
-namespace Content.Shared.Chemistry.EntitySystems;
-
-/// <summary>
-/// This event alerts system that the solution was changed
-/// </summary>
-public sealed class SolutionChangedEvent : EntityEventArgs
-{
-    public readonly Solution Solution;
-    public readonly string SolutionId;
-
-    public SolutionChangedEvent(Solution solution, string solutionId)
-    {
-        SolutionId = solutionId;
-        Solution = solution;
-    }
-}
-
-/// <summary>
-/// An event raised when more reagents are added to a (managed) solution than it can hold.
-/// </summary>
-[ByRefEvent]
-public record struct SolutionOverflowEvent(EntityUid SolutionEnt, Solution SolutionHolder, Solution Overflow)
-{
-    /// <summary>The entity which contains the solution that has overflowed.</summary>
-    public readonly EntityUid SolutionEnt = SolutionEnt;
-    /// <summary>The solution that has overflowed.</summary>
-    public readonly Solution SolutionHolder = SolutionHolder;
-    /// <summary>The reagents that have overflowed the solution.</summary>
-    public readonly Solution Overflow = Overflow;
-    /// <summary>The volume by which the solution has overflowed.</summary>
-    public readonly FixedPoint2 OverflowVol = Overflow.Volume;
-    /// <summary>Whether some subscriber has taken care of the effects of the overflow.</summary>
-    public bool Handled = false;
-}
-
-/// <summary>
-/// Part of Chemistry system deal with SolutionContainers
-/// </summary>
-[UsedImplicitly]
-public sealed partial class SolutionContainerSystem : EntitySystem
-{
-    [Dependency] private readonly ChemicalReactionSystem _chemistrySystem = default!;
-
-    [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
-
-    [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
-    [Dependency] private readonly ExamineSystemShared _examine = default!;
-
-    public override void Initialize()
-    {
-        base.Initialize();
-
-        SubscribeLocalEvent<SolutionContainerManagerComponent, ComponentInit>(InitSolution);
-        SubscribeLocalEvent<ExaminableSolutionComponent, ExaminedEvent>(OnExamineSolution);
-        SubscribeLocalEvent<ExaminableSolutionComponent, GetVerbsEvent<ExamineVerb>>(OnSolutionExaminableVerb);
-    }
-
-    private void InitSolution(EntityUid uid, SolutionContainerManagerComponent component, ComponentInit args)
-    {
-        foreach (var (name, solutionHolder) in component.Solutions)
-        {
-            solutionHolder.Name = name;
-            solutionHolder.ValidateSolution();
-            UpdateAppearance(uid, solutionHolder);
-        }
-    }
-
-    private void OnSolutionExaminableVerb(EntityUid uid, ExaminableSolutionComponent component, GetVerbsEvent<ExamineVerb> args)
-    {
-        if (!args.CanInteract || !args.CanAccess)
-            return;
-
-        var scanEvent = new SolutionScanEvent();
-        RaiseLocalEvent(args.User, scanEvent);
-        if (!scanEvent.CanScan)
-        {
-            return;
-        }
-
-        SolutionContainerManagerComponent? solutionsManager = null;
-        if (!Resolve(args.Target, ref solutionsManager)
-            || !solutionsManager.Solutions.TryGetValue(component.Solution, out var solutionHolder))
-        {
-            return;
-        }
-
-        var verb = new ExamineVerb()
-        {
-            Act = () =>
-            {
-                var markup = GetSolutionExamine(solutionHolder);
-                _examine.SendExamineTooltip(args.User, uid, markup, false, false);
-            },
-            Text = Loc.GetString("scannable-solution-verb-text"),
-            Message = Loc.GetString("scannable-solution-verb-message"),
-            Category = VerbCategory.Examine,
-            Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/drink.svg.192dpi.png")),
-        };
-
-        args.Verbs.Add(verb);
-    }
-
-    private FormattedMessage GetSolutionExamine(Solution solution)
-    {
-        var msg = new FormattedMessage();
-
-        if (solution.Volume == 0)
-        {
-            msg.AddMarkup(Loc.GetString("scannable-solution-empty-container"));
-            return msg;
-        }
-
-        msg.AddMarkup(Loc.GetString("scannable-solution-main-text"));
-
-        foreach (var (proto, quantity) in solution.GetReagentPrototypes(_prototypeManager))
-        {
-            msg.PushNewline();
-            msg.AddMarkup(Loc.GetString("scannable-solution-chemical"
-                , ("type", proto.LocalizedName)
-                , ("color", proto.SubstanceColor.ToHexNoAlpha())
-                , ("amount", quantity)));
-        }
-
-        return msg;
-    }
-
-    private void OnExamineSolution(EntityUid uid, ExaminableSolutionComponent examinableComponent,
-        ExaminedEvent args)
-    {
-        SolutionContainerManagerComponent? solutionsManager = null;
-        if (!Resolve(args.Examined, ref solutionsManager)
-            || !solutionsManager.Solutions.TryGetValue(examinableComponent.Solution, out var solution))
-        {
-            return;
-        }
-
-        var primaryReagent = solution.GetPrimaryReagentId();
-
-        if (string.IsNullOrEmpty(primaryReagent?.Prototype))
-        {
-            args.PushText(Loc.GetString("shared-solution-container-component-on-examine-empty-container"));
-            return;
-        }
-
-        if (!_prototypeManager.TryIndex(primaryReagent.Value.Prototype, out ReagentPrototype? primary))
-        {
-            Log.Error($"{nameof(Solution)} could not find the prototype associated with {primaryReagent}.");
-            return;
-        }
-
-        var colorHex = solution.GetColor(_prototypeManager)
-            .ToHexNoAlpha(); //TODO: If the chem has a dark color, the examine text becomes black on a black background, which is unreadable.
-        var messageString = "shared-solution-container-component-on-examine-main-text";
-
-        args.PushMarkup(Loc.GetString(messageString,
-            ("color", colorHex),
-            ("wordedAmount", Loc.GetString(solution.Contents.Count == 1
-                ? "shared-solution-container-component-on-examine-worded-amount-one-reagent"
-                : "shared-solution-container-component-on-examine-worded-amount-multiple-reagents")),
-            ("desc", primary.LocalizedPhysicalDescription)));
-
-
-        var reagentPrototypes = solution.GetReagentPrototypes(_prototypeManager);
-
-        // Sort the reagents by amount, descending then alphabetically
-        var sortedReagentPrototypes = reagentPrototypes
-            .OrderByDescending(pair => pair.Value.Value)
-            .ThenBy(pair => pair.Key.LocalizedName);
-
-        // Add descriptions of immediately recognizable reagents, like water or beer
-        var recognized = new List<ReagentPrototype>();
-        foreach (var keyValuePair in sortedReagentPrototypes)
-        {
-            var proto = keyValuePair.Key;
-            if (!proto.Recognizable)
-            {
-                continue;
-            }
-
-            recognized.Add(proto);
-        }
-
-        // Skip if there's nothing recognizable
-        if (recognized.Count == 0)
-            return;
-
-        var msg = new StringBuilder();
-        foreach (var reagent in recognized)
-        {
-            string part;
-            if (reagent == recognized[0])
-            {
-                part = "examinable-solution-recognized-first";
-            }
-            else if (reagent == recognized[^1])
-            {
-                // this loc specifically  requires space to be appended, fluent doesnt support whitespace
-                msg.Append(' ');
-                part = "examinable-solution-recognized-last";
-            }
-            else
-            {
-                part = "examinable-solution-recognized-next";
-            }
-
-            msg.Append(Loc.GetString(part, ("color", reagent.SubstanceColor.ToHexNoAlpha()),
-                ("chemical", reagent.LocalizedName)));
-        }
-
-        args.PushMarkup(Loc.GetString("examinable-solution-has-recognizable-chemicals", ("recognizedString", msg.ToString())));
-    }
-
-    public void UpdateAppearance(EntityUid uid, Solution solution,
-        AppearanceComponent? appearanceComponent = null)
-    {
-        if (!HasComp<SolutionContainerVisualsComponent>(uid) || !Resolve(uid, ref appearanceComponent, false))
-            return;
-
-        _appearance.SetData(uid, SolutionContainerVisuals.FillFraction, solution.FillFraction, appearanceComponent);
-        _appearance.SetData(uid, SolutionContainerVisuals.Color, solution.GetColor(_prototypeManager), appearanceComponent);
-        if (solution.Name != null)
-        {
-            _appearance.SetData(uid, SolutionContainerVisuals.SolutionName, solution.Name, appearanceComponent);
-        }
-
-        if (solution.GetPrimaryReagentId() is { } reagent)
-        {
-            _appearance.SetData(uid, SolutionContainerVisuals.BaseOverride, reagent.ToString(), appearanceComponent);
-        }
-        else
-        {
-            _appearance.SetData(uid, SolutionContainerVisuals.BaseOverride, string.Empty, appearanceComponent);
-        }
-    }
-
-    /// <summary>
-    ///     Removes part of the solution in the container.
-    /// </summary>
-    /// <param name="targetUid"></param>
-    /// <param name="solutionHolder"></param>
-    /// <param name="quantity">the volume of solution to remove.</param>
-    /// <returns>The solution that was removed.</returns>
-    public Solution SplitSolution(EntityUid targetUid, Solution solutionHolder, FixedPoint2 quantity)
-    {
-        var splitSol = solutionHolder.SplitSolution(quantity);
-        UpdateChemicals(targetUid, solutionHolder);
-        return splitSol;
-    }
-
-    public Solution SplitStackSolution(EntityUid targetUid, Solution solutionHolder, FixedPoint2 quantity, int stackCount)
-    {
-        var splitSol = solutionHolder.SplitSolution(quantity / stackCount);
-        solutionHolder.SplitSolution(quantity - splitSol.Volume);
-        UpdateChemicals(targetUid, solutionHolder);
-        return splitSol;
-    }
-
-    /// <summary>
-    /// Splits a solution without the specified reagent(s).
-    /// </summary>
-    public Solution SplitSolutionWithout(EntityUid targetUid, Solution solutionHolder, FixedPoint2 quantity,
-        params string[] reagents)
-    {
-        var splitSol = solutionHolder.SplitSolutionWithout(quantity, reagents);
-        UpdateChemicals(targetUid, solutionHolder);
-        return splitSol;
-    }
-
-    public void UpdateChemicals(EntityUid uid, Solution solutionHolder, bool needsReactionsProcessing = false, ReactionMixerComponent? mixerComponent = null)
-    {
-        DebugTools.Assert(solutionHolder.Name != null && TryGetSolution(uid, solutionHolder.Name, out var tmp) && tmp == solutionHolder);
-
-        // Process reactions
-        if (needsReactionsProcessing && solutionHolder.CanReact)
-        {
-            _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));
-    }
-
-    public void RemoveAllSolution(EntityUid uid, Solution solutionHolder)
-    {
-        if (solutionHolder.Volume == 0)
-            return;
-
-        solutionHolder.RemoveAllSolution();
-        UpdateChemicals(uid, solutionHolder);
-    }
-
-    public void RemoveAllSolution(EntityUid uid, SolutionContainerManagerComponent? solutionContainerManager = null)
-    {
-        if (!Resolve(uid, ref solutionContainerManager))
-            return;
-
-        foreach (var solution in solutionContainerManager.Solutions.Values)
-        {
-            RemoveAllSolution(uid, solution);
-        }
-    }
-
-    /// <summary>
-    ///     Sets the capacity (maximum volume) of a solution to a new value.
-    /// </summary>
-    /// <param name="targetUid">The entity containing the solution.</param>
-    /// <param name="targetSolution">The solution to set the capacity of.</param>
-    /// <param name="capacity">The value to set the capacity of the solution to.</param>
-    public void SetCapacity(EntityUid targetUid, Solution targetSolution, FixedPoint2 capacity)
-    {
-        if (targetSolution.MaxVolume == capacity)
-            return;
-
-        targetSolution.MaxVolume = capacity;
-        if (capacity < targetSolution.Volume)
-            targetSolution.RemoveSolution(targetSolution.Volume - capacity);
-
-        UpdateChemicals(targetUid, targetSolution);
-    }
-
-    /// <summary>
-    ///     Adds reagent of an Id to the container.
-    /// </summary>
-    /// <param name="targetUid"></param>
-    /// <param name="targetSolution">Container to which we are adding reagent</param>
-    /// <param name="reagentQuantity">The reagent to add.</param>
-    /// <param name="acceptedQuantity">The amount of reagent successfully added.</param>
-    /// <returns>If all the reagent could be added.</returns>
-    public bool TryAddReagent(EntityUid targetUid, Solution targetSolution, ReagentQuantity reagentQuantity,
-        out FixedPoint2 acceptedQuantity, float? temperature = null)
-    {
-        acceptedQuantity = targetSolution.AvailableVolume > reagentQuantity.Quantity
-            ? reagentQuantity.Quantity
-            : targetSolution.AvailableVolume;
-
-        if (acceptedQuantity <= 0)
-            return reagentQuantity.Quantity == 0;
-
-        if (temperature == null)
-        {
-            targetSolution.AddReagent(reagentQuantity.Reagent, acceptedQuantity);
-        }
-        else
-        {
-            var proto = _prototypeManager.Index<ReagentPrototype>(reagentQuantity.Reagent.Prototype);
-            targetSolution.AddReagent(proto, acceptedQuantity, temperature.Value, _prototypeManager);
-        }
-
-        UpdateChemicals(targetUid, targetSolution, true);
-        return acceptedQuantity == reagentQuantity.Quantity;
-    }
-
-    /// <summary>
-    ///     Adds reagent of an Id to the container.
-    /// </summary>
-    /// <param name="targetUid"></param>
-    /// <param name="targetSolution">Container to which we are adding reagent</param>
-    /// <param name="prototype">The Id of the reagent to add.</param>
-    /// <param name="quantity">The amount of reagent to add.</param>
-    /// <returns>If all the reagent could be added.</returns>
-    [PublicAPI]
-    public bool TryAddReagent(EntityUid targetUid, Solution targetSolution, string prototype, FixedPoint2 quantity,
-        float? temperature = null, ReagentData? data = null)
-    {
-        var reagent = new ReagentQuantity(prototype, quantity, data);
-        return TryAddReagent(targetUid, targetSolution, reagent, out _, temperature);
-    }
-
-    /// <summary>
-    ///     Adds reagent of an Id to the container.
-    /// </summary>
-    /// <param name="targetUid"></param>
-    /// <param name="targetSolution">Container to which we are adding reagent</param>
-    /// <param name="prototype">The Id of the reagent to add.</param>
-    /// <param name="quantity">The amount of reagent to add.</param>
-    /// <param name="acceptedQuantity">The amount of reagent successfully added.</param>
-    /// <returns>If all the reagent could be added.</returns>
-    public bool TryAddReagent(EntityUid targetUid, Solution targetSolution, string prototype, FixedPoint2 quantity,
-        out FixedPoint2 acceptedQuantity, float? temperature = null, ReagentData? data = null)
-    {
-        var reagent = new ReagentQuantity(prototype, quantity, data);
-        return TryAddReagent(targetUid, targetSolution, reagent, out acceptedQuantity, temperature);
-    }
-
-    /// <summary>
-    ///     Adds reagent of an Id to the container.
-    /// </summary>
-    /// <param name="targetUid"></param>
-    /// <param name="targetSolution">Container to which we are adding reagent</param>
-    /// <param name="reagentId">The reagent to add.</param>
-    /// <param name="quantity">The amount of reagent to add.</param>
-    /// <param name="acceptedQuantity">The amount of reagent successfully added.</param>
-    /// <returns>If all the reagent could be added.</returns>
-    public bool TryAddReagent(EntityUid targetUid, Solution targetSolution, ReagentId reagentId, FixedPoint2 quantity,
-        out FixedPoint2 acceptedQuantity, float? temperature = null)
-    {
-        var quant = new ReagentQuantity(reagentId, quantity);
-        return TryAddReagent(targetUid, targetSolution, quant, out acceptedQuantity, temperature);
-    }
-
-    /// <summary>
-    ///     Removes reagent from a container.
-    /// </summary>
-    /// <param name="targetUid"></param>
-    /// <param name="container">Solution container from which we are removing reagent</param>
-    /// <param name="reagentQuantity">The reagent to remove.</param>
-    /// <returns>If the reagent to remove was found in the container.</returns>
-    public bool RemoveReagent(EntityUid targetUid, Solution? container, ReagentQuantity reagentQuantity)
-    {
-        if (container == null)
-            return false;
-
-        var quant = container.RemoveReagent(reagentQuantity);
-        if (quant <= FixedPoint2.Zero)
-            return false;
-
-        UpdateChemicals(targetUid, container);
-        return true;
-    }
-
-    /// <summary>
-    ///     Removes reagent from a container.
-    /// </summary>
-    /// <param name="targetUid"></param>
-    /// <param name="container">Solution container from which we are removing reagent</param>
-    /// <param name="prototype">The Id of the reagent to remove.</param>
-    /// <param name="quantity">The amount of reagent to remove.</param>
-    /// <returns>If the reagent to remove was found in the container.</returns>
-    public bool RemoveReagent(EntityUid targetUid, Solution? container, string prototype, FixedPoint2 quantity, ReagentData? data = null)
-    {
-        return RemoveReagent(targetUid, container, new ReagentQuantity(prototype, quantity, data));
-    }
-
-    /// <summary>
-    ///     Removes reagent from a container.
-    /// </summary>
-    /// <param name="targetUid"></param>
-    /// <param name="container">Solution container from which we are removing reagent</param>
-    /// <param name="reagentId">The reagent to remove.</param>
-    /// <param name="quantity">The amount of reagent to remove.</param>
-    /// <returns>If the reagent to remove was found in the container.</returns>
-    public bool RemoveReagent(EntityUid targetUid, Solution? container, ReagentId reagentId, FixedPoint2 quantity)
-    {
-        return RemoveReagent(targetUid, container, new ReagentQuantity(reagentId, quantity));
-    }
-
-    /// <summary>
-    ///     Moves some quantity of a solution from one solution to another.
-    /// </summary>
-    /// <param name="sourceUid">entity holding the source solution</param>
-    /// <param name="targetUid">entity holding the target solution</param>
-    /// <param name="source">source solution</param>
-    /// <param name="target">target solution</param>
-    /// <param name="quantity">quantity of solution to move from source to target. If this is a negative number, the source & target roles are reversed.</param>
-    public bool TryTransferSolution(EntityUid sourceUid, EntityUid targetUid, Solution source, Solution target, FixedPoint2 quantity)
-    {
-        if (!TryTransferSolution(targetUid, target, source, quantity))
-            return false;
-
-        UpdateChemicals(sourceUid, source, false);
-        return true;
-    }
-
-    /// <summary>
-    ///     Moves some quantity of a solution from one solution to another.
-    /// </summary>
-    /// <param name="sourceUid">entity holding the source solution</param>
-    /// <param name="targetUid">entity holding the target solution</param>
-    /// <param name="source">source solution</param>
-    /// <param name="target">target solution</param>
-    /// <param name="quantity">quantity of solution to move from source to target. If this is a negative number, the source & target roles are reversed.</param>
-    public bool TryTransferSolution(EntityUid targetUid, Solution target, Solution source, FixedPoint2 quantity)
-    {
-        if (quantity < 0)
-            throw new InvalidOperationException("Quantity must be positive");
-
-        quantity = FixedPoint2.Min(quantity, target.AvailableVolume, source.Volume);
-        if (quantity == 0)
-            return false;
-
-        // TODO This should be made into a function that directly transfers reagents.
-        // Currently this is quite inefficient.
-        target.AddSolution(source.SplitSolution(quantity), _prototypeManager);
-
-        UpdateChemicals(targetUid, target, true);
-        return true;
-    }
-
-    /// <summary>
-    ///     Moves some quantity of a solution from one solution to another.
-    /// </summary>
-    /// <param name="sourceUid">entity holding the source solution</param>
-    /// <param name="targetUid">entity holding the target solution</param>
-    /// <param name="source">source solution</param>
-    /// <param name="target">target solution</param>
-    /// <param name="quantity">quantity of solution to move from source to target. If this is a negative number, the source & target roles are reversed.</param>
-    public bool TryTransferSolution(EntityUid sourceUid, EntityUid targetUid, string source, string target, FixedPoint2 quantity)
-    {
-        if (!TryGetSolution(sourceUid, source, out var sourceSoln))
-            return false;
-
-        if (!TryGetSolution(targetUid, target, out var targetSoln))
-            return false;
-
-        return TryTransferSolution(sourceUid, targetUid, sourceSoln, targetSoln, quantity);
-    }
-
-    /// <summary>
-    ///     Adds a solution to the container, if it can fully fit.
-    /// </summary>
-    /// <param name="targetUid">entity holding targetSolution</param>
-    ///  <param name="targetSolution">entity holding targetSolution</param>
-    /// <param name="toAdd">solution being added</param>
-    /// <returns>If the solution could be added.</returns>
-    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;
-    }
-
-    /// <summary>
-    ///     Adds as much of a solution to a container as can fit.
-    /// </summary>
-    /// <param name="targetUid">The entity containing <paramref cref="targetSolution"/></param>
-    /// <param name="targetSolution">The solution being added to.</param>
-    /// <param name="toAdd">The solution being added to <paramref cref="targetSolution"/></param>
-    /// <returns>The quantity of the solution actually added.</returns>
-    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;
-    }
-
-    /// <summary>
-    ///     Adds a solution to a container and updates the container.
-    /// </summary>
-    /// <param name="targetUid">The entity containing <paramref cref="targetSolution"/></param>
-    /// <param name="targetSolution">The solution being added to.</param>
-    /// <param name="toAdd">The solution being added to <paramref cref="targetSolution"/></param>
-    /// <returns>Whether any reagents were added to the solution.</returns>
-    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;
-    }
-
-    /// <summary>
-    ///     Adds a solution to the container, removing the overflow.
-    ///     Unlike <see cref="TryAddSolution"/> it will ignore size limits.
-    /// </summary>
-    /// <param name="targetUid">The entity containing <paramref cref="targetSolution"/></param>
-    /// <param name="targetSolution">The solution being added to.</param>
-    /// <param name="toAdd">The solution being added to <paramref cref="targetSolution"/></param>
-    /// <param name="overflowThreshold">The combined volume above which the overflow will be returned.
-    /// If the combined volume is below this an empty solution is returned.</param>
-    /// <param name="overflowingSolution">Solution that exceeded overflowThreshold</param>
-    /// <returns>Whether any reagents were added to <paramref cref="targetSolution"/>.</returns>
-    public bool TryMixAndOverflow(EntityUid targetUid, Solution targetSolution,
-        Solution toAdd,
-        FixedPoint2 overflowThreshold,
-        [NotNullWhen(true)] out Solution? overflowingSolution)
-    {
-        if (toAdd.Volume == 0 || overflowThreshold > targetSolution.MaxVolume)
-        {
-            overflowingSolution = null;
-            return false;
-        }
-
-        targetSolution.AddSolution(toAdd, _prototypeManager);
-        overflowingSolution = targetSolution.SplitSolution(FixedPoint2.Max(FixedPoint2.Zero, targetSolution.Volume - overflowThreshold));
-        UpdateChemicals(targetUid, targetSolution, true);
-        return true;
-    }
-
-    public bool TryGetSolution([NotNullWhen(true)] EntityUid? uid, string name,
-        [NotNullWhen(true)] out Solution? solution,
-        SolutionContainerManagerComponent? solutionsMgr = null)
-    {
-        if (uid == null || !Resolve(uid.Value, ref solutionsMgr, false))
-        {
-            solution = null;
-            return false;
-        }
-
-        return solutionsMgr.Solutions.TryGetValue(name, out solution);
-    }
-
-
-    /// <summary>
-    /// Will ensure a solution is added to given entity even if it's missing solutionContainerManager
-    /// </summary>
-    /// <param name="uid">EntityUid to which to add solution</param>
-    /// <param name="name">name for the solution</param>
-    /// <param name="solutionsMgr">solution components used in resolves</param>
-    /// <param name="existed">true if the solution already existed</param>
-    /// <returns>solution</returns>
-    public Solution EnsureSolution(EntityUid uid, string name, out bool existed,
-        SolutionContainerManagerComponent? solutionsMgr = null)
-    {
-        if (!Resolve(uid, ref solutionsMgr, false))
-        {
-            solutionsMgr = EntityManager.EnsureComponent<SolutionContainerManagerComponent>(uid);
-        }
-
-        if (!solutionsMgr.Solutions.TryGetValue(name, out var existing))
-        {
-            var newSolution = new Solution() { Name = name };
-            solutionsMgr.Solutions.Add(name, newSolution);
-            existed = false;
-            return newSolution;
-        }
-
-        existed = true;
-        return existing;
-    }
-
-    /// <summary>
-    /// Will ensure a solution is added to given entity even if it's missing solutionContainerManager
-    /// </summary>
-    /// <param name="uid">EntityUid to which to add solution</param>
-    /// <param name="name">name for the solution</param>
-    /// <param name="solutionsMgr">solution components used in resolves</param>
-    /// <returns>solution</returns>
-    public Solution EnsureSolution(EntityUid uid, string name, SolutionContainerManagerComponent? solutionsMgr = null)
-        => EnsureSolution(uid, name, out _, solutionsMgr);
-
-    /// <summary>
-    /// Will ensure a solution is added to given entity even if it's missing solutionContainerManager
-    /// </summary>
-    /// <param name="uid">EntityUid to which to add solution</param>
-    /// <param name="name">name for the solution</param>
-    /// <param name="minVol">Ensures that the solution's maximum volume is larger than this value.</param>
-    /// <param name="solutionsMgr">solution components used in resolves</param>
-    /// <returns>solution</returns>
-    public Solution EnsureSolution(EntityUid uid, string name, FixedPoint2 minVol, out bool existed,
-        SolutionContainerManagerComponent? solutionsMgr = null)
-    {
-        if (!Resolve(uid, ref solutionsMgr, false))
-        {
-            solutionsMgr = EntityManager.EnsureComponent<SolutionContainerManagerComponent>(uid);
-        }
-
-        if (!solutionsMgr.Solutions.TryGetValue(name, out var existing))
-        {
-            var newSolution = new Solution() { Name = name };
-            solutionsMgr.Solutions.Add(name, newSolution);
-            existed = false;
-            newSolution.MaxVolume = minVol;
-            return newSolution;
-        }
-
-        existed = true;
-        existing.MaxVolume = FixedPoint2.Max(existing.MaxVolume, minVol);
-        return existing;
-    }
-
-    public Solution EnsureSolution(EntityUid uid, string name,
-        IEnumerable<ReagentQuantity> reagents,
-        bool setMaxVol = true,
-        SolutionContainerManagerComponent? solutionsMgr = null)
-    {
-        if (!Resolve(uid, ref solutionsMgr, false))
-            solutionsMgr = EntityManager.EnsureComponent<SolutionContainerManagerComponent>(uid);
-
-        if (!solutionsMgr.Solutions.TryGetValue(name, out var existing))
-        {
-            var newSolution = new Solution(reagents, setMaxVol);
-            solutionsMgr.Solutions.Add(name, newSolution);
-            return newSolution;
-        }
-
-        existing.SetContents(reagents, setMaxVol);
-        return existing;
-    }
-    /// <summary>
-    ///     Removes an amount from all reagents in a solution, adding it to a new solution.
-    /// </summary>
-    /// <param name="uid">The entity containing the solution.</param>
-    /// <param name="solution">The solution to remove reagents from.</param>
-    /// <param name="quantity">The amount to remove from every reagent in the solution.</param>
-    /// <returns>A new solution containing every removed reagent from the original solution.</returns>
-    public Solution RemoveEachReagent(EntityUid uid, Solution solution, FixedPoint2 quantity)
-    {
-        if (quantity <= 0)
-            return new Solution();
-
-        var removedSolution = new Solution();
-
-        // RemoveReagent does a RemoveSwap, meaning we don't have to copy the list if we iterate it backwards.
-        for (var i = solution.Contents.Count - 1; i >= 0; i--)
-        {
-            var (reagent, _) = solution.Contents[i];
-            var removedQuantity = solution.RemoveReagent(reagent, quantity);
-            removedSolution.AddReagent(reagent, removedQuantity);
-        }
-
-        UpdateChemicals(uid, solution);
-        return removedSolution;
-    }
-
-    public FixedPoint2 GetTotalPrototypeQuantity(EntityUid owner, string reagentId)
-    {
-        var reagentQuantity = FixedPoint2.New(0);
-        if (EntityManager.EntityExists(owner)
-            && EntityManager.TryGetComponent(owner, out SolutionContainerManagerComponent? managerComponent))
-        {
-            foreach (var solution in managerComponent.Solutions.Values)
-            {
-                reagentQuantity += solution.GetTotalPrototypeQuantity(reagentId);
-            }
-        }
-
-        return reagentQuantity;
-    }
-
-    public bool TryGetMixableSolution(EntityUid uid,
-        [NotNullWhen(true)] out Solution? solution,
-        SolutionContainerManagerComponent? solutionsMgr = null)
-    {
-
-        if (!Resolve(uid, ref solutionsMgr, false))
-        {
-            solution = null;
-            return false;
-        }
-
-        var getMixableSolutionAttempt = new GetMixableSolutionAttemptEvent(uid);
-        RaiseLocalEvent(uid, ref getMixableSolutionAttempt);
-        if (getMixableSolutionAttempt.MixedSolution != null)
-        {
-            solution = getMixableSolutionAttempt.MixedSolution;
-            return true;
-        }
-
-        var tryGetSolution = solutionsMgr.Solutions.FirstOrNull(x => x.Value.CanMix);
-        if (tryGetSolution.HasValue)
-        {
-            solution = tryGetSolution.Value.Value;
-            return true;
-        }
-
-        solution = null;
-        return false;
-    }
-
-    /// <summary>
-    /// Gets the most common reagent across all solutions by volume.
-    /// </summary>
-    /// <param name="component"></param>
-    public ReagentPrototype? GetMaxReagent(SolutionContainerManagerComponent component)
-    {
-        if (component.Solutions.Count == 0)
-            return null;
-
-        var reagentCounts = new Dictionary<ReagentId, FixedPoint2>();
-
-        foreach (var solution in component.Solutions.Values)
-        {
-            foreach (var (reagent, quantity) in solution.Contents)
-            {
-                reagentCounts.TryGetValue(reagent, out var existing);
-                existing += quantity;
-                reagentCounts[reagent] = existing;
-            }
-        }
-
-        var max = reagentCounts.Max();
-
-        return _prototypeManager.Index<ReagentPrototype>(max.Key.Prototype);
-    }
-
-    public SoundSpecifier? GetSound(SolutionContainerManagerComponent component)
-    {
-        var max = GetMaxReagent(component);
-        return max?.FootstepSound;
-    }
-
-    // Thermal energy and temperature management.
-
-    #region Thermal Energy and Temperature
-
-    /// <summary>
-    ///     Sets the temperature of a solution to a new value and then checks for reaction processing.
-    /// </summary>
-    /// <param name="owner">The entity in which the solution is located.</param>
-    /// <param name="solution">The solution to set the temperature of.</param>
-    /// <param name="temperature">The new value to set the temperature to.</param>
-    public void SetTemperature(EntityUid owner, Solution solution, float temperature)
-    {
-        if (temperature == solution.Temperature)
-            return;
-
-        solution.Temperature = temperature;
-        UpdateChemicals(owner, solution, true);
-    }
-
-    /// <summary>
-    ///     Sets the thermal energy of a solution to a new value and then checks for reaction processing.
-    /// </summary>
-    /// <param name="owner">The entity in which the solution is located.</param>
-    /// <param name="solution">The solution to set the thermal energy of.</param>
-    /// <param name="thermalEnergy">The new value to set the thermal energy to.</param>
-    public void SetThermalEnergy(EntityUid owner, Solution solution, float thermalEnergy)
-    {
-        var heatCap = solution.GetHeatCapacity(_prototypeManager);
-        solution.Temperature = heatCap == 0 ? 0 : thermalEnergy / heatCap;
-        UpdateChemicals(owner, solution, true);
-    }
-
-    /// <summary>
-    ///     Adds some thermal energy to a solution and then checks for reaction processing.
-    /// </summary>
-    /// <param name="owner">The entity in which the solution is located.</param>
-    /// <param name="solution">The solution to set the thermal energy of.</param>
-    /// <param name="thermalEnergy">The new value to set the thermal energy to.</param>
-    public void AddThermalEnergy(EntityUid owner, Solution solution, float thermalEnergy)
-    {
-        if (thermalEnergy == 0.0f)
-            return;
-
-        var heatCap = solution.GetHeatCapacity(_prototypeManager);
-        solution.Temperature += heatCap == 0 ? 0 : thermalEnergy / heatCap;
-        UpdateChemicals(owner, solution, true);
-    }
-
-    #endregion Thermal Energy and Temperature
-
-    #region Event Handlers
-
-    #endregion Event Handlers
-}
index 05e176da5b82e327aea4825340ed83ffc0e6e5b6..dcd135e3ab972d684bc4b68d72d2a9fde589af9e 100644 (file)
@@ -1,14 +1,13 @@
-using System.Collections.Frozen;
-using System.Linq;
 using Content.Shared.Administration.Logs;
 using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.Database;
 using Content.Shared.FixedPoint;
-using Robust.Shared.Audio;
 using Robust.Shared.Audio.Systems;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Utility;
+using System.Collections.Frozen;
+using System.Linq;
 
 namespace Content.Shared.Chemistry.Reaction
 {
@@ -49,7 +48,7 @@ namespace Content.Shared.Chemistry.Reaction
         {
             // Construct single-reaction dictionary.
             var dict = new Dictionary<string, List<ReactionPrototype>>();
-            foreach(var reaction in _prototypeManager.EnumeratePrototypes<ReactionPrototype>())
+            foreach (var reaction in _prototypeManager.EnumeratePrototypes<ReactionPrototype>())
             {
                 // For this dictionary we only need to cache based on the first reagent.
                 var reagent = reaction.Reactants.Keys.First();
@@ -59,7 +58,7 @@ namespace Content.Shared.Chemistry.Reaction
             _reactionsSingle = dict.ToFrozenDictionary();
 
             dict.Clear();
-            foreach(var reaction in _prototypeManager.EnumeratePrototypes<ReactionPrototype>())
+            foreach (var reaction in _prototypeManager.EnumeratePrototypes<ReactionPrototype>())
             {
                 foreach (var reagent in reaction.Reactants.Keys)
                 {
@@ -87,8 +86,10 @@ namespace Content.Shared.Chemistry.Reaction
         /// <param name="reaction">The reaction to check.</param>
         /// <param name="lowestUnitReactions">How many times this reaction can occur.</param>
         /// <returns></returns>
-        private bool CanReact(Solution solution, ReactionPrototype reaction, EntityUid owner, ReactionMixerComponent? mixerComponent, out FixedPoint2 lowestUnitReactions)
+        private bool CanReact(Entity<SolutionComponent> soln, ReactionPrototype reaction, ReactionMixerComponent? mixerComponent, out FixedPoint2 lowestUnitReactions)
         {
+            var solution = soln.Comp.Solution;
+
             lowestUnitReactions = FixedPoint2.MaxValue;
             if (solution.Temperature < reaction.MinimumTemperature)
             {
@@ -101,15 +102,15 @@ namespace Content.Shared.Chemistry.Reaction
                 return false;
             }
 
-            if((mixerComponent == null && reaction.MixingCategories != null) ||
+            if ((mixerComponent == null && reaction.MixingCategories != null) ||
                 mixerComponent != null && reaction.MixingCategories != null && reaction.MixingCategories.Except(mixerComponent.ReactionTypes).Any())
             {
                 lowestUnitReactions = FixedPoint2.Zero;
                 return false;
             }
 
-            var attempt = new ReactionAttemptEvent(reaction, solution);
-            RaiseLocalEvent(owner, attempt);
+            var attempt = new ReactionAttemptEvent(reaction, soln);
+            RaiseLocalEvent(soln, ref attempt);
             if (attempt.Cancelled)
             {
                 lowestUnitReactions = FixedPoint2.Zero;
@@ -155,8 +156,11 @@ namespace Content.Shared.Chemistry.Reaction
         ///     Perform a reaction on a solution. This assumes all reaction criteria are met.
         ///     Removes the reactants from the solution, adds products, and returns a list of products.
         /// </summary>
-        private List<string> PerformReaction(Solution solution, EntityUid owner, ReactionPrototype reaction, FixedPoint2 unitReactions)
+        private List<string> PerformReaction(Entity<SolutionComponent> soln, ReactionPrototype reaction, FixedPoint2 unitReactions)
         {
+            var (uid, comp) = soln;
+            var solution = comp.Solution;
+
             var energy = reaction.ConserveEnergy ? solution.GetThermalEnergy(_prototypeManager) : 0;
 
             //Remove reactants
@@ -184,20 +188,20 @@ namespace Content.Shared.Chemistry.Reaction
                     solution.Temperature = energy / newCap;
             }
 
-            OnReaction(solution, reaction, null, owner, unitReactions);
+            OnReaction(soln, reaction, null, unitReactions);
 
             return products;
         }
 
-        private void OnReaction(Solution solution, ReactionPrototype reaction, ReagentPrototype? reagent, EntityUid owner, FixedPoint2 unitReactions)
+        private void OnReaction(Entity<SolutionComponent> soln, ReactionPrototype reaction, ReagentPrototype? reagent, FixedPoint2 unitReactions)
         {
-            var args = new ReagentEffectArgs(owner, null, solution,
+            var args = new ReagentEffectArgs(soln, null, soln.Comp.Solution,
                 reagent,
                 unitReactions, EntityManager, null, 1f);
 
-            var coordinates = Transform(owner).Coordinates;
+            var coordinates = Transform(soln).Coordinates;
             _adminLogger.Add(LogType.ChemicalReaction, reaction.Impact,
-                $"Chemical reaction {reaction.ID:reaction} occurred with strength {unitReactions:strength} on entity {ToPrettyString(owner):metabolizer} at {coordinates}");
+                $"Chemical reaction {reaction.ID:reaction} occurred with strength {unitReactions:strength} on entity {ToPrettyString(soln):metabolizer} at {coordinates}");
 
             foreach (var effect in reaction.Effects)
             {
@@ -214,7 +218,7 @@ namespace Content.Shared.Chemistry.Reaction
                 effect.Effect(args);
             }
 
-            _audio.PlayPvs(reaction.Sound, owner);
+            _audio.PlayPvs(reaction.Sound, soln);
         }
 
         /// <summary>
@@ -222,7 +226,7 @@ namespace Content.Shared.Chemistry.Reaction
         ///     Removes the reactants from the solution, then returns a solution with all products.
         ///     WARNING: Does not trigger reactions between solution and new products.
         /// </summary>
-        private bool ProcessReactions(Solution solution, EntityUid owner, FixedPoint2 maxVolume, SortedSet<ReactionPrototype> reactions, ReactionMixerComponent? mixerComponent)
+        private bool ProcessReactions(Entity<SolutionComponent> soln, SortedSet<ReactionPrototype> reactions, ReactionMixerComponent? mixerComponent)
         {
             HashSet<ReactionPrototype> toRemove = new();
             List<string>? products = null;
@@ -230,13 +234,13 @@ namespace Content.Shared.Chemistry.Reaction
             // attempt to perform any applicable reaction
             foreach (var reaction in reactions)
             {
-                if (!CanReact(solution, reaction, owner, mixerComponent, out var unitReactions))
+                if (!CanReact(soln, reaction, mixerComponent, out var unitReactions))
                 {
                     toRemove.Add(reaction);
                     continue;
                 }
 
-                products = PerformReaction(solution, owner, reaction, unitReactions);
+                products = PerformReaction(soln, reaction, unitReactions);
                 break;
             }
 
@@ -261,11 +265,11 @@ namespace Content.Shared.Chemistry.Reaction
         /// <summary>
         ///     Continually react a solution until no more reactions occur, with a volume constraint.
         /// </summary>
-        public void FullyReactSolution(Solution solution, EntityUid owner, FixedPoint2 maxVolume, ReactionMixerComponent? mixerComponent = null)
+        public void FullyReactSolution(Entity<SolutionComponent> soln, ReactionMixerComponent? mixerComponent = null)
         {
             // construct the initial set of reactions to check.
             SortedSet<ReactionPrototype> reactions = new();
-            foreach (var reactant in solution.Contents)
+            foreach (var reactant in soln.Comp.Solution.Contents)
             {
                 if (_reactionsSingle.TryGetValue(reactant.Reagent.Prototype, out var reactantReactions))
                     reactions.UnionWith(reactantReactions);
@@ -275,11 +279,11 @@ namespace Content.Shared.Chemistry.Reaction
             // exceed the iteration limit.
             for (var i = 0; i < MaxReactionIterations; i++)
             {
-                if (!ProcessReactions(solution, owner, maxVolume, reactions, mixerComponent))
+                if (!ProcessReactions(soln, reactions, mixerComponent))
                     return;
             }
 
-            Log.Error($"{nameof(Solution)} {owner} could not finish reacting in under {MaxReactionIterations} loops.");
+            Log.Error($"{nameof(Solution)} {soln.Owner} could not finish reacting in under {MaxReactionIterations} loops.");
         }
     }
 
@@ -289,15 +293,11 @@ namespace Content.Shared.Chemistry.Reaction
     /// <reamrks>
     ///     Some solution containers (e.g., bloodstream, smoke, foam) use this to block certain reactions from occurring.
     /// </reamrks>
-    public sealed class ReactionAttemptEvent : CancellableEntityEventArgs
+    [ByRefEvent]
+    public record struct ReactionAttemptEvent(ReactionPrototype Reaction, Entity<SolutionComponent> Solution)
     {
-        public readonly ReactionPrototype Reaction;
-        public readonly Solution Solution;
-
-        public ReactionAttemptEvent(ReactionPrototype reaction, Solution solution)
-        {
-            Reaction = reaction;
-            Solution = solution;
-        }
+        public readonly ReactionPrototype Reaction = Reaction;
+        public readonly Entity<SolutionComponent> Solution = Solution;
+        public bool Cancelled = false;
     }
 }
index a88bf02bf83a6ecba21677b54dc4ddcdfd6de426..ede73c496906d4350cc9e4f2b99ba1387232041f 100644 (file)
@@ -27,4 +27,4 @@ public record struct MixingAttemptEvent(EntityUid Mixed, bool Cancelled = false)
 public readonly record struct AfterMixingEvent(EntityUid Mixed, EntityUid Mixer);
 
 [ByRefEvent]
-public record struct GetMixableSolutionAttemptEvent(EntityUid Mixed, Solution? MixedSolution = null);
+public record struct GetMixableSolutionAttemptEvent(EntityUid Mixed, Entity<SolutionComponent>? MixedSolution = null);
index 4881a6bf96d082d75a5aca39e48f28f9fcc0bd83..57b008de0c7a519fcd1d12b86f9e50b041d58958 100644 (file)
@@ -3,7 +3,6 @@ using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Reaction;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.Database;
-using Content.Shared.FixedPoint;
 using JetBrains.Annotations;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Random;
index 027002098e056501def026d1d02b0cf6c18aa985..762131d7612284662db4827b8e6a207ae7e72706 100644 (file)
@@ -1,4 +1,3 @@
-using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.FixedPoint;
 using Robust.Shared.Serialization;
index 84331851d16d4c0a8d15885779f051263bfa1e24..4fb4fe9438335ce5a1474b2b16d9180cf1a07c5c 100644 (file)
@@ -1,3 +1,4 @@
+using Content.Shared.Chemistry.Components;
 using Content.Shared.Tag;
 using Robust.Shared.Audio;
 
@@ -19,6 +20,9 @@ public sealed partial class DrainComponent : Component
     [ValidatePrototypeId<TagPrototype>]
     public const string PlungerTag = "Plunger";
 
+    [DataField]
+    public Entity<SolutionComponent>? Solution = null;
+
     [DataField("accumulator")]
     public float Accumulator = 0f;
 
index 4abacf1ac996ae6c30edfa339da282cffe39845d..bc40e9960c60cb60166ae0986adab214c64c3d9d 100644 (file)
@@ -1,3 +1,4 @@
+using Content.Shared.Chemistry.Components;
 using Content.Shared.FixedPoint;
 using Robust.Shared.Audio;
 using Robust.Shared.GameStates;
@@ -17,5 +18,8 @@ namespace Content.Shared.Fluids.Components
         public FixedPoint2 OverflowVolume = FixedPoint2.New(20);
 
         [DataField("solution")] public string SolutionName = "puddle";
+
+        [DataField("solutionRef")]
+        public Entity<SolutionComponent>? Solution;
     }
 }
index 056c3ba91987dcecbbafc626b12e8332e1950308..08a7624c4a8a116bab832c7d1a55bad932c52f1e 100644 (file)
@@ -1,6 +1,5 @@
 using Content.Shared.Chemistry.Components;
 using Content.Shared.DragDrop;
-using Content.Shared.Fluids.Components;
 
 namespace Content.Shared.Fluids;
 
@@ -22,12 +21,12 @@ public abstract class SharedPuddleSystem : EntitySystem
         SubscribeLocalEvent<RefillableSolutionComponent, CanDropDraggedEvent>(OnRefillableCanDropDragged);
     }
 
-    private void OnRefillableCanDrag(EntityUid uid, RefillableSolutionComponent component, ref CanDragEvent args)
+    private void OnRefillableCanDrag(Entity<RefillableSolutionComponent> entity, ref CanDragEvent args)
     {
         args.Handled = true;
     }
 
-    private void OnDumpCanDropTarget(EntityUid uid, DumpableSolutionComponent component, ref CanDropTargetEvent args)
+    private void OnDumpCanDropTarget(Entity<DumpableSolutionComponent> entity, ref CanDropTargetEvent args)
     {
         if (HasComp<DrainableSolutionComponent>(args.Dragged))
         {
@@ -36,7 +35,7 @@ public abstract class SharedPuddleSystem : EntitySystem
         }
     }
 
-    private void OnDrainCanDropTarget(EntityUid uid, DrainableSolutionComponent component, ref CanDropTargetEvent args)
+    private void OnDrainCanDropTarget(Entity<DrainableSolutionComponent> entity, ref CanDropTargetEvent args)
     {
         if (HasComp<RefillableSolutionComponent>(args.Dragged))
         {
@@ -45,7 +44,7 @@ public abstract class SharedPuddleSystem : EntitySystem
         }
     }
 
-    private void OnRefillableCanDropDragged(EntityUid uid, RefillableSolutionComponent component, ref CanDropDraggedEvent args)
+    private void OnRefillableCanDropDragged(Entity<RefillableSolutionComponent> entity, ref CanDropDraggedEvent args)
     {
         if (!HasComp<DrainableSolutionComponent>(args.Target) && !HasComp<DumpableSolutionComponent>(args.Target))
             return;
index e86ecf01c5dcb5951851b66cb7a836188c4b33e7..ed07fd128716be092a39f88c4815acc076099ecf 100644 (file)
@@ -1,4 +1,3 @@
-using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Reagent;
 using Robust.Shared.Serialization;
 
index dc948842884a29f7c3a3896f5d2d2e049fa1ca6c..f5d679c293df2d25661ffd9053db61016d2c55e5 100644 (file)
@@ -1,4 +1,3 @@
-using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Reagent;
 using Robust.Shared.Serialization;
 
index eda5cc4058eb2074ac501d17900e72033f5c58b5..91bd59614a1bd7bcbb1eefab7a29cd2ed8fdaf87 100644 (file)
@@ -1,5 +1,4 @@
-using Content.Shared.Chemistry.Components;
-using Content.Shared.Construction.Prototypes;
+using Content.Shared.Construction.Prototypes;
 using Content.Shared.Whitelist;
 using Robust.Shared.Audio;
 using Robust.Shared.GameStates;
@@ -84,12 +83,6 @@ public sealed partial class MaterialReclaimerComponent : Component
     [DataField, ViewVariables(VVAccess.ReadWrite)]
     public string SolutionContainerId = "output";
 
-    /// <summary>
-    /// The solution itself.
-    /// </summary>
-    [ViewVariables(VVAccess.ReadWrite)]
-    public Solution OutputSolution = default!;
-
     /// <summary>
     /// a whitelist for what entities can be inserted into this reclaimer
     /// </summary>
index 97d96e7bfff227f5669d3869a43201dfaea93dcc..e27603763fa2f7519f95d24c41181c45c53057c6 100644 (file)
@@ -1,6 +1,6 @@
-using Content.Shared.DoAfter;
+using Content.Shared.Chemistry.Components;
+using Content.Shared.DoAfter;
 using Robust.Shared.Serialization;
-using Content.Shared.Chemistry.Components;
 
 namespace Content.Shared.Nutrition;
 
@@ -47,8 +47,8 @@ public sealed partial class VapeDoAfterEvent : DoAfterEvent
 
     public VapeDoAfterEvent(Solution solution, bool forced)
     {
-            Solution = solution;
-            Forced = forced;
+        Solution = solution;
+        Forced = forced;
     }
 
     public override DoAfterEvent Clone() => this;
index 0fa23d24913ba150d0d890845f767e51e223c635..7f6e335b36adaf92d6046b048eb283e73ef6957c 100644 (file)
@@ -1,9 +1,8 @@
 using Content.Shared.Chemistry.Components;
 using Content.Shared.FixedPoint;
+using NUnit.Framework;
 using Robust.Shared.IoC;
 using Robust.Shared.Prototypes;
-using NUnit.Framework;
-using Robust.Shared.Utility;
 
 namespace Content.Tests.Shared.Chemistry;