Solution Entities (#21916)

* 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>
This commit is contained in:
TemporalOroboros
2023-12-28 17:58:14 -08:00
committed by GitHub
parent a4d36d408d
commit d75e743dd7
180 changed files with 3540 additions and 2956 deletions

View File

@@ -4,9 +4,6 @@ using Robust.Client.Console;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
namespace Content.Client.Administration.UI.ManageSolutions namespace Content.Client.Administration.UI.ManageSolutions

View File

@@ -20,7 +20,7 @@ namespace Content.Client.Administration.UI.ManageSolutions
private NetEntity _target = NetEntity.Invalid; private NetEntity _target = NetEntity.Invalid;
private string? _selectedSolution; private string? _selectedSolution;
private AddReagentWindow? _addReagentWindow; private AddReagentWindow? _addReagentWindow;
private Dictionary<string, Solution>? _solutions; private Dictionary<string, EntityUid>? _solutions;
public EditSolutionsWindow() public EditSolutionsWindow()
{ {
@@ -60,9 +60,11 @@ namespace Content.Client.Administration.UI.ManageSolutions
if (_selectedSolution == null || _solutions == null) if (_selectedSolution == null || _solutions == null)
return; return;
if (!_solutions.TryGetValue(_selectedSolution, out var solution)) if (!_solutions.TryGetValue(_selectedSolution, out var solutionId) ||
!_entityManager.TryGetComponent(solutionId, out SolutionComponent? solutionComp))
return; return;
var solution = solutionComp.Solution;
UpdateVolumeBox(solution); UpdateVolumeBox(solution);
UpdateThermalBox(solution); UpdateThermalBox(solution);
@@ -198,10 +200,13 @@ namespace Content.Client.Administration.UI.ManageSolutions
/// </summary> /// </summary>
private void SetReagent(FloatSpinBox.FloatSpinBoxEventArgs args, string prototype) 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; return;
var current = _solutions[_selectedSolution].GetTotalPrototypeQuantity(prototype); var solution = solutionComp.Solution;
var current = solution.GetTotalPrototypeQuantity(prototype);
var delta = args.Value - current.Float(); var delta = args.Value - current.Float();
if (MathF.Abs(delta) < 0.01) if (MathF.Abs(delta) < 0.01)
@@ -275,22 +280,38 @@ namespace Content.Client.Administration.UI.ManageSolutions
/// <summary> /// <summary>
/// Update the solution options. /// Update the solution options.
/// </summary> /// </summary>
public void UpdateSolutions(Dictionary<string, Solution>? solutions) public void UpdateSolutions(List<(string, NetEntity)>? solutions)
{ {
SolutionOption.Clear(); 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) if (_solutions == null)
return; return;
int i = 0; 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.AddItem(name, i);
SolutionOption.SetItemMetadata(i, solution); SolutionOption.SetItemMetadata(i, name);
if (solution == _selectedSolution) if (name == _selectedSolution)
SolutionOption.Select(i); selectedIndex = i;
i++; i++;
} }
@@ -300,14 +321,11 @@ namespace Content.Client.Administration.UI.ManageSolutions
// No applicable solutions // No applicable solutions
Close(); Close();
Dispose(); Dispose();
return;
} }
if (_selectedSolution == null || !_solutions.ContainsKey(_selectedSolution)) SolutionOption.Select(selectedIndex);
{ _selectedSolution = (string?) SolutionOption.SelectedMetadata;
// the previously selected solution is no longer valid.
SolutionOption.Select(0);
_selectedSolution = (string?) SolutionOption.SelectedMetadata;
}
} }
} }
} }

View File

@@ -0,0 +1,7 @@
using Content.Shared.Chemistry.EntitySystems;
namespace Content.Client.Chemistry.Containers.EntitySystems;
public sealed partial class SolutionContainerSystem : SharedSolutionContainerSystem
{
}

View File

@@ -1,11 +1,9 @@
using Content.Client.UserInterface.Controls; using Content.Client.UserInterface.Controls;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.Kitchen; using Content.Shared.Kitchen;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -15,7 +13,7 @@ namespace Content.Client.Kitchen.UI
public sealed partial class GrinderMenu : FancyWindow public sealed partial class GrinderMenu : FancyWindow
{ {
private readonly IEntityManager _entityManager; private readonly IEntityManager _entityManager;
private readonly IPrototypeManager _prototypeManager ; private readonly IPrototypeManager _prototypeManager;
private readonly ReagentGrinderBoundUserInterface _owner; private readonly ReagentGrinderBoundUserInterface _owner;
private readonly Dictionary<int, EntityUid> _chamberVisualContents = new(); private readonly Dictionary<int, EntityUid> _chamberVisualContents = new();
@@ -122,8 +120,8 @@ namespace Content.Client.Kitchen.UI
{ {
foreach (var (reagent, quantity) in reagents) foreach (var (reagent, quantity) in reagents)
{ {
var reagentName = _prototypeManager.TryIndex(reagent.Prototype, out ReagentPrototype? proto) var reagentName = _prototypeManager.TryIndex(reagent.Prototype, out ReagentPrototype? proto)
? Loc.GetString($"{quantity} {proto.LocalizedName}") ? Loc.GetString($"{quantity} {proto.LocalizedName}")
: "???"; : "???";
BeakerContentBox.BoxContents.AddItem(reagentName); BeakerContentBox.BoxContents.AddItem(reagentName);
} }

View File

@@ -1,11 +1,9 @@
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.Kitchen.Components; using Content.Shared.Kitchen.Components;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Shared.Graphics;
namespace Content.Client.Kitchen.UI namespace Content.Client.Kitchen.UI
{ {

View File

@@ -1,5 +1,5 @@
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -51,7 +51,7 @@ public sealed class SolutionSystemTests
var entityManager = server.ResolveDependency<IEntityManager>(); var entityManager = server.ResolveDependency<IEntityManager>();
var protoMan = server.ResolveDependency<IPrototypeManager>(); var protoMan = server.ResolveDependency<IPrototypeManager>();
var containerSystem = entityManager.EntitySysManager.GetEntitySystem<SolutionContainerSystem>(); var containerSystem = entityManager.System<SolutionContainerSystem>();
var testMap = await pair.CreateTestMap(); var testMap = await pair.CreateTestMap();
var coordinates = testMap.GridCoords; var coordinates = testMap.GridCoords;
@@ -67,11 +67,11 @@ public sealed class SolutionSystemTests
beaker = entityManager.SpawnEntity("SolutionTarget", coordinates); beaker = entityManager.SpawnEntity("SolutionTarget", coordinates);
Assert.That(containerSystem Assert.That(containerSystem
.TryGetSolution(beaker, "beaker", out var solution)); .TryGetSolution(beaker, "beaker", out var solutionEnt, out var solution));
solution.AddSolution(originalWater, protoMan); solution.AddSolution(originalWater, protoMan);
Assert.That(containerSystem Assert.That(containerSystem
.TryAddSolution(beaker, solution, oilAdded)); .TryAddSolution(solutionEnt.Value, oilAdded));
var water = solution.GetTotalPrototypeQuantity("Water"); var water = solution.GetTotalPrototypeQuantity("Water");
var oil = solution.GetTotalPrototypeQuantity("Oil"); var oil = solution.GetTotalPrototypeQuantity("Oil");
@@ -97,7 +97,7 @@ public sealed class SolutionSystemTests
var entityManager = server.ResolveDependency<IEntityManager>(); var entityManager = server.ResolveDependency<IEntityManager>();
var protoMan = server.ResolveDependency<IPrototypeManager>(); var protoMan = server.ResolveDependency<IPrototypeManager>();
var containerSystem = entityManager.EntitySysManager.GetEntitySystem<SolutionContainerSystem>(); var containerSystem = entityManager.System<SolutionContainerSystem>();
var coordinates = testMap.GridCoords; var coordinates = testMap.GridCoords;
EntityUid beaker; EntityUid beaker;
@@ -112,11 +112,11 @@ public sealed class SolutionSystemTests
beaker = entityManager.SpawnEntity("SolutionTarget", coordinates); beaker = entityManager.SpawnEntity("SolutionTarget", coordinates);
Assert.That(containerSystem Assert.That(containerSystem
.TryGetSolution(beaker, "beaker", out var solution)); .TryGetSolution(beaker, "beaker", out var solutionEnt, out var solution));
solution.AddSolution(originalWater, protoMan); solution.AddSolution(originalWater, protoMan);
Assert.That(containerSystem Assert.That(containerSystem
.TryAddSolution(beaker, solution, oilAdded), Is.False); .TryAddSolution(solutionEnt.Value, oilAdded), Is.False);
var water = solution.GetTotalPrototypeQuantity("Water"); var water = solution.GetTotalPrototypeQuantity("Water");
var oil = solution.GetTotalPrototypeQuantity("Oil"); var oil = solution.GetTotalPrototypeQuantity("Oil");
@@ -141,7 +141,7 @@ public sealed class SolutionSystemTests
var entityManager = server.ResolveDependency<IEntityManager>(); var entityManager = server.ResolveDependency<IEntityManager>();
var protoMan = server.ResolveDependency<IPrototypeManager>(); var protoMan = server.ResolveDependency<IPrototypeManager>();
var testMap = await pair.CreateTestMap(); var testMap = await pair.CreateTestMap();
var containerSystem = entityManager.EntitySysManager.GetEntitySystem<SolutionContainerSystem>(); var containerSystem = entityManager.System<SolutionContainerSystem>();
var coordinates = testMap.GridCoords; var coordinates = testMap.GridCoords;
EntityUid beaker; EntityUid beaker;
@@ -158,11 +158,11 @@ public sealed class SolutionSystemTests
beaker = entityManager.SpawnEntity("SolutionTarget", coordinates); beaker = entityManager.SpawnEntity("SolutionTarget", coordinates);
Assert.That(containerSystem Assert.That(containerSystem
.TryGetSolution(beaker, "beaker", out var solution)); .TryGetSolution(beaker, "beaker", out var solutionEnt, out var solution));
solution.AddSolution(originalWater, protoMan); solution.AddSolution(originalWater, protoMan);
Assert.That(containerSystem Assert.That(containerSystem
.TryMixAndOverflow(beaker, solution, oilAdded, threshold, out var overflowingSolution)); .TryMixAndOverflow(solutionEnt.Value, oilAdded, threshold, out var overflowingSolution));
Assert.Multiple(() => Assert.Multiple(() =>
{ {
@@ -194,7 +194,7 @@ public sealed class SolutionSystemTests
var entityManager = server.ResolveDependency<IEntityManager>(); var entityManager = server.ResolveDependency<IEntityManager>();
var protoMan = server.ResolveDependency<IPrototypeManager>(); var protoMan = server.ResolveDependency<IPrototypeManager>();
var containerSystem = entityManager.EntitySysManager.GetEntitySystem<SolutionContainerSystem>(); var containerSystem = entityManager.System<SolutionContainerSystem>();
var testMap = await pair.CreateTestMap(); var testMap = await pair.CreateTestMap();
var coordinates = testMap.GridCoords; var coordinates = testMap.GridCoords;
@@ -212,11 +212,11 @@ public sealed class SolutionSystemTests
beaker = entityManager.SpawnEntity("SolutionTarget", coordinates); beaker = entityManager.SpawnEntity("SolutionTarget", coordinates);
Assert.That(containerSystem Assert.That(containerSystem
.TryGetSolution(beaker, "beaker", out var solution)); .TryGetSolution(beaker, "beaker", out var solutionEnt, out var solution));
solution.AddSolution(originalWater, protoMan); solution.AddSolution(originalWater, protoMan);
Assert.That(containerSystem Assert.That(containerSystem
.TryMixAndOverflow(beaker, solution, oilAdded, threshold, out _), .TryMixAndOverflow(solutionEnt.Value, oilAdded, threshold, out _),
Is.False); Is.False);
}); });

View File

@@ -1,11 +1,11 @@
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.Reaction;
using Content.Shared.Chemistry.Components;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using System.Linq;
namespace Content.IntegrationTests.Tests.Chemistry namespace Content.IntegrationTests.Tests.Chemistry
{ {
@@ -34,8 +34,7 @@ namespace Content.IntegrationTests.Tests.Chemistry
var prototypeManager = server.ResolveDependency<IPrototypeManager>(); var prototypeManager = server.ResolveDependency<IPrototypeManager>();
var testMap = await pair.CreateTestMap(); var testMap = await pair.CreateTestMap();
var coordinates = testMap.GridCoords; var coordinates = testMap.GridCoords;
var solutionSystem = server.ResolveDependency<IEntitySystemManager>() var solutionContainerSystem = entityManager.System<SolutionContainerSystem>();
.GetEntitySystem<SolutionContainerSystem>();
foreach (var reactionPrototype in prototypeManager.EnumeratePrototypes<ReactionPrototype>()) foreach (var reactionPrototype in prototypeManager.EnumeratePrototypes<ReactionPrototype>())
{ {
@@ -43,30 +42,31 @@ namespace Content.IntegrationTests.Tests.Chemistry
Console.WriteLine($"Testing {reactionPrototype.ID}"); Console.WriteLine($"Testing {reactionPrototype.ID}");
EntityUid beaker = default; EntityUid beaker = default;
Solution component = null; Entity<SolutionComponent>? solutionEnt = default!;
Solution solution = null;
await server.WaitAssertion(() => await server.WaitAssertion(() =>
{ {
beaker = entityManager.SpawnEntity("TestSolutionContainer", coordinates); beaker = entityManager.SpawnEntity("TestSolutionContainer", coordinates);
Assert.That(solutionSystem Assert.That(solutionContainerSystem
.TryGetSolution(beaker, "beaker", out component)); .TryGetSolution(beaker, "beaker", out solutionEnt, out solution));
foreach (var (id, reactant) in reactionPrototype.Reactants) foreach (var (id, reactant) in reactionPrototype.Reactants)
{ {
#pragma warning disable NUnit2045 #pragma warning disable NUnit2045
Assert.That(solutionSystem Assert.That(solutionContainerSystem
.TryAddReagent(beaker, component, id, reactant.Amount, out var quantity)); .TryAddReagent(solutionEnt.Value, id, reactant.Amount, out var quantity));
Assert.That(reactant.Amount, Is.EqualTo(quantity)); Assert.That(reactant.Amount, Is.EqualTo(quantity));
#pragma warning restore NUnit2045 #pragma warning restore NUnit2045
} }
solutionSystem.SetTemperature(beaker, component, reactionPrototype.MinimumTemperature); solutionContainerSystem.SetTemperature(solutionEnt.Value, reactionPrototype.MinimumTemperature);
if (reactionPrototype.MixingCategories != null) if (reactionPrototype.MixingCategories != null)
{ {
var dummyEntity = entityManager.SpawnEntity(null, MapCoordinates.Nullspace); var dummyEntity = entityManager.SpawnEntity(null, MapCoordinates.Nullspace);
var mixerComponent = entityManager.AddComponent<ReactionMixerComponent>(dummyEntity); var mixerComponent = entityManager.AddComponent<ReactionMixerComponent>(dummyEntity);
mixerComponent.ReactionTypes = reactionPrototype.MixingCategories; 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 var foundProductsMap = reactionPrototype.Products
.Concat(reactionPrototype.Reactants.Where(x => x.Value.Catalyst).ToDictionary(x => x.Key, x => x.Value.Amount)) .Concat(reactionPrototype.Reactants.Where(x => x.Value.Catalyst).ToDictionary(x => x.Key, x => x.Value.Amount))
.ToDictionary(x => x, _ => false); .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)); Assert.That(foundProductsMap.TryFirstOrNull(x => x.Key.Key == reagent.Prototype && x.Key.Value == quantity, out var foundProduct));
foundProductsMap[foundProduct.Value.Key] = true; foundProductsMap[foundProduct.Value.Key] = true;

View File

@@ -80,7 +80,7 @@ public sealed class AbsorbentTest
var entityManager = server.ResolveDependency<IEntityManager>(); var entityManager = server.ResolveDependency<IEntityManager>();
var absorbentSystem = entityManager.System<AbsorbentSystem>(); var absorbentSystem = entityManager.System<AbsorbentSystem>();
var solutionContainerSystem = entityManager.System<SolutionContainerSystem>(); var solutionContainerSystem = entityManager.System<SharedSolutionContainerSystem>();
var prototypeManager = server.ResolveDependency<IPrototypeManager>(); var prototypeManager = server.ResolveDependency<IPrototypeManager>();
EntityUid user = default; EntityUid user = default;
@@ -94,19 +94,19 @@ public sealed class AbsorbentTest
refillable = entityManager.SpawnEntity(RefillableDummyId, coordinates); refillable = entityManager.SpawnEntity(RefillableDummyId, coordinates);
entityManager.TryGetComponent(absorbent, out component); entityManager.TryGetComponent(absorbent, out component);
solutionContainerSystem.TryGetSolution(absorbent, AbsorbentComponent.SolutionName, out var absorbentSolution); solutionContainerSystem.TryGetSolution(absorbent, AbsorbentComponent.SolutionName, out var absorbentSoln, out var absorbentSolution);
solutionContainerSystem.TryGetRefillableSolution(refillable, out var refillableSolution); solutionContainerSystem.TryGetRefillableSolution(refillable, out var refillableSoln, out var refillableSolution);
// Arrange // Arrange
if (testCase.InitialAbsorbentSolution.VolumeOfEvaporable > FixedPoint2.Zero) 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) 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) 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) 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 // Act
absorbentSystem.Mop(user, refillable, absorbent, component); absorbentSystem.Mop(user, refillable, absorbent, component);
@@ -138,7 +138,7 @@ public sealed class AbsorbentTest
var entityManager = server.ResolveDependency<IEntityManager>(); var entityManager = server.ResolveDependency<IEntityManager>();
var absorbentSystem = entityManager.System<AbsorbentSystem>(); var absorbentSystem = entityManager.System<AbsorbentSystem>();
var solutionContainerSystem = entityManager.System<SolutionContainerSystem>(); var solutionContainerSystem = entityManager.System<SharedSolutionContainerSystem>();
var prototypeManager = server.ResolveDependency<IPrototypeManager>(); var prototypeManager = server.ResolveDependency<IPrototypeManager>();
EntityUid user = default; EntityUid user = default;
@@ -152,18 +152,18 @@ public sealed class AbsorbentTest
refillable = entityManager.SpawnEntity(SmallRefillableDummyId, coordinates); refillable = entityManager.SpawnEntity(SmallRefillableDummyId, coordinates);
entityManager.TryGetComponent(absorbent, out component); entityManager.TryGetComponent(absorbent, out component);
solutionContainerSystem.TryGetSolution(absorbent, AbsorbentComponent.SolutionName, out var absorbentSolution); solutionContainerSystem.TryGetSolution(absorbent, AbsorbentComponent.SolutionName, out var absorbentSoln, out var absorbentSolution);
solutionContainerSystem.TryGetRefillableSolution(refillable, out var refillableSolution); solutionContainerSystem.TryGetRefillableSolution(refillable, out var refillableSoln, out var refillableSolution);
// Arrange // 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) 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) 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) 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 // Act
absorbentSystem.Mop(user, refillable, absorbent, component); absorbentSystem.Mop(user, refillable, absorbent, component);

View File

@@ -1,10 +1,11 @@
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Robust.Shared.Console; using Robust.Shared.Console;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using System.Linq;
namespace Content.Server.Administration.Commands namespace Content.Server.Administration.Commands
{ {
@@ -41,13 +42,13 @@ namespace Content.Server.Administration.Commands
return; 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}"); shell.WriteLine($"Entity does not have a \"{args[1]}\" solution. Valid solutions are:\n{validSolutions}");
return; return;
} }
var solution = man.Solutions[args[1]];
if (!_protomanager.HasIndex<ReagentPrototype>(args[2])) if (!_protomanager.HasIndex<ReagentPrototype>(args[2]))
{ {
@@ -63,9 +64,9 @@ namespace Content.Server.Administration.Commands
var quantity = FixedPoint2.New(MathF.Abs(quantityFloat)); var quantity = FixedPoint2.New(MathF.Abs(quantityFloat));
if (quantityFloat > 0) if (quantityFloat > 0)
_entManager.System<SolutionContainerSystem>().TryAddReagent(uid.Value, solution, args[2], quantity, out _); solutionContainerSystem.TryAddReagent(solution.Value, args[2], quantity, out _);
else else
_entManager.System<SolutionContainerSystem>().RemoveReagent(uid.Value, solution, args[2], quantity); solutionContainerSystem.RemoveReagent(solution.Value, args[2], quantity);
} }
} }
} }

View File

@@ -1,8 +1,9 @@
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Robust.Shared.Console; using Robust.Shared.Console;
using System.Linq;
namespace Content.Server.Administration.Commands namespace Content.Server.Administration.Commands
{ {
@@ -35,13 +36,13 @@ namespace Content.Server.Administration.Commands
return; 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}"); shell.WriteLine($"Entity does not have a \"{args[1]}\" solution. Valid solutions are:\n{validSolutions}");
return; return;
} }
var solution = man.Solutions[args[1]];
if (!float.TryParse(args[2], out var quantityFloat)) if (!float.TryParse(args[2], out var quantityFloat))
{ {
@@ -49,14 +50,14 @@ namespace Content.Server.Administration.Commands
return; return;
} }
if(quantityFloat < 0.0f) if (quantityFloat < 0.0f)
{ {
shell.WriteLine($"Cannot set the maximum volume of a solution to a negative number."); shell.WriteLine($"Cannot set the maximum volume of a solution to a negative number.");
return; return;
} }
var quantity = FixedPoint2.New(quantityFloat); var quantity = FixedPoint2.New(quantityFloat);
_entManager.System<SolutionContainerSystem>().SetCapacity(uid.Value, solution, quantity); solutionContainerSystem.SetCapacity(solution.Value, quantity);
} }
} }
} }

