Allow solutions to store extra reagent data (#19323)

This commit is contained in:
Leon Friedrich
2023-09-05 09:55:10 +12:00
committed by GitHub
parent a6b81058d0
commit e4ca6f4fb9
52 changed files with 932 additions and 538 deletions

View File

@@ -1,4 +1,5 @@
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.Console; using Robust.Client.Console;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
@@ -176,16 +177,16 @@ namespace Content.Client.Administration.UI.ManageSolutions
/// <summary> /// <summary>
/// Add a single reagent entry to the list /// Add a single reagent entry to the list
/// </summary> /// </summary>
private void AddReagentEntry(Solution.ReagentQuantity reagent) private void AddReagentEntry(ReagentQuantity reagentQuantity)
{ {
var box = new BoxContainer(); var box = new BoxContainer();
var spin = new FloatSpinBox(1, 2); var spin = new FloatSpinBox(1, 2);
spin.Value = reagent.Quantity.Float(); spin.Value = reagentQuantity.Quantity.Float();
spin.OnValueChanged += (args) => SetReagent(args, reagent.ReagentId); spin.OnValueChanged += (args) => SetReagent(args, reagentQuantity.Reagent.Prototype);
spin.HorizontalExpand = true; 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); box.AddChild(spin);
ReagentList.AddChild(box); ReagentList.AddChild(box);
@@ -194,18 +195,18 @@ namespace Content.Client.Administration.UI.ManageSolutions
/// <summary> /// <summary>
/// Execute a command to modify the reagents in the solution. /// Execute a command to modify the reagents in the solution.
/// </summary> /// </summary>
private void SetReagent(FloatSpinBox.FloatSpinBoxEventArgs args, string reagentId) private void SetReagent(FloatSpinBox.FloatSpinBoxEventArgs args, string prototype)
{ {
if (_solutions == null || _selectedSolution == null) if (_solutions == null || _selectedSolution == null)
return; return;
var current = _solutions[_selectedSolution].GetReagentQuantity(reagentId); var current = _solutions[_selectedSolution].GetTotalPrototypeQuantity(prototype);
var delta = args.Value - current.Float(); var delta = args.Value - current.Float();
if (MathF.Abs(delta) < 0.01) if (MathF.Abs(delta) < 0.01)
return; return;
var command = $"addreagent {_target} {_selectedSolution} {reagentId} {delta}"; var command = $"addreagent {_target} {_selectedSolution} {prototype} {delta}";
_consoleHost.ExecuteCommand(command); _consoleHost.ExecuteCommand(command);
} }

View File

@@ -11,6 +11,7 @@ using Robust.Shared.Prototypes;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using Content.Shared.FixedPoint;
using static Robust.Client.UserInterface.Controls.BoxContainer; using static Robust.Client.UserInterface.Controls.BoxContainer;
namespace Content.Client.Chemistry.UI namespace Content.Client.Chemistry.UI
@@ -87,7 +88,7 @@ namespace Content.Client.Chemistry.UI
Tabs.SetTabTitle(1, Loc.GetString("chem-master-window-output-tab")); 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); var button = new ReagentButton(text, amount, id, isBuffer, styleClass);
button.OnPressed += args button.OnPressed += args
@@ -112,11 +113,11 @@ namespace Content.Client.Chemistry.UI
InputEjectButton.Disabled = castState.InputContainerInfo is null; InputEjectButton.Disabled = castState.InputContainerInfo is null;
OutputEjectButton.Disabled = output is null; OutputEjectButton.Disabled = output is null;
CreateBottleButton.Disabled = output is null || !output.HoldsReagents; CreateBottleButton.Disabled = output?.Reagents == null;
CreatePillButton.Disabled = output is null || output.HoldsReagents; CreatePillButton.Disabled = output?.Entities == null;
var remainingCapacity = output is null ? 0 : (output.MaxVolume - output.CurrentVolume).Int(); 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 pillNumberMax = holdsReagents ? 0 : remainingCapacity;
var bottleAmountMax = holdsReagents ? remainingCapacity : 0; var bottleAmountMax = holdsReagents ? remainingCapacity : 0;
@@ -139,13 +140,10 @@ namespace Content.Client.Chemistry.UI
{ {
if (state.BufferCurrentVolume == 0) if (state.BufferCurrentVolume == 0)
return ""; 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 ?? "";
} }
/// <summary> /// <summary>
@@ -184,10 +182,10 @@ namespace Content.Client.Chemistry.UI
}; };
bufferHBox.AddChild(bufferVol); 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. // 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"); var name = proto?.LocalizedName ?? Loc.GetString("chem-master-window-unknown-reagent-text");
if (proto != null) if (proto != null)
@@ -200,20 +198,20 @@ namespace Content.Client.Chemistry.UI
new Label {Text = $"{name}: "}, new Label {Text = $"{name}: "},
new Label new Label
{ {
Text = $"{reagent.Quantity}u", Text = $"{quantity}u",
StyleClasses = {StyleNano.StyleClassLabelSecondaryColor} StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}
}, },
// Padding // Padding
new Control {HorizontalExpand = true}, new Control {HorizontalExpand = true},
MakeReagentButton("1", ChemMasterReagentAmount.U1, reagent.ReagentId, true, StyleBase.ButtonOpenRight), MakeReagentButton("1", ChemMasterReagentAmount.U1, reagent, true, StyleBase.ButtonOpenRight),
MakeReagentButton("5", ChemMasterReagentAmount.U5, reagent.ReagentId, true, StyleBase.ButtonOpenBoth), MakeReagentButton("5", ChemMasterReagentAmount.U5, reagent, true, StyleBase.ButtonOpenBoth),
MakeReagentButton("10", ChemMasterReagentAmount.U10, reagent.ReagentId, true, StyleBase.ButtonOpenBoth), MakeReagentButton("10", ChemMasterReagentAmount.U10, reagent, true, StyleBase.ButtonOpenBoth),
MakeReagentButton("25", ChemMasterReagentAmount.U25, reagent.ReagentId, true, StyleBase.ButtonOpenBoth), MakeReagentButton("25", ChemMasterReagentAmount.U25, reagent, true, StyleBase.ButtonOpenBoth),
MakeReagentButton("50", ChemMasterReagentAmount.U50, reagent.ReagentId, true, StyleBase.ButtonOpenBoth), MakeReagentButton("50", ChemMasterReagentAmount.U50, reagent, true, StyleBase.ButtonOpenBoth),
MakeReagentButton("100", ChemMasterReagentAmount.U100, reagent.ReagentId, true, StyleBase.ButtonOpenBoth), MakeReagentButton("100", ChemMasterReagentAmount.U100, reagent, true, StyleBase.ButtonOpenBoth),
MakeReagentButton(Loc.GetString("chem-master-window-buffer-all-amount"), ChemMasterReagentAmount.All, reagent.ReagentId, true, StyleBase.ButtonOpenLeft), 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 IEnumerable<(string Name, ReagentId Id, FixedPoint2 Quantity)> contents;
.Select(lineItem =>
{
if (!info.HoldsReagents)
return (lineItem.Id, lineItem.Id, lineItem.Quantity);
// Try to get the prototype for the given reagent. This gives us its name. if (info.Entities != null)
_prototypeManager.TryIndex(lineItem.Id, out ReagentPrototype? proto); {
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 var name = proto?.LocalizedName
?? Loc.GetString("chem-master-window-unknown-reagent-text"); ?? Loc.GetString("chem-master-window-unknown-reagent-text");
return (name, lineItem.Id, lineItem.Quantity); return (name, Id: x.Reagent, x.Quantity);
}) })
.OrderBy(r => r.Item1); .OrderBy(r => r.Item1);
}
else
{
return;
}
foreach (var (name, id, quantity) in contents) foreach (var (name, id, quantity) in contents)
{ {
@@ -326,8 +333,8 @@ namespace Content.Client.Chemistry.UI
{ {
public ChemMasterReagentAmount Amount { get; set; } public ChemMasterReagentAmount Amount { get; set; }
public bool IsBuffer = true; public bool IsBuffer = true;
public string Id { get; set; } public ReagentId Id { get; set; }
public ReagentButton(string text, ChemMasterReagentAmount amount, string id, bool isBuffer, string styleClass) public ReagentButton(string text, ChemMasterReagentAmount amount, ReagentId id, bool isBuffer, string styleClass)
{ {
AddStyleClass(styleClass); AddStyleClass(styleClass);
Text = text; Text = text;

View File

@@ -48,17 +48,17 @@ namespace Content.Client.Chemistry.UI
/// Update the button grid of reagents which can be dispensed. /// Update the button grid of reagents which can be dispensed.
/// </summary> /// </summary>
/// <param name="inventory">Reagents which can be dispensed by this dispenser</param> /// <param name="inventory">Reagents which can be dispensed by this dispenser</param>
public void UpdateReagentsList(List<string> inventory) public void UpdateReagentsList(List<ReagentId> inventory)
{ {
if (ChemicalList == null) return; if (ChemicalList == null)
if (inventory == null) return; return;
ChemicalList.Children.Clear(); ChemicalList.Children.Clear();
foreach (var entry in inventory 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 ? p.LocalizedName
: Loc.GetString("reagent-dispenser-window-reagent-name-not-found-text"); : Loc.GetString("reagent-dispenser-window-reagent-name-not-found-text");
@@ -123,7 +123,7 @@ namespace Content.Client.Chemistry.UI
/// <param name="state">State data for the dispenser.</param> /// <param name="state">State data for the dispenser.</param>
/// <param name="highlightedReagentId">Prototype ID of the reagent whose dispense button is currently being mouse hovered, /// <param name="highlightedReagentId">Prototype ID of the reagent whose dispense button is currently being mouse hovered,
/// or null if no button is being hovered.</param> /// or null if no button is being hovered.</param>
public void UpdateContainerInfo(ReagentDispenserBoundUserInterfaceState state, string? highlightedReagentId = null) public void UpdateContainerInfo(ReagentDispenserBoundUserInterfaceState state, ReagentId? highlightedReagentId = null)
{ {
ContainerInfo.Children.Clear(); 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. // 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 ? p.LocalizedName
: Loc.GetString("reagent-dispenser-window-reagent-name-not-found-text"); : Loc.GetString("reagent-dispenser-window-reagent-name-not-found-text");
var nameLabel = new Label {Text = $"{localizedName}: "}; var nameLabel = new Label {Text = $"{localizedName}: "};
var quantityLabel = new Label 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}, StyleClasses = {StyleNano.StyleClassLabelSecondaryColor},
}; };
// Check if the reagent is being moused over. If so, color it green. // Check if the reagent is being moused over. If so, color it green.
if (reagent.Id == highlightedReagentId) { if (reagent == highlightedReagentId) {
nameLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateGood); nameLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateGood);
quantityLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateGood); quantityLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateGood);
} }
@@ -181,9 +181,9 @@ namespace Content.Client.Chemistry.UI
} }
public sealed class DispenseReagentButton : Button { 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; ReagentId = reagentId;
Text = text; Text = text;

View File

@@ -83,7 +83,7 @@ namespace Content.Client.Kitchen.UI
} }
} }
private void RefreshContentsDisplay(IList<Solution.ReagentQuantity>? reagents, IReadOnlyList<EntityUid> containedSolids, bool isBeakerAttached) private void RefreshContentsDisplay(IList<ReagentQuantity>? reagents, IReadOnlyList<EntityUid> containedSolids, bool isBeakerAttached)
{ {
//Refresh chamber contents //Refresh chamber contents
_chamberVisualContents.Clear(); _chamberVisualContents.Clear();
@@ -118,9 +118,11 @@ namespace Content.Client.Kitchen.UI
} }
else 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); BeakerContentBox.BoxContents.AddItem(reagentName);
} }
} }

View File

@@ -1,4 +1,5 @@
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Kitchen.Components; using Content.Shared.Kitchen.Components;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
@@ -17,7 +18,7 @@ namespace Content.Client.Kitchen.UI
private readonly Dictionary<int, EntityUid> _solids = new(); private readonly Dictionary<int, EntityUid> _solids = new();
[ViewVariables] [ViewVariables]
private readonly Dictionary<int, Solution.ReagentQuantity> _reagents = new(); private readonly Dictionary<int, ReagentQuantity> _reagents = new();
public MicrowaveBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) public MicrowaveBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{ {

View File

@@ -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<IReflectionManager>();
Assert.Multiple(() =>
{
foreach (var instance in reflection.GetAllChildren(typeof(ReagentData)))
{
Assert.That(instance.HasCustomAttribute<NetSerializableAttribute>(), $"{instance} must have the NetSerializable attribute.");
Assert.That(instance.HasCustomAttribute<SerializableAttribute>(), $"{instance} must have the serializable attribute.");
}
});
await pair.CleanReturnAsync();
}
}

View File

@@ -73,8 +73,8 @@ public sealed class SolutionSystemTests
Assert.That(containerSystem Assert.That(containerSystem
.TryAddSolution(beaker, solution, oilAdded)); .TryAddSolution(beaker, solution, oilAdded));
solution.TryGetReagent("Water", out var water); var water = solution.GetTotalPrototypeQuantity("Water");
solution.TryGetReagent("Oil", out var oil); var oil = solution.GetTotalPrototypeQuantity("Oil");
Assert.Multiple(() => Assert.Multiple(() =>
{ {
Assert.That(water, Is.EqualTo(waterQuantity)); Assert.That(water, Is.EqualTo(waterQuantity));
@@ -118,8 +118,8 @@ public sealed class SolutionSystemTests
Assert.That(containerSystem Assert.That(containerSystem
.TryAddSolution(beaker, solution, oilAdded), Is.False); .TryAddSolution(beaker, solution, oilAdded), Is.False);
solution.TryGetReagent("Water", out var water); var water = solution.GetTotalPrototypeQuantity("Water");
solution.TryGetReagent("Oil", out var oil); var oil = solution.GetTotalPrototypeQuantity("Oil");
Assert.Multiple(() => Assert.Multiple(() =>
{ {
Assert.That(water, Is.EqualTo(waterQuantity)); Assert.That(water, Is.EqualTo(waterQuantity));
@@ -168,15 +168,15 @@ public sealed class SolutionSystemTests
{ {
Assert.That(solution.Volume, Is.EqualTo(FixedPoint2.New(threshold))); Assert.That(solution.Volume, Is.EqualTo(FixedPoint2.New(threshold)));
solution.TryGetReagent("Water", out var waterMix); var waterMix = solution.GetTotalPrototypeQuantity("Water");
solution.TryGetReagent("Oil", out var oilMix); var oilMix = solution.GetTotalPrototypeQuantity("Oil");
Assert.That(waterMix, Is.EqualTo(FixedPoint2.New(threshold / (ratio + 1)))); Assert.That(waterMix, Is.EqualTo(FixedPoint2.New(threshold / (ratio + 1))));
Assert.That(oilMix, Is.EqualTo(FixedPoint2.New(threshold / (ratio + 1) * ratio))); Assert.That(oilMix, Is.EqualTo(FixedPoint2.New(threshold / (ratio + 1) * ratio)));
Assert.That(overflowingSolution.Volume, Is.EqualTo(FixedPoint2.New(80))); Assert.That(overflowingSolution.Volume, Is.EqualTo(FixedPoint2.New(80)));
overflowingSolution.TryGetReagent("Water", out var waterOverflow); var waterOverflow = overflowingSolution.GetTotalPrototypeQuantity("Water");
overflowingSolution.TryGetReagent("Oil", out var oilOverFlow); var oilOverFlow = overflowingSolution.GetTotalPrototypeQuantity("Oil");
Assert.That(waterOverflow, Is.EqualTo(waterQuantity - waterMix)); Assert.That(waterOverflow, Is.EqualTo(waterQuantity - waterMix));
Assert.That(oilOverFlow, Is.EqualTo(oilQuantity - oilMix)); Assert.That(oilOverFlow, Is.EqualTo(oilQuantity - oilMix));
}); });

View File

@@ -79,9 +79,9 @@ namespace Content.IntegrationTests.Tests.Chemistry
var foundProductsMap = reactionPrototype.Products var foundProductsMap = reactionPrototype.Products
.Concat(reactionPrototype.Reactants.Where(x => x.Value.Catalyst).ToDictionary(x => x.Key, x => x.Value.Amount)) .Concat(reactionPrototype.Reactants.Where(x => x.Value.Catalyst).ToDictionary(x => x.Key, x => x.Value.Amount))
.ToDictionary(x => x, _ => false); .ToDictionary(x => x, _ => false);
foreach (var reagent 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; foundProductsMap[foundProduct.Value.Key] = true;
} }

View File

@@ -62,7 +62,7 @@ namespace Content.Server.Administration.Commands
if (quantityFloat > 0) if (quantityFloat > 0)
EntitySystem.Get<SolutionContainerSystem>().TryAddReagent(uid, solution, args[2], quantity, out var _); EntitySystem.Get<SolutionContainerSystem>().TryAddReagent(uid, solution, args[2], quantity, out var _);
else else
EntitySystem.Get<SolutionContainerSystem>().TryRemoveReagent(uid, solution, args[2], quantity); EntitySystem.Get<SolutionContainerSystem>().RemoveReagent(uid, solution, args[2], quantity);
} }
} }
} }

