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.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Prototypes;
namespace Content.Client.Administration.UI.ManageSolutions

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
using Content.Server.Administration.Systems;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.EUI;
using Content.Shared.Administration;
using Content.Shared.Chemistry.Components.SolutionManager;
@@ -14,11 +15,13 @@ namespace Content.Server.Administration.UI
public sealed class EditSolutionsEui : BaseEui
{
[Dependency] private readonly IEntityManager _entityManager = default!;
private readonly SolutionContainerSystem _solutionContainerSystem = default!;
public readonly EntityUid Target;
public EditSolutionsEui(EntityUid entity)
{
IoCManager.InjectDependencies(this);
_solutionContainerSystem = _entityManager.System<SolutionContainerSystem>();
Target = entity;
}
@@ -36,8 +39,23 @@ namespace Content.Server.Administration.UI
public override EuiStateBase GetNewState()
{
var solutions = _entityManager.GetComponentOrNull<SolutionContainerManagerComponent>(Target)?.Solutions;
return new EditSolutionsEuiState(_entityManager.GetNetEntity(Target), solutions);
List<(string Name, NetEntity Solution)>? netSolutions;
if (_entityManager.TryGetComponent(Target, out SolutionContainerManagerComponent? container) && container.Containers.Count > 0)
{
netSolutions = new();
foreach (var (name, solution) in _solutionContainerSystem.EnumerateSolutions((Target, container)))
{
if (name is null || !_entityManager.TryGetNetEntity(solution, out var netSolution))
continue;
netSolutions.Add((name, netSolution.Value));
}
}
else
netSolutions = null;
return new EditSolutionsEuiState(_entityManager.GetNetEntity(Target), netSolutions);
}
}
}

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
using Content.Server.Animals.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Popups;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.DoAfter;
using Content.Shared.IdentityManagement;
using Content.Shared.Mobs.Systems;
@@ -61,11 +61,11 @@ internal sealed class UdderSystem : EntitySystem
_hunger.ModifyHunger(uid, -udder.HungerUsage, hunger);
}
if (!_solutionContainerSystem.TryGetSolution(uid, udder.Solution, out var solution))
if (!_solutionContainerSystem.ResolveSolution(uid, udder.SolutionName, ref udder.Solution))
continue;
//TODO: toxins from bloodstream !?
_solutionContainerSystem.TryAddReagent(uid, solution, udder.ReagentId, udder.QuantityPerUpdate, out _);
_solutionContainerSystem.TryAddReagent(udder.Solution.Value, udder.ReagentId, udder.QuantityPerUpdate, out _);
}
}
@@ -85,47 +85,50 @@ internal sealed class UdderSystem : EntitySystem
_doAfterSystem.TryStartDoAfter(doargs);
}
private void OnDoAfter(EntityUid uid, UdderComponent component, MilkingDoAfterEvent args)
private void OnDoAfter(Entity<UdderComponent> entity, ref MilkingDoAfterEvent args)
{
if (args.Cancelled || args.Handled || args.Args.Used == null)
return;
if (!_solutionContainerSystem.TryGetSolution(uid, component.Solution, out var solution))
if (!_solutionContainerSystem.ResolveSolution(entity.Owner, entity.Comp.SolutionName, ref entity.Comp.Solution, out var solution))
return;
if (!_solutionContainerSystem.TryGetRefillableSolution(args.Args.Used.Value, out var targetSolution))
if (!_solutionContainerSystem.TryGetRefillableSolution(args.Args.Used.Value, out var targetSoln, out var targetSolution))
return;
args.Handled = true;
var quantity = solution.Volume;
if (quantity == 0)
{
_popupSystem.PopupEntity(Loc.GetString("udder-system-dry"), uid, args.Args.User);
_popupSystem.PopupEntity(Loc.GetString("udder-system-dry"), entity.Owner, args.Args.User);
return;
}
if (quantity > targetSolution.AvailableVolume)
quantity = targetSolution.AvailableVolume;
var split = _solutionContainerSystem.SplitSolution(uid, solution, quantity);
_solutionContainerSystem.TryAddSolution(args.Args.Used.Value, targetSolution, split);
var split = _solutionContainerSystem.SplitSolution(entity.Comp.Solution.Value, quantity);
_solutionContainerSystem.TryAddSolution(targetSoln.Value, split);
_popupSystem.PopupEntity(Loc.GetString("udder-system-success", ("amount", quantity), ("target", Identity.Entity(args.Args.Used.Value, EntityManager))), uid,
_popupSystem.PopupEntity(Loc.GetString("udder-system-success", ("amount", quantity), ("target", Identity.Entity(args.Args.Used.Value, EntityManager))), entity.Owner,
args.Args.User, PopupType.Medium);
}
private void AddMilkVerb(EntityUid uid, UdderComponent component, GetVerbsEvent<AlternativeVerb> args)
private void AddMilkVerb(Entity<UdderComponent> entity, ref GetVerbsEvent<AlternativeVerb> args)
{
if (args.Using == null ||
!args.CanInteract ||
!EntityManager.HasComponent<RefillableSolutionComponent>(args.Using.Value))
return;
var uid = entity.Owner;
var user = args.User;
var used = args.Using.Value;
AlternativeVerb verb = new()
{
Act = () =>
{
AttemptMilk(uid, args.User, args.Using.Value);
AttemptMilk(uid, user, used);
},
Text = Loc.GetString("udder-system-verb-milk"),
Priority = 2

View File

@@ -1,6 +1,6 @@
using Content.Server.Animals.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Nutrition;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Mobs.Systems;
using Content.Shared.Nutrition.Components;
using Content.Shared.Nutrition.EntitySystems;
@@ -52,10 +52,10 @@ public sealed class WoolySystem : EntitySystem
_hunger.ModifyHunger(uid, -wooly.HungerUsage, hunger);
}
if (!_solutionContainer.TryGetSolution(uid, wooly.Solution, out var solution))
if (!_solutionContainer.ResolveSolution(uid, wooly.SolutionName, ref wooly.Solution))
continue;
_solutionContainer.TryAddReagent(uid, solution, wooly.ReagentId, wooly.Quantity, out _);
_solutionContainer.TryAddReagent(wooly.Solution.Value, wooly.ReagentId, wooly.Quantity, out _);
}
}