View File

@@ -1,7 +1,8 @@
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
using Robust.Shared.Console; using Robust.Shared.Console;
using System.Linq;
namespace Content.Server.Administration.Commands namespace Content.Server.Administration.Commands
{ {
@@ -34,13 +35,13 @@ namespace Content.Server.Administration.Commands
return; 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}"); shell.WriteLine($"Entity does not have a \"{args[1]}\" solution. Valid solutions are:\n{validSolutions}");
return; return;
} }
var solution = man.Solutions[args[1]];
if (!float.TryParse(args[2], out var quantity)) if (!float.TryParse(args[2], out var quantity))
{ {
@@ -54,7 +55,7 @@ namespace Content.Server.Administration.Commands
return; return;
} }
_entManager.System<SolutionContainerSystem>().SetTemperature(uid.Value, solution, quantity); solutionContainerSystem.SetTemperature(solution.Value, quantity);
} }
} }
} }

View File

@@ -1,7 +1,8 @@
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
using Robust.Shared.Console; using Robust.Shared.Console;
using System.Linq;
namespace Content.Server.Administration.Commands namespace Content.Server.Administration.Commands
{ {
@@ -34,13 +35,13 @@ namespace Content.Server.Administration.Commands
return; 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}"); shell.WriteLine($"Entity does not have a \"{args[1]}\" solution. Valid solutions are:\n{validSolutions}");
return; return;
} }
var solution = man.Solutions[args[1]];
if (!float.TryParse(args[2], out var quantity)) if (!float.TryParse(args[2], out var quantity))
{ {
@@ -50,19 +51,19 @@ namespace Content.Server.Administration.Commands
if (solution.GetHeatCapacity(null) <= 0.0f) 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."); shell.WriteLine($"Cannot set the thermal energy of a solution with 0 heat capacity to a non-zero number.");
return; 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."); shell.WriteLine($"Cannot set the thermal energy of a solution with heat capacity to a non-positive number.");
return; return;
} }
_entManager.System<SolutionContainerSystem>().SetThermalEnergy(uid.Value, solution, quantity); solutionContainerSystem.SetThermalEnergy(solutionEnt.Value, quantity);
} }
} }
} }

View File

@@ -1,4 +1,3 @@
using System.Linq;
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Administration.Managers; using Content.Server.Administration.Managers;
using Content.Server.Administration.UI; using Content.Server.Administration.UI;
@@ -34,6 +33,7 @@ using Robust.Shared.Prototypes;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Toolshed; using Robust.Shared.Toolshed;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using System.Linq;
using static Content.Shared.Configurable.ConfigurationComponent; using static Content.Shared.Configurable.ConfigurationComponent;
namespace Content.Server.Administration.Systems namespace Content.Server.Administration.Systems
@@ -71,7 +71,7 @@ namespace Content.Server.Administration.Systems
{ {
SubscribeLocalEvent<GetVerbsEvent<Verb>>(GetVerbs); SubscribeLocalEvent<GetVerbsEvent<Verb>>(GetVerbs);
SubscribeLocalEvent<RoundRestartCleanupEvent>(Reset); SubscribeLocalEvent<RoundRestartCleanupEvent>(Reset);
SubscribeLocalEvent<SolutionContainerManagerComponent, SolutionChangedEvent>(OnSolutionChanged); SubscribeLocalEvent<SolutionContainerManagerComponent, SolutionContainerChangedEvent>(OnSolutionChanged);
} }
private void GetVerbs(GetVerbsEvent<Verb> ev) private void GetVerbs(GetVerbsEvent<Verb> ev)
@@ -470,11 +470,11 @@ namespace Content.Server.Administration.Systems
} }
#region SolutionsEui #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) foreach (var eui in _openSolutionUis.Values)
{ {
if (eui.Target == uid) if (eui.Target == entity.Owner)
eui.StateDirty(); eui.StateDirty();
} }
} }

View File

@@ -1,12 +1,12 @@
using System.Linq; using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Robust.Shared.Toolshed; using Robust.Shared.Toolshed;
using Robust.Shared.Toolshed.Syntax; using Robust.Shared.Toolshed.Syntax;
using Robust.Shared.Toolshed.TypeParsers; using Robust.Shared.Toolshed.TypeParsers;
using System.Linq;
namespace Content.Server.Administration.Toolshed; namespace Content.Server.Administration.Toolshed;
@@ -24,10 +24,8 @@ public sealed class SolutionCommand : ToolshedCommand
{ {
_solutionContainer ??= GetSys<SolutionContainerSystem>(); _solutionContainer ??= GetSys<SolutionContainerSystem>();
_solutionContainer.TryGetSolution(input, name.Evaluate(ctx)!, out var solution); if (_solutionContainer.TryGetSolution(input, name.Evaluate(ctx)!, out var solution))
return new SolutionRef(solution.Value);
if (solution is not null)
return new SolutionRef(input, solution);
return null; return null;
} }
@@ -55,11 +53,11 @@ public sealed class SolutionCommand : ToolshedCommand
var amount = amountRef.Evaluate(ctx); var amount = amountRef.Evaluate(ctx);
if (amount > 0) 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) else if (amount < 0)
{ {
_solutionContainer.RemoveReagent(input.Owner, input.Solution, name.Value.ID, -amount); _solutionContainer.RemoveReagent(input.Solution, name.Value.ID, -amount);
} }
return input; return input;
@@ -75,10 +73,10 @@ public sealed class SolutionCommand : ToolshedCommand
=> input.Select(x => AdjReagent(ctx, x, name, amountRef)); => 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() public override string ToString()
{ {
return $"{Owner} {Solution}"; return $"{Solution.Owner} {Solution.Comp.Solution}";
} }
} }

View File

@@ -1,4 +1,5 @@
using Content.Server.Administration.Systems; using Content.Server.Administration.Systems;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.EUI; using Content.Server.EUI;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.Components.SolutionManager;
@@ -14,11 +15,13 @@ namespace Content.Server.Administration.UI
public sealed class EditSolutionsEui : BaseEui public sealed class EditSolutionsEui : BaseEui
{ {
[Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IEntityManager _entityManager = default!;
private readonly SolutionContainerSystem _solutionContainerSystem = default!;
public readonly EntityUid Target; public readonly EntityUid Target;
public EditSolutionsEui(EntityUid entity) public EditSolutionsEui(EntityUid entity)
{ {
IoCManager.InjectDependencies(this); IoCManager.InjectDependencies(this);
_solutionContainerSystem = _entityManager.System<SolutionContainerSystem>();
Target = entity; Target = entity;
} }
@@ -36,8 +39,23 @@ namespace Content.Server.Administration.UI
public override EuiStateBase GetNewState() public override EuiStateBase GetNewState()
{ {
var solutions = _entityManager.GetComponentOrNull<SolutionContainerManagerComponent>(Target)?.Solutions; List<(string Name, NetEntity Solution)>? netSolutions;
return new EditSolutionsEuiState(_entityManager.GetNetEntity(Target), solutions);
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);
} }
} }
} }

View File

@@ -1,10 +1,10 @@
using Content.Server.Animals.Systems; using Content.Server.Animals.Systems;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Server.Animals.Components namespace Content.Server.Animals.Components
/// <summary> /// <summary>
@@ -21,10 +21,16 @@ namespace Content.Server.Animals.Components
public ProtoId<ReagentPrototype> ReagentId = "Milk"; public ProtoId<ReagentPrototype> ReagentId = "Milk";
/// <summary> /// <summary>
/// The solution to add reagent to. /// The name of <see cref="Solution"/>.
/// </summary> /// </summary>
[DataField, ViewVariables(VVAccess.ReadOnly)] [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> /// <summary>
/// The amount of reagent to be generated on update. /// The amount of reagent to be generated on update.

View File

@@ -1,4 +1,5 @@
using Content.Server.Animals.Systems; using Content.Server.Animals.Systems;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -20,10 +21,16 @@ public sealed partial class WoolyComponent : Component
public ProtoId<ReagentPrototype> ReagentId = "Fiber"; public ProtoId<ReagentPrototype> ReagentId = "Fiber";
/// <summary> /// <summary>
/// The solution to add reagent to. /// The name of <see cref="Solution"/>.
/// </summary> /// </summary>
[DataField, ViewVariables(VVAccess.ReadOnly)] [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> /// <summary>
/// The amount of reagent to be generated on update. /// The amount of reagent to be generated on update.

View File

@@ -1,7 +1,7 @@
using Content.Server.Animals.Components; using Content.Server.Animals.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.IdentityManagement; using Content.Shared.IdentityManagement;
using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Systems;
@@ -61,11 +61,11 @@ internal sealed class UdderSystem : EntitySystem
_hunger.ModifyHunger(uid, -udder.HungerUsage, hunger); _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; continue;
//TODO: toxins from bloodstream !? //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); _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) if (args.Cancelled || args.Handled || args.Args.Used == null)
return; 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; 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; return;
args.Handled = true; args.Handled = true;
var quantity = solution.Volume; var quantity = solution.Volume;
if (quantity == 0) 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; return;
} }
if (quantity > targetSolution.AvailableVolume) if (quantity > targetSolution.AvailableVolume)
quantity = targetSolution.AvailableVolume; quantity = targetSolution.AvailableVolume;
var split = _solutionContainerSystem.SplitSolution(uid, solution, quantity); var split = _solutionContainerSystem.SplitSolution(entity.Comp.Solution.Value, quantity);
_solutionContainerSystem.TryAddSolution(args.Args.Used.Value, targetSolution, split); _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); 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 || if (args.Using == null ||
!args.CanInteract || !args.CanInteract ||
!EntityManager.HasComponent<RefillableSolutionComponent>(args.Using.Value)) !EntityManager.HasComponent<RefillableSolutionComponent>(args.Using.Value))
return; return;
var uid = entity.Owner;
var user = args.User;
var used = args.Using.Value;
AlternativeVerb verb = new() AlternativeVerb verb = new()
{ {
Act = () => Act = () =>
{ {
AttemptMilk(uid, args.User, args.Using.Value); AttemptMilk(uid, user, used);
}, },
Text = Loc.GetString("udder-system-verb-milk"), Text = Loc.GetString("udder-system-verb-milk"),
Priority = 2 Priority = 2

View File

@@ -1,6 +1,6 @@
using Content.Server.Animals.Components; using Content.Server.Animals.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Nutrition; using Content.Server.Nutrition;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Systems;
using Content.Shared.Nutrition.Components; using Content.Shared.Nutrition.Components;
using Content.Shared.Nutrition.EntitySystems; using Content.Shared.Nutrition.EntitySystems;
@@ -52,10 +52,10 @@ public sealed class WoolySystem : EntitySystem
_hunger.ModifyHunger(uid, -wooly.HungerUsage, hunger); _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; continue;
_solutionContainer.TryAddReagent(uid, solution, wooly.ReagentId, wooly.Quantity, out _); _solutionContainer.TryAddReagent(wooly.Solution.Value, wooly.ReagentId, wooly.Quantity, out _);
} }
} }

View File

@@ -1,4 +1,5 @@
using Content.Server.Anomaly.Effects; using Content.Server.Anomaly.Effects;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -85,10 +86,17 @@ public sealed partial class ReagentProducerAnomalyComponent : Component
/// </summary> /// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)] [DataField, ViewVariables(VVAccess.ReadWrite)]
public ProtoId<ReagentPrototype> ProducingReagent = "Water"; public ProtoId<ReagentPrototype> ProducingReagent = "Water";
/// <summary> /// <summary>
/// Solution name where the substance is generated /// Solution name where the substance is generated
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
[DataField("solution")] [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;
} }

View File

@@ -1,8 +1,8 @@
using System.Linq;
using Content.Server.Anomaly.Components; using Content.Server.Anomaly.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Anomaly.Components; using Content.Shared.Anomaly.Components;
using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems; using System.Linq;
namespace Content.Server.Anomaly.Effects; namespace Content.Server.Anomaly.Effects;
/// <summary> /// <summary>
@@ -27,41 +27,40 @@ public sealed class InjectionAnomalySystem : EntitySystem
_injectableQuery = GetEntityQuery<InjectableSolutionComponent>(); _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; return;
//We get all the entity in the radius into which the reagent will be injected. //We get all the entity in the radius into which the reagent will be injected.
var xformQuery = GetEntityQuery<TransformComponent>(); var xformQuery = GetEntityQuery<TransformComponent>();
var xform = xformQuery.GetComponent(uid); var xform = xformQuery.GetComponent(entity);
var allEnts = _lookup.GetEntitiesInRange<InjectableSolutionComponent>(xform.MapPosition, injectRadius) var allEnts = _lookup.GetEntitiesInRange<InjectableSolutionComponent>(xform.MapPosition, injectRadius)
.Select(x => x.Owner).ToList(); .Select(x => x.Owner).ToList();
//for each matching entity found //for each matching entity found
foreach (var ent in allEnts) foreach (var ent in allEnts)
{ {
if (!_solutionContainer.TryGetInjectableSolution(ent, out var injectable)) if (!_solutionContainer.TryGetInjectableSolution(ent, out var injectable, out _))
continue; continue;
if (_injectableQuery.TryGetComponent(ent, out var injEnt)) if (_injectableQuery.TryGetComponent(ent, out var injEnt))
{ {
var buffer = sol; _solutionContainer.TryTransferSolution(injectable.Value, sol, maxInject);
_solutionContainer.TryTransferSolution(ent, injectable, buffer, maxInject);
//Spawn Effect //Spawn Effect
var uidXform = Transform(ent); var uidXform = Transform(ent);
Spawn(component.VisualEffectPrototype, uidXform.Coordinates); Spawn(entity.Comp.VisualEffectPrototype, uidXform.Coordinates);
} }
} }
} }
} }

View File

@@ -1,7 +1,7 @@
using Content.Server.Anomaly.Components; using Content.Server.Anomaly.Components;
using Content.Shared.Anomaly.Components; using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Chemistry.EntitySystems;
using Content.Server.Fluids.EntitySystems; using Content.Server.Fluids.EntitySystems;
using Content.Shared.Anomaly.Components;
namespace Content.Server.Anomaly.Effects; namespace Content.Server.Anomaly.Effects;
@@ -19,21 +19,21 @@ public sealed class PuddleCreateAnomalySystem : EntitySystem
SubscribeLocalEvent<PuddleCreateAnomalyComponent, AnomalySupercriticalEvent>(OnSupercritical, before: new[] { typeof(InjectionAnomalySystem) }); 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; return;
var xform = Transform(uid); var xform = Transform(entity.Owner);
var puddleSol = _solutionContainer.SplitSolution(uid, sol, component.MaxPuddleSize * args.Severity); var puddleSol = _solutionContainer.SplitSolution(sol.Value, entity.Comp.MaxPuddleSize * args.Severity);
_puddle.TrySplashSpillAt(uid, xform.Coordinates, puddleSol, out _); _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; return;
var buffer = sol;
var xform = Transform(uid); var xform = Transform(entity.Owner);
_puddle.TrySpillAt(xform.Coordinates, buffer, out _); _puddle.TrySpillAt(xform.Coordinates, sol, out _);
} }
} }

View File

@@ -1,12 +1,12 @@
using Content.Server.Anomaly.Components; using Content.Server.Anomaly.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Anomaly.Components; using Content.Shared.Anomaly.Components;
using Robust.Shared.Random;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
using Robust.Shared.Prototypes;
using Content.Shared.Sprite; using Content.Shared.Sprite;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Server.Anomaly.Effects; namespace Content.Server.Anomaly.Effects;
@@ -42,17 +42,17 @@ public sealed class ReagentProducerAnomalySystem : EntitySystem
SubscribeLocalEvent<ReagentProducerAnomalyComponent, MapInitEvent>(OnMapInit); 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) 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); var reagent = GetRandomReagentType(entity, severity);
component.ProducingReagent = reagent; entity.Comp.ProducingReagent = reagent;
_audio.PlayPvs(component.ChangeSound, uid); _audio.PlayPvs(entity.Comp.ChangeSound, entity);
} }
//reagent realtime generation //reagent realtime generation
@@ -68,7 +68,7 @@ public sealed class ReagentProducerAnomalySystem : EntitySystem
if (component.AccumulatedFrametime < component.UpdateInterval) if (component.AccumulatedFrametime < component.UpdateInterval)
continue; continue;
if (!_solutionContainer.TryGetSolution(uid, component.Solution, out var producerSol)) if (!_solutionContainer.ResolveSolution(uid, component.SolutionName, ref component.Solution, out var producerSolution))
continue; continue;
Solution newSol = new(); Solution newSol = new();
@@ -76,7 +76,7 @@ public sealed class ReagentProducerAnomalySystem : EntitySystem
if (anomaly.Severity >= 0.97) reagentProducingAmount *= component.SupercriticalReagentProducingModifier; if (anomaly.Severity >= 0.97) reagentProducingAmount *= component.SupercriticalReagentProducingModifier;
newSol.AddReagent(component.ProducingReagent, reagentProducingAmount); 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; 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. // and nothing worked out for me. So for now it will be like this.
if (component.NeedRecolor) if (component.NeedRecolor)
{ {
var color = producerSol.GetColor(_prototypeManager); var color = producerSolution.GetColor(_prototypeManager);
_light.SetColor(uid, color); _light.SetColor(uid, color);
if (TryComp<RandomSpriteComponent>(uid, out var randomSprite)) 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. // 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. // 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. // 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 //Category Weight Randomization
var currentWeightDangerous = MathHelper.Lerp(component.WeightSpreadDangerous.X, component.WeightSpreadDangerous.Y, severity); var currentWeightDangerous = MathHelper.Lerp(entity.Comp.WeightSpreadDangerous.X, entity.Comp.WeightSpreadDangerous.Y, severity);
var currentWeightFun = MathHelper.Lerp(component.WeightSpreadFun.X, component.WeightSpreadFun.Y, severity); var currentWeightFun = MathHelper.Lerp(entity.Comp.WeightSpreadFun.X, entity.Comp.WeightSpreadFun.Y, severity);
var currentWeightUseful = MathHelper.Lerp(component.WeightSpreadUseful.X, component.WeightSpreadUseful.Y, severity); var currentWeightUseful = MathHelper.Lerp(entity.Comp.WeightSpreadUseful.X, entity.Comp.WeightSpreadUseful.Y, severity);
var sumWeight = currentWeightDangerous + currentWeightFun + currentWeightUseful; var sumWeight = currentWeightDangerous + currentWeightFun + currentWeightUseful;
var rnd = _random.NextFloat(0f, sumWeight); var rnd = _random.NextFloat(0f, sumWeight);
//Dangerous //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; return reagent;
} }
else rnd -= currentWeightDangerous; else rnd -= currentWeightDangerous;
//Fun //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; return reagent;
} }
else rnd -= currentWeightFun; else rnd -= currentWeightFun;
//Useful //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; return reagent;
} }
//We should never end up here. //We should never end up here.

View File

@@ -1,4 +1,5 @@
using Content.Server.Atmos.Piping.Unary.EntitySystems; using Content.Server.Atmos.Piping.Unary.EntitySystems;
using Content.Shared.Chemistry.Components;
namespace Content.Server.Atmos.Piping.Unary.Components; namespace Content.Server.Atmos.Piping.Unary.Components;
@@ -21,6 +22,12 @@ public sealed partial class GasCondenserComponent : Component
[DataField] [DataField]
public string SolutionId = "tank"; public string SolutionId = "tank";
/// <summary>
/// The solution that gases are condensed into.
/// </summary>
[DataField]
public Entity<SolutionComponent>? Solution = null;
/// <summary> /// <summary>
/// For a condenser, how many U of reagents are given per each mole of gas. /// For a condenser, how many U of reagents are given per each mole of gas.
/// </summary> /// </summary>

View File