View File

@@ -59,7 +59,7 @@ public sealed class SolutionCommand : ToolshedCommand
} }
else if (amount < 0) 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; return input;

View File

@@ -1,5 +1,7 @@
using Content.Server.Body.Systems; using Content.Server.Body.Systems;
using Content.Server.Nutrition.EntitySystems; using Content.Server.Nutrition.EntitySystems;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.Whitelist; using Content.Shared.Whitelist;
@@ -46,14 +48,12 @@ namespace Content.Server.Body.Components
/// </summary> /// </summary>
public sealed class ReagentDelta public sealed class ReagentDelta
{ {
public readonly string ReagentId; public readonly ReagentQuantity ReagentQuantity;
public readonly FixedPoint2 Quantity;
public float Lifetime { get; private set; } public float Lifetime { get; private set; }
public ReagentDelta(string reagentId, FixedPoint2 quantity) public ReagentDelta(ReagentQuantity reagentQuantity)
{ {
ReagentId = reagentId; ReagentQuantity = reagentQuantity;
Quantity = quantity;
Lifetime = 0.0f; Lifetime = 0.0f;
} }

View File

@@ -274,9 +274,9 @@ public sealed class BloodstreamSystem : EntitySystem
for (var i = component.ChemicalSolution.Contents.Count - 1; i >= 0; i--) for (var i = component.ChemicalSolution.Contents.Count - 1; i >= 0; i--)
{ {
var (reagentId, _) = component.ChemicalSolution.Contents[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);
} }
} }

View File

@@ -128,9 +128,9 @@ namespace Content.Server.Body.Systems
_random.Shuffle(list); _random.Shuffle(list);
int reagents = 0; int reagents = 0;
foreach (var reagent in list) foreach (var (reagent, quantity) in list)
{ {
if (!_prototypeManager.TryIndex<ReagentPrototype>(reagent.ReagentId, out var proto)) if (!_prototypeManager.TryIndex<ReagentPrototype>(reagent.Prototype, out var proto))
continue; continue;
var mostToRemove = FixedPoint2.Zero; var mostToRemove = FixedPoint2.Zero;
@@ -138,7 +138,7 @@ namespace Content.Server.Body.Systems
{ {
if (meta.RemoveEmpty) if (meta.RemoveEmpty)
{ {
_solutionContainerSystem.TryRemoveReagent(solutionEntityUid.Value, solution, reagent.ReagentId, _solutionContainerSystem.RemoveReagent(solutionEntityUid.Value, solution, reagent,
FixedPoint2.New(1)); FixedPoint2.New(1));
} }
@@ -168,7 +168,7 @@ namespace Content.Server.Body.Systems
mostToRemove *= group.MetabolismRateModifier; mostToRemove *= group.MetabolismRateModifier;
mostToRemove = FixedPoint2.Clamp(mostToRemove, 0, reagent.Quantity); mostToRemove = FixedPoint2.Clamp(mostToRemove, 0, quantity);
float scale = (float) mostToRemove / (float) entry.MetabolismRate; float scale = (float) mostToRemove / (float) entry.MetabolismRate;
@@ -204,8 +204,7 @@ namespace Content.Server.Body.Systems
// remove a certain amount of reagent // remove a certain amount of reagent
if (mostToRemove > FixedPoint2.Zero) if (mostToRemove > FixedPoint2.Zero)
{ {
_solutionContainerSystem.TryRemoveReagent(solutionEntityUid.Value, solution, reagent.ReagentId, _solutionContainerSystem.RemoveReagent(solutionEntityUid.Value, solution, reagent, mostToRemove);
mostToRemove);
} }
} }
} }

View File

@@ -45,14 +45,13 @@ namespace Content.Server.Body.Systems
delta.Increment(stomach.UpdateInterval); delta.Increment(stomach.UpdateInterval);
if (delta.Lifetime > stomach.DigestionDelay) 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) if (reagent.Quantity > delta.ReagentQuantity.Quantity)
quant = delta.Quantity; reagent = new(reagent.Reagent, delta.ReagentQuantity.Quantity);
_solutionContainerSystem.TryRemoveReagent((stomach).Owner, stomachSolution, _solutionContainerSystem.RemoveReagent((stomach).Owner, stomachSolution, reagent);
delta.ReagentId, quant); transferSolution.AddReagent(reagent);
transferSolution.AddReagent(delta.ReagentId, quant);
} }
queue.Add(delta); 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 // Add each reagent to ReagentDeltas. Used to track how long each reagent has been in the stomach
foreach (var reagent in solution.Contents) foreach (var reagent in solution.Contents)
{ {
stomach.ReagentDeltas.Add(new StomachComponent.ReagentDelta(reagent.ReagentId, reagent.Quantity)); stomach.ReagentDeltas.Add(new StomachComponent.ReagentDelta(reagent));
} }
return true; return true;

View File

@@ -824,10 +824,10 @@ namespace Content.Server.Botany.Systems
if (solution.Volume > 0 && component.MutationLevel < 25) if (solution.Volume > 0 && component.MutationLevel < 25)
{ {
var amt = FixedPoint2.New(1); var amt = FixedPoint2.New(1);
foreach (var (reagentId, quantity) in _solutionSystem.RemoveEachReagent(uid, solution, amt)) foreach (var entry in _solutionSystem.RemoveEachReagent(uid, solution, amt))
{ {
var reagentProto = _prototype.Index<ReagentPrototype>(reagentId); var reagentProto = _prototype.Index<ReagentPrototype>(entry.Reagent.Prototype);
reagentProto.ReactionPlant(uid, new Solution.ReagentQuantity(reagentId, quantity), solution); reagentProto.ReactionPlant(uid, entry, solution);
} }
} }

View File

@@ -110,11 +110,13 @@ public sealed class PricingSystem : EntitySystem
foreach (var solution in component.Solutions.Values) foreach (var solution in component.Solutions.Values)
{ {
foreach (var reagent in solution.Contents) foreach (var (reagent, quantity) in solution.Contents)
{ {
if (!_prototypeManager.TryIndex<ReagentPrototype>(reagent.ReagentId, out var reagentProto)) if (!_prototypeManager.TryIndex<ReagentPrototype>(reagent.Prototype, out var reagentProto))
continue; continue;
price += (float) reagent.Quantity * reagentProto.PricePerUnit;
// TODO check ReagentData for price information?
price += (float) quantity * reagentProto.PricePerUnit;
} }
} }

View File

@@ -8,6 +8,7 @@ using Content.Server.Storage.EntitySystems;
using Content.Shared.Administration.Logs; using Content.Shared.Administration.Logs;
using Content.Shared.Chemistry; using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Containers.ItemSlots; using Content.Shared.Containers.ItemSlots;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
@@ -117,7 +118,7 @@ namespace Content.Server.Chemistry.EntitySystems
ClickSound(chemMaster); 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); var container = _itemSlotsSystem.GetItemOrNull(chemMaster.Owner, SharedChemMaster.InputSlotName);
if (container is null || if (container is null ||
@@ -130,26 +131,26 @@ namespace Content.Server.Chemistry.EntitySystems
if (fromBuffer) // Buffer to container if (fromBuffer) // Buffer to container
{ {
amount = FixedPoint2.Min(amount, containerSolution.AvailableVolume); amount = FixedPoint2.Min(amount, containerSolution.AvailableVolume);
amount = bufferSolution.RemoveReagent(reagentId, amount); amount = bufferSolution.RemoveReagent(id, amount);
_solutionContainerSystem.TryAddReagent(container.Value, containerSolution, reagentId, amount, out var _); _solutionContainerSystem.TryAddReagent(container.Value, containerSolution, id, amount, out var _);
} }
else // Container to buffer else // Container to buffer
{ {
amount = FixedPoint2.Min(amount, containerSolution.GetReagentQuantity(reagentId)); amount = FixedPoint2.Min(amount, containerSolution.GetReagentQuantity(id));
_solutionContainerSystem.TryRemoveReagent(container.Value, containerSolution, reagentId, amount); _solutionContainerSystem.RemoveReagent(container.Value, containerSolution, id, amount);
bufferSolution.AddReagent(reagentId, amount); bufferSolution.AddReagent(id, amount);
} }
UpdateUiState(chemMaster, updateLabel: true); 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 (fromBuffer)
{ {
if (_solutionContainerSystem.TryGetSolution(chemMaster.Owner, SharedChemMaster.BufferSolutionName, out var bufferSolution)) if (_solutionContainerSystem.TryGetSolution(chemMaster.Owner, SharedChemMaster.BufferSolutionName, out var bufferSolution))
bufferSolution.RemoveReagent(reagentId, amount); bufferSolution.RemoveReagent(id, amount);
else else
return; return;
} }
@@ -159,7 +160,7 @@ namespace Content.Server.Chemistry.EntitySystems
if (container is not null && if (container is not null &&
_solutionContainerSystem.TryGetFitsInDispenser(container.Value, out var containerSolution)) _solutionContainerSystem.TryGetFitsInDispenser(container.Value, out var containerSolution))
{ {
_solutionContainerSystem.TryRemoveReagent(container.Value, containerSolution, reagentId, amount); _solutionContainerSystem.RemoveReagent(container.Value, containerSolution, id, amount);
} }
else else
return; return;
@@ -349,17 +350,21 @@ namespace Content.Server.Chemistry.EntitySystems
return (Name(pill), quantity); return (Name(pill), quantity);
})).ToList(); })).ToList();
return pills is null if (pills == null)
? null return null;
: new ContainerInfo(name, false, storage.StorageUsed, storage.StorageCapacityMax, pills);
return new ContainerInfo(name, storage.StorageUsed, storage.StorageCapacityMax)
{
Entities = pills
};
} }
private static ContainerInfo BuildContainerInfo(string name, Solution solution) private static ContainerInfo BuildContainerInfo(string name, Solution solution)
{ {
var reagents = solution.Contents return new ContainerInfo(name, solution.Volume, solution.MaxVolume)
.Select(reagent => (reagent.ReagentId, reagent.Quantity)).ToList(); {
Reagents = solution.Contents
return new ContainerInfo(name, true, solution.Volume, solution.MaxVolume, reagents); };
} }
} }
} }

View File