View File

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

View File

@@ -1,8 +1,8 @@
using System.Linq;
using Content.Server.Anomaly.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Anomaly.Components;
using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
using System.Linq;
namespace Content.Server.Anomaly.Effects;
/// <summary>
@@ -27,41 +27,40 @@ public sealed class InjectionAnomalySystem : EntitySystem
_injectableQuery = GetEntityQuery<InjectableSolutionComponent>();
}
private void OnPulse(EntityUid uid, InjectionAnomalyComponent component, ref AnomalyPulseEvent args)
private void OnPulse(Entity<InjectionAnomalyComponent> entity, ref AnomalyPulseEvent args)
{
PulseScalableEffect(uid, component, component.InjectRadius, component.MaxSolutionInjection * args.Severity);
PulseScalableEffect(entity, entity.Comp.InjectRadius, entity.Comp.MaxSolutionInjection * args.Severity);
}
private void OnSupercritical(EntityUid uid, InjectionAnomalyComponent component, ref AnomalySupercriticalEvent args)
private void OnSupercritical(Entity<InjectionAnomalyComponent> entity, ref AnomalySupercriticalEvent args)
{
PulseScalableEffect(uid, component, component.SuperCriticalInjectRadius, component.SuperCriticalSolutionInjection);
PulseScalableEffect(entity, entity.Comp.SuperCriticalInjectRadius, entity.Comp.SuperCriticalSolutionInjection);
}
private void PulseScalableEffect(EntityUid uid, InjectionAnomalyComponent component, float injectRadius, float maxInject)
private void PulseScalableEffect(Entity<InjectionAnomalyComponent> entity, float injectRadius, float maxInject)
{
if (!_solutionContainer.TryGetSolution(uid, component.Solution, out var sol))
if (!_solutionContainer.TryGetSolution(entity.Owner, entity.Comp.Solution, out _, out var sol))
return;
//We get all the entity in the radius into which the reagent will be injected.
var xformQuery = GetEntityQuery<TransformComponent>();
var xform = xformQuery.GetComponent(uid);
var xform = xformQuery.GetComponent(entity);
var allEnts = _lookup.GetEntitiesInRange<InjectableSolutionComponent>(xform.MapPosition, injectRadius)
.Select(x => x.Owner).ToList();
//for each matching entity found
foreach (var ent in allEnts)
{
if (!_solutionContainer.TryGetInjectableSolution(ent, out var injectable))
if (!_solutionContainer.TryGetInjectableSolution(ent, out var injectable, out _))
continue;
if (_injectableQuery.TryGetComponent(ent, out var injEnt))
{
var buffer = sol;
_solutionContainer.TryTransferSolution(ent, injectable, buffer, maxInject);
_solutionContainer.TryTransferSolution(injectable.Value, sol, maxInject);
//Spawn Effect
var uidXform = Transform(ent);
Spawn(component.VisualEffectPrototype, uidXform.Coordinates);
Spawn(entity.Comp.VisualEffectPrototype, uidXform.Coordinates);
}
}
}
}

View File

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

View File