@@ -5,10 +5,11 @@ using Content.Server.NodeContainer;
using Content.Server.NodeContainer.EntitySystems; using Content.Server.NodeContainer.EntitySystems;
using Content.Server.NodeContainer.Nodes; using Content.Server.NodeContainer.Nodes;
using Content.Server.Power.Components; using Content.Server.Power.Components;
using Content.Shared.Atmos;
using JetBrains.Annotations;
using Content.Server.Power.EntitySystems; using Content.Server.Power.EntitySystems;
using Content.Shared.Atmos;
using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.FixedPoint;
using JetBrains.Annotations;
namespace Content.Server.Atmos.Piping.Unary.EntitySystems; 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 AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly PowerReceiverSystem _power = default!; [Dependency] private readonly PowerReceiverSystem _power = default!;
[Dependency] private readonly NodeContainerSystem _nodeContainer = default!; [Dependency] private readonly NodeContainerSystem _nodeContainer = default!;
[Dependency] private readonly SolutionContainerSystem _solution = default!; [Dependency] private readonly SharedSolutionContainerSystem _solution = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -27,12 +28,12 @@ public sealed class GasCondenserSystem : EntitySystem
SubscribeLocalEvent<GasCondenserComponent, AtmosDeviceUpdateEvent>(OnCondenserUpdated); 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)) if (!(_power.IsPowered(entity) && TryComp<ApcPowerReceiverComponent>(entity, out var receiver))
|| !TryComp<NodeContainerComponent>(uid, out var nodeContainer) || !TryComp<NodeContainerComponent>(entity, out var nodeContainer)
|| !_nodeContainer.TryGetNode(nodeContainer, component.Inlet, out PipeNode? inlet) || !_nodeContainer.TryGetNode(nodeContainer, entity.Comp.Inlet, out PipeNode? inlet)
|| !_solution.TryGetSolution(uid, component.SolutionId, out var solution)) || !_solution.ResolveSolution(entity.Owner, entity.Comp.SolutionId, ref entity.Comp.Solution, out var solution))
{ {
return; return;
} }
@@ -48,18 +49,21 @@ public sealed class GasCondenserSystem : EntitySystem
if (moles <= 0) if (moles <= 0)
continue; continue;
if (_atmosphereSystem.GetGas(i).Reagent is not {} gasReagent) if (_atmosphereSystem.GetGas(i).Reagent is not { } gasReagent)
continue; continue;
var moleToReagentMultiplier = component.MolesToReagentMultiplier; var moleToReagentMultiplier = entity.Comp.MolesToReagentMultiplier;
var amount = moles * moleToReagentMultiplier; var amount = FixedPoint2.Min(FixedPoint2.New(moles * moleToReagentMultiplier), solution.AvailableVolume);
if (amount <= 0)
if (_solution.TryAddReagent(uid, solution, gasReagent, amount, out var remaining))
continue; continue;
solution.AddReagent(gasReagent, amount);
// if we have leftover reagent, then convert it back to moles and put it back in the mixture. // 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) public float NumberOfMolesToConvert(ApcPowerReceiverComponent comp, GasMixture mix, float dt)

View File

@@ -34,52 +34,52 @@ namespace Content.Server.Body.Components
/// <summary> /// <summary>
/// How much should bleeding should be reduced every update interval? /// How much should bleeding should be reduced every update interval?
/// </summary> /// </summary>
[DataField("bleedReductionAmount")] [DataField]
public float BleedReductionAmount = 1.0f; public float BleedReductionAmount = 1.0f;
/// <summary> /// <summary>
/// How high can <see cref="BleedAmount"/> go? /// How high can <see cref="BleedAmount"/> go?
/// </summary> /// </summary>
[DataField("maxBleedAmount")] [DataField]
public float MaxBleedAmount = 10.0f; public float MaxBleedAmount = 10.0f;
/// <summary> /// <summary>
/// What percentage of current blood is necessary to avoid dealing blood loss damage? /// What percentage of current blood is necessary to avoid dealing blood loss damage?
/// </summary> /// </summary>
[DataField("bloodlossThreshold")] [DataField]
public float BloodlossThreshold = 0.9f; public float BloodlossThreshold = 0.9f;
/// <summary> /// <summary>
/// The base bloodloss damage to be incurred if below <see cref="BloodlossThreshold"/> /// The base bloodloss damage to be incurred if below <see cref="BloodlossThreshold"/>
/// The default values are defined per mob/species in YML. /// The default values are defined per mob/species in YML.
/// </summary> /// </summary>
[DataField("bloodlossDamage", required: true)] [DataField(required: true)]
public DamageSpecifier BloodlossDamage = new(); public DamageSpecifier BloodlossDamage = new();
/// <summary> /// <summary>
/// The base bloodloss damage to be healed if above <see cref="BloodlossThreshold"/> /// The base bloodloss damage to be healed if above <see cref="BloodlossThreshold"/>
/// The default values are defined per mob/species in YML. /// The default values are defined per mob/species in YML.
/// </summary> /// </summary>
[DataField("bloodlossHealDamage", required: true)] [DataField(required: true)]
public DamageSpecifier BloodlossHealDamage = new(); public DamageSpecifier BloodlossHealDamage = new();
/// <summary> /// <summary>
/// How frequently should this bloodstream update, in seconds? /// How frequently should this bloodstream update, in seconds?
/// </summary> /// </summary>
[DataField("updateInterval")] [DataField]
public float UpdateInterval = 3.0f; public float UpdateInterval = 3.0f;
// TODO shouldn't be hardcoded, should just use some organ simulation like bone marrow or smth. // TODO shouldn't be hardcoded, should just use some organ simulation like bone marrow or smth.
/// <summary> /// <summary>
/// How much reagent of blood should be restored each update interval? /// How much reagent of blood should be restored each update interval?
/// </summary> /// </summary>
[DataField("bloodRefreshAmount")] [DataField]
public float BloodRefreshAmount = 1.0f; public float BloodRefreshAmount = 1.0f;
/// <summary> /// <summary>
/// How much blood needs to be in the temporary solution in order to create a puddle? /// How much blood needs to be in the temporary solution in order to create a puddle?
/// </summary> /// </summary>
[DataField("bleedPuddleThreshold")] [DataField]
public FixedPoint2 BleedPuddleThreshold = 1.0f; public FixedPoint2 BleedPuddleThreshold = 1.0f;
/// <summary> /// <summary>
@@ -89,19 +89,19 @@ namespace Content.Server.Body.Components
/// <remarks> /// <remarks>
/// For example, piercing damage is increased while poison damage is nullified entirely. /// For example, piercing damage is increased while poison damage is nullified entirely.
/// </remarks> /// </remarks>
[DataField("damageBleedModifiers", customTypeSerializer:typeof(PrototypeIdSerializer<DamageModifierSetPrototype>))] [DataField(customTypeSerializer:typeof(PrototypeIdSerializer<DamageModifierSetPrototype>))]
public string DamageBleedModifiers = "BloodlossHuman"; public string DamageBleedModifiers = "BloodlossHuman";
/// <summary> /// <summary>
/// The sound to be played when a weapon instantly deals blood loss damage. /// The sound to be played when a weapon instantly deals blood loss damage.
/// </summary> /// </summary>
[DataField("instantBloodSound")] [DataField]
public SoundSpecifier InstantBloodSound = new SoundCollectionSpecifier("blood"); public SoundSpecifier InstantBloodSound = new SoundCollectionSpecifier("blood");
/// <summary> /// <summary>
/// The sound to be played when some damage actually heals bleeding rather than starting it. /// The sound to be played when some damage actually heals bleeding rather than starting it.
/// </summary> /// </summary>
[DataField("bloodHealedSound")] [DataField]
public SoundSpecifier BloodHealedSound = new SoundPathSpecifier("/Audio/Effects/lightburn.ogg"); public SoundSpecifier BloodHealedSound = new SoundPathSpecifier("/Audio/Effects/lightburn.ogg");
// TODO probably damage bleed thresholds. // TODO probably damage bleed thresholds.
@@ -109,14 +109,14 @@ namespace Content.Server.Body.Components
/// <summary> /// <summary>
/// Max volume of internal chemical solution storage /// Max volume of internal chemical solution storage
/// </summary> /// </summary>
[DataField("chemicalMaxVolume")] [DataField]
public FixedPoint2 ChemicalMaxVolume = FixedPoint2.New(250); public FixedPoint2 ChemicalMaxVolume = FixedPoint2.New(250);
/// <summary> /// <summary>
/// Max volume of internal blood storage, /// Max volume of internal blood storage,
/// and starting level of blood. /// and starting level of blood.
/// </summary> /// </summary>
[DataField("bloodMaxVolume")] [DataField]
public FixedPoint2 BloodMaxVolume = FixedPoint2.New(300); public FixedPoint2 BloodMaxVolume = FixedPoint2.New(300);
/// <summary> /// <summary>
@@ -125,29 +125,40 @@ namespace Content.Server.Body.Components
/// <remarks> /// <remarks>
/// Slime-people might use slime as their blood or something like that. /// Slime-people might use slime as their blood or something like that.
/// </remarks> /// </remarks>
[DataField("bloodReagent")] [DataField]
public string BloodReagent = "Blood"; public string BloodReagent = "Blood";
/// <summary> /// <summary>Name/Key that <see cref="BloodSolution"/> is indexed by.</summary>
/// Internal solution for reagent storage [DataField]
/// </summary> public string BloodSolutionName = DefaultBloodSolutionName;
[ViewVariables(VVAccess.ReadWrite)]
[Access(typeof(BloodstreamSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends /// <summary>Name/Key that <see cref="ChemicalSolution"/> is indexed by.</summary>
public Solution ChemicalSolution = default!; [DataField]
public string ChemicalSolutionName = DefaultChemicalsSolutionName;
/// <summary>Name/Key that <see cref="TemporarySolution"/> is indexed by.</summary>
[DataField]
public string BloodTemporarySolutionName = DefaultBloodTemporarySolutionName;
/// <summary> /// <summary>
/// Internal solution for blood storage /// Internal solution for blood storage
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite)] [DataField]
public Solution BloodSolution = default!; public Entity<SolutionComponent>? BloodSolution = null;
/// <summary>
/// Internal solution for reagent storage
/// </summary>
[DataField]
public Entity<SolutionComponent>? ChemicalSolution = null;
/// <summary> /// <summary>
/// Temporary blood solution. /// Temporary blood solution.
/// When blood is lost, it goes to this solution, and when this /// 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. /// solution hits a certain cap, the blood is actually spilled as a puddle.
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite)] [DataField]
public Solution BloodTemporarySolution = default!; public Entity<SolutionComponent>? TemporarySolution = null;
/// <summary> /// <summary>
/// Variable that stores the amount of status time added by having a low blood level. /// Variable that stores the amount of status time added by having a low blood level.

View File

@@ -8,7 +8,7 @@ namespace Content.Server.Body.Components;
[RegisterComponent, Access(typeof(LungSystem))] [RegisterComponent, Access(typeof(LungSystem))]
public sealed partial class LungComponent : Component public sealed partial class LungComponent : Component
{ {
[DataField("air")] [DataField]
[Access(typeof(LungSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends [Access(typeof(LungSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends
public GasMixture Air { get; set; } = new() public GasMixture Air { get; set; } = new()
{ {
@@ -16,7 +16,15 @@ public sealed partial class LungComponent : Component
Temperature = Atmospherics.NormalBodyTemperature Temperature = Atmospherics.NormalBodyTemperature
}; };
[ViewVariables] /// <summary>
[Access(typeof(LungSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends /// The name/key of the solution on this entity which these lungs act on.
public Solution LungSolution = default!; /// </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;
} }

View File

@@ -18,7 +18,7 @@ namespace Content.Server.Body.Components
/// How often to metabolize reagents, in seconds. /// How often to metabolize reagents, in seconds.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[DataField("updateFrequency")] [DataField]
public float UpdateFrequency = 1.0f; public float UpdateFrequency = 1.0f;
/// <summary> /// <summary>
@@ -33,13 +33,13 @@ namespace Content.Server.Body.Components
/// <remarks> /// <remarks>
/// Most things will use the parent entity (bloodstream). /// Most things will use the parent entity (bloodstream).
/// </remarks> /// </remarks>
[DataField("solutionOnBody")] [DataField]
public bool SolutionOnBody = true; public bool SolutionOnBody = true;
/// <summary> /// <summary>
/// List of metabolizer types that this organ is. ex. Human, Slime, Felinid, w/e. /// List of metabolizer types that this organ is. ex. Human, Slime, Felinid, w/e.
/// </summary> /// </summary>
[DataField("metabolizerTypes", customTypeSerializer:typeof(PrototypeIdHashSetSerializer<MetabolizerTypePrototype>))] [DataField(customTypeSerializer:typeof(PrototypeIdHashSetSerializer<MetabolizerTypePrototype>))]
[Access(typeof(MetabolizerSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends [Access(typeof(MetabolizerSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends
public HashSet<string>? MetabolizerTypes = null; public HashSet<string>? MetabolizerTypes = null;
@@ -47,7 +47,7 @@ namespace Content.Server.Body.Components
/// Should this metabolizer remove chemicals that have no metabolisms defined? /// Should this metabolizer remove chemicals that have no metabolisms defined?
/// As a stop-gap, basically. /// As a stop-gap, basically.
/// </summary> /// </summary>
[DataField("removeEmpty")] [DataField]
public bool RemoveEmpty = false; public bool RemoveEmpty = false;
/// <summary> /// <summary>
@@ -72,7 +72,7 @@ namespace Content.Server.Body.Components
[DataDefinition] [DataDefinition]
public sealed partial class MetabolismGroupEntry public sealed partial class MetabolismGroupEntry
{ {
[DataField("id", required: true, customTypeSerializer:typeof(PrototypeIdSerializer<MetabolismGroupPrototype>))] [DataField(required: true, customTypeSerializer:typeof(PrototypeIdSerializer<MetabolismGroupPrototype>))]
public string Id = default!; public string Id = default!;
[DataField("rateModifier")] [DataField("rateModifier")]

View File

@@ -2,7 +2,6 @@
using Content.Server.Nutrition.EntitySystems; using Content.Server.Nutrition.EntitySystems;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint;
using Content.Shared.Whitelist; using Content.Shared.Whitelist;
namespace Content.Server.Body.Components namespace Content.Server.Body.Components
@@ -15,26 +14,32 @@ namespace Content.Server.Body.Components
/// <summary> /// <summary>
/// How fast should this component update, in seconds? /// How fast should this component update, in seconds?
/// </summary> /// </summary>
[DataField("updateInterval")] [DataField]
public float UpdateInterval = 1.0f; 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> /// <summary>
/// What solution should this stomach push reagents into, on the body? /// What solution should this stomach push reagents into, on the body?
/// </summary> /// </summary>
[DataField("bodySolutionName")] [DataField]
public string BodySolutionName = BloodstreamComponent.DefaultChemicalsSolutionName; public string BodySolutionName = BloodstreamComponent.DefaultChemicalsSolutionName;
/// <summary> /// <summary>
/// Time in seconds between reagents being ingested and them being /// Time in seconds between reagents being ingested and them being
/// transferred to <see cref="BloodstreamComponent"/> /// transferred to <see cref="BloodstreamComponent"/>
/// </summary> /// </summary>
[DataField("digestionDelay")] [DataField]
public float DigestionDelay = 20; public float DigestionDelay = 20;
/// <summary> /// <summary>
/// A whitelist for what special-digestible-required foods this stomach is capable of eating. /// A whitelist for what special-digestible-required foods this stomach is capable of eating.
/// </summary> /// </summary>
[DataField("specialDigestible")] [DataField]
public EntityWhitelist? SpecialDigestible = null; public EntityWhitelist? SpecialDigestible = null;
/// <summary> /// <summary>

View File

@@ -1,6 +1,8 @@
using Content.Server.Body.Components; using Content.Server.Body.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Chemistry.ReactionEffects; using Content.Server.Chemistry.ReactionEffects;
using Content.Server.Fluids.EntitySystems; using Content.Server.Fluids.EntitySystems;
using Content.Server.Forensics;
using Content.Server.HealthExaminable; using Content.Server.HealthExaminable;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Shared.Alert; using Content.Shared.Alert;
@@ -16,13 +18,9 @@ using Content.Shared.Mobs.Systems;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Rejuvenate; using Content.Shared.Rejuvenate;
using Content.Shared.Speech.EntitySystems; using Content.Shared.Speech.EntitySystems;
using Robust.Server.GameObjects; using Robust.Server.Audio;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; 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; namespace Content.Server.Body.Systems;
@@ -51,14 +49,13 @@ public sealed class BloodstreamSystem : EntitySystem
SubscribeLocalEvent<BloodstreamComponent, BeingGibbedEvent>(OnBeingGibbed); SubscribeLocalEvent<BloodstreamComponent, BeingGibbedEvent>(OnBeingGibbed);
SubscribeLocalEvent<BloodstreamComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier); SubscribeLocalEvent<BloodstreamComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
SubscribeLocalEvent<BloodstreamComponent, ReactionAttemptEvent>(OnReactionAttempt); SubscribeLocalEvent<BloodstreamComponent, ReactionAttemptEvent>(OnReactionAttempt);
SubscribeLocalEvent<BloodstreamComponent, SolutionRelayEvent<ReactionAttemptEvent>>(OnReactionAttempt);
SubscribeLocalEvent<BloodstreamComponent, RejuvenateEvent>(OnRejuvenate); 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 if (args.Cancelled)
&& args.Solution.Name != BloodstreamComponent.DefaultChemicalsSolutionName
&& args.Solution.Name != BloodstreamComponent.DefaultBloodTemporarySolutionName)
return; return;
foreach (var effect in args.Reaction.Effects) 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 CreateEntityReactionEffect: // Prevent entities from spawning in the bloodstream
case AreaReactionEffect: // No spontaneous smoke or foam leaking out of blood vessels. case AreaReactionEffect: // No spontaneous smoke or foam leaking out of blood vessels.
args.Cancel(); args.Cancelled = true;
return; return;
} }
} }
@@ -81,6 +78,16 @@ public sealed class BloodstreamSystem : EntitySystem
// Having cheese-clots form in your veins can't be good for you. // 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) public override void Update(float frameTime)
{ {
base.Update(frameTime); base.Update(frameTime);
@@ -95,8 +102,11 @@ public sealed class BloodstreamSystem : EntitySystem
bloodstream.AccumulatedFrametime -= bloodstream.UpdateInterval; 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. // 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); 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); var chemicalSolution = _solutionContainerSystem.EnsureSolution(entity.Owner, entity.Comp.ChemicalSolutionName);
component.BloodSolution = _solutionContainerSystem.EnsureSolution(uid, BloodstreamComponent.DefaultBloodSolutionName); var bloodSolution = _solutionContainerSystem.EnsureSolution(entity.Owner, entity.Comp.BloodSolutionName);
component.BloodTemporarySolution = _solutionContainerSystem.EnsureSolution(uid, BloodstreamComponent.DefaultBloodTemporarySolutionName); var tempSolution = _solutionContainerSystem.EnsureSolution(entity.Owner, entity.Comp.BloodTemporarySolutionName);
component.ChemicalSolution.MaxVolume = component.ChemicalMaxVolume; chemicalSolution.MaxVolume = entity.Comp.ChemicalMaxVolume;
component.BloodSolution.MaxVolume = component.BloodMaxVolume; bloodSolution.MaxVolume = entity.Comp.BloodMaxVolume;
component.BloodTemporarySolution.MaxVolume = component.BleedPuddleThreshold * 4; // give some leeway, for chemstream as well tempSolution.MaxVolume = entity.Comp.BleedPuddleThreshold * 4; // give some leeway, for chemstream as well
// Fill blood solution with BLOOD // Fill blood solution with BLOOD
_solutionContainerSystem.TryAddReagent(uid, component.BloodSolution, component.BloodReagent, bloodSolution.AddReagent(entity.Comp.BloodReagent, entity.Comp.BloodMaxVolume - bloodSolution.Volume);
component.BloodMaxVolume, out _);
} }
private void OnDamageChanged(EntityUid uid, BloodstreamComponent component, DamageChangedEvent args) private void OnDamageChanged(EntityUid uid, BloodstreamComponent component, DamageChangedEvent args)
@@ -250,34 +259,45 @@ public sealed class BloodstreamSystem : EntitySystem
component.AccumulatedFrametime = component.UpdateInterval; 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); TryModifyBleedAmount(entity.Owner, -entity.Comp.BleedAmount, entity.Comp);
TryModifyBloodLevel(uid, component.BloodSolution.AvailableVolume, component);
_solutionContainerSystem.RemoveAllSolution(uid, component.ChemicalSolution); 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> /// <summary>
/// Attempt to transfer provided solution to internal solution. /// Attempt to transfer provided solution to internal solution.
/// </summary> /// </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)) if (!Resolve(uid, ref component, false))
return 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)) if (!Resolve(uid, ref component, false))
return 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) 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)) if (!Resolve(uid, ref component))
return 0.0f; 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) 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)) if (!Resolve(uid, ref component, false))
return false; return false;
if (!_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution))
return false;
if (amount >= 0) 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, // Removal is more involved,
// since we also wanna handle moving it to the temporary solution // since we also wanna handle moving it to the temporary solution
// and then spilling it if necessary. // and then spilling it if necessary.
var newSol = component.BloodSolution.SplitSolution(-amount); var newSol = _solutionContainerSystem.SplitSolution(component.BloodSolution.Value, -amount);
component.BloodTemporarySolution.AddSolution(newSol, _prototypeManager);
if (component.BloodTemporarySolution.Volume > component.BleedPuddleThreshold) if (!_solutionContainerSystem.ResolveSolution(uid, component.BloodTemporarySolutionName, ref component.TemporarySolution, out var tempSolution))
return true;
tempSolution.AddSolution(newSol, _prototypeManager);
if (tempSolution.Volume > component.BleedPuddleThreshold)
{ {
// Pass some of the chemstream into the spilled blood. // Pass some of the chemstream into the spilled blood.
var temp = component.ChemicalSolution.SplitSolution(component.BloodTemporarySolution.Volume / 10); if (_solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution))
component.BloodTemporarySolution.AddSolution(temp, _prototypeManager); {
if (_puddleSystem.TrySpillAt(uid, component.BloodTemporarySolution, out var puddleUid, false)) 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); _forensicsSystem.TransferDna(puddleUid, uid, false);
} }
component.BloodTemporarySolution.RemoveAllSolution(); tempSolution.RemoveAllSolution();
} }
_solutionContainerSystem.UpdateChemicals(component.TemporarySolution.Value);
return true; return true;
} }
@@ -363,16 +399,28 @@ public sealed class BloodstreamSystem : EntitySystem
if (!Resolve(uid, ref component)) if (!Resolve(uid, ref component))
return; return;
var max = component.BloodSolution.MaxVolume + component.BloodTemporarySolution.MaxVolume + var tempSol = new Solution();
component.ChemicalSolution.MaxVolume;
var tempSol = new Solution() { MaxVolume = max };
tempSol.AddSolution(component.BloodSolution, _prototypeManager); if (_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution, out var bloodSolution))
component.BloodSolution.RemoveAllSolution(); {
tempSol.AddSolution(component.BloodTemporarySolution, _prototypeManager); tempSol.MaxVolume += bloodSolution.MaxVolume;
component.BloodTemporarySolution.RemoveAllSolution(); tempSol.AddSolution(bloodSolution, _prototypeManager);
tempSol.AddSolution(component.ChemicalSolution, _prototypeManager); _solutionContainerSystem.RemoveAllSolution(component.BloodSolution.Value);
component.ChemicalSolution.RemoveAllSolution(); }
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)) if (_puddleSystem.TrySpillAt(uid, tempSol, out var puddleUid))
{ {
@@ -388,13 +436,20 @@ public sealed class BloodstreamSystem : EntitySystem
if (!Resolve(uid, ref component, false)) if (!Resolve(uid, ref component, false))
return; return;
if(reagent == component.BloodReagent) if (reagent == component.BloodReagent)
return; 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.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 _);
} }
} }

