Solution rejig (#12428)

This commit is contained in:
Leon Friedrich
2023-01-12 16:41:40 +13:00
committed by GitHub
parent 38504f6412
commit 466384b081
61 changed files with 873 additions and 619 deletions

View File

@@ -82,7 +82,7 @@ namespace Content.Client.Administration.UI.ManageSolutions
volumeLabel.HorizontalExpand = true;
volumeLabel.Margin = new Thickness(0, 4);
volumeLabel.Text = Loc.GetString("admin-solutions-window-volume-label",
("currentVolume", solution.CurrentVolume),
("currentVolume", solution.Volume),
("maxVolume", solution.MaxVolume));
var capacityBox = new BoxContainer();
@@ -116,16 +116,16 @@ namespace Content.Client.Administration.UI.ManageSolutions
private void UpdateThermalBox(Solution solution)
{
ThermalBox.DisposeAllChildren();
var heatCap = solution.GetHeatCapacity(null);
var specificHeatLabel = new Label();
specificHeatLabel.HorizontalExpand = true;
specificHeatLabel.Margin = new Thickness(0, 1);
specificHeatLabel.Text = Loc.GetString("admin-solutions-window-specific-heat-label", ("specificHeat", solution.SpecificHeat.ToString("G3")));
specificHeatLabel.Text = Loc.GetString("admin-solutions-window-specific-heat-label", ("specificHeat", heatCap.ToString("G3")));
var heatCapacityLabel = new Label();
heatCapacityLabel.HorizontalExpand = true;
heatCapacityLabel.Margin = new Thickness(0, 1);
heatCapacityLabel.Text = Loc.GetString("admin-solutions-window-heat-capacity-label", ("heatCapacity", solution.HeatCapacity.ToString("G3")));
heatCapacityLabel.Text = Loc.GetString("admin-solutions-window-heat-capacity-label", ("heatCapacity", (heatCap/solution.Volume.Float()).ToString("G3")));
// Temperature entry:
var temperatureBox = new BoxContainer();
@@ -161,7 +161,7 @@ namespace Content.Client.Administration.UI.ManageSolutions
var thermalEnergySpin = new FloatSpinBox(1, 2);
thermalEnergySpin.HorizontalExpand = true;
thermalEnergySpin.Margin = new Thickness(0, 1);
thermalEnergySpin.Value = solution.ThermalEnergy;
thermalEnergySpin.Value = solution.Temperature * heatCap;
thermalEnergySpin.OnValueChanged += SetThermalEnergy;
thermalEnergyBox.AddChild(thermalEnergyLabel);

View File

@@ -1,10 +1,10 @@
using System.Threading.Tasks;
using System.Threading.Tasks;
using Content.Server.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Components;
using Content.Shared.FixedPoint;
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
namespace Content.IntegrationTests.Tests.Chemistry;
@@ -24,6 +24,25 @@ public sealed class SolutionSystemTests
solutions:
beaker:
maxVol: 50
- type: reagent
id: TestReagentA
name: nah
desc: nah
physicalDesc: nah
- type: reagent
id: TestReagentB
name: nah
desc: nah
physicalDesc: nah
- type: reagent
id: TestReagentC
specificHeat: 2.0
name: nah
desc: nah
physicalDesc: nah
";
[Test]
public async Task TryAddTwoNonReactiveReagent()
@@ -32,6 +51,7 @@ public sealed class SolutionSystemTests
var server = pairTracker.Pair.Server;
var entityManager = server.ResolveDependency<IEntityManager>();
var protoMan = server.ResolveDependency<IPrototypeManager>();
var containerSystem = entityManager.EntitySysManager.GetEntitySystem<SolutionContainerSystem>();
var testMap = await PoolManager.CreateTestMap(pairTracker);
var coordinates = testMap.GridCoords;
@@ -50,12 +70,12 @@ public sealed class SolutionSystemTests
Assert.That(containerSystem
.TryGetSolution(beaker, "beaker", out var solution));
solution.AddSolution(originalWater);
solution.AddSolution(originalWater, protoMan);
Assert.That(containerSystem
.TryAddSolution(beaker, solution, oilAdded));
solution.ContainsReagent("Water", out var water);
solution.ContainsReagent("Oil", out var oil);
solution.TryGetReagent("Water", out var water);
solution.TryGetReagent("Oil", out var oil);
Assert.That(water, Is.EqualTo(waterQuantity));
Assert.That(oil, Is.EqualTo(oilQuantity));
});
@@ -74,6 +94,7 @@ public sealed class SolutionSystemTests
var testMap = await PoolManager.CreateTestMap(pairTracker);
var entityManager = server.ResolveDependency<IEntityManager>();
var protoMan = server.ResolveDependency<IPrototypeManager>();
var containerSystem = entityManager.EntitySysManager.GetEntitySystem<SolutionContainerSystem>();
var coordinates = testMap.GridCoords;
@@ -91,12 +112,12 @@ public sealed class SolutionSystemTests
Assert.That(containerSystem
.TryGetSolution(beaker, "beaker", out var solution));
solution.AddSolution(originalWater);
solution.AddSolution(originalWater, protoMan);
Assert.That(containerSystem
.TryAddSolution(beaker, solution, oilAdded), Is.False);
solution.ContainsReagent("Water", out var water);
solution.ContainsReagent("Oil", out var oil);
solution.TryGetReagent("Water", out var water);
solution.TryGetReagent("Oil", out var oil);
Assert.That(water, Is.EqualTo(waterQuantity));
Assert.That(oil, Is.EqualTo(FixedPoint2.Zero));
});
@@ -113,13 +134,14 @@ public sealed class SolutionSystemTests
var entityManager = server.ResolveDependency<IEntityManager>();
var protoMan = server.ResolveDependency<IPrototypeManager>();
var testMap = await PoolManager.CreateTestMap(pairTracker);
var containerSystem = entityManager.EntitySysManager.GetEntitySystem<SolutionContainerSystem>();
var coordinates = testMap.GridCoords;
EntityUid beaker;
await server.WaitAssertion(() =>
await server.WaitAssertion((System.Action)(() =>
{
int ratio = 9;
int threshold = 20;
@@ -133,22 +155,22 @@ public sealed class SolutionSystemTests
Assert.That(containerSystem
.TryGetSolution(beaker, "beaker", out var solution));
solution.AddSolution(originalWater);
solution.AddSolution(originalWater, protoMan);
Assert.That(containerSystem
.TryMixAndOverflow(beaker, solution, oilAdded, threshold, out var overflowingSolution));
Assert.That(solution.CurrentVolume, Is.EqualTo(FixedPoint2.New(threshold)));
solution.ContainsReagent("Water", out var waterMix);
solution.ContainsReagent("Oil", out var oilMix);
Assert.That((FixedPoint2) solution.Volume, Is.EqualTo(FixedPoint2.New(threshold)));
solution.TryGetReagent("Water", out var waterMix);
solution.TryGetReagent("Oil", out var oilMix);
Assert.That(waterMix, Is.EqualTo(FixedPoint2.New(threshold / (ratio + 1))));
Assert.That(oilMix, Is.EqualTo(FixedPoint2.New(threshold / (ratio + 1) * ratio)));
Assert.That(overflowingSolution.CurrentVolume, Is.EqualTo(FixedPoint2.New(80)));
overflowingSolution.ContainsReagent("Water", out var waterOverflow);
overflowingSolution.ContainsReagent("Oil", out var oilOverFlow);
Assert.That((FixedPoint2) overflowingSolution.Volume, Is.EqualTo(FixedPoint2.New(80)));
overflowingSolution.TryGetReagent("Water", out var waterOverflow);
overflowingSolution.TryGetReagent("Oil", out var oilOverFlow);
Assert.That(waterOverflow, Is.EqualTo(waterQuantity - waterMix));
Assert.That(oilOverFlow, Is.EqualTo(oilQuantity - oilMix));
});
}));
await pairTracker.CleanReturnAsync();
}
@@ -161,6 +183,7 @@ public sealed class SolutionSystemTests
var server = pairTracker.Pair.Server;
var entityManager = server.ResolveDependency<IEntityManager>();
var protoMan = server.ResolveDependency<IPrototypeManager>();
var containerSystem = entityManager.EntitySysManager.GetEntitySystem<SolutionContainerSystem>();
var testMap = await PoolManager.CreateTestMap(pairTracker);
var coordinates = testMap.GridCoords;
@@ -181,7 +204,7 @@ public sealed class SolutionSystemTests
Assert.That(containerSystem
.TryGetSolution(beaker, "beaker", out var solution));
solution.AddSolution(originalWater);
solution.AddSolution(originalWater, protoMan);
Assert.That(containerSystem
.TryMixAndOverflow(beaker, solution, oilAdded, threshold, out _),
Is.False);
@@ -189,4 +212,43 @@ public sealed class SolutionSystemTests
await pairTracker.CleanReturnAsync();
}
[Test]
public async Task TestTemperatureCalculations()
{
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings { NoClient = true, ExtraPrototypes = Prototypes });
var server = pairTracker.Pair.Server;
var protoMan = server.ResolveDependency<IPrototypeManager>();
const float temp = 100.0f;
// Adding reagent with adjusts temperature
await server.WaitAssertion(() =>
{
var solution = new Solution("TestReagentA", FixedPoint2.New(100)) { Temperature = temp };
Assert.That(solution.Temperature, Is.EqualTo(temp * 1));
solution.AddSolution(new Solution("TestReagentA", FixedPoint2.New(100)) { Temperature = temp * 3 }, protoMan);
Assert.That(solution.Temperature, Is.EqualTo(temp * 2));
solution.AddSolution(new Solution("TestReagentB", FixedPoint2.New(100)) { Temperature = temp * 5 }, protoMan);
Assert.That(solution.Temperature, Is.EqualTo(temp * 3));
});
// adding solutions combines thermal energy
await server.WaitAssertion(() =>
{
var solutionOne = new Solution("TestReagentA", FixedPoint2.New(100)) { Temperature = temp };
var solutionTwo = new Solution("TestReagentB", FixedPoint2.New(100)) { Temperature = temp };
solutionTwo.AddReagent("TestReagentC", FixedPoint2.New(100));
var thermalEnergyOne = solutionOne.GetHeatCapacity(protoMan) * solutionOne.Temperature;
var thermalEnergyTwo = solutionTwo.GetHeatCapacity(protoMan) * solutionTwo.Temperature;
solutionOne.AddSolution(solutionTwo, protoMan);
Assert.That(solutionOne.GetHeatCapacity(protoMan) * solutionOne.Temperature, Is.EqualTo(thermalEnergyOne + thermalEnergyTwo));
});
await pairTracker.CleanReturnAsync();
}
}

View File

@@ -46,7 +46,7 @@ namespace Content.Server.Administration.Commands
return;
}
if (solution.HeatCapacity <= 0.0f)
if (solution.GetHeatCapacity(null) <= 0.0f)
{
if(quantity != 0.0f)
{

View File

@@ -98,7 +98,7 @@ namespace Content.Server.Animals.Systems
if (!_solutionContainerSystem.TryGetRefillableSolution(ev.ContainerUid, out var targetSolution))
return;
var quantity = solution.TotalVolume;
var quantity = solution.Volume;
if(quantity == 0)
{
_popupSystem.PopupEntity(Loc.GetString("udder-system-dry"), uid, ev.UserUid);

View File

@@ -23,8 +23,8 @@ namespace Content.Server.Body.Components
/// <summary>
/// Initial internal solution storage volume
/// </summary>
[DataField("maxVolume")]
public FixedPoint2 InitialMaxVolume { get; private set; } = FixedPoint2.New(50);
[DataField("initialMaxVolume", readOnly: true)]
public readonly FixedPoint2 InitialMaxVolume = FixedPoint2.New(50);
/// <summary>
/// Time in seconds between reagents being ingested and them being

View File

@@ -96,7 +96,7 @@ public sealed class BloodstreamSystem : EntitySystem
continue;
// First, let's refresh their blood if possible.
if (bloodstream.BloodSolution.CurrentVolume < bloodstream.BloodSolution.MaxVolume)
if (bloodstream.BloodSolution.Volume < bloodstream.BloodSolution.MaxVolume)
TryModifyBloodLevel(uid, bloodstream.BloodRefreshAmount, bloodstream);
// Next, let's remove some blood from them according to their bleed level.
@@ -258,7 +258,7 @@ public sealed class BloodstreamSystem : EntitySystem
if (!Resolve(uid, ref component))
return 0.0f;
return (component.BloodSolution.CurrentVolume / component.BloodSolution.MaxVolume).Float();
return component.BloodSolution.FillFraction;
}
public void SetBloodLossThreshold(EntityUid uid, float threshold, BloodstreamComponent? comp = null)
@@ -284,13 +284,13 @@ public sealed class BloodstreamSystem : EntitySystem
// since we also wanna handle moving it to the temporary solution
// and then spilling it if necessary.
var newSol = component.BloodSolution.SplitSolution(-amount);
component.BloodTemporarySolution.AddSolution(newSol);
component.BloodTemporarySolution.AddSolution(newSol, _prototypeManager);
if (component.BloodTemporarySolution.CurrentVolume > component.BleedPuddleThreshold)
if (component.BloodTemporarySolution.Volume > component.BleedPuddleThreshold)
{
// Pass some of the chemstream into the spilled blood.
var temp = component.ChemicalSolution.SplitSolution(component.BloodTemporarySolution.CurrentVolume / 10);
component.BloodTemporarySolution.AddSolution(temp);
var temp = component.ChemicalSolution.SplitSolution(component.BloodTemporarySolution.Volume / 10);
component.BloodTemporarySolution.AddSolution(temp, _prototypeManager);
_spillableSystem.SpillAt(uid, component.BloodTemporarySolution, "PuddleBlood", false);
component.BloodTemporarySolution.RemoveAllSolution();
}
@@ -324,11 +324,11 @@ public sealed class BloodstreamSystem : EntitySystem
component.ChemicalSolution.MaxVolume;
var tempSol = new Solution() { MaxVolume = max };
tempSol.AddSolution(component.BloodSolution);
tempSol.AddSolution(component.BloodSolution, _prototypeManager);
component.BloodSolution.RemoveAllSolution();
tempSol.AddSolution(component.BloodTemporarySolution);
tempSol.AddSolution(component.BloodTemporarySolution, _prototypeManager);
component.BloodTemporarySolution.RemoveAllSolution();
tempSol.AddSolution(component.ChemicalSolution);
tempSol.AddSolution(component.ChemicalSolution, _prototypeManager);
component.ChemicalSolution.RemoveAllSolution();
_spillableSystem.SpillAt(uid, tempSol, "PuddleBlood", true);
}

View File

@@ -1,4 +1,4 @@
using Content.Server.Body.Components;
using Content.Server.Body.Components;
using Content.Server.Chemistry.Components.SolutionManager;
using Content.Server.Chemistry.EntitySystems;
using Content.Shared.Body.Organ;
@@ -47,7 +47,7 @@ namespace Content.Server.Body.Systems
delta.Increment(stomach.UpdateInterval);
if (delta.Lifetime > stomach.DigestionDelay)
{
if (stomachSolution.ContainsReagent(delta.ReagentId, out var quant))
if (stomachSolution.TryGetReagent(delta.ReagentId, out var quant))
{
if (quant > delta.Quantity)
quant = delta.Quantity;
@@ -89,8 +89,7 @@ namespace Content.Server.Body.Systems
private void OnComponentInit(EntityUid uid, StomachComponent component, ComponentInit args)
{
var solution = _solutionContainerSystem.EnsureSolution(uid, DefaultSolutionName);
solution.MaxVolume = component.InitialMaxVolume;
_solutionContainerSystem.EnsureSolution(uid, DefaultSolutionName, component.InitialMaxVolume, out _);
}
public bool CanTransferSolution(EntityUid uid, Solution solution,

View File

@@ -1,4 +1,5 @@
using Content.Server.Botany.Components;
using Content.Shared.Chemistry.Components;
using Content.Shared.FixedPoint;
using Robust.Server.GameObjects;
@@ -17,17 +18,17 @@ public sealed partial class BotanySystem
sprite.LayerSetState(0, seed.PlantIconState);
}
var solutionContainer = _solutionContainerSystem.EnsureSolution(uid, produce.SolutionName);
solutionContainer.RemoveAllSolution();
Solution.ReagentQuantity[] reagents = new Solution.ReagentQuantity[seed.Chemicals.Count];
int i = 0;
foreach (var (chem, quantity) in seed.Chemicals)
{
var amount = FixedPoint2.New(quantity.Min);
if (quantity.PotencyDivisor > 0 && seed.Potency > 0)
amount += FixedPoint2.New(seed.Potency / quantity.PotencyDivisor);
amount = FixedPoint2.New((int) MathHelper.Clamp(amount.Float(), quantity.Min, quantity.Max));
solutionContainer.MaxVolume += amount;
solutionContainer.AddReagent(chem, amount);
}
reagents[i++] = new(chem, amount);
}
_solutionContainerSystem.EnsureSolution(uid, produce.SolutionName, reagents);
}
}