@@ -3,6 +3,7 @@ using Content.Server.Administration.Logs;
using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Components;
using Content.Shared.Chemistry; using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Dispenser; using Content.Shared.Chemistry.Dispenser;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Containers.ItemSlots; using Content.Shared.Containers.ItemSlots;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.Emag.Components; using Content.Shared.Emag.Components;
@@ -63,28 +64,30 @@ namespace Content.Server.Chemistry.EntitySystems
if (_solutionContainerSystem.TryGetFitsInDispenser(container.Value, out var solution)) 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), solution.Volume, solution.MaxVolume)
return new ContainerInfo(Name(container.Value), true, solution.Volume, solution.MaxVolume, reagents); {
Reagents = solution.Contents
};
} }
return null; return null;
} }
private List<string> GetInventory(ReagentDispenserComponent reagentDispenser) private List<ReagentId> GetInventory(ReagentDispenserComponent reagentDispenser)
{ {
var inventory = new List<string>(); var inventory = new List<ReagentId>();
if (reagentDispenser.PackPrototypeId is not null if (reagentDispenser.PackPrototypeId is not null
&& _prototypeManager.TryIndex(reagentDispenser.PackPrototypeId, out ReagentDispenserInventoryPrototype? packPrototype)) && _prototypeManager.TryIndex(reagentDispenser.PackPrototypeId, out ReagentDispenserInventoryPrototype? packPrototype))
{ {
inventory.AddRange(packPrototype.Inventory); inventory.AddRange(packPrototype.Inventory.Select(x => new ReagentId(x, null)));
} }
if (HasComp<EmaggedComponent>(reagentDispenser.Owner) if (HasComp<EmaggedComponent>(reagentDispenser.Owner)
&& reagentDispenser.EmagPackPrototypeId is not null && reagentDispenser.EmagPackPrototypeId is not null
&& _prototypeManager.TryIndex(reagentDispenser.EmagPackPrototypeId, out ReagentDispenserInventoryPrototype? emagPackPrototype)) && _prototypeManager.TryIndex(reagentDispenser.EmagPackPrototypeId, out ReagentDispenserInventoryPrototype? emagPackPrototype))
{ {
inventory.AddRange(emagPackPrototype.Inventory); inventory.AddRange(emagPackPrototype.Inventory.Select(x => new ReagentId(x, null)));
} }
return inventory; return inventory;

View File

@@ -20,7 +20,7 @@ public sealed class RehydratableSystem : EntitySystem
private void OnSolutionChange(EntityUid uid, RehydratableComponent comp, SolutionChangedEvent args) 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) if (quantity != FixedPoint2.Zero && quantity >= comp.CatalystMinimum)
{ {
Expand(uid, comp); Expand(uid, comp);

View File

@@ -23,9 +23,11 @@ namespace Content.Server.Chemistry.EntitySystems;
public sealed class SolutionChangedEvent : EntityEventArgs public sealed class SolutionChangedEvent : EntityEventArgs
{ {
public readonly Solution Solution; public readonly Solution Solution;
public readonly string SolutionId;
public SolutionChangedEvent(Solution solution) public SolutionChangedEvent(Solution solution, string solutionId)
{ {
SolutionId = solutionId;
Solution = solution; Solution = solution;
} }
} }
@@ -103,7 +105,7 @@ public sealed partial class SolutionContainerSystem : EntitySystem
{ {
var msg = new FormattedMessage(); 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")); msg.AddMarkup(Loc.GetString("scannable-solution-empty-container"));
return msg; return msg;
@@ -111,17 +113,13 @@ public sealed partial class SolutionContainerSystem : EntitySystem
msg.AddMarkup(Loc.GetString("scannable-solution-main-text")); msg.AddMarkup(Loc.GetString("scannable-solution-main-text"));
foreach (var reagent in solution) foreach (var (proto, quantity) in solution.GetReagentPrototypes(_prototypeManager))
{ {
if (!_prototypeManager.TryIndex<ReagentPrototype>(reagent.ReagentId, out var proto))
{
continue;
}
msg.PushNewline(); msg.PushNewline();
msg.AddMarkup(Loc.GetString("scannable-solution-chemical" msg.AddMarkup(Loc.GetString("scannable-solution-chemical"
, ("type", proto.LocalizedName) , ("type", proto.LocalizedName)
, ("color", proto.SubstanceColor.ToHexNoAlpha()) , ("color", proto.SubstanceColor.ToHexNoAlpha())
, ("amount", reagent.Quantity))); , ("amount", quantity)));
} }
return msg; return msg;
@@ -132,46 +130,41 @@ public sealed partial class SolutionContainerSystem : EntitySystem
{ {
SolutionContainerManagerComponent? solutionsManager = null; SolutionContainerManagerComponent? solutionsManager = null;
if (!Resolve(args.Examined, ref solutionsManager) if (!Resolve(args.Examined, ref solutionsManager)
|| !solutionsManager.Solutions.TryGetValue(examinableComponent.Solution, out var solutionHolder)) || !solutionsManager.Solutions.TryGetValue(examinableComponent.Solution, out var solution))
{ {
return; 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")); args.PushText(Loc.GetString("shared-solution-container-component-on-examine-empty-container"));
return; return;
} }
if (!_prototypeManager.TryIndex(primaryReagent, out ReagentPrototype? primary)) if (!_prototypeManager.TryIndex(primaryReagent.Value.Prototype, out ReagentPrototype? primary))
{ {
Logger.Error( Logger.Error(
$"{nameof(Solution)} could not find the prototype associated with {primaryReagent}."); $"{nameof(Solution)} could not find the prototype associated with {primaryReagent}.");
return; 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. .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"; var messageString = "shared-solution-container-component-on-examine-main-text";
args.PushMarkup(Loc.GetString(messageString, args.PushMarkup(Loc.GetString(messageString,
("color", colorHex), ("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-one-reagent"
: "shared-solution-container-component-on-examine-worded-amount-multiple-reagents")), : "shared-solution-container-component-on-examine-worded-amount-multiple-reagents")),
("desc", primary.LocalizedPhysicalDescription))); ("desc", primary.LocalizedPhysicalDescription)));
// Add descriptions of immediately recognizable reagents, like water or beer // Add descriptions of immediately recognizable reagents, like water or beer
var recognized = new List<ReagentPrototype>(); var recognized = new List<ReagentPrototype>();
foreach (var (id, _) in solutionHolder) foreach (var proto in solution.GetReagentPrototypes(_prototypeManager).Keys)
{ {
if (!_prototypeManager.TryIndex<ReagentPrototype>(id, out var proto))
{
continue;
}
if (!proto.Recognizable) if (!proto.Recognizable)
{ {
continue; continue;
@@ -278,7 +271,7 @@ public sealed partial class SolutionContainerSystem : EntitySystem
} }
UpdateAppearance(uid, solutionHolder); UpdateAppearance(uid, solutionHolder);
RaiseLocalEvent(uid, new SolutionChangedEvent(solutionHolder)); RaiseLocalEvent(uid, new SolutionChangedEvent(solutionHolder, solutionHolder.Name));
} }
public void RemoveAllSolution(EntityUid uid, Solution solutionHolder) public void RemoveAllSolution(EntityUid uid, Solution solutionHolder)
@@ -324,46 +317,111 @@ public sealed partial class SolutionContainerSystem : EntitySystem
/// </summary> /// </summary>
/// <param name="targetUid"></param> /// <param name="targetUid"></param>
/// <param name="targetSolution">Container to which we are adding reagent</param> /// <param name="targetSolution">Container to which we are adding reagent</param>
/// <param name="reagentId">The Id of the reagent to add.</param> /// <param name="reagentQuantity">The reagent to add.</param>
/// <param name="acceptedQuantity">The amount of reagent successfully added.</param>
/// <returns>If all the reagent could be added.</returns>
public bool TryAddReagent(EntityUid targetUid, Solution targetSolution, ReagentQuantity reagentQuantity,
out FixedPoint2 acceptedQuantity, float? temperature = null)
{
acceptedQuantity = targetSolution.AvailableVolume > reagentQuantity.Quantity
? reagentQuantity.Quantity
: targetSolution.AvailableVolume;
if (acceptedQuantity <= 0)
return reagentQuantity.Quantity == 0;
if (temperature == null)
{
targetSolution.AddReagent(reagentQuantity.Reagent, acceptedQuantity);
}
else
{
var proto = _prototypeManager.Index<ReagentPrototype>(reagentQuantity.Reagent.Prototype);
targetSolution.AddReagent(proto, acceptedQuantity, temperature.Value, _prototypeManager);
}
UpdateChemicals(targetUid, targetSolution, true);
return acceptedQuantity == reagentQuantity.Quantity;
}
/// <summary>
/// Adds reagent of an Id to the container.
/// </summary>
/// <param name="targetUid"></param>
/// <param name="targetSolution">Container to which we are adding reagent</param>
/// <param name="prototype">The Id of the reagent to add.</param>
/// <param name="quantity">The amount of reagent to add.</param> /// <param name="quantity">The amount of reagent to add.</param>
/// <param name="acceptedQuantity">The amount of reagent successfully added.</param> /// <param name="acceptedQuantity">The amount of reagent successfully added.</param>
/// <returns>If all the reagent could be added.</returns> /// <returns>If all the reagent could be added.</returns>
public bool TryAddReagent(EntityUid targetUid, Solution targetSolution, string reagentId, FixedPoint2 quantity, public bool TryAddReagent(EntityUid targetUid, Solution targetSolution, string prototype, FixedPoint2 quantity,
out FixedPoint2 acceptedQuantity, float? temperature = null) out FixedPoint2 acceptedQuantity, float? temperature = null, ReagentData? data = null)
{ {
acceptedQuantity = targetSolution.AvailableVolume > quantity ? quantity : targetSolution.AvailableVolume; var reagent = new ReagentQuantity(prototype, quantity, data);
return TryAddReagent(targetUid, targetSolution, reagent, out acceptedQuantity, temperature);
if (acceptedQuantity <= 0)
return quantity == 0;
if (temperature == null)
targetSolution.AddReagent(reagentId, acceptedQuantity);
else
targetSolution.AddReagent(_prototypeManager.Index<ReagentPrototype>(reagentId), acceptedQuantity, temperature.Value, _prototypeManager);
UpdateChemicals(targetUid, targetSolution, true);
return acceptedQuantity == quantity;
} }
/// <summary>
/// Adds reagent of an Id to the container.
/// </summary>
/// <param name="targetUid"></param>
/// <param name="targetSolution">Container to which we are adding reagent</param>
/// <param name="reagentId">The reagent to add.</param>
/// <param name="quantity">The amount of reagent to add.</param>
/// <param name="acceptedQuantity">The amount of reagent successfully added.</param>
/// <returns>If all the reagent could be added.</returns>
public bool TryAddReagent(EntityUid targetUid, Solution targetSolution, ReagentId reagentId, FixedPoint2 quantity,
out FixedPoint2 acceptedQuantity, float? temperature = null)
{
var quant = new ReagentQuantity(reagentId, quantity);
return TryAddReagent(targetUid, targetSolution, quant, out acceptedQuantity, temperature);
}
/// <summary> /// <summary>
/// Removes reagent of an Id to the container. /// Removes reagent from a container.
/// </summary> /// </summary>
/// <param name="targetUid"></param> /// <param name="targetUid"></param>
/// <param name="container">Solution container from which we are removing reagent</param> /// <param name="container">Solution container from which we are removing reagent</param>
/// <param name="reagentId">The Id of the reagent to remove.</param> /// <param name="reagentQuantity">The reagent to remove.</param>
/// <param name="quantity">The amount of reagent to remove.</param>
/// <returns>If the reagent to remove was found in the container.</returns> /// <returns>If the reagent to remove was found in the container.</returns>
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; return false;
container.RemoveReagent(reagentId, quantity);
UpdateChemicals(targetUid, container); UpdateChemicals(targetUid, container);
return true; return true;
} }
/// <summary>
/// Removes reagent from a container.
/// </summary>
/// <param name="targetUid"></param>
/// <param name="container">Solution container from which we are removing reagent</param>
/// <param name="prototype">The Id of the reagent to remove.</param>
/// <param name="quantity">The amount of reagent to remove.</param>
/// <returns>If the reagent to remove was found in the container.</returns>
public bool RemoveReagent(EntityUid targetUid, Solution? container, string prototype, FixedPoint2 quantity, ReagentData? data = null)
{
return RemoveReagent(targetUid, container, new ReagentQuantity(prototype, quantity, data));
}
/// <summary>
/// Removes reagent from a container.
/// </summary>
/// <param name="targetUid"></param>
/// <param name="container">Solution container from which we are removing reagent</param>
/// <param name="reagentId">The reagent to remove.</param>
/// <param name="quantity">The amount of reagent to remove.</param>
/// <returns>If the reagent to remove was found in the container.</returns>
public bool RemoveReagent(EntityUid targetUid, Solution? container, ReagentId reagentId, FixedPoint2 quantity)
{
return RemoveReagent(targetUid, container, new ReagentQuantity(reagentId, quantity));
}
/// <summary> /// <summary>
/// Adds a solution to the container, if it can fully fit. /// Adds a solution to the container, if it can fully fit.
/// </summary> /// </summary>
@@ -391,19 +449,35 @@ public sealed partial class SolutionContainerSystem : EntitySystem
/// <param name="target">target solution</param> /// <param name="target">target solution</param>
/// <param name="quantity">quantity of solution to move from source to target. If this is a negative number, the source & target roles are reversed.</param> /// <param name="quantity">quantity of solution to move from source to target. If this is a negative number, the source & target roles are reversed.</param>
public bool TryTransferSolution(EntityUid sourceUid, EntityUid targetUid, Solution source, Solution target, FixedPoint2 quantity) public bool TryTransferSolution(EntityUid sourceUid, EntityUid targetUid, Solution source, Solution target, FixedPoint2 quantity)
{
if (!TryTransferSolution(targetUid, target, source, quantity))
return false;
UpdateChemicals(sourceUid, source, false);
return true;
}
/// <summary>
/// Moves some quantity of a solution from one solution to another.
/// </summary>
/// <param name="sourceUid">entity holding the source solution</param>
/// <param name="targetUid">entity holding the target solution</param>
/// <param name="source">source solution</param>
/// <param name="target">target solution</param>
/// <param name="quantity">quantity of solution to move from source to target. If this is a negative number, the source & target roles are reversed.</param>
public bool TryTransferSolution(EntityUid targetUid, Solution target, Solution source, FixedPoint2 quantity)
{ {
if (quantity < 0) 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); quantity = FixedPoint2.Min(quantity, target.AvailableVolume, source.Volume);
if (quantity == 0) if (quantity == 0)
return false; return false;
// TODO This should be made into a function that directly transfers reagents. currently this is quite // TODO This should be made into a function that directly transfers reagents.
// inefficient. // Currently this is quite inefficient.
target.AddSolution(source.SplitSolution(quantity), _prototypeManager); target.AddSolution(source.SplitSolution(quantity), _prototypeManager);
UpdateChemicals(sourceUid, source, false);
UpdateChemicals(targetUid, target, true); UpdateChemicals(targetUid, target, true);
return true; return true;
} }
@@ -540,7 +614,7 @@ public sealed partial class SolutionContainerSystem : EntitySystem
} }
public Solution EnsureSolution(EntityUid uid, string name, public Solution EnsureSolution(EntityUid uid, string name,
IEnumerable<Solution.ReagentQuantity> reagents, IEnumerable<ReagentQuantity> reagents,
bool setMaxVol = true, bool setMaxVol = true,
SolutionContainerManagerComponent? solutionsMgr = null) 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. // 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--) for (var i = solution.Contents.Count-1; i >= 0; i--)
{ {
var (reagentId, _) = solution.Contents[i]; var (reagent, _) = solution.Contents[i];
var removedQuantity = solution.RemoveReagent(reagent, quantity);
var removedQuantity = solution.RemoveReagent(reagentId, quantity); removedSolution.AddReagent(reagent, removedQuantity);
if(removedQuantity > 0)
removedSolution.AddReagent(reagentId, removedQuantity);
} }
UpdateChemicals(uid, solution); UpdateChemicals(uid, solution);
return removedSolution; return removedSolution;
} }
public FixedPoint2 GetReagentQuantity(EntityUid owner, string reagentId) public FixedPoint2 GetTotalPrototypeQuantity(EntityUid owner, string reagentId)
{ {
var reagentQuantity = FixedPoint2.New(0); var reagentQuantity = FixedPoint2.New(0);
if (EntityManager.EntityExists(owner) if (EntityManager.EntityExists(owner)
@@ -594,7 +665,7 @@ public sealed partial class SolutionContainerSystem : EntitySystem
{ {
foreach (var solution in managerComponent.Solutions.Values) 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) if (component.Solutions.Count == 0)
return null; return null;
var reagentCounts = new Dictionary<string, FixedPoint2>(); var reagentCounts = new Dictionary<ReagentId, FixedPoint2>();
foreach (var solution in component.Solutions.Values) 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); reagentCounts.TryGetValue(reagent, out var existing);
existing += reagent.Quantity; existing += quantity;
reagentCounts[reagent.ReagentId] = existing; reagentCounts[reagent] = existing;
} }
} }
var max = reagentCounts.Max(); var max = reagentCounts.Max();
return _prototypeManager.Index<ReagentPrototype>(max.Key); return _prototypeManager.Index<ReagentPrototype>(max.Key.Prototype);
} }
public SoundSpecifier? GetSound(SolutionContainerManagerComponent component) public SoundSpecifier? GetSound(SolutionContainerManagerComponent component)

View File