View File

@@ -1,8 +1,8 @@
using Content.Server.Atmos.Components; using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.EntitySystems;
using Content.Server.Body.Components; using Content.Server.Body.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Clothing; using Content.Shared.Clothing;
using Content.Shared.Inventory.Events; 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); var solution = _solutionContainerSystem.EnsureSolution(entity.Owner, entity.Comp.SolutionName);
component.LungSolution.MaxVolume = 100.0f; solution.MaxVolume = 100.0f;
component.LungSolution.CanReact = false; // No dexalin lungs solution.CanReact = false; // No dexalin lungs
} }
private void OnMaskToggled(Entity<BreathToolComponent> ent, ref ItemMaskToggledEvent args) 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) 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>()) foreach (var gas in Enum.GetValues<Gas>())
{ {
var i = (int) gas; var i = (int) gas;
@@ -81,11 +84,13 @@ public sealed class LungSystem : EntitySystem
if (reagent == null) continue; if (reagent == null) continue;
var amount = moles * Atmospherics.BreathMolesToReagentMultiplier; 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, // We don't remove the gas from the lung mix,
// that's the responsibility of whatever gas is being metabolized. // that's the responsibility of whatever gas is being metabolized.
// Most things will just want to exhale again. // Most things will just want to exhale again.
} }
_solutionContainerSystem.UpdateChemicals(lung.Solution.Value);
} }
} }

View File

@@ -1,9 +1,9 @@
using Content.Server.Body.Components; using Content.Server.Body.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Administration.Logs; using Content.Shared.Administration.Logs;
using Content.Shared.Body.Organ; using Content.Shared.Body.Organ;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
@@ -37,15 +37,15 @@ namespace Content.Server.Body.Systems
SubscribeLocalEvent<MetabolizerComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier); 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 // First step is get the solution we actually care about
Solution? solution = null; Solution? solution = null;
Entity<SolutionComponent>? soln = default!;
EntityUid? solutionEntityUid = null; EntityUid? solutionEntityUid = null;
SolutionContainerManagerComponent? manager = null; SolutionContainerManagerComponent? manager = null;
@@ -106,7 +107,7 @@ namespace Content.Server.Body.Systems
if (!_solutionQuery.Resolve(body, ref manager, false)) if (!_solutionQuery.Resolve(body, ref manager, false))
return; return;
_solutionContainerSystem.TryGetSolution(body, meta.SolutionName, out solution, manager); _solutionContainerSystem.TryGetSolution((body, manager), meta.SolutionName, out soln, out solution);
solutionEntityUid = body; solutionEntityUid = body;
} }
} }
@@ -115,11 +116,11 @@ namespace Content.Server.Body.Systems
if (!_solutionQuery.Resolve(uid, ref manager, false)) if (!_solutionQuery.Resolve(uid, ref manager, false))
return; return;
_solutionContainerSystem.TryGetSolution(uid, meta.SolutionName, out solution, manager); _solutionContainerSystem.TryGetSolution((uid, manager), meta.SolutionName, out soln, out solution);
solutionEntityUid = uid; 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; return;
// randomize the reagent list so we don't have any weird quirks // randomize the reagent list so we don't have any weird quirks
@@ -138,8 +139,7 @@ namespace Content.Server.Body.Systems
{ {
if (meta.RemoveEmpty) if (meta.RemoveEmpty)
{ {
_solutionContainerSystem.RemoveReagent(solutionEntityUid.Value, solution, reagent, solution.RemoveReagent(reagent, FixedPoint2.New(1));
FixedPoint2.New(1));
} }
continue; continue;
@@ -198,12 +198,14 @@ namespace Content.Server.Body.Systems
// remove a certain amount of reagent // remove a certain amount of reagent
if (mostToRemove > FixedPoint2.Zero) 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 // We have processed a reagant, so count it towards the cap
reagents += 1; reagents += 1;
} }
} }
_solutionContainerSystem.UpdateChemicals(soln.Value);
} }
} }

View File