View File

@@ -211,7 +211,7 @@ namespace Content.Server.Botany.Systems
var split =_solutionSystem.Drain(solutionEntity, solution, amount);
if (split.TotalVolume == 0)
if (split.Volume == 0)
{
_popupSystem.PopupCursor(Loc.GetString("plant-holder-component-no-plant-message",
("owner", args.Used)), args.User);
@@ -220,7 +220,7 @@ namespace Content.Server.Botany.Systems
_popupSystem.PopupCursor(Loc.GetString("plant-holder-component-spray-message",
("owner", uid),
("amount", split.TotalVolume)), args.User, PopupType.Medium);
("amount", split.Volume)), args.User, PopupType.Medium);
_solutionSystem.TryAddSolution(targetEntity, targetSolution, split);
@@ -284,7 +284,7 @@ namespace Content.Server.Botany.Systems
{
// This deliberately discards overfill.
_solutionSystem.TryAddSolution(args.Used, solution2,
_solutionSystem.SplitSolution(args.Used, solution2, solution2.TotalVolume));
_solutionSystem.SplitSolution(args.Used, solution2, solution2.Volume));
ForceUpdateByExternalCause(uid, component);
}
@@ -780,7 +780,7 @@ namespace Content.Server.Botany.Systems
if (!_solutionSystem.TryGetSolution(uid, component.SoilSolutionName, out var solution))
return;
if (solution.TotalVolume > 0 && component.MutationLevel < 25)
if (solution.Volume > 0 && component.MutationLevel < 25)
{
var amt = FixedPoint2.New(1);
foreach (var (reagentId, quantity) in _solutionSystem.RemoveEachReagent(uid, solution, amt))

View File

@@ -1,4 +1,4 @@
using Content.Server.Body.Components;
using Content.Server.Body.Components;
using Content.Server.Body.Systems;
using Content.Server.Chemistry.EntitySystems;
using Content.Shared.Administration.Logs;
@@ -6,6 +6,7 @@ using Content.Shared.Database;
using Content.Shared.FixedPoint;
using Content.Shared.Foam;
using Content.Shared.Inventory;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.Components
{
@@ -14,6 +15,7 @@ namespace Content.Server.Chemistry.Components
public sealed class FoamSolutionAreaEffectComponent : SolutionAreaEffectComponent
{
[Dependency] private readonly IEntityManager _entMan = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
public new const string SolutionName = "solutionArea";
@@ -25,7 +27,7 @@ namespace Content.Server.Chemistry.Components
if (_entMan.TryGetComponent(Owner, out AppearanceComponent? appearance) &&
EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solution))
{
appearance.SetData(FoamVisuals.Color, solution.Color.WithAlpha(0.80f));
appearance.SetData(FoamVisuals.Color, solution.GetColor(_proto).WithAlpha(0.80f));
}
}
@@ -60,7 +62,7 @@ namespace Content.Server.Chemistry.Components
var bloodstreamSys = EntitySystem.Get<BloodstreamSystem>();
var cloneSolution = solution.Clone();
var transferAmount = FixedPoint2.Min(cloneSolution.TotalVolume * solutionFraction * (1 - protection),
var transferAmount = FixedPoint2.Min(cloneSolution.Volume * solutionFraction * (1 - protection),
bloodstream.ChemicalSolution.AvailableVolume);
var transferSolution = cloneSolution.SplitSolution(transferAmount);

View File

@@ -25,7 +25,7 @@ namespace Content.Server.Chemistry.Components
{
var solutionSys = _entMan.EntitySysManager.GetEntitySystem<SolutionContainerSystem>();
return solutionSys.TryGetSolution(Owner, SolutionName, out var solution)
? new HyposprayComponentState(solution.CurrentVolume, solution.MaxVolume)
? new HyposprayComponentState(solution.Volume, solution.MaxVolume)
: new HyposprayComponentState(FixedPoint2.Zero, FixedPoint2.Zero);
}
}

View File

@@ -1,4 +1,4 @@
using Content.Server.Body.Components;
using Content.Server.Body.Components;
using Content.Server.Body.Systems;
using Content.Server.Chemistry.EntitySystems;
using Content.Shared.Administration.Logs;
@@ -7,6 +7,7 @@ using Content.Shared.Chemistry.Reagent;
using Content.Shared.Database;
using Content.Shared.FixedPoint;
using Content.Shared.Smoking;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.Components
{
@@ -15,6 +16,7 @@ namespace Content.Server.Chemistry.Components
public sealed class SmokeSolutionAreaEffectComponent : SolutionAreaEffectComponent
{
[Dependency] private readonly IEntityManager _entMan = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
public new const string SolutionName = "solutionArea";
@@ -24,7 +26,7 @@ namespace Content.Server.Chemistry.Components
if (_entMan.TryGetComponent(Owner, out AppearanceComponent? appearance) &&
EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solution))
{
appearance.SetData(SmokeVisuals.Color, solution.Color);
appearance.SetData(SmokeVisuals.Color, solution.GetColor(_proto));
}
}
@@ -42,7 +44,7 @@ namespace Content.Server.Chemistry.Components
var chemistry = EntitySystem.Get<ReactiveSystem>();
var cloneSolution = solution.Clone();
var transferAmount = FixedPoint2.Min(cloneSolution.TotalVolume * solutionFraction, bloodstream.ChemicalSolution.AvailableVolume);
var transferAmount = FixedPoint2.Min(cloneSolution.Volume * solutionFraction, bloodstream.ChemicalSolution.AvailableVolume);
var transferSolution = cloneSolution.SplitSolution(transferAmount);
foreach (var reagentQuantity in transferSolution.Contents.ToArray())

View File

@@ -194,14 +194,14 @@ namespace Content.Server.Chemistry.Components
public void TryAddSolution(Solution solution)
{
if (solution.TotalVolume == 0)
if (solution.Volume == 0)
return;
if (!EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solutionArea))
return;
var addSolution =
solution.SplitSolution(FixedPoint2.Min(solution.TotalVolume, solutionArea.AvailableVolume));
solution.SplitSolution(FixedPoint2.Min(solution.Volume, solutionArea.AvailableVolume));
EntitySystem.Get<SolutionContainerSystem>().TryAddSolution(Owner, solutionArea, addSolution);

View File

@@ -66,7 +66,7 @@ namespace Content.Server.Chemistry.EntitySystems
var outputContainer = _itemSlotsSystem.GetItemOrNull(chemMaster.Owner, SharedChemMaster.OutputSlotName);
var bufferReagents = bufferSolution.Contents;
var bufferCurrentVolume = bufferSolution.CurrentVolume;
var bufferCurrentVolume = bufferSolution.Volume;
var state = new ChemMasterBoundUserInterfaceState(
chemMaster.Mode, BuildInputContainerInfo(inputContainer), BuildOutputContainerInfo(outputContainer),
@@ -288,7 +288,7 @@ namespace Content.Server.Chemistry.EntitySystems
return false;
}
if (solution.TotalVolume == 0)
if (solution.Volume == 0)
{
if (user.HasValue)
_popupSystem.PopupCursor(Loc.GetString("chem-master-window-buffer-empty-text"), user.Value);
@@ -296,7 +296,7 @@ namespace Content.Server.Chemistry.EntitySystems
}
// ReSharper disable once InvertIf
if (neededVolume > solution.CurrentVolume)
if (neededVolume > solution.Volume)
{
if (user.HasValue)
_popupSystem.PopupCursor(Loc.GetString("chem-master-window-buffer-low-text"), user.Value);
@@ -343,12 +343,12 @@ namespace Content.Server.Chemistry.EntitySystems
if (!TryComp(container, out ServerStorageComponent? storage))
return null;
var pills = storage.Storage?.ContainedEntities.Select(pill =>
var pills = storage.Storage?.ContainedEntities.Select((Func<EntityUid, (string, FixedPoint2 quantity)>) (pill =>
{
_solutionContainerSystem.TryGetSolution(pill, SharedChemMaster.PillSolutionName, out var solution);
var quantity = solution?.CurrentVolume ?? FixedPoint2.Zero;
return (Name(pill), quantity);
}).ToList();
var quantity = solution?.Volume ?? FixedPoint2.Zero;
return ((string, FixedPoint2 quantity))(Name(pill), quantity:(FixedPoint2) quantity);
})).ToList();
return pills is null
? null
@@ -360,7 +360,7 @@ namespace Content.Server.Chemistry.EntitySystems
var reagents = solution.Contents
.Select(reagent => (reagent.ReagentId, reagent.Quantity)).ToList();
return new ContainerInfo(name, true, solution.CurrentVolume, solution.MaxVolume, reagents);
return new ContainerInfo(name, true, solution.Volume, solution.MaxVolume, reagents);
}
}
}

View File

@@ -145,7 +145,7 @@ public sealed partial class ChemistrySystem
{
_solutions.TryGetSolution(uid, InjectorComponent.SolutionName, out var solution);
var currentVolume = solution?.CurrentVolume ?? FixedPoint2.Zero;
var currentVolume = solution?.Volume ?? FixedPoint2.Zero;
var maxVolume = solution?.MaxVolume ?? FixedPoint2.Zero;
args.State = new SharedInjectorComponent.InjectorComponentState(currentVolume, maxVolume, component.ToggleState);
@@ -323,7 +323,7 @@ public sealed partial class ChemistrySystem
removedSolution.DoEntityReaction(targetBloodstream.Owner, ReactionMethod.Injection);
_popup.PopupEntity(Loc.GetString("injector-component-inject-success-message",
("amount", removedSolution.TotalVolume),
("amount", removedSolution.Volume),
("target", Identity.Entity(targetBloodstream.Owner, EntityManager))), component.Owner, user);
Dirty(component);
@@ -333,7 +333,7 @@ public sealed partial class ChemistrySystem
private void TryInject(InjectorComponent component, EntityUid targetEntity, Solution targetSolution, EntityUid user, bool asRefill)
{
if (!_solutions.TryGetSolution(component.Owner, InjectorComponent.SolutionName, out var solution)
|| solution.CurrentVolume == 0)
|| solution.Volume == 0)
{
return;
}
@@ -363,7 +363,7 @@ public sealed partial class ChemistrySystem
}
_popup.PopupEntity(Loc.GetString("injector-component-transfer-success-message",
("amount", removedSolution.TotalVolume),
("amount", removedSolution.Volume),
("target", Identity.Entity(targetEntity, EntityManager))), component.Owner, user);
Dirty(component);
@@ -374,7 +374,7 @@ public sealed partial class ChemistrySystem
{
// Automatically set syringe to draw after completely draining it.
if (_solutions.TryGetSolution(component.Owner, InjectorComponent.SolutionName, out var solution)
&& solution.CurrentVolume == 0)
&& solution.Volume == 0)
{
component.ToggleState = SharedInjectorComponent.InjectorToggleMode.Draw;
}
@@ -399,7 +399,7 @@ public sealed partial class ChemistrySystem
}
// Get transfer amount. May be smaller than _transferAmount if not enough room, also make sure there's room in the injector
var realTransferAmount = FixedPoint2.Min(component.TransferAmount, targetSolution.DrawAvailable, solution.AvailableVolume);
var realTransferAmount = FixedPoint2.Min(component.TransferAmount, targetSolution.Volume, solution.AvailableVolume);
if (realTransferAmount <= 0)
{
@@ -424,7 +424,7 @@ public sealed partial class ChemistrySystem
}
_popup.PopupEntity(Loc.GetString("injector-component-draw-success-message",
("amount", removedSolution.TotalVolume),
("amount", removedSolution.Volume),
("target", Identity.Entity(targetEntity, EntityManager))), component.Owner, user);
Dirty(component);
@@ -436,7 +436,7 @@ public sealed partial class ChemistrySystem
var drawAmount = (float) transferAmount;
var bloodAmount = drawAmount;
var chemAmount = 0f;
if (stream.ChemicalSolution.CurrentVolume > 0f) // If they have stuff in their chem stream, we'll draw some of that
if (stream.ChemicalSolution.Volume > 0f) // If they have stuff in their chem stream, we'll draw some of that
{
bloodAmount = drawAmount * 0.85f;
chemAmount = drawAmount * 0.15f;

View File

@@ -77,7 +77,7 @@ namespace Content.Server.Chemistry.EntitySystems
_solutions.TryGetSolution(uid, component.SolutionName, out var hypoSpraySolution);
if (hypoSpraySolution == null || hypoSpraySolution.CurrentVolume == 0)
if (hypoSpraySolution == null || hypoSpraySolution.Volume == 0)
{
_popup.PopupCursor(Loc.GetString("hypospray-component-empty-message"), user);
return true;

View File

@@ -6,6 +6,7 @@ using Content.Shared.Chemistry.Dispenser;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.Database;
using Content.Shared.Emag.Systems;
using Content.Shared.FixedPoint;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
@@ -62,7 +63,7 @@ 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.CurrentVolume, solution.MaxVolume, reagents);
return new ContainerInfo(Name(container.Value), true, solution.Volume, solution.MaxVolume, reagents);
}
return null;

View File