@@ -47,14 +47,14 @@ public sealed class TransformableContainerSystem : EntitySystem
var reagentId = solution.GetPrimaryReagentId(); var reagentId = solution.GetPrimaryReagentId();
//If biggest reagent didn't changed - don't change anything at all //If biggest reagent didn't changed - don't change anything at all
if (component.CurrentReagent != null && component.CurrentReagent.ID == reagentId) if (component.CurrentReagent != null && component.CurrentReagent.ID == reagentId?.Prototype)
{ {
return; return;
} }
//Only reagents with spritePath property can change appearance of transformable containers! //Only reagents with spritePath property can change appearance of transformable containers!
if (!string.IsNullOrWhiteSpace(reagentId) if (!string.IsNullOrWhiteSpace(reagentId?.Prototype)
&& _prototypeManager.TryIndex(reagentId, out ReagentPrototype? proto)) && _prototypeManager.TryIndex(reagentId.Value.Prototype, out ReagentPrototype? proto))
{ {
var metadata = MetaData(owner); var metadata = MetaData(owner);
var val = Loc.GetString("transformable-container-component-glass", ("name", proto.LocalizedName)); var val = Loc.GetString("transformable-container-component-glass", ("name", proto.LocalizedName));

View File

@@ -1,18 +1,17 @@
using System.Numerics; using System.Numerics;
using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Components.SolutionManager; using Content.Server.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.Physics; using Content.Shared.Physics;
using Content.Shared.Spawners.Components; using Content.Shared.Spawners.Components;
using Content.Shared.Throwing; using Content.Shared.Throwing;
using Content.Shared.Vapor;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Map.Components; using Robust.Shared.Map.Components;
using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Physics.Events; using Robust.Shared.Physics.Events;
using Robust.Shared.Physics.Systems; using Robust.Shared.Physics.Systems;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -27,6 +26,7 @@ namespace Content.Server.Chemistry.EntitySystems
[Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly ThrowingSystem _throwing = default!; [Dependency] private readonly ThrowingSystem _throwing = default!;
[Dependency] private readonly ReactiveSystem _reactive = default!;
private const float ReactTime = 0.125f; private const float ReactTime = 0.125f;
@@ -45,7 +45,7 @@ namespace Content.Server.Chemistry.EntitySystems
foreach (var value in contents.Solutions.Values) 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 // 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()) foreach (var reagentQuantity in contents.Contents.ToArray())
{ {
if (reagentQuantity.Quantity == FixedPoint2.Zero) continue; if (reagentQuantity.Quantity == FixedPoint2.Zero) continue;
var reagent = _protoManager.Index<ReagentPrototype>(reagentQuantity.ReagentId); var reagent = _protoManager.Index<ReagentPrototype>(reagentQuantity.Reagent.Prototype);
var reaction = var reaction =
reagent.ReactionTile(tile, (reagentQuantity.Quantity / vapor.TransferAmount) * 0.25f); reagent.ReactionTile(tile, (reagentQuantity.Quantity / vapor.TransferAmount) * 0.25f);
if (reaction > reagentQuantity.Quantity) 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; reaction = reagentQuantity.Quantity;
} }
_solutionContainerSystem.TryRemoveReagent(vapor.Owner, contents, reagentQuantity.ReagentId, reaction); _solutionContainerSystem.RemoveReagent(vapor.Owner, contents, reagentQuantity.Reagent, reaction);
} }
} }

View File

@@ -19,6 +19,7 @@ namespace Content.Server.Chemistry.ReagentEffectConditions
[DataField("max")] [DataField("max")]
public FixedPoint2 Max = FixedPoint2.MaxValue; public FixedPoint2 Max = FixedPoint2.MaxValue;
// TODO use ReagentId
[DataField("reagent")] [DataField("reagent")]
public string? Reagent; public string? Reagent;
@@ -29,10 +30,8 @@ namespace Content.Server.Chemistry.ReagentEffectConditions
return true; // No condition to apply. return true; // No condition to apply.
var quant = FixedPoint2.Zero; var quant = FixedPoint2.Zero;
if (args.Source != null && args.Source.ContainsReagent(reagent)) if (args.Source != null)
{ quant = args.Source.GetTotalPrototypeQuantity(reagent);
quant = args.Source.GetReagentQuantity(reagent);
}
return quant >= Min && quant <= Max; return quant >= Min && quant <= Max;
} }

View File

@@ -16,6 +16,7 @@ namespace Content.Server.Chemistry.ReagentEffects
/// </summary> /// </summary>
[DataField("reagent", customTypeSerializer:typeof(PrototypeIdSerializer<ReagentPrototype>))] [DataField("reagent", customTypeSerializer:typeof(PrototypeIdSerializer<ReagentPrototype>))]
public string? Reagent = null; public string? Reagent = null;
// TODO use ReagentId
/// <summary> /// <summary>
/// The metabolism group to remove, if the reagent satisfies any. /// The metabolism group to remove, if the reagent satisfies any.
@@ -38,8 +39,8 @@ namespace Content.Server.Chemistry.ReagentEffects
if (Reagent != null) if (Reagent != null)
{ {
if (amount < 0 && args.Source.ContainsReagent(Reagent)) if (amount < 0 && args.Source.ContainsPrototype(Reagent))
solutionSys.TryRemoveReagent(args.SolutionEntity, args.Source, Reagent, -amount); solutionSys.RemoveReagent(args.SolutionEntity, args.Source, Reagent, -amount);
if (amount > 0) if (amount > 0)
solutionSys.TryAddReagent(args.SolutionEntity, args.Source, Reagent, amount, out _); solutionSys.TryAddReagent(args.SolutionEntity, args.Source, Reagent, amount, out _);
} }
@@ -48,13 +49,13 @@ namespace Content.Server.Chemistry.ReagentEffects
var prototypeMan = IoCManager.Resolve<IPrototypeManager>(); var prototypeMan = IoCManager.Resolve<IPrototypeManager>();
foreach (var quant in args.Source.Contents.ToArray()) foreach (var quant in args.Source.Contents.ToArray())
{ {
var proto = prototypeMan.Index<ReagentPrototype>(quant.ReagentId); var proto = prototypeMan.Index<ReagentPrototype>(quant.Reagent.Prototype);
if (proto.Metabolisms != null && proto.Metabolisms.ContainsKey(Group)) if (proto.Metabolisms != null && proto.Metabolisms.ContainsKey(Group))
{ {
if (amount < 0) if (amount < 0)
solutionSys.TryRemoveReagent(args.SolutionEntity, args.Source, quant.ReagentId, amount); solutionSys.RemoveReagent(args.SolutionEntity, args.Source, quant.Reagent, amount);
if (amount > 0) if (amount > 0)
solutionSys.TryAddReagent(args.SolutionEntity, args.Source, quant.ReagentId, amount, out _); solutionSys.TryAddReagent(args.SolutionEntity, args.Source, quant.Reagent, amount, out _);
} }
} }
} }

View File

@@ -55,7 +55,8 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
var oldProgress = component.Progress.ShallowClone(); var oldProgress = component.Progress.ShallowClone();
component.Progress.Clear(); 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<ReagentPrototype>(PuddleSystem.EvaporationReagent).SubstanceColor] = water.Float(); component.Progress[_prototype.Index<ReagentPrototype>(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 // 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 // No material
if (available == FixedPoint2.Zero) if (available == FixedPoint2.Zero)

View File

@@ -25,7 +25,7 @@ public sealed partial class PuddleSystem
return; return;
} }
if (solution.ContainsReagent(EvaporationReagent)) if (solution.ContainsPrototype(EvaporationReagent))
{ {
var evaporation = AddComp<EvaporationComponent>(uid); var evaporation = AddComp<EvaporationComponent>(uid);
evaporation.NextTick = _timing.CurTime + EvaporationCooldown; evaporation.NextTick = _timing.CurTime + EvaporationCooldown;
@@ -51,7 +51,7 @@ public sealed partial class PuddleSystem
continue; continue;
var reagentTick = evaporation.EvaporationAmount * EvaporationCooldown.TotalSeconds; var reagentTick = evaporation.EvaporationAmount * EvaporationCooldown.TotalSeconds;
_solutionContainerSystem.TryRemoveReagent(uid, puddleSolution, EvaporationReagent, reagentTick); _solutionContainerSystem.RemoveReagent(uid, puddleSolution, EvaporationReagent, reagentTick);
// Despawn if we're done // Despawn if we're done
if (puddleSolution.Volume == FixedPoint2.Zero) if (puddleSolution.Volume == FixedPoint2.Zero)
@@ -65,6 +65,6 @@ public sealed partial class PuddleSystem
public bool CanFullyEvaporate(Solution solution) public bool CanFullyEvaporate(Solution solution)
{ {
return solution.Contents.Count == 1 && solution.ContainsReagent(EvaporationReagent); return solution.Contents.Count == 1 && solution.ContainsPrototype(EvaporationReagent);
} }
} }

View File

