Solution rejig (#12428)
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
- ReagentId: Nutriment
|
||||
Quantity: 11
|
||||
- ReagentId: Vitamin
|
||||
Quanity: 5
|
||||
Quantity: 5
|
||||
- type: SliceableFood
|
||||
count: 4
|
||||
- type: Tag
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
- type: SolutionContainerManager
|
||||
solutions:
|
||||
puddle:
|
||||
maxVol: 1000
|
||||
reagents:
|
||||
- ReagentId: Egg
|
||||
Quantity: 2
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
- type: SolutionContainerManager
|
||||
solutions:
|
||||
puddle:
|
||||
maxVol: 1000
|
||||
reagents:
|
||||
- ReagentId: Flour
|
||||
Quantity: 10
|
||||
|
||||
@@ -410,6 +410,7 @@
|
||||
- type: SolutionContainerManager
|
||||
solutions:
|
||||
puddle:
|
||||
maxVol: 1000
|
||||
reagents:
|
||||
- ReagentId: JuiceTomato
|
||||
Quantity: 10
|
||||
|
||||
Reference in New Issue
Block a user