@@ -110,7 +110,7 @@ public sealed partial class SolutionContainerSystem
{
return !TryGetDrainableSolution(uid, out var solution)
? FixedPoint2.Zero
: solution.CurrentVolume;
: solution.Volume;
}
public float PercentFull(EntityUid uid)
@@ -118,7 +118,7 @@ public sealed partial class SolutionContainerSystem
if (!TryGetDrainableSolution(uid, out var solution) || solution.MaxVolume.Equals(FixedPoint2.Zero))
return 0;
return ((solution.CurrentVolume.Float() / solution.MaxVolume.Float()) * 100);
return solution.FillFraction * 100;
}
public bool TryGetFitsInDispenser(EntityUid owner,

View File

@@ -51,13 +51,7 @@ public sealed partial class SolutionContainerSystem : EntitySystem
foreach (var (name, solutionHolder) in component.Solutions)
{
solutionHolder.Name = name;
if (solutionHolder.MaxVolume == FixedPoint2.Zero)
{
solutionHolder.MaxVolume = solutionHolder.TotalVolume > solutionHolder.InitialMaxVolume
? solutionHolder.TotalVolume
: solutionHolder.InitialMaxVolume;
}
solutionHolder.ValidateSolution();
UpdateAppearance(uid, solutionHolder);
}
}
@@ -70,14 +64,14 @@ public sealed partial class SolutionContainerSystem : EntitySystem
|| !solutionsManager.Solutions.TryGetValue(examinableComponent.Solution, out var solutionHolder))
return;
if (solutionHolder.Contents.Count == 0)
var primaryReagent = solutionHolder.GetPrimaryReagentId();
if (string.IsNullOrEmpty(primaryReagent))
{
args.PushText(Loc.GetString("shared-solution-container-component-on-examine-empty-container"));
return;
}
var primaryReagent = solutionHolder.GetPrimaryReagentId();
if (!_prototypeManager.TryIndex(primaryReagent, out ReagentPrototype? proto))
{
Logger.Error(
@@ -85,7 +79,7 @@ public sealed partial class SolutionContainerSystem : EntitySystem
return;
}
var colorHex = solutionHolder.Color
var colorHex = solutionHolder.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";
@@ -104,9 +98,9 @@ public sealed partial class SolutionContainerSystem : EntitySystem
|| !Resolve(uid, ref appearanceComponent, false))
return;
var filledVolumePercent = Math.Min(1.0f, solution.CurrentVolume.Float() / solution.MaxVolume.Float());
var filledVolumePercent = solution.FillFraction * 100;
appearanceComponent.SetData(SolutionContainerVisuals.VisualState,
new SolutionContainerVisualState(solution.Color, filledVolumePercent));
new SolutionContainerVisualState(solution.GetColor(_prototypeManager), filledVolumePercent));
}
/// <summary>
@@ -139,7 +133,7 @@ public sealed partial class SolutionContainerSystem : EntitySystem
public void RemoveAllSolution(EntityUid uid, Solution solutionHolder)
{
if (solutionHolder.CurrentVolume == 0)
if (solutionHolder.Volume == 0)
return;
solutionHolder.RemoveAllSolution();
@@ -169,8 +163,8 @@ public sealed partial class SolutionContainerSystem : EntitySystem
return;
targetSolution.MaxVolume = capacity;
if (capacity < targetSolution.CurrentVolume)
targetSolution.RemoveSolution(targetSolution.CurrentVolume - capacity);
if (capacity < targetSolution.Volume)
targetSolution.RemoveSolution(targetSolution.Volume - capacity);
UpdateChemicals(targetUid, targetSolution);
}
@@ -188,14 +182,20 @@ public sealed partial class SolutionContainerSystem : EntitySystem
out FixedPoint2 acceptedQuantity, float? temperature = null)
{
acceptedQuantity = targetSolution.AvailableVolume > quantity ? quantity : targetSolution.AvailableVolume;
targetSolution.AddReagent(reagentId, acceptedQuantity, temperature);
if (acceptedQuantity > 0)
if (acceptedQuantity <= 0)
return quantity == 0;
if (temperature == null)
targetSolution.AddReagent(reagentId, acceptedQuantity);
else
targetSolution.AddReagent(_prototypeManager.Index<ReagentPrototype>(reagentId), acceptedQuantity, temperature.Value, _prototypeManager);
UpdateChemicals(targetUid, targetSolution, true);
return acceptedQuantity == quantity;
}
/// <summary>
/// Removes reagent of an Id to the container.
/// </summary>
@@ -224,10 +224,10 @@ public sealed partial class SolutionContainerSystem : EntitySystem
public bool TryAddSolution(EntityUid targetUid, Solution? targetSolution, Solution addedSolution)
{
if (targetSolution == null
|| !targetSolution.CanAddSolution(addedSolution) || addedSolution.TotalVolume == 0)
|| !targetSolution.CanAddSolution(addedSolution) || addedSolution.Volume == 0)
return false;
targetSolution.AddSolution(addedSolution);
targetSolution.AddSolution(addedSolution, _prototypeManager);
UpdateChemicals(targetUid, targetSolution, true);
return true;
}
@@ -245,13 +245,13 @@ public sealed partial class SolutionContainerSystem : EntitySystem
if (quantity < 0)
return TryTransferSolution(targetUid, sourceUid, target, source, -quantity);
quantity = FixedPoint2.Min(quantity, target.AvailableVolume, source.CurrentVolume);
quantity = FixedPoint2.Min(quantity, target.AvailableVolume, source.Volume);
if (quantity == 0)
return false;
// TODO after #12428 is merged, this should be made into a function that directly transfers reagents.
// currently this is quite inefficient.
target.AddSolution(source.SplitSolution(quantity));
// 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);
@@ -294,16 +294,16 @@ public sealed partial class SolutionContainerSystem : EntitySystem
FixedPoint2 overflowThreshold,
[NotNullWhen(true)] out Solution? overflowingSolution)
{
if (addedSolution.TotalVolume == 0 || overflowThreshold > targetSolution.MaxVolume)
if (addedSolution.Volume == 0 || overflowThreshold > targetSolution.MaxVolume)
{
overflowingSolution = null;
return false;
}
targetSolution.AddSolution(addedSolution);
targetSolution.AddSolution(addedSolution, _prototypeManager);
UpdateChemicals(targetUid, targetSolution, true);
overflowingSolution = targetSolution.SplitSolution(FixedPoint2.Max(FixedPoint2.Zero,
targetSolution.CurrentVolume - overflowThreshold));
targetSolution.Volume - overflowThreshold));
return true;
}
@@ -320,14 +320,16 @@ public sealed partial class SolutionContainerSystem : EntitySystem
return solutionsMgr.Solutions.TryGetValue(name, out solution);
}
/// <summary>
/// Will ensure a solution is added to given entity even if it's missing solutionContainerManager
/// </summary>
/// <param name="uid">EntityUid to which to add solution</param>
/// <param name="name">name for the solution</param>
/// <param name="solutionsMgr">solution components used in resolves</param>
/// <param name="existed">true if the solution already existed</param>
/// <returns>solution</returns>
public Solution EnsureSolution(EntityUid uid, string name,
public Solution EnsureSolution(EntityUid uid, string name, out bool existed,
SolutionContainerManagerComponent? solutionsMgr = null)
{
if (!Resolve(uid, ref solutionsMgr, false))
@@ -335,15 +337,76 @@ public sealed partial class SolutionContainerSystem : EntitySystem
solutionsMgr = EntityManager.EnsureComponent<SolutionContainerManagerComponent>(uid);
}
if (!solutionsMgr.Solutions.ContainsKey(name))
if (!solutionsMgr.Solutions.TryGetValue(name, out var existing))
{
var newSolution = new Solution() { Name = name };
solutionsMgr.Solutions.Add(name, newSolution);
existed = false;
return newSolution;
}
return solutionsMgr.Solutions[name];
existed = true;
return existing;
}
/// <summary>
/// Will ensure a solution is added to given entity even if it's missing solutionContainerManager
/// </summary>
/// <param name="uid">EntityUid to which to add solution</param>
/// <param name="name">name for the solution</param>
/// <param name="solutionsMgr">solution components used in resolves</param>
/// <returns>solution</returns>
public Solution EnsureSolution(EntityUid uid, string name, SolutionContainerManagerComponent? solutionsMgr = null)
=> EnsureSolution(uid, name, out _, solutionsMgr);
/// <summary>
/// Will ensure a solution is added to given entity even if it's missing solutionContainerManager
/// </summary>
/// <param name="uid">EntityUid to which to add solution</param>
/// <param name="name">name for the solution</param>
/// <param name="minVol">Ensures that the solution's maximum volume is larger than this value./param>
/// <param name="solutionsMgr">solution components used in resolves</param>
/// <returns>solution</returns>
public Solution EnsureSolution(EntityUid uid, string name, FixedPoint2 minVol, out bool existed,
SolutionContainerManagerComponent? solutionsMgr = null)
{
if (!Resolve(uid, ref solutionsMgr, false))
{
solutionsMgr = EntityManager.EnsureComponent<SolutionContainerManagerComponent>(uid);
}
if (!solutionsMgr.Solutions.TryGetValue(name, out var existing))
{
var newSolution = new Solution() { Name = name };
solutionsMgr.Solutions.Add(name, newSolution);
existed = false;
newSolution.MaxVolume = minVol;
return newSolution;
}
existed = true;
existing.MaxVolume = FixedPoint2.Max(existing.MaxVolume, minVol);
return existing;
}
public Solution EnsureSolution(EntityUid uid, string name,
IEnumerable<Solution.ReagentQuantity> reagents,
bool setMaxVol = true,
SolutionContainerManagerComponent? solutionsMgr = null)
{
if (!Resolve(uid, ref solutionsMgr, false))
solutionsMgr = EntityManager.EnsureComponent<SolutionContainerManagerComponent>(uid);
if (!solutionsMgr.Solutions.TryGetValue(name, out var existing))
{
var newSolution = new Solution(reagents, setMaxVol);
solutionsMgr.Solutions.Add(name, newSolution);
return newSolution;
}
existing.SetContents(reagents, setMaxVol);
return existing;
}
/// <summary>
/// Removes an amount from all reagents in a solution, adding it to a new solution.
/// </summary>
@@ -446,10 +509,8 @@ public sealed partial class SolutionContainerSystem : EntitySystem
/// <param name="thermalEnergy">The new value to set the thermal energy to.</param>
public void SetThermalEnergy(EntityUid owner, Solution solution, float thermalEnergy)
{
if (thermalEnergy == solution.ThermalEnergy)
return;
solution.ThermalEnergy = thermalEnergy;
var heatCap = solution.GetHeatCapacity(_prototypeManager);
solution.Temperature = heatCap == 0 ? 0 : thermalEnergy / heatCap;
UpdateChemicals(owner, solution, true);
}
@@ -464,7 +525,8 @@ public sealed partial class SolutionContainerSystem : EntitySystem
if (thermalEnergy == 0.0f)
return;
solution.ThermalEnergy += thermalEnergy;
var heatCap = solution.GetHeatCapacity(_prototypeManager);
solution.Temperature += heatCap == 0 ? 0 : thermalEnergy / heatCap;
UpdateChemicals(owner, solution, true);
}

View File

@@ -52,7 +52,7 @@ namespace Content.Server.Chemistry.EntitySystems
}
var solRemoved = solution.SplitSolution(component.TransferAmount);
var solRemovedVol = solRemoved.TotalVolume;
var solRemovedVol = solRemoved.Volume;
var solToInject = solRemoved.SplitSolution(solRemovedVol * component.TransferEfficiency);

View File

@@ -54,7 +54,7 @@ public sealed class SolutionSpikableSystem : EntitySystem
return;
}
if (targetSolution.CurrentVolume == 0 && !spikableSource.IgnoreEmpty)
if (targetSolution.Volume == 0 && !spikableSource.IgnoreEmpty)
{
_popupSystem.PopupEntity(Loc.GetString(spikableSource.PopupEmpty, ("spiked-entity", target), ("spike-entity", source)), user, user);
return;
@@ -66,7 +66,7 @@ public sealed class SolutionSpikableSystem : EntitySystem
targetSolution.MaxVolume,
out var overflow))
{
if (overflow.TotalVolume > 0)
if (overflow.Volume > 0)
{
RaiseLocalEvent(target, new SolutionSpikeOverflowEvent(overflow));
}

View File

@@ -156,7 +156,7 @@ namespace Content.Server.Chemistry.EntitySystems
return FixedPoint2.Zero;
}
if (source.DrainAvailable == 0)
if (source.Volume == 0)
{
sourceEntity.PopupMessage(user,
Loc.GetString("comp-solution-transfer-is-empty", ("target", sourceEntity)));
@@ -178,7 +178,7 @@ namespace Content.Server.Chemistry.EntitySystems
return FixedPoint2.Zero;
}
var actualAmount = FixedPoint2.Min(amount, FixedPoint2.Min(source.DrainAvailable, target.AvailableVolume));
var actualAmount = FixedPoint2.Min(amount, FixedPoint2.Min(source.Volume, target.AvailableVolume));
var solutionSystem = Get<SolutionContainerSystem>();
var solution = solutionSystem.Drain(sourceEntity, source, actualAmount);

View File

@@ -71,7 +71,7 @@ namespace Content.Server.Chemistry.EntitySystems
internal bool TryAddSolution(VaporComponent vapor, Solution solution)
{
if (solution.TotalVolume == 0)
if (solution.Volume == 0)
{
return false;
}
@@ -120,7 +120,7 @@ namespace Content.Server.Chemistry.EntitySystems
}
}
if (contents.CurrentVolume == 0)
if (contents.Volume == 0)
{
// Delete this
EntityManager.QueueDeleteEntity(entity);

View File

@@ -77,7 +77,7 @@ namespace Content.Server.Chemistry.ReactionEffects
if (args.Source == null)
return;
var splitSolution = EntitySystem.Get<SolutionContainerSystem>().SplitSolution(args.SolutionEntity, args.Source, args.Source.MaxVolume);
var splitSolution = EntitySystem.Get<SolutionContainerSystem>().SplitSolution(args.SolutionEntity, args.Source, args.Source.Volume);
// We take the square root so it becomes harder to reach higher amount values
var amount = (int) Math.Round(_rangeConstant + _rangeMultiplier*Math.Sqrt(args.Quantity.Float()));
amount = Math.Min(amount, _maxRange);
@@ -90,7 +90,7 @@ namespace Content.Server.Chemistry.ReactionEffects
// Weird formulas here but basically when amount increases, solutionFraction gets closer to 0 in a reciprocal manner
// _reagentDilutionFactor defines how fast solutionFraction gets closer to 0
float solutionFraction = 1 / (_reagentDilutionFactor*(amount) + 1);
splitSolution.RemoveSolution(splitSolution.TotalVolume * (1 - solutionFraction));
splitSolution.RemoveSolution(splitSolution.Volume * (1 - solutionFraction));
}
var transform = args.EntityManager.GetComponent<TransformComponent>(args.SolutionEntity);

View File

