diff --git a/Content.Client/Administration/UI/ManageSolutions/EditSolutionsWindow.xaml.cs b/Content.Client/Administration/UI/ManageSolutions/EditSolutionsWindow.xaml.cs index 7e46cb0ab2..6c070d3c2e 100644 --- a/Content.Client/Administration/UI/ManageSolutions/EditSolutionsWindow.xaml.cs +++ b/Content.Client/Administration/UI/ManageSolutions/EditSolutionsWindow.xaml.cs @@ -1,4 +1,5 @@ using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Reagent; using Robust.Client.AutoGenerated; using Robust.Client.Console; using Robust.Client.UserInterface.Controls; @@ -176,16 +177,16 @@ namespace Content.Client.Administration.UI.ManageSolutions /// /// Add a single reagent entry to the list /// - private void AddReagentEntry(Solution.ReagentQuantity reagent) + private void AddReagentEntry(ReagentQuantity reagentQuantity) { var box = new BoxContainer(); var spin = new FloatSpinBox(1, 2); - spin.Value = reagent.Quantity.Float(); - spin.OnValueChanged += (args) => SetReagent(args, reagent.ReagentId); + spin.Value = reagentQuantity.Quantity.Float(); + spin.OnValueChanged += (args) => SetReagent(args, reagentQuantity.Reagent.Prototype); spin.HorizontalExpand = true; - box.AddChild(new Label() { Text = reagent.ReagentId , HorizontalExpand = true}); + box.AddChild(new Label() { Text = reagentQuantity.Reagent.Prototype , HorizontalExpand = true}); box.AddChild(spin); ReagentList.AddChild(box); @@ -194,18 +195,18 @@ namespace Content.Client.Administration.UI.ManageSolutions /// /// Execute a command to modify the reagents in the solution. /// - private void SetReagent(FloatSpinBox.FloatSpinBoxEventArgs args, string reagentId) + private void SetReagent(FloatSpinBox.FloatSpinBoxEventArgs args, string prototype) { if (_solutions == null || _selectedSolution == null) return; - var current = _solutions[_selectedSolution].GetReagentQuantity(reagentId); + var current = _solutions[_selectedSolution].GetTotalPrototypeQuantity(prototype); var delta = args.Value - current.Float(); if (MathF.Abs(delta) < 0.01) return; - var command = $"addreagent {_target} {_selectedSolution} {reagentId} {delta}"; + var command = $"addreagent {_target} {_selectedSolution} {prototype} {delta}"; _consoleHost.ExecuteCommand(command); } diff --git a/Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs b/Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs index bf68a55d00..5eace08a7f 100644 --- a/Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs +++ b/Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs @@ -11,6 +11,7 @@ using Robust.Shared.Prototypes; using Robust.Shared.Utility; using System.Linq; using System.Numerics; +using Content.Shared.FixedPoint; using static Robust.Client.UserInterface.Controls.BoxContainer; namespace Content.Client.Chemistry.UI @@ -87,7 +88,7 @@ namespace Content.Client.Chemistry.UI Tabs.SetTabTitle(1, Loc.GetString("chem-master-window-output-tab")); } - private ReagentButton MakeReagentButton(string text, ChemMasterReagentAmount amount, string id, bool isBuffer, string styleClass) + private ReagentButton MakeReagentButton(string text, ChemMasterReagentAmount amount, ReagentId id, bool isBuffer, string styleClass) { var button = new ReagentButton(text, amount, id, isBuffer, styleClass); button.OnPressed += args @@ -112,11 +113,11 @@ namespace Content.Client.Chemistry.UI InputEjectButton.Disabled = castState.InputContainerInfo is null; OutputEjectButton.Disabled = output is null; - CreateBottleButton.Disabled = output is null || !output.HoldsReagents; - CreatePillButton.Disabled = output is null || output.HoldsReagents; + CreateBottleButton.Disabled = output?.Reagents == null; + CreatePillButton.Disabled = output?.Entities == null; var remainingCapacity = output is null ? 0 : (output.MaxVolume - output.CurrentVolume).Int(); - var holdsReagents = output?.HoldsReagents ?? false; + var holdsReagents = output?.Reagents != null; var pillNumberMax = holdsReagents ? 0 : remainingCapacity; var bottleAmountMax = holdsReagents ? remainingCapacity : 0; @@ -139,13 +140,10 @@ namespace Content.Client.Chemistry.UI { if (state.BufferCurrentVolume == 0) return ""; - else - { - var reagent = state.BufferReagents.OrderBy(r => r.Quantity).First(); - _prototypeManager.TryIndex(reagent.ReagentId, out ReagentPrototype? proto); - return proto?.LocalizedName ?? ""; - } + var reagent = state.BufferReagents.OrderBy(r => r.Quantity).First().Reagent; + _prototypeManager.TryIndex(reagent.Prototype, out ReagentPrototype? proto); + return proto?.LocalizedName ?? ""; } /// @@ -184,10 +182,10 @@ namespace Content.Client.Chemistry.UI }; bufferHBox.AddChild(bufferVol); - foreach (var reagent in state.BufferReagents) + foreach (var (reagent, quantity) in state.BufferReagents) { // Try to get the prototype for the given reagent. This gives us its name. - _prototypeManager.TryIndex(reagent.ReagentId, out ReagentPrototype? proto); + _prototypeManager.TryIndex(reagent.Prototype, out ReagentPrototype? proto); var name = proto?.LocalizedName ?? Loc.GetString("chem-master-window-unknown-reagent-text"); if (proto != null) @@ -200,20 +198,20 @@ namespace Content.Client.Chemistry.UI new Label {Text = $"{name}: "}, new Label { - Text = $"{reagent.Quantity}u", + Text = $"{quantity}u", StyleClasses = {StyleNano.StyleClassLabelSecondaryColor} }, // Padding new Control {HorizontalExpand = true}, - MakeReagentButton("1", ChemMasterReagentAmount.U1, reagent.ReagentId, true, StyleBase.ButtonOpenRight), - MakeReagentButton("5", ChemMasterReagentAmount.U5, reagent.ReagentId, true, StyleBase.ButtonOpenBoth), - MakeReagentButton("10", ChemMasterReagentAmount.U10, reagent.ReagentId, true, StyleBase.ButtonOpenBoth), - MakeReagentButton("25", ChemMasterReagentAmount.U25, reagent.ReagentId, true, StyleBase.ButtonOpenBoth), - MakeReagentButton("50", ChemMasterReagentAmount.U50, reagent.ReagentId, true, StyleBase.ButtonOpenBoth), - MakeReagentButton("100", ChemMasterReagentAmount.U100, reagent.ReagentId, true, StyleBase.ButtonOpenBoth), - MakeReagentButton(Loc.GetString("chem-master-window-buffer-all-amount"), ChemMasterReagentAmount.All, reagent.ReagentId, true, StyleBase.ButtonOpenLeft), + MakeReagentButton("1", ChemMasterReagentAmount.U1, reagent, true, StyleBase.ButtonOpenRight), + MakeReagentButton("5", ChemMasterReagentAmount.U5, reagent, true, StyleBase.ButtonOpenBoth), + MakeReagentButton("10", ChemMasterReagentAmount.U10, reagent, true, StyleBase.ButtonOpenBoth), + MakeReagentButton("25", ChemMasterReagentAmount.U25, reagent, true, StyleBase.ButtonOpenBoth), + MakeReagentButton("50", ChemMasterReagentAmount.U50, reagent, true, StyleBase.ButtonOpenBoth), + MakeReagentButton("100", ChemMasterReagentAmount.U100, reagent, true, StyleBase.ButtonOpenBoth), + MakeReagentButton(Loc.GetString("chem-master-window-buffer-all-amount"), ChemMasterReagentAmount.All, reagent, true, StyleBase.ButtonOpenLeft), } }); } @@ -248,20 +246,29 @@ namespace Content.Client.Chemistry.UI } }); - var contents = info.Contents - .Select(lineItem => - { - if (!info.HoldsReagents) - return (lineItem.Id, lineItem.Id, lineItem.Quantity); + IEnumerable<(string Name, ReagentId Id, FixedPoint2 Quantity)> contents; - // Try to get the prototype for the given reagent. This gives us its name. - _prototypeManager.TryIndex(lineItem.Id, out ReagentPrototype? proto); - var name = proto?.LocalizedName - ?? Loc.GetString("chem-master-window-unknown-reagent-text"); + if (info.Entities != null) + { + contents = info.Entities.Select(x => (x.Id, default(ReagentId), x.Quantity)); + } + else if (info.Reagents != null) + { + contents = info.Reagents.Select(x => + { + _prototypeManager.TryIndex(x.Reagent.Prototype, out ReagentPrototype? proto); + var name = proto?.LocalizedName + ?? Loc.GetString("chem-master-window-unknown-reagent-text"); + + return (name, Id: x.Reagent, x.Quantity); + }) + .OrderBy(r => r.Item1); + } + else + { + return; + } - return (name, lineItem.Id, lineItem.Quantity); - }) - .OrderBy(r => r.Item1); foreach (var (name, id, quantity) in contents) { @@ -326,8 +333,8 @@ namespace Content.Client.Chemistry.UI { public ChemMasterReagentAmount Amount { get; set; } public bool IsBuffer = true; - public string Id { get; set; } - public ReagentButton(string text, ChemMasterReagentAmount amount, string id, bool isBuffer, string styleClass) + public ReagentId Id { get; set; } + public ReagentButton(string text, ChemMasterReagentAmount amount, ReagentId id, bool isBuffer, string styleClass) { AddStyleClass(styleClass); Text = text; diff --git a/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml.cs b/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml.cs index 64b5f5ddd2..bf08d4978e 100644 --- a/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml.cs +++ b/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml.cs @@ -48,17 +48,17 @@ namespace Content.Client.Chemistry.UI /// Update the button grid of reagents which can be dispensed. /// /// Reagents which can be dispensed by this dispenser - public void UpdateReagentsList(List inventory) + public void UpdateReagentsList(List inventory) { - if (ChemicalList == null) return; - if (inventory == null) return; + if (ChemicalList == null) + return; ChemicalList.Children.Clear(); foreach (var entry in inventory - .OrderBy(r => {_prototypeManager.TryIndex(r, out ReagentPrototype? p); return p?.LocalizedName;})) + .OrderBy(r => {_prototypeManager.TryIndex(r.Prototype, out ReagentPrototype? p); return p?.LocalizedName;})) { - var localizedName = _prototypeManager.TryIndex(entry, out ReagentPrototype? p) + var localizedName = _prototypeManager.TryIndex(entry.Prototype, out ReagentPrototype? p) ? p.LocalizedName : Loc.GetString("reagent-dispenser-window-reagent-name-not-found-text"); @@ -123,7 +123,7 @@ namespace Content.Client.Chemistry.UI /// State data for the dispenser. /// Prototype ID of the reagent whose dispense button is currently being mouse hovered, /// or null if no button is being hovered. - public void UpdateContainerInfo(ReagentDispenserBoundUserInterfaceState state, string? highlightedReagentId = null) + public void UpdateContainerInfo(ReagentDispenserBoundUserInterfaceState state, ReagentId? highlightedReagentId = null) { ContainerInfo.Children.Clear(); @@ -147,22 +147,22 @@ namespace Content.Client.Chemistry.UI } }); - foreach (var reagent in state.OutputContainer.Contents) + foreach (var (reagent, quantity) in state.OutputContainer.Reagents!) { // Try get to the prototype for the given reagent. This gives us its name. - var localizedName = _prototypeManager.TryIndex(reagent.Id, out ReagentPrototype? p) + var localizedName = _prototypeManager.TryIndex(reagent.Prototype, out ReagentPrototype? p) ? p.LocalizedName : Loc.GetString("reagent-dispenser-window-reagent-name-not-found-text"); var nameLabel = new Label {Text = $"{localizedName}: "}; var quantityLabel = new Label { - Text = Loc.GetString("reagent-dispenser-window-quantity-label-text", ("quantity", reagent.Quantity)), + Text = Loc.GetString("reagent-dispenser-window-quantity-label-text", ("quantity", quantity)), StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}, }; // Check if the reagent is being moused over. If so, color it green. - if (reagent.Id == highlightedReagentId) { + if (reagent == highlightedReagentId) { nameLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateGood); quantityLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateGood); } @@ -181,9 +181,9 @@ namespace Content.Client.Chemistry.UI } public sealed class DispenseReagentButton : Button { - public string ReagentId { get; } + public ReagentId ReagentId { get; } - public DispenseReagentButton(string reagentId, string text) + public DispenseReagentButton(ReagentId reagentId, string text) { ReagentId = reagentId; Text = text; diff --git a/Content.Client/Kitchen/UI/GrinderMenu.xaml.cs b/Content.Client/Kitchen/UI/GrinderMenu.xaml.cs index a2237e5f09..79f212bbf6 100644 --- a/Content.Client/Kitchen/UI/GrinderMenu.xaml.cs +++ b/Content.Client/Kitchen/UI/GrinderMenu.xaml.cs @@ -83,7 +83,7 @@ namespace Content.Client.Kitchen.UI } } - private void RefreshContentsDisplay(IList? reagents, IReadOnlyList containedSolids, bool isBeakerAttached) + private void RefreshContentsDisplay(IList? reagents, IReadOnlyList containedSolids, bool isBeakerAttached) { //Refresh chamber contents _chamberVisualContents.Clear(); @@ -118,9 +118,11 @@ namespace Content.Client.Kitchen.UI } else { - foreach (var reagent in reagents) + foreach (var (reagent, quantity) in reagents) { - var reagentName = _prototypeManager.TryIndex(reagent.ReagentId, out ReagentPrototype? proto) ? Loc.GetString($"{reagent.Quantity} {proto.LocalizedName}") : "???"; + var reagentName = _prototypeManager.TryIndex(reagent.Prototype, out ReagentPrototype? proto) + ? Loc.GetString($"{quantity} {proto.LocalizedName}") + : "???"; BeakerContentBox.BoxContents.AddItem(reagentName); } } diff --git a/Content.Client/Kitchen/UI/MicrowaveBoundUserInterface.cs b/Content.Client/Kitchen/UI/MicrowaveBoundUserInterface.cs index 87f77e24f7..968d9f30a2 100644 --- a/Content.Client/Kitchen/UI/MicrowaveBoundUserInterface.cs +++ b/Content.Client/Kitchen/UI/MicrowaveBoundUserInterface.cs @@ -1,4 +1,5 @@ using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Reagent; using Content.Shared.Kitchen.Components; using JetBrains.Annotations; using Robust.Client.GameObjects; @@ -17,7 +18,7 @@ namespace Content.Client.Kitchen.UI private readonly Dictionary _solids = new(); [ViewVariables] - private readonly Dictionary _reagents = new(); + private readonly Dictionary _reagents = new(); public MicrowaveBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { diff --git a/Content.IntegrationTests/Tests/Chemistry/ReagentDataTest.cs b/Content.IntegrationTests/Tests/Chemistry/ReagentDataTest.cs new file mode 100644 index 0000000000..f488734655 --- /dev/null +++ b/Content.IntegrationTests/Tests/Chemistry/ReagentDataTest.cs @@ -0,0 +1,30 @@ +using Content.IntegrationTests.Tests.Interaction; +using Content.Shared.Chemistry.Reagent; +using Robust.Shared.Reflection; +using Robust.Shared.Serialization; +using Robust.Shared.Utility; + +namespace Content.IntegrationTests.Tests.Chemistry; + +[TestFixture] +[TestOf(typeof(ReagentData))] +public sealed class ReagentDataTest : InteractionTest +{ + [Test] + public async Task ReagentDataIsSerializable() + { + await using var pair = await PoolManager.GetServerClient(); + var reflection = pair.Server.ResolveDependency(); + + Assert.Multiple(() => + { + foreach (var instance in reflection.GetAllChildren(typeof(ReagentData))) + { + Assert.That(instance.HasCustomAttribute(), $"{instance} must have the NetSerializable attribute."); + Assert.That(instance.HasCustomAttribute(), $"{instance} must have the serializable attribute."); + } + }); + + await pair.CleanReturnAsync(); + } +} diff --git a/Content.IntegrationTests/Tests/Chemistry/SolutionSystemTests.cs b/Content.IntegrationTests/Tests/Chemistry/SolutionSystemTests.cs index a319bc08e0..8d0bbbd067 100644 --- a/Content.IntegrationTests/Tests/Chemistry/SolutionSystemTests.cs +++ b/Content.IntegrationTests/Tests/Chemistry/SolutionSystemTests.cs @@ -73,8 +73,8 @@ public sealed class SolutionSystemTests Assert.That(containerSystem .TryAddSolution(beaker, solution, oilAdded)); - solution.TryGetReagent("Water", out var water); - solution.TryGetReagent("Oil", out var oil); + var water = solution.GetTotalPrototypeQuantity("Water"); + var oil = solution.GetTotalPrototypeQuantity("Oil"); Assert.Multiple(() => { Assert.That(water, Is.EqualTo(waterQuantity)); @@ -118,8 +118,8 @@ public sealed class SolutionSystemTests Assert.That(containerSystem .TryAddSolution(beaker, solution, oilAdded), Is.False); - solution.TryGetReagent("Water", out var water); - solution.TryGetReagent("Oil", out var oil); + var water = solution.GetTotalPrototypeQuantity("Water"); + var oil = solution.GetTotalPrototypeQuantity("Oil"); Assert.Multiple(() => { Assert.That(water, Is.EqualTo(waterQuantity)); @@ -168,15 +168,15 @@ public sealed class SolutionSystemTests { Assert.That(solution.Volume, Is.EqualTo(FixedPoint2.New(threshold))); - solution.TryGetReagent("Water", out var waterMix); - solution.TryGetReagent("Oil", out var oilMix); + var waterMix = solution.GetTotalPrototypeQuantity("Water"); + var oilMix = solution.GetTotalPrototypeQuantity("Oil"); Assert.That(waterMix, Is.EqualTo(FixedPoint2.New(threshold / (ratio + 1)))); Assert.That(oilMix, Is.EqualTo(FixedPoint2.New(threshold / (ratio + 1) * ratio))); Assert.That(overflowingSolution.Volume, Is.EqualTo(FixedPoint2.New(80))); - overflowingSolution.TryGetReagent("Water", out var waterOverflow); - overflowingSolution.TryGetReagent("Oil", out var oilOverFlow); + var waterOverflow = overflowingSolution.GetTotalPrototypeQuantity("Water"); + var oilOverFlow = overflowingSolution.GetTotalPrototypeQuantity("Oil"); Assert.That(waterOverflow, Is.EqualTo(waterQuantity - waterMix)); Assert.That(oilOverFlow, Is.EqualTo(oilQuantity - oilMix)); }); diff --git a/Content.IntegrationTests/Tests/Chemistry/TryAllReactionsTest.cs b/Content.IntegrationTests/Tests/Chemistry/TryAllReactionsTest.cs index deb3b95a93..30c15e8da3 100644 --- a/Content.IntegrationTests/Tests/Chemistry/TryAllReactionsTest.cs +++ b/Content.IntegrationTests/Tests/Chemistry/TryAllReactionsTest.cs @@ -79,9 +79,9 @@ 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 in component.Contents) + foreach (var (reagent, quantity) in component.Contents) { - Assert.That(foundProductsMap.TryFirstOrNull(x => x.Key.Key == reagent.ReagentId && x.Key.Value == reagent.Quantity, out var foundProduct)); + Assert.That(foundProductsMap.TryFirstOrNull(x => x.Key.Key == reagent.Prototype && x.Key.Value == quantity, out var foundProduct)); foundProductsMap[foundProduct.Value.Key] = true; } diff --git a/Content.Server/Administration/Commands/AddReagent.cs b/Content.Server/Administration/Commands/AddReagent.cs index aef7f04d46..795de6f91f 100644 --- a/Content.Server/Administration/Commands/AddReagent.cs +++ b/Content.Server/Administration/Commands/AddReagent.cs @@ -62,7 +62,7 @@ namespace Content.Server.Administration.Commands if (quantityFloat > 0) EntitySystem.Get().TryAddReagent(uid, solution, args[2], quantity, out var _); else - EntitySystem.Get().TryRemoveReagent(uid, solution, args[2], quantity); + EntitySystem.Get().RemoveReagent(uid, solution, args[2], quantity); } } } diff --git a/Content.Server/Administration/Toolshed/SolutionCommand.cs b/Content.Server/Administration/Toolshed/SolutionCommand.cs index 90149ca1dd..5cbd931a70 100644 --- a/Content.Server/Administration/Toolshed/SolutionCommand.cs +++ b/Content.Server/Administration/Toolshed/SolutionCommand.cs @@ -59,7 +59,7 @@ public sealed class SolutionCommand : ToolshedCommand } else if (amount < 0) { - _solutionContainer.TryRemoveReagent(input.Owner, input.Solution, name.Value.ID, -amount); + _solutionContainer.RemoveReagent(input.Owner, input.Solution, name.Value.ID, -amount); } return input; diff --git a/Content.Server/Body/Components/StomachComponent.cs b/Content.Server/Body/Components/StomachComponent.cs index b476e298a6..30f9791525 100644 --- a/Content.Server/Body/Components/StomachComponent.cs +++ b/Content.Server/Body/Components/StomachComponent.cs @@ -1,5 +1,7 @@ using Content.Server.Body.Systems; using Content.Server.Nutrition.EntitySystems; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Reagent; using Content.Shared.FixedPoint; using Content.Shared.Whitelist; @@ -46,14 +48,12 @@ namespace Content.Server.Body.Components /// public sealed class ReagentDelta { - public readonly string ReagentId; - public readonly FixedPoint2 Quantity; + public readonly ReagentQuantity ReagentQuantity; public float Lifetime { get; private set; } - public ReagentDelta(string reagentId, FixedPoint2 quantity) + public ReagentDelta(ReagentQuantity reagentQuantity) { - ReagentId = reagentId; - Quantity = quantity; + ReagentQuantity = reagentQuantity; Lifetime = 0.0f; } diff --git a/Content.Server/Body/Systems/BloodstreamSystem.cs b/Content.Server/Body/Systems/BloodstreamSystem.cs index 33cb59775b..9ed92da8fc 100644 --- a/Content.Server/Body/Systems/BloodstreamSystem.cs +++ b/Content.Server/Body/Systems/BloodstreamSystem.cs @@ -274,9 +274,9 @@ public sealed class BloodstreamSystem : EntitySystem for (var i = component.ChemicalSolution.Contents.Count - 1; i >= 0; i--) { var (reagentId, _) = component.ChemicalSolution.Contents[i]; - if (reagentId != excludedReagentID) + if (reagentId.Prototype != excludedReagentID) { - _solutionContainerSystem.TryRemoveReagent(uid, component.ChemicalSolution, reagentId, quantity); + _solutionContainerSystem.RemoveReagent(uid, component.ChemicalSolution, reagentId, quantity); } } diff --git a/Content.Server/Body/Systems/MetabolizerSystem.cs b/Content.Server/Body/Systems/MetabolizerSystem.cs index 89ffff718b..de06fdc81f 100644 --- a/Content.Server/Body/Systems/MetabolizerSystem.cs +++ b/Content.Server/Body/Systems/MetabolizerSystem.cs @@ -128,9 +128,9 @@ namespace Content.Server.Body.Systems _random.Shuffle(list); int reagents = 0; - foreach (var reagent in list) + foreach (var (reagent, quantity) in list) { - if (!_prototypeManager.TryIndex(reagent.ReagentId, out var proto)) + if (!_prototypeManager.TryIndex(reagent.Prototype, out var proto)) continue; var mostToRemove = FixedPoint2.Zero; @@ -138,7 +138,7 @@ namespace Content.Server.Body.Systems { if (meta.RemoveEmpty) { - _solutionContainerSystem.TryRemoveReagent(solutionEntityUid.Value, solution, reagent.ReagentId, + _solutionContainerSystem.RemoveReagent(solutionEntityUid.Value, solution, reagent, FixedPoint2.New(1)); } @@ -168,7 +168,7 @@ namespace Content.Server.Body.Systems mostToRemove *= group.MetabolismRateModifier; - mostToRemove = FixedPoint2.Clamp(mostToRemove, 0, reagent.Quantity); + mostToRemove = FixedPoint2.Clamp(mostToRemove, 0, quantity); float scale = (float) mostToRemove / (float) entry.MetabolismRate; @@ -204,8 +204,7 @@ namespace Content.Server.Body.Systems // remove a certain amount of reagent if (mostToRemove > FixedPoint2.Zero) { - _solutionContainerSystem.TryRemoveReagent(solutionEntityUid.Value, solution, reagent.ReagentId, - mostToRemove); + _solutionContainerSystem.RemoveReagent(solutionEntityUid.Value, solution, reagent, mostToRemove); } } } diff --git a/Content.Server/Body/Systems/StomachSystem.cs b/Content.Server/Body/Systems/StomachSystem.cs index b5d1282465..e33931315a 100644 --- a/Content.Server/Body/Systems/StomachSystem.cs +++ b/Content.Server/Body/Systems/StomachSystem.cs @@ -45,14 +45,13 @@ namespace Content.Server.Body.Systems delta.Increment(stomach.UpdateInterval); if (delta.Lifetime > stomach.DigestionDelay) { - if (stomachSolution.TryGetReagent(delta.ReagentId, out var quant)) + if (stomachSolution.TryGetReagent(delta.ReagentQuantity.Reagent, out var reagent)) { - if (quant > delta.Quantity) - quant = delta.Quantity; + if (reagent.Quantity > delta.ReagentQuantity.Quantity) + reagent = new(reagent.Reagent, delta.ReagentQuantity.Quantity); - _solutionContainerSystem.TryRemoveReagent((stomach).Owner, stomachSolution, - delta.ReagentId, quant); - transferSolution.AddReagent(delta.ReagentId, quant); + _solutionContainerSystem.RemoveReagent((stomach).Owner, stomachSolution, reagent); + transferSolution.AddReagent(reagent); } queue.Add(delta); @@ -116,7 +115,7 @@ namespace Content.Server.Body.Systems // Add each reagent to ReagentDeltas. Used to track how long each reagent has been in the stomach foreach (var reagent in solution.Contents) { - stomach.ReagentDeltas.Add(new StomachComponent.ReagentDelta(reagent.ReagentId, reagent.Quantity)); + stomach.ReagentDeltas.Add(new StomachComponent.ReagentDelta(reagent)); } return true; diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 1c0582e5f4..43255170b2 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -824,10 +824,10 @@ namespace Content.Server.Botany.Systems if (solution.Volume > 0 && component.MutationLevel < 25) { var amt = FixedPoint2.New(1); - foreach (var (reagentId, quantity) in _solutionSystem.RemoveEachReagent(uid, solution, amt)) + foreach (var entry in _solutionSystem.RemoveEachReagent(uid, solution, amt)) { - var reagentProto = _prototype.Index(reagentId); - reagentProto.ReactionPlant(uid, new Solution.ReagentQuantity(reagentId, quantity), solution); + var reagentProto = _prototype.Index(entry.Reagent.Prototype); + reagentProto.ReactionPlant(uid, entry, solution); } } diff --git a/Content.Server/Cargo/Systems/PricingSystem.cs b/Content.Server/Cargo/Systems/PricingSystem.cs index e8e9909a2a..1f6d941b6b 100644 --- a/Content.Server/Cargo/Systems/PricingSystem.cs +++ b/Content.Server/Cargo/Systems/PricingSystem.cs @@ -110,11 +110,13 @@ public sealed class PricingSystem : EntitySystem foreach (var solution in component.Solutions.Values) { - foreach (var reagent in solution.Contents) + foreach (var (reagent, quantity) in solution.Contents) { - if (!_prototypeManager.TryIndex(reagent.ReagentId, out var reagentProto)) + if (!_prototypeManager.TryIndex(reagent.Prototype, out var reagentProto)) continue; - price += (float) reagent.Quantity * reagentProto.PricePerUnit; + + // TODO check ReagentData for price information? + price += (float) quantity * reagentProto.PricePerUnit; } } diff --git a/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs b/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs index 2ec7ad5c49..3e5250d8bd 100644 --- a/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs @@ -8,6 +8,7 @@ using Content.Server.Storage.EntitySystems; using Content.Shared.Administration.Logs; using Content.Shared.Chemistry; using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Reagent; using Content.Shared.Containers.ItemSlots; using Content.Shared.Database; using Content.Shared.FixedPoint; @@ -117,7 +118,7 @@ namespace Content.Server.Chemistry.EntitySystems ClickSound(chemMaster); } - private void TransferReagents(ChemMasterComponent chemMaster, string reagentId, FixedPoint2 amount, bool fromBuffer) + private void TransferReagents(ChemMasterComponent chemMaster, ReagentId id, FixedPoint2 amount, bool fromBuffer) { var container = _itemSlotsSystem.GetItemOrNull(chemMaster.Owner, SharedChemMaster.InputSlotName); if (container is null || @@ -130,26 +131,26 @@ namespace Content.Server.Chemistry.EntitySystems if (fromBuffer) // Buffer to container { amount = FixedPoint2.Min(amount, containerSolution.AvailableVolume); - amount = bufferSolution.RemoveReagent(reagentId, amount); - _solutionContainerSystem.TryAddReagent(container.Value, containerSolution, reagentId, amount, out var _); + amount = bufferSolution.RemoveReagent(id, amount); + _solutionContainerSystem.TryAddReagent(container.Value, containerSolution, id, amount, out var _); } else // Container to buffer { - amount = FixedPoint2.Min(amount, containerSolution.GetReagentQuantity(reagentId)); - _solutionContainerSystem.TryRemoveReagent(container.Value, containerSolution, reagentId, amount); - bufferSolution.AddReagent(reagentId, amount); + amount = FixedPoint2.Min(amount, containerSolution.GetReagentQuantity(id)); + _solutionContainerSystem.RemoveReagent(container.Value, containerSolution, id, amount); + bufferSolution.AddReagent(id, amount); } UpdateUiState(chemMaster, updateLabel: true); } - private void DiscardReagents(ChemMasterComponent chemMaster, string reagentId, FixedPoint2 amount, bool fromBuffer) + private void DiscardReagents(ChemMasterComponent chemMaster, ReagentId id, FixedPoint2 amount, bool fromBuffer) { if (fromBuffer) { if (_solutionContainerSystem.TryGetSolution(chemMaster.Owner, SharedChemMaster.BufferSolutionName, out var bufferSolution)) - bufferSolution.RemoveReagent(reagentId, amount); + bufferSolution.RemoveReagent(id, amount); else return; } @@ -159,7 +160,7 @@ namespace Content.Server.Chemistry.EntitySystems if (container is not null && _solutionContainerSystem.TryGetFitsInDispenser(container.Value, out var containerSolution)) { - _solutionContainerSystem.TryRemoveReagent(container.Value, containerSolution, reagentId, amount); + _solutionContainerSystem.RemoveReagent(container.Value, containerSolution, id, amount); } else return; @@ -349,17 +350,21 @@ namespace Content.Server.Chemistry.EntitySystems return (Name(pill), quantity); })).ToList(); - return pills is null - ? null - : new ContainerInfo(name, false, storage.StorageUsed, storage.StorageCapacityMax, pills); + if (pills == null) + return null; + + return new ContainerInfo(name, storage.StorageUsed, storage.StorageCapacityMax) + { + Entities = pills + }; } private static ContainerInfo BuildContainerInfo(string name, Solution solution) { - var reagents = solution.Contents - .Select(reagent => (reagent.ReagentId, reagent.Quantity)).ToList(); - - return new ContainerInfo(name, true, solution.Volume, solution.MaxVolume, reagents); + return new ContainerInfo(name, solution.Volume, solution.MaxVolume) + { + Reagents = solution.Contents + }; } } } diff --git a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs index 9a1d560f34..a84dff49d8 100644 --- a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs @@ -3,6 +3,7 @@ using Content.Server.Administration.Logs; using Content.Server.Chemistry.Components; using Content.Shared.Chemistry; using Content.Shared.Chemistry.Dispenser; +using Content.Shared.Chemistry.Reagent; using Content.Shared.Containers.ItemSlots; using Content.Shared.Database; using Content.Shared.Emag.Components; @@ -63,28 +64,30 @@ namespace Content.Server.Chemistry.EntitySystems if (_solutionContainerSystem.TryGetFitsInDispenser(container.Value, out var solution)) { - var reagents = solution.Contents.Select(reagent => (reagent.ReagentId, reagent.Quantity)).ToList(); - return new ContainerInfo(Name(container.Value), true, solution.Volume, solution.MaxVolume, reagents); + return new ContainerInfo(Name(container.Value), solution.Volume, solution.MaxVolume) + { + Reagents = solution.Contents + }; } return null; } - private List GetInventory(ReagentDispenserComponent reagentDispenser) + private List GetInventory(ReagentDispenserComponent reagentDispenser) { - var inventory = new List(); + var inventory = new List(); if (reagentDispenser.PackPrototypeId is not null && _prototypeManager.TryIndex(reagentDispenser.PackPrototypeId, out ReagentDispenserInventoryPrototype? packPrototype)) { - inventory.AddRange(packPrototype.Inventory); + inventory.AddRange(packPrototype.Inventory.Select(x => new ReagentId(x, null))); } if (HasComp(reagentDispenser.Owner) && reagentDispenser.EmagPackPrototypeId is not null && _prototypeManager.TryIndex(reagentDispenser.EmagPackPrototypeId, out ReagentDispenserInventoryPrototype? emagPackPrototype)) { - inventory.AddRange(emagPackPrototype.Inventory); + inventory.AddRange(emagPackPrototype.Inventory.Select(x => new ReagentId(x, null))); } return inventory; diff --git a/Content.Server/Chemistry/EntitySystems/RehydratableSystem.cs b/Content.Server/Chemistry/EntitySystems/RehydratableSystem.cs index 3dd1c6ae58..85f823f7aa 100644 --- a/Content.Server/Chemistry/EntitySystems/RehydratableSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/RehydratableSystem.cs @@ -20,7 +20,7 @@ public sealed class RehydratableSystem : EntitySystem private void OnSolutionChange(EntityUid uid, RehydratableComponent comp, SolutionChangedEvent args) { - var quantity = _solutions.GetReagentQuantity(uid, comp.CatalystPrototype); + var quantity = _solutions.GetTotalPrototypeQuantity(uid, comp.CatalystPrototype); if (quantity != FixedPoint2.Zero && quantity >= comp.CatalystMinimum) { Expand(uid, comp); diff --git a/Content.Server/Chemistry/EntitySystems/SolutionContainerSystem.cs b/Content.Server/Chemistry/EntitySystems/SolutionContainerSystem.cs index 61da862be9..3ab07cc49b 100644 --- a/Content.Server/Chemistry/EntitySystems/SolutionContainerSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/SolutionContainerSystem.cs @@ -23,9 +23,11 @@ namespace Content.Server.Chemistry.EntitySystems; public sealed class SolutionChangedEvent : EntityEventArgs { public readonly Solution Solution; + public readonly string SolutionId; - public SolutionChangedEvent(Solution solution) + public SolutionChangedEvent(Solution solution, string solutionId) { + SolutionId = solutionId; Solution = solution; } } @@ -103,7 +105,7 @@ public sealed partial class SolutionContainerSystem : EntitySystem { var msg = new FormattedMessage(); - if (solution.Contents.Count == 0) //TODO: better way to see if empty? + if (solution.Volume == 0) { msg.AddMarkup(Loc.GetString("scannable-solution-empty-container")); return msg; @@ -111,17 +113,13 @@ public sealed partial class SolutionContainerSystem : EntitySystem msg.AddMarkup(Loc.GetString("scannable-solution-main-text")); - foreach (var reagent in solution) + foreach (var (proto, quantity) in solution.GetReagentPrototypes(_prototypeManager)) { - if (!_prototypeManager.TryIndex(reagent.ReagentId, out var proto)) - { - continue; - } msg.PushNewline(); msg.AddMarkup(Loc.GetString("scannable-solution-chemical" , ("type", proto.LocalizedName) , ("color", proto.SubstanceColor.ToHexNoAlpha()) - , ("amount", reagent.Quantity))); + , ("amount", quantity))); } return msg; @@ -132,46 +130,41 @@ public sealed partial class SolutionContainerSystem : EntitySystem { SolutionContainerManagerComponent? solutionsManager = null; if (!Resolve(args.Examined, ref solutionsManager) - || !solutionsManager.Solutions.TryGetValue(examinableComponent.Solution, out var solutionHolder)) + || !solutionsManager.Solutions.TryGetValue(examinableComponent.Solution, out var solution)) { return; } - var primaryReagent = solutionHolder.GetPrimaryReagentId(); + var primaryReagent = solution.GetPrimaryReagentId(); - if (string.IsNullOrEmpty(primaryReagent)) + if (string.IsNullOrEmpty(primaryReagent?.Prototype)) { args.PushText(Loc.GetString("shared-solution-container-component-on-examine-empty-container")); return; } - if (!_prototypeManager.TryIndex(primaryReagent, out ReagentPrototype? primary)) + if (!_prototypeManager.TryIndex(primaryReagent.Value.Prototype, out ReagentPrototype? primary)) { Logger.Error( $"{nameof(Solution)} could not find the prototype associated with {primaryReagent}."); return; } - var colorHex = solutionHolder.GetColor(_prototypeManager) + var colorHex = solution.GetColor(_prototypeManager) .ToHexNoAlpha(); //TODO: If the chem has a dark color, the examine text becomes black on a black background, which is unreadable. var messageString = "shared-solution-container-component-on-examine-main-text"; args.PushMarkup(Loc.GetString(messageString, ("color", colorHex), - ("wordedAmount", Loc.GetString(solutionHolder.Contents.Count == 1 + ("wordedAmount", Loc.GetString(solution.Contents.Count == 1 ? "shared-solution-container-component-on-examine-worded-amount-one-reagent" : "shared-solution-container-component-on-examine-worded-amount-multiple-reagents")), ("desc", primary.LocalizedPhysicalDescription))); // Add descriptions of immediately recognizable reagents, like water or beer var recognized = new List(); - foreach (var (id, _) in solutionHolder) + foreach (var proto in solution.GetReagentPrototypes(_prototypeManager).Keys) { - if (!_prototypeManager.TryIndex(id, out var proto)) - { - continue; - } - if (!proto.Recognizable) { continue; @@ -278,7 +271,7 @@ public sealed partial class SolutionContainerSystem : EntitySystem } UpdateAppearance(uid, solutionHolder); - RaiseLocalEvent(uid, new SolutionChangedEvent(solutionHolder)); + RaiseLocalEvent(uid, new SolutionChangedEvent(solutionHolder, solutionHolder.Name)); } public void RemoveAllSolution(EntityUid uid, Solution solutionHolder) @@ -324,46 +317,111 @@ public sealed partial class SolutionContainerSystem : EntitySystem /// /// /// Container to which we are adding reagent - /// The Id of the reagent to add. + /// The reagent to add. + /// The amount of reagent successfully added. + /// If all the reagent could be added. + public bool TryAddReagent(EntityUid targetUid, Solution targetSolution, ReagentQuantity reagentQuantity, + out FixedPoint2 acceptedQuantity, float? temperature = null) + { + acceptedQuantity = targetSolution.AvailableVolume > reagentQuantity.Quantity + ? reagentQuantity.Quantity + : targetSolution.AvailableVolume; + + if (acceptedQuantity <= 0) + return reagentQuantity.Quantity == 0; + + if (temperature == null) + { + targetSolution.AddReagent(reagentQuantity.Reagent, acceptedQuantity); + } + else + { + var proto = _prototypeManager.Index(reagentQuantity.Reagent.Prototype); + targetSolution.AddReagent(proto, acceptedQuantity, temperature.Value, _prototypeManager); + } + + UpdateChemicals(targetUid, targetSolution, true); + return acceptedQuantity == reagentQuantity.Quantity; + } + + /// + /// Adds reagent of an Id to the container. + /// + /// + /// Container to which we are adding reagent + /// The Id of the reagent to add. /// The amount of reagent to add. /// The amount of reagent successfully added. /// If all the reagent could be added. - public bool TryAddReagent(EntityUid targetUid, Solution targetSolution, string reagentId, FixedPoint2 quantity, - out FixedPoint2 acceptedQuantity, float? temperature = null) + public bool TryAddReagent(EntityUid targetUid, Solution targetSolution, string prototype, FixedPoint2 quantity, + out FixedPoint2 acceptedQuantity, float? temperature = null, ReagentData? data = null) { - acceptedQuantity = targetSolution.AvailableVolume > quantity ? quantity : targetSolution.AvailableVolume; - - if (acceptedQuantity <= 0) - return quantity == 0; - - if (temperature == null) - targetSolution.AddReagent(reagentId, acceptedQuantity); - else - targetSolution.AddReagent(_prototypeManager.Index(reagentId), acceptedQuantity, temperature.Value, _prototypeManager); - - UpdateChemicals(targetUid, targetSolution, true); - return acceptedQuantity == quantity; + var reagent = new ReagentQuantity(prototype, quantity, data); + return TryAddReagent(targetUid, targetSolution, reagent, out acceptedQuantity, temperature); } + /// + /// Adds reagent of an Id to the container. + /// + /// + /// Container to which we are adding reagent + /// The reagent to add. + /// The amount of reagent to add. + /// The amount of reagent successfully added. + /// If all the reagent could be added. + public bool TryAddReagent(EntityUid targetUid, Solution targetSolution, ReagentId reagentId, FixedPoint2 quantity, + out FixedPoint2 acceptedQuantity, float? temperature = null) + { + var quant = new ReagentQuantity(reagentId, quantity); + return TryAddReagent(targetUid, targetSolution, quant, out acceptedQuantity, temperature); + } /// - /// Removes reagent of an Id to the container. + /// Removes reagent from a container. /// /// /// Solution container from which we are removing reagent - /// The Id of the reagent to remove. - /// The amount of reagent to remove. + /// The reagent to remove. /// If the reagent to remove was found in the container. - public bool TryRemoveReagent(EntityUid targetUid, Solution? container, string reagentId, FixedPoint2 quantity) + public bool RemoveReagent(EntityUid targetUid, Solution? container, ReagentQuantity reagentQuantity) { - if (container == null || !container.ContainsReagent(reagentId)) + if (container == null) + return false; + + var quant = container.RemoveReagent(reagentQuantity); + if (quant <= FixedPoint2.Zero) return false; - container.RemoveReagent(reagentId, quantity); UpdateChemicals(targetUid, container); return true; } + /// + /// Removes reagent from a container. + /// + /// + /// Solution container from which we are removing reagent + /// The Id of the reagent to remove. + /// The amount of reagent to remove. + /// If the reagent to remove was found in the container. + public bool RemoveReagent(EntityUid targetUid, Solution? container, string prototype, FixedPoint2 quantity, ReagentData? data = null) + { + return RemoveReagent(targetUid, container, new ReagentQuantity(prototype, quantity, data)); + } + + /// + /// Removes reagent from a container. + /// + /// + /// Solution container from which we are removing reagent + /// The reagent to remove. + /// The amount of reagent to remove. + /// If the reagent to remove was found in the container. + public bool RemoveReagent(EntityUid targetUid, Solution? container, ReagentId reagentId, FixedPoint2 quantity) + { + return RemoveReagent(targetUid, container, new ReagentQuantity(reagentId, quantity)); + } + /// /// Adds a solution to the container, if it can fully fit. /// @@ -391,19 +449,35 @@ public sealed partial class SolutionContainerSystem : EntitySystem /// target solution /// quantity of solution to move from source to target. If this is a negative number, the source & target roles are reversed. public bool TryTransferSolution(EntityUid sourceUid, EntityUid targetUid, Solution source, Solution target, FixedPoint2 quantity) + { + if (!TryTransferSolution(targetUid, target, source, quantity)) + return false; + + UpdateChemicals(sourceUid, source, false); + return true; + } + + /// + /// Moves some quantity of a solution from one solution to another. + /// + /// entity holding the source solution + /// entity holding the target solution + /// source solution + /// target solution + /// quantity of solution to move from source to target. If this is a negative number, the source & target roles are reversed. + public bool TryTransferSolution(EntityUid targetUid, Solution target, Solution source, FixedPoint2 quantity) { if (quantity < 0) - return TryTransferSolution(targetUid, sourceUid, target, source, -quantity); + throw new InvalidOperationException("Quantity must be positive"); quantity = FixedPoint2.Min(quantity, target.AvailableVolume, source.Volume); if (quantity == 0) return false; - // TODO This should be made into a function that directly transfers reagents. currently this is quite - // inefficient. + // TODO This should be made into a function that directly transfers reagents. + // Currently this is quite inefficient. target.AddSolution(source.SplitSolution(quantity), _prototypeManager); - UpdateChemicals(sourceUid, source, false); UpdateChemicals(targetUid, target, true); return true; } @@ -540,7 +614,7 @@ public sealed partial class SolutionContainerSystem : EntitySystem } public Solution EnsureSolution(EntityUid uid, string name, - IEnumerable reagents, + IEnumerable reagents, bool setMaxVol = true, SolutionContainerManagerComponent? solutionsMgr = null) { @@ -574,19 +648,16 @@ public sealed partial class SolutionContainerSystem : EntitySystem // RemoveReagent does a RemoveSwap, meaning we don't have to copy the list if we iterate it backwards. for (var i = solution.Contents.Count-1; i >= 0; i--) { - var (reagentId, _) = solution.Contents[i]; - - var removedQuantity = solution.RemoveReagent(reagentId, quantity); - - if(removedQuantity > 0) - removedSolution.AddReagent(reagentId, removedQuantity); + var (reagent, _) = solution.Contents[i]; + var removedQuantity = solution.RemoveReagent(reagent, quantity); + removedSolution.AddReagent(reagent, removedQuantity); } UpdateChemicals(uid, solution); return removedSolution; } - public FixedPoint2 GetReagentQuantity(EntityUid owner, string reagentId) + public FixedPoint2 GetTotalPrototypeQuantity(EntityUid owner, string reagentId) { var reagentQuantity = FixedPoint2.New(0); if (EntityManager.EntityExists(owner) @@ -594,7 +665,7 @@ public sealed partial class SolutionContainerSystem : EntitySystem { foreach (var solution in managerComponent.Solutions.Values) { - reagentQuantity += solution.GetReagentQuantity(reagentId); + reagentQuantity += solution.GetTotalPrototypeQuantity(reagentId); } } @@ -640,21 +711,21 @@ public sealed partial class SolutionContainerSystem : EntitySystem if (component.Solutions.Count == 0) return null; - var reagentCounts = new Dictionary(); + var reagentCounts = new Dictionary(); foreach (var solution in component.Solutions.Values) { - foreach (var reagent in solution.Contents) + foreach (var (reagent, quantity) in solution.Contents) { - reagentCounts.TryGetValue(reagent.ReagentId, out var existing); - existing += reagent.Quantity; - reagentCounts[reagent.ReagentId] = existing; + reagentCounts.TryGetValue(reagent, out var existing); + existing += quantity; + reagentCounts[reagent] = existing; } } var max = reagentCounts.Max(); - return _prototypeManager.Index(max.Key); + return _prototypeManager.Index(max.Key.Prototype); } public SoundSpecifier? GetSound(SolutionContainerManagerComponent component) diff --git a/Content.Server/Chemistry/EntitySystems/TransformableContainerSystem.cs b/Content.Server/Chemistry/EntitySystems/TransformableContainerSystem.cs index 41ab11aab0..13246c22e1 100644 --- a/Content.Server/Chemistry/EntitySystems/TransformableContainerSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/TransformableContainerSystem.cs @@ -47,14 +47,14 @@ public sealed class TransformableContainerSystem : EntitySystem var reagentId = solution.GetPrimaryReagentId(); //If biggest reagent didn't changed - don't change anything at all - if (component.CurrentReagent != null && component.CurrentReagent.ID == reagentId) + if (component.CurrentReagent != null && component.CurrentReagent.ID == reagentId?.Prototype) { return; } //Only reagents with spritePath property can change appearance of transformable containers! - if (!string.IsNullOrWhiteSpace(reagentId) - && _prototypeManager.TryIndex(reagentId, out ReagentPrototype? proto)) + if (!string.IsNullOrWhiteSpace(reagentId?.Prototype) + && _prototypeManager.TryIndex(reagentId.Value.Prototype, out ReagentPrototype? proto)) { var metadata = MetaData(owner); var val = Loc.GetString("transformable-container-component-glass", ("name", proto.LocalizedName)); diff --git a/Content.Server/Chemistry/EntitySystems/VaporSystem.cs b/Content.Server/Chemistry/EntitySystems/VaporSystem.cs index 92ba44b2c6..7288a993d3 100644 --- a/Content.Server/Chemistry/EntitySystems/VaporSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/VaporSystem.cs @@ -1,18 +1,17 @@ using System.Numerics; using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Components.SolutionManager; +using Content.Shared.Chemistry; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; using Content.Shared.FixedPoint; using Content.Shared.Physics; using Content.Shared.Spawners.Components; using Content.Shared.Throwing; -using Content.Shared.Vapor; using JetBrains.Annotations; using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; -using Robust.Shared.Physics.Dynamics; using Robust.Shared.Physics.Events; using Robust.Shared.Physics.Systems; using Robust.Shared.Prototypes; @@ -27,6 +26,7 @@ namespace Content.Server.Chemistry.EntitySystems [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; [Dependency] private readonly ThrowingSystem _throwing = default!; + [Dependency] private readonly ReactiveSystem _reactive = default!; private const float ReactTime = 0.125f; @@ -45,7 +45,7 @@ namespace Content.Server.Chemistry.EntitySystems foreach (var value in contents.Solutions.Values) { - value.DoEntityReaction(args.OtherEntity, ReactionMethod.Touch); + _reactive.DoEntityReaction(args.OtherEntity, value, ReactionMethod.Touch); } // Check for collision with a impassable object (e.g. wall) and stop @@ -120,18 +120,18 @@ namespace Content.Server.Chemistry.EntitySystems foreach (var reagentQuantity in contents.Contents.ToArray()) { if (reagentQuantity.Quantity == FixedPoint2.Zero) continue; - var reagent = _protoManager.Index(reagentQuantity.ReagentId); + var reagent = _protoManager.Index(reagentQuantity.Reagent.Prototype); var reaction = reagent.ReactionTile(tile, (reagentQuantity.Quantity / vapor.TransferAmount) * 0.25f); if (reaction > reagentQuantity.Quantity) { - _sawmill.Error($"Tried to tile react more than we have for reagent {reagentQuantity.ReagentId}. Found {reaction} and we only have {reagentQuantity.Quantity}"); + _sawmill.Error($"Tried to tile react more than we have for reagent {reagentQuantity}. Found {reaction} and we only have {reagentQuantity.Quantity}"); reaction = reagentQuantity.Quantity; } - _solutionContainerSystem.TryRemoveReagent(vapor.Owner, contents, reagentQuantity.ReagentId, reaction); + _solutionContainerSystem.RemoveReagent(vapor.Owner, contents, reagentQuantity.Reagent, reaction); } } diff --git a/Content.Server/Chemistry/ReagentEffectConditions/ReagentThreshold.cs b/Content.Server/Chemistry/ReagentEffectConditions/ReagentThreshold.cs index 87e1e683a8..203656be73 100644 --- a/Content.Server/Chemistry/ReagentEffectConditions/ReagentThreshold.cs +++ b/Content.Server/Chemistry/ReagentEffectConditions/ReagentThreshold.cs @@ -19,6 +19,7 @@ namespace Content.Server.Chemistry.ReagentEffectConditions [DataField("max")] public FixedPoint2 Max = FixedPoint2.MaxValue; + // TODO use ReagentId [DataField("reagent")] public string? Reagent; @@ -29,10 +30,8 @@ namespace Content.Server.Chemistry.ReagentEffectConditions return true; // No condition to apply. var quant = FixedPoint2.Zero; - if (args.Source != null && args.Source.ContainsReagent(reagent)) - { - quant = args.Source.GetReagentQuantity(reagent); - } + if (args.Source != null) + quant = args.Source.GetTotalPrototypeQuantity(reagent); return quant >= Min && quant <= Max; } diff --git a/Content.Server/Chemistry/ReagentEffects/AdjustReagent.cs b/Content.Server/Chemistry/ReagentEffects/AdjustReagent.cs index 00c6a91046..c79dd24332 100644 --- a/Content.Server/Chemistry/ReagentEffects/AdjustReagent.cs +++ b/Content.Server/Chemistry/ReagentEffects/AdjustReagent.cs @@ -16,6 +16,7 @@ namespace Content.Server.Chemistry.ReagentEffects /// [DataField("reagent", customTypeSerializer:typeof(PrototypeIdSerializer))] public string? Reagent = null; + // TODO use ReagentId /// /// The metabolism group to remove, if the reagent satisfies any. @@ -38,8 +39,8 @@ namespace Content.Server.Chemistry.ReagentEffects if (Reagent != null) { - if (amount < 0 && args.Source.ContainsReagent(Reagent)) - solutionSys.TryRemoveReagent(args.SolutionEntity, args.Source, Reagent, -amount); + 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 _); } @@ -48,13 +49,13 @@ namespace Content.Server.Chemistry.ReagentEffects var prototypeMan = IoCManager.Resolve(); foreach (var quant in args.Source.Contents.ToArray()) { - var proto = prototypeMan.Index(quant.ReagentId); + var proto = prototypeMan.Index(quant.Reagent.Prototype); if (proto.Metabolisms != null && proto.Metabolisms.ContainsKey(Group)) { if (amount < 0) - solutionSys.TryRemoveReagent(args.SolutionEntity, args.Source, quant.ReagentId, amount); + solutionSys.RemoveReagent(args.SolutionEntity, args.Source, quant.Reagent, amount); if (amount > 0) - solutionSys.TryAddReagent(args.SolutionEntity, args.Source, quant.ReagentId, amount, out _); + solutionSys.TryAddReagent(args.SolutionEntity, args.Source, quant.Reagent, amount, out _); } } } diff --git a/Content.Server/Fluids/EntitySystems/AbsorbentSystem.cs b/Content.Server/Fluids/EntitySystems/AbsorbentSystem.cs index db58430e39..58757af660 100644 --- a/Content.Server/Fluids/EntitySystems/AbsorbentSystem.cs +++ b/Content.Server/Fluids/EntitySystems/AbsorbentSystem.cs @@ -55,7 +55,8 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem var oldProgress = component.Progress.ShallowClone(); component.Progress.Clear(); - if (solution.TryGetReagent(PuddleSystem.EvaporationReagent, out var water)) + var water = solution.GetTotalPrototypeQuantity(PuddleSystem.EvaporationReagent); + if (water > FixedPoint2.Zero) { component.Progress[_prototype.Index(PuddleSystem.EvaporationReagent).SubstanceColor] = water.Float(); } @@ -230,7 +231,7 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem } // Check if we have any evaporative reagents on our absorber to transfer - absorberSoln.TryGetReagent(PuddleSystem.EvaporationReagent, out var available); + var available = absorberSoln.GetTotalPrototypeQuantity(PuddleSystem.EvaporationReagent); // No material if (available == FixedPoint2.Zero) diff --git a/Content.Server/Fluids/EntitySystems/PuddleSystem.Evaporation.cs b/Content.Server/Fluids/EntitySystems/PuddleSystem.Evaporation.cs index 429f128810..1392a23356 100644 --- a/Content.Server/Fluids/EntitySystems/PuddleSystem.Evaporation.cs +++ b/Content.Server/Fluids/EntitySystems/PuddleSystem.Evaporation.cs @@ -25,7 +25,7 @@ public sealed partial class PuddleSystem return; } - if (solution.ContainsReagent(EvaporationReagent)) + if (solution.ContainsPrototype(EvaporationReagent)) { var evaporation = AddComp(uid); evaporation.NextTick = _timing.CurTime + EvaporationCooldown; @@ -51,7 +51,7 @@ public sealed partial class PuddleSystem continue; var reagentTick = evaporation.EvaporationAmount * EvaporationCooldown.TotalSeconds; - _solutionContainerSystem.TryRemoveReagent(uid, puddleSolution, EvaporationReagent, reagentTick); + _solutionContainerSystem.RemoveReagent(uid, puddleSolution, EvaporationReagent, reagentTick); // Despawn if we're done if (puddleSolution.Volume == FixedPoint2.Zero) @@ -65,6 +65,6 @@ public sealed partial class PuddleSystem public bool CanFullyEvaporate(Solution solution) { - return solution.Contents.Count == 1 && solution.ContainsReagent(EvaporationReagent); + return solution.Contents.Count == 1 && solution.ContainsPrototype(EvaporationReagent); } } diff --git a/Content.Server/Fluids/EntitySystems/PuddleSystem.cs b/Content.Server/Fluids/EntitySystems/PuddleSystem.cs index 991bbac6de..614e685456 100644 --- a/Content.Server/Fluids/EntitySystems/PuddleSystem.cs +++ b/Content.Server/Fluids/EntitySystems/PuddleSystem.cs @@ -56,6 +56,17 @@ public sealed partial class PuddleSystem : SharedPuddleSystem [Dependency] private readonly SlowContactsSystem _slowContacts = default!; [Dependency] private readonly TileFrictionController _tile = default!; + [ValidatePrototypeId] + private const string Blood = "Blood"; + + [ValidatePrototypeId] + private const string Slime = "Slime"; + + [ValidatePrototypeId] + private const string SpiderBlood = "SpiderBlood"; + + private static string[] _standoutReagents = new[] { Blood, Slime, SpiderBlood }; + public static float PuddleVolume = 1000; // Using local deletion queue instead of the standard queue so that we can easily "undelete" if a puddle @@ -272,14 +283,14 @@ public sealed partial class PuddleSystem : SharedPuddleSystem // Make blood stand out more // Kinda EH // Could potentially do alpha per-solution but future problem. - var standoutReagents = new string[] { "Blood", "Slime", "SpiderBlood" }; - color = solution.GetColorWithout(_prototypeManager, standoutReagents); + color = solution.GetColorWithout(_prototypeManager, _standoutReagents); color = color.WithAlpha(0.7f); - foreach (var standout in standoutReagents) + foreach (var standout in _standoutReagents) { - if (!solution.TryGetReagent(standout, out var quantity)) + var quantity = solution.GetTotalPrototypeQuantity(standout); + if (quantity <= FixedPoint2.Zero) continue; var interpolateValue = quantity.Float() / solution.Volume.Float(); @@ -298,13 +309,13 @@ public sealed partial class PuddleSystem : SharedPuddleSystem var amountRequired = FixedPoint2.New(component.OverflowVolume.Float() * LowThreshold); var slipperyAmount = FixedPoint2.Zero; - foreach (var reagent in solution.Contents) + foreach (var (reagent, quantity) in solution.Contents) { - var reagentProto = _prototypeManager.Index(reagent.ReagentId); + var reagentProto = _prototypeManager.Index(reagent.Prototype); if (reagentProto.Slippery) { - slipperyAmount += reagent.Quantity; + slipperyAmount += quantity; if (slipperyAmount > amountRequired) { @@ -331,9 +342,9 @@ public sealed partial class PuddleSystem : SharedPuddleSystem private void UpdateSlow(EntityUid uid, Solution solution) { var maxViscosity = 0f; - foreach (var reagent in solution.Contents) + foreach (var (reagent, _) in solution.Contents) { - var reagentProto = _prototypeManager.Index(reagent.ReagentId); + var reagentProto = _prototypeManager.Index(reagent.Prototype); maxViscosity = Math.Max(maxViscosity, reagentProto.Viscosity); } if (maxViscosity > 0) @@ -362,7 +373,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem { args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating")); } - else if (solution?.ContainsReagent(EvaporationReagent) == true) + else if (solution?.ContainsPrototype(EvaporationReagent) == true) { args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating-partial")); } @@ -588,15 +599,15 @@ public sealed partial class PuddleSystem : SharedPuddleSystem if (tileReact) { // First, do all tile reactions - for (var i = 0; i < solution.Contents.Count; i++) + for (var i = solution.Contents.Count - 1; i >= 0; i--) { - var (reagentId, quantity) = solution.Contents[i]; - var proto = _prototypeManager.Index(reagentId); + var (reagent, quantity) = solution.Contents[i]; + var proto = _prototypeManager.Index(reagent.Prototype); var removed = proto.ReactionTile(tileRef, quantity); if (removed <= FixedPoint2.Zero) continue; - solution.RemoveReagent(reagentId, removed); + solution.RemoveReagent(reagent, removed); } } diff --git a/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs b/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs index 41a32775d9..cee2e6ce49 100644 --- a/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs @@ -113,6 +113,8 @@ namespace Content.Server.Kitchen.EntitySystems private void SubtractContents(MicrowaveComponent component, FoodRecipePrototype recipe) { + // TODO Turn recipe.IngredientsReagents into a ReagentQuantity[] + var totalReagentsToRemove = new Dictionary(recipe.IngredientsReagents); // this is spaghetti ngl @@ -130,10 +132,7 @@ namespace Content.Server.Kitchen.EntitySystems if (!totalReagentsToRemove.ContainsKey(reagent)) continue; - if (!solution.ContainsReagent(reagent)) - continue; - - var quant = solution.GetReagentQuantity(reagent); + var quant = solution.GetTotalPrototypeQuantity(reagent); if (quant >= totalReagentsToRemove[reagent]) { @@ -145,7 +144,7 @@ namespace Content.Server.Kitchen.EntitySystems totalReagentsToRemove[reagent] -= quant; } - _solutionContainer.TryRemoveReagent(item, solution, reagent, quant); + _solutionContainer.RemoveReagent(item, solution, reagent, quant); } } } @@ -324,6 +323,8 @@ namespace Content.Server.Kitchen.EntitySystems var solidsDict = new Dictionary(); var reagentDict = new Dictionary(); + // TODO use lists of Reagent quantities instead of reagent prototype ids. + foreach (var item in component.Storage.ContainedEntities) { // special behavior when being microwaved ;) @@ -370,12 +371,12 @@ namespace Content.Server.Kitchen.EntitySystems foreach (var (_, solution) in solMan.Solutions) { - foreach (var reagent in solution.Contents) + foreach (var (reagent, quantity) in solution.Contents) { - if (reagentDict.ContainsKey(reagent.ReagentId)) - reagentDict[reagent.ReagentId] += reagent.Quantity; + if (reagentDict.ContainsKey(reagent.Prototype)) + reagentDict[reagent.Prototype] += quantity; else - reagentDict.Add(reagent.ReagentId, reagent.Quantity); + reagentDict.Add(reagent.Prototype, quantity); } } } @@ -417,6 +418,7 @@ namespace Content.Server.Kitchen.EntitySystems foreach (var reagent in recipe.IngredientsReagents) { + // TODO Turn recipe.IngredientsReagents into a ReagentQuantity[] if (!reagents.ContainsKey(reagent.Key)) return (recipe, 0); diff --git a/Content.Server/Materials/MaterialReclaimerSystem.cs b/Content.Server/Materials/MaterialReclaimerSystem.cs index caac0ae577..fe6f9dcc9d 100644 --- a/Content.Server/Materials/MaterialReclaimerSystem.cs +++ b/Content.Server/Materials/MaterialReclaimerSystem.cs @@ -220,14 +220,16 @@ public sealed class MaterialReclaimerSystem : SharedMaterialReclaimerSystem if (!Resolve(reclaimer, ref reclaimerComponent, ref xform)) return; - var overflow = new Solution(); - var totalChemicals = new Dictionary(); + efficiency *= reclaimerComponent.Efficiency; + + var totalChemicals = new Solution(); if (Resolve(item, ref composition, false)) { foreach (var (key, value) in composition.ChemicalComposition) { - totalChemicals[key] = totalChemicals.GetValueOrDefault(key) + value; + // TODO use ReagentQuantity + totalChemicals.AddReagent(key, value * efficiency, false); } } @@ -238,27 +240,15 @@ public sealed class MaterialReclaimerSystem : SharedMaterialReclaimerSystem { foreach (var quantity in solution.Contents) { - totalChemicals[quantity.ReagentId] = - totalChemicals.GetValueOrDefault(quantity.ReagentId) + quantity.Quantity; + totalChemicals.AddReagent(quantity.Reagent.Prototype, quantity.Quantity * efficiency, false); } } } - foreach (var (reagent, amount) in totalChemicals) + _solutionContainer.TryTransferSolution(reclaimer, reclaimerComponent.OutputSolution, totalChemicals, totalChemicals.Volume); + if (totalChemicals.Volume > 0) { - var outputAmount = amount * efficiency * reclaimerComponent.Efficiency; - _solutionContainer.TryAddReagent(reclaimer, reclaimerComponent.OutputSolution, reagent, outputAmount, - out var accepted); - var overflowAmount = outputAmount - accepted; - if (overflowAmount > 0) - { - overflow.AddReagent(reagent, overflowAmount); - } - } - - if (overflow.Volume > 0) - { - _puddle.TrySpillAt(reclaimer, overflow, out _, transformComponent: xform); + _puddle.TrySpillAt(reclaimer, totalChemicals, out _, transformComponent: xform); } } } diff --git a/Content.Server/Nutrition/Components/VapeComponent.cs b/Content.Server/Nutrition/Components/VapeComponent.cs index a3d583cffd..41b03edfb4 100644 --- a/Content.Server/Nutrition/Components/VapeComponent.cs +++ b/Content.Server/Nutrition/Components/VapeComponent.cs @@ -1,12 +1,8 @@ -using System.Threading; using Content.Server.Nutrition.EntitySystems; using Content.Shared.Damage; using Content.Shared.Atmos; -/// -/// Component for vapes -/// -namespace Content.Server.Nutrition.Components +namespace Content.Server.Nutrition.Components // Vapes are very nutritious. { [RegisterComponent, Access(typeof(SmokingSystem))] public sealed partial class VapeComponent : Component @@ -23,6 +19,7 @@ namespace Content.Server.Nutrition.Components [ViewVariables(VVAccess.ReadWrite)] public float ExplosionIntensity { get; set; } = 2.5f; + // TODO use RiggableComponent. [DataField("explodeOnUse")] [ViewVariables(VVAccess.ReadWrite)] public bool ExplodeOnUse { get; set; } = false; @@ -42,10 +39,9 @@ namespace Content.Server.Nutrition.Components [ViewVariables(VVAccess.ReadWrite)] public float ReductionFactor { get; set; } = 300f; + // TODO when this gets fixed, use prototype serializers [DataField("solutionNeeded")] [ViewVariables(VVAccess.ReadWrite)] public string SolutionNeeded = "Water"; - - public CancellationTokenSource? CancelToken; } } diff --git a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs index 36521568a1..980c08d4bc 100644 --- a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs @@ -101,7 +101,7 @@ public sealed class DrinkSystem : EntitySystem var total = 0f; foreach (var quantity in solution.Contents) { - var reagent = _proto.Index(quantity.ReagentId); + var reagent = _proto.Index(quantity.Reagent.Prototype); if (reagent.Metabolisms == null) continue; diff --git a/Content.Server/Nutrition/EntitySystems/FlavorProfileSystem.cs b/Content.Server/Nutrition/EntitySystems/FlavorProfileSystem.cs index 9e20484c31..e01f199ae1 100644 --- a/Content.Server/Nutrition/EntitySystems/FlavorProfileSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/FlavorProfileSystem.cs @@ -84,9 +84,9 @@ public sealed class FlavorProfileSystem : EntitySystem private HashSet GetFlavorsFromReagents(Solution solution, int desiredAmount, HashSet? toIgnore = null) { var flavors = new HashSet(); - foreach (var reagent in solution.Contents) + foreach (var (reagent, quantity) in solution.GetReagentPrototypes(_prototypeManager)) { - if (toIgnore != null && toIgnore.Contains(reagent.ReagentId)) + if (toIgnore != null && toIgnore.Contains(reagent.ID)) { continue; } @@ -96,15 +96,14 @@ public sealed class FlavorProfileSystem : EntitySystem break; } - var proto = _prototypeManager.Index(reagent.ReagentId); // don't care if the quantity is negligible - if (reagent.Quantity < proto.FlavorMinimum) + if (quantity < reagent.FlavorMinimum) { continue; } - if (proto.Flavor != null) - flavors.Add(proto.Flavor); + if (reagent.Flavor != null) + flavors.Add(reagent.Flavor); } return flavors; diff --git a/Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs b/Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs index 07fb24cd20..5b1ced8b18 100644 --- a/Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs +++ b/Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs @@ -35,7 +35,7 @@ namespace Content.Server.Nutrition.EntitySystems SubscribeLocalEvent(OnEmagged); } - private void OnVapeInteraction(EntityUid uid, VapeComponent comp, AfterInteractEvent args) + private void OnVapeInteraction(EntityUid uid, VapeComponent comp, AfterInteractEvent args) { _solutionContainerSystem.TryGetRefillableSolution(uid, out var solution); @@ -44,11 +44,12 @@ namespace Content.Server.Nutrition.EntitySystems var exploded = false; if (!args.CanReach - || solution == null - || comp.CancelToken != null - || !TryComp(args.Target, out var _) - || _foodSystem.IsMouthBlocked(args.Target.Value, args.User)) + || solution == null + || !HasComp(args.Target) + || _foodSystem.IsMouthBlocked(args.Target.Value, args.User)) + { return; + } if (solution.Contents.Count == 0) { @@ -72,9 +73,14 @@ namespace Content.Server.Nutrition.EntitySystems } else { + // All vapes explode if they contain anything other than pure water??? + // WTF is this? Why is this? Am I going insane? + // Who the fuck vapes pure water? + // If this isn't how this is meant to work and this is meant to be for vapes with plasma or something, + // just re-use the existing RiggableSystem. foreach (var name in solution.Contents) { - if (name.ReagentId != comp.SolutionNeeded) + if (name.Reagent.Prototype != comp.SolutionNeeded) { exploded = true; _explosionSystem.QueueExplosion(uid, "Default", comp.ExplosionIntensity, 0.5f, 3, canCreateVacuum: false); @@ -92,7 +98,7 @@ namespace Content.Server.Nutrition.EntitySystems _popupSystem.PopupEntity( Loc.GetString("vape-component-try-use-vape-forced", ("user", userName)), args.Target.Value, args.Target.Value); - + _popupSystem.PopupEntity( Loc.GetString("vape-component-try-use-vape-forced-user", ("target", targetName)), args.User, args.User); @@ -106,8 +112,6 @@ namespace Content.Server.Nutrition.EntitySystems if (!exploded) { - comp.CancelToken = new CancellationTokenSource(); - var vapeDoAfterEvent = new VapeDoAfterEvent(solution, forced); _doAfterSystem.TryStartDoAfter(new DoAfterArgs(args.User, delay, vapeDoAfterEvent, uid, target: args.Target, used: uid) { @@ -121,14 +125,6 @@ namespace Content.Server.Nutrition.EntitySystems private void OnVapeDoAfter(EntityUid uid, VapeComponent comp, VapeDoAfterEvent args) { - if (args.Cancelled) - { - comp.CancelToken = null; - return; - } - - comp.CancelToken = null; - if (args.Handled || args.Args.Target == null) return; @@ -148,7 +144,7 @@ namespace Content.Server.Nutrition.EntitySystems _atmosphereSystem.Merge(environment, merger); args.Solution.RemoveAllSolution(); - + if (args.Forced) { var targetName = Identity.Entity(args.Args.Target.Value, EntityManager); @@ -157,7 +153,7 @@ namespace Content.Server.Nutrition.EntitySystems _popupSystem.PopupEntity( Loc.GetString("vape-component-vape-success-forced", ("user", userName)), args.Args.Target.Value, args.Args.Target.Value); - + _popupSystem.PopupEntity( Loc.GetString("vape-component-vape-success-user-forced", ("target", targetName)), args.Args.User, args.Args.Target.Value); diff --git a/Content.Server/Power/Components/RiggableComponent.cs b/Content.Server/Power/Components/RiggableComponent.cs index eaf216fd5f..797c8c81f4 100644 --- a/Content.Server/Power/Components/RiggableComponent.cs +++ b/Content.Server/Power/Components/RiggableComponent.cs @@ -1,11 +1,20 @@ +using Content.Shared.Chemistry.Reagent; +using Content.Shared.FixedPoint; + namespace Content.Server.Power.Components; [RegisterComponent] public sealed partial class RiggableComponent : Component { - public const string SolutionName = "battery"; - [ViewVariables(VVAccess.ReadWrite)] [DataField("isRigged")] public bool IsRigged; + + [ViewVariables(VVAccess.ReadWrite)] + [DataField("solution")] + public string Solution = "battery"; + + [ViewVariables(VVAccess.ReadWrite)] + [DataField("reagent")] + public ReagentQuantity RequiredQuantity = new("Plasma", FixedPoint2.New(5), null); } diff --git a/Content.Server/Power/EntitySystems/RiggableSystem.cs b/Content.Server/Power/EntitySystems/RiggableSystem.cs index 9d988f00b9..b35d936ace 100644 --- a/Content.Server/Power/EntitySystems/RiggableSystem.cs +++ b/Content.Server/Power/EntitySystems/RiggableSystem.cs @@ -4,6 +4,7 @@ using Content.Server.Explosion.EntitySystems; using Content.Server.Kitchen.Components; using Content.Server.Power.Components; using Content.Server.Stunnable.Components; +using Content.Shared.Chemistry.Components; using Content.Shared.Database; using Content.Shared.Rejuvenate; @@ -47,27 +48,19 @@ public sealed class RiggableSystem : EntitySystem private void OnSolutionChanged(EntityUid uid, RiggableComponent component, SolutionChangedEvent args) { - if (TryComp(uid, out var battery)) - { - IsRigged(uid, args); - } + if (args.SolutionId != component.Solution) + return; - if (component.IsRigged) + var wasRigged = component.IsRigged; + var quantity = args.Solution.GetReagentQuantity(component.RequiredQuantity.Reagent); + component.IsRigged = quantity >= component.RequiredQuantity.Quantity; + + if (component.IsRigged && !wasRigged) { _adminLogger.Add(LogType.Explosion, LogImpact.Medium, $"{ToPrettyString(uid)} has been rigged up to explode when used."); } } - public void IsRigged(EntityUid uid, SolutionChangedEvent args) - { - if (TryComp(uid, out var riggableComp)) - { - riggableComp.IsRigged = _solutionsSystem.TryGetSolution(uid, RiggableComponent.SolutionName, out var solution) - && solution.TryGetReagent("Plasma", out var plasma) - && plasma >= 5; - } - } - public void Explode(EntityUid uid, BatteryComponent? battery = null, EntityUid? cause = null) { if (!Resolve(uid, ref battery)) diff --git a/Content.Server/Power/Generator/GeneratorSystem.cs b/Content.Server/Power/Generator/GeneratorSystem.cs index e3b678584b..591fd7f300 100644 --- a/Content.Server/Power/Generator/GeneratorSystem.cs +++ b/Content.Server/Power/Generator/GeneratorSystem.cs @@ -81,7 +81,7 @@ public sealed class GeneratorSystem : SharedGeneratorSystem foreach (var reagentQuantity in solution) { - if (reagentQuantity.ReagentId != component.Reagent) + if (reagentQuantity.Reagent.Prototype != component.Reagent) { args.Clogged = true; return; @@ -94,7 +94,7 @@ public sealed class GeneratorSystem : SharedGeneratorSystem if (!_solutionContainer.TryGetSolution(uid, component.Solution, out var solution)) return; - var availableReagent = solution.GetReagentQuantity(component.Reagent).Value; + var availableReagent = solution.GetTotalPrototypeQuantity(component.Reagent).Value; var toRemove = RemoveFractionalFuel( ref component.FractionalReagent, args.FuelUsed, @@ -112,8 +112,8 @@ public sealed class GeneratorSystem : SharedGeneratorSystem if (!_solutionContainer.TryGetSolution(uid, component.Solution, out var solution)) return; - var reagent = component.FractionalReagent * FixedPoint2.Epsilon.Float() - + solution.GetReagentQuantity(component.Reagent).Float(); + var availableReagent = solution.GetTotalPrototypeQuantity(component.Reagent).Float(); + var reagent = component.FractionalReagent * FixedPoint2.Epsilon.Float() + availableReagent; args.Fuel = reagent * component.Multiplier; } diff --git a/Content.Server/Tools/ToolSystem.Welder.cs b/Content.Server/Tools/ToolSystem.Welder.cs index 1af28f70e1..7097bc4dc9 100644 --- a/Content.Server/Tools/ToolSystem.Welder.cs +++ b/Content.Server/Tools/ToolSystem.Welder.cs @@ -58,7 +58,7 @@ namespace Content.Server.Tools || !_solutionContainerSystem.TryGetSolution(uid, welder.FuelSolution, out var fuelSolution, solutionContainer)) return (FixedPoint2.Zero, FixedPoint2.Zero); - return (_solutionContainerSystem.GetReagentQuantity(uid, welder.FuelReagent), fuelSolution.MaxVolume); + return (_solutionContainerSystem.GetTotalPrototypeQuantity(uid, welder.FuelReagent), fuelSolution.MaxVolume); } public bool TryToggleWelder(EntityUid uid, EntityUid? user, @@ -95,7 +95,7 @@ namespace Content.Server.Tools if (!_solutionContainerSystem.TryGetSolution(uid, welder.FuelSolution, out var solution, solutionContainer)) return false; - var fuel = solution.GetReagentQuantity(welder.FuelReagent); + var fuel = solution.GetTotalPrototypeQuantity(welder.FuelReagent); // Not enough fuel to lit welder. if (fuel == FixedPoint2.Zero || fuel < welder.FuelLitCost) @@ -307,7 +307,7 @@ namespace Content.Server.Tools solution.RemoveReagent(welder.FuelReagent, welder.FuelConsumption * _welderTimer); - if (solution.GetReagentQuantity(welder.FuelReagent) <= FixedPoint2.Zero) + if (solution.GetTotalPrototypeQuantity(welder.FuelReagent) <= FixedPoint2.Zero) TryTurnWelderOff(tool, null, welder); _entityManager.Dirty(welder); diff --git a/Content.Shared/Chemistry/Components/Solution.cs b/Content.Shared/Chemistry/Components/Solution.cs index ff53a4bebd..65fdcd62dd 100644 --- a/Content.Shared/Chemistry/Components/Solution.cs +++ b/Content.Shared/Chemistry/Components/Solution.cs @@ -3,10 +3,8 @@ using Content.Shared.FixedPoint; using JetBrains.Annotations; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Utility; using System.Collections; -using System.Diagnostics.CodeAnalysis; using System.Linq; namespace Content.Shared.Chemistry.Components @@ -16,12 +14,12 @@ namespace Content.Shared.Chemistry.Components /// [Serializable, NetSerializable] [DataDefinition] - public sealed partial class Solution : IEnumerable, ISerializationHooks + public sealed partial class Solution : IEnumerable, ISerializationHooks { // This is a list because it is actually faster to add and remove reagents from // a list than a dictionary, though contains-reagent checks are slightly slower, [DataField("reagents")] - public List Contents = new(2); + public List Contents; /// /// The calculated total volume of all reagents in the solution (ex. Total volume of liquid in beaker). @@ -86,14 +84,12 @@ namespace Content.Shared.Chemistry.Components /// /// The total heat capacity of all reagents in the solution. /// - [ViewVariables] - private float _heatCapacity; + [ViewVariables] private float _heatCapacity; /// /// If true, then needs to be recomputed. /// - [ViewVariables] - private bool _heatCapacityDirty = true; + [ViewVariables] private bool _heatCapacityDirty = true; public void UpdateHeatCapacity(IPrototypeManager? protoMan) { @@ -101,9 +97,10 @@ namespace Content.Shared.Chemistry.Components DebugTools.Assert(_heatCapacityDirty); _heatCapacityDirty = false; _heatCapacity = 0; - foreach (var reagent in Contents) + foreach (var (reagent, quantity) in Contents) { - _heatCapacity += (float) reagent.Quantity * protoMan.Index(reagent.ReagentId).SpecificHeat; + _heatCapacity += (float) quantity * + protoMan.Index(reagent.Prototype).SpecificHeat; } } @@ -137,11 +134,11 @@ namespace Content.Shared.Chemistry.Components /// /// Constructs a solution containing 100% of a reagent (ex. A beaker of pure water). /// - /// The prototype ID of the reagent to add. + /// The prototype ID of the reagent to add. /// The quantity in milli-units. - public Solution(string reagentId, FixedPoint2 quantity) : this() + public Solution(string prototype, FixedPoint2 quantity, ReagentData? data = null) : this() { - AddReagent(reagentId, quantity); + AddReagent(new ReagentId(prototype, data), quantity); } public Solution(IEnumerable reagents, bool setMaxVol = true) @@ -173,7 +170,6 @@ namespace Content.Shared.Chemistry.Components return new Solution(this); } - [AssertionMethod] public void ValidateSolution() { @@ -185,8 +181,8 @@ namespace Content.Shared.Chemistry.Components // All reagents have at least some reagent present. DebugTools.Assert(!Contents.Any(x => x.Quantity <= FixedPoint2.Zero)); - // No duplicate reagent iDs - DebugTools.Assert(Contents.Select(x => x.ReagentId).ToHashSet().Count() == Contents.Count); + // No duplicate reagents iDs + DebugTools.Assert(Contents.Select(x => x.Reagent).ToHashSet().Count == Contents.Count); // If it isn't flagged as dirty, check heat capacity is correct. if (!_heatCapacityDirty) @@ -211,33 +207,101 @@ namespace Content.Shared.Chemistry.Components MaxVolume = Volume; } - public bool ContainsReagent(string reagentId) + public bool ContainsPrototype(string prototype) { - foreach (var reagent in Contents) + foreach (var (reagent, _) in Contents) { - if (reagent.ReagentId == reagentId) + if (reagent.Prototype == prototype) return true; } return false; } - public bool TryGetReagent(string reagentId, out FixedPoint2 quantity) + public bool ContainsReagent(ReagentId id) { - foreach (var reagent in Contents) + foreach (var (reagent, _) in Contents) { - if (reagent.ReagentId == reagentId) - { - quantity = reagent.Quantity; + if (reagent == id) return true; - } } - quantity = FixedPoint2.New(0); return false; } - public string? GetPrimaryReagentId() + public bool ContainsReagent(string reagentId, ReagentData? data) + => ContainsReagent(new(reagentId, data)); + + public bool TryGetReagent(ReagentId id, out ReagentQuantity quantity) + { + foreach (var tuple in Contents) + { + if (tuple.Reagent != id) + continue; + + DebugTools.Assert(tuple.Quantity > FixedPoint2.Zero); + quantity = tuple; + return true; + } + + quantity = new ReagentQuantity(id, FixedPoint2.Zero); + return false; + } + + public bool TryGetReagentQuantity(ReagentId id, out FixedPoint2 volume) + { + volume = FixedPoint2.Zero; + if (!TryGetReagent(id, out var quant)) + return false; + + volume = quant.Quantity; + return true; + } + + [Pure] + public ReagentQuantity GetReagent(ReagentId id) + { + TryGetReagent(id, out var quantity); + return quantity; + } + + public ReagentQuantity this[ReagentId id] + { + get + { + if (!TryGetReagent(id, out var quantity)) + throw new KeyNotFoundException(id.ToString()); + return quantity; + } + } + + /// + /// Get the volume/quantity of a single reagent in the solution. + /// + [Pure] + public FixedPoint2 GetReagentQuantity(ReagentId id) + { + return GetReagent(id).Quantity; + } + + /// + /// Gets the total volume of all reagents in the solution with the given prototype Id. + /// If you only want the volume of a single reagent, use + /// + [Pure] + public FixedPoint2 GetTotalPrototypeQuantity(string prototype) + { + var total = FixedPoint2.Zero; + foreach (var (reagent, quantity) in Contents) + { + if (reagent.Prototype == prototype) + total += quantity; + } + + return total; + } + + public ReagentId? GetPrimaryReagentId() { if (Contents.Count == 0) return null; @@ -252,15 +316,23 @@ namespace Content.Shared.Chemistry.Components } } - return max.ReagentId!; + return max.Reagent; } /// /// Adds a given quantity of a reagent directly into the solution. /// - /// The prototype ID of the reagent to add. + /// The prototype ID of the reagent to add. /// The quantity in milli-units. - public void AddReagent(string reagentId, FixedPoint2 quantity, bool dirtyHeatCap = true) + public void AddReagent(string prototype, FixedPoint2 quantity, bool dirtyHeatCap = true) + => AddReagent(new ReagentId(prototype, null), quantity, dirtyHeatCap); + + /// + /// Adds a given quantity of a reagent directly into the solution. + /// + /// The reagent to add. + /// The quantity in milli-units. + public void AddReagent(ReagentId id, FixedPoint2 quantity, bool dirtyHeatCap = true) { if (quantity <= 0) { @@ -272,42 +344,45 @@ namespace Content.Shared.Chemistry.Components _heatCapacityDirty |= dirtyHeatCap; for (var i = 0; i < Contents.Count; i++) { - var reagent = Contents[i]; - if (reagent.ReagentId != reagentId) + var (reagent, existingQuantity) = Contents[i]; + if (reagent != id) continue; - Contents[i] = new ReagentQuantity(reagentId, reagent.Quantity + quantity); + Contents[i] = new ReagentQuantity(id, existingQuantity + quantity); ValidateSolution(); return; } - Contents.Add(new ReagentQuantity(reagentId, quantity)); + Contents.Add(new ReagentQuantity(id, quantity)); ValidateSolution(); } /// /// Adds a given quantity of a reagent directly into the solution. /// - /// The prototype of the reagent to add. + /// The reagent to add. /// The quantity in milli-units. - public void AddReagent(ReagentPrototype proto, FixedPoint2 quantity) + public void AddReagent(ReagentPrototype proto, ReagentId reagentId, FixedPoint2 quantity) { - AddReagent(proto.ID, quantity, false); + AddReagent(reagentId, quantity, false); _heatCapacity += quantity.Float() * proto.SpecificHeat; } + public void AddReagent(ReagentQuantity reagentQuantity) + => AddReagent(reagentQuantity.Reagent, reagentQuantity.Quantity); + /// /// Adds a given quantity of a reagent directly into the solution. /// /// The prototype of the reagent to add. /// The quantity in milli-units. - public void AddReagent(ReagentPrototype proto, FixedPoint2 quantity, float temperature, IPrototypeManager? protoMan) + public void AddReagent(ReagentPrototype proto, FixedPoint2 quantity, float temperature, IPrototypeManager? protoMan, ReagentData? data = null) { if (_heatCapacityDirty) UpdateHeatCapacity(protoMan); var totalThermalEnergy = Temperature * _heatCapacity + temperature * proto.SpecificHeat; - AddReagent(proto, quantity); + AddReagent(new ReagentId(proto.ID, data), quantity); Temperature = _heatCapacity == 0 ? 0 : totalThermalEnergy / _heatCapacity; } @@ -333,7 +408,7 @@ namespace Content.Shared.Chemistry.Components for (int i = 0; i < Contents.Count; i++) { var old = Contents[i]; - Contents[i] = new ReagentQuantity(old.ReagentId, old.Quantity * scale); + Contents[i] = new ReagentQuantity(old.Reagent, old.Quantity * scale); } ValidateSolution(); } @@ -362,7 +437,7 @@ namespace Content.Shared.Chemistry.Components Contents.RemoveSwap(i); else { - Contents[i] = new ReagentQuantity(old.ReagentId, newQuantity); + Contents[i] = new ReagentQuantity(old.Reagent, newQuantity); Volume += newQuantity; } } @@ -371,43 +446,24 @@ namespace Content.Shared.Chemistry.Components ValidateSolution(); } - /// - /// Returns the amount of a single reagent inside the solution. - /// - /// The prototype ID of the reagent to add. - /// The quantity in milli-units. - public FixedPoint2 GetReagentQuantity(string reagentId) - { - for (var i = 0; i < Contents.Count; i++) - { - var reagent = Contents[i]; - if (reagent.ReagentId == reagentId) - return reagent.Quantity; - } - - return FixedPoint2.Zero; - } - /// /// Attempts to remove an amount of reagent from the solution. /// - /// The reagent to be removed. - /// The amount of reagent to remove. + /// The reagent to be removed. /// How much reagent was actually removed. Zero if the reagent is not present on the solution. - public FixedPoint2 RemoveReagent(string reagentId, FixedPoint2 quantity) + public FixedPoint2 RemoveReagent(ReagentQuantity toRemove) { - if (quantity <= FixedPoint2.Zero) + if (toRemove.Quantity <= FixedPoint2.Zero) return FixedPoint2.Zero; for (var i = 0; i < Contents.Count; i++) { - var reagent = Contents[i]; + var (reagent, curQuantity) = Contents[i]; - if(reagent.ReagentId != reagentId) + if(reagent != toRemove.Reagent) continue; - var curQuantity = reagent.Quantity; - var newQuantity = curQuantity - quantity; + var newQuantity = curQuantity - toRemove.Quantity; _heatCapacityDirty = true; if (newQuantity <= 0) @@ -418,16 +474,38 @@ namespace Content.Shared.Chemistry.Components return curQuantity; } - Contents[i] = new ReagentQuantity(reagentId, newQuantity); - Volume -= quantity; + Contents[i] = new ReagentQuantity(reagent, newQuantity); + Volume -= toRemove.Quantity; ValidateSolution(); - return quantity; + return toRemove.Quantity; } // Reagent is not on the solution... return FixedPoint2.Zero; } + /// + /// Attempts to remove an amount of reagent from the solution. + /// + /// The prototype of the reagent to be removed. + /// The amount of reagent to remove. + /// How much reagent was actually removed. Zero if the reagent is not present on the solution. + public FixedPoint2 RemoveReagent(string prototype, FixedPoint2 quantity, ReagentData? data = null) + { + return RemoveReagent(new ReagentQuantity(prototype, quantity, data)); + } + + /// + /// Attempts to remove an amount of reagent from the solution. + /// + /// The reagent to be removed. + /// The amount of reagent to remove. + /// How much reagent was actually removed. Zero if the reagent is not present on the solution. + public FixedPoint2 RemoveReagent(ReagentId reagentId, FixedPoint2 quantity) + { + return RemoveReagent(new ReagentQuantity(reagentId, quantity)); + } + public void RemoveAllSolution() { Contents.Clear(); @@ -437,21 +515,33 @@ namespace Content.Shared.Chemistry.Components } /// - /// Splits a solution without the specified reagent. + /// Splits a solution without the specified reagent prototypes. /// - public Solution SplitSolutionWithout(FixedPoint2 toTake, params string[] without) + public Solution SplitSolutionWithout(FixedPoint2 toTake, params string[] excludedPrototypes) { - var existing = new FixedPoint2[without.Length]; - for (var i = 0; i < without.Length; i++) + // First remove the blacklisted prototypes + List excluded = new(); + foreach (var id in excludedPrototypes) { - TryGetReagent(without[i], out existing[i]); - RemoveReagent(without[i], existing[i]); + foreach (var tuple in Contents) + { + if (tuple.Reagent.Prototype != id) + continue; + + excluded.Add(tuple); + RemoveReagent(tuple); + break; + } } + // Then split the solution var sol = SplitSolution(toTake); - for (var i = 0; i < without.Length; i++) - AddReagent(without[i], existing[i]); + // Then re-add the excluded reagents to the original solution. + foreach (var reagent in excluded) + { + AddReagent(reagent); + } return sol; } @@ -477,32 +567,32 @@ namespace Content.Shared.Chemistry.Components for (var i = Contents.Count - 1; i >= 0; i--) // iterate backwards because of remove swap. { - var reagent = Contents[i]; + var (reagent, quantity) = Contents[i]; // This is set up such that integer rounding will tend to take more reagents. - var split = remaining * reagent.Quantity.Value / effVol; + var split = remaining * quantity.Value / effVol; if (split <= 0) { - effVol -= reagent.Quantity.Value; + effVol -= quantity.Value; DebugTools.Assert(split == 0, "Negative solution quantity while splitting? Long/int overflow?"); continue; } var splitQuantity = FixedPoint2.FromCents((int) split); - var newQuantity = reagent.Quantity - splitQuantity; + var newQuantity = quantity - splitQuantity; DebugTools.Assert(newQuantity >= 0); if (newQuantity > FixedPoint2.Zero) - Contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity); + Contents[i] = new ReagentQuantity(reagent, newQuantity); else Contents.RemoveSwap(i); - newSolution.Contents.Add(new ReagentQuantity(reagent.ReagentId, splitQuantity)); + newSolution.Contents.Add(new ReagentQuantity(reagent, splitQuantity)); Volume -= splitQuantity; remaining -= split; - effVol -= reagent.Quantity.Value; + effVol -= quantity.Value; } newSolution.Volume = origVol - Volume; @@ -539,28 +629,28 @@ namespace Content.Shared.Chemistry.Components var remaining = (long) toTake.Value; for (var i = Contents.Count - 1; i >= 0; i--)// iterate backwards because of remove swap. { - var reagent = Contents[i]; + var (reagent, quantity) = Contents[i]; // This is set up such that integer rounding will tend to take more reagents. - var split = remaining * reagent.Quantity.Value / effVol; + var split = remaining * quantity.Value / effVol; if (split <= 0) { - effVol -= reagent.Quantity.Value; + effVol -= quantity.Value; DebugTools.Assert(split == 0, "Negative solution quantity while splitting? Long/int overflow?"); continue; } var splitQuantity = FixedPoint2.FromCents((int) split); - var newQuantity = reagent.Quantity - splitQuantity; + var newQuantity = quantity - splitQuantity; if (newQuantity > FixedPoint2.Zero) - Contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity); + Contents[i] = new ReagentQuantity(reagent, newQuantity); else Contents.RemoveSwap(i); remaining -= split; - effVol -= reagent.Quantity.Value; + effVol -= quantity.Value; } DebugTools.Assert(remaining >= 0); @@ -594,23 +684,23 @@ namespace Content.Shared.Chemistry.Components for (var i = 0; i < otherSolution.Contents.Count; i++) { - var otherReagent = otherSolution.Contents[i]; + var (otherReagent, otherQuantity) = otherSolution.Contents[i]; var found = false; for (var j = 0; j < Contents.Count; j++) { - var reagent = Contents[j]; - if (reagent.ReagentId == otherReagent.ReagentId) + var (reagent, quantity) = Contents[j]; + if (reagent == otherReagent) { found = true; - Contents[j] = new ReagentQuantity(reagent.ReagentId, reagent.Quantity + otherReagent.Quantity); + Contents[j] = new ReagentQuantity(reagent, quantity + otherQuantity); break; } } if (!found) { - Contents.Add(new ReagentQuantity(otherReagent.ReagentId, otherReagent.Quantity)); + Contents.Add(new ReagentQuantity(otherReagent, otherQuantity)); } } @@ -636,14 +726,14 @@ namespace Content.Shared.Chemistry.Components var runningTotalQuantity = FixedPoint2.New(0); bool first = true; - foreach (var reagent in Contents) + foreach (var (reagent, quantity) in Contents) { - if (without.Contains(reagent.ReagentId)) + if (without.Contains(reagent.Prototype)) continue; - runningTotalQuantity += reagent.Quantity; + runningTotalQuantity += quantity; - if (!protoMan.TryIndex(reagent.ReagentId, out ReagentPrototype? proto)) + if (!protoMan.TryIndex(reagent.Prototype, out ReagentPrototype? proto)) { continue; } @@ -655,7 +745,7 @@ namespace Content.Shared.Chemistry.Components continue; } - var interpolateValue = reagent.Quantity.Float() / runningTotalQuantity.Float(); + var interpolateValue = quantity.Float() / runningTotalQuantity.Float(); mixColor = Color.InterpolateBetween(mixColor, proto.SubstanceColor, interpolateValue); } return mixColor; @@ -666,46 +756,6 @@ namespace Content.Shared.Chemistry.Components return GetColorWithout(protoMan); } - [Obsolete("Use ReactiveSystem.DoEntityReaction")] - public void DoEntityReaction(EntityUid uid, ReactionMethod method) - { - IoCManager.Resolve().GetEntitySystem().DoEntityReaction(uid, this, method); - } - - [Serializable, NetSerializable] - [DataDefinition] - public readonly partial struct ReagentQuantity: IComparable - { - [DataField("ReagentId", customTypeSerializer:typeof(PrototypeIdSerializer), required:true)] - public string ReagentId { get; init; } - [DataField("Quantity", required:true)] - public FixedPoint2 Quantity { get; init; } - - public ReagentQuantity(string reagentId, FixedPoint2 quantity) - { - ReagentId = reagentId; - Quantity = quantity; - } - - public ReagentQuantity() : this(string.Empty, default) - { - } - - [ExcludeFromCodeCoverage] - public override string ToString() - { - return $"{ReagentId}:{Quantity}"; - } - - public int CompareTo(ReagentQuantity other) { return Quantity.Float().CompareTo(other.Quantity.Float()); } - - public void Deconstruct(out string reagentId, out FixedPoint2 quantity) - { - reagentId = ReagentId; - quantity = Quantity; - } - } - #region Enumeration public IEnumerator GetEnumerator() @@ -721,6 +771,7 @@ namespace Content.Shared.Chemistry.Components public void SetContents(IEnumerable reagents, bool setMaxVol = false) { + Volume = 0; RemoveAllSolution(); _heatCapacityDirty = true; Contents = new(reagents); @@ -735,5 +786,15 @@ namespace Content.Shared.Chemistry.Components ValidateSolution(); } + public Dictionary GetReagentPrototypes(IPrototypeManager protoMan) + { + var dict = new Dictionary(Contents.Count); + foreach (var (reagent, quantity) in Contents) + { + var proto = protoMan.Index(reagent.Prototype); + dict[proto] = quantity + dict.GetValueOrDefault(proto); + } + return dict; + } } } diff --git a/Content.Shared/Chemistry/Dispenser/ReagentDispenserInventoryPrototype.cs b/Content.Shared/Chemistry/Dispenser/ReagentDispenserInventoryPrototype.cs index c308e46da5..8457816838 100644 --- a/Content.Shared/Chemistry/Dispenser/ReagentDispenserInventoryPrototype.cs +++ b/Content.Shared/Chemistry/Dispenser/ReagentDispenserInventoryPrototype.cs @@ -14,13 +14,11 @@ namespace Content.Shared.Chemistry.Dispenser [Serializable, NetSerializable, Prototype("reagentDispenserInventory")] public sealed class ReagentDispenserInventoryPrototype : IPrototype { + // TODO use ReagentId [DataField("inventory", customTypeSerializer: typeof(PrototypeIdListSerializer))] - private List _inventory = new(); + public List Inventory = new(); - [ViewVariables] - [IdDataField] - public string ID { get; private set; } = default!; - - public List Inventory => _inventory; + [ViewVariables, IdDataField] + public string ID { get; } = default!; } } diff --git a/Content.Shared/Chemistry/Reaction/ChemicalReactionSystem.cs b/Content.Shared/Chemistry/Reaction/ChemicalReactionSystem.cs index da8560fc5b..4ec50bc61a 100644 --- a/Content.Shared/Chemistry/Reaction/ChemicalReactionSystem.cs +++ b/Content.Shared/Chemistry/Reaction/ChemicalReactionSystem.cs @@ -136,7 +136,9 @@ namespace Content.Shared.Chemistry.Reaction var reactantName = reactantData.Key; var reactantCoefficient = reactantData.Value.Amount; - if (!solution.TryGetReagent(reactantName, out var reactantQuantity)) + var reactantQuantity = solution.GetTotalPrototypeQuantity(reactantName); + + if (reactantQuantity <= FixedPoint2.Zero) return false; if (reactantData.Value.Catalyst) @@ -292,7 +294,7 @@ namespace Content.Shared.Chemistry.Reaction SortedSet reactions = new(); foreach (var reactant in solution.Contents) { - if (_reactions.TryGetValue(reactant.ReagentId, out var reactantReactions)) + if (_reactions.TryGetValue(reactant.Reagent.Prototype, out var reactantReactions)) reactions.UnionWith(reactantReactions); } diff --git a/Content.Shared/Chemistry/ReactiveSystem.cs b/Content.Shared/Chemistry/ReactiveSystem.cs index ca372e940b..4881a6bf96 100644 --- a/Content.Shared/Chemistry/ReactiveSystem.cs +++ b/Content.Shared/Chemistry/ReactiveSystem.cs @@ -19,32 +19,33 @@ namespace Content.Shared.Chemistry public void DoEntityReaction(EntityUid uid, Solution solution, ReactionMethod method) { - foreach (var (reagentId, quantity) in solution.Contents.ToArray()) + foreach (var reagent in solution.Contents.ToArray()) { - ReactionEntity(uid, method, reagentId, quantity, solution); + ReactionEntity(uid, method, reagent, solution); } } - public void ReactionEntity(EntityUid uid, ReactionMethod method, string reagentId, FixedPoint2 reactVolume, Solution? source) + public void ReactionEntity(EntityUid uid, ReactionMethod method, ReagentQuantity reagentQuantity, Solution? source) { // We throw if the reagent specified doesn't exist. - ReactionEntity(uid, method, _prototypeManager.Index(reagentId), reactVolume, source); + var proto = _prototypeManager.Index(reagentQuantity.Reagent.Prototype); + ReactionEntity(uid, method, proto, reagentQuantity, source); } - public void ReactionEntity(EntityUid uid, ReactionMethod method, ReagentPrototype reagent, - FixedPoint2 reactVolume, Solution? source) + public void ReactionEntity(EntityUid uid, ReactionMethod method, ReagentPrototype proto, + ReagentQuantity reagentQuantity, Solution? source) { - if (!EntityManager.TryGetComponent(uid, out ReactiveComponent? reactive)) + if (!TryComp(uid, out ReactiveComponent? reactive)) return; // If we have a source solution, use the reagent quantity we have left. Otherwise, use the reaction volume specified. - var args = new ReagentEffectArgs(uid, null, source, reagent, - source?.GetReagentQuantity(reagent.ID) ?? reactVolume, EntityManager, method, 1f); + var args = new ReagentEffectArgs(uid, null, source, proto, + source?.GetReagentQuantity(reagentQuantity.Reagent) ?? reagentQuantity.Quantity, EntityManager, method, 1f); // First, check if the reagent wants to apply any effects. - if (reagent.ReactiveEffects != null && reactive.ReactiveGroups != null) + if (proto.ReactiveEffects != null && reactive.ReactiveGroups != null) { - foreach (var (key, val) in reagent.ReactiveEffects) + foreach (var (key, val) in proto.ReactiveEffects) { if (!val.Methods.Contains(method)) continue; @@ -64,7 +65,7 @@ namespace Content.Shared.Chemistry { var entity = args.SolutionEntity; _adminLogger.Add(LogType.ReagentEffect, effect.LogImpact, - $"Reactive effect {effect.GetType().Name:effect} of reagent {reagent.ID:reagent} with method {method} applied on entity {ToPrettyString(entity):entity} at {Transform(entity).Coordinates:coordinates}"); + $"Reactive effect {effect.GetType().Name:effect} of reagent {proto.ID:reagent} with method {method} applied on entity {ToPrettyString(entity):entity} at {Transform(entity).Coordinates:coordinates}"); } effect.Effect(args); @@ -80,7 +81,7 @@ namespace Content.Shared.Chemistry if (!entry.Methods.Contains(method)) continue; - if (entry.Reagents != null && !entry.Reagents.Contains(reagent.ID)) + if (entry.Reagents != null && !entry.Reagents.Contains(proto.ID)) continue; foreach (var effect in entry.Effects) @@ -92,7 +93,7 @@ namespace Content.Shared.Chemistry { var entity = args.SolutionEntity; _adminLogger.Add(LogType.ReagentEffect, effect.LogImpact, - $"Reactive effect {effect.GetType().Name:effect} of {ToPrettyString(entity):entity} using reagent {reagent.ID:reagent} with method {method} at {Transform(entity).Coordinates:coordinates}"); + $"Reactive effect {effect.GetType().Name:effect} of {ToPrettyString(entity):entity} using reagent {proto.ID:reagent} with method {method} at {Transform(entity).Coordinates:coordinates}"); } effect.Effect(args); diff --git a/Content.Shared/Chemistry/Reagent/ReagentData.cs b/Content.Shared/Chemistry/Reagent/ReagentData.cs new file mode 100644 index 0000000000..a4a77f552e --- /dev/null +++ b/Content.Shared/Chemistry/Reagent/ReagentData.cs @@ -0,0 +1,44 @@ +using Content.Shared.FixedPoint; +using Robust.Shared.Serialization; + +namespace Content.Shared.Chemistry.Reagent; + +[ImplicitDataDefinitionForInheritors, Serializable, NetSerializable] +public abstract partial class ReagentData : IEquatable +{ + /// + /// Convert to a string representation. This if for logging & debugging. This is not localized and should not be + /// shown to players. + /// + public virtual string ToString(string prototype, FixedPoint2 quantity) + { + return $"{prototype}:{GetType().Name}:{quantity}"; + } + + /// + /// Convert to a string representation. This if for logging & debugging. This is not localized and should not be + /// shown to players. + /// + public virtual string ToString(string prototype) + { + return $"{prototype}:{GetType().Name}"; + } + + public abstract bool Equals(ReagentData? other); + + public override bool Equals(object? obj) + { + if (ReferenceEquals(null, obj)) + return false; + if (ReferenceEquals(this, obj)) + return true; + if (obj.GetType() != GetType()) + return false; + + return Equals((ReagentData) obj); + } + + public abstract override int GetHashCode(); + + public abstract ReagentData Clone(); +} diff --git a/Content.Shared/Chemistry/Reagent/ReagentId.cs b/Content.Shared/Chemistry/Reagent/ReagentId.cs new file mode 100644 index 0000000000..07a4200219 --- /dev/null +++ b/Content.Shared/Chemistry/Reagent/ReagentId.cs @@ -0,0 +1,93 @@ +using Content.Shared.FixedPoint; +using Robust.Shared.Serialization; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Shared.Chemistry.Reagent; + +/// +/// Struct used to uniquely identify a reagent. This is usually just a ReagentPrototype id string, however some reagents +/// contain additional data (e.g., blood could store DNA data). +/// +[Serializable, NetSerializable] +[DataDefinition] +public partial struct ReagentId : IEquatable +{ + // TODO rename data field. + [DataField("ReagentId", customTypeSerializer: typeof(PrototypeIdSerializer), required: true)] + public string Prototype { get; private set; } + + /// + /// Any additional data that is unique to this reagent type. E.g., for blood this could be DNA data. + /// + [DataField("data")] + public ReagentData? Data { get; private set; } + + public ReagentId(string prototype, ReagentData? data) + { + Prototype = prototype; + Data = data; + } + + public ReagentId() + { + Prototype = default!; + } + + public bool Equals(ReagentId other) + { + if (Prototype != other.Prototype) + return false; + + if (Data == null) + return other.Data == null; + + if (other.Data == null) + return false; + + if (Data.GetType() != other.Data.GetType()) + return false; + + return Data.Equals(other.Data); + } + + public bool Equals(string prototype, ReagentData? otherData = null) + { + if (Prototype != prototype) + return false; + + if (Data == null) + return otherData == null; + + return Data.Equals(otherData); + } + + public override bool Equals(object? obj) + { + return obj is ReagentId other && Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(Prototype, Data); + } + + public string ToString(FixedPoint2 quantity) + { + return Data?.ToString(Prototype, quantity) ?? $"{Prototype}:{quantity}"; + } + + public override string ToString() + { + return Data?.ToString(Prototype) ?? Prototype; + } + + public static bool operator ==(ReagentId left, ReagentId right) + { + return left.Equals(right); + } + + public static bool operator !=(ReagentId left, ReagentId right) + { + return !(left == right); + } +} diff --git a/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs b/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs index 8d1af84edb..0f24a36339 100644 --- a/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs +++ b/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs @@ -141,7 +141,7 @@ namespace Content.Shared.Chemistry.Reagent return removed; } - public void ReactionPlant(EntityUid? plantHolder, Solution.ReagentQuantity amount, Solution solution) + public void ReactionPlant(EntityUid? plantHolder, ReagentQuantity amount, Solution solution) { if (plantHolder == null) return; diff --git a/Content.Shared/Chemistry/Reagent/ReagentQuantity.cs b/Content.Shared/Chemistry/Reagent/ReagentQuantity.cs new file mode 100644 index 0000000000..9644f919f7 --- /dev/null +++ b/Content.Shared/Chemistry/Reagent/ReagentQuantity.cs @@ -0,0 +1,77 @@ +using Content.Shared.FixedPoint; +using Robust.Shared.Serialization; + +namespace Content.Shared.Chemistry.Reagent; + +/// +/// Simple struct for storing a & quantity tuple. +/// +[Serializable, NetSerializable] +[DataDefinition] +public partial struct ReagentQuantity : IEquatable +{ + [DataField("Quantity", required:true)] + public FixedPoint2 Quantity { get; private set; } + + [IncludeDataField] + [ViewVariables] + public ReagentId Reagent { get; private set; } + + public ReagentQuantity(string reagentId, FixedPoint2 quantity, ReagentData? data) + : this(new ReagentId(reagentId, data), quantity) + { + } + + public ReagentQuantity(ReagentId reagent, FixedPoint2 quantity) + { + Reagent = reagent; + Quantity = quantity; + } + + public ReagentQuantity() : this(default, default) + { + } + + public override string ToString() + { + return Reagent.ToString(Quantity); + } + + public void Deconstruct(out string prototype, out FixedPoint2 quantity, out ReagentData? data) + { + prototype = Reagent.Prototype; + quantity = Quantity; + data = Reagent.Data; + } + + public void Deconstruct(out ReagentId id, out FixedPoint2 quantity) + { + id = Reagent; + quantity = Quantity; + } + + public bool Equals(ReagentQuantity other) + { + return Quantity != other.Quantity && Reagent.Equals(other.Reagent); + } + + public override bool Equals(object? obj) + { + return obj is ReagentQuantity other && Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(Reagent.GetHashCode(), Quantity); + } + + public static bool operator ==(ReagentQuantity left, ReagentQuantity right) + { + return left.Equals(right); + } + + public static bool operator !=(ReagentQuantity left, ReagentQuantity right) + { + return !(left == right); + } +} diff --git a/Content.Shared/Chemistry/SharedChemMaster.cs b/Content.Shared/Chemistry/SharedChemMaster.cs index 0e20e91a51..027002098e 100644 --- a/Content.Shared/Chemistry/SharedChemMaster.cs +++ b/Content.Shared/Chemistry/SharedChemMaster.cs @@ -1,4 +1,5 @@ using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Reagent; using Content.Shared.FixedPoint; using Robust.Shared.Serialization; @@ -43,11 +44,11 @@ namespace Content.Shared.Chemistry [Serializable, NetSerializable] public sealed class ChemMasterReagentAmountButtonMessage : BoundUserInterfaceMessage { - public readonly string ReagentId; + public readonly ReagentId ReagentId; public readonly ChemMasterReagentAmount Amount; public readonly bool FromBuffer; - public ChemMasterReagentAmountButtonMessage(string reagentId, ChemMasterReagentAmount amount, bool fromBuffer) + public ChemMasterReagentAmountButtonMessage(ReagentId reagentId, ChemMasterReagentAmount amount, bool fromBuffer) { ReagentId = reagentId; Amount = amount; @@ -121,34 +122,29 @@ namespace Content.Shared.Chemistry /// The container name to show to the player /// public readonly string DisplayName; - /// - /// Whether the container holds reagents or entities - /// - public readonly bool HoldsReagents; + /// /// The currently used volume of the container /// public readonly FixedPoint2 CurrentVolume; + /// /// The maximum volume of the container /// public readonly FixedPoint2 MaxVolume; - /// - /// A list of the reagents/entities and their sizes within the container - /// - // todo: this causes NetSerializer exceptions if it's an IReadOnlyList (which would be preferred) - public readonly List<(string Id, FixedPoint2 Quantity)> Contents; - public ContainerInfo( - string displayName, bool holdsReagents, - FixedPoint2 currentVolume, FixedPoint2 maxVolume, - List<(string, FixedPoint2)> contents) + /// + /// A list of the entities and their sizes within the container + /// + public List<(string Id, FixedPoint2 Quantity)>? Entities { get; init; } + + public List? Reagents { get; init; } + + public ContainerInfo(string displayName, FixedPoint2 currentVolume, FixedPoint2 maxVolume) { DisplayName = displayName; - HoldsReagents = holdsReagents; CurrentVolume = currentVolume; MaxVolume = maxVolume; - Contents = contents; } } @@ -161,7 +157,7 @@ namespace Content.Shared.Chemistry /// /// A list of the reagents and their amounts within the buffer, if applicable. /// - public readonly IReadOnlyList BufferReagents; + public readonly IReadOnlyList BufferReagents; public readonly ChemMasterMode Mode; @@ -174,7 +170,7 @@ namespace Content.Shared.Chemistry public ChemMasterBoundUserInterfaceState( ChemMasterMode mode, ContainerInfo? inputContainerInfo, ContainerInfo? outputContainerInfo, - IReadOnlyList bufferReagents, FixedPoint2 bufferCurrentVolume, + IReadOnlyList bufferReagents, FixedPoint2 bufferCurrentVolume, uint selectedPillType, uint pillDosageLimit, bool updateLabel) { InputContainerInfo = inputContainerInfo; diff --git a/Content.Shared/Chemistry/SharedReagentDispenser.cs b/Content.Shared/Chemistry/SharedReagentDispenser.cs index 3dd9a18f42..1ecb0993f5 100644 --- a/Content.Shared/Chemistry/SharedReagentDispenser.cs +++ b/Content.Shared/Chemistry/SharedReagentDispenser.cs @@ -1,3 +1,4 @@ +using Content.Shared.Chemistry.Reagent; using Robust.Shared.Serialization; namespace Content.Shared.Chemistry @@ -24,9 +25,9 @@ namespace Content.Shared.Chemistry [Serializable, NetSerializable] public sealed class ReagentDispenserDispenseReagentMessage : BoundUserInterfaceMessage { - public readonly string ReagentId; + public readonly ReagentId ReagentId; - public ReagentDispenserDispenseReagentMessage(string reagentId) + public ReagentDispenserDispenseReagentMessage(ReagentId reagentId) { ReagentId = reagentId; } @@ -58,11 +59,11 @@ namespace Content.Shared.Chemistry /// /// A list of the reagents which this dispenser can dispense. /// - public readonly List Inventory; + public readonly List Inventory; public readonly ReagentDispenserDispenseAmount SelectedDispenseAmount; - public ReagentDispenserBoundUserInterfaceState(ContainerInfo? outputContainer, List inventory, ReagentDispenserDispenseAmount selectedDispenseAmount) + public ReagentDispenserBoundUserInterfaceState(ContainerInfo? outputContainer, List inventory, ReagentDispenserDispenseAmount selectedDispenseAmount) { OutputContainer = outputContainer; Inventory = inventory; diff --git a/Content.Shared/Kitchen/Components/SharedMicrowave.cs b/Content.Shared/Kitchen/Components/SharedMicrowave.cs index c625987ae7..06ac79af98 100644 --- a/Content.Shared/Kitchen/Components/SharedMicrowave.cs +++ b/Content.Shared/Kitchen/Components/SharedMicrowave.cs @@ -1,4 +1,5 @@ using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Reagent; using Robust.Shared.Serialization; namespace Content.Shared.Kitchen.Components @@ -27,8 +28,8 @@ namespace Content.Shared.Kitchen.Components [Serializable, NetSerializable] public sealed class MicrowaveVaporizeReagentIndexedMessage : BoundUserInterfaceMessage { - public Solution.ReagentQuantity ReagentQuantity; - public MicrowaveVaporizeReagentIndexedMessage(Solution.ReagentQuantity reagentQuantity) + public ReagentQuantity ReagentQuantity; + public MicrowaveVaporizeReagentIndexedMessage(ReagentQuantity reagentQuantity) { ReagentQuantity = reagentQuantity; } diff --git a/Content.Shared/Kitchen/MicrowaveMealRecipePrototype.cs b/Content.Shared/Kitchen/MicrowaveMealRecipePrototype.cs index 898d911141..3ee8e8ee4a 100644 --- a/Content.Shared/Kitchen/MicrowaveMealRecipePrototype.cs +++ b/Content.Shared/Kitchen/MicrowaveMealRecipePrototype.cs @@ -33,6 +33,7 @@ namespace Content.Shared.Kitchen public string Name => Loc.GetString(_name); + // TODO Turn this into a ReagentQuantity[] public IReadOnlyDictionary IngredientsReagents => _ingsReagents; public IReadOnlyDictionary IngredientsSolids => _ingsSolids; diff --git a/Content.Shared/Kitchen/SharedReagentGrinder.cs b/Content.Shared/Kitchen/SharedReagentGrinder.cs index a9a3d88bb8..97c13814b0 100644 --- a/Content.Shared/Kitchen/SharedReagentGrinder.cs +++ b/Content.Shared/Kitchen/SharedReagentGrinder.cs @@ -1,4 +1,5 @@ using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Reagent; using Robust.Shared.Serialization; namespace Content.Shared.Kitchen @@ -84,8 +85,8 @@ namespace Content.Shared.Kitchen public bool CanJuice; public bool CanGrind; public EntityUid[] ChamberContents; - public Solution.ReagentQuantity[]? ReagentQuantities; - public ReagentGrinderInterfaceState(bool isBusy, bool hasBeaker, bool powered, bool canJuice, bool canGrind, EntityUid[] chamberContents, Solution.ReagentQuantity[]? heldBeakerContents) + public ReagentQuantity[]? ReagentQuantities; + public ReagentGrinderInterfaceState(bool isBusy, bool hasBeaker, bool powered, bool canJuice, bool canGrind, EntityUid[] chamberContents, ReagentQuantity[]? heldBeakerContents) { IsBusy = isBusy; HasBeakerIn = hasBeaker; diff --git a/Content.Shared/Materials/PhysicalCompositionComponent.cs b/Content.Shared/Materials/PhysicalCompositionComponent.cs index 97ec3d7b65..4066bb9f7c 100644 --- a/Content.Shared/Materials/PhysicalCompositionComponent.cs +++ b/Content.Shared/Materials/PhysicalCompositionComponent.cs @@ -27,4 +27,5 @@ public sealed partial class PhysicalCompositionComponent : Component /// [DataField("chemicalComposition", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] public Dictionary ChemicalComposition = new(); + // TODO use ReagentQuantity[] } diff --git a/Content.Tests/Shared/Chemistry/Solution_Tests.cs b/Content.Tests/Shared/Chemistry/SolutionTests.cs similarity index 75% rename from Content.Tests/Shared/Chemistry/Solution_Tests.cs rename to Content.Tests/Shared/Chemistry/SolutionTests.cs index 3819876a44..0fa23d2491 100644 --- a/Content.Tests/Shared/Chemistry/Solution_Tests.cs +++ b/Content.Tests/Shared/Chemistry/SolutionTests.cs @@ -8,7 +8,7 @@ using Robust.Shared.Utility; namespace Content.Tests.Shared.Chemistry; [TestFixture, Parallelizable, TestOf(typeof(Solution))] -public sealed class Solution_Tests : ContentUnitTest +public sealed class SolutionTests : ContentUnitTest { [OneTimeSetUp] public void Setup() @@ -21,7 +21,7 @@ public sealed class Solution_Tests : ContentUnitTest { var solution = new Solution(); solution.AddReagent("water", FixedPoint2.New(1000)); - var quantity = solution.GetReagentQuantity("water"); + var quantity = solution.GetTotalPrototypeQuantity("water"); Assert.That(quantity.Int(), Is.EqualTo(1000)); } @@ -44,8 +44,8 @@ public sealed class Solution_Tests : ContentUnitTest tmp.ScaleSolution(2); Assert.That(tmp.Contents.Count, Is.EqualTo(2)); Assert.That(tmp.Volume, Is.EqualTo(FixedPoint2.New(100))); - Assert.That(tmp.GetReagentQuantity("water"), Is.EqualTo(FixedPoint2.New(40))); - Assert.That(tmp.GetReagentQuantity("fire"), Is.EqualTo(FixedPoint2.New(60))); + Assert.That(tmp.GetTotalPrototypeQuantity("water"), Is.EqualTo(FixedPoint2.New(40))); + Assert.That(tmp.GetTotalPrototypeQuantity("fire"), Is.EqualTo(FixedPoint2.New(60))); } // Test float scaling @@ -59,15 +59,15 @@ public sealed class Solution_Tests : ContentUnitTest tmp.ScaleSolution(2f); Assert.That(tmp.Contents.Count, Is.EqualTo(2)); Assert.That(tmp.Volume, Is.EqualTo(FixedPoint2.New(100))); - Assert.That(tmp.GetReagentQuantity("water"), Is.EqualTo(FixedPoint2.New(40))); - Assert.That(tmp.GetReagentQuantity("fire"), Is.EqualTo(FixedPoint2.New(60))); + Assert.That(tmp.GetTotalPrototypeQuantity("water"), Is.EqualTo(FixedPoint2.New(40))); + Assert.That(tmp.GetTotalPrototypeQuantity("fire"), Is.EqualTo(FixedPoint2.New(60))); tmp = solution.Clone(); tmp.ScaleSolution(0.3f); Assert.That(tmp.Contents.Count, Is.EqualTo(2)); Assert.That(tmp.Volume, Is.EqualTo(FixedPoint2.New(15))); - Assert.That(tmp.GetReagentQuantity("water"), Is.EqualTo(FixedPoint2.New(6))); - Assert.That(tmp.GetReagentQuantity("fire"), Is.EqualTo(FixedPoint2.New(9))); + Assert.That(tmp.GetTotalPrototypeQuantity("water"), Is.EqualTo(FixedPoint2.New(6))); + Assert.That(tmp.GetTotalPrototypeQuantity("fire"), Is.EqualTo(FixedPoint2.New(9))); } } @@ -75,7 +75,7 @@ public sealed class Solution_Tests : ContentUnitTest public void ConstructorAddReagent() { var solution = new Solution("water", FixedPoint2.New(1000)); - var quantity = solution.GetReagentQuantity("water"); + var quantity = solution.GetTotalPrototypeQuantity("water"); Assert.That(quantity.Int(), Is.EqualTo(1000)); } @@ -84,7 +84,7 @@ public sealed class Solution_Tests : ContentUnitTest public void NonExistingReagentReturnsZero() { var solution = new Solution(); - var quantity = solution.GetReagentQuantity("water"); + var quantity = solution.GetTotalPrototypeQuantity("water"); Assert.That(quantity.Int(), Is.EqualTo(0)); } @@ -94,7 +94,7 @@ public sealed class Solution_Tests : ContentUnitTest public void AddLessThanZeroReagentReturnsZero() { var solution = new Solution("water", FixedPoint2.New(-1000)); - var quantity = solution.GetReagentQuantity("water"); + var quantity = solution.GetTotalPrototypeQuantity("water"); Assert.That(quantity.Int(), Is.EqualTo(0)); } @@ -106,7 +106,7 @@ public sealed class Solution_Tests : ContentUnitTest var solution = new Solution(); solution.AddReagent("water", FixedPoint2.New(1000)); solution.AddReagent("water", FixedPoint2.New(2000)); - var quantity = solution.GetReagentQuantity("water"); + var quantity = solution.GetTotalPrototypeQuantity("water"); Assert.That(quantity.Int(), Is.EqualTo(3000)); } @@ -118,8 +118,8 @@ public sealed class Solution_Tests : ContentUnitTest solution.AddReagent("water", FixedPoint2.New(1000)); solution.AddReagent("fire", FixedPoint2.New(2000)); - Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(1000)); - Assert.That(solution.GetReagentQuantity("fire").Int(), Is.EqualTo(2000)); + Assert.That(solution.GetTotalPrototypeQuantity("water").Int(), Is.EqualTo(1000)); + Assert.That(solution.GetTotalPrototypeQuantity("fire").Int(), Is.EqualTo(2000)); } [Test] @@ -141,8 +141,8 @@ public sealed class Solution_Tests : ContentUnitTest var newSolution = solution.Clone(); - Assert.That(newSolution.GetReagentQuantity("water").Int(), Is.EqualTo(1000)); - Assert.That(newSolution.GetReagentQuantity("fire").Int(), Is.EqualTo(2000)); + Assert.That(newSolution.GetTotalPrototypeQuantity("water").Int(), Is.EqualTo(1000)); + Assert.That(newSolution.GetTotalPrototypeQuantity("fire").Int(), Is.EqualTo(2000)); Assert.That(newSolution.Volume.Int(), Is.EqualTo(3000)); } @@ -155,8 +155,8 @@ public sealed class Solution_Tests : ContentUnitTest solution.RemoveReagent("water", FixedPoint2.New(500)); - Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(500)); - Assert.That(solution.GetReagentQuantity("fire").Int(), Is.EqualTo(2000)); + Assert.That(solution.GetTotalPrototypeQuantity("water").Int(), Is.EqualTo(500)); + Assert.That(solution.GetTotalPrototypeQuantity("fire").Int(), Is.EqualTo(2000)); Assert.That(solution.Volume.Int(), Is.EqualTo(2500)); } @@ -167,7 +167,7 @@ public sealed class Solution_Tests : ContentUnitTest solution.RemoveReagent("water", FixedPoint2.New(-100)); - Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(100)); + Assert.That(solution.GetTotalPrototypeQuantity("water").Int(), Is.EqualTo(100)); Assert.That(solution.Volume.Int(), Is.EqualTo(100)); } @@ -178,7 +178,7 @@ public sealed class Solution_Tests : ContentUnitTest solution.RemoveReagent("water", FixedPoint2.New(1000)); - Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(0)); + Assert.That(solution.GetTotalPrototypeQuantity("water").Int(), Is.EqualTo(0)); Assert.That(solution.Volume.Int(), Is.EqualTo(0)); } @@ -189,7 +189,7 @@ public sealed class Solution_Tests : ContentUnitTest solution.RemoveReagent("fire", FixedPoint2.New(1000)); - Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(100)); + Assert.That(solution.GetTotalPrototypeQuantity("water").Int(), Is.EqualTo(100)); Assert.That(solution.Volume.Int(), Is.EqualTo(100)); } @@ -201,7 +201,7 @@ public sealed class Solution_Tests : ContentUnitTest solution.RemoveSolution(FixedPoint2.New(500)); //Check that edited solution is correct - Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(200)); + Assert.That(solution.GetTotalPrototypeQuantity("water").Int(), Is.EqualTo(200)); Assert.That(solution.Volume.Int(), Is.EqualTo(200)); } @@ -213,7 +213,7 @@ public sealed class Solution_Tests : ContentUnitTest solution.RemoveSolution(FixedPoint2.New(1000)); //Check that edited solution is correct - Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(0)); + Assert.That(solution.GetTotalPrototypeQuantity("water").Int(), Is.EqualTo(0)); Assert.That(solution.Volume.Int(), Is.EqualTo(0)); } @@ -226,8 +226,8 @@ public sealed class Solution_Tests : ContentUnitTest solution.RemoveSolution(FixedPoint2.New(1500)); - Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(500)); - Assert.That(solution.GetReagentQuantity("fire").Int(), Is.EqualTo(1000)); + Assert.That(solution.GetTotalPrototypeQuantity("water").Int(), Is.EqualTo(500)); + Assert.That(solution.GetTotalPrototypeQuantity("fire").Int(), Is.EqualTo(1000)); Assert.That(solution.Volume.Int(), Is.EqualTo(1500)); } @@ -238,7 +238,7 @@ public sealed class Solution_Tests : ContentUnitTest solution.RemoveSolution(FixedPoint2.New(-200)); - Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(800)); + Assert.That(solution.GetTotalPrototypeQuantity("water").Int(), Is.EqualTo(800)); Assert.That(solution.Volume.Int(), Is.EqualTo(800)); } @@ -251,12 +251,12 @@ public sealed class Solution_Tests : ContentUnitTest var splitSolution = solution.SplitSolution(FixedPoint2.New(750)); - Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(750)); - Assert.That(solution.GetReagentQuantity("fire").Int(), Is.EqualTo(1500)); + Assert.That(solution.GetTotalPrototypeQuantity("water").Int(), Is.EqualTo(750)); + Assert.That(solution.GetTotalPrototypeQuantity("fire").Int(), Is.EqualTo(1500)); Assert.That(solution.Volume.Int(), Is.EqualTo(2250)); - Assert.That(splitSolution.GetReagentQuantity("water").Int(), Is.EqualTo(250)); - Assert.That(splitSolution.GetReagentQuantity("fire").Int(), Is.EqualTo(500)); + Assert.That(splitSolution.GetTotalPrototypeQuantity("water").Int(), Is.EqualTo(250)); + Assert.That(splitSolution.GetTotalPrototypeQuantity("fire").Int(), Is.EqualTo(500)); Assert.That(splitSolution.Volume.Int(), Is.EqualTo(750)); } @@ -269,12 +269,12 @@ public sealed class Solution_Tests : ContentUnitTest var splitSolution = solution.SplitSolution(FixedPoint2.New(1)); - Assert.That(solution.GetReagentQuantity("water").Float(), Is.EqualTo(0.66f)); - Assert.That(solution.GetReagentQuantity("fire").Float(), Is.EqualTo(1.34f)); + Assert.That(solution.GetTotalPrototypeQuantity("water").Float(), Is.EqualTo(0.66f)); + Assert.That(solution.GetTotalPrototypeQuantity("fire").Float(), Is.EqualTo(1.34f)); Assert.That(solution.Volume.Int(), Is.EqualTo(2)); - Assert.That(splitSolution.GetReagentQuantity("water").Float(), Is.EqualTo(0.34f)); - Assert.That(splitSolution.GetReagentQuantity("fire").Float(), Is.EqualTo(0.66f)); + Assert.That(splitSolution.GetTotalPrototypeQuantity("water").Float(), Is.EqualTo(0.34f)); + Assert.That(splitSolution.GetTotalPrototypeQuantity("fire").Float(), Is.EqualTo(0.66f)); Assert.That(splitSolution.Volume.Int(), Is.EqualTo(1)); } @@ -287,12 +287,12 @@ public sealed class Solution_Tests : ContentUnitTest var splitSolution = solution.SplitSolution(FixedPoint2.New(2)); - Assert.That(solution.GetReagentQuantity("water").Float(), Is.EqualTo(0.33f)); - Assert.That(solution.GetReagentQuantity("fire").Float(), Is.EqualTo(0.67f)); + Assert.That(solution.GetTotalPrototypeQuantity("water").Float(), Is.EqualTo(0.33f)); + Assert.That(solution.GetTotalPrototypeQuantity("fire").Float(), Is.EqualTo(0.67f)); Assert.That(solution.Volume.Int(), Is.EqualTo(1)); - Assert.That(splitSolution.GetReagentQuantity("water").Float(), Is.EqualTo(0.67f)); - Assert.That(splitSolution.GetReagentQuantity("fire").Float(), Is.EqualTo(1.33f)); + Assert.That(splitSolution.GetTotalPrototypeQuantity("water").Float(), Is.EqualTo(0.67f)); + Assert.That(splitSolution.GetTotalPrototypeQuantity("fire").Float(), Is.EqualTo(1.33f)); Assert.That(splitSolution.Volume.Int(), Is.EqualTo(2)); } @@ -306,10 +306,10 @@ public sealed class Solution_Tests : ContentUnitTest var splitSolution = solution.SplitSolution(FixedPoint2.New(reduce)); - Assert.That(solution.GetReagentQuantity("water").Float(), Is.EqualTo(remainder)); + Assert.That(solution.GetTotalPrototypeQuantity("water").Float(), Is.EqualTo(remainder)); Assert.That(solution.Volume.Float(), Is.EqualTo(remainder)); - Assert.That(splitSolution.GetReagentQuantity("water").Float(), Is.EqualTo(reduce)); + Assert.That(splitSolution.GetTotalPrototypeQuantity("water").Float(), Is.EqualTo(reduce)); Assert.That(splitSolution.Volume.Float(), Is.EqualTo(reduce)); } @@ -338,10 +338,10 @@ public sealed class Solution_Tests : ContentUnitTest var splitSolution = solution.SplitSolution(FixedPoint2.New(1000)); - Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(0)); + Assert.That(solution.GetTotalPrototypeQuantity("water").Int(), Is.EqualTo(0)); Assert.That(solution.Volume.Int(), Is.EqualTo(0)); - Assert.That(splitSolution.GetReagentQuantity("water").Int(), Is.EqualTo(800)); + Assert.That(splitSolution.GetTotalPrototypeQuantity("water").Int(), Is.EqualTo(800)); Assert.That(splitSolution.Volume.Int(), Is.EqualTo(800)); } @@ -352,10 +352,10 @@ public sealed class Solution_Tests : ContentUnitTest var splitSolution = solution.SplitSolution(FixedPoint2.New(-200)); - Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(800)); + Assert.That(solution.GetTotalPrototypeQuantity("water").Int(), Is.EqualTo(800)); Assert.That(solution.Volume.Int(), Is.EqualTo(800)); - Assert.That(splitSolution.GetReagentQuantity("water").Int(), Is.EqualTo(0)); + Assert.That(splitSolution.GetTotalPrototypeQuantity("water").Int(), Is.EqualTo(0)); Assert.That(splitSolution.Volume.Int(), Is.EqualTo(0)); } @@ -393,9 +393,9 @@ public sealed class Solution_Tests : ContentUnitTest solutionOne.AddSolution(solutionTwo, null); - Assert.That(solutionOne.GetReagentQuantity("water").Int(), Is.EqualTo(1500)); - Assert.That(solutionOne.GetReagentQuantity("fire").Int(), Is.EqualTo(2000)); - Assert.That(solutionOne.GetReagentQuantity("earth").Int(), Is.EqualTo(1000)); + Assert.That(solutionOne.GetTotalPrototypeQuantity("water").Int(), Is.EqualTo(1500)); + Assert.That(solutionOne.GetTotalPrototypeQuantity("fire").Int(), Is.EqualTo(2000)); + Assert.That(solutionOne.GetTotalPrototypeQuantity("earth").Int(), Is.EqualTo(1000)); Assert.That(solutionOne.Volume.Int(), Is.EqualTo(4500)); }