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