@@ -1,5 +1,7 @@
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Prototypes;
using static Robust.Shared.Physics.DynamicTree;
namespace Content.Server.Chemistry.ReactionEffects
{
@@ -28,13 +30,12 @@ namespace Content.Server.Chemistry.ReactionEffects
/// Adjusts the temperature of the solution involved in the reaction.
/// </summary>
[DataDefinition]
[Virtual]
public class AdjustSolutionTemperatureEffect : ReagentEffect
public sealed class AdjustSolutionTemperatureEffect : ReagentEffect
{
/// <summary>
/// The total change in the thermal energy of the solution.
/// The change in temperature.
/// </summary>
[DataField("delta", required: true)] protected float Delta;
[DataField("delta", required: true)] private float _delta;
/// <summary>
/// The minimum temperature this effect can reach.
@@ -51,47 +52,62 @@ namespace Content.Server.Chemistry.ReactionEffects
/// </summary>
[DataField("scaled")] private bool _scaled;
/// <summary>
///
/// </summary>
/// <param name="solution"></param>
/// <returns></returns>
protected virtual float GetDeltaT(Solution solution) => Delta;
public override void Effect(ReagentEffectArgs args)
{
var solution = args.Source;
if (solution == null)
if (solution == null || solution.Volume == 0)
return;
var deltaT = GetDeltaT(solution);
if (_scaled)
deltaT = deltaT * (float) args.Quantity;
if (deltaT == 0.0d)
return;
if (deltaT > 0.0d && solution.Temperature >= _maxTemp)
return;
if (deltaT < 0.0d && solution.Temperature <= _minTemp)
return;
solution.Temperature = MathF.Max(MathF.Min(solution.Temperature + deltaT, _minTemp), _maxTemp);
var deltaT = _scaled ? _delta * (float) args.Quantity : _delta;
solution.Temperature = Math.Clamp(solution.Temperature + deltaT, _minTemp, _maxTemp);
}
}
/// <summary>
/// Adjusts the thermal energy of the solution involved in the reaction.
/// </summary>
public sealed class AdjustSolutionThermalEnergyEffect : AdjustSolutionTemperatureEffect
public sealed class AdjustSolutionThermalEnergyEffect : ReagentEffect
{
protected override float GetDeltaT(Solution solution)
/// <summary>
/// The change in energy.
/// </summary>
[DataField("delta", required: true)] private float _delta;
/// <summary>
/// The minimum temperature this effect can reach.
/// </summary>
[DataField("minTemp")] private float _minTemp = 0.0f;
/// <summary>
/// The maximum temperature this effect can reach.
/// </summary>
[DataField("maxTemp")] private float _maxTemp = float.PositiveInfinity;
/// <summary>
/// If true, then scale ranges by intensity. If not, the ranges are the same regardless of reactant amount.
/// </summary>
[DataField("scaled")] private bool _scaled;
public override void Effect(ReagentEffectArgs args)
{
var heatCapacity = solution.HeatCapacity;
if (heatCapacity == 0.0f)
return 0.0f;
return Delta / heatCapacity;
}
var solution = args.Source;
if (solution == null || solution.Volume == 0)
return;
if (_delta > 0 && solution.Temperature >= _maxTemp)
return;
if (_delta < 0 && solution.Temperature <= _minTemp)
return;
var heatCap = solution.GetHeatCapacity(null);
var deltaT = _scaled
? _delta / heatCap * (float) args.Quantity
: _delta / heatCap;
solution.Temperature = Math.Clamp(solution.Temperature + deltaT, _minTemp, _maxTemp);
}
}
}

View File

@@ -1,28 +0,0 @@
using Content.Shared.Chemistry.Reagent;
namespace Content.Server.Chemistry.ReagentEffectConditions
{
/// <summary>
/// Requires the solution to be above or below a certain thermal energy.
/// Used for things like explosives.
/// </summary>
public sealed class SolutionThermalEnergy : ReagentEffectCondition
{
[DataField("min")]
public float Min = 0.0f;
[DataField("max")]
public float Max = float.PositiveInfinity;
public override bool Condition(ReagentEffectArgs args)
{
if (args.Source == null)
return false;
if (args.Source.ThermalEnergy < Min)
return false;
if (args.Source.ThermalEnergy > Max)
return false;
return true;
}
}
}

View File

@@ -22,11 +22,11 @@ namespace Content.Server.Destructible.Thresholds.Behaviors
&& system.EntityManager.TryGetComponent(owner, out ExplosiveComponent? explosiveComponent))
{
// Don't explode if there's no solution
if (explodingSolution.CurrentVolume == 0)
if (explodingSolution.Volume == 0)
return;
// Scale the explosion intensity based on the remaining volume of solution
var explosionScaleFactor = (explodingSolution.CurrentVolume.Float() / explodingSolution.MaxVolume.Float());
var explosionScaleFactor = explodingSolution.FillFraction;
// TODO: Perhaps some of the liquid should be discarded as if it's being consumed by the explosion

View File

@@ -78,7 +78,7 @@ public sealed class FireExtinguisherSystem : EntitySystem
{
transfer = solTrans.TransferAmount;
}
transfer = FixedPoint2.Min(transfer, targetSolution.DrainAvailable);
transfer = FixedPoint2.Min(transfer, targetSolution.Volume);
if (transfer > 0)
{

View File

@@ -36,9 +36,6 @@ namespace Content.Server.Fluids.Components
[DataField("spillSound")]
public SoundSpecifier SpillSound = new SoundPathSpecifier("/Audio/Effects/Fluids/splat.ogg");
[ViewVariables(VVAccess.ReadOnly)]
public FixedPoint2 CurrentVolume => EntitySystem.Get<PuddleSystem>().CurrentVolume(Owner);
[DataField("overflowVolume")]
public FixedPoint2 OverflowVolume = DefaultOverflowVolume;

View File

@@ -85,11 +85,11 @@ namespace Content.Server.Fluids.EntitySystems
// the puddle's remaining volume (making it cleanly zero)
// the drain's remaining volume in its buffer.
var transferSolution = _solutionSystem.SplitSolution(puddle, puddleSolution,
FixedPoint2.Min(FixedPoint2.New(amount), puddleSolution.CurrentVolume, drainSolution.AvailableVolume));
FixedPoint2.Min(FixedPoint2.New(amount), puddleSolution.Volume, drainSolution.AvailableVolume));
_solutionSystem.TryAddSolution(drain.Owner, drainSolution, transferSolution);
if (puddleSolution.CurrentVolume <= 0)
if (puddleSolution.Volume <= 0)
{
QueueDel(puddle);
}

View File

@@ -33,12 +33,12 @@ namespace Content.Server.Fluids.EntitySystems
if (evaporationComponent.EvaporationToggle == true)
{
_solutionContainerSystem.SplitSolution(uid, solution,
FixedPoint2.Min(FixedPoint2.New(1), solution.CurrentVolume)); // removes 1 unit, or solution current volume, whichever is lower.
FixedPoint2.Min(FixedPoint2.New(1), solution.Volume)); // removes 1 unit, or solution current volume, whichever is lower.
}
evaporationComponent.EvaporationToggle =
solution.CurrentVolume > evaporationComponent.LowerLimit
&& solution.CurrentVolume < evaporationComponent.UpperLimit;
solution.Volume > evaporationComponent.LowerLimit
&& solution.Volume < evaporationComponent.UpperLimit;
}
}

View File

@@ -60,13 +60,13 @@ public sealed class MoppingSystem : EntitySystem
/// </summary>
private bool TryCreatePuddle(EntityUid user, EntityCoordinates clickLocation, AbsorbentComponent absorbent, Solution absorberSoln)
{
if (absorberSoln.CurrentVolume <= 0)
if (absorberSoln.Volume <= 0)
return false;
if (!_mapManager.TryGetGrid(clickLocation.GetGridUid(EntityManager), out var mapGrid))
return false;
var releaseAmount = FixedPoint2.Min(absorbent.ResidueAmount, absorberSoln.CurrentVolume);
var releaseAmount = FixedPoint2.Min(absorbent.ResidueAmount, absorberSoln.Volume);
var releasedSolution = _solutionSystem.SplitSolution(absorbent.Owner, absorberSoln, releaseAmount);
_spillableSystem.SpillAt(mapGrid.GetTileRef(clickLocation), releasedSolution, PuddlePrototypeId);
_popups.PopupEntity(Loc.GetString("mopping-system-release-to-floor"), user, user);
@@ -84,7 +84,7 @@ public sealed class MoppingSystem : EntitySystem
if (!_solutionSystem.TryGetDrainableSolution(target, out var drainableSolution))
return false;
if (drainableSolution.CurrentVolume <= 0)
if (drainableSolution.Volume <= 0)
{
var msg = Loc.GetString("mopping-system-target-container-empty", ("target", target));
_popups.PopupEntity(msg, user, user);
@@ -93,7 +93,7 @@ public sealed class MoppingSystem : EntitySystem
// Let's transfer up to to half the tool's available capacity to the tool.
var quantity = FixedPoint2.Max(component.PickupAmount, absorberSoln.AvailableVolume / 2);
quantity = FixedPoint2.Min(quantity, drainableSolution.CurrentVolume);
quantity = FixedPoint2.Min(quantity, drainableSolution.Volume);
DoMopInteraction(user, used, target, component, drainable.Solution, quantity, 1, "mopping-system-drainable-success", component.TransferSound);
return true;
@@ -104,7 +104,7 @@ public sealed class MoppingSystem : EntitySystem
/// </summary>
private bool TryEmptyAbsorber(EntityUid user, EntityUid used, EntityUid target, AbsorbentComponent component, Solution absorberSoln)
{
if (absorberSoln.CurrentVolume <= 0 || !TryComp(target, out RefillableSolutionComponent? refillable))
if (absorberSoln.Volume <= 0 || !TryComp(target, out RefillableSolutionComponent? refillable))
return false;
if (!_solutionSystem.TryGetRefillableSolution(target, out var targetSolution))
@@ -128,7 +128,7 @@ public sealed class MoppingSystem : EntitySystem
}
float delay;
FixedPoint2 quantity = absorberSoln.CurrentVolume;
FixedPoint2 quantity = absorberSoln.Volume;
// TODO this really needs cleaning up. Less magic numbers, more data-fields.
@@ -171,7 +171,7 @@ public sealed class MoppingSystem : EntitySystem
if (!TryComp(target, out PuddleComponent? puddle))
return false;
if (!_solutionSystem.TryGetSolution(target, puddle.SolutionName, out var puddleSolution) || puddleSolution.TotalVolume <= 0)
if (!_solutionSystem.TryGetSolution(target, puddle.SolutionName, out var puddleSolution) || puddleSolution.Volume <= 0)
return false;
FixedPoint2 quantity;
@@ -186,12 +186,12 @@ public sealed class MoppingSystem : EntitySystem
}
// Can our absorber even absorb any liquid?
if (puddleSolution.TotalVolume <= lowerLimit)
if (puddleSolution.Volume <= lowerLimit)
{
// Cannot absorb any more liquid. So clearly the user wants to add liquid to the puddle... right?
// This is the old behavior and I CBF fixing this, for the record I don't like this.
quantity = FixedPoint2.Min(absorber.ResidueAmount, absorberSoln.CurrentVolume);
quantity = FixedPoint2.Min(absorber.ResidueAmount, absorberSoln.Volume);
if (quantity <= 0)
return false;
@@ -208,7 +208,7 @@ public sealed class MoppingSystem : EntitySystem
return true;
}
quantity = FixedPoint2.Min(absorber.PickupAmount, puddleSolution.TotalVolume - lowerLimit, absorberSoln.AvailableVolume);
quantity = FixedPoint2.Min(absorber.PickupAmount, puddleSolution.Volume - lowerLimit, absorberSoln.AvailableVolume);
if (quantity <= 0)
return false;

View File

@@ -1,4 +1,4 @@
using Content.Server.Fluids.Components;
using Content.Server.Fluids.Components;
using Content.Shared.Fluids;
using Robust.Server.Player;
using Robust.Shared.Map;
@@ -10,6 +10,7 @@ public sealed class PuddleDebugDebugOverlaySystem : SharedPuddleDebugOverlaySyst
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly PuddleSystem _puddle = default!;
private readonly HashSet<IPlayerSession> _playerObservers = new();
@@ -72,7 +73,8 @@ public sealed class PuddleDebugDebugOverlaySystem : SharedPuddleDebugOverlaySyst
continue;
var pos = xform.Coordinates.ToVector2i(EntityManager, _mapManager);
data.Add(new PuddleDebugOverlayData(pos, puddle.CurrentVolume));
var vol = _puddle.CurrentVolume(uid, puddle);
data.Add(new PuddleDebugOverlayData(pos, vol));
}
RaiseNetworkEvent(new PuddleOverlayDebugMessage(gridUid, data.ToArray()));

View File

@@ -10,6 +10,7 @@ using Robust.Shared.Audio;
using Robust.Shared.Map;
using Robust.Shared.Player;
using Solution = Content.Shared.Chemistry.Components.Solution;
using Robust.Shared.Prototypes;
namespace Content.Server.Fluids.EntitySystems
{
@@ -19,6 +20,10 @@ namespace Content.Server.Fluids.EntitySystems
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly FluidSpreaderSystem _fluidSpreaderSystem = default!;
[Dependency] private readonly StepTriggerSystem _stepTrigger = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly IPrototypeManager _protoMan = default!;
public static float PuddleVolume = 1000;
// Using local deletion queue instead of the standard queue so that we can easily "undelete" if a puddle
// loses & then gains reagents in a single tick.
@@ -47,8 +52,7 @@ namespace Content.Server.Fluids.EntitySystems
private void OnPuddleInit(EntityUid uid, PuddleComponent component, ComponentInit args)
{
var solution = _solutionContainerSystem.EnsureSolution(uid, component.SolutionName);
solution.MaxVolume = FixedPoint2.New(1000);
_solutionContainerSystem.EnsureSolution(uid, component.SolutionName, FixedPoint2.New(PuddleVolume), out _);
}
private void OnSolutionUpdate(EntityUid uid, PuddleComponent component, SolutionChangedEvent args)
@@ -56,7 +60,7 @@ namespace Content.Server.Fluids.EntitySystems
if (args.Solution.Name != component.SolutionName)
return;
if (args.Solution.CurrentVolume <= 0)
if (args.Solution.Volume <= 0)
{
_deletionQueue.Add(uid);
return;
@@ -77,10 +81,10 @@ namespace Content.Server.Fluids.EntitySystems
// Opacity based on level of fullness to overflow
// Hard-cap lower bound for visibility reasons
var volumeScale = CurrentVolume(puddleComponent.Owner, puddleComponent).Float() /
var puddleSolution = _solutionContainerSystem.EnsureSolution(uid, puddleComponent.SolutionName);
var volumeScale = puddleSolution.Volume.Float() /
puddleComponent.OverflowVolume.Float() *
puddleComponent.OpacityModifier;
var puddleSolution = _solutionContainerSystem.EnsureSolution(uid, puddleComponent.SolutionName);
bool isEvaporating;
@@ -91,21 +95,24 @@ namespace Content.Server.Fluids.EntitySystems
}
else isEvaporating = false;
appearance.SetData(PuddleVisuals.VolumeScale, volumeScale);
appearance.SetData(PuddleVisuals.CurrentVolume, puddleComponent.CurrentVolume);
appearance.SetData(PuddleVisuals.SolutionColor, puddleSolution.Color);
appearance.SetData(PuddleVisuals.IsEvaporatingVisual, isEvaporating);
var color = puddleSolution.GetColor(_protoMan);
_appearance.SetData(uid, PuddleVisuals.VolumeScale, volumeScale, appearance);
_appearance.SetData(uid, PuddleVisuals.CurrentVolume, puddleSolution.Volume, appearance);
_appearance.SetData(uid, PuddleVisuals.SolutionColor, color, appearance);
_appearance.SetData(uid, PuddleVisuals.IsEvaporatingVisual, isEvaporating, appearance);
}
private void UpdateSlip(EntityUid entityUid, PuddleComponent puddleComponent)
{
var vol = CurrentVolume(puddleComponent.Owner, puddleComponent);
if ((puddleComponent.SlipThreshold == FixedPoint2.New(-1) ||
CurrentVolume(puddleComponent.Owner, puddleComponent) < puddleComponent.SlipThreshold) &&
vol < puddleComponent.SlipThreshold) &&
TryComp(entityUid, out StepTriggerComponent? stepTrigger))
{
_stepTrigger.SetActive(entityUid, false, stepTrigger);
}
else if (CurrentVolume(puddleComponent.Owner, puddleComponent) >= puddleComponent.SlipThreshold)
else if (vol >= puddleComponent.SlipThreshold)
{
var comp = EnsureComp<StepTriggerComponent>(entityUid);
_stepTrigger.SetActive(entityUid, true, comp);
@@ -143,7 +150,7 @@ namespace Content.Server.Fluids.EntitySystems
return _solutionContainerSystem.TryGetSolution(puddleComponent.Owner, puddleComponent.SolutionName,
out var solution)
? solution.CurrentVolume
? solution.Volume
: FixedPoint2.Zero;
}
@@ -165,15 +172,16 @@ namespace Content.Server.Fluids.EntitySystems
if (!Resolve(puddleUid, ref puddleComponent))
return false;
if (addedSolution.TotalVolume == 0 ||
if (addedSolution.Volume == 0 ||
!_solutionContainerSystem.TryGetSolution(puddleComponent.Owner, puddleComponent.SolutionName,
out var solution))
{
return false;
}
solution.AddSolution(addedSolution);
solution.AddSolution(addedSolution, _protoMan);
_solutionContainerSystem.UpdateChemicals(puddleUid, solution, true);
if (checkForOverflow && IsOverflowing(puddleUid, puddleComponent))
{
_fluidSpreaderSystem.AddOverflowingPuddle(puddleComponent.Owner, puddleComponent);
@@ -215,7 +223,7 @@ namespace Content.Server.Fluids.EntitySystems
out var destSolution))
continue;
var takeAmount = FixedPoint2.Max(0, dividedVolume - destSolution.CurrentVolume);
var takeAmount = FixedPoint2.Max(0, dividedVolume - destSolution.Volume);
TryAddSolution(destPuddle.Owner, srcSolution.SplitSolution(takeAmount), false, false, destPuddle);
if (stillOverflowing != null && IsOverflowing(destPuddle.Owner, destPuddle))
{
@@ -223,7 +231,7 @@ namespace Content.Server.Fluids.EntitySystems
}
}
if (stillOverflowing != null && srcSolution.CurrentVolume > sourcePuddleComponent.OverflowVolume)
if (stillOverflowing != null && srcSolution.Volume > sourcePuddleComponent.OverflowVolume)
{
stillOverflowing.Add(srcPuddle);
}
@@ -241,7 +249,7 @@ namespace Content.Server.Fluids.EntitySystems
if (!Resolve(uid, ref puddle))
return false;
return CurrentVolume(uid, puddle) + solution.TotalVolume > puddle.OverflowVolume;
return CurrentVolume(uid, puddle) + solution.Volume > puddle.OverflowVolume;
}
/// <summary>