@@ -2,6 +2,7 @@ using Content.Server.Administration.Logs;
using Content.Server.Atmos; using Content.Server.Atmos;
using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.EntitySystems;
using Content.Server.Body.Components; using Content.Server.Body.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Shared.Alert; using Content.Shared.Alert;
using Content.Shared.Atmos; using Content.Shared.Atmos;
@@ -26,6 +27,7 @@ namespace Content.Server.Body.Systems
[Dependency] private readonly LungSystem _lungSystem = default!; [Dependency] private readonly LungSystem _lungSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -144,7 +146,9 @@ namespace Content.Server.Body.Systems
{ {
_atmosSys.Merge(outGas, lung.Air); _atmosSys.Merge(outGas, lung.Air);
lung.Air.Clear(); 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); _atmosSys.Merge(ev.Gas, outGas);

View File

@@ -1,8 +1,8 @@
using Content.Server.Body.Components; using Content.Server.Body.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Body.Organ; using Content.Shared.Body.Organ;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
using Robust.Shared.Utility; using Robust.Shared.Utility;
namespace Content.Server.Body.Systems namespace Content.Server.Body.Systems
@@ -31,8 +31,7 @@ namespace Content.Server.Body.Systems
stomach.AccumulatedFrameTime -= stomach.UpdateInterval; stomach.AccumulatedFrameTime -= stomach.UpdateInterval;
// Get our solutions // Get our solutions
if (!_solutionContainerSystem.TryGetSolution(uid, DefaultSolutionName, if (!_solutionContainerSystem.ResolveSolution((uid, sol), DefaultSolutionName, ref stomach.Solution, out var stomachSolution))
out var stomachSolution, sol))
continue; continue;
if (organ.Body is not { } body || !_solutionContainerSystem.TryGetSolution(body, stomach.BodySolutionName, out var bodySolution)) 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) if (reagent.Quantity > delta.ReagentQuantity.Quantity)
reagent = new(reagent.Reagent, delta.ReagentQuantity.Quantity); reagent = new(reagent.Reagent, delta.ReagentQuantity.Quantity);
_solutionContainerSystem.RemoveReagent(uid, stomachSolution, reagent); stomachSolution.RemoveReagent(reagent);
transferSolution.AddReagent(reagent); transferSolution.AddReagent(reagent);
} }
@@ -64,8 +63,10 @@ namespace Content.Server.Body.Systems
stomach.ReagentDeltas.Remove(item); stomach.ReagentDeltas.Remove(item);
} }
_solutionContainerSystem.UpdateChemicals(stomach.Solution.Value);
// Transfer everything to the body solution! // 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, public bool CanTransferSolution(EntityUid uid, Solution solution,
StomachComponent? stomach = null,
SolutionContainerManagerComponent? solutions = null) SolutionContainerManagerComponent? solutions = null)
{ {
if (!Resolve(uid, ref solutions, false)) if (!Resolve(uid, ref stomach, ref solutions, false))
return 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; return false;
// TODO: For now no partial transfers. Potentially change by design // 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)) if (!Resolve(uid, ref stomach, ref solutions, false))
return false; return false;
if (!_solutionContainerSystem.TryGetSolution(uid, DefaultSolutionName, out var stomachSolution, solutions) if (!_solutionContainerSystem.ResolveSolution((uid, solutions), DefaultSolutionName, ref stomach.Solution)
|| !CanTransferSolution(uid, solution, solutions)) || !CanTransferSolution(uid, solution, stomach, solutions))
return false; 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 // Add each reagent to ReagentDeltas. Used to track how long each reagent has been in the stomach
foreach (var reagent in solution.Contents) foreach (var reagent in solution.Contents)
{ {

View File

@@ -1,3 +1,4 @@
using Content.Shared.Chemistry.Components;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Server.Botany.Components; namespace Content.Server.Botany.Components;
@@ -90,4 +91,7 @@ public sealed partial class PlantHolderComponent : Component
[ViewVariables(VVAccess.ReadWrite), DataField("solution")] [ViewVariables(VVAccess.ReadWrite), DataField("solution")]
public string SoilSolutionName = "soil"; public string SoilSolutionName = "soil";
[DataField]
public Entity<SolutionComponent>? SoilSolution = null;
} }

View File

@@ -1,6 +1,5 @@
using Content.Server.Botany.Components; using Content.Server.Botany.Components;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Robust.Server.GameObjects;
namespace Content.Server.Botany.Systems; namespace Content.Server.Botany.Systems;
@@ -11,7 +10,7 @@ public sealed partial class BotanySystem
if (!TryGetSeed(produce, out var seed)) if (!TryGetSeed(produce, out var seed))
return; return;
var solutionContainer = _solutionContainerSystem.EnsureSolution(uid, produce.SolutionName); var solutionContainer = _solutionContainerSystem.EnsureSolution(uid, produce.SolutionName, FixedPoint2.Zero, out _);
solutionContainer.RemoveAllSolution(); solutionContainer.RemoveAllSolution();
foreach (var (chem, quantity) in seed.Chemicals) foreach (var (chem, quantity) in seed.Chemicals)

View File

@@ -1,10 +1,8 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Server.Botany.Components; using Content.Server.Botany.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Kitchen.Components; using Content.Server.Kitchen.Components;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Shared.Botany; using Content.Shared.Botany;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Examine; using Content.Shared.Examine;
using Content.Shared.Hands.EntitySystems; using Content.Shared.Hands.EntitySystems;
using Content.Shared.Physics; using Content.Shared.Physics;
@@ -20,6 +18,8 @@ using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Systems; using Robust.Shared.Physics.Systems;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace Content.Server.Botany.Systems; namespace Content.Server.Botany.Systems;

View File

@@ -1,12 +1,12 @@
using Content.Server.Atmos; using Content.Server.Atmos;
using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.EntitySystems;
using Content.Server.Botany.Components; using Content.Server.Botany.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Fluids.Components; using Content.Server.Fluids.Components;
using Content.Server.Ghost.Roles.Components; using Content.Server.Ghost.Roles.Components;
using Content.Server.Kitchen.Components; using Content.Server.Kitchen.Components;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Shared.Botany; using Content.Shared.Botany;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.Coordinates.Helpers; using Content.Shared.Coordinates.Helpers;
using Content.Shared.Examine; using Content.Shared.Examine;
@@ -38,7 +38,7 @@ public sealed class PlantHolderSystem : EntitySystem
[Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly SharedPointLightSystem _pointLight = 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 TagSystem _tagSystem = default!;
[Dependency] private readonly RandomHelperSystem _randomHelper = default!; [Dependency] private readonly RandomHelperSystem _randomHelper = default!;
[Dependency] private readonly IRobustRandom _random = 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) if (!args.IsInDetailsRange)
return; return;
var (_, component) = entity;
if (component.Seed == null) if (component.Seed == null)
{ {
args.PushMarkup(Loc.GetString("plant-holder-component-nothing-planted-message")); 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 (TryComp(args.Used, out SeedComponent? seeds))
{ {
if (component.Seed == null) if (component.Seed == null)
@@ -203,8 +207,8 @@ public sealed class PlantHolderSystem : EntitySystem
return; return;
} }
if (_solutionSystem.TryGetDrainableSolution(args.Used, out var solution) if (_solutionContainerSystem.TryGetDrainableSolution(args.Used, out var solution, out _)
&& _solutionSystem.TryGetSolution(uid, component.SoilSolutionName, out var targetSolution) && _solutionContainerSystem.ResolveSolution(uid, component.SoilSolutionName, ref component.SoilSolution)
&& TryComp(args.Used, out SprayComponent? spray)) && TryComp(args.Used, out SprayComponent? spray))
{ {
var amount = FixedPoint2.New(1); 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)); _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) if (split.Volume == 0)
{ {
@@ -227,7 +231,7 @@ public sealed class PlantHolderSystem : EntitySystem
("owner", uid), ("owner", uid),
("amount", split.Volume)), args.User, PopupType.Medium); ("amount", split.Volume)), args.User, PopupType.Medium);
_solutionSystem.TryAddSolution(targetEntity, targetSolution, split); _solutionContainerSystem.TryAddSolution(component.SoilSolution.Value, split);
ForceUpdateByExternalCause(uid, component); ForceUpdateByExternalCause(uid, component);
@@ -290,16 +294,15 @@ public sealed class PlantHolderSystem : EntitySystem
("usingItem", args.Used), ("usingItem", args.Used),
("owner", uid)), uid, Filter.PvsExcept(args.User), true); ("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, // 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. // since the plant will be consumed anyway.
var fillAmount = FixedPoint2.Min(solution2.Volume, solution1.AvailableVolume); var fillAmount = FixedPoint2.Min(solution2.Volume, solution1.AvailableVolume);
_solutionSystem.TryAddSolution(uid, solution1, _solutionContainerSystem.TryAddSolution(component.SoilSolution.Value, _solutionContainerSystem.SplitSolution(soln2.Value, fillAmount));
_solutionSystem.SplitSolution(args.Used, solution2, fillAmount));
ForceUpdateByExternalCause(uid, component); 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() public void WeedInvasion()
@@ -819,13 +822,13 @@ public sealed class PlantHolderSystem : EntitySystem
if (!Resolve(uid, ref component)) if (!Resolve(uid, ref component))
return; return;
if (!_solutionSystem.TryGetSolution(uid, component.SoilSolutionName, out var solution)) if (!_solutionContainerSystem.ResolveSolution(uid, component.SoilSolutionName, ref component.SoilSolution, out var solution))
return; return;
if (solution.Volume > 0 && component.MutationLevel < 25) if (solution.Volume > 0 && component.MutationLevel < 25)
{ {
var amt = FixedPoint2.New(1); 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); var reagentProto = _prototype.Index<ReagentPrototype>(entry.Reagent.Prototype);
reagentProto.ReactionPlant(uid, entry, solution); reagentProto.ReactionPlant(uid, entry, solution);

View File

@@ -1,7 +1,7 @@
using System.Linq; using Content.Server.Administration;
using Content.Server.Administration;
using Content.Server.Body.Systems; using Content.Server.Body.Systems;
using Content.Server.Cargo.Components; using Content.Server.Cargo.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.Body.Components; using Content.Shared.Body.Components;
using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.Components.SolutionManager;
@@ -16,6 +16,7 @@ using Robust.Shared.Map;
using Robust.Shared.Map.Components; using Robust.Shared.Map.Components;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using System.Linq;
namespace Content.Server.Cargo.Systems; namespace Content.Server.Cargo.Systems;
@@ -30,6 +31,7 @@ public sealed class PricingSystem : EntitySystem
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly BodySystem _bodySystem = default!; [Dependency] private readonly BodySystem _bodySystem = default!;
[Dependency] private readonly MobStateSystem _mobStateSystem = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
/// <inheritdoc/> /// <inheritdoc/>
public override void Initialize() public override void Initialize()
@@ -106,13 +108,36 @@ public sealed class PricingSystem : EntitySystem
args.Price += (component.Price - partPenalty) * (_mobStateSystem.IsAlive(uid, state) ? 1.0 : component.DeathPenalty); args.Price += (component.Price - partPenalty) * (_mobStateSystem.IsAlive(uid, state) ? 1.0 : component.DeathPenalty);
} }
private double GetSolutionPrice(Entity<SolutionContainerManagerComponent> entity)
{
if (Comp<MetaDataComponent>(entity).EntityLifeStage < EntityLifeStage.MapInitialized)
return GetSolutionPrice(entity.Comp);
var price = 0.0;
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))
continue;
// TODO check ReagentData for price information?
price += (float) quantity * reagentProto.PricePerUnit;
}
}
return price;
}
private double GetSolutionPrice(SolutionContainerManagerComponent component) private double GetSolutionPrice(SolutionContainerManagerComponent component)
{ {
var price = 0.0; var price = 0.0;
foreach (var solution in component.Solutions.Values) foreach (var (_, prototype) in _solutionContainerSystem.EnumerateSolutions(component))
{ {
foreach (var (reagent, quantity) in solution.Contents) foreach (var (reagent, quantity) in prototype.Contents)
{ {
if (!_prototypeManager.TryIndex<ReagentPrototype>(reagent.Prototype, out var reagentProto)) if (!_prototypeManager.TryIndex<ReagentPrototype>(reagent.Prototype, out var reagentProto))
continue; continue;
@@ -257,7 +282,7 @@ public sealed class PricingSystem : EntitySystem
if (TryComp<SolutionContainerManagerComponent>(uid, out var solComp)) if (TryComp<SolutionContainerManagerComponent>(uid, out var solComp))
{ {
price += GetSolutionPrice(solComp); price += GetSolutionPrice((uid, solComp));
} }
return price; return price;

View File

@@ -1,4 +1,3 @@
using Content.Server.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Robust.Shared.Audio; using Robust.Shared.Audio;

View File

@@ -1,8 +1,6 @@
using Content.Server.Chemistry.EntitySystems; using Content.Server.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.FixedPoint;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Chemistry.Components; namespace Content.Server.Chemistry.Components;
@@ -17,7 +15,13 @@ public sealed partial class SolutionRegenerationComponent : Component
/// The name of the solution to add to. /// The name of the solution to add to.
/// </summary> /// </summary>
[DataField("solution", required: true), ViewVariables(VVAccess.ReadWrite)] [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> /// <summary>
/// The reagent(s) to be regenerated in the solution. /// The reagent(s) to be regenerated in the solution.

View File

@@ -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
}

View File

@@ -1,6 +1,5 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Labels; using Content.Server.Labels;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Server.Storage.EntitySystems; using Content.Server.Storage.EntitySystems;
@@ -19,6 +18,8 @@ using Robust.Server.GameObjects;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace Content.Server.Chemistry.EntitySystems namespace Content.Server.Chemistry.EntitySystems
{ {
@@ -47,7 +48,7 @@ namespace Content.Server.Chemistry.EntitySystems
base.Initialize(); base.Initialize();
SubscribeLocalEvent<ChemMasterComponent, ComponentStartup>(SubscribeUpdateUiState); SubscribeLocalEvent<ChemMasterComponent, ComponentStartup>(SubscribeUpdateUiState);
SubscribeLocalEvent<ChemMasterComponent, SolutionChangedEvent>(SubscribeUpdateUiState); SubscribeLocalEvent<ChemMasterComponent, SolutionContainerChangedEvent>(SubscribeUpdateUiState);
SubscribeLocalEvent<ChemMasterComponent, EntInsertedIntoContainerMessage>(SubscribeUpdateUiState); SubscribeLocalEvent<ChemMasterComponent, EntInsertedIntoContainerMessage>(SubscribeUpdateUiState);
SubscribeLocalEvent<ChemMasterComponent, EntRemovedFromContainerMessage>(SubscribeUpdateUiState); SubscribeLocalEvent<ChemMasterComponent, EntRemovedFromContainerMessage>(SubscribeUpdateUiState);
SubscribeLocalEvent<ChemMasterComponent, BoundUIOpenedEvent>(SubscribeUpdateUiState); SubscribeLocalEvent<ChemMasterComponent, BoundUIOpenedEvent>(SubscribeUpdateUiState);
@@ -67,7 +68,7 @@ namespace Content.Server.Chemistry.EntitySystems
private void UpdateUiState(Entity<ChemMasterComponent> ent, bool updateLabel = false) private void UpdateUiState(Entity<ChemMasterComponent> ent, bool updateLabel = false)
{ {
var (owner, chemMaster) = ent; var (owner, chemMaster) = ent;
if (!_solutionContainerSystem.TryGetSolution(owner, SharedChemMaster.BufferSolutionName, out var bufferSolution)) if (!_solutionContainerSystem.TryGetSolution(owner, SharedChemMaster.BufferSolutionName, out _, out var bufferSolution))
return; return;
var inputContainer = _itemSlotsSystem.GetItemOrNull(owner, SharedChemMaster.InputSlotName); var inputContainer = _itemSlotsSystem.GetItemOrNull(owner, SharedChemMaster.InputSlotName);
var outputContainer = _itemSlotsSystem.GetItemOrNull(owner, SharedChemMaster.OutputSlotName); var outputContainer = _itemSlotsSystem.GetItemOrNull(owner, SharedChemMaster.OutputSlotName);
@@ -130,8 +131,8 @@ namespace Content.Server.Chemistry.EntitySystems
{ {
var container = _itemSlotsSystem.GetItemOrNull(chemMaster, SharedChemMaster.InputSlotName); var container = _itemSlotsSystem.GetItemOrNull(chemMaster, SharedChemMaster.InputSlotName);
if (container is null || if (container is null ||
!_solutionContainerSystem.TryGetFitsInDispenser(container.Value, out var containerSolution) || !_solutionContainerSystem.TryGetFitsInDispenser(container.Value, out var containerSoln, out var containerSolution) ||
!_solutionContainerSystem.TryGetSolution(chemMaster, SharedChemMaster.BufferSolutionName, out var bufferSolution)) !_solutionContainerSystem.TryGetSolution(chemMaster.Owner, SharedChemMaster.BufferSolutionName, out _, out var bufferSolution))
{ {
return; return;
} }
@@ -140,12 +141,12 @@ namespace Content.Server.Chemistry.EntitySystems
{ {
amount = FixedPoint2.Min(amount, containerSolution.AvailableVolume); amount = FixedPoint2.Min(amount, containerSolution.AvailableVolume);
amount = bufferSolution.RemoveReagent(id, amount); 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 else // Container to buffer
{ {
amount = FixedPoint2.Min(amount, containerSolution.GetReagentQuantity(id)); amount = FixedPoint2.Min(amount, containerSolution.GetReagentQuantity(id));
_solutionContainerSystem.RemoveReagent(container.Value, containerSolution, id, amount); _solutionContainerSystem.RemoveReagent(containerSoln.Value, id, amount);
bufferSolution.AddReagent(id, amount); bufferSolution.AddReagent(id, amount);
} }
@@ -156,7 +157,7 @@ namespace Content.Server.Chemistry.EntitySystems
{ {
if (fromBuffer) 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); bufferSolution.RemoveReagent(id, amount);
else else
return; return;
@@ -165,9 +166,9 @@ namespace Content.Server.Chemistry.EntitySystems
{ {
var container = _itemSlotsSystem.GetItemOrNull(chemMaster, SharedChemMaster.InputSlotName); var container = _itemSlotsSystem.GetItemOrNull(chemMaster, SharedChemMaster.InputSlotName);
if (container is not null && 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 else
return; return;
@@ -210,10 +211,8 @@ namespace Content.Server.Chemistry.EntitySystems
_storageSystem.Insert(container, item, out _, user: user, storage); _storageSystem.Insert(container, item, out _, user: user, storage);
_labelSystem.Label(item, message.Label); _labelSystem.Label(item, message.Label);
var itemSolution = _solutionContainerSystem.EnsureSolution(item, SharedChemMaster.PillSolutionName); var itemSolution = _solutionContainerSystem.EnsureSolutionEntity(item, SharedChemMaster.PillSolutionName, message.Dosage, null, out _);
_solutionContainerSystem.TryAddSolution(itemSolution, withdrawal.SplitSolution(message.Dosage));
_solutionContainerSystem.TryAddSolution(
item, itemSolution, withdrawal.SplitSolution(message.Dosage));
var pill = EnsureComp<PillComponent>(item); var pill = EnsureComp<PillComponent>(item);
pill.PillType = chemMaster.Comp.PillType; pill.PillType = chemMaster.Comp.PillType;
@@ -223,13 +222,13 @@ namespace Content.Server.Chemistry.EntitySystems
{ {
// Log pill creation by a user // Log pill creation by a user
_adminLogger.Add(LogType.Action, LogImpact.Low, _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 else
{ {
// Log pill creation by magic? This should never happen... right? // Log pill creation by magic? This should never happen... right?
_adminLogger.Add(LogType.Action, LogImpact.Low, _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 user = message.Session.AttachedEntity;
var maybeContainer = _itemSlotsSystem.GetItemOrNull(chemMaster, SharedChemMaster.OutputSlotName); var maybeContainer = _itemSlotsSystem.GetItemOrNull(chemMaster, SharedChemMaster.OutputSlotName);
if (maybeContainer is not { Valid: true } container if (maybeContainer is not { Valid: true } container
|| !_solutionContainerSystem.TryGetSolution( || !_solutionContainerSystem.TryGetSolution(container, SharedChemMaster.BottleSolutionName, out var soln, out var solution))
container, SharedChemMaster.BottleSolutionName, out var solution))
{ {
return; // output can't fit reagents return; // output can't fit reagents
} }
@@ -260,8 +258,7 @@ namespace Content.Server.Chemistry.EntitySystems
return; return;
_labelSystem.Label(container, message.Label); _labelSystem.Label(container, message.Label);
_solutionContainerSystem.TryAddSolution( _solutionContainerSystem.TryAddSolution(soln.Value, withdrawal);
container, solution, withdrawal);
if (user.HasValue) if (user.HasValue)
{ {
@@ -287,8 +284,7 @@ namespace Content.Server.Chemistry.EntitySystems
{ {
outputSolution = null; outputSolution = null;
if (!_solutionContainerSystem.TryGetSolution( if (!_solutionContainerSystem.TryGetSolution(chemMaster.Owner, SharedChemMaster.BufferSolutionName, out _, out var solution))
chemMaster, SharedChemMaster.BufferSolutionName, out var solution))
{ {
return false; return false;
} }
@@ -323,7 +319,7 @@ namespace Content.Server.Chemistry.EntitySystems
return null; return null;
if (!TryComp(container, out FitsInDispenserComponent? fits) 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; return null;
} }
@@ -339,7 +335,7 @@ namespace Content.Server.Chemistry.EntitySystems
var name = Name(container.Value); var name = Name(container.Value);
{ {
if (_solutionContainerSystem.TryGetSolution( if (_solutionContainerSystem.TryGetSolution(
container.Value, SharedChemMaster.BottleSolutionName, out var solution)) container.Value, SharedChemMaster.BottleSolutionName, out _, out var solution))
{ {
return BuildContainerInfo(name, 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 => 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; var quantity = solution?.Volume ?? FixedPoint2.Zero;
return (Name(pill), quantity); return (Name(pill), quantity);
})).ToList(); })).ToList();

View File

@@ -1,21 +1,22 @@
using Content.Server.Body.Components; using Content.Server.Body.Components;
using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.DoAfter;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.Forensics;
using Content.Shared.IdentityManagement; using Content.Shared.IdentityManagement;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Interaction.Events; using Content.Shared.Interaction.Events;
using Robust.Shared.GameStates;
using Content.Shared.DoAfter;
using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Components;
using Content.Shared.Verbs;
using Content.Shared.Stacks; using Content.Shared.Stacks;
using Content.Shared.Verbs;
using Robust.Shared.GameStates;
using Robust.Shared.Player; using Robust.Shared.Player;
using Content.Shared.Forensics;
namespace Content.Server.Chemistry.EntitySystems; namespace Content.Server.Chemistry.EntitySystems;
@@ -25,11 +26,11 @@ public sealed partial class ChemistrySystem
/// <summary> /// <summary>
/// Default transfer amounts for the set-transfer verb. /// Default transfer amounts for the set-transfer verb.
/// </summary> /// </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() private void InitializeInjector()
{ {
SubscribeLocalEvent<InjectorComponent, GetVerbsEvent<AlternativeVerb>>(AddSetTransferVerbs); SubscribeLocalEvent<InjectorComponent, GetVerbsEvent<AlternativeVerb>>(AddSetTransferVerbs);
SubscribeLocalEvent<InjectorComponent, SolutionChangedEvent>(OnSolutionChange); SubscribeLocalEvent<InjectorComponent, SolutionContainerChangedEvent>(OnSolutionChange);
SubscribeLocalEvent<InjectorComponent, InjectorDoAfterEvent>(OnInjectDoAfter); SubscribeLocalEvent<InjectorComponent, InjectorDoAfterEvent>(OnInjectDoAfter);
SubscribeLocalEvent<InjectorComponent, ComponentStartup>(OnInjectorStartup); SubscribeLocalEvent<InjectorComponent, ComponentStartup>(OnInjectorStartup);
SubscribeLocalEvent<InjectorComponent, UseInHandEvent>(OnInjectorUse); SubscribeLocalEvent<InjectorComponent, UseInHandEvent>(OnInjectorUse);
@@ -37,7 +38,7 @@ public sealed partial class ChemistrySystem
SubscribeLocalEvent<InjectorComponent, ComponentGetState>(OnInjectorGetState); 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) if (!args.CanAccess || !args.CanInteract || args.Hands == null)
return; return;
@@ -45,11 +46,14 @@ public sealed partial class ChemistrySystem
if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor))
return; return;
var (uid, component) = entity;
// Add specific transfer verbs according to the container's size // Add specific transfer verbs according to the container's size
var priority = 0; var priority = 0;
var user = args.User;
foreach (var amount in TransferAmounts) foreach (var amount in TransferAmounts)
{ {
if ( amount < component.MinimumTransferAmount.Int() || amount > component.MaximumTransferAmount.Int()) if (amount < component.MinimumTransferAmount.Int() || amount > component.MaximumTransferAmount.Int())
continue; continue;
AlternativeVerb verb = new(); AlternativeVerb verb = new();
@@ -58,7 +62,7 @@ public sealed partial class ChemistrySystem
verb.Act = () => verb.Act = () =>
{ {
component.TransferAmount = FixedPoint2.New(amount); 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. // 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 // 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)) else if (TryComp<BloodstreamComponent>(target, out var bloodstream))
{ {
TryInjectIntoBloodstream(component, injector, target, bloodstream, user); TryInjectIntoBloodstream(injector, (target, bloodstream), user);
} }
else else
{ {
@@ -92,111 +96,112 @@ public sealed partial class ChemistrySystem
("target", Identity.Entity(target, EntityManager))), injector, user); ("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 // 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; return;
} }
// Draw from an object (food, beaker, etc) // 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 else
{ {
_popup.PopupEntity(Loc.GetString("injector-component-cannot-draw-message", _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 currentVolume = solution?.Volume ?? FixedPoint2.Zero;
var maxVolume = solution?.MaxVolume ?? 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) if (args.Cancelled || args.Handled || args.Args.Target == null)
return; return;
UseInjector(args.Args.Target.Value, args.Args.User, uid, component); UseInjector(entity, args.Args.Target.Value, args.Args.User);
args.Handled = true; 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) if (args.Handled || !args.CanReach)
return; return;
//Make sure we have the attacking entity //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; return;
// Is the target a mob? If yes, use a do-after to give them time to respond. // Is the target a mob? If yes, use a do-after to give them time to respond.
if (HasComp<MobStateComponent>(target) || HasComp<BloodstreamComponent>(target)) if (HasComp<MobStateComponent>(target) || HasComp<BloodstreamComponent>(target))
{ {
// Are use using an injector capible of targeting a mob? // Are use using an injector capible of targeting a mob?
if (component.IgnoreMobs) if (entity.Comp.IgnoreMobs)
return; return;
InjectDoAfter(component, args.User, target, uid); InjectDoAfter(entity, target, args.User);
args.Handled = true; args.Handled = true;
return; return;
} }
UseInjector(target, args.User, uid, component); UseInjector(entity, target, args.User);
args.Handled = true; args.Handled = true;
} }
private void OnInjectorStartup(EntityUid uid, InjectorComponent component, ComponentStartup args) private void OnInjectorStartup(Entity<InjectorComponent> entity, ref ComponentStartup args)
{ {
// ???? why ????? // ???? 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) if (args.Handled)
return; return;
Toggle(component, args.User, uid); Toggle(entity, args.User);
args.Handled = true; args.Handled = true;
} }
/// <summary> /// <summary>
/// Toggle between draw/inject state if applicable /// Toggle between draw/inject state if applicable
/// </summary> /// </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; return;
} }
string msg; string msg;
switch (component.ToggleState) switch (injector.Comp.ToggleState)
{ {
case SharedInjectorComponent.InjectorToggleMode.Inject: case SharedInjectorComponent.InjectorToggleMode.Inject:
component.ToggleState = SharedInjectorComponent.InjectorToggleMode.Draw; injector.Comp.ToggleState = SharedInjectorComponent.InjectorToggleMode.Draw;
msg = "injector-component-drawing-text"; msg = "injector-component-drawing-text";
break; break;
case SharedInjectorComponent.InjectorToggleMode.Draw: case SharedInjectorComponent.InjectorToggleMode.Draw:
component.ToggleState = SharedInjectorComponent.InjectorToggleMode.Inject; injector.Comp.ToggleState = SharedInjectorComponent.InjectorToggleMode.Inject;
msg = "injector-component-injecting-text"; msg = "injector-component-injecting-text";
break; break;
default: default:
@@ -209,18 +214,18 @@ public sealed partial class ChemistrySystem
/// <summary> /// <summary>
/// Send informative pop-up messages and wait for a do-after to complete. /// Send informative pop-up messages and wait for a do-after to complete.
/// </summary> /// </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 // Create a pop-up for the user
_popup.PopupEntity(Loc.GetString("injector-component-injecting-user"), target, 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; 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 // 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; 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. // 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, _adminLogger.Add(LogType.ForceFeed,
$"{EntityManager.ToPrettyString(user):user} is attempting to inject {EntityManager.ToPrettyString(target):target} with a solution {SolutionContainerSystem.ToPrettyString(solution):solution}"); $"{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. // Self-injections take half as long.
actualDelay /= 2; 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}."); _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, BreakOnUserMove = true,
BreakOnDamage = 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 // 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) 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; return;
} }
// Move units from attackSolution to targetSolution // 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); _reactiveSystem.DoEntityReaction(target, removedSolution, ReactionMethod.Injection);
_popup.PopupEntity(Loc.GetString("injector-component-inject-success-message", _popup.PopupEntity(Loc.GetString("injector-component-inject-success-message",
("amount", removedSolution.Volume), ("amount", removedSolution.Volume),
("target", Identity.Entity(target, EntityManager))), injector, user); ("target", Identity.Entity(target, EntityManager))), injector.Owner, user);
Dirty(component); Dirty(injector);
AfterInject(component, injector, target); 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) if (!_solutionContainers.TryGetSolution(injector.Owner, InjectorComponent.SolutionName, out var soln, out var solution) || solution.Volume == 0)
|| solution.Volume == 0)
return; return;
// Get transfer amount. May be smaller than _transferAmount if not enough room // 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) if (realTransferAmount <= 0)
{ {
_popup.PopupEntity(Loc.GetString("injector-component-target-already-full-message", ("target", Identity.Entity(targetEntity, EntityManager))), _popup.PopupEntity(Loc.GetString("injector-component-target-already-full-message", ("target", Identity.Entity(targetEntity, EntityManager))),
injector, user); injector.Owner, user);
return; return;
} }
// Move units from attackSolution to targetSolution // Move units from attackSolution to targetSolution
Solution removedSolution; Solution removedSolution;
if (TryComp<StackComponent>(targetEntity, out var stack)) if (TryComp<StackComponent>(targetEntity, out var stack))
removedSolution = _solutions.SplitStackSolution(injector, solution, realTransferAmount, stack.Count); removedSolution = _solutionContainers.SplitStackSolution(soln.Value, realTransferAmount, stack.Count);
else else
removedSolution = _solutions.SplitSolution(injector, solution, realTransferAmount); removedSolution = _solutionContainers.SplitSolution(soln.Value, realTransferAmount);
_reactiveSystem.DoEntityReaction(targetEntity, removedSolution, ReactionMethod.Injection); _reactiveSystem.DoEntityReaction(targetEntity, removedSolution, ReactionMethod.Injection);
if (!asRefill) if (!asRefill)
_solutions.Inject(targetEntity, targetSolution, removedSolution); _solutionContainers.Inject(targetEntity, targetSolution, removedSolution);
else else
_solutions.Refill(targetEntity, targetSolution, removedSolution); _solutionContainers.Refill(targetEntity, targetSolution, removedSolution);
_popup.PopupEntity(Loc.GetString("injector-component-transfer-success-message", _popup.PopupEntity(Loc.GetString("injector-component-transfer-success-message",
("amount", removedSolution.Volume), ("amount", removedSolution.Volume),
("target", Identity.Entity(targetEntity, EntityManager))), injector, user); ("target", Identity.Entity(targetEntity, EntityManager))), injector.Owner, user);
Dirty(component); Dirty(injector);
AfterInject(component, injector, targetEntity); 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. // Automatically set syringe to draw after completely draining it.
if (_solutions.TryGetSolution(injector, InjectorComponent.SolutionName, out var solution) if (_solutionContainers.TryGetSolution(injector.Owner, InjectorComponent.SolutionName, out _, out var solution) && solution.Volume == 0)
&& solution.Volume == 0)
{ {
component.ToggleState = SharedInjectorComponent.InjectorToggleMode.Draw; injector.Comp.ToggleState = SharedInjectorComponent.InjectorToggleMode.Draw;
} }
// Leave some DNA from the injectee on it // Leave some DNA from the injectee on it
@@ -346,13 +354,12 @@ public sealed partial class ChemistrySystem
RaiseLocalEvent(target, ref ev); 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. // Automatically set syringe to inject after completely filling it.
if (_solutions.TryGetSolution(injector, InjectorComponent.SolutionName, out var solution) if (_solutionContainers.TryGetSolution(injector.Owner, InjectorComponent.SolutionName, out _, out var solution) && solution.AvailableVolume == 0)
&& solution.AvailableVolume == 0)
{ {
component.ToggleState = SharedInjectorComponent.InjectorToggleMode.Inject; injector.Comp.ToggleState = SharedInjectorComponent.InjectorToggleMode.Inject;
} }
// Leave some DNA from the drawee on it // Leave some DNA from the drawee on it
@@ -360,70 +367,68 @@ public sealed partial class ChemistrySystem
RaiseLocalEvent(target, ref ev); 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) if (!_solutionContainers.TryGetSolution(injector.Owner, InjectorComponent.SolutionName, out var soln, out var solution) || solution.AvailableVolume == 0)
|| solution.AvailableVolume == 0)
{ {
return; return;
} }
// Get transfer amount. May be smaller than _transferAmount if not enough room, also make sure there's room in the injector // 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) if (realTransferAmount <= 0)
{ {
_popup.PopupEntity(Loc.GetString("injector-component-target-is-empty-message", ("target", Identity.Entity(targetEntity, EntityManager))), _popup.PopupEntity(Loc.GetString("injector-component-target-is-empty-message", ("target", Identity.Entity(target, EntityManager))),
injector, user); injector.Owner, user);
return; return;
} }
// We have some snowflaked behavior for streams. // 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; return;
} }
// Move units from attackSolution to targetSolution // 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; return;
} }
_popup.PopupEntity(Loc.GetString("injector-component-draw-success-message", _popup.PopupEntity(Loc.GetString("injector-component-draw-success-message",
("amount", removedSolution.Volume), ("amount", removedSolution.Volume),
("target", Identity.Entity(targetEntity, EntityManager))), injector, user); ("target", Identity.Entity(target, EntityManager))), injector.Owner, user);
Dirty(component); Dirty(injector);
AfterDraw(component, injector, targetEntity); 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 drawAmount = (float) transferAmount;
var bloodAmount = drawAmount;
var chemAmount = 0f; if (_solutionContainers.ResolveSolution(target.Owner, target.Comp.ChemicalSolutionName, ref target.Comp.ChemicalSolution))
if (stream.ChemicalSolution.Volume > 0f) // If they have stuff in their chem stream, we'll draw some of that
{ {
bloodAmount = drawAmount * 0.85f; var chemTemp = _solutionContainers.SplitSolution(target.Comp.ChemicalSolution.Value, drawAmount * 0.15f);
chemAmount = drawAmount * 0.15f; _solutionContainers.TryAddSolution(injectorSolution, chemTemp);
drawAmount -= (float) chemTemp.Volume;
} }
var bloodTemp = stream.BloodSolution.SplitSolution(bloodAmount); if (_solutionContainers.ResolveSolution(target.Owner, target.Comp.BloodSolutionName, ref target.Comp.BloodSolution))
var chemTemp = stream.ChemicalSolution.SplitSolution(chemAmount); {
var bloodTemp = _solutionContainers.SplitSolution(target.Comp.BloodSolution.Value, drawAmount);
_solutions.TryAddSolution(injector, injectorSolution, bloodTemp); _solutionContainers.TryAddSolution(injectorSolution, bloodTemp);
_solutions.TryAddSolution(injector, injectorSolution, chemTemp); }
_popup.PopupEntity(Loc.GetString("injector-component-draw-success-message", _popup.PopupEntity(Loc.GetString("injector-component-draw-success-message",
("amount", transferAmount), ("amount", transferAmount),
("target", Identity.Entity(target, EntityManager))), injector, user); ("target", Identity.Entity(target, EntityManager))), injector.Owner, user);
Dirty(component); Dirty(injector);
AfterDraw(component, injector, target); AfterDraw(injector, target);
} }
} }

View File

@@ -1,13 +1,12 @@
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Body.Systems; using Content.Server.Body.Systems;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Interaction; using Content.Server.Interaction;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Shared.CombatMode;
using Content.Shared.Chemistry; using Content.Shared.Chemistry;
using Content.Shared.Chemistry.EntitySystems; using Content.Shared.CombatMode;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Systems;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
namespace Content.Server.Chemistry.EntitySystems; namespace Content.Server.Chemistry.EntitySystems;
@@ -24,7 +23,7 @@ public sealed partial class ChemistrySystem : EntitySystem
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly SharedCombatModeSystem _combat = default!; [Dependency] private readonly SharedCombatModeSystem _combat = default!;
[Dependency] private readonly SolutionContainerSystem _solutions = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainers = default!;
public override void Initialize() public override void Initialize()
{ {

View File

@@ -1,20 +1,21 @@
using System.Linq;
using System.Diagnostics.CodeAnalysis;
using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.Forensics;
using Content.Shared.IdentityManagement; using Content.Shared.IdentityManagement;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Interaction.Events; using Content.Shared.Interaction.Events;
using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Components;
using Content.Shared.Weapons.Melee.Events;
using Content.Shared.Timing; using Content.Shared.Timing;
using Content.Shared.Weapons.Melee.Events;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Content.Shared.Forensics; using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace Content.Server.Chemistry.EntitySystems namespace Content.Server.Chemistry.EntitySystems
{ {
@@ -26,33 +27,33 @@ namespace Content.Server.Chemistry.EntitySystems
{ {
SubscribeLocalEvent<HyposprayComponent, AfterInteractEvent>(OnAfterInteract); SubscribeLocalEvent<HyposprayComponent, AfterInteractEvent>(OnAfterInteract);
SubscribeLocalEvent<HyposprayComponent, MeleeHitEvent>(OnAttack); SubscribeLocalEvent<HyposprayComponent, MeleeHitEvent>(OnAttack);
SubscribeLocalEvent<HyposprayComponent, SolutionChangedEvent>(OnSolutionChange); SubscribeLocalEvent<HyposprayComponent, SolutionContainerChangedEvent>(OnSolutionChange);
SubscribeLocalEvent<HyposprayComponent, UseInHandEvent>(OnUseInHand); SubscribeLocalEvent<HyposprayComponent, UseInHandEvent>(OnUseInHand);
SubscribeLocalEvent<HyposprayComponent, ComponentGetState>(OnHypoGetState); 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(solution.Volume, solution.MaxVolume)
: new HyposprayComponentState(FixedPoint2.Zero, FixedPoint2.Zero); : 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) if (args.Handled)
return; return;
TryDoInject(uid, args.User, args.User); TryDoInject(entity, args.User, args.User);
args.Handled = true; 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) if (!args.CanReach)
return; return;
@@ -60,21 +61,20 @@ namespace Content.Server.Chemistry.EntitySystems
var target = args.Target; var target = args.Target;
var user = args.User; 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()) if (!args.HitEntities.Any())
return; 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)) var (uid, component) = hypo;
return false;
if (!EligibleEntity(target, _entMan, component)) if (!EligibleEntity(target, _entMan, component))
return false; return false;
@@ -92,15 +92,13 @@ namespace Content.Server.Chemistry.EntitySystems
target = user; target = user;
} }
_solutions.TryGetSolution(uid, component.SolutionName, out var hypoSpraySolution); if (!_solutionContainers.TryGetSolution(uid, component.SolutionName, out var hypoSpraySoln, out var hypoSpraySolution) || hypoSpraySolution.Volume == 0)
if (hypoSpraySolution == null || hypoSpraySolution.Volume == 0)
{ {
_popup.PopupCursor(Loc.GetString("hypospray-component-empty-message"), user); _popup.PopupCursor(Loc.GetString("hypospray-component-empty-message"), user);
return true; 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); _popup.PopupCursor(Loc.GetString("hypospray-cant-inject", ("target", Identity.Entity(target.Value, _entMan))), user);
return false; return false;
@@ -127,17 +125,17 @@ namespace Content.Server.Chemistry.EntitySystems
if (realTransferAmount <= 0) 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; return true;
} }
// Move units from attackSolution to targetSolution // Move units from attackSolution to targetSolution
var removedSolution = _solutions.SplitSolution(uid, hypoSpraySolution, realTransferAmount); var removedSolution = _solutionContainers.SplitSolution(hypoSpraySoln.Value, realTransferAmount);
if (!targetSolution.CanAddSolution(removedSolution)) if (!targetSolution.CanAddSolution(removedSolution))
return true; return true;
_reactiveSystem.DoEntityReaction(target.Value, removedSolution, ReactionMethod.Injection); _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 }; var ev = new TransferDnaEvent { Donor = target.Value, Recipient = uid };
RaiseLocalEvent(target.Value, ref ev); RaiseLocalEvent(target.Value, ref ev);

View 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.Chemistry.Reaction;
using Content.Shared.IdentityManagement; using Content.Shared.IdentityManagement;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Robust.Shared.Player;
namespace Content.Server.Chemistry.EntitySystems; namespace Content.Server.Chemistry.EntitySystems;
@@ -15,27 +11,26 @@ public sealed partial class ChemistrySystem
SubscribeLocalEvent<ReactionMixerComponent, AfterInteractEvent>(OnAfterInteract); 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) if (!args.Target.HasValue || !args.CanReach)
return; return;
var mixAttemptEvent = new MixingAttemptEvent(uid); var mixAttemptEvent = new MixingAttemptEvent(entity);
RaiseLocalEvent(uid, ref mixAttemptEvent); RaiseLocalEvent(entity, ref mixAttemptEvent);
if(mixAttemptEvent.Cancelled) if (mixAttemptEvent.Cancelled)
{ {
return; return;
} }
Solution? solution = null; if (!_solutionContainers.TryGetMixableSolution(args.Target.Value, out var solution))
if (!_solutions.TryGetMixableSolution(args.Target.Value, out solution)) return;
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); var afterMixingEvent = new AfterMixingEvent(entity, args.Target.Value);
RaiseLocalEvent(uid, afterMixingEvent); RaiseLocalEvent(entity, afterMixingEvent);
} }
} }

View File

@@ -1,4 +1,5 @@
using Content.Server.Chemistry.Components.DeleteOnSolutionEmptyComponent; using Content.Server.Chemistry.Components.DeleteOnSolutionEmptyComponent;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.EntitySystems;
@@ -12,27 +13,27 @@ namespace Content.Server.Chemistry.EntitySystems.DeleteOnSolutionEmptySystem
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<DeleteOnSolutionEmptyComponent, ComponentStartup>(OnStartup); 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; 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) if (solution.Volume <= 0)
EntityManager.QueueDeleteEntity(uid); EntityManager.QueueDeleteEntity(entity);
} }
} }
} }