@@ -1,12 +1,12 @@
using Content.Server.Anomaly.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Anomaly.Components;
using Robust.Shared.Random;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
using Robust.Shared.Prototypes;
using Content.Shared.Sprite;
using Robust.Server.GameObjects;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Server.Anomaly.Effects;
@@ -42,17 +42,17 @@ public sealed class ReagentProducerAnomalySystem : EntitySystem
SubscribeLocalEvent<ReagentProducerAnomalyComponent, MapInitEvent>(OnMapInit);
}
private void OnPulse(EntityUid uid, ReagentProducerAnomalyComponent component, ref AnomalyPulseEvent args)
private void OnPulse(Entity<ReagentProducerAnomalyComponent> entity, ref AnomalyPulseEvent args)
{
if (_random.NextFloat(0.0f, 1.0f) > args.Stability)
ChangeReagent(uid, component, args.Severity);
ChangeReagent(entity, args.Severity);
}
private void ChangeReagent(EntityUid uid, ReagentProducerAnomalyComponent component, float severity)
private void ChangeReagent(Entity<ReagentProducerAnomalyComponent> entity, float severity)
{
var reagent = GetRandomReagentType(uid, component, severity);
component.ProducingReagent = reagent;
_audio.PlayPvs(component.ChangeSound, uid);
var reagent = GetRandomReagentType(entity, severity);
entity.Comp.ProducingReagent = reagent;
_audio.PlayPvs(entity.Comp.ChangeSound, entity);
}
//reagent realtime generation
@@ -68,7 +68,7 @@ public sealed class ReagentProducerAnomalySystem : EntitySystem
if (component.AccumulatedFrametime < component.UpdateInterval)
continue;
if (!_solutionContainer.TryGetSolution(uid, component.Solution, out var producerSol))
if (!_solutionContainer.ResolveSolution(uid, component.SolutionName, ref component.Solution, out var producerSolution))
continue;
Solution newSol = new();
@@ -76,7 +76,7 @@ public sealed class ReagentProducerAnomalySystem : EntitySystem
if (anomaly.Severity >= 0.97) reagentProducingAmount *= component.SupercriticalReagentProducingModifier;
newSol.AddReagent(component.ProducingReagent, reagentProducingAmount);
_solutionContainer.TryAddSolution(uid, producerSol, newSol); //TO DO - the container is not fully filled.
_solutionContainer.TryAddSolution(component.Solution.Value, newSol); //TO DO - the container is not fully filled.
component.AccumulatedFrametime = 0;
@@ -87,7 +87,7 @@ public sealed class ReagentProducerAnomalySystem : EntitySystem
// and nothing worked out for me. So for now it will be like this.
if (component.NeedRecolor)
{
var color = producerSol.GetColor(_prototypeManager);
var color = producerSolution.GetColor(_prototypeManager);
_light.SetColor(uid, color);
if (TryComp<RandomSpriteComponent>(uid, out var randomSprite))
{
@@ -103,9 +103,9 @@ public sealed class ReagentProducerAnomalySystem : EntitySystem
}
}
private void OnMapInit(EntityUid uid, ReagentProducerAnomalyComponent component, MapInitEvent args)
private void OnMapInit(Entity<ReagentProducerAnomalyComponent> entity, ref MapInitEvent args)
{
ChangeReagent(uid, component, 0.1f); //MapInit Reagent 100% change
ChangeReagent(entity, 0.1f); //MapInit Reagent 100% change
}
// returns a random reagent based on a system of random weights.
@@ -117,33 +117,33 @@ public sealed class ReagentProducerAnomalySystem : EntitySystem
// After that, a random reagent in the selected category is selected.
//
// Such a system is made to control the danger and interest of the anomaly more.
private string GetRandomReagentType(EntityUid uid, ReagentProducerAnomalyComponent component, float severity)
private string GetRandomReagentType(Entity<ReagentProducerAnomalyComponent> entity, float severity)
{
//Category Weight Randomization
var currentWeightDangerous = MathHelper.Lerp(component.WeightSpreadDangerous.X, component.WeightSpreadDangerous.Y, severity);
var currentWeightFun = MathHelper.Lerp(component.WeightSpreadFun.X, component.WeightSpreadFun.Y, severity);
var currentWeightUseful = MathHelper.Lerp(component.WeightSpreadUseful.X, component.WeightSpreadUseful.Y, severity);
var currentWeightDangerous = MathHelper.Lerp(entity.Comp.WeightSpreadDangerous.X, entity.Comp.WeightSpreadDangerous.Y, severity);
var currentWeightFun = MathHelper.Lerp(entity.Comp.WeightSpreadFun.X, entity.Comp.WeightSpreadFun.Y, severity);
var currentWeightUseful = MathHelper.Lerp(entity.Comp.WeightSpreadUseful.X, entity.Comp.WeightSpreadUseful.Y, severity);
var sumWeight = currentWeightDangerous + currentWeightFun + currentWeightUseful;
var rnd = _random.NextFloat(0f, sumWeight);
//Dangerous
if (rnd <= currentWeightDangerous && component.DangerousChemicals.Count > 0)
if (rnd <= currentWeightDangerous && entity.Comp.DangerousChemicals.Count > 0)
{
var reagent = _random.Pick(component.DangerousChemicals);
var reagent = _random.Pick(entity.Comp.DangerousChemicals);
return reagent;
}
else rnd -= currentWeightDangerous;
//Fun
if (rnd <= currentWeightFun && component.FunChemicals.Count > 0)
if (rnd <= currentWeightFun && entity.Comp.FunChemicals.Count > 0)
{
var reagent = _random.Pick(component.FunChemicals);
var reagent = _random.Pick(entity.Comp.FunChemicals);
return reagent;
}
else rnd -= currentWeightFun;
//Useful
if (rnd <= currentWeightUseful && component.UsefulChemicals.Count > 0)
if (rnd <= currentWeightUseful && entity.Comp.UsefulChemicals.Count > 0)
{
var reagent = _random.Pick(component.UsefulChemicals);
var reagent = _random.Pick(entity.Comp.UsefulChemicals);
return reagent;
}
//We should never end up here.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,6 +2,7 @@ using Content.Server.Administration.Logs;
using Content.Server.Atmos;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Body.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Popups;
using Content.Shared.Alert;
using Content.Shared.Atmos;
@@ -26,6 +27,7 @@ namespace Content.Server.Body.Systems
[Dependency] private readonly LungSystem _lungSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
public override void Initialize()
{
@@ -144,7 +146,9 @@ namespace Content.Server.Body.Systems
{
_atmosSys.Merge(outGas, lung.Air);
lung.Air.Clear();
lung.LungSolution.RemoveAllSolution();
if (_solutionContainerSystem.ResolveSolution(lung.Owner, lung.SolutionName, ref lung.Solution))
_solutionContainerSystem.RemoveAllSolution(lung.Solution.Value);
}
_atmosSys.Merge(ev.Gas, outGas);

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,12 +1,12 @@
using Content.Server.Atmos;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Botany.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Fluids.Components;
using Content.Server.Ghost.Roles.Components;
using Content.Server.Kitchen.Components;
using Content.Server.Popups;
using Content.Shared.Botany;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Coordinates.Helpers;
using Content.Shared.Examine;
@@ -38,7 +38,7 @@ public sealed class PlantHolderSystem : EntitySystem
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly SharedPointLightSystem _pointLight = default!;
[Dependency] private readonly SolutionContainerSystem _solutionSystem = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly TagSystem _tagSystem = default!;
[Dependency] private readonly RandomHelperSystem _randomHelper = default!;
[Dependency] private readonly IRobustRandom _random = default!;
@@ -70,11 +70,13 @@ public sealed class PlantHolderSystem : EntitySystem
}
}
private void OnExamine(EntityUid uid, PlantHolderComponent component, ExaminedEvent args)
private void OnExamine(Entity<PlantHolderComponent> entity, ref ExaminedEvent args)
{
if (!args.IsInDetailsRange)
return;
var (_, component) = entity;
if (component.Seed == null)
{
args.PushMarkup(Loc.GetString("plant-holder-component-nothing-planted-message"));
@@ -131,8 +133,10 @@ public sealed class PlantHolderSystem : EntitySystem
}
}
private void OnInteractUsing(EntityUid uid, PlantHolderComponent component, InteractUsingEvent args)
private void OnInteractUsing(Entity<PlantHolderComponent> entity, ref InteractUsingEvent args)
{
var (uid, component) = entity;
if (TryComp(args.Used, out SeedComponent? seeds))
{
if (component.Seed == null)
@@ -203,8 +207,8 @@ public sealed class PlantHolderSystem : EntitySystem
return;
}
if (_solutionSystem.TryGetDrainableSolution(args.Used, out var solution)
&& _solutionSystem.TryGetSolution(uid, component.SoilSolutionName, out var targetSolution)
if (_solutionContainerSystem.TryGetDrainableSolution(args.Used, out var solution, out _)
&& _solutionContainerSystem.ResolveSolution(uid, component.SoilSolutionName, ref component.SoilSolution)
&& TryComp(args.Used, out SprayComponent? spray))
{
var amount = FixedPoint2.New(1);
@@ -214,7 +218,7 @@ public sealed class PlantHolderSystem : EntitySystem
_audio.PlayPvs(spray.SpraySound, args.Used, AudioParams.Default.WithVariation(0.125f));
var split = _solutionSystem.Drain(solutionEntity, solution, amount);
var split = _solutionContainerSystem.Drain(solutionEntity, solution.Value, amount);
if (split.Volume == 0)
{
@@ -227,7 +231,7 @@ public sealed class PlantHolderSystem : EntitySystem
("owner", uid),
("amount", split.Volume)), args.User, PopupType.Medium);
_solutionSystem.TryAddSolution(targetEntity, targetSolution, split);
_solutionContainerSystem.TryAddSolution(component.SoilSolution.Value, split);
ForceUpdateByExternalCause(uid, component);
@@ -290,16 +294,15 @@ public sealed class PlantHolderSystem : EntitySystem
("usingItem", args.Used),
("owner", uid)), uid, Filter.PvsExcept(args.User), true);
if (_solutionSystem.TryGetSolution(args.Used, produce.SolutionName, out var solution2))
if (_solutionContainerSystem.TryGetSolution(args.Used, produce.SolutionName, out var soln2, out var solution2))
{
if (_solutionSystem.TryGetSolution(uid, component.SoilSolutionName, out var solution1))
if (_solutionContainerSystem.ResolveSolution(uid, component.SoilSolutionName, ref component.SoilSolution, out var solution1))
{
// We try to fit as much of the composted plant's contained solution into the hydroponics tray as we can,
// since the plant will be consumed anyway.
var fillAmount = FixedPoint2.Min(solution2.Volume, solution1.AvailableVolume);
_solutionSystem.TryAddSolution(uid, solution1,
_solutionSystem.SplitSolution(args.Used, solution2, fillAmount));
_solutionContainerSystem.TryAddSolution(component.SoilSolution.Value, _solutionContainerSystem.SplitSolution(soln2.Value, fillAmount));
ForceUpdateByExternalCause(uid, component);
}
@@ -314,9 +317,9 @@ public sealed class PlantHolderSystem : EntitySystem
}
}
private void OnInteractHand(EntityUid uid, PlantHolderComponent component, InteractHandEvent args)
private void OnInteractHand(Entity<PlantHolderComponent> entity, ref InteractHandEvent args)
{
DoHarvest(uid, args.User, component);
DoHarvest(entity, args.User, entity.Comp);
}
public void WeedInvasion()
@@ -819,13 +822,13 @@ public sealed class PlantHolderSystem : EntitySystem
if (!Resolve(uid, ref component))
return;
if (!_solutionSystem.TryGetSolution(uid, component.SoilSolutionName, out var solution))
if (!_solutionContainerSystem.ResolveSolution(uid, component.SoilSolutionName, ref component.SoilSolution, out var solution))
return;
if (solution.Volume > 0 && component.MutationLevel < 25)
{
var amt = FixedPoint2.New(1);
foreach (var entry in _solutionSystem.RemoveEachReagent(uid, solution, amt))
foreach (var entry in _solutionContainerSystem.RemoveEachReagent(component.SoilSolution.Value, amt))
{
var reagentProto = _prototype.Index<ReagentPrototype>(entry.Reagent.Prototype);
reagentProto.ReactionPlant(uid, entry, solution);

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.Cargo.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Administration;
using Content.Shared.Body.Components;
using Content.Shared.Chemistry.Components.SolutionManager;
@@ -16,6 +16,7 @@ using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
using System.Linq;
namespace Content.Server.Cargo.Systems;
@@ -30,6 +31,7 @@ public sealed class PricingSystem : EntitySystem
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly BodySystem _bodySystem = default!;
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
/// <inheritdoc/>
public override void Initialize()
@@ -106,13 +108,36 @@ public sealed class PricingSystem : EntitySystem
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)
{
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))
continue;
@@ -257,7 +282,7 @@ public sealed class PricingSystem : EntitySystem
if (TryComp<SolutionContainerManagerComponent>(uid, out var solComp))
{
price += GetSolutionPrice(solComp);
price += GetSolutionPrice((uid, solComp));
}
return price;

