Files
crystall-punk-14/Content.Shared/Chemistry/Components/Solution.cs

704 lines
23 KiB
C#
Raw Normal View History

2021-06-09 22:19:39 +02:00
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint;
2023-01-12 16:41:40 +13:00
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Robust.Shared.Utility;
2023-01-12 16:41:40 +13:00
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace Content.Shared.Chemistry.Components
{
/// <summary>
/// A solution of reagents.
/// </summary>
[Serializable, NetSerializable]
Serialization v3 content PR (#3491) * serv3 in shared pt 1 * beginning of deepclone api * progress in implementing ideepclone & serv3 in content * adds target * its cant hurt you it cant hurt you * more changes to content.server * adds dataclasses * almost there * renamed & edited entry * finishes refactoring content to use serv3 * gasmixture runtimes, next: reagentunit * fucin hell that was an annoying one * adds flags * fixes some yaml errors * removes comment * fixes generic components for now * removes todo actually clones values my god paul fixes bug involving resolving custom data classes from other proj renames dataclass fixes spritecomp adds WithFormat.Constants support * adds deepclone to ResistanceSet * adds a bunch of deepclone implementations adds a deepclone analyzer (TODO) adds a deep clone fallback for classes & structs * fixes a bunch of runtimes * adds deepclone to entityuid * adds generator to sln * gets rid of warnings * fixes * argh * componentdata refactors * more deepclone impl * heck me i reworked all of content deepclone * renames custom dataclasstarget * misc * reworks prototypes * deepclone nuke * renamed customdataclass attribute * fixes everything * misc fixed * the killcommit * getting there * changed yamlfieldattribute namespace * adds back iselfserialize * renames everything to data(field/definition) * ouch * Fix most errors on content * Fix more errors in content * Fix some components * work on tests * fixes some customdataclasses * fuggin shit * yes * yeas * Remove data classes * Data field naming fixes * arg * Git resetti RobustToolbox * Merge fixes * General fixes * Fix startup serialization errors * Fix DamageContainerPrototype when supported classes or types are null * Implement construction graph step type serializer * Fix up construction serialization * Fix up construction serialization part 2 * Fix null list in technology database component * Fix body serialization * Fix entity storage serialization * Fix actions serialization * Fix AI serialization * Fix reaction serialization * Fix body serialization * Fix grid atmosphere serialization * Rename IServ3Manager to ISerializationManager * Convert every non generic serializer to the new format, general fixes * Serialization and body system fix * pushinheritance fix * Update all prototypes to have a parent and have consistent id/parent properties * Merge fixes * smh my head * cuddling slaps * Content commit for engine PR * stuff * more fixes * argh * yes even you are fixed * changelog fixes * fixes seeds * argh * Test fixes * Add writing for alert order prototype * Fix alert order writing * FIX * its been alot ok * Fix the rest of the visualizers * Fix server alerts component tests * Fix alert prototype tests not using the read value * Fix alert prototype tests initializing serialization multiple times * THIS IS AN AMERICAN CODEBASE GOD BLESS THE USA * Add ImplicitDataDefinitionForInheritors to IMechanismBehavior Fixes the behaviors not being found * Fix NRE in strap component Good night to the 1 buckle optimization * Fix clothing component slot flags serialization tag * Fix body component in all components test * Merge fixes * ffs * Make construction graph prototype use serialization hooks * human yaml linted * a * Do the thing for construction * stuff * a * monke see yaml linter * LINT HARDER * Remove redundant todo * yes * Add skip hook argument to readers and copiers * we gamin * test/datafield fixes * adds more verbose validation * moves linter to action * Improve construction graph step type serializer error message * Fix ammo box component NRE * gamin * some updates to the linter * yes * removes that test * misc fixes * array fix priority fix misc fixes * adds proper info the validation * adds alwaysrelevant usa * Make yaml linter take half as long to run (~50% less) * Make yaml linter 5 times faster (~80% less execution time) * based vera being based * fixes mapsaving * warning cleanup & moves surpressor * removes old msbuild targets * Revert "Make yaml linter 5 times faster (~80% less execution time)" This reverts commit 3e6091359a26252c3e98828199553de668031c63. * Add -nowarn to yaml linter run configuration * Improve yaml linter message feedback * Make dependencies an argument instead of a property on the serialization manager * yamllinting slaps * Clean up type serializers * Move yaml linter code to its own method * Fix yaml errors * Change yaml linter action name and remove -nowarn * yaml linter please shut * Git resetti robust toolbox Co-authored-by: Paul <ritter.paul1+git@googlemail.com> Co-authored-by: DrSmugleaf <DrSmugleaf@users.noreply.github.com>
2021-03-05 01:08:38 +01:00
[DataDefinition]
public sealed partial class Solution : IEnumerable<Solution.ReagentQuantity>, ISerializationHooks
{
2023-01-12 16:41:40 +13:00
// 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,
Serialization v3 content PR (#3491) * serv3 in shared pt 1 * beginning of deepclone api * progress in implementing ideepclone & serv3 in content * adds target * its cant hurt you it cant hurt you * more changes to content.server * adds dataclasses * almost there * renamed & edited entry * finishes refactoring content to use serv3 * gasmixture runtimes, next: reagentunit * fucin hell that was an annoying one * adds flags * fixes some yaml errors * removes comment * fixes generic components for now * removes todo actually clones values my god paul fixes bug involving resolving custom data classes from other proj renames dataclass fixes spritecomp adds WithFormat.Constants support * adds deepclone to ResistanceSet * adds a bunch of deepclone implementations adds a deepclone analyzer (TODO) adds a deep clone fallback for classes & structs * fixes a bunch of runtimes * adds deepclone to entityuid * adds generator to sln * gets rid of warnings * fixes * argh * componentdata refactors * more deepclone impl * heck me i reworked all of content deepclone * renames custom dataclasstarget * misc * reworks prototypes * deepclone nuke * renamed customdataclass attribute * fixes everything * misc fixed * the killcommit * getting there * changed yamlfieldattribute namespace * adds back iselfserialize * renames everything to data(field/definition) * ouch * Fix most errors on content * Fix more errors in content * Fix some components * work on tests * fixes some customdataclasses * fuggin shit * yes * yeas * Remove data classes * Data field naming fixes * arg * Git resetti RobustToolbox * Merge fixes * General fixes * Fix startup serialization errors * Fix DamageContainerPrototype when supported classes or types are null * Implement construction graph step type serializer * Fix up construction serialization * Fix up construction serialization part 2 * Fix null list in technology database component * Fix body serialization * Fix entity storage serialization * Fix actions serialization * Fix AI serialization * Fix reaction serialization * Fix body serialization * Fix grid atmosphere serialization * Rename IServ3Manager to ISerializationManager * Convert every non generic serializer to the new format, general fixes * Serialization and body system fix * pushinheritance fix * Update all prototypes to have a parent and have consistent id/parent properties * Merge fixes * smh my head * cuddling slaps * Content commit for engine PR * stuff * more fixes * argh * yes even you are fixed * changelog fixes * fixes seeds * argh * Test fixes * Add writing for alert order prototype * Fix alert order writing * FIX * its been alot ok * Fix the rest of the visualizers * Fix server alerts component tests * Fix alert prototype tests not using the read value * Fix alert prototype tests initializing serialization multiple times * THIS IS AN AMERICAN CODEBASE GOD BLESS THE USA * Add ImplicitDataDefinitionForInheritors to IMechanismBehavior Fixes the behaviors not being found * Fix NRE in strap component Good night to the 1 buckle optimization * Fix clothing component slot flags serialization tag * Fix body component in all components test * Merge fixes * ffs * Make construction graph prototype use serialization hooks * human yaml linted * a * Do the thing for construction * stuff * a * monke see yaml linter * LINT HARDER * Remove redundant todo * yes * Add skip hook argument to readers and copiers * we gamin * test/datafield fixes * adds more verbose validation * moves linter to action * Improve construction graph step type serializer error message * Fix ammo box component NRE * gamin * some updates to the linter * yes * removes that test * misc fixes * array fix priority fix misc fixes * adds proper info the validation * adds alwaysrelevant usa * Make yaml linter take half as long to run (~50% less) * Make yaml linter 5 times faster (~80% less execution time) * based vera being based * fixes mapsaving * warning cleanup & moves surpressor * removes old msbuild targets * Revert "Make yaml linter 5 times faster (~80% less execution time)" This reverts commit 3e6091359a26252c3e98828199553de668031c63. * Add -nowarn to yaml linter run configuration * Improve yaml linter message feedback * Make dependencies an argument instead of a property on the serialization manager * yamllinting slaps * Clean up type serializers * Move yaml linter code to its own method * Fix yaml errors * Change yaml linter action name and remove -nowarn * yaml linter please shut * Git resetti robust toolbox Co-authored-by: Paul <ritter.paul1+git@googlemail.com> Co-authored-by: DrSmugleaf <DrSmugleaf@users.noreply.github.com>
2021-03-05 01:08:38 +01:00
[DataField("reagents")]
public List<ReagentQuantity> Contents = new(2);
/// <summary>
/// The calculated total volume of all reagents in the solution (ex. Total volume of liquid in beaker).
/// </summary>
[ViewVariables]
2023-01-12 16:41:40 +13:00
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;
2021-12-24 01:22:34 -08:00
/// <summary>
/// The temperature of the reagents in the solution.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("temperature")]
public float Temperature { get; set; } = 293.15f;
2022-04-05 04:02:33 +12:00
/// <summary>
/// The name of this solution, if it is contained in some <see cref="SolutionContainerManagerComponent"/>
/// </summary>
public string? Name;
2023-01-12 16:41:40 +13:00
/// <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() : this(2) // Most objects on the station hold only 1 or 2 reagents.
{
}
/// <summary>
/// Constructs an empty solution (ex. an empty beaker).
/// </summary>
2023-01-12 16:41:40 +13:00
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>
2023-01-12 16:41:40 +13:00
public Solution(string reagentId, FixedPoint2 quantity) : this()
{
AddReagent(reagentId, quantity);
}
2023-01-12 16:41:40 +13:00
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
}
Serialization v3 content PR (#3491) * serv3 in shared pt 1 * beginning of deepclone api * progress in implementing ideepclone & serv3 in content * adds target * its cant hurt you it cant hurt you * more changes to content.server * adds dataclasses * almost there * renamed & edited entry * finishes refactoring content to use serv3 * gasmixture runtimes, next: reagentunit * fucin hell that was an annoying one * adds flags * fixes some yaml errors * removes comment * fixes generic components for now * removes todo actually clones values my god paul fixes bug involving resolving custom data classes from other proj renames dataclass fixes spritecomp adds WithFormat.Constants support * adds deepclone to ResistanceSet * adds a bunch of deepclone implementations adds a deepclone analyzer (TODO) adds a deep clone fallback for classes & structs * fixes a bunch of runtimes * adds deepclone to entityuid * adds generator to sln * gets rid of warnings * fixes * argh * componentdata refactors * more deepclone impl * heck me i reworked all of content deepclone * renames custom dataclasstarget * misc * reworks prototypes * deepclone nuke * renamed customdataclass attribute * fixes everything * misc fixed * the killcommit * getting there * changed yamlfieldattribute namespace * adds back iselfserialize * renames everything to data(field/definition) * ouch * Fix most errors on content * Fix more errors in content * Fix some components * work on tests * fixes some customdataclasses * fuggin shit * yes * yeas * Remove data classes * Data field naming fixes * arg * Git resetti RobustToolbox * Merge fixes * General fixes * Fix startup serialization errors * Fix DamageContainerPrototype when supported classes or types are null * Implement construction graph step type serializer * Fix up construction serialization * Fix up construction serialization part 2 * Fix null list in technology database component * Fix body serialization * Fix entity storage serialization * Fix actions serialization * Fix AI serialization * Fix reaction serialization * Fix body serialization * Fix grid atmosphere serialization * Rename IServ3Manager to ISerializationManager * Convert every non generic serializer to the new format, general fixes * Serialization and body system fix * pushinheritance fix * Update all prototypes to have a parent and have consistent id/parent properties * Merge fixes * smh my head * cuddling slaps * Content commit for engine PR * stuff * more fixes * argh * yes even you are fixed * changelog fixes * fixes seeds * argh * Test fixes * Add writing for alert order prototype * Fix alert order writing * FIX * its been alot ok * Fix the rest of the visualizers * Fix server alerts component tests * Fix alert prototype tests not using the read value * Fix alert prototype tests initializing serialization multiple times * THIS IS AN AMERICAN CODEBASE GOD BLESS THE USA * Add ImplicitDataDefinitionForInheritors to IMechanismBehavior Fixes the behaviors not being found * Fix NRE in strap component Good night to the 1 buckle optimization * Fix clothing component slot flags serialization tag * Fix body component in all components test * Merge fixes * ffs * Make construction graph prototype use serialization hooks * human yaml linted * a * Do the thing for construction * stuff * a * monke see yaml linter * LINT HARDER * Remove redundant todo * yes * Add skip hook argument to readers and copiers * we gamin * test/datafield fixes * adds more verbose validation * moves linter to action * Improve construction graph step type serializer error message * Fix ammo box component NRE * gamin * some updates to the linter * yes * removes that test * misc fixes * array fix priority fix misc fixes * adds proper info the validation * adds alwaysrelevant usa * Make yaml linter take half as long to run (~50% less) * Make yaml linter 5 times faster (~80% less execution time) * based vera being based * fixes mapsaving * warning cleanup & moves surpressor * removes old msbuild targets * Revert "Make yaml linter 5 times faster (~80% less execution time)" This reverts commit 3e6091359a26252c3e98828199553de668031c63. * Add -nowarn to yaml linter run configuration * Improve yaml linter message feedback * Make dependencies an argument instead of a property on the serialization manager * yamllinting slaps * Clean up type serializers * Move yaml linter code to its own method * Fix yaml errors * Change yaml linter action name and remove -nowarn * yaml linter please shut * Git resetti robust toolbox Co-authored-by: Paul <ritter.paul1+git@googlemail.com> Co-authored-by: DrSmugleaf <DrSmugleaf@users.noreply.github.com>
2021-03-05 01:08:38 +01:00
void ISerializationHooks.AfterDeserialization()
{
2023-01-12 16:41:40 +13:00
Volume = FixedPoint2.Zero;
foreach (var reagent in Contents)
{
Volume += reagent.Quantity;
}
if (MaxVolume == FixedPoint2.Zero)
MaxVolume = Volume;
}
public bool ContainsReagent(string reagentId)
{
2023-01-12 16:41:40 +13:00
foreach (var reagent in Contents)
{
if (reagent.ReagentId == reagentId)
return true;
}
return false;
}
2023-01-12 16:41:40 +13:00
public bool TryGetReagent(string reagentId, out FixedPoint2 quantity)
{
foreach (var reagent in Contents)
{
if (reagent.ReagentId == reagentId)
{
quantity = reagent.Quantity;
return true;
}
}
quantity = FixedPoint2.New(0);
return false;
}
2023-01-12 16:41:40 +13:00
public string? GetPrimaryReagentId()
{
if (Contents.Count == 0)
2023-01-12 16:41:40 +13:00
return null;
ReagentQuantity max = default;
foreach (var reagent in Contents)
{
2023-01-12 16:41:40 +13:00
if (reagent.Quantity >= max.Quantity)
{
max = reagent;
}
}
2023-01-12 16:41:40 +13:00
return max.ReagentId!;
}
/// <summary>
2019-07-31 15:02:36 +02:00
/// Adds a given quantity of a reagent directly into the solution.
/// </summary>
/// <param name="reagentId">The prototype ID of the reagent to add.</param>
/// <param name="quantity">The quantity in milli-units.</param>
2023-01-12 16:41:40 +13:00
public void AddReagent(string reagentId, FixedPoint2 quantity, bool dirtyHeatCap = true)
{
if (quantity <= 0)
2023-01-12 16:41:40 +13:00
{
DebugTools.Assert(quantity == 0, "Attempted to add negative reagent quantity");
return;
2023-01-12 16:41:40 +13:00
}
2023-01-12 16:41:40 +13:00
Volume += quantity;
_heatCapacityDirty |= dirtyHeatCap;
for (var i = 0; i < Contents.Count; i++)
{
var reagent = Contents[i];
if (reagent.ReagentId != reagentId)
continue;
Contents[i] = new ReagentQuantity(reagentId, reagent.Quantity + quantity);
2023-01-12 16:41:40 +13:00
ValidateSolution();
return;
}
Contents.Add(new ReagentQuantity(reagentId, quantity));
2023-01-12 16:41:40 +13:00
ValidateSolution();
}
/// <summary>
/// Adds a given quantity of a reagent directly into the solution.
/// </summary>
/// <param name="proto">The prototype of the reagent to add.</param>
/// <param name="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);
2021-12-24 01:22:34 -08:00
2023-01-12 16:41:40 +13:00
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;
2023-01-22 03:35:12 +13:00
if (scale <= 0)
2023-01-12 16:41:40 +13:00
{
RemoveAllSolution();
return;
}
2023-01-12 16:41:40 +13:00
_heatCapacity *= scale;
Volume *= scale;
2023-01-22 03:35:12 +13:00
for (int i = 0; i < Contents.Count; i++)
2023-01-12 16:41:40 +13:00
{
var old = Contents[i];
Contents[i] = new ReagentQuantity(old.ReagentId, old.Quantity * scale);
}
ValidateSolution();
}
/// <summary>
/// Scales the amount of solution.
/// </summary>
/// <param name="scale">The scalar to modify the solution by.</param>
public void ScaleSolution(float scale)
{
2023-01-12 16:41:40 +13:00
if (scale == 1)
2022-10-04 14:24:19 +11:00
return;
2023-01-12 16:41:40 +13:00
if (scale == 0)
{
2023-01-12 16:41:40 +13:00
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
{
2023-01-12 16:41:40 +13:00
Contents[i] = new ReagentQuantity(old.ReagentId, newQuantity);
Volume += newQuantity;
}
}
2023-01-12 16:41:40 +13:00
_heatCapacityDirty = true;
ValidateSolution();
}
/// <summary>
/// Returns the amount of a single reagent inside the solution.
/// </summary>
/// <param name="reagentId">The prototype ID of the reagent to add.</param>
/// <returns>The quantity in milli-units.</returns>
public FixedPoint2 GetReagentQuantity(string reagentId)
{
for (var i = 0; i < Contents.Count; i++)
{
2023-01-12 16:41:40 +13:00
var reagent = Contents[i];
if (reagent.ReagentId == reagentId)
return reagent.Quantity;
}
2023-01-12 16:41:40 +13:00
return FixedPoint2.Zero;
}
/// <summary>
/// Attempts to remove an amount of reagent from the solution.
/// </summary>
/// <param name="reagentId">The reagent to be removed.</param>
/// <param name="quantity">The amount of reagent to remove.</param>
/// <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)
{
2023-01-12 16:41:40 +13:00
if (quantity <= FixedPoint2.Zero)
return FixedPoint2.Zero;
for (var i = 0; i < Contents.Count; i++)
{
var reagent = Contents[i];
if(reagent.ReagentId != reagentId)
continue;
var curQuantity = reagent.Quantity;
2020-04-05 11:36:12 +02:00
var newQuantity = curQuantity - quantity;
2023-01-12 16:41:40 +13:00
_heatCapacityDirty = true;
if (newQuantity <= 0)
{
Contents.RemoveSwap(i);
2023-01-12 16:41:40 +13:00
Volume -= curQuantity;
ValidateSolution();
return curQuantity;
}
Contents[i] = new ReagentQuantity(reagentId, newQuantity);
2023-01-12 16:41:40 +13:00
Volume -= quantity;
ValidateSolution();
return quantity;
}
// Reagent is not on the solution...
return FixedPoint2.Zero;
}
2023-01-12 16:41:40 +13:00
public void RemoveAllSolution()
{
2023-01-12 16:41:40 +13:00
Contents.Clear();
Volume = FixedPoint2.Zero;
_heatCapacityDirty = false;
_heatCapacity = 0;
}
public Solution SplitSolution(FixedPoint2 toTake)
{
if (toTake <= FixedPoint2.Zero)
return new Solution();
2023-01-12 16:41:40 +13:00
Solution newSolution;
2023-01-12 16:41:40 +13:00
if (toTake >= Volume)
{
2023-01-12 16:41:40 +13:00
newSolution = Clone();
RemoveAllSolution();
2023-01-12 16:41:40 +13:00
return newSolution;
}
2023-01-12 16:41:40 +13:00
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--) // iterate backwards because of remove swap.
{
var reagent = Contents[i];
2023-01-12 16:41:40 +13:00
// 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;
DebugTools.Assert(newQuantity >= 0);
if (newQuantity > FixedPoint2.Zero)
Contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity);
else
Contents.RemoveSwap(i);
2023-01-12 16:41:40 +13:00
newSolution.Contents.Add(new ReagentQuantity(reagent.ReagentId, splitQuantity));
Volume -= splitQuantity;
remaining -= split;
effVol -= reagent.Quantity.Value;
}
2023-01-12 16:41:40 +13:00
newSolution.Volume = origVol - Volume;
2023-01-12 16:41:40 +13:00
DebugTools.Assert(remaining >= 0);
DebugTools.Assert(remaining == 0 || Volume == FixedPoint2.Zero);
_heatCapacityDirty = true;
newSolution._heatCapacityDirty = true;
ValidateSolution();
newSolution.ValidateSolution();
return newSolution;
}
2023-01-12 16:41:40 +13:00
/// <summary>
/// Variant of <see cref="SplitSolution(FixedPoint2)"/> that doesn't return a new solution containing the removed reagents.
/// </summary>
/// <param name="toTake">The quantity of this solution to remove</param>
2023-01-12 16:41:40 +13:00
public void RemoveSolution(FixedPoint2 toTake)
{
2023-01-12 16:41:40 +13:00
if (toTake <= FixedPoint2.Zero)
return;
2023-01-12 16:41:40 +13:00
if (toTake >= Volume)
{
RemoveAllSolution();
2023-01-12 16:41:40 +13:00
return;
}
2023-01-12 16:41:40 +13:00
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];
2021-12-24 01:22:34 -08:00
2023-01-12 16:41:40 +13:00
// This is set up such that integer rounding will tend to take more reagents.
var split = remaining * reagent.Quantity.Value / effVol;
2023-01-12 16:41:40 +13:00
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;
2023-01-12 16:41:40 +13:00
if (newQuantity > FixedPoint2.Zero)
2021-11-25 18:16:06 +13:00
Contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity);
else
2023-01-12 16:41:40 +13:00
Contents.RemoveSwap(i);
2021-11-25 18:16:06 +13:00
2023-01-12 16:41:40 +13:00
remaining -= split;
effVol -= reagent.Quantity.Value;
}
2023-01-12 16:41:40 +13:00
DebugTools.Assert(remaining >= 0);
DebugTools.Assert(remaining == 0 || Volume == FixedPoint2.Zero);
2023-01-12 16:41:40 +13:00
_heatCapacityDirty = true;
ValidateSolution();
}
2019-07-31 15:02:36 +02:00
2023-01-12 16:41:40 +13:00
public void AddSolution(Solution otherSolution, IPrototypeManager? protoMan)
{
2023-01-12 16:41:40 +13:00
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];
var found = false;
for (var j = 0; j < Contents.Count; j++)
{
var reagent = Contents[j];
if (reagent.ReagentId == otherReagent.ReagentId)
{
found = true;
Contents[j] = new ReagentQuantity(reagent.ReagentId, reagent.Quantity + otherReagent.Quantity);
break;
}
}
if (!found)
{
Contents.Add(new ReagentQuantity(otherReagent.ReagentId, otherReagent.Quantity));
}
}
2023-01-12 16:41:40 +13:00
_heatCapacity += otherSolution._heatCapacity;
if (closeTemps)
_heatCapacityDirty |= otherSolution._heatCapacityDirty;
else
Temperature = _heatCapacity == 0 ? 0 : totalThermalEnergy / _heatCapacity;
ValidateSolution();
}
2023-01-12 16:41:40 +13:00
public Color GetColor(IPrototypeManager? protoMan)
{
2023-01-12 16:41:40 +13:00
if (Volume == FixedPoint2.Zero)
{
return Color.Transparent;
}
2023-01-12 16:41:40 +13:00
IoCManager.Resolve(ref protoMan);
Color mixColor = default;
var runningTotalQuantity = FixedPoint2.New(0);
2023-01-12 16:41:40 +13:00
bool first = true;
foreach (var reagent in Contents)
{
runningTotalQuantity += reagent.Quantity;
2023-01-12 16:41:40 +13:00
if (!protoMan.TryIndex(reagent.ReagentId, out ReagentPrototype? proto))
{
continue;
}
2023-01-12 16:41:40 +13:00
if (first)
{
2023-01-12 16:41:40 +13:00
first = false;
mixColor = proto.SubstanceColor;
continue;
}
2023-01-12 16:41:40 +13:00
var interpolateValue = reagent.Quantity.Float() / runningTotalQuantity.Float();
mixColor = Color.InterpolateBetween(mixColor, proto.SubstanceColor, interpolateValue);
}
return mixColor;
}
2022-10-04 14:24:19 +11:00
[Obsolete("Use ReactiveSystem.DoEntityReaction")]
public void DoEntityReaction(EntityUid uid, ReactionMethod method)
{
2022-10-04 14:24:19 +11:00
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<ReactiveSystem>().DoEntityReaction(uid, this, method);
}
[Serializable, NetSerializable]
Serialization v3 content PR (#3491) * serv3 in shared pt 1 * beginning of deepclone api * progress in implementing ideepclone & serv3 in content * adds target * its cant hurt you it cant hurt you * more changes to content.server * adds dataclasses * almost there * renamed & edited entry * finishes refactoring content to use serv3 * gasmixture runtimes, next: reagentunit * fucin hell that was an annoying one * adds flags * fixes some yaml errors * removes comment * fixes generic components for now * removes todo actually clones values my god paul fixes bug involving resolving custom data classes from other proj renames dataclass fixes spritecomp adds WithFormat.Constants support * adds deepclone to ResistanceSet * adds a bunch of deepclone implementations adds a deepclone analyzer (TODO) adds a deep clone fallback for classes & structs * fixes a bunch of runtimes * adds deepclone to entityuid * adds generator to sln * gets rid of warnings * fixes * argh * componentdata refactors * more deepclone impl * heck me i reworked all of content deepclone * renames custom dataclasstarget * misc * reworks prototypes * deepclone nuke * renamed customdataclass attribute * fixes everything * misc fixed * the killcommit * getting there * changed yamlfieldattribute namespace * adds back iselfserialize * renames everything to data(field/definition) * ouch * Fix most errors on content * Fix more errors in content * Fix some components * work on tests * fixes some customdataclasses * fuggin shit * yes * yeas * Remove data classes * Data field naming fixes * arg * Git resetti RobustToolbox * Merge fixes * General fixes * Fix startup serialization errors * Fix DamageContainerPrototype when supported classes or types are null * Implement construction graph step type serializer * Fix up construction serialization * Fix up construction serialization part 2 * Fix null list in technology database component * Fix body serialization * Fix entity storage serialization * Fix actions serialization * Fix AI serialization * Fix reaction serialization * Fix body serialization * Fix grid atmosphere serialization * Rename IServ3Manager to ISerializationManager * Convert every non generic serializer to the new format, general fixes * Serialization and body system fix * pushinheritance fix * Update all prototypes to have a parent and have consistent id/parent properties * Merge fixes * smh my head * cuddling slaps * Content commit for engine PR * stuff * more fixes * argh * yes even you are fixed * changelog fixes * fixes seeds * argh * Test fixes * Add writing for alert order prototype * Fix alert order writing * FIX * its been alot ok * Fix the rest of the visualizers * Fix server alerts component tests * Fix alert prototype tests not using the read value * Fix alert prototype tests initializing serialization multiple times * THIS IS AN AMERICAN CODEBASE GOD BLESS THE USA * Add ImplicitDataDefinitionForInheritors to IMechanismBehavior Fixes the behaviors not being found * Fix NRE in strap component Good night to the 1 buckle optimization * Fix clothing component slot flags serialization tag * Fix body component in all components test * Merge fixes * ffs * Make construction graph prototype use serialization hooks * human yaml linted * a * Do the thing for construction * stuff * a * monke see yaml linter * LINT HARDER * Remove redundant todo * yes * Add skip hook argument to readers and copiers * we gamin * test/datafield fixes * adds more verbose validation * moves linter to action * Improve construction graph step type serializer error message * Fix ammo box component NRE * gamin * some updates to the linter * yes * removes that test * misc fixes * array fix priority fix misc fixes * adds proper info the validation * adds alwaysrelevant usa * Make yaml linter take half as long to run (~50% less) * Make yaml linter 5 times faster (~80% less execution time) * based vera being based * fixes mapsaving * warning cleanup & moves surpressor * removes old msbuild targets * Revert "Make yaml linter 5 times faster (~80% less execution time)" This reverts commit 3e6091359a26252c3e98828199553de668031c63. * Add -nowarn to yaml linter run configuration * Improve yaml linter message feedback * Make dependencies an argument instead of a property on the serialization manager * yamllinting slaps * Clean up type serializers * Move yaml linter code to its own method * Fix yaml errors * Change yaml linter action name and remove -nowarn * yaml linter please shut * Git resetti robust toolbox Co-authored-by: Paul <ritter.paul1+git@googlemail.com> Co-authored-by: DrSmugleaf <DrSmugleaf@users.noreply.github.com>
2021-03-05 01:08:38 +01:00
[DataDefinition]
public readonly struct ReagentQuantity: IComparable<ReagentQuantity>
{
[DataField("ReagentId", customTypeSerializer:typeof(PrototypeIdSerializer<ReagentPrototype>))]
public readonly string ReagentId;
Serialization v3 content PR (#3491) * serv3 in shared pt 1 * beginning of deepclone api * progress in implementing ideepclone & serv3 in content * adds target * its cant hurt you it cant hurt you * more changes to content.server * adds dataclasses * almost there * renamed & edited entry * finishes refactoring content to use serv3 * gasmixture runtimes, next: reagentunit * fucin hell that was an annoying one * adds flags * fixes some yaml errors * removes comment * fixes generic components for now * removes todo actually clones values my god paul fixes bug involving resolving custom data classes from other proj renames dataclass fixes spritecomp adds WithFormat.Constants support * adds deepclone to ResistanceSet * adds a bunch of deepclone implementations adds a deepclone analyzer (TODO) adds a deep clone fallback for classes & structs * fixes a bunch of runtimes * adds deepclone to entityuid * adds generator to sln * gets rid of warnings * fixes * argh * componentdata refactors * more deepclone impl * heck me i reworked all of content deepclone * renames custom dataclasstarget * misc * reworks prototypes * deepclone nuke * renamed customdataclass attribute * fixes everything * misc fixed * the killcommit * getting there * changed yamlfieldattribute namespace * adds back iselfserialize * renames everything to data(field/definition) * ouch * Fix most errors on content * Fix more errors in content * Fix some components * work on tests * fixes some customdataclasses * fuggin shit * yes * yeas * Remove data classes * Data field naming fixes * arg * Git resetti RobustToolbox * Merge fixes * General fixes * Fix startup serialization errors * Fix DamageContainerPrototype when supported classes or types are null * Implement construction graph step type serializer * Fix up construction serialization * Fix up construction serialization part 2 * Fix null list in technology database component * Fix body serialization * Fix entity storage serialization * Fix actions serialization * Fix AI serialization * Fix reaction serialization * Fix body serialization * Fix grid atmosphere serialization * Rename IServ3Manager to ISerializationManager * Convert every non generic serializer to the new format, general fixes * Serialization and body system fix * pushinheritance fix * Update all prototypes to have a parent and have consistent id/parent properties * Merge fixes * smh my head * cuddling slaps * Content commit for engine PR * stuff * more fixes * argh * yes even you are fixed * changelog fixes * fixes seeds * argh * Test fixes * Add writing for alert order prototype * Fix alert order writing * FIX * its been alot ok * Fix the rest of the visualizers * Fix server alerts component tests * Fix alert prototype tests not using the read value * Fix alert prototype tests initializing serialization multiple times * THIS IS AN AMERICAN CODEBASE GOD BLESS THE USA * Add ImplicitDataDefinitionForInheritors to IMechanismBehavior Fixes the behaviors not being found * Fix NRE in strap component Good night to the 1 buckle optimization * Fix clothing component slot flags serialization tag * Fix body component in all components test * Merge fixes * ffs * Make construction graph prototype use serialization hooks * human yaml linted * a * Do the thing for construction * stuff * a * monke see yaml linter * LINT HARDER * Remove redundant todo * yes * Add skip hook argument to readers and copiers * we gamin * test/datafield fixes * adds more verbose validation * moves linter to action * Improve construction graph step type serializer error message * Fix ammo box component NRE * gamin * some updates to the linter * yes * removes that test * misc fixes * array fix priority fix misc fixes * adds proper info the validation * adds alwaysrelevant usa * Make yaml linter take half as long to run (~50% less) * Make yaml linter 5 times faster (~80% less execution time) * based vera being based * fixes mapsaving * warning cleanup & moves surpressor * removes old msbuild targets * Revert "Make yaml linter 5 times faster (~80% less execution time)" This reverts commit 3e6091359a26252c3e98828199553de668031c63. * Add -nowarn to yaml linter run configuration * Improve yaml linter message feedback * Make dependencies an argument instead of a property on the serialization manager * yamllinting slaps * Clean up type serializers * Move yaml linter code to its own method * Fix yaml errors * Change yaml linter action name and remove -nowarn * yaml linter please shut * Git resetti robust toolbox Co-authored-by: Paul <ritter.paul1+git@googlemail.com> Co-authored-by: DrSmugleaf <DrSmugleaf@users.noreply.github.com>
2021-03-05 01:08:38 +01:00
[DataField("Quantity")]
public readonly FixedPoint2 Quantity;
public ReagentQuantity(string reagentId, FixedPoint2 quantity)
{
ReagentId = reagentId;
Quantity = quantity;
}
[ExcludeFromCodeCoverage]
public override string ToString()
{
return $"{ReagentId}:{Quantity}";
}
public int CompareTo(ReagentQuantity other) { return Quantity.Float().CompareTo(other.Quantity.Float()); }
public void Deconstruct(out string reagentId, out FixedPoint2 quantity)
{
reagentId = ReagentId;
quantity = Quantity;
}
}
#region Enumeration
2019-07-31 15:02:36 +02:00
public IEnumerator<ReagentQuantity> GetEnumerator()
{
return Contents.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
2023-01-12 16:41:40 +13:00
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();
}
}
}