View File

@@ -1,6 +1,6 @@
using System.Linq;
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Chemistry; using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Dispenser; using Content.Shared.Chemistry.Dispenser;
using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.EntitySystems;
@@ -15,6 +15,7 @@ using Robust.Server.GameObjects;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using System.Linq;
namespace Content.Server.Chemistry.EntitySystems namespace Content.Server.Chemistry.EntitySystems
{ {
@@ -36,7 +37,7 @@ namespace Content.Server.Chemistry.EntitySystems
base.Initialize(); base.Initialize();
SubscribeLocalEvent<ReagentDispenserComponent, ComponentStartup>(SubscribeUpdateUiState); SubscribeLocalEvent<ReagentDispenserComponent, ComponentStartup>(SubscribeUpdateUiState);
SubscribeLocalEvent<ReagentDispenserComponent, SolutionChangedEvent>(SubscribeUpdateUiState); SubscribeLocalEvent<ReagentDispenserComponent, SolutionContainerChangedEvent>(SubscribeUpdateUiState);
SubscribeLocalEvent<ReagentDispenserComponent, EntInsertedIntoContainerMessage>(SubscribeUpdateUiState); SubscribeLocalEvent<ReagentDispenserComponent, EntInsertedIntoContainerMessage>(SubscribeUpdateUiState);
SubscribeLocalEvent<ReagentDispenserComponent, EntRemovedFromContainerMessage>(SubscribeUpdateUiState); SubscribeLocalEvent<ReagentDispenserComponent, EntRemovedFromContainerMessage>(SubscribeUpdateUiState);
SubscribeLocalEvent<ReagentDispenserComponent, BoundUIOpenedEvent>(SubscribeUpdateUiState); SubscribeLocalEvent<ReagentDispenserComponent, BoundUIOpenedEvent>(SubscribeUpdateUiState);
@@ -68,7 +69,7 @@ namespace Content.Server.Chemistry.EntitySystems
if (container is not { Valid: true }) if (container is not { Valid: true })
return null; 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) return new ContainerInfo(Name(container.Value), solution.Volume, solution.MaxVolume)
{ {
@@ -122,10 +123,10 @@ namespace Content.Server.Chemistry.EntitySystems
return; return;
var outputContainer = _itemSlotsSystem.GetItemOrNull(reagentDispenser, SharedReagentDispenser.OutputSlotName); 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; 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) && message.Session.AttachedEntity is not null)
{ {
_adminLogger.Add(LogType.ChemicalReaction, LogImpact.Medium, _adminLogger.Add(LogType.ChemicalReaction, LogImpact.Medium,
@@ -139,10 +140,10 @@ namespace Content.Server.Chemistry.EntitySystems
private void OnClearContainerSolutionMessage(Entity<ReagentDispenserComponent> reagentDispenser, ref ReagentDispenserClearContainerSolutionMessage message) private void OnClearContainerSolutionMessage(Entity<ReagentDispenserComponent> reagentDispenser, ref ReagentDispenserClearContainerSolutionMessage message)
{ {
var outputContainer = _itemSlotsSystem.GetItemOrNull(reagentDispenser, SharedReagentDispenser.OutputSlotName); 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; return;
_solutionContainerSystem.RemoveAllSolution(outputContainer.Value, solution); _solutionContainerSystem.RemoveAllSolution(solution.Value);
UpdateUiState(reagentDispenser); UpdateUiState(reagentDispenser);
ClickSound(reagentDispenser); ClickSound(reagentDispenser);
} }

View File

@@ -1,4 +1,5 @@
using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.Popups; using Content.Shared.Popups;
@@ -16,21 +17,23 @@ public sealed class RehydratableSystem : EntitySystem
{ {
base.Initialize(); 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); var quantity = _solutions.GetTotalPrototypeQuantity(entity, entity.Comp.CatalystPrototype);
if (quantity != FixedPoint2.Zero && quantity >= comp.CatalystMinimum) if (quantity != FixedPoint2.Zero && quantity >= entity.Comp.CatalystMinimum)
{ {
Expand(uid, comp); Expand(entity);
} }
} }
// Try not to make this public if you can help it. // 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); _popups.PopupEntity(Loc.GetString("rehydratable-component-expands-message", ("owner", uid)), uid);
var randomMob = _random.Pick(comp.PossibleSpawns); var randomMob = _random.Pick(comp.PossibleSpawns);

View File

@@ -1,10 +1,10 @@
using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Construction; using Content.Server.Construction;
using Content.Server.Power.Components; using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems; using Content.Server.Power.EntitySystems;
using Content.Shared.Chemistry; using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Placeable; using Content.Shared.Placeable;
namespace Content.Server.Chemistry.EntitySystems; namespace Content.Server.Chemistry.EntitySystems;
@@ -13,7 +13,7 @@ public sealed class SolutionHeaterSystem : EntitySystem
{ {
[Dependency] private readonly PowerReceiverSystem _powerReceiver = default!; [Dependency] private readonly PowerReceiverSystem _powerReceiver = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SolutionContainerSystem _solution = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainer = default!;
/// <inheritdoc/> /// <inheritdoc/>
public override void Initialize() public override void Initialize()
@@ -51,41 +51,41 @@ public sealed class SolutionHeaterSystem : EntitySystem
RemComp<ActiveSolutionHeaterComponent>(uid); 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) if (args.Powered && placer.PlacedEntities.Count > 0)
{ {
TurnOn(uid); TurnOn(entity);
} }
else 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 if (placer.PlacedEntities.Count == 0) // Last entity was removed
TurnOff(uid); TurnOff(entity);
} }
public override void Update(float frameTime) public override void Update(float frameTime)
@@ -97,13 +97,13 @@ public sealed class SolutionHeaterSystem : EntitySystem
{ {
foreach (var heatingEntity in placer.PlacedEntities) foreach (var heatingEntity in placer.PlacedEntities)
{ {
if (!TryComp<SolutionContainerManagerComponent>(heatingEntity, out var solution)) if (!TryComp<SolutionContainerManagerComponent>(heatingEntity, out var container))
continue; continue;
var energy = heater.HeatPerSecond * frameTime; 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);
} }
} }
} }

View File

@@ -1,19 +1,17 @@
using Content.Server.Body.Components; using Content.Server.Body.Components;
using Content.Server.Body.Systems; using Content.Server.Body.Systems;
using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems; using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Inventory; using Content.Shared.Inventory;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.Physics.Events; using Robust.Shared.Physics.Events;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.EntitySystems namespace Content.Server.Chemistry.EntitySystems
{ {
[UsedImplicitly] [UsedImplicitly]
internal sealed class SolutionInjectOnCollideSystem : EntitySystem internal sealed class SolutionInjectOnCollideSystem : EntitySystem
{ {
[Dependency] private readonly IPrototypeManager _protoManager = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainersSystem = default!;
[Dependency] private readonly SolutionContainerSystem _solutionsSystem = default!;
[Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!; [Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!;
[Dependency] private readonly InventorySystem _inventorySystem = default!; [Dependency] private readonly InventorySystem _inventorySystem = default!;
@@ -30,7 +28,7 @@ namespace Content.Server.Chemistry.EntitySystems
if (!args.OtherBody.Hard || if (!args.OtherBody.Hard ||
!EntityManager.TryGetComponent<BloodstreamComponent>(target, out var bloodstream) || !EntityManager.TryGetComponent<BloodstreamComponent>(target, out var bloodstream) ||
!_solutionsSystem.TryGetInjectableSolution(ent, out var solution)) !_solutionContainersSystem.TryGetInjectableSolution(ent.Owner, out var solution, out _))
{ {
return; return;
} }
@@ -44,7 +42,7 @@ namespace Content.Server.Chemistry.EntitySystems
return; return;
} }
var solRemoved = solution.SplitSolution(component.TransferAmount); var solRemoved = _solutionContainersSystem.SplitSolution(solution.Value, component.TransferAmount);
var solRemovedVol = solRemoved.Volume; var solRemovedVol = solRemoved.Volume;
var solToInject = solRemoved.SplitSolution(solRemovedVol * component.TransferEfficiency); var solToInject = solRemoved.SplitSolution(solRemovedVol * component.TransferEfficiency);

View File

@@ -1,7 +1,6 @@
using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.FixedPoint;
using Robust.Shared.Timing; using Robust.Shared.Timing;
namespace Content.Server.Chemistry.EntitySystems; 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 // timer ignores if it's empty, it's just a fixed cycle
purge.NextPurgeTime += purge.Duration; purge.NextPurgeTime += purge.Duration;
if (_solutionContainer.TryGetSolution(uid, purge.Solution, out var solution, manager)) if (_solutionContainer.TryGetSolution((uid, manager), purge.Solution, out var solution))
_solutionContainer.SplitSolutionWithout(uid, solution, purge.Quantity, purge.Preserve.ToArray()); _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;
} }
} }

View File

@@ -1,5 +1,5 @@
using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems; using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.Random; using Content.Shared.Random;
using Content.Shared.Random.Helpers; using Content.Shared.Random.Helpers;
@@ -21,13 +21,12 @@ public sealed class SolutionRandomFillSystem : EntitySystem
SubscribeLocalEvent<RandomFillSolutionComponent, MapInitEvent>(OnRandomSolutionFillMapInit); 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; return;
var target = _solutionsSystem.EnsureSolution(uid, component.Solution); var pick = _proto.Index<WeightedRandomFillSolutionPrototype>(entity.Comp.WeightedRandomId).Pick(_random);
var pick = _proto.Index<WeightedRandomFillSolutionPrototype>(component.WeightedRandomId).Pick(_random);
var reagent = pick.reagent; var reagent = pick.reagent;
var quantity = pick.quantity; var quantity = pick.quantity;
@@ -38,6 +37,7 @@ public sealed class SolutionRandomFillSystem : EntitySystem
return; return;
} }
target.AddReagent(reagent, quantity); var target = _solutionsSystem.EnsureSolutionEntity(entity.Owner, entity.Comp.Solution, pick.quantity, null, out _);
_solutionsSystem.TryAddReagent(target, reagent, quantity, out _);
} }
} }

View File

@@ -1,7 +1,7 @@
using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Robust.Shared.Timing; using Robust.Shared.Timing;
@@ -31,7 +31,7 @@ public sealed class SolutionRegenerationSystem : EntitySystem
// timer ignores if its full, it's just a fixed cycle // timer ignores if its full, it's just a fixed cycle
regen.NextRegenTime = _timing.CurTime + regen.Duration; 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); var amount = FixedPoint2.Min(solution.AvailableVolume, regen.Generated.Volume);
if (amount <= FixedPoint2.Zero) if (amount <= FixedPoint2.Zero)
@@ -48,13 +48,13 @@ public sealed class SolutionRegenerationSystem : EntitySystem
generated = regen.Generated.Clone().SplitSolution(amount); 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;
} }
} }

View File

@@ -1,12 +1,10 @@
using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Explosion.EntitySystems; using Content.Server.Explosion.EntitySystems;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.FixedPoint;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Robust.Shared.Player;
namespace Content.Server.Chemistry.EntitySystems; namespace Content.Server.Chemistry.EntitySystems;
@@ -21,7 +19,7 @@ namespace Content.Server.Chemistry.EntitySystems;
/// </summary> /// </summary>
public sealed class SolutionSpikableSystem : EntitySystem 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 TriggerSystem _triggerSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
@@ -30,9 +28,9 @@ public sealed class SolutionSpikableSystem : EntitySystem
SubscribeLocalEvent<RefillableSolutionComponent, InteractUsingEvent>(OnInteractUsing); 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> /// <summary>
@@ -49,8 +47,8 @@ public sealed class SolutionSpikableSystem : EntitySystem
{ {
if (!Resolve(source, ref spikableSource, ref managerSource, false) if (!Resolve(source, ref spikableSource, ref managerSource, false)
|| !Resolve(target, ref spikableTarget, ref managerTarget, false) || !Resolve(target, ref spikableTarget, ref managerTarget, false)
|| !_solutionSystem.TryGetRefillableSolution(target, out var targetSolution, managerTarget, spikableTarget) || !_solutionContainerSystem.TryGetRefillableSolution((target, spikableTarget, managerTarget), out var targetSoln, out var targetSolution)
|| !managerSource.Solutions.TryGetValue(spikableSource.SourceSolution, out var sourceSolution)) || !_solutionContainerSystem.TryGetSolution((source, managerSource), spikableSource.SourceSolution, out _, out var sourceSolution))
{ {
return; return;
} }
@@ -61,7 +59,7 @@ public sealed class SolutionSpikableSystem : EntitySystem
return; return;
} }
if (!_solutionSystem.ForceAddSolution(target, targetSolution, sourceSolution)) if (!_solutionContainerSystem.ForceAddSolution(targetSoln.Value, sourceSolution))
return; return;
_popupSystem.PopupEntity(Loc.GetString(spikableSource.Popup, ("spiked-entity", target), ("spike-entity", source)), user, user); _popupSystem.PopupEntity(Loc.GetString(spikableSource.Popup, ("spiked-entity", target), ("spike-entity", source)), user, user);

View File