@@ -56,6 +56,17 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
[Dependency] private readonly SlowContactsSystem _slowContacts = default!; [Dependency] private readonly SlowContactsSystem _slowContacts = default!;
[Dependency] private readonly TileFrictionController _tile = default!; [Dependency] private readonly TileFrictionController _tile = default!;
[ValidatePrototypeId<ReagentPrototype>]
private const string Blood = "Blood";
[ValidatePrototypeId<ReagentPrototype>]
private const string Slime = "Slime";
[ValidatePrototypeId<ReagentPrototype>]
private const string SpiderBlood = "SpiderBlood";
private static string[] _standoutReagents = new[] { Blood, Slime, SpiderBlood };
public static float PuddleVolume = 1000; public static float PuddleVolume = 1000;
// Using local deletion queue instead of the standard queue so that we can easily "undelete" if a puddle // 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 // Make blood stand out more
// Kinda EH // Kinda EH
// Could potentially do alpha per-solution but future problem. // 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); 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; continue;
var interpolateValue = quantity.Float() / solution.Volume.Float(); 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 amountRequired = FixedPoint2.New(component.OverflowVolume.Float() * LowThreshold);
var slipperyAmount = FixedPoint2.Zero; var slipperyAmount = FixedPoint2.Zero;
foreach (var reagent in solution.Contents) foreach (var (reagent, quantity) in solution.Contents)
{ {
var reagentProto = _prototypeManager.Index<ReagentPrototype>(reagent.ReagentId); var reagentProto = _prototypeManager.Index<ReagentPrototype>(reagent.Prototype);
if (reagentProto.Slippery) if (reagentProto.Slippery)
{ {
slipperyAmount += reagent.Quantity; slipperyAmount += quantity;
if (slipperyAmount > amountRequired) if (slipperyAmount > amountRequired)
{ {
@@ -331,9 +342,9 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
private void UpdateSlow(EntityUid uid, Solution solution) private void UpdateSlow(EntityUid uid, Solution solution)
{ {
var maxViscosity = 0f; var maxViscosity = 0f;
foreach (var reagent in solution.Contents) foreach (var (reagent, _) in solution.Contents)
{ {
var reagentProto = _prototypeManager.Index<ReagentPrototype>(reagent.ReagentId); var reagentProto = _prototypeManager.Index<ReagentPrototype>(reagent.Prototype);
maxViscosity = Math.Max(maxViscosity, reagentProto.Viscosity); maxViscosity = Math.Max(maxViscosity, reagentProto.Viscosity);
} }
if (maxViscosity > 0) if (maxViscosity > 0)
@@ -362,7 +373,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
{ {
args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating")); 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")); args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating-partial"));
} }
@@ -588,15 +599,15 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
if (tileReact) if (tileReact)
{ {
// First, do all tile reactions // 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 (reagent, quantity) = solution.Contents[i];
var proto = _prototypeManager.Index<ReagentPrototype>(reagentId); var proto = _prototypeManager.Index<ReagentPrototype>(reagent.Prototype);
var removed = proto.ReactionTile(tileRef, quantity); var removed = proto.ReactionTile(tileRef, quantity);
if (removed <= FixedPoint2.Zero) if (removed <= FixedPoint2.Zero)
continue; continue;
solution.RemoveReagent(reagentId, removed); solution.RemoveReagent(reagent, removed);
} }
} }

View File

@@ -113,6 +113,8 @@ namespace Content.Server.Kitchen.EntitySystems
private void SubtractContents(MicrowaveComponent component, FoodRecipePrototype recipe) private void SubtractContents(MicrowaveComponent component, FoodRecipePrototype recipe)
{ {
// TODO Turn recipe.IngredientsReagents into a ReagentQuantity[]
var totalReagentsToRemove = new Dictionary<string, FixedPoint2>(recipe.IngredientsReagents); var totalReagentsToRemove = new Dictionary<string, FixedPoint2>(recipe.IngredientsReagents);
// this is spaghetti ngl // this is spaghetti ngl
@@ -130,10 +132,7 @@ namespace Content.Server.Kitchen.EntitySystems
if (!totalReagentsToRemove.ContainsKey(reagent)) if (!totalReagentsToRemove.ContainsKey(reagent))
continue; continue;
if (!solution.ContainsReagent(reagent)) var quant = solution.GetTotalPrototypeQuantity(reagent);
continue;
var quant = solution.GetReagentQuantity(reagent);
if (quant >= totalReagentsToRemove[reagent]) if (quant >= totalReagentsToRemove[reagent])
{ {
@@ -145,7 +144,7 @@ namespace Content.Server.Kitchen.EntitySystems
totalReagentsToRemove[reagent] -= quant; 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<string, int>(); var solidsDict = new Dictionary<string, int>();
var reagentDict = new Dictionary<string, FixedPoint2>(); var reagentDict = new Dictionary<string, FixedPoint2>();
// TODO use lists of Reagent quantities instead of reagent prototype ids.
foreach (var item in component.Storage.ContainedEntities) foreach (var item in component.Storage.ContainedEntities)
{ {
// special behavior when being microwaved ;) // special behavior when being microwaved ;)
@@ -370,12 +371,12 @@ namespace Content.Server.Kitchen.EntitySystems
foreach (var (_, solution) in solMan.Solutions) foreach (var (_, solution) in solMan.Solutions)
{ {
foreach (var reagent in solution.Contents) foreach (var (reagent, quantity) in solution.Contents)
{ {
if (reagentDict.ContainsKey(reagent.ReagentId)) if (reagentDict.ContainsKey(reagent.Prototype))
reagentDict[reagent.ReagentId] += reagent.Quantity; reagentDict[reagent.Prototype] += quantity;
else 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) foreach (var reagent in recipe.IngredientsReagents)
{ {
// TODO Turn recipe.IngredientsReagents into a ReagentQuantity[]
if (!reagents.ContainsKey(reagent.Key)) if (!reagents.ContainsKey(reagent.Key))
return (recipe, 0); return (recipe, 0);

View File

@@ -220,14 +220,16 @@ public sealed class MaterialReclaimerSystem : SharedMaterialReclaimerSystem
if (!Resolve(reclaimer, ref reclaimerComponent, ref xform)) if (!Resolve(reclaimer, ref reclaimerComponent, ref xform))
return; return;
var overflow = new Solution(); efficiency *= reclaimerComponent.Efficiency;
var totalChemicals = new Dictionary<string, FixedPoint2>();
var totalChemicals = new Solution();
if (Resolve(item, ref composition, false)) if (Resolve(item, ref composition, false))
{ {
foreach (var (key, value) in composition.ChemicalComposition) 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) foreach (var quantity in solution.Contents)
{ {
totalChemicals[quantity.ReagentId] = totalChemicals.AddReagent(quantity.Reagent.Prototype, quantity.Quantity * efficiency, false);
totalChemicals.GetValueOrDefault(quantity.ReagentId) + quantity.Quantity;
} }
} }
} }
foreach (var (reagent, amount) in totalChemicals) _solutionContainer.TryTransferSolution(reclaimer, reclaimerComponent.OutputSolution, totalChemicals, totalChemicals.Volume);
if (totalChemicals.Volume > 0)
{ {
var outputAmount = amount * efficiency * reclaimerComponent.Efficiency; _puddle.TrySpillAt(reclaimer, totalChemicals, out _, transformComponent: xform);
_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);
} }
} }
} }

View File

@@ -1,12 +1,8 @@
using System.Threading;
using Content.Server.Nutrition.EntitySystems; using Content.Server.Nutrition.EntitySystems;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.Atmos; using Content.Shared.Atmos;
/// <summary> namespace Content.Server.Nutrition.Components // Vapes are very nutritious.
/// Component for vapes
/// </summary>
namespace Content.Server.Nutrition.Components
{ {
[RegisterComponent, Access(typeof(SmokingSystem))] [RegisterComponent, Access(typeof(SmokingSystem))]
public sealed partial class VapeComponent : Component public sealed partial class VapeComponent : Component
@@ -23,6 +19,7 @@ namespace Content.Server.Nutrition.Components
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
public float ExplosionIntensity { get; set; } = 2.5f; public float ExplosionIntensity { get; set; } = 2.5f;
// TODO use RiggableComponent.
[DataField("explodeOnUse")] [DataField("explodeOnUse")]
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
public bool ExplodeOnUse { get; set; } = false; public bool ExplodeOnUse { get; set; } = false;
@@ -42,10 +39,9 @@ namespace Content.Server.Nutrition.Components
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
public float ReductionFactor { get; set; } = 300f; public float ReductionFactor { get; set; } = 300f;
// TODO when this gets fixed, use prototype serializers
[DataField("solutionNeeded")] [DataField("solutionNeeded")]
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
public string SolutionNeeded = "Water"; public string SolutionNeeded = "Water";
public CancellationTokenSource? CancelToken;
} }
} }

View File

@@ -101,7 +101,7 @@ public sealed class DrinkSystem : EntitySystem
var total = 0f; var total = 0f;
foreach (var quantity in solution.Contents) foreach (var quantity in solution.Contents)
{ {
var reagent = _proto.Index<ReagentPrototype>(quantity.ReagentId); var reagent = _proto.Index<ReagentPrototype>(quantity.Reagent.Prototype);
if (reagent.Metabolisms == null) if (reagent.Metabolisms == null)
continue; continue;

View File

@@ -84,9 +84,9 @@ public sealed class FlavorProfileSystem : EntitySystem
private HashSet<string> GetFlavorsFromReagents(Solution solution, int desiredAmount, HashSet<string>? toIgnore = null) private HashSet<string> GetFlavorsFromReagents(Solution solution, int desiredAmount, HashSet<string>? toIgnore = null)
{ {
var flavors = new HashSet<string>(); var flavors = new HashSet<string>();
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; continue;
} }
@@ -96,15 +96,14 @@ public sealed class FlavorProfileSystem : EntitySystem
break; break;
} }
var proto = _prototypeManager.Index<ReagentPrototype>(reagent.ReagentId);
// don't care if the quantity is negligible // don't care if the quantity is negligible
if (reagent.Quantity < proto.FlavorMinimum) if (quantity < reagent.FlavorMinimum)
{ {
continue; continue;
} }
if (proto.Flavor != null) if (reagent.Flavor != null)
flavors.Add(proto.Flavor); flavors.Add(reagent.Flavor);
} }
return flavors; return flavors;

View File

@@ -45,10 +45,11 @@ namespace Content.Server.Nutrition.EntitySystems
if (!args.CanReach if (!args.CanReach
|| solution == null || solution == null
|| comp.CancelToken != null || !HasComp<BloodstreamComponent>(args.Target)
|| !TryComp<BloodstreamComponent>(args.Target, out var _)
|| _foodSystem.IsMouthBlocked(args.Target.Value, args.User)) || _foodSystem.IsMouthBlocked(args.Target.Value, args.User))
{
return; return;
}
if (solution.Contents.Count == 0) if (solution.Contents.Count == 0)
{ {
@@ -72,9 +73,14 @@ namespace Content.Server.Nutrition.EntitySystems
} }
else 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) foreach (var name in solution.Contents)
{ {
if (name.ReagentId != comp.SolutionNeeded) if (name.Reagent.Prototype != comp.SolutionNeeded)
{ {
exploded = true; exploded = true;
_explosionSystem.QueueExplosion(uid, "Default", comp.ExplosionIntensity, 0.5f, 3, canCreateVacuum: false); _explosionSystem.QueueExplosion(uid, "Default", comp.ExplosionIntensity, 0.5f, 3, canCreateVacuum: false);
@@ -106,8 +112,6 @@ namespace Content.Server.Nutrition.EntitySystems
if (!exploded) if (!exploded)
{ {
comp.CancelToken = new CancellationTokenSource();
var vapeDoAfterEvent = new VapeDoAfterEvent(solution, forced); var vapeDoAfterEvent = new VapeDoAfterEvent(solution, forced);
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(args.User, delay, vapeDoAfterEvent, uid, target: args.Target, used: uid) _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) private void OnVapeDoAfter(EntityUid uid, VapeComponent comp, VapeDoAfterEvent args)
{ {
if (args.Cancelled)
{
comp.CancelToken = null;
return;
}
comp.CancelToken = null;
if (args.Handled if (args.Handled
|| args.Args.Target == null) || args.Args.Target == null)
return; return;

View File

@@ -1,11 +1,20 @@
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint;
namespace Content.Server.Power.Components; namespace Content.Server.Power.Components;
[RegisterComponent] [RegisterComponent]
public sealed partial class RiggableComponent : Component public sealed partial class RiggableComponent : Component
{ {
public const string SolutionName = "battery";
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
[DataField("isRigged")] [DataField("isRigged")]
public bool 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);
} }

View File

@@ -4,6 +4,7 @@ using Content.Server.Explosion.EntitySystems;
using Content.Server.Kitchen.Components; using Content.Server.Kitchen.Components;
using Content.Server.Power.Components; using Content.Server.Power.Components;
using Content.Server.Stunnable.Components; using Content.Server.Stunnable.Components;
using Content.Shared.Chemistry.Components;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.Rejuvenate; using Content.Shared.Rejuvenate;
@@ -47,27 +48,19 @@ public sealed class RiggableSystem : EntitySystem
private void OnSolutionChanged(EntityUid uid, RiggableComponent component, SolutionChangedEvent args) private void OnSolutionChanged(EntityUid uid, RiggableComponent component, SolutionChangedEvent args)
{ {
if (TryComp<BatteryComponent>(uid, out var battery)) if (args.SolutionId != component.Solution)
{ return;
IsRigged(uid, args);
}
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."); _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<RiggableComponent>(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) public void Explode(EntityUid uid, BatteryComponent? battery = null, EntityUid? cause = null)
{ {
if (!Resolve(uid, ref battery)) if (!Resolve(uid, ref battery))

View File

@@ -81,7 +81,7 @@ public sealed class GeneratorSystem : SharedGeneratorSystem
foreach (var reagentQuantity in solution) foreach (var reagentQuantity in solution)
{ {
if (reagentQuantity.ReagentId != component.Reagent) if (reagentQuantity.Reagent.Prototype != component.Reagent)
{ {
args.Clogged = true; args.Clogged = true;
return; return;
@@ -94,7 +94,7 @@ public sealed class GeneratorSystem : SharedGeneratorSystem
if (!_solutionContainer.TryGetSolution(uid, component.Solution, out var solution)) if (!_solutionContainer.TryGetSolution(uid, component.Solution, out var solution))
return; return;
var availableReagent = solution.GetReagentQuantity(component.Reagent).Value; var availableReagent = solution.GetTotalPrototypeQuantity(component.Reagent).Value;
var toRemove = RemoveFractionalFuel( var toRemove = RemoveFractionalFuel(
ref component.FractionalReagent, ref component.FractionalReagent,
args.FuelUsed, args.FuelUsed,
@@ -112,8 +112,8 @@ public sealed class GeneratorSystem : SharedGeneratorSystem
if (!_solutionContainer.TryGetSolution(uid, component.Solution, out var solution)) if (!_solutionContainer.TryGetSolution(uid, component.Solution, out var solution))
return; return;
var reagent = component.FractionalReagent * FixedPoint2.Epsilon.Float() var availableReagent = solution.GetTotalPrototypeQuantity(component.Reagent).Float();
+ solution.GetReagentQuantity(component.Reagent).Float(); var reagent = component.FractionalReagent * FixedPoint2.Epsilon.Float() + availableReagent;
args.Fuel = reagent * component.Multiplier; args.Fuel = reagent * component.Multiplier;
} }

View File

@@ -58,7 +58,7 @@ namespace Content.Server.Tools
|| !_solutionContainerSystem.TryGetSolution(uid, welder.FuelSolution, out var fuelSolution, solutionContainer)) || !_solutionContainerSystem.TryGetSolution(uid, welder.FuelSolution, out var fuelSolution, solutionContainer))
return (FixedPoint2.Zero, FixedPoint2.Zero); 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, 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)) if (!_solutionContainerSystem.TryGetSolution(uid, welder.FuelSolution, out var solution, solutionContainer))
return false; return false;
var fuel = solution.GetReagentQuantity(welder.FuelReagent); var fuel = solution.GetTotalPrototypeQuantity(welder.FuelReagent);
// Not enough fuel to lit welder. // Not enough fuel to lit welder.
if (fuel == FixedPoint2.Zero || fuel < welder.FuelLitCost) if (fuel == FixedPoint2.Zero || fuel < welder.FuelLitCost)
@@ -307,7 +307,7 @@ namespace Content.Server.Tools
solution.RemoveReagent(welder.FuelReagent, welder.FuelConsumption * _welderTimer); 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); TryTurnWelderOff(tool, null, welder);
_entityManager.Dirty(welder); _entityManager.Dirty(welder);

View File

@@ -3,10 +3,8 @@ using Content.Shared.FixedPoint;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using System.Collections; using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
namespace Content.Shared.Chemistry.Components namespace Content.Shared.Chemistry.Components
@@ -16,12 +14,12 @@ namespace Content.Shared.Chemistry.Components
/// </summary> /// </summary>
[Serializable, NetSerializable] [Serializable, NetSerializable]
[DataDefinition] [DataDefinition]
public sealed partial class Solution : IEnumerable<Solution.ReagentQuantity>, ISerializationHooks public sealed partial class Solution : IEnumerable<ReagentQuantity>, ISerializationHooks
{ {
// This is a list because it is actually faster to add and remove reagents from // 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, // a list than a dictionary, though contains-reagent checks are slightly slower,
[DataField("reagents")] [DataField("reagents")]
public List<ReagentQuantity> Contents = new(2); public List<ReagentQuantity> Contents;
/// <summary> /// <summary>
/// The calculated total volume of all reagents in the solution (ex. Total volume of liquid in beaker). /// 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
/// <summary> /// <summary>
/// The total heat capacity of all reagents in the solution. /// The total heat capacity of all reagents in the solution.
/// </summary> /// </summary>
[ViewVariables] [ViewVariables] private float _heatCapacity;
private float _heatCapacity;
/// <summary> /// <summary>
/// If true, then <see cref="_heatCapacity"/> needs to be recomputed. /// If true, then <see cref="_heatCapacity"/> needs to be recomputed.
/// </summary> /// </summary>
[ViewVariables] [ViewVariables] private bool _heatCapacityDirty = true;
private bool _heatCapacityDirty = true;
public void UpdateHeatCapacity(IPrototypeManager? protoMan) public void UpdateHeatCapacity(IPrototypeManager? protoMan)
{ {
@@ -101,9 +97,10 @@ namespace Content.Shared.Chemistry.Components
DebugTools.Assert(_heatCapacityDirty); DebugTools.Assert(_heatCapacityDirty);
_heatCapacityDirty = false; _heatCapacityDirty = false;
_heatCapacity = 0; _heatCapacity = 0;
foreach (var reagent in Contents) foreach (var (reagent, quantity) in Contents)
{ {
_heatCapacity += (float) reagent.Quantity * protoMan.Index<ReagentPrototype>(reagent.ReagentId).SpecificHeat; _heatCapacity += (float) quantity *
protoMan.Index<ReagentPrototype>(reagent.Prototype).SpecificHeat;
} }
} }
@@ -137,11 +134,11 @@ namespace Content.Shared.Chemistry.Components
/// <summary> /// <summary>
/// Constructs a solution containing 100% of a reagent (ex. A beaker of pure water). /// Constructs a solution containing 100% of a reagent (ex. A beaker of pure water).
/// </summary> /// </summary>
/// <param name="reagentId">The prototype ID of the reagent to add.</param> /// <param name="prototype">The prototype ID of the reagent to add.</param>
/// <param name="quantity">The quantity in milli-units.</param> /// <param name="quantity">The quantity in milli-units.</param>
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<ReagentQuantity> reagents, bool setMaxVol = true) public Solution(IEnumerable<ReagentQuantity> reagents, bool setMaxVol = true)
@@ -173,7 +170,6 @@ namespace Content.Shared.Chemistry.Components
return new Solution(this); return new Solution(this);
} }
[AssertionMethod] [AssertionMethod]
public void ValidateSolution() public void ValidateSolution()
{ {
@@ -185,8 +181,8 @@ namespace Content.Shared.Chemistry.Components
// All reagents have at least some reagent present. // All reagents have at least some reagent present.
DebugTools.Assert(!Contents.Any(x => x.Quantity <= FixedPoint2.Zero)); DebugTools.Assert(!Contents.Any(x => x.Quantity <= FixedPoint2.Zero));
// No duplicate reagent iDs // No duplicate reagents iDs
DebugTools.Assert(Contents.Select(x => x.ReagentId).ToHashSet().Count() == Contents.Count); DebugTools.Assert(Contents.Select(x => x.Reagent).ToHashSet().Count == Contents.Count);
// If it isn't flagged as dirty, check heat capacity is correct. // If it isn't flagged as dirty, check heat capacity is correct.
if (!_heatCapacityDirty) if (!_heatCapacityDirty)
@@ -211,33 +207,101 @@ namespace Content.Shared.Chemistry.Components
MaxVolume = Volume; 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 true;
} }
return false; 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) if (reagent == id)
{
quantity = reagent.Quantity;
return true; return true;
} }
}
quantity = FixedPoint2.New(0);
return false; 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;
}
}
/// <summary>
/// Get the volume/quantity of a single reagent in the solution.
/// </summary>
[Pure]
public FixedPoint2 GetReagentQuantity(ReagentId id)
{
return GetReagent(id).Quantity;
}
/// <summary>
/// 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 <see cref="GetReagentQuantity"/>
/// </summary>
[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) if (Contents.Count == 0)
return null; return null;
@@ -252,15 +316,23 @@ namespace Content.Shared.Chemistry.Components
} }
} }
return max.ReagentId!; return max.Reagent;
} }
/// <summary> /// <summary>
/// Adds a given quantity of a reagent directly into the solution. /// Adds a given quantity of a reagent directly into the solution.
/// </summary> /// </summary>
/// <param name="reagentId">The prototype ID of the reagent to add.</param> /// <param name="prototype">The prototype ID of the reagent to add.</param>
/// <param name="quantity">The quantity in milli-units.</param> /// <param name="quantity">The quantity in milli-units.</param>
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);
/// <summary>
/// Adds a given quantity of a reagent directly into the solution.
/// </summary>
/// <param name="id">The reagent to add.</param>
/// <param name="quantity">The quantity in milli-units.</param>
public void AddReagent(ReagentId id, FixedPoint2 quantity, bool dirtyHeatCap = true)
{ {
if (quantity <= 0) if (quantity <= 0)
{ {
@@ -272,42 +344,45 @@ namespace Content.Shared.Chemistry.Components
_heatCapacityDirty |= dirtyHeatCap; _heatCapacityDirty |= dirtyHeatCap;
for (var i = 0; i < Contents.Count; i++) for (var i = 0; i < Contents.Count; i++)
{ {
var reagent = Contents[i]; var (reagent, existingQuantity) = Contents[i];
if (reagent.ReagentId != reagentId) if (reagent != id)
continue; continue;
Contents[i] = new ReagentQuantity(reagentId, reagent.Quantity + quantity); Contents[i] = new ReagentQuantity(id, existingQuantity + quantity);
ValidateSolution(); ValidateSolution();
return; return;
} }
Contents.Add(new ReagentQuantity(reagentId, quantity)); Contents.Add(new ReagentQuantity(id, quantity));
ValidateSolution(); ValidateSolution();
} }
/// <summary> /// <summary>
/// Adds a given quantity of a reagent directly into the solution. /// Adds a given quantity of a reagent directly into the solution.
/// </summary> /// </summary>
/// <param name="proto">The prototype of the reagent to add.</param> /// <param name="reagentId">The reagent to add.</param>
/// <param name="quantity">The quantity in milli-units.</param> /// <param name="quantity">The quantity in milli-units.</param>
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; _heatCapacity += quantity.Float() * proto.SpecificHeat;
} }
public void AddReagent(ReagentQuantity reagentQuantity)
=> AddReagent(reagentQuantity.Reagent, reagentQuantity.Quantity);
/// <summary> /// <summary>
/// Adds a given quantity of a reagent directly into the solution. /// Adds a given quantity of a reagent directly into the solution.
/// </summary> /// </summary>
/// <param name="proto">The prototype of the reagent to add.</param> /// <param name="proto">The prototype of the reagent to add.</param>
/// <param name="quantity">The quantity in milli-units.</param> /// <param name="quantity">The quantity in milli-units.</param>
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) if (_heatCapacityDirty)
UpdateHeatCapacity(protoMan); UpdateHeatCapacity(protoMan);
var totalThermalEnergy = Temperature * _heatCapacity + temperature * proto.SpecificHeat; var totalThermalEnergy = Temperature * _heatCapacity + temperature * proto.SpecificHeat;
AddReagent(proto, quantity); AddReagent(new ReagentId(proto.ID, data), quantity);
Temperature = _heatCapacity == 0 ? 0 : totalThermalEnergy / _heatCapacity; Temperature = _heatCapacity == 0 ? 0 : totalThermalEnergy / _heatCapacity;
} }
@@ -333,7 +408,7 @@ namespace Content.Shared.Chemistry.Components
for (int i = 0; i < Contents.Count; i++) for (int i = 0; i < Contents.Count; i++)
{ {
var old = Contents[i]; var old = Contents[i];
Contents[i] = new ReagentQuantity(old.ReagentId, old.Quantity * scale); Contents[i] = new ReagentQuantity(old.Reagent, old.Quantity * scale);
} }
ValidateSolution(); ValidateSolution();
} }
@@ -362,7 +437,7 @@ namespace Content.Shared.Chemistry.Components
Contents.RemoveSwap(i); Contents.RemoveSwap(i);
else else
{ {
Contents[i] = new ReagentQuantity(old.ReagentId, newQuantity); Contents[i] = new ReagentQuantity(old.Reagent, newQuantity);
Volume += newQuantity; Volume += newQuantity;
} }
} }
@@ -371,43 +446,24 @@ namespace Content.Shared.Chemistry.Components
ValidateSolution(); ValidateSolution();
} }
/// <summary>
/// Returns the amount of a single reagent inside the solution.
/// </summary>
/// <param name="reagentId">The prototype ID of the reagent to add.</param>
/// <returns>The quantity in milli-units.</returns>
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;
}
/// <summary> /// <summary>
/// Attempts to remove an amount of reagent from the solution. /// Attempts to remove an amount of reagent from the solution.
/// </summary> /// </summary>
/// <param name="reagentId">The reagent to be removed.</param> /// <param name="toRemove">The reagent to be removed.</param>
/// <param name="quantity">The amount of reagent to remove.</param>
/// <returns>How much reagent was actually removed. Zero if the reagent is not present on the solution.</returns> /// <returns>How much reagent was actually removed. Zero if the reagent is not present on the solution.</returns>
public FixedPoint2 RemoveReagent(string reagentId, FixedPoint2 quantity) public FixedPoint2 RemoveReagent(ReagentQuantity toRemove)
{ {
if (quantity <= FixedPoint2.Zero) if (toRemove.Quantity <= FixedPoint2.Zero)
return FixedPoint2.Zero; return FixedPoint2.Zero;
for (var i = 0; i < Contents.Count; i++) 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; continue;
var curQuantity = reagent.Quantity; var newQuantity = curQuantity - toRemove.Quantity;
var newQuantity = curQuantity - quantity;
_heatCapacityDirty = true; _heatCapacityDirty = true;
if (newQuantity <= 0) if (newQuantity <= 0)
@@ -418,16 +474,38 @@ namespace Content.Shared.Chemistry.Components
return curQuantity; return curQuantity;
} }
Contents[i] = new ReagentQuantity(reagentId, newQuantity); Contents[i] = new ReagentQuantity(reagent, newQuantity);
Volume -= quantity; Volume -= toRemove.Quantity;
ValidateSolution(); ValidateSolution();
return quantity; return toRemove.Quantity;
} }
// Reagent is not on the solution... // Reagent is not on the solution...
return FixedPoint2.Zero; return FixedPoint2.Zero;
} }
/// <summary>
/// Attempts to remove an amount of reagent from the solution.
/// </summary>
/// <param name="prototype">The prototype of the reagent to be removed.</param>
/// <param name="quantity">The amount of reagent to remove.</param>
/// <returns>How much reagent was actually removed. Zero if the reagent is not present on the solution.</returns>
public FixedPoint2 RemoveReagent(string prototype, FixedPoint2 quantity, ReagentData? data = null)
{
return RemoveReagent(new ReagentQuantity(prototype, quantity, data));
}
/// <summary>
/// Attempts to remove an amount of reagent from the solution.
/// </summary>
/// <param name="reagentId">The reagent to be removed.</param>
/// <param name="quantity">The amount of reagent to remove.</param>
/// <returns>How much reagent was actually removed. Zero if the reagent is not present on the solution.</returns>
public FixedPoint2 RemoveReagent(ReagentId reagentId, FixedPoint2 quantity)
{
return RemoveReagent(new ReagentQuantity(reagentId, quantity));
}
public void RemoveAllSolution() public void RemoveAllSolution()
{ {
Contents.Clear(); Contents.Clear();
@@ -437,21 +515,33 @@ namespace Content.Shared.Chemistry.Components
} }
/// <summary> /// <summary>
/// Splits a solution without the specified reagent. /// Splits a solution without the specified reagent prototypes.
/// </summary> /// </summary>
public Solution SplitSolutionWithout(FixedPoint2 toTake, params string[] without) public Solution SplitSolutionWithout(FixedPoint2 toTake, params string[] excludedPrototypes)
{ {
var existing = new FixedPoint2[without.Length]; // First remove the blacklisted prototypes
for (var i = 0; i < without.Length; i++) List<ReagentQuantity> excluded = new();
foreach (var id in excludedPrototypes)
{ {
TryGetReagent(without[i], out existing[i]); foreach (var tuple in Contents)
RemoveReagent(without[i], existing[i]); {
if (tuple.Reagent.Prototype != id)
continue;
excluded.Add(tuple);
RemoveReagent(tuple);
break;
}
} }
// Then split the solution
var sol = SplitSolution(toTake); var sol = SplitSolution(toTake);
for (var i = 0; i < without.Length; i++) // Then re-add the excluded reagents to the original solution.
AddReagent(without[i], existing[i]); foreach (var reagent in excluded)
{
AddReagent(reagent);
}
return sol; 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. 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. // 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) if (split <= 0)
{ {
effVol -= reagent.Quantity.Value; effVol -= quantity.Value;
DebugTools.Assert(split == 0, "Negative solution quantity while splitting? Long/int overflow?"); DebugTools.Assert(split == 0, "Negative solution quantity while splitting? Long/int overflow?");
continue; continue;
} }
var splitQuantity = FixedPoint2.FromCents((int) split); var splitQuantity = FixedPoint2.FromCents((int) split);
var newQuantity = reagent.Quantity - splitQuantity; var newQuantity = quantity - splitQuantity;
DebugTools.Assert(newQuantity >= 0); DebugTools.Assert(newQuantity >= 0);
if (newQuantity > FixedPoint2.Zero) if (newQuantity > FixedPoint2.Zero)
Contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity); Contents[i] = new ReagentQuantity(reagent, newQuantity);
else else
Contents.RemoveSwap(i); Contents.RemoveSwap(i);
newSolution.Contents.Add(new ReagentQuantity(reagent.ReagentId, splitQuantity)); newSolution.Contents.Add(new ReagentQuantity(reagent, splitQuantity));
Volume -= splitQuantity; Volume -= splitQuantity;
remaining -= split; remaining -= split;
effVol -= reagent.Quantity.Value; effVol -= quantity.Value;
} }
newSolution.Volume = origVol - Volume; newSolution.Volume = origVol - Volume;
@@ -539,28 +629,28 @@ namespace Content.Shared.Chemistry.Components
var remaining = (long) toTake.Value; var remaining = (long) toTake.Value;
for (var i = Contents.Count - 1; i >= 0; i--)// iterate backwards because of remove swap. 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. // 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) if (split <= 0)
{ {
effVol -= reagent.Quantity.Value; effVol -= quantity.Value;
DebugTools.Assert(split == 0, "Negative solution quantity while splitting? Long/int overflow?"); DebugTools.Assert(split == 0, "Negative solution quantity while splitting? Long/int overflow?");
continue; continue;
} }
var splitQuantity = FixedPoint2.FromCents((int) split); var splitQuantity = FixedPoint2.FromCents((int) split);
var newQuantity = reagent.Quantity - splitQuantity; var newQuantity = quantity - splitQuantity;
if (newQuantity > FixedPoint2.Zero) if (newQuantity > FixedPoint2.Zero)
Contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity); Contents[i] = new ReagentQuantity(reagent, newQuantity);
else else
Contents.RemoveSwap(i); Contents.RemoveSwap(i);
remaining -= split; remaining -= split;
effVol -= reagent.Quantity.Value; effVol -= quantity.Value;
} }
DebugTools.Assert(remaining >= 0); DebugTools.Assert(remaining >= 0);
@@ -594,23 +684,23 @@ namespace Content.Shared.Chemistry.Components
for (var i = 0; i < otherSolution.Contents.Count; i++) for (var i = 0; i < otherSolution.Contents.Count; i++)
{ {
var otherReagent = otherSolution.Contents[i]; var (otherReagent, otherQuantity) = otherSolution.Contents[i];
var found = false; var found = false;
for (var j = 0; j < Contents.Count; j++) for (var j = 0; j < Contents.Count; j++)
{ {
var reagent = Contents[j]; var (reagent, quantity) = Contents[j];
if (reagent.ReagentId == otherReagent.ReagentId) if (reagent == otherReagent)
{ {
found = true; found = true;
Contents[j] = new ReagentQuantity(reagent.ReagentId, reagent.Quantity + otherReagent.Quantity); Contents[j] = new ReagentQuantity(reagent, quantity + otherQuantity);
break; break;
} }
} }
if (!found) 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); var runningTotalQuantity = FixedPoint2.New(0);
bool first = true; 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; continue;
runningTotalQuantity += reagent.Quantity; runningTotalQuantity += quantity;
if (!protoMan.TryIndex(reagent.ReagentId, out ReagentPrototype? proto)) if (!protoMan.TryIndex(reagent.Prototype, out ReagentPrototype? proto))
{ {
continue; continue;
} }
@@ -655,7 +745,7 @@ namespace Content.Shared.Chemistry.Components
continue; continue;
} }
var interpolateValue = reagent.Quantity.Float() / runningTotalQuantity.Float(); var interpolateValue = quantity.Float() / runningTotalQuantity.Float();
mixColor = Color.InterpolateBetween(mixColor, proto.SubstanceColor, interpolateValue); mixColor = Color.InterpolateBetween(mixColor, proto.SubstanceColor, interpolateValue);
} }
return mixColor; return mixColor;
@@ -666,46 +756,6 @@ namespace Content.Shared.Chemistry.Components
return GetColorWithout(protoMan); return GetColorWithout(protoMan);
} }
[Obsolete("Use ReactiveSystem.DoEntityReaction")]
public void DoEntityReaction(EntityUid uid, ReactionMethod method)
{
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<ReactiveSystem>().DoEntityReaction(uid, this, method);
}
[Serializable, NetSerializable]
[DataDefinition]
public readonly partial struct ReagentQuantity: IComparable<ReagentQuantity>
{
[DataField("ReagentId", customTypeSerializer:typeof(PrototypeIdSerializer<ReagentPrototype>), 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 #region Enumeration
public IEnumerator<ReagentQuantity> GetEnumerator() public IEnumerator<ReagentQuantity> GetEnumerator()
@@ -721,6 +771,7 @@ namespace Content.Shared.Chemistry.Components
public void SetContents(IEnumerable<ReagentQuantity> reagents, bool setMaxVol = false) public void SetContents(IEnumerable<ReagentQuantity> reagents, bool setMaxVol = false)
{ {
Volume = 0;
RemoveAllSolution(); RemoveAllSolution();
_heatCapacityDirty = true; _heatCapacityDirty = true;
Contents = new(reagents); Contents = new(reagents);
@@ -735,5 +786,15 @@ namespace Content.Shared.Chemistry.Components
ValidateSolution(); ValidateSolution();
} }
public Dictionary<ReagentPrototype, FixedPoint2> GetReagentPrototypes(IPrototypeManager protoMan)
{
var dict = new Dictionary<ReagentPrototype, FixedPoint2>(Contents.Count);
foreach (var (reagent, quantity) in Contents)
{
var proto = protoMan.Index<ReagentPrototype>(reagent.Prototype);
dict[proto] = quantity + dict.GetValueOrDefault(proto);
}
return dict;
}
} }
} }