View File

@@ -67,11 +67,11 @@ public sealed class SpillableSystem : EntitySystem
if (!_solutionContainerSystem.TryGetSolution(uid, component.SolutionName, out var solution))
return;
if (solution.TotalVolume == 0)
if (solution.Volume == 0)
return;
// spill all solution on the player
var drainedSolution = _solutionContainerSystem.Drain(uid, solution, solution.DrainAvailable);
var drainedSolution = _solutionContainerSystem.Drain(uid, solution, solution.Volume);
SpillAt(args.Equipee, drainedSolution, "PuddleSmear");
}
@@ -108,7 +108,7 @@ public sealed class SpillableSystem : EntitySystem
$"{ToPrettyString(uid):entity} spilled a solution {SolutionContainerSystem.ToPrettyString(solution):solution} on landing");
}
var drainedSolution = _solutionContainerSystem.Drain(uid, solution, solution.DrainAvailable);
var drainedSolution = _solutionContainerSystem.Drain(uid, solution, solution.Volume);
SpillAt(drainedSolution, EntityManager.GetComponent<TransformComponent>(uid).Coordinates, "PuddleSmear");
}
@@ -123,7 +123,7 @@ public sealed class SpillableSystem : EntitySystem
if (TryComp<DrinkComponent>(args.Target, out var drink) && (!drink.Opened))
return;
if (solution.DrainAvailable == FixedPoint2.Zero)
if (solution.Volume == FixedPoint2.Zero)
return;
Verb verb = new();
@@ -134,7 +134,7 @@ public sealed class SpillableSystem : EntitySystem
verb.Act = () =>
{
var puddleSolution = _solutionContainerSystem.SplitSolution(args.Target,
solution, solution.DrainAvailable);
solution, solution.Volume);
SpillAt(puddleSolution, Transform(args.Target).Coordinates, "PuddleSmear");
};
}
@@ -176,7 +176,7 @@ public sealed class SpillableSystem : EntitySystem
public PuddleComponent? SpillAt(Solution solution, EntityCoordinates coordinates, string prototype,
bool overflow = true, bool sound = true, bool combine = true)
{
if (solution.TotalVolume == 0) return null;
if (solution.Volume == 0) return null;
if (!_mapManager.TryGetGrid(coordinates.GetGridUid(EntityManager), out var mapGrid))
@@ -204,7 +204,7 @@ public sealed class SpillableSystem : EntitySystem
public PuddleComponent? SpillAt(TileRef tileRef, Solution solution, string prototype,
bool overflow = true, bool sound = true, bool noTileReact = false, bool combine = true)
{
if (solution.TotalVolume <= 0) return null;
if (solution.Volume <= 0) return null;
// If space return early, let that spill go out into the void
if (tileRef.Tile.IsEmpty) return null;
@@ -226,7 +226,7 @@ public sealed class SpillableSystem : EntitySystem
}
// Tile reactions used up everything.
if (solution.CurrentVolume == FixedPoint2.Zero)
if (solution.Volume == FixedPoint2.Zero)
return null;
// Get normalized co-ordinate for spill location and spill it in the centre
@@ -268,11 +268,11 @@ public sealed class SpillableSystem : EntitySystem
component.CancelToken = null;
//solution gone by other means before doafter completes
if (ev.Solution == null || ev.Solution.CurrentVolume == 0)
if (ev.Solution == null || ev.Solution.Volume == 0)
return;
var puddleSolution = _solutionContainerSystem.SplitSolution(uid,
ev.Solution, ev.Solution.DrainAvailable);
ev.Solution, ev.Solution.Volume);
SpillAt(puddleSolution, Transform(component.Owner).Coordinates, "PuddleSmear");
}

View File

@@ -12,6 +12,7 @@ using Content.Shared.Vapor;
using Robust.Shared.Audio;
using Robust.Shared.Map;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Server.Fluids.EntitySystems;
@@ -19,6 +20,7 @@ namespace Content.Server.Fluids.EntitySystems;
public sealed class SpraySystem : EntitySystem
{
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
@@ -51,7 +53,7 @@ public sealed class SpraySystem : EntitySystem
&& curTime < cooldown.CooldownEnd)
return;
if (solution.CurrentVolume <= 0)
if (solution.Volume <= 0)
{
_popupSystem.PopupEntity(Loc.GetString("spray-component-is-empty-message"), uid,
args.User);
@@ -76,7 +78,7 @@ public sealed class SpraySystem : EntitySystem
var threeQuarters = diffNorm * 0.75f;
var quarter = diffNorm * 0.25f;
var amount = Math.Max(Math.Min((solution.CurrentVolume / component.TransferAmount).Int(), component.VaporAmount), 1);
var amount = Math.Max(Math.Min((solution.Volume / component.TransferAmount).Int(), component.VaporAmount), 1);
var spread = component.VaporSpread / amount;
for (var i = 0; i < amount; i++)
@@ -94,7 +96,7 @@ public sealed class SpraySystem : EntitySystem
var newSolution = _solutionContainerSystem.SplitSolution(uid, solution, component.TransferAmount);
if (newSolution.TotalVolume <= FixedPoint2.Zero)
if (newSolution.Volume <= FixedPoint2.Zero)
break;
// Spawn the vapor cloud onto the grid/map the user is present on. Offset the start position based on how far the target destination is.
@@ -106,7 +108,7 @@ public sealed class SpraySystem : EntitySystem
if (TryComp(vapor, out AppearanceComponent? appearance))
{
appearance.SetData(VaporVisuals.Color, solution.Color.WithAlpha(1f));
appearance.SetData(VaporVisuals.Color, solution.GetColor(_proto).WithAlpha(1f));
appearance.SetData(VaporVisuals.State, true);
}

View File

@@ -87,7 +87,7 @@ namespace Content.Server.Kitchen.EntitySystems
if (TryComp<StackComponent>(item, out var stack))
{
var totalVolume = solution.TotalVolume * stack.Count;
var totalVolume = solution.Volume * stack.Count;
if (totalVolume <= 0)
continue;
@@ -102,7 +102,7 @@ namespace Content.Server.Kitchen.EntitySystems
}
else
{
if (solution.TotalVolume > containerSolution.AvailableVolume)
if (solution.Volume > containerSolution.AvailableVolume)
continue;
QueueDel(item);

View File

@@ -1,4 +1,4 @@
using System.Threading;
using System.Threading;
using Content.Server.Atmos;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Atmos.Piping.Components;
@@ -204,7 +204,7 @@ public sealed partial class CryoPodSystem: SharedCryoPodSystem
if (args.IsInDetailsRange && container != null && _solutionContainerSystem.TryGetFitsInDispenser(container.Value, out var containerSolution))
{
args.PushMarkup(Loc.GetString("cryo-pod-examine", ("beaker", Name(container.Value))));
if (containerSolution.CurrentVolume == 0)
if (containerSolution.Volume == 0)
{
args.PushMarkup(Loc.GetString("cryo-pod-empty-beaker"));
}

View File

@@ -71,11 +71,11 @@ namespace Content.Server.Nutrition.Components
}
if (TransferAmount == null)
return solution.CurrentVolume == 0 ? 0 : 1;
return solution.Volume == 0 ? 0 : 1;
return solution.CurrentVolume == 0
return solution.Volume == 0
? 0
: Math.Max(1, (int) Math.Ceiling((solution.CurrentVolume / (FixedPoint2)TransferAmount).Float()));
: Math.Max(1, (int) Math.Ceiling((solution.Volume / (FixedPoint2)TransferAmount).Float()));
}
}
}

View File

@@ -164,7 +164,7 @@ namespace Content.Server.Nutrition.EntitySystems
component.Opened = true;
UpdateAppearance(component);
var solution = _solutionContainerSystem.Drain(uid, interactions, interactions.DrainAvailable);
var solution = _solutionContainerSystem.Drain(uid, interactions, interactions.Volume);
_spillableSystem.SpillAt(uid, solution, "PuddleSmear");
_audio.PlayPvs(_audio.GetSound(component.BurstSound), uid, AudioParams.Default.WithVolume(-4));
@@ -240,7 +240,7 @@ namespace Content.Server.Nutrition.EntitySystems
}
if (!_solutionContainerSystem.TryGetDrainableSolution(drink.Owner, out var drinkSolution) ||
drinkSolution.DrainAvailable <= 0)
drinkSolution.Volume <= 0)
{
_popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-is-empty",
("entity", EntityManager.GetComponent<MetaDataComponent>(drink.Owner).EntityName)), drink.Owner, user);
@@ -301,7 +301,7 @@ namespace Content.Server.Nutrition.EntitySystems
return;
args.Drink.CancelToken = null;
var transferAmount = FixedPoint2.Min(args.Drink.TransferAmount, args.DrinkSolution.DrainAvailable);
var transferAmount = FixedPoint2.Min(args.Drink.TransferAmount, args.DrinkSolution.Volume);
var drained = _solutionContainerSystem.Drain(args.Drink.Owner, args.DrinkSolution, transferAmount);
var forceDrink = uid != args.User;

View File

@@ -163,8 +163,8 @@ namespace Content.Server.Nutrition.EntitySystems
return;
var transferAmount = args.Food.TransferAmount != null
? FixedPoint2.Min((FixedPoint2) args.Food.TransferAmount, args.FoodSolution.CurrentVolume)
: args.FoodSolution.CurrentVolume;
? FixedPoint2.Min((FixedPoint2) args.Food.TransferAmount, args.FoodSolution.Volume)
: args.FoodSolution.Volume;
var split = _solutionContainerSystem.SplitSolution((args.Food).Owner, args.FoodSolution, transferAmount);

View File

@@ -59,7 +59,7 @@ namespace Content.Server.Nutrition.EntitySystems
var sliceUid = EntityManager.SpawnEntity(component.Slice, transform.Coordinates);
var lostSolution = _solutionContainerSystem.SplitSolution(uid, solution,
solution.CurrentVolume / FixedPoint2.New(component.Count));
solution.Volume / FixedPoint2.New(component.Count));
// Fill new slice
FillSlice(sliceUid, lostSolution);

View File

@@ -117,12 +117,12 @@ namespace Content.Server.Nutrition.EntitySystems
var inhaledSolution = _solutionContainerSystem.SplitSolution(uid, solution, smokable.InhaleAmount * _timer);
if (solution.TotalVolume == FixedPoint2.Zero)
if (solution.Volume == FixedPoint2.Zero)
{
RaiseLocalEvent(uid, new SmokableSolutionEmptyEvent(), true);
}
if (inhaledSolution.TotalVolume == FixedPoint2.Zero)
if (inhaledSolution.Volume == FixedPoint2.Zero)
continue;
// This is awful. I hate this so much.

View File

@@ -39,7 +39,7 @@ namespace Content.Server.Nutrition.EntitySystems
public void UpdateTags(TrashOnEmptyComponent component, Solution solution)
{
if (solution.DrainAvailable <= 0)
if (solution.Volume <= 0)
{
_tagSystem.AddTag(component.Owner, "Trash");
return;

View File

@@ -147,8 +147,8 @@ public sealed class PayloadSystem : EntitySystem
|| !TryComp(beakerB, out FitsInDispenserComponent? compB)
|| !_solutionSystem.TryGetSolution(beakerA, compA.Solution, out var solutionA)
|| !_solutionSystem.TryGetSolution(beakerB, compB.Solution, out var solutionB)
|| solutionA.TotalVolume == 0
|| solutionB.TotalVolume == 0)
|| solutionA.Volume == 0
|| solutionB.Volume == 0)
{
return;
}
@@ -164,7 +164,7 @@ public sealed class PayloadSystem : EntitySystem
_solutionSystem.RemoveAllSolution(beakerB, solutionB);
// The grenade might be a dud. Redistribute solution:
var tmpSol = _solutionSystem.SplitSolution(beakerA, solutionA, solutionA.CurrentVolume * solutionB.MaxVolume / solutionA.MaxVolume);
var tmpSol = _solutionSystem.SplitSolution(beakerA, solutionA, solutionA.Volume * solutionB.MaxVolume / solutionA.MaxVolume);
_solutionSystem.TryAddSolution(beakerB, solutionB, tmpSol);
solutionA.MaxVolume -= solutionB.MaxVolume;
_solutionSystem.UpdateChemicals(beakerA, solutionA, false);

View File