@@ -1,14 +1,14 @@
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Shared.Verbs; using Content.Server.Chemistry.Containers.EntitySystems;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Content.Shared.Chemistry; using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Verbs;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.Player; using Robust.Shared.Player;
namespace Content.Server.Chemistry.EntitySystems namespace Content.Server.Chemistry.EntitySystems
@@ -24,7 +24,7 @@ namespace Content.Server.Chemistry.EntitySystems
/// <summary> /// <summary>
/// Default transfer amounts for the set-transfer verb. /// Default transfer amounts for the set-transfer verb.
/// </summary> /// </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() public override void Initialize()
{ {
@@ -35,17 +35,19 @@ namespace Content.Server.Chemistry.EntitySystems
SubscribeLocalEvent<SolutionTransferComponent, TransferAmountSetValueMessage>(OnTransferAmountSetValueMessage); 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); var newTransferAmount = FixedPoint2.Clamp(message.Value, entity.Comp.MinimumTransferAmount, entity.Comp.MaximumTransferAmount);
solutionTransfer.TransferAmount = newTransferAmount; entity.Comp.TransferAmount = newTransferAmount;
if (message.Session.AttachedEntity is {Valid: true} user) if (message.Session.AttachedEntity is { Valid: true } user)
_popupSystem.PopupEntity(Loc.GetString("comp-solution-transfer-set-amount", ("amount", newTransferAmount)), uid, 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) if (!args.CanAccess || !args.CanInteract || !component.CanChangeTransferAmount || args.Hands == null)
return; return;
@@ -56,15 +58,16 @@ namespace Content.Server.Chemistry.EntitySystems
AlternativeVerb custom = new(); AlternativeVerb custom = new();
custom.Text = Loc.GetString("comp-solution-transfer-verb-custom-amount"); custom.Text = Loc.GetString("comp-solution-transfer-verb-custom-amount");
custom.Category = VerbCategory.SetTransferAmount; 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; custom.Priority = 1;
args.Verbs.Add(custom); args.Verbs.Add(custom);
// Add specific transfer verbs according to the container's size // Add specific transfer verbs according to the container's size
var priority = 0; var priority = 0;
var user = args.User;
foreach (var amount in DefaultTransferAmounts) foreach (var amount in DefaultTransferAmounts)
{ {
if ( amount < component.MinimumTransferAmount.Int() || amount > component.MaximumTransferAmount.Int()) if (amount < component.MinimumTransferAmount.Int() || amount > component.MaximumTransferAmount.Int())
continue; continue;
AlternativeVerb verb = new(); AlternativeVerb verb = new();
@@ -73,7 +76,7 @@ namespace Content.Server.Chemistry.EntitySystems
verb.Act = () => verb.Act = () =>
{ {
component.TransferAmount = FixedPoint2.New(amount); 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. // 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) if (!args.CanReach || args.Target == null)
return; return;
var target = args.Target!.Value; 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. //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) 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 && _solutionContainerSystem.TryGetDrainableSolution(target, out var targetSoln, out _) // target must be drainable
&& EntityManager.TryGetComponent(uid, out RefillableSolutionComponent? refillComp) && EntityManager.TryGetComponent(uid, out RefillableSolutionComponent? refillComp)
&& _solutionContainerSystem.TryGetRefillableSolution(uid, out var ownerRefill, refillable: 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 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) if (transferred > 0)
{ {
var toTheBrim = ownerRefill.AvailableVolume == 0; var toTheBrim = ownerRefill.AvailableVolume == 0;
@@ -122,8 +126,8 @@ namespace Content.Server.Chemistry.EntitySystems
} }
// if target is refillable, and owner is drainable // if target is refillable, and owner is drainable
if (component.CanSend && _solutionContainerSystem.TryGetRefillableSolution(target, out var targetRefill) if (component.CanSend && _solutionContainerSystem.TryGetRefillableSolution(target, out targetSoln, out var targetRefill)
&& _solutionContainerSystem.TryGetDrainableSolution(uid, out var ownerDrain)) && _solutionContainerSystem.TryGetDrainableSolution(uid, out ownerSoln, out var ownerDrain))
{ {
var transferAmount = component.TransferAmount; var transferAmount = component.TransferAmount;
@@ -132,7 +136,7 @@ namespace Content.Server.Chemistry.EntitySystems
transferAmount = FixedPoint2.Min(transferAmount, (FixedPoint2) refill.MaxRefill); 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) if (transferred > 0)
{ {
@@ -150,9 +154,9 @@ namespace Content.Server.Chemistry.EntitySystems
/// <returns>The actual amount transferred.</returns> /// <returns>The actual amount transferred.</returns>
public FixedPoint2 Transfer(EntityUid user, public FixedPoint2 Transfer(EntityUid user,
EntityUid sourceEntity, EntityUid sourceEntity,
Solution source, Entity<SolutionComponent> source,
EntityUid targetEntity, EntityUid targetEntity,
Solution target, Entity<SolutionComponent> target,
FixedPoint2 amount) FixedPoint2 amount)
{ {
var transferAttempt = new SolutionTransferAttemptEvent(sourceEntity, targetEntity); var transferAttempt = new SolutionTransferAttemptEvent(sourceEntity, targetEntity);
@@ -165,7 +169,8 @@ namespace Content.Server.Chemistry.EntitySystems
return FixedPoint2.Zero; 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); _popupSystem.PopupEntity(Loc.GetString("comp-solution-transfer-is-empty", ("target", sourceEntity)), sourceEntity, user);
return FixedPoint2.Zero; return FixedPoint2.Zero;
@@ -179,19 +184,20 @@ namespace Content.Server.Chemistry.EntitySystems
return FixedPoint2.Zero; 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); _popupSystem.PopupEntity(Loc.GetString("comp-solution-transfer-is-full", ("target", targetEntity)), targetEntity, user);
return FixedPoint2.Zero; 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); var solution = _solutionContainerSystem.Drain(sourceEntity, source, actualAmount);
_solutionContainerSystem.Refill(targetEntity, target, solution); _solutionContainerSystem.Refill(targetEntity, target, solution);
_adminLogger.Add(LogType.Action, LogImpact.Medium, _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; return actualAmount;
} }

View File

@@ -1,4 +1,5 @@
using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -16,38 +17,38 @@ public sealed class TransformableContainerSystem : EntitySystem
base.Initialize(); base.Initialize();
SubscribeLocalEvent<TransformableContainerComponent, MapInitEvent>(OnMapInit); 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); var meta = MetaData(entity.Owner);
if (string.IsNullOrEmpty(component.InitialName)) 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, private void OnSolutionChange(Entity<TransformableContainerComponent> entity, ref SolutionContainerChangedEvent args)
SolutionChangedEvent args)
{ {
if (!_solutionsSystem.TryGetFitsInDispenser(owner, out var solution)) if (!_solutionsSystem.TryGetFitsInDispenser(entity.Owner, out _, out var solution))
return; return;
//Transform container into initial state when emptied //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 //the biggest reagent in the solution decides the appearance
var reagentId = solution.GetPrimaryReagentId(); var reagentId = solution.GetPrimaryReagentId();
//If biggest reagent didn't changed - don't change anything at all //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; return;
} }
@@ -56,29 +57,29 @@ public sealed class TransformableContainerSystem : EntitySystem
if (!string.IsNullOrWhiteSpace(reagentId?.Prototype) if (!string.IsNullOrWhiteSpace(reagentId?.Prototype)
&& _prototypeManager.TryIndex(reagentId.Value.Prototype, out ReagentPrototype? proto)) && _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)); var val = Loc.GetString("transformable-container-component-glass", ("name", proto.LocalizedName));
_metadataSystem.SetEntityName(owner, val, metadata); _metadataSystem.SetEntityName(entity.Owner, val, metadata);
_metadataSystem.SetEntityDescription(owner, proto.LocalizedDescription, metadata); _metadataSystem.SetEntityDescription(entity.Owner, proto.LocalizedDescription, metadata);
component.CurrentReagent = proto; entity.Comp.CurrentReagent = proto;
component.Transformed = true; entity.Comp.Transformed = true;
} }
} }
private void CancelTransformation(EntityUid owner, TransformableContainerComponent component) private void CancelTransformation(Entity<TransformableContainerComponent> entity)
{ {
component.CurrentReagent = null; entity.Comp.CurrentReagent = null;
component.Transformed = false; 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);
} }
} }
} }

View File

@@ -1,9 +1,8 @@
using System.Numerics;
using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Chemistry; using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.Physics; using Content.Shared.Physics;
@@ -16,6 +15,7 @@ using Robust.Shared.Physics.Events;
using Robust.Shared.Physics.Systems; using Robust.Shared.Physics.Systems;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Spawners; using Robust.Shared.Spawners;
using System.Numerics;
namespace Content.Server.Chemistry.EntitySystems namespace Content.Server.Chemistry.EntitySystems
{ {
@@ -40,19 +40,20 @@ namespace Content.Server.Chemistry.EntitySystems
SubscribeLocalEvent<VaporComponent, StartCollideEvent>(HandleCollide); 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 // Check for collision with a impassable object (e.g. wall) and stop
if ((args.OtherFixture.CollisionLayer & (int) CollisionGroup.Impassable) != 0 && args.OtherFixture.Hard) 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; return false;
} }
if (!_solutionContainerSystem.TryGetSolution(vapor, VaporComponent.SolutionName, if (!_solutionContainerSystem.TryGetSolution(vapor.Owner, VaporComponent.SolutionName, out var vaporSolution))
out var vaporSolution))
{ {
return false; return false;
} }
return _solutionContainerSystem.TryAddSolution(vapor, vaporSolution, solution); return _solutionContainerSystem.TryAddSolution(vaporSolution.Value, solution);
} }
public override void Update(float frameTime) public override void Update(float frameTime)
{ {
var query = EntityQueryEnumerator<VaporComponent, SolutionContainerManagerComponent, TransformComponent>(); 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; var (entity, vapor) = ent;
if (!vapor.Active) if (!vapor.Active)
@@ -112,6 +112,7 @@ namespace Content.Server.Chemistry.EntitySystems
vapor.ReactTimer += frameTime; vapor.ReactTimer += frameTime;
var contents = soln.Comp.Solution;
if (vapor.ReactTimer >= ReactTime && TryComp(xform.GridUid, out MapGridComponent? gridComp)) if (vapor.ReactTimer >= ReactTime && TryComp(xform.GridUid, out MapGridComponent? gridComp))
{ {
vapor.ReactTimer = 0; vapor.ReactTimer = 0;
@@ -131,7 +132,7 @@ namespace Content.Server.Chemistry.EntitySystems
reaction = reagentQuantity.Quantity; reaction = reagentQuantity.Quantity;
} }
_solutionContainerSystem.RemoveReagent(entity, contents, reagentQuantity.Reagent, reaction); _solutionContainerSystem.RemoveReagent(soln, reagentQuantity.Reagent, reaction);
} }
} }

View File

@@ -1,6 +1,5 @@
using Content.Server.Fluids.EntitySystems; using Content.Server.Fluids.EntitySystems;
using Content.Shared.Audio; using Content.Shared.Audio;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.Coordinates.Helpers; using Content.Shared.Coordinates.Helpers;
using Content.Shared.Database; using Content.Shared.Database;
@@ -30,7 +29,7 @@ namespace Content.Server.Chemistry.ReactionEffects
/// <summary> /// <summary>
/// How many units of reaction for 1 smoke entity. /// How many units of reaction for 1 smoke entity.
/// </summary> /// </summary>
[DataField("overflowThreshold")] public FixedPoint2 OverflowThreshold = FixedPoint2.New(2.5); [DataField] public FixedPoint2 OverflowThreshold = FixedPoint2.New(2.5);
/// <summary> /// <summary>
/// The entity prototype that will be spawned as the effect. /// The entity prototype that will be spawned as the effect.
@@ -56,7 +55,7 @@ namespace Content.Server.Chemistry.ReactionEffects
return; return;
var spreadAmount = (int) Math.Max(0, Math.Ceiling((args.Quantity / OverflowThreshold).Float())); 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 transform = args.EntityManager.GetComponent<TransformComponent>(args.SolutionEntity);
var mapManager = IoCManager.Resolve<IMapManager>(); var mapManager = IoCManager.Resolve<IMapManager>();

View File

@@ -11,13 +11,13 @@ public sealed partial class CreateEntityReactionEffect : ReagentEffect
/// <summary> /// <summary>
/// What entity to create. /// What entity to create.
/// </summary> /// </summary>
[DataField("entity", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))] [DataField(required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string Entity = default!; public string Entity = default!;
/// <summary> /// <summary>
/// How many entities to create per unit reaction. /// How many entities to create per unit reaction.
/// </summary> /// </summary>
[DataField("number")] [DataField]
public uint Number = 1; public uint Number = 1;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)

View File

@@ -23,7 +23,7 @@ public sealed partial class EmpReactionEffect : ReagentEffect
/// <summary> /// <summary>
/// How much energy will be drain from sources /// How much energy will be drain from sources
/// </summary> /// </summary>
[DataField("energyConsumption")] [DataField]
public float EnergyConsumption = 12500; public float EnergyConsumption = 12500;
/// <summary> /// <summary>

View File

@@ -1,10 +1,10 @@
using System.Text.Json.Serialization;
using Content.Server.Explosion.EntitySystems; using Content.Server.Explosion.EntitySystems;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.Explosion; using Content.Shared.Explosion;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using System.Text.Json.Serialization;
namespace Content.Server.Chemistry.ReactionEffects namespace Content.Server.Chemistry.ReactionEffects
{ {
@@ -14,7 +14,7 @@ namespace Content.Server.Chemistry.ReactionEffects
/// <summary> /// <summary>
/// The type of explosion. Determines damage types and tile break chance scaling. /// The type of explosion. Determines damage types and tile break chance scaling.
/// </summary> /// </summary>
[DataField("explosionType", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<ExplosionPrototype>))] [DataField(required: true, customTypeSerializer: typeof(PrototypeIdSerializer<ExplosionPrototype>))]
[JsonIgnore] [JsonIgnore]
public string ExplosionType = default!; 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 /// The max intensity the explosion can have at a given tile. Places an upper limit of damage and tile break
/// chance. /// chance.
/// </summary> /// </summary>
[DataField("maxIntensity")] [DataField]
[JsonIgnore] [JsonIgnore]
public float MaxIntensity = 5; public float MaxIntensity = 5;
/// <summary> /// <summary>
/// How quickly intensity drops off as you move away from the epicenter /// How quickly intensity drops off as you move away from the epicenter
/// </summary> /// </summary>
[DataField("intensitySlope")] [DataField]
[JsonIgnore] [JsonIgnore]
public float IntensitySlope = 1; public float IntensitySlope = 1;
@@ -40,14 +40,14 @@ namespace Content.Server.Chemistry.ReactionEffects
/// <remarks> /// <remarks>
/// A slope of 1 and MaxTotalIntensity of 100 corresponds to a radius of around 4.5 tiles. /// A slope of 1 and MaxTotalIntensity of 100 corresponds to a radius of around 4.5 tiles.
/// </remarks> /// </remarks>
[DataField("maxTotalIntensity")] [DataField]
[JsonIgnore] [JsonIgnore]
public float MaxTotalIntensity = 100; public float MaxTotalIntensity = 100;
/// <summary> /// <summary>
/// The intensity of the explosion per unit reaction. /// The intensity of the explosion per unit reaction.
/// </summary> /// </summary>
[DataField("intensityPerUnit")] [DataField]
[JsonIgnore] [JsonIgnore]
public float IntensityPerUnit = 1; public float IntensityPerUnit = 1;

View File

@@ -1,7 +1,5 @@
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using static Robust.Shared.Physics.DynamicTree;
namespace Content.Server.Chemistry.ReactionEffects namespace Content.Server.Chemistry.ReactionEffects
{ {
@@ -121,5 +119,3 @@ namespace Content.Server.Chemistry.ReactionEffects
} }
} }

View File