View File

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

View File

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

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

View File

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

View File

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

View File

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

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.IdentityManagement;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Robust.Shared.Player;
namespace Content.Server.Chemistry.EntitySystems;
@@ -15,27 +11,26 @@ public sealed partial class ChemistrySystem
SubscribeLocalEvent<ReactionMixerComponent, AfterInteractEvent>(OnAfterInteract);
}
private void OnAfterInteract(EntityUid uid, ReactionMixerComponent component, AfterInteractEvent args)
private void OnAfterInteract(Entity<ReactionMixerComponent> entity, ref AfterInteractEvent args)
{
if (!args.Target.HasValue || !args.CanReach)
return;
var mixAttemptEvent = new MixingAttemptEvent(uid);
RaiseLocalEvent(uid, ref mixAttemptEvent);
if(mixAttemptEvent.Cancelled)
var mixAttemptEvent = new MixingAttemptEvent(entity);
RaiseLocalEvent(entity, ref mixAttemptEvent);
if (mixAttemptEvent.Cancelled)
{
return;
}
Solution? solution = null;
if (!_solutions.TryGetMixableSolution(args.Target.Value, out solution))
return;
if (!_solutionContainers.TryGetMixableSolution(args.Target.Value, out var solution))
return;
_popup.PopupEntity(Loc.GetString(component.MixMessage, ("mixed", Identity.Entity(args.Target.Value, EntityManager)), ("mixer", Identity.Entity(uid, EntityManager))), args.User, args.User);
_popup.PopupEntity(Loc.GetString(entity.Comp.MixMessage, ("mixed", Identity.Entity(args.Target.Value, EntityManager)), ("mixer", Identity.Entity(entity.Owner, EntityManager))), args.User, args.User);
_solutions.UpdateChemicals(args.Target.Value, solution, true, component);
_solutionContainers.UpdateChemicals(solution.Value, true, entity.Comp);
var afterMixingEvent = new AfterMixingEvent(uid, args.Target.Value);
RaiseLocalEvent(uid, afterMixingEvent);
var afterMixingEvent = new AfterMixingEvent(entity, args.Target.Value);
RaiseLocalEvent(entity, afterMixingEvent);
}
}

