Allow solutions to store extra reagent data (#19323)

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

View File

@@ -1,4 +1,5 @@
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.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);
}

View File

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

View File

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

View File

@@ -83,7 +83,7 @@ namespace Content.Client.Kitchen.UI
}
}
private void RefreshContentsDisplay(IList<Solution.ReagentQuantity>? reagents, IReadOnlyList<EntityUid> containedSolids, bool isBeakerAttached)
private void RefreshContentsDisplay(IList<ReagentQuantity>? reagents, IReadOnlyList<EntityUid> containedSolids, bool isBeakerAttached)
{
//Refresh chamber contents
_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);
}
}

View File

@@ -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)
{

View File

@@ -0,0 +1,30 @@
using Content.IntegrationTests.Tests.Interaction;
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Reflection;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
namespace Content.IntegrationTests.Tests.Chemistry;
[TestFixture]
[TestOf(typeof(ReagentData))]
public sealed class ReagentDataTest : InteractionTest
{
[Test]
public async Task ReagentDataIsSerializable()
{
await using var pair = await PoolManager.GetServerClient();
var reflection = pair.Server.ResolveDependency<IReflectionManager>();
Assert.Multiple(() =>
{
foreach (var instance in reflection.GetAllChildren(typeof(ReagentData)))
{
Assert.That(instance.HasCustomAttribute<NetSerializableAttribute>(), $"{instance} must have the NetSerializable attribute.");
Assert.That(instance.HasCustomAttribute<SerializableAttribute>(), $"{instance} must have the serializable attribute.");
}
});
await pair.CleanReturnAsync();
}
}

View File

@@ -73,8 +73,8 @@ public sealed class SolutionSystemTests
Assert.That(containerSystem
.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));
});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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);

View File

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

View File

@@ -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));

View File

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

View File

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

View File

@@ -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 _);
}
}
}

View File

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

View File

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

View File

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

View File

@@ -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);

View File

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

View File

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

View File

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

View File

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

View File

@@ -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);

View File

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

View File

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

View File

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

View File

@@ -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);

View File

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

View File

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

View File

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

View File

@@ -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);

View File

@@ -0,0 +1,44 @@
using Content.Shared.FixedPoint;
using Robust.Shared.Serialization;
namespace Content.Shared.Chemistry.Reagent;
[ImplicitDataDefinitionForInheritors, Serializable, NetSerializable]
public abstract partial class ReagentData : IEquatable<ReagentData>
{
/// <summary>
/// Convert to a string representation. This if for logging & debugging. This is not localized and should not be
/// shown to players.
/// </summary>
public virtual string ToString(string prototype, FixedPoint2 quantity)
{
return $"{prototype}:{GetType().Name}:{quantity}";
}
/// <summary>
/// Convert to a string representation. This if for logging & debugging. This is not localized and should not be
/// shown to players.
/// </summary>
public virtual string ToString(string prototype)
{
return $"{prototype}:{GetType().Name}";
}
public abstract bool Equals(ReagentData? other);
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj))
return false;
if (ReferenceEquals(this, obj))
return true;
if (obj.GetType() != GetType())
return false;
return Equals((ReagentData) obj);
}
public abstract override int GetHashCode();
public abstract ReagentData Clone();
}

View File

@@ -0,0 +1,93 @@
using Content.Shared.FixedPoint;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared.Chemistry.Reagent;
/// <summary>
/// Struct used to uniquely identify a reagent. This is usually just a ReagentPrototype id string, however some reagents
/// contain additional data (e.g., blood could store DNA data).
/// </summary>
[Serializable, NetSerializable]
[DataDefinition]
public partial struct ReagentId : IEquatable<ReagentId>
{
// TODO rename data field.
[DataField("ReagentId", customTypeSerializer: typeof(PrototypeIdSerializer<ReagentPrototype>), required: true)]
public string Prototype { get; private set; }
/// <summary>
/// Any additional data that is unique to this reagent type. E.g., for blood this could be DNA data.
/// </summary>
[DataField("data")]
public ReagentData? Data { get; private set; }
public ReagentId(string prototype, ReagentData? data)
{
Prototype = prototype;
Data = data;
}
public ReagentId()
{
Prototype = default!;
}
public bool Equals(ReagentId other)
{
if (Prototype != other.Prototype)
return false;
if (Data == null)
return other.Data == null;
if (other.Data == null)
return false;
if (Data.GetType() != other.Data.GetType())
return false;
return Data.Equals(other.Data);
}
public bool Equals(string prototype, ReagentData? otherData = null)
{
if (Prototype != prototype)
return false;
if (Data == null)
return otherData == null;
return Data.Equals(otherData);
}
public override bool Equals(object? obj)
{
return obj is ReagentId other && Equals(other);
}
public override int GetHashCode()
{
return HashCode.Combine(Prototype, Data);
}
public string ToString(FixedPoint2 quantity)
{
return Data?.ToString(Prototype, quantity) ?? $"{Prototype}:{quantity}";
}
public override string ToString()
{
return Data?.ToString(Prototype) ?? Prototype;
}
public static bool operator ==(ReagentId left, ReagentId right)
{
return left.Equals(right);
}
public static bool operator !=(ReagentId left, ReagentId right)
{
return !(left == right);
}
}

View File

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

View File

@@ -0,0 +1,77 @@
using Content.Shared.FixedPoint;
using Robust.Shared.Serialization;
namespace Content.Shared.Chemistry.Reagent;
/// <summary>
/// Simple struct for storing a <see cref="ReagentId"/> & quantity tuple.
/// </summary>
[Serializable, NetSerializable]
[DataDefinition]
public partial struct ReagentQuantity : IEquatable<ReagentQuantity>
{
[DataField("Quantity", required:true)]
public FixedPoint2 Quantity { get; private set; }
[IncludeDataField]
[ViewVariables]
public ReagentId Reagent { get; private set; }
public ReagentQuantity(string reagentId, FixedPoint2 quantity, ReagentData? data)
: this(new ReagentId(reagentId, data), quantity)
{
}
public ReagentQuantity(ReagentId reagent, FixedPoint2 quantity)
{
Reagent = reagent;
Quantity = quantity;
}
public ReagentQuantity() : this(default, default)
{
}
public override string ToString()
{
return Reagent.ToString(Quantity);
}
public void Deconstruct(out string prototype, out FixedPoint2 quantity, out ReagentData? data)
{
prototype = Reagent.Prototype;
quantity = Quantity;
data = Reagent.Data;
}
public void Deconstruct(out ReagentId id, out FixedPoint2 quantity)
{
id = Reagent;
quantity = Quantity;
}
public bool Equals(ReagentQuantity other)
{
return Quantity != other.Quantity && Reagent.Equals(other.Reagent);
}
public override bool Equals(object? obj)
{
return obj is ReagentQuantity other && Equals(other);
}
public override int GetHashCode()
{
return HashCode.Combine(Reagent.GetHashCode(), Quantity);
}
public static bool operator ==(ReagentQuantity left, ReagentQuantity right)
{
return left.Equals(right);
}
public static bool operator !=(ReagentQuantity left, ReagentQuantity right)
{
return !(left == right);
}
}

View File

@@ -1,4 +1,5 @@
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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