@@ -120,7 +120,7 @@ public sealed class PowerCellSystem : SharedPowerCellSystem
private void OnSolutionChange(EntityUid uid, PowerCellComponent component, SolutionChangedEvent args)
{
component.IsRigged = _solutionsSystem.TryGetSolution(uid, PowerCellComponent.SolutionName, out var solution)
&& solution.ContainsReagent("Plasma", out var plasma)
&& solution.TryGetReagent("Plasma", out var plasma)
&& plasma >= 5;
if (component.IsRigged)

View File

@@ -1,4 +1,4 @@
using System.Linq;
using System.Linq;
using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Components.SolutionManager;
using Content.Server.Chemistry.EntitySystems;
@@ -221,7 +221,7 @@ namespace Content.Server.Tools
&& _solutionContainerSystem.TryGetDrainableSolution(target, out var targetSolution)
&& _solutionContainerSystem.TryGetSolution(uid, welder.FuelSolution, out var welderSolution))
{
var trans = FixedPoint2.Min(welderSolution.AvailableVolume, targetSolution.DrainAvailable);
var trans = FixedPoint2.Min(welderSolution.AvailableVolume, targetSolution.Volume);
if (trans > 0)
{
var drained = _solutionContainerSystem.Drain(target, targetSolution, trans);

View File

@@ -250,9 +250,9 @@ public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem
return;
var removedSolution = solutionContainer.SplitSolution(comp.TransferAmount * hitBloodstreams.Count);
var removedVol = removedSolution.TotalVolume;
var removedVol = removedSolution.Volume;
var solutionToInject = removedSolution.SplitSolution(removedVol * comp.TransferEfficiency);
var volPerBloodstream = solutionToInject.TotalVolume * (1 / hitBloodstreams.Count);
var volPerBloodstream = solutionToInject.Volume * (1 / hitBloodstreams.Count);
foreach (var bloodstream in hitBloodstreams)
{

View File

@@ -35,7 +35,7 @@ namespace Content.Server.Weapons.Ranged.Systems
if (!projectileSolutionContainers.Any())
return;
var solutionPerProjectile = ammoSolution.CurrentVolume * (1 / projectileSolutionContainers.Count);
var solutionPerProjectile = ammoSolution.Volume * (1 / projectileSolutionContainers.Count);
foreach (var (projectileUid, projectileSolution) in projectileSolutionContainers)
{

View File

@@ -1,98 +0,0 @@
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes;
namespace Content.Shared.Chemistry.Components
{
public sealed partial class Solution
{
/// <summary>
/// If reactions will be checked for when adding reagents to the container.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("canReact")]
public bool CanReact { get; set; } = true;
/// <summary>
/// If reactions can occur via mixing.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("canMix")]
public bool CanMix { get; set; } = false;
/// <summary>
/// Volume needed to fill this container.
/// </summary>
[ViewVariables]
public FixedPoint2 AvailableVolume => MaxVolume - CurrentVolume;
public FixedPoint2 DrawAvailable => CurrentVolume;
public FixedPoint2 DrainAvailable => CurrentVolume;
/// <summary>
/// Checks if a solution can fit into the container.
/// </summary>
/// <param name="solution">The solution that is trying to be added.</param>
/// <returns>If the solution can be fully added.</returns>
public bool CanAddSolution(Solution solution)
{
return solution.TotalVolume <= AvailableVolume;
}
[DataField("maxSpillRefill")]
public FixedPoint2 MaxSpillRefill { get; set; }
/// <summary>
/// Initially set <see cref="MaxVolume"/>. If empty will be calculated based
/// on sum of <see cref="Contents"/> fixed units.
/// </summary>
[DataField("maxVol")] public FixedPoint2 InitialMaxVolume;
[ViewVariables(VVAccess.ReadWrite)]
public FixedPoint2 MaxVolume { get; set; } = FixedPoint2.Zero;
[ViewVariables]
public FixedPoint2 CurrentVolume => TotalVolume;
/// <summary>
/// The total heat capacity of all reagents in the solution.
/// </summary>
[ViewVariables]
public float HeatCapacity => GetHeatCapacity();
/// <summary>
/// The average specific heat of all reagents in the solution.
/// </summary>
[ViewVariables]
public float SpecificHeat => HeatCapacity / (float) TotalVolume;
/// <summary>
/// The total thermal energy of the reagents in the solution.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public float ThermalEnergy {
get { return Temperature * HeatCapacity; }
set { Temperature = ((HeatCapacity == 0.0f) ? 0.0f : (value / HeatCapacity)); }
}
/// <summary>
/// Returns the total heat capacity of the reagents in this solution.
/// </summary>
/// <returns>The total heat capacity of the reagents in this solution.</returns>
private float GetHeatCapacity()
{
var heatCapacity = 0.0f;
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
foreach(var reagent in Contents)
{
if (!prototypeManager.TryIndex(reagent.ReagentId, out ReagentPrototype? proto))
proto = new ReagentPrototype();
heatCapacity += (float) reagent.Quantity * proto.SpecificHeat;
}
return heatCapacity;
}
}
}

View File

@@ -1,12 +1,13 @@
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Shared.Chemistry.Reagent;
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
{
@@ -17,7 +18,8 @@ namespace Content.Shared.Chemistry.Components
[DataDefinition]
public sealed partial class Solution : IEnumerable<Solution.ReagentQuantity>, ISerializationHooks
{
// Most objects on the station hold only 1 or 2 reagents
// 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);
@@ -25,7 +27,41 @@ namespace Content.Shared.Chemistry.Components
/// The calculated total volume of all reagents in the solution (ex. Total volume of liquid in beaker).
/// </summary>
[ViewVariables]
public FixedPoint2 TotalVolume { get; set; }
public FixedPoint2 Volume { get; set; }
/// <summary>
/// Maximum volume this solution supports.
/// </summary>
/// <remarks>
/// A value of zero means the maximum will automatically be set equal to the current volume during
/// initialization. Note that most solution methods ignore max volume altogether, but various solution
/// systems use this.
/// </remarks>
[DataField("maxVol")]
[ViewVariables(VVAccess.ReadWrite)]
public FixedPoint2 MaxVolume { get; set; } = FixedPoint2.Zero;
public float FillFraction => MaxVolume == 0 ? 1 : Volume.Float() / MaxVolume.Float();
/// <summary>
/// If reactions will be checked for when adding reagents to the container.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("canReact")]
public bool CanReact { get; set; } = true;
/// <summary>
/// If reactions can occur via mixing.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("canMix")]
public bool CanMix { get; set; } = false;
/// <summary>
/// Volume needed to fill this container.
/// </summary>
[ViewVariables]
public FixedPoint2 AvailableVolume => MaxVolume - Volume;
/// <summary>
/// The temperature of the reagents in the solution.
@@ -34,40 +70,155 @@ namespace Content.Shared.Chemistry.Components
[DataField("temperature")]
public float Temperature { get; set; } = 293.15f;
public Color Color => GetColor();
/// <summary>
/// The name of this solution, if it is contained in some <see cref="SolutionContainerManagerComponent"/>
/// </summary>
public string? Name;
/// <summary>
/// Checks if a solution can fit into the container.
/// </summary>
public bool CanAddSolution(Solution solution)
{
return solution.Volume <= AvailableVolume;
}
/// <summary>
/// The total heat capacity of all reagents in the solution.
/// </summary>
[ViewVariables]
private float _heatCapacity;
/// <summary>
/// If true, then <see cref="_heatCapacity"/> needs to be recomputed.
/// </summary>
[ViewVariables]
private bool _heatCapacityDirty = true;
public void UpdateHeatCapacity(IPrototypeManager? protoMan)
{
IoCManager.Resolve(ref protoMan);
DebugTools.Assert(_heatCapacityDirty);
_heatCapacityDirty = false;
_heatCapacity = 0;
foreach (var reagent in Contents)
{
_heatCapacity += (float) reagent.Quantity * protoMan.Index<ReagentPrototype>(reagent.ReagentId).SpecificHeat;
}
}
public float GetHeatCapacity(IPrototypeManager? protoMan)
{
if (_heatCapacityDirty)
UpdateHeatCapacity(protoMan);
return _heatCapacity;
}
/// <summary>
/// Constructs an empty solution (ex. an empty beaker).
/// </summary>
public Solution() { }
public Solution() : this(2) // Most objects on the station hold only 1 or 2 reagents.
{
}
/// <summary>
/// Constructs an empty solution (ex. an empty beaker).
/// </summary>
public Solution(int capacity)
{
Contents = new(capacity);
}
/// <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="quantity">The quantity in milli-units.</param>
public Solution(string reagentId, FixedPoint2 quantity)
public Solution(string reagentId, FixedPoint2 quantity) : this()
{
AddReagent(reagentId, quantity);
}
public Solution(IEnumerable<ReagentQuantity> reagents, bool setMaxVol = true)
{
Contents = new(reagents);
Volume = FixedPoint2.Zero;
foreach (var reagent in Contents)
{
Volume += reagent.Quantity;
}
if (setMaxVol)
MaxVolume = Volume;
ValidateSolution();
}
public Solution(Solution solution)
{
Volume = solution.Volume;
_heatCapacity = solution._heatCapacity;
_heatCapacityDirty = solution._heatCapacityDirty;
Contents = solution.Contents.ShallowClone();
ValidateSolution();
}
public Solution Clone()
{
return new Solution(this);
}
[AssertionMethod]
public void ValidateSolution()
{
// sandbox forbids: [Conditional("DEBUG")]
#if DEBUG
// Correct volume
DebugTools.Assert(Contents.Select(x => x.Quantity).Sum() == Volume);
// 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);
// If it isn't flagged as dirty, check heat capacity is correct.
if (!_heatCapacityDirty)
{
var cur = _heatCapacity;
_heatCapacityDirty = true;
UpdateHeatCapacity(null);
DebugTools.Assert(MathHelper.CloseTo(_heatCapacity, cur));
}
#endif
}
void ISerializationHooks.AfterDeserialization()
{
TotalVolume = FixedPoint2.Zero;
Contents.ForEach(reagent => TotalVolume += reagent.Quantity);
Volume = FixedPoint2.Zero;
foreach (var reagent in Contents)
{
Volume += reagent.Quantity;
}
if (MaxVolume == FixedPoint2.Zero)
MaxVolume = Volume;
}
public bool ContainsReagent(string reagentId)
{
return ContainsReagent(reagentId, out _);
foreach (var reagent in Contents)
{
if (reagent.ReagentId == reagentId)
return true;
}
public bool ContainsReagent(string reagentId, out FixedPoint2 quantity)
return false;
}
public bool TryGetReagent(string reagentId, out FixedPoint2 quantity)
{
foreach (var reagent in Contents)
{
@@ -82,15 +233,22 @@ namespace Content.Shared.Chemistry.Components
return false;
}
public string GetPrimaryReagentId()
public string? GetPrimaryReagentId()
{
if (Contents.Count == 0)
return null;
ReagentQuantity max = default;
foreach (var reagent in Contents)
{
return "";
if (reagent.Quantity >= max.Quantity)
{
max = reagent;
}
}
var majorReagent = Contents.MaxBy(reagent => reagent.Quantity);
return majorReagent.ReagentId;
return max.ReagentId!;
}
/// <summary>
@@ -98,16 +256,16 @@ namespace Content.Shared.Chemistry.Components
/// </summary>
/// <param name="reagentId">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, float? temperature = null)
public void AddReagent(string reagentId, FixedPoint2 quantity, bool dirtyHeatCap = true)
{
if (quantity <= 0)
{
DebugTools.Assert(quantity == 0, "Attempted to add negative reagent quantity");
return;
if (!IoCManager.Resolve<IPrototypeManager>().TryIndex(reagentId, out ReagentPrototype? proto))
proto = new ReagentPrototype();
}
var actualTemp = temperature ?? Temperature;
var oldThermalEnergy = Temperature * GetHeatCapacity();
var addedThermalEnergy = (float) quantity * proto.SpecificHeat * actualTemp;
Volume += quantity;
_heatCapacityDirty |= dirtyHeatCap;
for (var i = 0; i < Contents.Count; i++)
{
var reagent = Contents[i];
@@ -115,16 +273,65 @@ namespace Content.Shared.Chemistry.Components
continue;
Contents[i] = new ReagentQuantity(reagentId, reagent.Quantity + quantity);
TotalVolume += quantity;
ThermalEnergy = oldThermalEnergy + addedThermalEnergy;
ValidateSolution();
return;
}
Contents.Add(new ReagentQuantity(reagentId, quantity));
ValidateSolution();
}
TotalVolume += quantity;
ThermalEnergy = oldThermalEnergy + addedThermalEnergy;
/// <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)
{
AddReagent(proto.ID, quantity, false);
_heatCapacity += quantity.Float() * proto.SpecificHeat;
}
/// <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)
{
if (_heatCapacityDirty)
UpdateHeatCapacity(protoMan);
var totalThermalEnergy = Temperature * _heatCapacity + temperature * proto.SpecificHeat;
AddReagent(proto, quantity);
Temperature = _heatCapacity == 0 ? 0 : totalThermalEnergy / _heatCapacity;
}
/// <summary>
/// Scales the amount of solution by some integer quantity.
/// </summary>
/// <param name="scale">The scalar to modify the solution by.</param>
public void ScaleSolution(int scale)
{
if (scale == 1)
return;
if (scale == 0)
{
RemoveAllSolution();
return;
}
_heatCapacity *= scale;
Volume *= scale;
for (int i = 0; i <= Contents.Count; i++)
{
var old = Contents[i];
Contents[i] = new ReagentQuantity(old.ReagentId, old.Quantity * scale);
}
ValidateSolution();
}
/// <summary>
@@ -133,21 +340,31 @@ namespace Content.Shared.Chemistry.Components
/// <param name="scale">The scalar to modify the solution by.</param>
public void ScaleSolution(float scale)
{
if (scale.Equals(1f))
if (scale == 1)
return;
var tempContents = new List<ReagentQuantity>(Contents);
foreach(var current in tempContents)
if (scale == 0)
{
if(scale > 1)
{
AddReagent(current.ReagentId, current.Quantity * scale - current.Quantity);
RemoveAllSolution();
return;
}
Volume = FixedPoint2.Zero;
for (int i = Contents.Count - 1; i >= 0; i--)
{
var old = Contents[i];
var newQuantity = old.Quantity * scale;
if (newQuantity == FixedPoint2.Zero)
Contents.RemoveSwap(i);
else
{
RemoveReagent(current.ReagentId, current.Quantity - current.Quantity * scale);
Contents[i] = new ReagentQuantity(old.ReagentId, newQuantity);
Volume += newQuantity;
}
}
_heatCapacityDirty = true;
ValidateSolution();
}
/// <summary>
@@ -159,11 +376,12 @@ namespace Content.Shared.Chemistry.Components
{
for (var i = 0; i < Contents.Count; i++)
{
if (Contents[i].ReagentId == reagentId)
return Contents[i].Quantity;
var reagent = Contents[i];
if (reagent.ReagentId == reagentId)
return reagent.Quantity;
}
return FixedPoint2.New(0);
return FixedPoint2.Zero;
}
/// <summary>
@@ -174,7 +392,7 @@ namespace Content.Shared.Chemistry.Components
/// <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)
{
if(quantity <= 0)
if (quantity <= FixedPoint2.Zero)
return FixedPoint2.Zero;
for (var i = 0; i < Contents.Count; i++)
@@ -186,16 +404,19 @@ namespace Content.Shared.Chemistry.Components
var curQuantity = reagent.Quantity;
var newQuantity = curQuantity - quantity;
_heatCapacityDirty = true;
if (newQuantity <= 0)
{
Contents.RemoveSwap(i);
TotalVolume -= curQuantity;
Volume -= curQuantity;
ValidateSolution();
return curQuantity;
}
Contents[i] = new ReagentQuantity(reagentId, newQuantity);
TotalVolume -= quantity;
Volume -= quantity;
ValidateSolution();
return quantity;
}
@@ -203,104 +424,150 @@ namespace Content.Shared.Chemistry.Components
return FixedPoint2.Zero;
}
/// <summary>
/// Remove the specified quantity from this solution.
/// </summary>
/// <param name="quantity">The quantity of this solution to remove</param>
public void RemoveSolution(FixedPoint2 quantity)
{
if(quantity <= 0)
return;
var ratio = (TotalVolume - quantity).Double() / TotalVolume.Double();
if (ratio <= 0)
{
RemoveAllSolution();
return;
}
for (var i = 0; i < Contents.Count; i++)
{
var reagent = Contents[i];
var oldQuantity = reagent.Quantity;
// quantity taken is always a little greedy, so fractional quantities get rounded up to the nearest
// whole unit. This should prevent little bits of chemical remaining because of float rounding errors.
var newQuantity = oldQuantity * ratio;
Contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity);
}
TotalVolume *= ratio;
}
public void RemoveAllSolution()
{
Contents.Clear();
TotalVolume = FixedPoint2.New(0);
Volume = FixedPoint2.Zero;
_heatCapacityDirty = false;
_heatCapacity = 0;
}
public Solution SplitSolution(FixedPoint2 quantity)
public Solution SplitSolution(FixedPoint2 toTake)
{
if (quantity <= 0)
if (toTake <= FixedPoint2.Zero)
return new Solution();
Solution newSolution;
if (quantity >= TotalVolume)
if (toTake >= Volume)
{
newSolution = Clone();
RemoveAllSolution();
return newSolution;
}
newSolution = new Solution();
var newTotalVolume = FixedPoint2.New(0);
var newHeatCapacity = 0.0d;
var remainingVolume = TotalVolume;
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
var origVol = Volume;
var effVol = Volume.Value;
newSolution = new Solution(Contents.Count) { Temperature = Temperature };
var remaining = (long) toTake.Value;
for (var i = Contents.Count - 1; i >= 0; i--)
for (var i = Contents.Count - 1; i >= 0; i--) // iterate backwards because of remove swap.
{
if (remainingVolume == FixedPoint2.Zero)
// shouldn't happen, but it can if someone, somehow has a reagent with 0-quantity in a solution.
break;
var reagent = Contents[i];
var ratio = (remainingVolume - quantity).Double() / remainingVolume.Double();
if(!prototypeManager.TryIndex(reagent.ReagentId, out ReagentPrototype? proto))
proto = new ReagentPrototype();
remainingVolume -= reagent.Quantity;
// This is set up such that integer rounding will tend to take more reagents.
var split = remaining * reagent.Quantity.Value / effVol;
var newQuantity = reagent.Quantity * ratio;
var splitQuantity = reagent.Quantity - newQuantity;
if (newQuantity > 0)
Contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity);
else
Contents.RemoveAt(i);
if (splitQuantity > 0)
newSolution.Contents.Add(new ReagentQuantity(reagent.ReagentId, splitQuantity));
newTotalVolume += splitQuantity;
newHeatCapacity += (float) splitQuantity * proto.SpecificHeat;
quantity -= splitQuantity;
if (split <= 0)
{
effVol -= reagent.Quantity.Value;
DebugTools.Assert(split == 0, "Negative solution quantity while splitting? Long/int overflow?");
continue;
}
newSolution.TotalVolume = newTotalVolume;
newSolution.Temperature = Temperature;
TotalVolume -= newTotalVolume;
var splitQuantity = FixedPoint2.FromCents((int) split);
var newQuantity = reagent.Quantity - splitQuantity;
DebugTools.Assert(newQuantity >= 0);
if (newQuantity > FixedPoint2.Zero)
Contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity);
else
Contents.RemoveSwap(i);
newSolution.Contents.Add(new ReagentQuantity(reagent.ReagentId, splitQuantity));
Volume -= splitQuantity;
remaining -= split;
effVol -= reagent.Quantity.Value;
}
newSolution.Volume = origVol - Volume;
DebugTools.Assert(remaining >= 0);
DebugTools.Assert(remaining == 0 || Volume == FixedPoint2.Zero);
_heatCapacityDirty = true;
newSolution._heatCapacityDirty = true;
ValidateSolution();
newSolution.ValidateSolution();
return newSolution;
}
public void AddSolution(Solution otherSolution)
/// <summary>
/// Variant of <see cref="SplitSolution(FixedPoint2)"/> that doesn't return a new solution containing the removed reagents.
/// </summary>
/// <param name="quantity">The quantity of this solution to remove</param>
public void RemoveSolution(FixedPoint2 toTake)
{
var oldThermalEnergy = Temperature * GetHeatCapacity();
var addedThermalEnergy = otherSolution.Temperature * otherSolution.GetHeatCapacity();
if (toTake <= FixedPoint2.Zero)
return;
if (toTake >= Volume)
{
RemoveAllSolution();
return;
}
var effVol = Volume.Value;
Volume -= toTake;
var remaining = (long) toTake.Value;
for (var i = Contents.Count - 1; i >= 0; i--)// iterate backwards because of remove swap.
{
var reagent = Contents[i];
// This is set up such that integer rounding will tend to take more reagents.
var split = remaining * reagent.Quantity.Value / effVol;
if (split <= 0)
{
effVol -= reagent.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;
if (newQuantity > FixedPoint2.Zero)
Contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity);
else
Contents.RemoveSwap(i);
remaining -= split;
effVol -= reagent.Quantity.Value;
}
DebugTools.Assert(remaining >= 0);
DebugTools.Assert(remaining == 0 || Volume == FixedPoint2.Zero);
_heatCapacityDirty = true;
ValidateSolution();
}
public void AddSolution(Solution otherSolution, IPrototypeManager? protoMan)
{
if (otherSolution.Volume <= FixedPoint2.Zero)
return;
Volume += otherSolution.Volume;
var closeTemps = MathHelper.CloseTo(otherSolution.Temperature, Temperature);
float totalThermalEnergy = 0;
if (!closeTemps)
{
IoCManager.Resolve(ref protoMan);
if (_heatCapacityDirty)
UpdateHeatCapacity(protoMan);
if (otherSolution._heatCapacityDirty)
otherSolution.UpdateHeatCapacity(protoMan);
totalThermalEnergy = _heatCapacity * Temperature + otherSolution._heatCapacity * otherSolution.Temperature;
}
for (var i = 0; i < otherSolution.Contents.Count; i++)
{
var otherReagent = otherSolution.Contents[i];
@@ -323,65 +590,50 @@ namespace Content.Shared.Chemistry.Components
}
}
TotalVolume += otherSolution.TotalVolume;
ThermalEnergy = oldThermalEnergy + addedThermalEnergy;
_heatCapacity += otherSolution._heatCapacity;
if (closeTemps)
_heatCapacityDirty |= otherSolution._heatCapacityDirty;
else
Temperature = _heatCapacity == 0 ? 0 : totalThermalEnergy / _heatCapacity;
ValidateSolution();
}
private Color GetColor()
public Color GetColor(IPrototypeManager? protoMan)
{
if (TotalVolume == 0)
if (Volume == FixedPoint2.Zero)
{
return Color.Transparent;
}
IoCManager.Resolve(ref protoMan);
Color mixColor = default;
var runningTotalQuantity = FixedPoint2.New(0);
var protoManager = IoCManager.Resolve<IPrototypeManager>();
bool first = true;
foreach (var reagent in Contents)
{
runningTotalQuantity += reagent.Quantity;
if (!protoManager.TryIndex(reagent.ReagentId, out ReagentPrototype? proto))
if (!protoMan.TryIndex(reagent.ReagentId, out ReagentPrototype? proto))
{
continue;
}
if (mixColor == default)
if (first)
{
first = false;
mixColor = proto.SubstanceColor;
continue;
}
var interpolateValue = (1 / runningTotalQuantity.Float()) * reagent.Quantity.Float();
var interpolateValue = reagent.Quantity.Float() / runningTotalQuantity.Float();
mixColor = Color.InterpolateBetween(mixColor, proto.SubstanceColor, interpolateValue);
}
return mixColor;
}
public Solution Clone()
{
var volume = FixedPoint2.New(0);
var heatCapacity = 0.0d;
var newSolution = new Solution();
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
for (var i = 0; i < Contents.Count; i++)
{
var reagent = Contents[i];
if (!prototypeManager.TryIndex(reagent.ReagentId, out ReagentPrototype? proto))
proto = new ReagentPrototype();
newSolution.Contents.Add(reagent);
volume += reagent.Quantity;
heatCapacity += (float) reagent.Quantity * proto.SpecificHeat;
}
newSolution.TotalVolume = volume;
newSolution.Temperature = Temperature;
return newSolution;
}
[Obsolete("Use ReactiveSystem.DoEntityReaction")]
public void DoEntityReaction(EntityUid uid, ReactionMethod method)
{
@@ -429,7 +681,23 @@ namespace Content.Shared.Chemistry.Components
{
return GetEnumerator();
}
#endregion
public void SetContents(IEnumerable<ReagentQuantity> reagents, bool setMaxVol = false)
{
RemoveAllSolution();
_heatCapacityDirty = true;
Contents = new(reagents);
foreach (var reagent in Contents)
{
Volume += reagent.Quantity;
}
if (setMaxVol)
MaxVolume = Volume;
ValidateSolution();
}
}
}

View File

@@ -140,7 +140,7 @@ namespace Content.Shared.Chemistry.Reaction
var reactantName = reactantData.Key;
var reactantCoefficient = reactantData.Value.Amount;
if (!solution.ContainsReagent(reactantName, out var reactantQuantity))
if (!solution.TryGetReagent(reactantName, out var reactantQuantity))
return false;
if (reactantData.Value.Catalyst)
@@ -252,12 +252,12 @@ namespace Content.Shared.Chemistry.Reaction
// Remove any reactions that were not applicable. Avoids re-iterating over them in future.
reactions.Except(toRemove);
if (products.TotalVolume <= 0)
if (products.Volume <= 0)
return true;
// remove excess product
// TODO spill excess?
var excessVolume = solution.TotalVolume + products.TotalVolume - maxVolume;
var excessVolume = solution.Volume + products.Volume - maxVolume;
if (excessVolume > 0)
products.RemoveSolution(excessVolume);
@@ -269,7 +269,7 @@ namespace Content.Shared.Chemistry.Reaction
reactions.UnionWith(reactantReactions);
}
solution.AddSolution(products);
solution.AddSolution(products, _prototypeManager);
return true;
}