View File

@@ -1,4 +1,5 @@
using Content.Server.Chemistry.Components.DeleteOnSolutionEmptyComponent;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
@@ -12,27 +13,27 @@ namespace Content.Server.Chemistry.EntitySystems.DeleteOnSolutionEmptySystem
{
base.Initialize();
SubscribeLocalEvent<DeleteOnSolutionEmptyComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<DeleteOnSolutionEmptyComponent, SolutionChangedEvent>(OnSolutionChange);
SubscribeLocalEvent<DeleteOnSolutionEmptyComponent, SolutionContainerChangedEvent>(OnSolutionChange);
}
public void OnStartup(EntityUid uid, DeleteOnSolutionEmptyComponent component, ComponentStartup args)
public void OnStartup(Entity<DeleteOnSolutionEmptyComponent> entity, ref ComponentStartup args)
{
CheckSolutions(uid, component);
CheckSolutions(entity);
}
public void OnSolutionChange(EntityUid uid, DeleteOnSolutionEmptyComponent component, SolutionChangedEvent args)
public void OnSolutionChange(Entity<DeleteOnSolutionEmptyComponent> entity, ref SolutionContainerChangedEvent args)
{
CheckSolutions(uid, component);
CheckSolutions(entity);
}
public void CheckSolutions(EntityUid uid, DeleteOnSolutionEmptyComponent component)
public void CheckSolutions(Entity<DeleteOnSolutionEmptyComponent> entity)
{
if (!EntityManager.HasComponent<SolutionContainerManagerComponent>(uid))
if (!TryComp(entity, out SolutionContainerManagerComponent? solutions))
return;
if (_solutionContainerSystem.TryGetSolution(uid, component.Solution, out var solution))
if (_solutionContainerSystem.TryGetSolution((entity.Owner, solutions), entity.Comp.Solution, out _, out var solution))
if (solution.Volume <= 0)
EntityManager.QueueDeleteEntity(uid);
EntityManager.QueueDeleteEntity(entity);
}
}
}

View File

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

View File

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

View File

@@ -1,10 +1,10 @@
using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Construction;
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Placeable;
namespace Content.Server.Chemistry.EntitySystems;
@@ -13,7 +13,7 @@ public sealed class SolutionHeaterSystem : EntitySystem
{
[Dependency] private readonly PowerReceiverSystem _powerReceiver = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SolutionContainerSystem _solution = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainer = default!;
/// <inheritdoc/>
public override void Initialize()
@@ -51,41 +51,41 @@ public sealed class SolutionHeaterSystem : EntitySystem
RemComp<ActiveSolutionHeaterComponent>(uid);
}
private void OnPowerChanged(EntityUid uid, SolutionHeaterComponent component, ref PowerChangedEvent args)
private void OnPowerChanged(Entity<SolutionHeaterComponent> entity, ref PowerChangedEvent args)
{
var placer = Comp<ItemPlacerComponent>(uid);
var placer = Comp<ItemPlacerComponent>(entity);
if (args.Powered && placer.PlacedEntities.Count > 0)
{
TurnOn(uid);
TurnOn(entity);
}
else
{
TurnOff(uid);
TurnOff(entity);
}
}
private void OnRefreshParts(EntityUid uid, SolutionHeaterComponent component, RefreshPartsEvent args)
private void OnRefreshParts(Entity<SolutionHeaterComponent> entity, ref RefreshPartsEvent args)
{
var heatRating = args.PartRatings[component.MachinePartHeatMultiplier] - 1;
var heatRating = args.PartRatings[entity.Comp.MachinePartHeatMultiplier] - 1;
component.HeatPerSecond = component.BaseHeatPerSecond * MathF.Pow(component.PartRatingHeatMultiplier, heatRating);
entity.Comp.HeatPerSecond = entity.Comp.BaseHeatPerSecond * MathF.Pow(entity.Comp.PartRatingHeatMultiplier, heatRating);
}
private void OnUpgradeExamine(EntityUid uid, SolutionHeaterComponent component, UpgradeExamineEvent args)
private void OnUpgradeExamine(Entity<SolutionHeaterComponent> entity, ref UpgradeExamineEvent args)
{
args.AddPercentageUpgrade("solution-heater-upgrade-heat", component.HeatPerSecond / component.BaseHeatPerSecond);
args.AddPercentageUpgrade("solution-heater-upgrade-heat", entity.Comp.HeatPerSecond / entity.Comp.BaseHeatPerSecond);
}
private void OnItemPlaced(EntityUid uid, SolutionHeaterComponent comp, ref ItemPlacedEvent args)
private void OnItemPlaced(Entity<SolutionHeaterComponent> entity, ref ItemPlacedEvent args)
{
TryTurnOn(uid);
TryTurnOn(entity);
}
private void OnItemRemoved(EntityUid uid, SolutionHeaterComponent component, ref ItemRemovedEvent args)
private void OnItemRemoved(Entity<SolutionHeaterComponent> entity, ref ItemRemovedEvent args)
{
var placer = Comp<ItemPlacerComponent>(uid);
var placer = Comp<ItemPlacerComponent>(entity);
if (placer.PlacedEntities.Count == 0) // Last entity was removed
TurnOff(uid);
TurnOff(entity);
}
public override void Update(float frameTime)
@@ -97,13 +97,13 @@ public sealed class SolutionHeaterSystem : EntitySystem
{
foreach (var heatingEntity in placer.PlacedEntities)
{
if (!TryComp<SolutionContainerManagerComponent>(heatingEntity, out var solution))
if (!TryComp<SolutionContainerManagerComponent>(heatingEntity, out var container))
continue;
var energy = heater.HeatPerSecond * frameTime;
foreach (var s in solution.Solutions.Values)
foreach (var (_, soln) in _solutionContainer.EnumerateSolutions((heatingEntity, container)))
{
_solution.AddThermalEnergy(heatingEntity, s, energy);
_solutionContainer.AddThermalEnergy(soln, energy);
}
}
}