View File

@@ -14,13 +14,11 @@ namespace Content.Shared.Chemistry.Dispenser
[Serializable, NetSerializable, Prototype("reagentDispenserInventory")] [Serializable, NetSerializable, Prototype("reagentDispenserInventory")]
public sealed class ReagentDispenserInventoryPrototype : IPrototype public sealed class ReagentDispenserInventoryPrototype : IPrototype
{ {
// TODO use ReagentId
[DataField("inventory", customTypeSerializer: typeof(PrototypeIdListSerializer<ReagentPrototype>))] [DataField("inventory", customTypeSerializer: typeof(PrototypeIdListSerializer<ReagentPrototype>))]
private List<string> _inventory = new(); public List<string> Inventory = new();
[ViewVariables] [ViewVariables, IdDataField]
[IdDataField] public string ID { get; } = default!;
public string ID { get; private set; } = default!;
public List<string> Inventory => _inventory;
} }
} }

View File

@@ -136,7 +136,9 @@ namespace Content.Shared.Chemistry.Reaction
var reactantName = reactantData.Key; var reactantName = reactantData.Key;
var reactantCoefficient = reactantData.Value.Amount; var reactantCoefficient = reactantData.Value.Amount;
if (!solution.TryGetReagent(reactantName, out var reactantQuantity)) var reactantQuantity = solution.GetTotalPrototypeQuantity(reactantName);
if (reactantQuantity <= FixedPoint2.Zero)
return false; return false;
if (reactantData.Value.Catalyst) if (reactantData.Value.Catalyst)
@@ -292,7 +294,7 @@ namespace Content.Shared.Chemistry.Reaction
SortedSet<ReactionPrototype> reactions = new(); SortedSet<ReactionPrototype> reactions = new();
foreach (var reactant in solution.Contents) 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); reactions.UnionWith(reactantReactions);
} }