View File

@@ -11,7 +11,7 @@ namespace Content.Shared.FixedPoint
[Serializable, CopyByRef]
public struct FixedPoint2 : ISelfSerialize, IComparable<FixedPoint2>, IEquatable<FixedPoint2>, IFormattable
{
private int _value;
public int Value { get; private set; }
private const int Shift = 2;
public static FixedPoint2 MaxValue { get; } = new(int.MaxValue);
@@ -20,12 +20,12 @@ namespace Content.Shared.FixedPoint
private readonly double ShiftDown()
{
return _value / Math.Pow(10, Shift);
return Value / Math.Pow(10, Shift);
}
private FixedPoint2(int value)
{
_value = value;
Value = value;
}
public static FixedPoint2 New(int value)
@@ -33,6 +33,8 @@ namespace Content.Shared.FixedPoint
return new(value * (int) Math.Pow(10, Shift));
}
public static FixedPoint2 FromCents(int value) => new(value);
public static FixedPoint2 New(float value)
{
return new(FromFloat(value));
@@ -60,42 +62,42 @@ namespace Content.Shared.FixedPoint
public static FixedPoint2 operator +(FixedPoint2 a) => a;
public static FixedPoint2 operator -(FixedPoint2 a) => new(-a._value);
public static FixedPoint2 operator -(FixedPoint2 a) => new(-a.Value);
public static FixedPoint2 operator +(FixedPoint2 a, FixedPoint2 b)
=> new(a._value + b._value);
=> new(a.Value + b.Value);
public static FixedPoint2 operator -(FixedPoint2 a, FixedPoint2 b)
=> new(a._value - b._value);
=> new(a.Value - b.Value);
public static FixedPoint2 operator *(FixedPoint2 a, FixedPoint2 b)
{
return new((int) MathF.Round(b._value * a._value / MathF.Pow(10, Shift), MidpointRounding.AwayFromZero));
return new((int) MathF.Round(b.Value * a.Value / MathF.Pow(10, Shift), MidpointRounding.AwayFromZero));
}
public static FixedPoint2 operator *(FixedPoint2 a, float b)
{
return new((int) MathF.Round(a._value * b, MidpointRounding.AwayFromZero));
return new((int) MathF.Round(a.Value * b, MidpointRounding.AwayFromZero));
}
public static FixedPoint2 operator *(FixedPoint2 a, double b)
{
return new((int) Math.Round(a._value * b, MidpointRounding.AwayFromZero));
return new((int) Math.Round(a.Value * b, MidpointRounding.AwayFromZero));
}
public static FixedPoint2 operator *(FixedPoint2 a, int b)
{
return new(a._value * b);
return new(a.Value * b);
}
public static FixedPoint2 operator /(FixedPoint2 a, FixedPoint2 b)
{
return new((int) MathF.Round((MathF.Pow(10, Shift) * a._value) / b._value, MidpointRounding.AwayFromZero));
return new((int) MathF.Round((MathF.Pow(10, Shift) * a.Value) / b.Value, MidpointRounding.AwayFromZero));
}
public static FixedPoint2 operator /(FixedPoint2 a, float b)
{
return new((int) MathF.Round(a._value / b, MidpointRounding.AwayFromZero));
return new((int) MathF.Round(a.Value / b, MidpointRounding.AwayFromZero));
}
public static bool operator <=(FixedPoint2 a, int b)
@@ -140,22 +142,22 @@ namespace Content.Shared.FixedPoint
public static bool operator <=(FixedPoint2 a, FixedPoint2 b)
{
return a._value <= b._value;
return a.Value <= b.Value;
}
public static bool operator >=(FixedPoint2 a, FixedPoint2 b)
{
return a._value >= b._value;
return a.Value >= b.Value;
}
public static bool operator <(FixedPoint2 a, FixedPoint2 b)
{
return a._value < b._value;
return a.Value < b.Value;
}
public static bool operator >(FixedPoint2 a, FixedPoint2 b)
{
return a._value > b._value;
return a.Value > b.Value;
}
public readonly float Float()
@@ -199,7 +201,7 @@ namespace Content.Shared.FixedPoint
public static FixedPoint2 Abs(FixedPoint2 a)
{
return FixedPoint2.New(Math.Abs(a._value));
return FixedPoint2.New(Math.Abs(a.Value));
}
public static FixedPoint2 Dist(FixedPoint2 a, FixedPoint2 b)
@@ -220,18 +222,18 @@ namespace Content.Shared.FixedPoint
public override readonly bool Equals(object? obj)
{
return obj is FixedPoint2 unit &&
_value == unit._value;
Value == unit.Value;
}
public override readonly int GetHashCode()
{
// ReSharper disable once NonReadonlyMemberInGetHashCode
return HashCode.Combine(_value);
return HashCode.Combine(Value);
}
public void Deserialize(string value)
{
_value = FromFloat(FloatFromString(value));
Value = FromFloat(FloatFromString(value));
}
public override readonly string ToString() => $"{ShiftDown().ToString(CultureInfo.InvariantCulture)}";
@@ -248,16 +250,16 @@ namespace Content.Shared.FixedPoint
public readonly bool Equals(FixedPoint2 other)
{
return _value == other._value;
return Value == other.Value;
}
public readonly int CompareTo(FixedPoint2 other)
{
if (other._value > _value)
if (other.Value > Value)
{
return -1;
}
if (other._value < _value)
if (other.Value < Value)
{
return 1;
}

View File

@@ -43,6 +43,7 @@ public sealed class Solution_Tests : ContentUnitTest
Assert.That(quantity.Int(), Is.EqualTo(0));
}
#if !DEBUG
[Test]
public void AddLessThanZeroReagentReturnsZero()
{
@@ -51,6 +52,7 @@ public sealed class Solution_Tests : ContentUnitTest
Assert.That(quantity.Int(), Is.EqualTo(0));
}
#endif
[Test]
public void AddingReagentsSumsProperly()
@@ -81,7 +83,7 @@ public sealed class Solution_Tests : ContentUnitTest
solution.AddReagent("water", FixedPoint2.New(1000));
solution.AddReagent("fire", FixedPoint2.New(2000));
Assert.That(solution.TotalVolume.Int(), Is.EqualTo(3000));
Assert.That(solution.Volume.Int(), Is.EqualTo(3000));
}
[Test]
@@ -95,7 +97,7 @@ public sealed class Solution_Tests : ContentUnitTest
Assert.That(newSolution.GetReagentQuantity("water").Int(), Is.EqualTo(1000));
Assert.That(newSolution.GetReagentQuantity("fire").Int(), Is.EqualTo(2000));
Assert.That(newSolution.TotalVolume.Int(), Is.EqualTo(3000));
Assert.That(newSolution.Volume.Int(), Is.EqualTo(3000));
}
[Test]
@@ -109,7 +111,7 @@ public sealed class Solution_Tests : ContentUnitTest
Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(500));
Assert.That(solution.GetReagentQuantity("fire").Int(), Is.EqualTo(2000));
Assert.That(solution.TotalVolume.Int(), Is.EqualTo(2500));
Assert.That(solution.Volume.Int(), Is.EqualTo(2500));
}
[Test]
@@ -120,7 +122,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.TotalVolume.Int(), Is.EqualTo(100));
Assert.That(solution.Volume.Int(), Is.EqualTo(100));
}
[Test]
@@ -131,7 +133,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.TotalVolume.Int(), Is.EqualTo(0));
Assert.That(solution.Volume.Int(), Is.EqualTo(0));
}
[Test]
@@ -142,7 +144,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.TotalVolume.Int(), Is.EqualTo(100));
Assert.That(solution.Volume.Int(), Is.EqualTo(100));
}
[Test]
@@ -154,7 +156,7 @@ public sealed class Solution_Tests : ContentUnitTest
//Check that edited solution is correct
Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(200));
Assert.That(solution.TotalVolume.Int(), Is.EqualTo(200));
Assert.That(solution.Volume.Int(), Is.EqualTo(200));
}
[Test]
@@ -166,7 +168,7 @@ public sealed class Solution_Tests : ContentUnitTest
//Check that edited solution is correct
Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(0));
Assert.That(solution.TotalVolume.Int(), Is.EqualTo(0));
Assert.That(solution.Volume.Int(), Is.EqualTo(0));
}
[Test]
@@ -180,7 +182,7 @@ public sealed class Solution_Tests : ContentUnitTest
Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(500));
Assert.That(solution.GetReagentQuantity("fire").Int(), Is.EqualTo(1000));
Assert.That(solution.TotalVolume.Int(), Is.EqualTo(1500));
Assert.That(solution.Volume.Int(), Is.EqualTo(1500));
}
[Test]
@@ -191,7 +193,7 @@ public sealed class Solution_Tests : ContentUnitTest
solution.RemoveSolution(FixedPoint2.New(-200));
Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(800));
Assert.That(solution.TotalVolume.Int(), Is.EqualTo(800));
Assert.That(solution.Volume.Int(), Is.EqualTo(800));
}
[Test]
@@ -205,11 +207,11 @@ public sealed class Solution_Tests : ContentUnitTest
Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(750));
Assert.That(solution.GetReagentQuantity("fire").Int(), Is.EqualTo(1500));
Assert.That(solution.TotalVolume.Int(), Is.EqualTo(2250));
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.TotalVolume.Int(), Is.EqualTo(750));
Assert.That(splitSolution.Volume.Int(), Is.EqualTo(750));
}
[Test]
@@ -221,13 +223,13 @@ public sealed class Solution_Tests : ContentUnitTest
var splitSolution = solution.SplitSolution(FixedPoint2.New(1));
Assert.That(solution.GetReagentQuantity("water").Float(), Is.EqualTo(0.67f));
Assert.That(solution.GetReagentQuantity("fire").Float(), Is.EqualTo(1.33f));
Assert.That(solution.TotalVolume.Int(), Is.EqualTo(2));
Assert.That(solution.GetReagentQuantity("water").Float(), Is.EqualTo(0.66f));
Assert.That(solution.GetReagentQuantity("fire").Float(), Is.EqualTo(1.34f));
Assert.That(solution.Volume.Int(), Is.EqualTo(2));
Assert.That(splitSolution.GetReagentQuantity("water").Float(), Is.EqualTo(0.33f));
Assert.That(splitSolution.GetReagentQuantity("fire").Float(), Is.EqualTo(0.67f));
Assert.That(splitSolution.TotalVolume.Int(), Is.EqualTo(1));
Assert.That(splitSolution.GetReagentQuantity("water").Float(), Is.EqualTo(0.34f));
Assert.That(splitSolution.GetReagentQuantity("fire").Float(), Is.EqualTo(0.66f));
Assert.That(splitSolution.Volume.Int(), Is.EqualTo(1));
}
[Test]
@@ -241,11 +243,11 @@ public sealed class Solution_Tests : ContentUnitTest
Assert.That(solution.GetReagentQuantity("water").Float(), Is.EqualTo(0.33f));
Assert.That(solution.GetReagentQuantity("fire").Float(), Is.EqualTo(0.67f));
Assert.That(solution.TotalVolume.Int(), Is.EqualTo(1));
Assert.That(solution.Volume.Int(), Is.EqualTo(1));
Assert.That(splitSolution.GetReagentQuantity("water").Float(), Is.EqualTo(0.67f));
Assert.That(splitSolution.GetReagentQuantity("fire").Float(), Is.EqualTo(1.33f));
Assert.That(splitSolution.TotalVolume.Int(), Is.EqualTo(2));
Assert.That(splitSolution.Volume.Int(), Is.EqualTo(2));
}
[Test]
@@ -259,10 +261,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.TotalVolume.Float(), Is.EqualTo(remainder));
Assert.That(solution.Volume.Float(), Is.EqualTo(remainder));
Assert.That(splitSolution.GetReagentQuantity("water").Float(), Is.EqualTo(reduce));
Assert.That(splitSolution.TotalVolume.Float(), Is.EqualTo(reduce));
Assert.That(splitSolution.Volume.Float(), Is.EqualTo(reduce));
}
[Test]
@@ -280,7 +282,7 @@ public sealed class Solution_Tests : ContentUnitTest
var splitAmount = FixedPoint2.New(5);
var split = solutionOne.SplitSolution(splitAmount);
Assert.That(split.TotalVolume, Is.EqualTo(splitAmount));
Assert.That(split.Volume, Is.EqualTo(splitAmount));
}
[Test]
@@ -291,10 +293,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.TotalVolume.Int(), Is.EqualTo(0));
Assert.That(solution.Volume.Int(), Is.EqualTo(0));
Assert.That(splitSolution.GetReagentQuantity("water").Int(), Is.EqualTo(800));
Assert.That(splitSolution.TotalVolume.Int(), Is.EqualTo(800));
Assert.That(splitSolution.Volume.Int(), Is.EqualTo(800));
}
[Test]
@@ -305,10 +307,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.TotalVolume.Int(), Is.EqualTo(800));
Assert.That(solution.Volume.Int(), Is.EqualTo(800));
Assert.That(splitSolution.GetReagentQuantity("water").Int(), Is.EqualTo(0));
Assert.That(splitSolution.TotalVolume.Int(), Is.EqualTo(0));
Assert.That(splitSolution.Volume.Int(), Is.EqualTo(0));
}
[Test]
@@ -343,12 +345,12 @@ public sealed class Solution_Tests : ContentUnitTest
solutionTwo.AddReagent("water", FixedPoint2.New(500));
solutionTwo.AddReagent("earth", FixedPoint2.New(1000));
solutionOne.AddSolution(solutionTwo);
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.TotalVolume.Int(), Is.EqualTo(4500));
Assert.That(solutionOne.Volume.Int(), Is.EqualTo(4500));
}
// Tests concerning thermal energy and temperature.
@@ -359,24 +361,7 @@ public sealed class Solution_Tests : ContentUnitTest
public void EmptySolutionHasNoHeatCapacity()
{
var solution = new Solution();
Assert.That(solution.HeatCapacity, Is.EqualTo(0.0f));
}
[Test]
public void EmptySolutionHasNoThermalEnergy()
{
var solution = new Solution();
Assert.That(solution.ThermalEnergy, Is.EqualTo(0.0f));
}
[Test]
public void AddReagentToEmptySolutionSetsTemperature()
{
const float testTemp = 100.0f;
var solution = new Solution();
solution.AddReagent("water", FixedPoint2.New(100), testTemp);
Assert.That(solution.Temperature, Is.EqualTo(testTemp));
Assert.That(solution.GetHeatCapacity(null), Is.EqualTo(0.0f));
}
[Test]
@@ -384,8 +369,7 @@ public sealed class Solution_Tests : ContentUnitTest
{
const float initialTemp = 100.0f;
var solution = new Solution();
solution.AddReagent("water", FixedPoint2.New(100), initialTemp);
var solution = new Solution("water", FixedPoint2.New(100)) { Temperature = initialTemp };
solution.AddReagent("water", FixedPoint2.New(100));
Assert.That(solution.Temperature, Is.EqualTo(initialTemp));
@@ -408,7 +392,7 @@ public sealed class Solution_Tests : ContentUnitTest
solutionTwo.AddReagent("earth", FixedPoint2.New(100));
solutionTwo.Temperature = initialTemp;
solutionOne.AddSolution(solutionTwo);
solutionOne.AddSolution(solutionTwo, null);
Assert.That(solutionOne.Temperature, Is.EqualTo(initialTemp));
}
@@ -417,8 +401,7 @@ public sealed class Solution_Tests : ContentUnitTest
{
const float initialTemp = 100.0f;
var solution = new Solution();
solution.AddReagent("water", FixedPoint2.New(100), initialTemp);
var solution = new Solution("water", FixedPoint2.New(100)) { Temperature = initialTemp };
solution.RemoveReagent("water", FixedPoint2.New(50));
Assert.That(solution.Temperature, Is.EqualTo(initialTemp));
}
@@ -428,8 +411,7 @@ public sealed class Solution_Tests : ContentUnitTest
{
const float initialTemp = 100.0f;
var solution = new Solution();
solution.AddReagent("water", FixedPoint2.New(100), initialTemp);
var solution = new Solution("water", FixedPoint2.New(100)) { Temperature = initialTemp };
solution.RemoveSolution(FixedPoint2.New(50));
Assert.That(solution.Temperature, Is.EqualTo(initialTemp));
}
@@ -439,45 +421,10 @@ public sealed class Solution_Tests : ContentUnitTest
{
const float initialTemp = 100.0f;
var solution = new Solution();
solution.AddReagent("water", FixedPoint2.New(100), initialTemp);
var solution = new Solution("water", FixedPoint2.New(100)) { Temperature = initialTemp };
solution.SplitSolution(FixedPoint2.New(50));
Assert.That(solution.Temperature, Is.EqualTo(initialTemp));
}
[Test]
public void AddReagentWithSetTemperatureAdjustsTemperature()
{
const float temp = 100.0f;
var solution = new Solution();
solution.AddReagent("water", FixedPoint2.New(100), temp * 1);
Assert.That(solution.Temperature, Is.EqualTo(temp * 1));
solution.AddReagent("water", FixedPoint2.New(100), temp * 3);
Assert.That(solution.Temperature, Is.EqualTo(temp * 2));
solution.AddReagent("earth", FixedPoint2.New(100), temp * 5);
Assert.That(solution.Temperature, Is.EqualTo(temp * 3));
}
[Test]
public void AddSolutionCombinesThermalEnergy()
{
const float initialTemp = 100.0f;
var solutionOne = new Solution();
solutionOne.AddReagent("water", FixedPoint2.New(100), initialTemp);
var solutionTwo = new Solution();
solutionTwo.AddReagent("water", FixedPoint2.New(100), initialTemp);
solutionTwo.AddReagent("earth", FixedPoint2.New(100));
var thermalEnergyOne = solutionOne.ThermalEnergy;
var thermalEnergyTwo = solutionTwo.ThermalEnergy;
solutionOne.AddSolution(solutionTwo);
Assert.That(solutionOne.ThermalEnergy, Is.EqualTo(thermalEnergyOne + thermalEnergyTwo));
}
#endregion Thermal Energy and Temperature
}