View File

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

View File

@@ -1,7 +1,6 @@
using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.FixedPoint;
using Robust.Shared.Timing;
namespace Content.Server.Chemistry.EntitySystems;
@@ -30,13 +29,13 @@ public sealed class SolutionPurgeSystem : EntitySystem
// timer ignores if it's empty, it's just a fixed cycle
purge.NextPurgeTime += purge.Duration;
if (_solutionContainer.TryGetSolution(uid, purge.Solution, out var solution, manager))
_solutionContainer.SplitSolutionWithout(uid, solution, purge.Quantity, purge.Preserve.ToArray());
if (_solutionContainer.TryGetSolution((uid, manager), purge.Solution, out var solution))
_solutionContainer.SplitSolutionWithout(solution.Value, purge.Quantity, purge.Preserve.ToArray());
}
}
private void OnUnpaused(EntityUid uid, SolutionPurgeComponent comp, ref EntityUnpausedEvent args)
private void OnUnpaused(Entity<SolutionPurgeComponent> entity, ref EntityUnpausedEvent args)
{
comp.NextPurgeTime += args.PausedTime;
entity.Comp.NextPurgeTime += args.PausedTime;
}
}

View File

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

View File

@@ -1,7 +1,7 @@
using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.FixedPoint;
using Robust.Shared.Timing;
@@ -31,7 +31,7 @@ public sealed class SolutionRegenerationSystem : EntitySystem
// timer ignores if its full, it's just a fixed cycle
regen.NextRegenTime = _timing.CurTime + regen.Duration;
if (_solutionContainer.TryGetSolution(uid, regen.Solution, out var solution, manager))
if (_solutionContainer.ResolveSolution((uid, manager), regen.SolutionName, ref regen.Solution, out var solution))
{
var amount = FixedPoint2.Min(solution.AvailableVolume, regen.Generated.Volume);
if (amount <= FixedPoint2.Zero)
@@ -48,13 +48,13 @@ public sealed class SolutionRegenerationSystem : EntitySystem
generated = regen.Generated.Clone().SplitSolution(amount);
}
_solutionContainer.TryAddSolution(uid, solution, generated);
_solutionContainer.TryAddSolution(regen.Solution.Value, generated);
}
}
}
private void OnUnpaused(EntityUid uid, SolutionRegenerationComponent comp, ref EntityUnpausedEvent args)
private void OnUnpaused(Entity<SolutionRegenerationComponent> entity, ref EntityUnpausedEvent args)
{
comp.NextRegenTime += args.PausedTime;
entity.Comp.NextRegenTime += args.PausedTime;
}
}

View File

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

View File