View File

@@ -19,32 +19,33 @@ namespace Content.Shared.Chemistry
public void DoEntityReaction(EntityUid uid, Solution solution, ReactionMethod method) 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. // We throw if the reagent specified doesn't exist.
ReactionEntity(uid, method, _prototypeManager.Index<ReagentPrototype>(reagentId), reactVolume, source); var proto = _prototypeManager.Index<ReagentPrototype>(reagentQuantity.Reagent.Prototype);
ReactionEntity(uid, method, proto, reagentQuantity, source);
} }
public void ReactionEntity(EntityUid uid, ReactionMethod method, ReagentPrototype reagent, public void ReactionEntity(EntityUid uid, ReactionMethod method, ReagentPrototype proto,
FixedPoint2 reactVolume, Solution? source) ReagentQuantity reagentQuantity, Solution? source)
{ {
if (!EntityManager.TryGetComponent(uid, out ReactiveComponent? reactive)) if (!TryComp(uid, out ReactiveComponent? reactive))
return; return;
// If we have a source solution, use the reagent quantity we have left. Otherwise, use the reaction volume specified. // 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, var args = new ReagentEffectArgs(uid, null, source, proto,
source?.GetReagentQuantity(reagent.ID) ?? reactVolume, EntityManager, method, 1f); source?.GetReagentQuantity(reagentQuantity.Reagent) ?? reagentQuantity.Quantity, EntityManager, method, 1f);
// First, check if the reagent wants to apply any effects. // 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)) if (!val.Methods.Contains(method))
continue; continue;
@@ -64,7 +65,7 @@ namespace Content.Shared.Chemistry
{ {
var entity = args.SolutionEntity; var entity = args.SolutionEntity;
_adminLogger.Add(LogType.ReagentEffect, effect.LogImpact, _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); effect.Effect(args);
@@ -80,7 +81,7 @@ namespace Content.Shared.Chemistry
if (!entry.Methods.Contains(method)) if (!entry.Methods.Contains(method))
continue; continue;
if (entry.Reagents != null && !entry.Reagents.Contains(reagent.ID)) if (entry.Reagents != null && !entry.Reagents.Contains(proto.ID))
continue; continue;
foreach (var effect in entry.Effects) foreach (var effect in entry.Effects)
@@ -92,7 +93,7 @@ namespace Content.Shared.Chemistry
{ {
var entity = args.SolutionEntity; var entity = args.SolutionEntity;
_adminLogger.Add(LogType.ReagentEffect, effect.LogImpact, _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); effect.Effect(args);

View File

@@ -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<ReagentData>
{
/// <summary>
/// Convert to a string representation. This if for logging & debugging. This is not localized and should not be
/// shown to players.
/// </summary>
public virtual string ToString(string prototype, FixedPoint2 quantity)
{
return $"{prototype}:{GetType().Name}:{quantity}";
}
/// <summary>
/// Convert to a string representation. This if for logging & debugging. This is not localized and should not be
/// shown to players.
/// </summary>
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();
}

View File

@@ -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;
/// <summary>
/// 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).
/// </summary>
[Serializable, NetSerializable]
[DataDefinition]
public partial struct ReagentId : IEquatable<ReagentId>
{
// TODO rename data field.
[DataField("ReagentId", customTypeSerializer: typeof(PrototypeIdSerializer<ReagentPrototype>), required: true)]
public string Prototype { get; private set; }
/// <summary>
/// Any additional data that is unique to this reagent type. E.g., for blood this could be DNA data.
/// </summary>
[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);
}
}

View File

@@ -141,7 +141,7 @@ namespace Content.Shared.Chemistry.Reagent
return removed; return removed;
} }
public void ReactionPlant(EntityUid? plantHolder, Solution.ReagentQuantity amount, Solution solution) public void ReactionPlant(EntityUid? plantHolder, ReagentQuantity amount, Solution solution)
{ {
if (plantHolder == null) if (plantHolder == null)
return; return;

View File

@@ -0,0 +1,77 @@
using Content.Shared.FixedPoint;
using Robust.Shared.Serialization;
namespace Content.Shared.Chemistry.Reagent;
/// <summary>
/// Simple struct for storing a <see cref="ReagentId"/> & quantity tuple.
/// </summary>
[Serializable, NetSerializable]
[DataDefinition]
public partial struct ReagentQuantity : IEquatable<ReagentQuantity>
{
[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);
}
}

View File

@@ -1,4 +1,5 @@
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
@@ -43,11 +44,11 @@ namespace Content.Shared.Chemistry
[Serializable, NetSerializable] [Serializable, NetSerializable]
public sealed class ChemMasterReagentAmountButtonMessage : BoundUserInterfaceMessage public sealed class ChemMasterReagentAmountButtonMessage : BoundUserInterfaceMessage
{ {
public readonly string ReagentId; public readonly ReagentId ReagentId;
public readonly ChemMasterReagentAmount Amount; public readonly ChemMasterReagentAmount Amount;
public readonly bool FromBuffer; public readonly bool FromBuffer;
public ChemMasterReagentAmountButtonMessage(string reagentId, ChemMasterReagentAmount amount, bool fromBuffer) public ChemMasterReagentAmountButtonMessage(ReagentId reagentId, ChemMasterReagentAmount amount, bool fromBuffer)
{ {
ReagentId = reagentId; ReagentId = reagentId;
Amount = amount; Amount = amount;
@@ -121,34 +122,29 @@ namespace Content.Shared.Chemistry
/// The container name to show to the player /// The container name to show to the player
/// </summary> /// </summary>
public readonly string DisplayName; public readonly string DisplayName;
/// <summary>
/// Whether the container holds reagents or entities
/// </summary>
public readonly bool HoldsReagents;
/// <summary> /// <summary>
/// The currently used volume of the container /// The currently used volume of the container
/// </summary> /// </summary>
public readonly FixedPoint2 CurrentVolume; public readonly FixedPoint2 CurrentVolume;
/// <summary> /// <summary>
/// The maximum volume of the container /// The maximum volume of the container
/// </summary> /// </summary>
public readonly FixedPoint2 MaxVolume; public readonly FixedPoint2 MaxVolume;
/// <summary>
/// A list of the reagents/entities and their sizes within the container
/// </summary>
// todo: this causes NetSerializer exceptions if it's an IReadOnlyList (which would be preferred)
public readonly List<(string Id, FixedPoint2 Quantity)> Contents;
public ContainerInfo( /// <summary>
string displayName, bool holdsReagents, /// A list of the entities and their sizes within the container
FixedPoint2 currentVolume, FixedPoint2 maxVolume, /// </summary>
List<(string, FixedPoint2)> contents) public List<(string Id, FixedPoint2 Quantity)>? Entities { get; init; }
public List<ReagentQuantity>? Reagents { get; init; }
public ContainerInfo(string displayName, FixedPoint2 currentVolume, FixedPoint2 maxVolume)
{ {
DisplayName = displayName; DisplayName = displayName;
HoldsReagents = holdsReagents;
CurrentVolume = currentVolume; CurrentVolume = currentVolume;
MaxVolume = maxVolume; MaxVolume = maxVolume;
Contents = contents;
} }
} }
@@ -161,7 +157,7 @@ namespace Content.Shared.Chemistry
/// <summary> /// <summary>
/// A list of the reagents and their amounts within the buffer, if applicable. /// A list of the reagents and their amounts within the buffer, if applicable.
/// </summary> /// </summary>
public readonly IReadOnlyList<Solution.ReagentQuantity> BufferReagents; public readonly IReadOnlyList<ReagentQuantity> BufferReagents;
public readonly ChemMasterMode Mode; public readonly ChemMasterMode Mode;
@@ -174,7 +170,7 @@ namespace Content.Shared.Chemistry
public ChemMasterBoundUserInterfaceState( public ChemMasterBoundUserInterfaceState(
ChemMasterMode mode, ContainerInfo? inputContainerInfo, ContainerInfo? outputContainerInfo, ChemMasterMode mode, ContainerInfo? inputContainerInfo, ContainerInfo? outputContainerInfo,
IReadOnlyList<Solution.ReagentQuantity> bufferReagents, FixedPoint2 bufferCurrentVolume, IReadOnlyList<ReagentQuantity> bufferReagents, FixedPoint2 bufferCurrentVolume,
uint selectedPillType, uint pillDosageLimit, bool updateLabel) uint selectedPillType, uint pillDosageLimit, bool updateLabel)
{ {
InputContainerInfo = inputContainerInfo; InputContainerInfo = inputContainerInfo;

View File

@@ -1,3 +1,4 @@
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
namespace Content.Shared.Chemistry namespace Content.Shared.Chemistry
@@ -24,9 +25,9 @@ namespace Content.Shared.Chemistry
[Serializable, NetSerializable] [Serializable, NetSerializable]
public sealed class ReagentDispenserDispenseReagentMessage : BoundUserInterfaceMessage public sealed class ReagentDispenserDispenseReagentMessage : BoundUserInterfaceMessage
{ {
public readonly string ReagentId; public readonly ReagentId ReagentId;
public ReagentDispenserDispenseReagentMessage(string reagentId) public ReagentDispenserDispenseReagentMessage(ReagentId reagentId)
{ {
ReagentId = reagentId; ReagentId = reagentId;
} }
@@ -58,11 +59,11 @@ namespace Content.Shared.Chemistry
/// <summary> /// <summary>
/// A list of the reagents which this dispenser can dispense. /// A list of the reagents which this dispenser can dispense.
/// </summary> /// </summary>
public readonly List<string> Inventory; public readonly List<ReagentId> Inventory;
public readonly ReagentDispenserDispenseAmount SelectedDispenseAmount; public readonly ReagentDispenserDispenseAmount SelectedDispenseAmount;
public ReagentDispenserBoundUserInterfaceState(ContainerInfo? outputContainer, List<string> inventory, ReagentDispenserDispenseAmount selectedDispenseAmount) public ReagentDispenserBoundUserInterfaceState(ContainerInfo? outputContainer, List<ReagentId> inventory, ReagentDispenserDispenseAmount selectedDispenseAmount)
{ {
OutputContainer = outputContainer; OutputContainer = outputContainer;
Inventory = inventory; Inventory = inventory;

View File

@@ -1,4 +1,5 @@
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
namespace Content.Shared.Kitchen.Components namespace Content.Shared.Kitchen.Components
@@ -27,8 +28,8 @@ namespace Content.Shared.Kitchen.Components
[Serializable, NetSerializable] [Serializable, NetSerializable]
public sealed class MicrowaveVaporizeReagentIndexedMessage : BoundUserInterfaceMessage public sealed class MicrowaveVaporizeReagentIndexedMessage : BoundUserInterfaceMessage
{ {
public Solution.ReagentQuantity ReagentQuantity; public ReagentQuantity ReagentQuantity;
public MicrowaveVaporizeReagentIndexedMessage(Solution.ReagentQuantity reagentQuantity) public MicrowaveVaporizeReagentIndexedMessage(ReagentQuantity reagentQuantity)
{ {
ReagentQuantity = reagentQuantity; ReagentQuantity = reagentQuantity;
} }

View File

@@ -33,6 +33,7 @@ namespace Content.Shared.Kitchen
public string Name => Loc.GetString(_name); public string Name => Loc.GetString(_name);
// TODO Turn this into a ReagentQuantity[]
public IReadOnlyDictionary<string, FixedPoint2> IngredientsReagents => _ingsReagents; public IReadOnlyDictionary<string, FixedPoint2> IngredientsReagents => _ingsReagents;
public IReadOnlyDictionary<string, FixedPoint2> IngredientsSolids => _ingsSolids; public IReadOnlyDictionary<string, FixedPoint2> IngredientsSolids => _ingsSolids;

View File

@@ -1,4 +1,5 @@
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
namespace Content.Shared.Kitchen namespace Content.Shared.Kitchen
@@ -84,8 +85,8 @@ namespace Content.Shared.Kitchen
public bool CanJuice; public bool CanJuice;
public bool CanGrind; public bool CanGrind;
public EntityUid[] ChamberContents; public EntityUid[] ChamberContents;
public Solution.ReagentQuantity[]? ReagentQuantities; public ReagentQuantity[]? ReagentQuantities;
public ReagentGrinderInterfaceState(bool isBusy, bool hasBeaker, bool powered, bool canJuice, bool canGrind, EntityUid[] chamberContents, Solution.ReagentQuantity[]? heldBeakerContents) public ReagentGrinderInterfaceState(bool isBusy, bool hasBeaker, bool powered, bool canJuice, bool canGrind, EntityUid[] chamberContents, ReagentQuantity[]? heldBeakerContents)
{ {
IsBusy = isBusy; IsBusy = isBusy;
HasBeakerIn = hasBeaker; HasBeakerIn = hasBeaker;

View File

@@ -27,4 +27,5 @@ public sealed partial class PhysicalCompositionComponent : Component
/// </summary> /// </summary>
[DataField("chemicalComposition", customTypeSerializer: typeof(PrototypeIdDictionarySerializer<FixedPoint2, ReagentPrototype>))] [DataField("chemicalComposition", customTypeSerializer: typeof(PrototypeIdDictionarySerializer<FixedPoint2, ReagentPrototype>))]
public Dictionary<string, FixedPoint2> ChemicalComposition = new(); public Dictionary<string, FixedPoint2> ChemicalComposition = new();
// TODO use ReagentQuantity[]
} }

View File

@@ -8,7 +8,7 @@ using Robust.Shared.Utility;
namespace Content.Tests.Shared.Chemistry; namespace Content.Tests.Shared.Chemistry;
[TestFixture, Parallelizable, TestOf(typeof(Solution))] [TestFixture, Parallelizable, TestOf(typeof(Solution))]
public sealed class Solution_Tests : ContentUnitTest public sealed class SolutionTests : ContentUnitTest
{ {
[OneTimeSetUp] [OneTimeSetUp]
public void Setup() public void Setup()
@@ -21,7 +21,7 @@ public sealed class Solution_Tests : ContentUnitTest
{ {
var solution = new Solution(); var solution = new Solution();
solution.AddReagent("water", FixedPoint2.New(1000)); solution.AddReagent("water", FixedPoint2.New(1000));
var quantity = solution.GetReagentQuantity("water"); var quantity = solution.GetTotalPrototypeQuantity("water");
Assert.That(quantity.Int(), Is.EqualTo(1000)); Assert.That(quantity.Int(), Is.EqualTo(1000));
} }
@@ -44,8 +44,8 @@ public sealed class Solution_Tests : ContentUnitTest
tmp.ScaleSolution(2); tmp.ScaleSolution(2);
Assert.That(tmp.Contents.Count, Is.EqualTo(2)); Assert.That(tmp.Contents.Count, Is.EqualTo(2));
Assert.That(tmp.Volume, Is.EqualTo(FixedPoint2.New(100))); Assert.That(tmp.Volume, Is.EqualTo(FixedPoint2.New(100)));
Assert.That(tmp.GetReagentQuantity("water"), Is.EqualTo(FixedPoint2.New(40))); Assert.That(tmp.GetTotalPrototypeQuantity("water"), Is.EqualTo(FixedPoint2.New(40)));
Assert.That(tmp.GetReagentQuantity("fire"), Is.EqualTo(FixedPoint2.New(60))); Assert.That(tmp.GetTotalPrototypeQuantity("fire"), Is.EqualTo(FixedPoint2.New(60)));
} }
// Test float scaling // Test float scaling
@@ -59,15 +59,15 @@ public sealed class Solution_Tests : ContentUnitTest
tmp.ScaleSolution(2f); tmp.ScaleSolution(2f);
Assert.That(tmp.Contents.Count, Is.EqualTo(2)); Assert.That(tmp.Contents.Count, Is.EqualTo(2));
Assert.That(tmp.Volume, Is.EqualTo(FixedPoint2.New(100))); Assert.That(tmp.Volume, Is.EqualTo(FixedPoint2.New(100)));
Assert.That(tmp.GetReagentQuantity("water"), Is.EqualTo(FixedPoint2.New(40))); Assert.That(tmp.GetTotalPrototypeQuantity("water"), Is.EqualTo(FixedPoint2.New(40)));
Assert.That(tmp.GetReagentQuantity("fire"), Is.EqualTo(FixedPoint2.New(60))); Assert.That(tmp.GetTotalPrototypeQuantity("fire"), Is.EqualTo(FixedPoint2.New(60)));
tmp = solution.Clone(); tmp = solution.Clone();
tmp.ScaleSolution(0.3f); tmp.ScaleSolution(0.3f);
Assert.That(tmp.Contents.Count, Is.EqualTo(2)); Assert.That(tmp.Contents.Count, Is.EqualTo(2));
Assert.That(tmp.Volume, Is.EqualTo(FixedPoint2.New(15))); Assert.That(tmp.Volume, Is.EqualTo(FixedPoint2.New(15)));
Assert.That(tmp.GetReagentQuantity("water"), Is.EqualTo(FixedPoint2.New(6))); Assert.That(tmp.GetTotalPrototypeQuantity("water"), Is.EqualTo(FixedPoint2.New(6)));
Assert.That(tmp.GetReagentQuantity("fire"), Is.EqualTo(FixedPoint2.New(9))); Assert.That(tmp.GetTotalPrototypeQuantity("fire"), Is.EqualTo(FixedPoint2.New(9)));
} }
} }
@@ -75,7 +75,7 @@ public sealed class Solution_Tests : ContentUnitTest
public void ConstructorAddReagent() public void ConstructorAddReagent()
{ {
var solution = new Solution("water", FixedPoint2.New(1000)); 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)); Assert.That(quantity.Int(), Is.EqualTo(1000));
} }
@@ -84,7 +84,7 @@ public sealed class Solution_Tests : ContentUnitTest
public void NonExistingReagentReturnsZero() public void NonExistingReagentReturnsZero()
{ {
var solution = new Solution(); var solution = new Solution();
var quantity = solution.GetReagentQuantity("water"); var quantity = solution.GetTotalPrototypeQuantity("water");
Assert.That(quantity.Int(), Is.EqualTo(0)); Assert.That(quantity.Int(), Is.EqualTo(0));
} }
@@ -94,7 +94,7 @@ public sealed class Solution_Tests : ContentUnitTest
public void AddLessThanZeroReagentReturnsZero() public void AddLessThanZeroReagentReturnsZero()
{ {
var solution = new Solution("water", FixedPoint2.New(-1000)); 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)); Assert.That(quantity.Int(), Is.EqualTo(0));
} }
@@ -106,7 +106,7 @@ public sealed class Solution_Tests : ContentUnitTest
var solution = new Solution(); var solution = new Solution();
solution.AddReagent("water", FixedPoint2.New(1000)); solution.AddReagent("water", FixedPoint2.New(1000));
solution.AddReagent("water", FixedPoint2.New(2000)); solution.AddReagent("water", FixedPoint2.New(2000));
var quantity = solution.GetReagentQuantity("water"); var quantity = solution.GetTotalPrototypeQuantity("water");
Assert.That(quantity.Int(), Is.EqualTo(3000)); 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("water", FixedPoint2.New(1000));
solution.AddReagent("fire", FixedPoint2.New(2000)); solution.AddReagent("fire", FixedPoint2.New(2000));
Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(1000)); Assert.That(solution.GetTotalPrototypeQuantity("water").Int(), Is.EqualTo(1000));
Assert.That(solution.GetReagentQuantity("fire").Int(), Is.EqualTo(2000)); Assert.That(solution.GetTotalPrototypeQuantity("fire").Int(), Is.EqualTo(2000));
} }
[Test] [Test]
@@ -141,8 +141,8 @@ public sealed class Solution_Tests : ContentUnitTest
var newSolution = solution.Clone(); var newSolution = solution.Clone();
Assert.That(newSolution.GetReagentQuantity("water").Int(), Is.EqualTo(1000)); Assert.That(newSolution.GetTotalPrototypeQuantity("water").Int(), Is.EqualTo(1000));
Assert.That(newSolution.GetReagentQuantity("fire").Int(), Is.EqualTo(2000)); Assert.That(newSolution.GetTotalPrototypeQuantity("fire").Int(), Is.EqualTo(2000));
Assert.That(newSolution.Volume.Int(), Is.EqualTo(3000)); Assert.That(newSolution.Volume.Int(), Is.EqualTo(3000));
} }
@@ -155,8 +155,8 @@ public sealed class Solution_Tests : ContentUnitTest
solution.RemoveReagent("water", FixedPoint2.New(500)); solution.RemoveReagent("water", FixedPoint2.New(500));
Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(500)); Assert.That(solution.GetTotalPrototypeQuantity("water").Int(), Is.EqualTo(500));
Assert.That(solution.GetReagentQuantity("fire").Int(), Is.EqualTo(2000)); Assert.That(solution.GetTotalPrototypeQuantity("fire").Int(), Is.EqualTo(2000));
Assert.That(solution.Volume.Int(), Is.EqualTo(2500)); Assert.That(solution.Volume.Int(), Is.EqualTo(2500));
} }
@@ -167,7 +167,7 @@ public sealed class Solution_Tests : ContentUnitTest
solution.RemoveReagent("water", FixedPoint2.New(-100)); 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)); Assert.That(solution.Volume.Int(), Is.EqualTo(100));
} }
@@ -178,7 +178,7 @@ public sealed class Solution_Tests : ContentUnitTest
solution.RemoveReagent("water", FixedPoint2.New(1000)); 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)); Assert.That(solution.Volume.Int(), Is.EqualTo(0));
} }
@@ -189,7 +189,7 @@ public sealed class Solution_Tests : ContentUnitTest
solution.RemoveReagent("fire", FixedPoint2.New(1000)); 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)); Assert.That(solution.Volume.Int(), Is.EqualTo(100));
} }
@@ -201,7 +201,7 @@ public sealed class Solution_Tests : ContentUnitTest
solution.RemoveSolution(FixedPoint2.New(500)); solution.RemoveSolution(FixedPoint2.New(500));
//Check that edited solution is correct //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)); Assert.That(solution.Volume.Int(), Is.EqualTo(200));
} }
@@ -213,7 +213,7 @@ public sealed class Solution_Tests : ContentUnitTest
solution.RemoveSolution(FixedPoint2.New(1000)); solution.RemoveSolution(FixedPoint2.New(1000));
//Check that edited solution is correct //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)); Assert.That(solution.Volume.Int(), Is.EqualTo(0));
} }
@@ -226,8 +226,8 @@ public sealed class Solution_Tests : ContentUnitTest
solution.RemoveSolution(FixedPoint2.New(1500)); solution.RemoveSolution(FixedPoint2.New(1500));
Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(500)); Assert.That(solution.GetTotalPrototypeQuantity("water").Int(), Is.EqualTo(500));
Assert.That(solution.GetReagentQuantity("fire").Int(), Is.EqualTo(1000)); Assert.That(solution.GetTotalPrototypeQuantity("fire").Int(), Is.EqualTo(1000));
Assert.That(solution.Volume.Int(), Is.EqualTo(1500)); Assert.That(solution.Volume.Int(), Is.EqualTo(1500));
} }
@@ -238,7 +238,7 @@ public sealed class Solution_Tests : ContentUnitTest
solution.RemoveSolution(FixedPoint2.New(-200)); 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)); 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)); var splitSolution = solution.SplitSolution(FixedPoint2.New(750));
Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(750)); Assert.That(solution.GetTotalPrototypeQuantity("water").Int(), Is.EqualTo(750));
Assert.That(solution.GetReagentQuantity("fire").Int(), Is.EqualTo(1500)); Assert.That(solution.GetTotalPrototypeQuantity("fire").Int(), Is.EqualTo(1500));
Assert.That(solution.Volume.Int(), Is.EqualTo(2250)); Assert.That(solution.Volume.Int(), Is.EqualTo(2250));
Assert.That(splitSolution.GetReagentQuantity("water").Int(), Is.EqualTo(250)); Assert.That(splitSolution.GetTotalPrototypeQuantity("water").Int(), Is.EqualTo(250));
Assert.That(splitSolution.GetReagentQuantity("fire").Int(), Is.EqualTo(500)); Assert.That(splitSolution.GetTotalPrototypeQuantity("fire").Int(), Is.EqualTo(500));
Assert.That(splitSolution.Volume.Int(), Is.EqualTo(750)); 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)); var splitSolution = solution.SplitSolution(FixedPoint2.New(1));
Assert.That(solution.GetReagentQuantity("water").Float(), Is.EqualTo(0.66f)); Assert.That(solution.GetTotalPrototypeQuantity("water").Float(), Is.EqualTo(0.66f));
Assert.That(solution.GetReagentQuantity("fire").Float(), Is.EqualTo(1.34f)); Assert.That(solution.GetTotalPrototypeQuantity("fire").Float(), Is.EqualTo(1.34f));
Assert.That(solution.Volume.Int(), Is.EqualTo(2)); Assert.That(solution.Volume.Int(), Is.EqualTo(2));
Assert.That(splitSolution.GetReagentQuantity("water").Float(), Is.EqualTo(0.34f)); Assert.That(splitSolution.GetTotalPrototypeQuantity("water").Float(), Is.EqualTo(0.34f));
Assert.That(splitSolution.GetReagentQuantity("fire").Float(), Is.EqualTo(0.66f)); Assert.That(splitSolution.GetTotalPrototypeQuantity("fire").Float(), Is.EqualTo(0.66f));
Assert.That(splitSolution.Volume.Int(), Is.EqualTo(1)); 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)); var splitSolution = solution.SplitSolution(FixedPoint2.New(2));
Assert.That(solution.GetReagentQuantity("water").Float(), Is.EqualTo(0.33f)); Assert.That(solution.GetTotalPrototypeQuantity("water").Float(), Is.EqualTo(0.33f));
Assert.That(solution.GetReagentQuantity("fire").Float(), Is.EqualTo(0.67f)); Assert.That(solution.GetTotalPrototypeQuantity("fire").Float(), Is.EqualTo(0.67f));
Assert.That(solution.Volume.Int(), Is.EqualTo(1)); Assert.That(solution.Volume.Int(), Is.EqualTo(1));
Assert.That(splitSolution.GetReagentQuantity("water").Float(), Is.EqualTo(0.67f)); Assert.That(splitSolution.GetTotalPrototypeQuantity("water").Float(), Is.EqualTo(0.67f));
Assert.That(splitSolution.GetReagentQuantity("fire").Float(), Is.EqualTo(1.33f)); Assert.That(splitSolution.GetTotalPrototypeQuantity("fire").Float(), Is.EqualTo(1.33f));
Assert.That(splitSolution.Volume.Int(), Is.EqualTo(2)); 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)); 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(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)); 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)); 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(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)); 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)); 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(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)); Assert.That(splitSolution.Volume.Int(), Is.EqualTo(0));
} }
@@ -393,9 +393,9 @@ public sealed class Solution_Tests : ContentUnitTest
solutionOne.AddSolution(solutionTwo, null); solutionOne.AddSolution(solutionTwo, null);
Assert.That(solutionOne.GetReagentQuantity("water").Int(), Is.EqualTo(1500)); Assert.That(solutionOne.GetTotalPrototypeQuantity("water").Int(), Is.EqualTo(1500));
Assert.That(solutionOne.GetReagentQuantity("fire").Int(), Is.EqualTo(2000)); Assert.That(solutionOne.GetTotalPrototypeQuantity("fire").Int(), Is.EqualTo(2000));
Assert.That(solutionOne.GetReagentQuantity("earth").Int(), Is.EqualTo(1000)); Assert.That(solutionOne.GetTotalPrototypeQuantity("earth").Int(), Is.EqualTo(1000));
Assert.That(solutionOne.Volume.Int(), Is.EqualTo(4500)); Assert.That(solutionOne.Volume.Int(), Is.EqualTo(4500));
} }