@@ -10,10 +10,10 @@ namespace Content.Server.Chemistry.ReagentEffectConditions
/// </summary> /// </summary>
public sealed partial class Temperature : ReagentEffectCondition public sealed partial class Temperature : ReagentEffectCondition
{ {
[DataField("min")] [DataField]
public float Min = 0; public float Min = 0;
[DataField("max")] [DataField]
public float Max = float.PositiveInfinity; public float Max = float.PositiveInfinity;
public override bool Condition(ReagentEffectArgs args) public override bool Condition(ReagentEffectArgs args)
{ {

View File

@@ -9,10 +9,10 @@ namespace Content.Server.Chemistry.ReagentEffectConditions;
[UsedImplicitly] [UsedImplicitly]
public sealed partial class HasTag : ReagentEffectCondition public sealed partial class HasTag : ReagentEffectCondition
{ {
[DataField("tag", customTypeSerializer: typeof(PrototypeIdSerializer<TagPrototype>))] [DataField(customTypeSerializer: typeof(PrototypeIdSerializer<TagPrototype>))]
public string Tag = default!; public string Tag = default!;
[DataField("invert")] [DataField]
public bool Invert = false; public bool Invert = false;
public override bool Condition(ReagentEffectArgs args) public override bool Condition(ReagentEffectArgs args)

View File

@@ -7,16 +7,14 @@ namespace Content.Server.Chemistry.ReagentEffectConditions
{ {
public sealed partial class MobStateCondition : ReagentEffectCondition public sealed partial class MobStateCondition : ReagentEffectCondition
{ {
[DataField]
public MobState Mobstate = MobState.Alive;
[DataField("mobstate")]
public MobState mobstate = MobState.Alive;
public override bool Condition(ReagentEffectArgs args) public override bool Condition(ReagentEffectArgs args)
{ {
if (args.EntityManager.TryGetComponent(args.SolutionEntity, out MobStateComponent? mobState)) if (args.EntityManager.TryGetComponent(args.SolutionEntity, out MobStateComponent? mobState))
{ {
if (mobState.CurrentState == mobstate) if (mobState.CurrentState == Mobstate)
return true; return true;
} }
@@ -25,7 +23,7 @@ namespace Content.Server.Chemistry.ReagentEffectConditions
public override string GuidebookExplanation(IPrototypeManager prototype) 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));
} }
} }
} }

View File

@@ -11,13 +11,13 @@ namespace Content.Server.Chemistry.ReagentEffectConditions
/// </summary> /// </summary>
public sealed partial class OrganType : ReagentEffectCondition 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!; public string Type = default!;
/// <summary> /// <summary>
/// Does this condition pass when the organ has the type, or when it doesn't have the type? /// Does this condition pass when the organ has the type, or when it doesn't have the type?
/// </summary> /// </summary>
[DataField("shouldHave")] [DataField]
public bool ShouldHave = true; public bool ShouldHave = true;
public override bool Condition(ReagentEffectArgs args) public override bool Condition(ReagentEffectArgs args)

View File

@@ -13,14 +13,14 @@ namespace Content.Server.Chemistry.ReagentEffectConditions
/// </summary> /// </summary>
public sealed partial class ReagentThreshold : ReagentEffectCondition public sealed partial class ReagentThreshold : ReagentEffectCondition
{ {
[DataField("min")] [DataField]
public FixedPoint2 Min = FixedPoint2.Zero; public FixedPoint2 Min = FixedPoint2.Zero;
[DataField("max")] [DataField]
public FixedPoint2 Max = FixedPoint2.MaxValue; public FixedPoint2 Max = FixedPoint2.MaxValue;
// TODO use ReagentId // TODO use ReagentId
[DataField("reagent")] [DataField]
public string? Reagent; public string? Reagent;
public override bool Condition(ReagentEffectArgs args) public override bool Condition(ReagentEffectArgs args)

View File

@@ -9,10 +9,10 @@ namespace Content.Server.Chemistry.ReagentEffectConditions
/// </summary> /// </summary>
public sealed partial class SolutionTemperature : ReagentEffectCondition public sealed partial class SolutionTemperature : ReagentEffectCondition
{ {
[DataField("min")] [DataField]
public float Min = 0.0f; public float Min = 0.0f;
[DataField("max")] [DataField]
public float Max = float.PositiveInfinity; public float Max = float.PositiveInfinity;
public override bool Condition(ReagentEffectArgs args) public override bool Condition(ReagentEffectArgs args)
{ {

View File

@@ -7,10 +7,10 @@ namespace Content.Server.Chemistry.ReagentEffectConditions
{ {
public sealed partial class TotalDamage : ReagentEffectCondition public sealed partial class TotalDamage : ReagentEffectCondition
{ {
[DataField("max")] [DataField]
public FixedPoint2 Max = FixedPoint2.MaxValue; public FixedPoint2 Max = FixedPoint2.MaxValue;
[DataField("min")] [DataField]
public FixedPoint2 Min = FixedPoint2.Zero; public FixedPoint2 Min = FixedPoint2.Zero;
public override bool Condition(ReagentEffectArgs args) public override bool Condition(ReagentEffectArgs args)

View File

@@ -1,4 +1,4 @@
using Content.Shared.Chemistry.EntitySystems; using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -17,12 +17,11 @@ namespace Content.Server.Chemistry.ReagentEffects
return; return;
// TODO see if this is correct // TODO see if this is correct
if (!EntitySystem.Get<SolutionContainerSystem>() var solutionContainerSystem = args.EntityManager.System<SolutionContainerSystem>();
.TryGetSolution(args.SolutionEntity, _solution, out var solutionContainer)) if (!solutionContainerSystem.TryGetSolution(args.SolutionEntity, _solution, out var solutionContainer))
return; return;
if (EntitySystem.Get<SolutionContainerSystem>() if (solutionContainerSystem.TryAddReagent(solutionContainer.Value, args.Reagent.ID, args.Quantity, out var accepted))
.TryAddReagent(args.SolutionEntity, solutionContainer, args.Reagent.ID, args.Quantity, out var accepted))
args.Source?.RemoveReagent(args.Reagent.ID, accepted); args.Source?.RemoveReagent(args.Reagent.ID, accepted);
} }

View File

@@ -10,13 +10,13 @@ public sealed partial class AdjustAlert : ReagentEffect
[DataField("alertType", required: true)] [DataField("alertType", required: true)]
public AlertType Type; public AlertType Type;
[DataField("clear")] [DataField]
public bool Clear; public bool Clear;
[DataField("cooldown")] [DataField]
public bool Cooldown; public bool Cooldown;
[DataField("time")] [DataField]
public float Time; public float Time;
//JUSTIFICATION: This just changes some visuals, doesn't need to be documented. //JUSTIFICATION: This just changes some visuals, doesn't need to be documented.

View File

@@ -1,5 +1,4 @@
using Content.Shared.Body.Prototypes; using Content.Shared.Body.Prototypes;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using JetBrains.Annotations; using JetBrains.Annotations;
@@ -14,7 +13,7 @@ namespace Content.Server.Chemistry.ReagentEffects
/// <summary> /// <summary>
/// The reagent ID to remove. Only one of this and <see cref="Group"/> should be active. /// The reagent ID to remove. Only one of this and <see cref="Group"/> should be active.
/// </summary> /// </summary>
[DataField("reagent", customTypeSerializer:typeof(PrototypeIdSerializer<ReagentPrototype>))] [DataField(customTypeSerializer: typeof(PrototypeIdSerializer<ReagentPrototype>))]
public string? Reagent = null; public string? Reagent = null;
// TODO use ReagentId // TODO use ReagentId
@@ -22,41 +21,39 @@ namespace Content.Server.Chemistry.ReagentEffects
/// The metabolism group to remove, if the reagent satisfies any. /// The metabolism group to remove, if the reagent satisfies any.
/// Only one of this and <see cref="Reagent"/> should be active. /// Only one of this and <see cref="Reagent"/> should be active.
/// </summary> /// </summary>
[DataField("group", customTypeSerializer:typeof(PrototypeIdSerializer<MetabolismGroupPrototype>))] [DataField(customTypeSerializer: typeof(PrototypeIdSerializer<MetabolismGroupPrototype>))]
public string? Group = null; public string? Group = null;
[DataField("amount", required: true)] [DataField(required: true)]
public FixedPoint2 Amount = default!; public FixedPoint2 Amount = default!;
public override void Effect(ReagentEffectArgs args) public override void Effect(ReagentEffectArgs args)
{ {
if (args.Source != null) if (args.Source == null)
return;
var amount = Amount;
amount *= args.Scale;
if (Reagent != null)
{ {
var solutionSys = args.EntityManager.EntitySysManager.GetEntitySystem<SolutionContainerSystem>(); if (amount < 0 && args.Source.ContainsPrototype(Reagent))
var amount = Amount; args.Source.RemoveReagent(Reagent, -amount);
if (amount > 0)
amount *= args.Scale; args.Source.AddReagent(Reagent, amount);
}
if (Reagent != null) else if (Group != null)
{
var prototypeMan = IoCManager.Resolve<IPrototypeManager>();
foreach (var quant in args.Source.Contents.ToArray())
{ {
if (amount < 0 && args.Source.ContainsPrototype(Reagent)) var proto = prototypeMan.Index<ReagentPrototype>(quant.Reagent.Prototype);
solutionSys.RemoveReagent(args.SolutionEntity, args.Source, Reagent, -amount); if (proto.Metabolisms != null && proto.Metabolisms.ContainsKey(Group))
if (amount > 0)
solutionSys.TryAddReagent(args.SolutionEntity, args.Source, Reagent, amount, out _);
}
else if (Group != null)
{
var prototypeMan = IoCManager.Resolve<IPrototypeManager>();
foreach (var quant in args.Source.Contents.ToArray())
{ {
var proto = prototypeMan.Index<ReagentPrototype>(quant.Reagent.Prototype); if (amount < 0)
if (proto.Metabolisms != null && proto.Metabolisms.ContainsKey(Group)) args.Source.RemoveReagent(quant.Reagent, amount);
{ if (amount > 0)
if (amount < 0) args.Source.AddReagent(quant.Reagent, amount);
solutionSys.RemoveReagent(args.SolutionEntity, args.Source, quant.Reagent, amount);
if (amount > 0)
solutionSys.TryAddReagent(args.SolutionEntity, args.Source, quant.Reagent, amount, out _);
}
} }
} }
} }

View File

@@ -7,7 +7,7 @@ namespace Content.Server.Chemistry.ReagentEffects
{ {
public sealed partial class AdjustTemperature : ReagentEffect public sealed partial class AdjustTemperature : ReagentEffect
{ {
[DataField("amount")] [DataField]
public float Amount; public float Amount;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)

View File

@@ -1,9 +1,7 @@
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Prototypes;
using Robust.Shared.Configuration;
using Content.Server.Zombies; using Content.Server.Zombies;
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Configuration;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects; namespace Content.Server.Chemistry.ReagentEffects;

View File

@@ -1,6 +1,6 @@
using Content.Server.Body.Systems;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using JetBrains.Annotations; using JetBrains.Annotations;
using Content.Server.Body.Systems;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReactionEffects namespace Content.Server.Chemistry.ReactionEffects
@@ -11,7 +11,7 @@ namespace Content.Server.Chemistry.ReactionEffects
[UsedImplicitly] [UsedImplicitly]
public sealed partial class ChemCleanBloodstream : ReagentEffect public sealed partial class ChemCleanBloodstream : ReagentEffect
{ {
[DataField("cleanseRate")] [DataField]
public float CleanseRate = 3.0f; public float CleanseRate = 3.0f;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)

View File

@@ -15,7 +15,7 @@ namespace Content.Server.Chemistry.ReagentEffects
/// <summary> /// <summary>
/// How much eye damage to add. /// How much eye damage to add.
/// </summary> /// </summary>
[DataField("amount")] [DataField]
public int Amount = -1; public int Amount = -1;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)

View File

@@ -1,5 +1,5 @@
using Content.Shared.Chemistry.Reagent;
using Content.Server.Medical; using Content.Server.Medical;
using Content.Shared.Chemistry.Reagent;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -12,10 +12,10 @@ namespace Content.Server.Chemistry.ReagentEffects
public sealed partial class ChemVomit : ReagentEffect public sealed partial class ChemVomit : ReagentEffect
{ {
/// How many units of thirst to add each time we vomit /// How many units of thirst to add each time we vomit
[DataField("thirstAmount")] [DataField]
public float ThirstAmount = -8f; public float ThirstAmount = -8f;
/// How many units of hunger to add each time we vomit /// How many units of hunger to add each time we vomit
[DataField("hungerAmount")] [DataField]
public float HungerAmount = -8f; public float HungerAmount = -8f;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)

View File

@@ -8,13 +8,13 @@ namespace Content.Server.Chemistry.ReagentEffects;
public sealed partial class CreateGas : ReagentEffect public sealed partial class CreateGas : ReagentEffect
{ {
[DataField("gas", required: true)] [DataField(required: true)]
public Gas Gas = default!; public Gas Gas = default!;
/// <summary> /// <summary>
/// For each unit consumed, how many moles of gas should be created? /// For each unit consumed, how many moles of gas should be created?
/// </summary> /// </summary>
[DataField("multiplier")] [DataField]
public float Multiplier = 3f; public float Multiplier = 3f;
public override bool ShouldLog => true; public override bool ShouldLog => true;

View File

@@ -1,14 +1,13 @@
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Prototypes;
using Robust.Shared.Configuration;
using Content.Server.Zombies; using Content.Server.Zombies;
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Configuration;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects; namespace Content.Server.Chemistry.ReagentEffects;
public sealed partial class CureZombieInfection : ReagentEffect public sealed partial class CureZombieInfection : ReagentEffect
{ {
[DataField("innoculate")] [DataField]
public bool Innoculate; public bool Innoculate;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)

View File

@@ -9,13 +9,13 @@ public sealed partial class Drunk : ReagentEffect
/// <summary> /// <summary>
/// BoozePower is how long each metabolism cycle will make the drunk effect last for. /// BoozePower is how long each metabolism cycle will make the drunk effect last for.
/// </summary> /// </summary>
[DataField("boozePower")] [DataField]
public float BoozePower = 3f; public float BoozePower = 3f;
/// <summary> /// <summary>
/// Whether speech should be slurred. /// Whether speech should be slurred.
/// </summary> /// </summary>
[DataField("slurSpeech")] [DataField]
public bool SlurSpeech = true; public bool SlurSpeech = true;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)

View File

@@ -6,14 +6,14 @@ namespace Content.Server.Chemistry.ReagentEffects;
public sealed partial class Electrocute : ReagentEffect 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> /// <remarks>
/// true - refresh electrocute time, false - accumulate electrocute time /// true - refresh electrocute time, false - accumulate electrocute time
/// </remarks> /// </remarks>
[DataField("refresh")] public bool Refresh = true; [DataField] public bool Refresh = true;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-electrocute", ("chance", Probability), ("time", ElectrocuteTime)); => Loc.GetString("reagent-effect-guidebook-electrocute", ("chance", Probability), ("time", ElectrocuteTime));

View File

@@ -16,7 +16,7 @@ public sealed partial class Emote : ReagentEffect
[DataField("emote", customTypeSerializer: typeof(PrototypeIdSerializer<EmotePrototype>))] [DataField("emote", customTypeSerializer: typeof(PrototypeIdSerializer<EmotePrototype>))]
public string? EmoteId; public string? EmoteId;
[DataField("showInChat")] [DataField]
public bool ShowInChat; public bool ShowInChat;
// JUSTIFICATION: Emoting is flavor, so same reason popup messages are not in here. // JUSTIFICATION: Emoting is flavor, so same reason popup messages are not in here.

View File

@@ -10,7 +10,7 @@ namespace Content.Server.Chemistry.ReagentEffects
[UsedImplicitly] [UsedImplicitly]
public sealed partial class FlammableReaction : ReagentEffect public sealed partial class FlammableReaction : ReagentEffect
{ {
[DataField("multiplier")] [DataField]
public float Multiplier = 0.05f; public float Multiplier = 0.05f;
public override bool ShouldLog => true; public override bool ShouldLog => true;

View File

@@ -1,5 +1,3 @@
using System.Linq;
using System.Text.Json.Serialization;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes; using Content.Shared.Damage.Prototypes;
@@ -7,6 +5,8 @@ using Content.Shared.FixedPoint;
using Content.Shared.Localizations; using Content.Shared.Localizations;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using System.Linq;
using System.Text.Json.Serialization;
namespace Content.Server.Chemistry.ReagentEffects namespace Content.Server.Chemistry.ReagentEffects
{ {
@@ -19,19 +19,19 @@ namespace Content.Server.Chemistry.ReagentEffects
/// <summary> /// <summary>
/// Damage to apply every metabolism cycle. Damage Ignores resistances. /// Damage to apply every metabolism cycle. Damage Ignores resistances.
/// </summary> /// </summary>
[DataField(required: true)]
[JsonPropertyName("damage")] [JsonPropertyName("damage")]
[DataField("damage", required: true)]
public DamageSpecifier Damage = default!; public DamageSpecifier Damage = default!;
/// <summary> /// <summary>
/// Should this effect scale the damage by the amount of chemical in the solution? /// Should this effect scale the damage by the amount of chemical in the solution?
/// Useful for touch reactions, like styptic powder or acid. /// Useful for touch reactions, like styptic powder or acid.
/// </summary> /// </summary>
[DataField]
[JsonPropertyName("scaleByQuantity")] [JsonPropertyName("scaleByQuantity")]
[DataField("scaleByQuantity")]
public bool ScaleByQuantity; public bool ScaleByQuantity;
[DataField("ignoreResistances")] [DataField]
[JsonPropertyName("ignoreResistances")] [JsonPropertyName("ignoreResistances")]
public bool IgnoreResistances = true; public bool IgnoreResistances = true;

View File

@@ -7,10 +7,10 @@ namespace Content.Server.Chemistry.ReagentEffects;
public sealed partial class ModifyBleedAmount : ReagentEffect public sealed partial class ModifyBleedAmount : ReagentEffect
{ {
[DataField("scaled")] [DataField]
public bool Scaled = false; public bool Scaled = false;
[DataField("amount")] [DataField]
public float Amount = -1.0f; public float Amount = -1.0f;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)

View File

@@ -8,10 +8,10 @@ namespace Content.Server.Chemistry.ReagentEffects;
public sealed partial class ModifyBloodLevel : ReagentEffect public sealed partial class ModifyBloodLevel : ReagentEffect
{ {
[DataField("scaled")] [DataField]
public bool Scaled = false; public bool Scaled = false;
[DataField("amount")] [DataField]
public FixedPoint2 Amount = 1.0f; public FixedPoint2 Amount = 1.0f;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)

View File

@@ -15,19 +15,19 @@ namespace Content.Server.Chemistry.ReagentEffects
/// <summary> /// <summary>
/// How much the entities' walk speed is multiplied by. /// How much the entities' walk speed is multiplied by.
/// </summary> /// </summary>
[DataField("walkSpeedModifier")] [DataField]
public float WalkSpeedModifier { get; set; } = 1; public float WalkSpeedModifier { get; set; } = 1;
/// <summary> /// <summary>
/// How much the entities' run speed is multiplied by. /// How much the entities' run speed is multiplied by.
/// </summary> /// </summary>
[DataField("sprintSpeedModifier")] [DataField]
public float SprintSpeedModifier { get; set; } = 1; public float SprintSpeedModifier { get; set; } = 1;
/// <summary> /// <summary>
/// How long the modifier applies (in seconds) when metabolized. /// How long the modifier applies (in seconds) when metabolized.
/// </summary> /// </summary>
[DataField("statusLifetime")] [DataField]
public float StatusLifetime = 2f; public float StatusLifetime = 2f;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)

View File

@@ -7,7 +7,7 @@ namespace Content.Server.Chemistry.ReagentEffects;
public sealed partial class Oxygenate : ReagentEffect public sealed partial class Oxygenate : ReagentEffect
{ {
[DataField("factor")] [DataField]
public float Factor = 1f; public float Factor = 1f;
// JUSTIFICATION: This is internal magic that players never directly interact with. // JUSTIFICATION: This is internal magic that players never directly interact with.

View File

@@ -1,17 +1,17 @@
using Content.Shared.Chemistry.Reagent;
using Content.Server.Stunnable; using Content.Server.Stunnable;
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects; namespace Content.Server.Chemistry.ReagentEffects;
public sealed partial class Paralyze : ReagentEffect public sealed partial class Paralyze : ReagentEffect
{ {
[DataField("paralyzeTime")] public double ParalyzeTime = 2; [DataField] public double ParalyzeTime = 2;
/// <remarks> /// <remarks>
/// true - refresh paralyze time, false - accumulate paralyze time /// true - refresh paralyze time, false - accumulate paralyze time
/// </remarks> /// </remarks>
[DataField("refresh")] public bool Refresh = true; [DataField] public bool Refresh = true;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-paralyze", => Loc.GetString("reagent-effect-guidebook-paralyze",

View 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 Content.Shared.Chemistry.Reagent;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
using System.Diagnostics.CodeAnalysis;
namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
{ {
[ImplicitDataDefinitionForInheritors] [ImplicitDataDefinitionForInheritors]
public abstract partial class PlantAdjustAttribute : ReagentEffect public abstract partial class PlantAdjustAttribute : ReagentEffect
{ {
[DataField("amount")] public float Amount { get; protected set; } = 1; [DataField]
[DataField("prob")] public float Prob { get; protected set; } = 1; // = (80); public float Amount { get; protected set; } = 1;
[DataField]
public float Prob { get; protected set; } = 1; // = (80);
/// <summary> /// <summary>
/// Checks if the plant holder can metabolize the reagent or not. Checks if it has an alive plant by default. /// Checks if the plant holder can metabolize the reagent or not. Checks if it has an alive plant by default.

View File

@@ -11,13 +11,13 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
[DataDefinition] [DataDefinition]
public sealed partial class RobustHarvest : ReagentEffect public sealed partial class RobustHarvest : ReagentEffect
{ {
[DataField("potencyLimit")] [DataField]
public int PotencyLimit = 50; public int PotencyLimit = 50;
[DataField("potencyIncrease")] [DataField]
public int PotencyIncrease = 3; public int PotencyIncrease = 3;
[DataField("potencySeedlessThreshold")] [DataField]
public int PotencySeedlessThreshold = 30; public int PotencySeedlessThreshold = 30;
public override void Effect(ReagentEffectArgs args) public override void Effect(ReagentEffectArgs args)

View File

@@ -8,13 +8,13 @@ namespace Content.Server.Chemistry.ReagentEffects
{ {
public sealed partial class PopupMessage : ReagentEffect public sealed partial class PopupMessage : ReagentEffect
{ {
[DataField("messages", required: true)] [DataField(required: true)]
public string[] Messages = default!; public string[] Messages = default!;
[DataField("type")] [DataField]
public PopupRecipients Type = PopupRecipients.Local; public PopupRecipients Type = PopupRecipients.Local;
[DataField("visualType")] [DataField]
public PopupType VisualType = PopupType.Small; public PopupType VisualType = PopupType.Small;
// JUSTIFICATION: This is purely cosmetic. // JUSTIFICATION: This is purely cosmetic.

View File

@@ -17,25 +17,25 @@ namespace Content.Server.Chemistry.ReagentEffects.StatusEffects
[UsedImplicitly] [UsedImplicitly]
public sealed partial class GenericStatusEffect : ReagentEffect public sealed partial class GenericStatusEffect : ReagentEffect
{ {
[DataField("key", required: true)] [DataField(required: true)]
public string Key = default!; public string Key = default!;
[DataField("component")] [DataField]
public string Component = String.Empty; public string Component = String.Empty;
[DataField("time")] [DataField]
public float Time = 2.0f; public float Time = 2.0f;
/// <remarks> /// <remarks>
/// true - refresh status effect time, false - accumulate status effect time /// true - refresh status effect time, false - accumulate status effect time
/// </remarks> /// </remarks>
[DataField("refresh")] [DataField]
public bool Refresh = true; public bool Refresh = true;
/// <summary> /// <summary>
/// Should this effect add the status effect, remove time from it, or set its cooldown? /// Should this effect add the status effect, remove time from it, or set its cooldown?
/// </summary> /// </summary>
[DataField("type")] [DataField]
public StatusEffectMetabolismType Type = StatusEffectMetabolismType.Add; public StatusEffectMetabolismType Type = StatusEffectMetabolismType.Add;
public override void Effect(ReagentEffectArgs args) public override void Effect(ReagentEffectArgs args)

View File

@@ -11,19 +11,19 @@ namespace Content.Server.Chemistry.ReagentEffects.StatusEffects
/// </summary> /// </summary>
public sealed partial class Jitter : ReagentEffect public sealed partial class Jitter : ReagentEffect
{ {
[DataField("amplitude")] [DataField]
public float Amplitude = 10.0f; public float Amplitude = 10.0f;
[DataField("frequency")] [DataField]
public float Frequency = 4.0f; public float Frequency = 4.0f;
[DataField("time")] [DataField]
public float Time = 2.0f; public float Time = 2.0f;
/// <remarks> /// <remarks>
/// true - refresh jitter time, false - accumulate jitter time /// true - refresh jitter time, false - accumulate jitter time
/// </remarks> /// </remarks>
[DataField("refresh")] [DataField]
public bool Refresh = true; public bool Refresh = true;
public override void Effect(ReagentEffectArgs args) public override void Effect(ReagentEffectArgs args)

View File

@@ -1,4 +1,3 @@
using System.Numerics;
using Content.Server.Decals; using Content.Server.Decals;
using Content.Shared.Chemistry.Reaction; using Content.Shared.Chemistry.Reaction;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
@@ -6,6 +5,7 @@ using Content.Shared.Decals;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Map.Components; using Robust.Shared.Map.Components;
using System.Numerics;
namespace Content.Server.Chemistry.TileReactions; namespace Content.Server.Chemistry.TileReactions;
@@ -18,7 +18,7 @@ public sealed partial class CleanDecalsReaction : ITileReaction
/// <summary> /// <summary>
/// For every cleaned decal we lose this much reagent. /// For every cleaned decal we lose this much reagent.
/// </summary> /// </summary>
[DataField("cleanCost")] [DataField]
public FixedPoint2 CleanCost { get; private set; } = FixedPoint2.New(0.25f); public FixedPoint2 CleanCost { get; private set; } = FixedPoint2.New(0.25f);
public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume) public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume)

View File

@@ -1,12 +1,12 @@
using System.Linq; using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Reaction; using Content.Shared.Chemistry.Reaction;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.Fluids.Components; using Content.Shared.Fluids.Components;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using System.Linq;
namespace Content.Server.Chemistry.TileReactions; namespace Content.Server.Chemistry.TileReactions;
@@ -28,7 +28,7 @@ public sealed partial class CleanTileReaction : ITileReaction
/// <summary> /// <summary>
/// What reagent to replace the tile conents with. /// What reagent to replace the tile conents with.
/// </summary> /// </summary>
[DataField("reagent", customTypeSerializer:typeof(PrototypeIdSerializer<ReagentPrototype>))] [DataField("reagent", customTypeSerializer: typeof(PrototypeIdSerializer<ReagentPrototype>))]
public string ReplacementReagent = "Water"; public string ReplacementReagent = "Water";
FixedPoint2 ITileReaction.TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume) FixedPoint2 ITileReaction.TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume)
@@ -43,17 +43,16 @@ public sealed partial class CleanTileReaction : ITileReaction
foreach (var entity in entities) foreach (var entity in entities)
{ {
if (!puddleQuery.TryGetComponent(entity, out var puddle) || 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; continue;
} }
var purgeable = var purgeable = solutionContainerSystem.SplitSolutionWithout(puddleSolution.Value, purgeAmount, ReplacementReagent, reagent.ID);
solutionContainerSystem.SplitSolutionWithout(entity, puddleSolution, purgeAmount, ReplacementReagent, reagent.ID);
purgeAmount -= purgeable.Volume; 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) if (purgeable.Volume <= FixedPoint2.Zero)
break; break;

View 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.Chemistry.Reagent;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.Maps; using Content.Shared.Maps;
@@ -8,22 +7,23 @@ using Robust.Shared.Map;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using System.Numerics;
namespace Content.Server.Chemistry.TileReactions; namespace Content.Server.Chemistry.TileReactions;
[DataDefinition] [DataDefinition]
public sealed partial class CreateEntityTileReaction : ITileReaction 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!; public string Entity = default!;
[DataField("usage")] [DataField]
public FixedPoint2 Usage = FixedPoint2.New(1); public FixedPoint2 Usage = FixedPoint2.New(1);
/// <summary> /// <summary>
/// How many of the whitelisted entity can fit on one tile? /// How many of the whitelisted entity can fit on one tile?
/// </summary> /// </summary>
[DataField("maxOnTile")] [DataField]
public int MaxOnTile = 1; public int MaxOnTile = 1;
/// <summary> /// <summary>
@@ -32,7 +32,7 @@ public sealed partial class CreateEntityTileReaction : ITileReaction
[DataField("maxOnTileWhitelist")] [DataField("maxOnTileWhitelist")]
public EntityWhitelist? Whitelist; public EntityWhitelist? Whitelist;
[DataField("randomOffsetMax")] [DataField]
public float RandomOffsetMax = 0.0f; public float RandomOffsetMax = 0.0f;
public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume) public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume)

Some files were not shown because too many files have changed in this diff Show More