@@ -1,14 +1,14 @@
using Content.Server.Administration.Logs;
using Content.Shared.Verbs;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Database;
using Content.Shared.FixedPoint;
using Content.Shared.Interaction;
using Content.Shared.Popups;
using Content.Shared.Verbs;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.Player;
namespace Content.Server.Chemistry.EntitySystems
@@ -24,7 +24,7 @@ namespace Content.Server.Chemistry.EntitySystems
/// <summary>
/// Default transfer amounts for the set-transfer verb.
/// </summary>
public static readonly List<int> DefaultTransferAmounts = new() { 1, 5, 10, 25, 50, 100, 250, 500, 1000};
public static readonly List<int> DefaultTransferAmounts = new() { 1, 5, 10, 25, 50, 100, 250, 500, 1000 };
public override void Initialize()
{
@@ -35,17 +35,19 @@ namespace Content.Server.Chemistry.EntitySystems
SubscribeLocalEvent<SolutionTransferComponent, TransferAmountSetValueMessage>(OnTransferAmountSetValueMessage);
}
private void OnTransferAmountSetValueMessage(EntityUid uid, SolutionTransferComponent solutionTransfer, TransferAmountSetValueMessage message)
private void OnTransferAmountSetValueMessage(Entity<SolutionTransferComponent> entity, ref TransferAmountSetValueMessage message)
{
var newTransferAmount = FixedPoint2.Clamp(message.Value, solutionTransfer.MinimumTransferAmount, solutionTransfer.MaximumTransferAmount);
solutionTransfer.TransferAmount = newTransferAmount;
var newTransferAmount = FixedPoint2.Clamp(message.Value, entity.Comp.MinimumTransferAmount, entity.Comp.MaximumTransferAmount);
entity.Comp.TransferAmount = newTransferAmount;
if (message.Session.AttachedEntity is {Valid: true} user)
_popupSystem.PopupEntity(Loc.GetString("comp-solution-transfer-set-amount", ("amount", newTransferAmount)), uid, user);
if (message.Session.AttachedEntity is { Valid: true } user)
_popupSystem.PopupEntity(Loc.GetString("comp-solution-transfer-set-amount", ("amount", newTransferAmount)), entity.Owner, user);
}
private void AddSetTransferVerbs(EntityUid uid, SolutionTransferComponent component, GetVerbsEvent<AlternativeVerb> args)
private void AddSetTransferVerbs(Entity<SolutionTransferComponent> entity, ref GetVerbsEvent<AlternativeVerb> args)
{
var (uid, component) = entity;
if (!args.CanAccess || !args.CanInteract || !component.CanChangeTransferAmount || args.Hands == null)
return;
@@ -56,15 +58,16 @@ namespace Content.Server.Chemistry.EntitySystems
AlternativeVerb custom = new();
custom.Text = Loc.GetString("comp-solution-transfer-verb-custom-amount");
custom.Category = VerbCategory.SetTransferAmount;
custom.Act = () => _userInterfaceSystem.TryOpen(args.Target, TransferAmountUiKey.Key, actor.PlayerSession);
custom.Act = () => _userInterfaceSystem.TryOpen(uid, TransferAmountUiKey.Key, actor.PlayerSession);
custom.Priority = 1;
args.Verbs.Add(custom);
// Add specific transfer verbs according to the container's size
var priority = 0;
var user = args.User;
foreach (var amount in DefaultTransferAmounts)
{
if ( amount < component.MinimumTransferAmount.Int() || amount > component.MaximumTransferAmount.Int())
if (amount < component.MinimumTransferAmount.Int() || amount > component.MaximumTransferAmount.Int())
continue;
AlternativeVerb verb = new();
@@ -73,7 +76,7 @@ namespace Content.Server.Chemistry.EntitySystems
verb.Act = () =>
{
component.TransferAmount = FixedPoint2.New(amount);
_popupSystem.PopupEntity(Loc.GetString("comp-solution-transfer-set-amount", ("amount", amount)), uid, args.User);
_popupSystem.PopupEntity(Loc.GetString("comp-solution-transfer-set-amount", ("amount", amount)), uid, user);
};
// we want to sort by size, not alphabetically by the verb text.
@@ -84,18 +87,19 @@ namespace Content.Server.Chemistry.EntitySystems
}
}
private void OnAfterInteract(EntityUid uid, SolutionTransferComponent component, AfterInteractEvent args)
private void OnAfterInteract(Entity<SolutionTransferComponent> entity, ref AfterInteractEvent args)
{
if (!args.CanReach || args.Target == null)
return;
var target = args.Target!.Value;
var (uid, component) = entity;
//Special case for reagent tanks, because normally clicking another container will give solution, not take it.
if (component.CanReceive && !EntityManager.HasComponent<RefillableSolutionComponent>(target) // target must not be refillable (e.g. Reagent Tanks)
&& _solutionContainerSystem.TryGetDrainableSolution(target, out var targetDrain) // target must be drainable
&& EntityManager.TryGetComponent(uid, out RefillableSolutionComponent? refillComp)
&& _solutionContainerSystem.TryGetRefillableSolution(uid, out var ownerRefill, refillable: refillComp))
if (component.CanReceive && !EntityManager.HasComponent<RefillableSolutionComponent>(target) // target must not be refillable (e.g. Reagent Tanks)
&& _solutionContainerSystem.TryGetDrainableSolution(target, out var targetSoln, out _) // target must be drainable
&& EntityManager.TryGetComponent(uid, out RefillableSolutionComponent? refillComp)
&& _solutionContainerSystem.TryGetRefillableSolution((uid, refillComp, null), out var ownerSoln, out var ownerRefill))
{
@@ -106,7 +110,7 @@ namespace Content.Server.Chemistry.EntitySystems
transferAmount = FixedPoint2.Min(transferAmount, (FixedPoint2) refill.MaxRefill); // if the receiver has a smaller transfer limit, use that instead
}
var transferred = Transfer(args.User, target, targetDrain, uid, ownerRefill, transferAmount);
var transferred = Transfer(args.User, target, targetSoln.Value, uid, ownerSoln.Value, transferAmount);
if (transferred > 0)
{
var toTheBrim = ownerRefill.AvailableVolume == 0;
@@ -122,8 +126,8 @@ namespace Content.Server.Chemistry.EntitySystems
}
// if target is refillable, and owner is drainable
if (component.CanSend && _solutionContainerSystem.TryGetRefillableSolution(target, out var targetRefill)
&& _solutionContainerSystem.TryGetDrainableSolution(uid, out var ownerDrain))
if (component.CanSend && _solutionContainerSystem.TryGetRefillableSolution(target, out targetSoln, out var targetRefill)
&& _solutionContainerSystem.TryGetDrainableSolution(uid, out ownerSoln, out var ownerDrain))
{
var transferAmount = component.TransferAmount;
@@ -132,7 +136,7 @@ namespace Content.Server.Chemistry.EntitySystems
transferAmount = FixedPoint2.Min(transferAmount, (FixedPoint2) refill.MaxRefill);
}
var transferred = Transfer(args.User, uid, ownerDrain, target, targetRefill, transferAmount);
var transferred = Transfer(args.User, uid, ownerSoln.Value, target, targetSoln.Value, transferAmount);
if (transferred > 0)
{
@@ -150,9 +154,9 @@ namespace Content.Server.Chemistry.EntitySystems
/// <returns>The actual amount transferred.</returns>
public FixedPoint2 Transfer(EntityUid user,
EntityUid sourceEntity,
Solution source,
Entity<SolutionComponent> source,
EntityUid targetEntity,
Solution target,
Entity<SolutionComponent> target,
FixedPoint2 amount)
{
var transferAttempt = new SolutionTransferAttemptEvent(sourceEntity, targetEntity);
@@ -165,7 +169,8 @@ namespace Content.Server.Chemistry.EntitySystems
return FixedPoint2.Zero;
}
if (source.Volume == 0)
var sourceSolution = source.Comp.Solution;
if (sourceSolution.Volume == 0)
{
_popupSystem.PopupEntity(Loc.GetString("comp-solution-transfer-is-empty", ("target", sourceEntity)), sourceEntity, user);
return FixedPoint2.Zero;
@@ -179,19 +184,20 @@ namespace Content.Server.Chemistry.EntitySystems
return FixedPoint2.Zero;
}
if (target.AvailableVolume == 0)
var targetSolution = target.Comp.Solution;
if (targetSolution.AvailableVolume == 0)
{
_popupSystem.PopupEntity(Loc.GetString("comp-solution-transfer-is-full", ("target", targetEntity)), targetEntity, user);
return FixedPoint2.Zero;
}
var actualAmount = FixedPoint2.Min(amount, FixedPoint2.Min(source.Volume, target.AvailableVolume));
var actualAmount = FixedPoint2.Min(amount, FixedPoint2.Min(sourceSolution.Volume, targetSolution.AvailableVolume));
var solution = _solutionContainerSystem.Drain(sourceEntity, source, actualAmount);
_solutionContainerSystem.Refill(targetEntity, target, solution);
_adminLogger.Add(LogType.Action, LogImpact.Medium,
$"{EntityManager.ToPrettyString(user):player} transferred {string.Join(", ", solution.Contents)} to {EntityManager.ToPrettyString(targetEntity):entity}, which now contains {string.Join(", ", target.Contents)}");
$"{EntityManager.ToPrettyString(user):player} transferred {string.Join(", ", solution.Contents)} to {EntityManager.ToPrettyString(targetEntity):entity}, which now contains {SolutionContainerSystem.ToPrettyString(targetSolution)}");
return actualAmount;
}