View File

@@ -13,6 +13,7 @@
- type: SolutionContainerManager
solutions:
organ:
maxVol: 10
reagents:
- ReagentId: Nutriment
Quantity: 10
@@ -32,10 +33,12 @@
- type: SolutionContainerManager
solutions:
organ:
maxVol: 10
reagents:
- ReagentId: Nutriment
Quantity: 10
Lung:
maxVol: 100
canReact: False
- type: MovementSpeedModifier
baseWalkSpeed: 0
@@ -76,7 +79,7 @@
- type: SolutionContainerManager
solutions:
stomach:
maxVol: 250
maxVol: 50
- type: Stomach
- type: Metabolizer
maxReagents: 6

View File

@@ -141,7 +141,7 @@
- type: SolutionContainerManager
solutions:
stomach:
maxVol: 250
maxVol: 50
- type: Stomach
# The stomach metabolizes stuff like foods and drinks.
# TODO: Have it work off of the ent's solution container, and move this

View File

@@ -9,7 +9,7 @@
drawdepth: FloorObjects
- type: SolutionContainerManager
solutions:
puddle: {}
puddle: { maxVol: 1000 }
- type: Puddle
spillSound:
path: /Audio/Effects/Fluids/splat.ogg
@@ -53,6 +53,7 @@
- type: SolutionContainerManager
solutions:
puddle:
maxVol: 1000
reagents:
- ReagentId: Water
Quantity: 10
@@ -140,6 +141,7 @@
- type: SolutionContainerManager
solutions:
puddle:
maxVol: 1000
reagents:
- ReagentId: Nutriment
Quantity: 5
@@ -166,6 +168,7 @@
- type: SolutionContainerManager
solutions:
puddle:
maxVol: 1000
reagents:
- ReagentId: Toxin
Quantity: 5

View File

@@ -21,7 +21,7 @@
- ReagentId: Nutriment
Quantity: 11
- ReagentId: Vitamin
Quanity: 5
Quantity: 5
- type: SliceableFood
count: 4
- type: Tag

View File

@@ -81,6 +81,7 @@
- type: SolutionContainerManager
solutions:
puddle:
maxVol: 1000
reagents:
- ReagentId: Egg
Quantity: 2

View File

@@ -17,6 +17,7 @@
- type: SolutionContainerManager
solutions:
puddle:
maxVol: 1000
reagents:
- ReagentId: Flour
Quantity: 10

View File

@@ -410,6 +410,7 @@
- type: SolutionContainerManager
solutions:
puddle:
maxVol: 1000
reagents:
- ReagentId: JuiceTomato
Quantity: 10