View File

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

View File

@@ -1,9 +1,8 @@
using System.Numerics;
using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint;
using Content.Shared.Physics;
@@ -16,6 +15,7 @@ using Robust.Shared.Physics.Events;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Prototypes;
using Robust.Shared.Spawners;
using System.Numerics;
namespace Content.Server.Chemistry.EntitySystems
{
@@ -40,19 +40,20 @@ namespace Content.Server.Chemistry.EntitySystems
SubscribeLocalEvent<VaporComponent, StartCollideEvent>(HandleCollide);
}
private void HandleCollide(EntityUid uid, VaporComponent component, ref StartCollideEvent args)
private void HandleCollide(Entity<VaporComponent> entity, ref StartCollideEvent args)
{
if (!EntityManager.TryGetComponent(uid, out SolutionContainerManagerComponent? contents)) return;
if (!EntityManager.TryGetComponent(entity.Owner, out SolutionContainerManagerComponent? contents)) return;
foreach (var value in contents.Solutions.Values)
foreach (var (_, soln) in _solutionContainerSystem.EnumerateSolutions((entity.Owner, contents)))
{
_reactive.DoEntityReaction(args.OtherEntity, value, ReactionMethod.Touch);
var solution = soln.Comp.Solution;
_reactive.DoEntityReaction(args.OtherEntity, solution, ReactionMethod.Touch);
}
// Check for collision with a impassable object (e.g. wall) and stop
if ((args.OtherFixture.CollisionLayer & (int) CollisionGroup.Impassable) != 0 && args.OtherFixture.Hard)
{
EntityManager.QueueDeleteEntity(uid);
EntityManager.QueueDeleteEntity(entity);
}
}
@@ -83,28 +84,27 @@ namespace Content.Server.Chemistry.EntitySystems
return false;
}
if (!_solutionContainerSystem.TryGetSolution(vapor, VaporComponent.SolutionName,
out var vaporSolution))
if (!_solutionContainerSystem.TryGetSolution(vapor.Owner, VaporComponent.SolutionName, out var vaporSolution))
{
return false;
}
return _solutionContainerSystem.TryAddSolution(vapor, vaporSolution, solution);
return _solutionContainerSystem.TryAddSolution(vaporSolution.Value, solution);
}
public override void Update(float frameTime)
{
var query = EntityQueryEnumerator<VaporComponent, SolutionContainerManagerComponent, TransformComponent>();
while (query.MoveNext(out var uid, out var vaporComp, out var solution, out var xform))
while (query.MoveNext(out var uid, out var vaporComp, out var container, out var xform))
{
foreach (var (_, value) in solution.Solutions)
foreach (var (_, soln) in _solutionContainerSystem.EnumerateSolutions((uid, container)))
{
Update(frameTime, (uid, vaporComp), value, xform);
Update(frameTime, (uid, vaporComp), soln, xform);
}
}
}
private void Update(float frameTime, Entity<VaporComponent> ent, Solution contents, TransformComponent xform)
private void Update(float frameTime, Entity<VaporComponent> ent, Entity<SolutionComponent> soln, TransformComponent xform)
{
var (entity, vapor) = ent;
if (!vapor.Active)
@@ -112,6 +112,7 @@ namespace Content.Server.Chemistry.EntitySystems
vapor.ReactTimer += frameTime;
var contents = soln.Comp.Solution;
if (vapor.ReactTimer >= ReactTime && TryComp(xform.GridUid, out MapGridComponent? gridComp))
{
vapor.ReactTimer = 0;
@@ -131,7 +132,7 @@ namespace Content.Server.Chemistry.EntitySystems
reaction = reagentQuantity.Quantity;
}
_solutionContainerSystem.RemoveReagent(entity, contents, reagentQuantity.Reagent, reaction);
_solutionContainerSystem.RemoveReagent(soln, reagentQuantity.Reagent, reaction);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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.Shared.Chemistry.Reagent;
using Robust.Shared.Configuration;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects;

View File

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

View File

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

View File

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

View File

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

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.Shared.Chemistry.Reagent;
using Robust.Shared.Configuration;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects;
public sealed partial class CureZombieInfection : ReagentEffect
{
[DataField("innoculate")]
[DataField]
public bool Innoculate;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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