From bea34f7f347ebb8795986e4d3be2b4348cfb4c3d Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Sat, 7 Mar 2020 18:30:10 +0100 Subject: [PATCH 01/18] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5abfcb8979..0c20a5b39f 100644 --- a/.gitignore +++ b/.gitignore @@ -280,3 +280,4 @@ BuildFiles/Windows/Godot/* # Working on the tools scripts is getting annoying okay? .mypy_cache/ +RobustToolbox From b9f9eb6651ce8d4f44b4f989836e97917c56d341 Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Sat, 7 Mar 2020 18:33:03 +0100 Subject: [PATCH 02/18] Revert "Update .gitignore" This reverts commit bea34f7f347ebb8795986e4d3be2b4348cfb4c3d. --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 0c20a5b39f..5abfcb8979 100644 --- a/.gitignore +++ b/.gitignore @@ -280,4 +280,3 @@ BuildFiles/Windows/Godot/* # Working on the tools scripts is getting annoying okay? .mypy_cache/ -RobustToolbox From f05fdfb5fca78f5c5af4593cbfa139d9b75fd4ee Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Sat, 14 Mar 2020 12:55:07 +0100 Subject: [PATCH 03/18] Changed all int and some float things in Reagent code to Decimals --- .../Components/Chemistry/InjectorComponent.cs | 8 ++--- .../Chemistry/Metabolism/DefaultDrink.cs | 2 +- .../Chemistry/Metabolism/DefaultFood.cs | 19 ++++++----- .../ExplosionReactionEffect.cs | 4 +-- .../Components/Chemistry/InjectorComponent.cs | 8 ++--- .../Components/Chemistry/PourableComponent.cs | 2 +- .../Components/Chemistry/SolutionComponent.cs | 25 ++++++++------ .../Metabolism/BloodstreamComponent.cs | 4 +-- .../Components/Nutrition/DrinkComponent.cs | 9 ++--- .../Components/Nutrition/FoodComponent.cs | 2 +- .../Components/Nutrition/StomachComponent.cs | 6 ++-- .../Interfaces/Chemistry/IReactionEffect.cs | 2 +- .../Chemistry/DefaultMetabolizable.cs | 9 ++--- Content.Shared/Chemistry/Solution.cs | 34 ++++++++++--------- .../Chemistry/SharedInjectorComponent.cs | 6 ++-- .../SharedReagentDispenserComponent.cs | 10 +++--- .../Components/Chemistry/SolutionComponent.cs | 18 +++++----- .../Interfaces/Chemistry/IMetabolizable.cs | 2 +- Content.Shared/Maths/Rounders.cs | 12 +++++++ 19 files changed, 101 insertions(+), 81 deletions(-) create mode 100644 Content.Shared/Maths/Rounders.cs diff --git a/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs b/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs index ab1bb415d1..23e8c1a6ad 100644 --- a/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs +++ b/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs @@ -16,8 +16,8 @@ namespace Content.Client.GameObjects.Components.Chemistry [RegisterComponent] public class InjectorComponent : SharedInjectorComponent, IItemStatus { - [ViewVariables] private int CurrentVolume { get; set; } - [ViewVariables] private int TotalVolume { get; set; } + [ViewVariables] private decimal CurrentVolume { get; set; } + [ViewVariables] private decimal TotalVolume { get; set; } [ViewVariables] private InjectorToggleMode CurrentMode { get; set; } [ViewVariables(VVAccess.ReadWrite)] private bool _uiUpdateNeeded; @@ -28,9 +28,9 @@ namespace Content.Client.GameObjects.Components.Chemistry //Handle net updates public override void HandleComponentState(ComponentState curState, ComponentState nextState) { - var cast = (InjectorComponentState)curState; - if (cast != null) + if(curState.GetType() == typeof(InjectorComponentState)) { + var cast = (InjectorComponentState) curState; CurrentVolume = cast.CurrentVolume; TotalVolume = cast.TotalVolume; CurrentMode = cast.CurrentMode; diff --git a/Content.Server/Chemistry/Metabolism/DefaultDrink.cs b/Content.Server/Chemistry/Metabolism/DefaultDrink.cs index e43a1d7dd3..0839d6f164 100644 --- a/Content.Server/Chemistry/Metabolism/DefaultDrink.cs +++ b/Content.Server/Chemistry/Metabolism/DefaultDrink.cs @@ -28,7 +28,7 @@ namespace Content.Server.Chemistry.Metabolism } //Remove reagent at set rate, satiate thirst if a ThirstComponent can be found - int IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime) + decimal IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime) { int metabolismAmount = (int)Math.Round(MetabolismRate * tickTime); if (solutionEntity.TryGetComponent(out ThirstComponent thirst)) diff --git a/Content.Server/Chemistry/Metabolism/DefaultFood.cs b/Content.Server/Chemistry/Metabolism/DefaultFood.cs index 824721acfd..4de983702a 100644 --- a/Content.Server/Chemistry/Metabolism/DefaultFood.cs +++ b/Content.Server/Chemistry/Metabolism/DefaultFood.cs @@ -1,6 +1,7 @@ using System; using Content.Server.GameObjects.Components.Nutrition; using Content.Shared.Interfaces.Chemistry; +using Content.Shared.Maths; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Serialization; using Robust.Shared.Serialization; @@ -14,25 +15,25 @@ namespace Content.Server.Chemistry.Metabolism class DefaultFood : IMetabolizable { //Rate of metabolism in units / second - private int _metabolismRate; - public int MetabolismRate => _metabolismRate; + private decimal _metabolismRate; + public decimal MetabolismRate => _metabolismRate; //How much hunger is satiated when 1u of the reagent is metabolized - private float _nutritionFactor; - public float NutritionFactor => _nutritionFactor; + private decimal _nutritionFactor; + public decimal NutritionFactor => _nutritionFactor; void IExposeData.ExposeData(ObjectSerializer serializer) { - serializer.DataField(ref _metabolismRate, "rate", 1); - serializer.DataField(ref _nutritionFactor, "nutrimentFactor", 30.0f); + serializer.DataField(ref _metabolismRate, "rate", 1M); + serializer.DataField(ref _nutritionFactor, "nutrimentFactor", 30.0M); } //Remove reagent at set rate, satiate hunger if a HungerComponent can be found - int IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime) + decimal IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime) { - int metabolismAmount = (int)Math.Round(MetabolismRate * tickTime); + var metabolismAmount = (MetabolismRate * (decimal) tickTime).RoundForReagents(); if (solutionEntity.TryGetComponent(out HungerComponent hunger)) - hunger.UpdateFood(metabolismAmount * NutritionFactor); + hunger.UpdateFood((float)(metabolismAmount * NutritionFactor)); //Return amount of reagent to be removed, remove reagent regardless of HungerComponent presence return metabolismAmount; diff --git a/Content.Server/Chemistry/ReactionEffects/ExplosionReactionEffect.cs b/Content.Server/Chemistry/ReactionEffects/ExplosionReactionEffect.cs index 7382a7bdc2..2bdff9b1c2 100644 --- a/Content.Server/Chemistry/ReactionEffects/ExplosionReactionEffect.cs +++ b/Content.Server/Chemistry/ReactionEffects/ExplosionReactionEffect.cs @@ -35,9 +35,9 @@ namespace Content.Server.Chemistry.ReactionEffects serializer.DataField(ref _maxScale, "maxScale", 1); } - public void React(IEntity solutionEntity, int intensity) + public void React(IEntity solutionEntity, decimal intensity) { - float floatIntensity = intensity; //Use float to avoid truncation in scaling + float floatIntensity = (float)intensity; if (solutionEntity == null) return; if(!solutionEntity.TryGetComponent(out SolutionComponent solution)) diff --git a/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs b/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs index d4140d31f4..b9f0527793 100644 --- a/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs @@ -37,7 +37,7 @@ namespace Content.Server.GameObjects.Components.Chemistry /// attempt to inject it's entire contents upon use. /// [ViewVariables] - private int _transferAmount; + private decimal _transferAmount; /// /// Initial storage volume of the injector @@ -165,7 +165,7 @@ namespace Content.Server.GameObjects.Components.Chemistry } //Get transfer amount. May be smaller than _transferAmount if not enough room - int realTransferAmount = Math.Min(_transferAmount, targetBloodstream.EmptyVolume); + decimal realTransferAmount = Math.Min(_transferAmount, targetBloodstream.EmptyVolume); if (realTransferAmount <= 0) { _notifyManager.PopupMessage(Owner.Transform.GridPosition, user, @@ -193,7 +193,7 @@ namespace Content.Server.GameObjects.Components.Chemistry } //Get transfer amount. May be smaller than _transferAmount if not enough room - int realTransferAmount = Math.Min(_transferAmount, targetSolution.EmptyVolume); + decimal realTransferAmount = Math.Min(_transferAmount, targetSolution.EmptyVolume); if (realTransferAmount <= 0) { _notifyManager.PopupMessage(Owner.Transform.GridPosition, user, @@ -221,7 +221,7 @@ namespace Content.Server.GameObjects.Components.Chemistry } //Get transfer amount. May be smaller than _transferAmount if not enough room - int realTransferAmount = Math.Min(_transferAmount, targetSolution.CurrentVolume); + decimal realTransferAmount = Math.Min(_transferAmount, targetSolution.CurrentVolume); if (realTransferAmount <= 0) { _notifyManager.PopupMessage(Owner.Transform.GridPosition, user, diff --git a/Content.Server/GameObjects/Components/Chemistry/PourableComponent.cs b/Content.Server/GameObjects/Components/Chemistry/PourableComponent.cs index b0f47233b0..1ad4386634 100644 --- a/Content.Server/GameObjects/Components/Chemistry/PourableComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/PourableComponent.cs @@ -69,7 +69,7 @@ namespace Content.Server.GameObjects.Components.Chemistry return false; //Get transfer amount. May be smaller than _transferAmount if not enough room - int realTransferAmount = Math.Min(attackPourable.TransferAmount, targetSolution.EmptyVolume); + decimal realTransferAmount = Math.Min(attackPourable.TransferAmount, targetSolution.EmptyVolume); if (realTransferAmount <= 0) //Special message if container is full { _notifyManager.PopupMessage(Owner.Transform.GridPosition, eventArgs.User, diff --git a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs index 98b064bfc2..b612c4052d 100644 --- a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -7,6 +7,7 @@ using Content.Server.GameObjects.EntitySystems; using Content.Server.Interfaces; using Content.Shared.Chemistry; using Content.Shared.GameObjects; +using Content.Shared.Maths; using Robust.Server.GameObjects.EntitySystems; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; @@ -205,7 +206,7 @@ namespace Content.Server.GameObjects.Components.Chemistry //Check the solution for every reaction foreach (var reaction in _reactions) { - if (SolutionValidReaction(reaction, out int unitReactions)) + if (SolutionValidReaction(reaction, out var unitReactions)) { PerformReaction(reaction, unitReactions); checkForNewReaction = true; @@ -223,11 +224,13 @@ namespace Content.Server.GameObjects.Components.Chemistry } } - public bool TryAddReagent(string reagentId, int quantity, out int acceptedQuantity, bool skipReactionCheck = false, bool skipColor = false) + public bool TryAddReagent(string reagentId, decimal quantity, out decimal acceptedQuantity, bool skipReactionCheck = false, bool skipColor = false) { - if (quantity > _maxVolume - _containedSolution.TotalVolume) + quantity = quantity.RoundForReagents(); + var toAcceptQuantity = (_maxVolume - _containedSolution.TotalVolume).RoundForReagents(); + if (quantity > toAcceptQuantity) { - acceptedQuantity = _maxVolume - _containedSolution.TotalVolume; + acceptedQuantity = toAcceptQuantity; if (acceptedQuantity == 0) return false; } else @@ -267,16 +270,16 @@ namespace Content.Server.GameObjects.Components.Chemistry /// The reaction whose reactants will be checked for in the solution. /// The number of times the reaction can occur with the given solution. /// - private bool SolutionValidReaction(ReactionPrototype reaction, out int unitReactions) + private bool SolutionValidReaction(ReactionPrototype reaction, out decimal unitReactions) { - unitReactions = int.MaxValue; //Set to some impossibly large number initially + unitReactions = decimal.MaxValue; //Set to some impossibly large number initially foreach (var reactant in reaction.Reactants) { - if (!ContainsReagent(reactant.Key, out int reagentQuantity)) + if (!ContainsReagent(reactant.Key, out decimal reagentQuantity)) { return false; } - int currentUnitReactions = reagentQuantity / reactant.Value.Amount; + var currentUnitReactions = (reagentQuantity / reactant.Value.Amount).RoundForReagents(); if (currentUnitReactions < unitReactions) { unitReactions = currentUnitReactions; @@ -299,21 +302,21 @@ namespace Content.Server.GameObjects.Components.Chemistry /// Solution to be reacted. /// Reaction to occur. /// The number of times to cause this reaction. - private void PerformReaction(ReactionPrototype reaction, int unitReactions) + private void PerformReaction(ReactionPrototype reaction, decimal unitReactions) { //Remove non-catalysts foreach (var reactant in reaction.Reactants) { if (!reactant.Value.Catalyst) { - int amountToRemove = unitReactions * reactant.Value.Amount; + var amountToRemove = (unitReactions * reactant.Value.Amount).RoundForReagents(); TryRemoveReagent(reactant.Key, amountToRemove); } } //Add products foreach (var product in reaction.Products) { - TryAddReagent(product.Key, (int)(unitReactions * product.Value), out int acceptedQuantity, true); + TryAddReagent(product.Key, (int)(unitReactions * product.Value), out var acceptedQuantity, true); } //Trigger reaction effects foreach (var effect in reaction.Effects) diff --git a/Content.Server/GameObjects/Components/Metabolism/BloodstreamComponent.cs b/Content.Server/GameObjects/Components/Metabolism/BloodstreamComponent.cs index 0fa862ce82..ef4579331d 100644 --- a/Content.Server/GameObjects/Components/Metabolism/BloodstreamComponent.cs +++ b/Content.Server/GameObjects/Components/Metabolism/BloodstreamComponent.cs @@ -39,7 +39,7 @@ namespace Content.Server.GameObjects.Components.Metabolism /// /// Empty volume of internal solution /// - public int EmptyVolume => _internalSolution.EmptyVolume; + public decimal EmptyVolume => _internalSolution.EmptyVolume; public override void ExposeData(ObjectSerializer serializer) { @@ -98,7 +98,7 @@ namespace Content.Server.GameObjects.Components.Metabolism //Run metabolism code for each reagent foreach (var metabolizable in proto.Metabolism) { - int reagentDelta = metabolizable.Metabolize(Owner, reagent.ReagentId, tickTime); + var reagentDelta = metabolizable.Metabolize(Owner, reagent.ReagentId, tickTime); _internalSolution.TryRemoveReagent(reagent.ReagentId, reagentDelta); } } diff --git a/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs b/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs index ab6f259b24..26d552f71f 100644 --- a/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs +++ b/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs @@ -5,6 +5,7 @@ using Content.Server.GameObjects.EntitySystems; using Content.Shared.Chemistry; using Content.Shared.GameObjects.Components.Nutrition; using Content.Shared.Interfaces; +using Content.Shared.Maths; using Robust.Server.GameObjects; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; @@ -32,11 +33,11 @@ namespace Content.Server.GameObjects.Components.Nutrition [ViewVariables] private string _finishPrototype; - public int TransferAmount => _transferAmount; + public decimal TransferAmount => _transferAmount; [ViewVariables] - private int _transferAmount = 2; + private decimal _transferAmount = 2; - public int MaxVolume + public decimal MaxVolume { get => _contents.MaxVolume; set => _contents.MaxVolume = value; @@ -56,7 +57,7 @@ namespace Content.Server.GameObjects.Components.Nutrition { return 0; } - return Math.Max(1, _contents.CurrentVolume / _transferAmount); + return Math.Max(1, (int)Math.Ceiling(_contents.CurrentVolume / _transferAmount)); } diff --git a/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs b/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs index df990b392c..a528c223b6 100644 --- a/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs +++ b/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs @@ -99,7 +99,7 @@ namespace Content.Server.GameObjects.Components.Nutrition { return 0; } - return Math.Max(1, _contents.CurrentVolume / _transferAmount); + return Math.Max(1, (int)Math.Ceiling(_contents.CurrentVolume / _transferAmount)); } bool IUse.UseEntity(UseEntityEventArgs eventArgs) diff --git a/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs b/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs index 95353351f9..294fc20fcb 100644 --- a/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs +++ b/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs @@ -27,7 +27,7 @@ namespace Content.Server.GameObjects.Components.Nutrition /// /// Max volume of internal solution storage /// - public int MaxVolume + public decimal MaxVolume { get => _stomachContents.MaxVolume; set => _stomachContents.MaxVolume = value; @@ -141,10 +141,10 @@ namespace Content.Server.GameObjects.Components.Nutrition private class ReagentDelta { public readonly string ReagentId; - public readonly int Quantity; + public readonly decimal Quantity; public float Lifetime { get; private set; } - public ReagentDelta(string reagentId, int quantity) + public ReagentDelta(string reagentId, decimal quantity) { ReagentId = reagentId; Quantity = quantity; diff --git a/Content.Server/Interfaces/Chemistry/IReactionEffect.cs b/Content.Server/Interfaces/Chemistry/IReactionEffect.cs index bd01799557..9e5e28071b 100644 --- a/Content.Server/Interfaces/Chemistry/IReactionEffect.cs +++ b/Content.Server/Interfaces/Chemistry/IReactionEffect.cs @@ -8,6 +8,6 @@ namespace Content.Shared.Interfaces /// public interface IReactionEffect : IExposeData { - void React(IEntity solutionEntity, int intensity); + void React(IEntity solutionEntity, decimal intensity); } } diff --git a/Content.Shared/Chemistry/DefaultMetabolizable.cs b/Content.Shared/Chemistry/DefaultMetabolizable.cs index ab5816d9f0..7ceb3372c4 100644 --- a/Content.Shared/Chemistry/DefaultMetabolizable.cs +++ b/Content.Shared/Chemistry/DefaultMetabolizable.cs @@ -1,5 +1,6 @@ using System; using Content.Shared.Interfaces.Chemistry; +using Content.Shared.Maths; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Serialization; using Robust.Shared.Serialization; @@ -10,17 +11,17 @@ namespace Content.Shared.Chemistry class DefaultMetabolizable : IMetabolizable { //Rate of metabolism in units / second - private int _metabolismRate = 1; - public int MetabolismRate => _metabolismRate; + private decimal _metabolismRate = 1; + public decimal MetabolismRate => _metabolismRate; void IExposeData.ExposeData(ObjectSerializer serializer) { serializer.DataField(ref _metabolismRate, "rate", 1); } - int IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime) + decimal IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime) { - int metabolismAmount = (int)Math.Round(MetabolismRate * tickTime); + var metabolismAmount = (MetabolismRate * (decimal)tickTime).RoundForReagents(); return metabolismAmount; } } diff --git a/Content.Shared/Chemistry/Solution.cs b/Content.Shared/Chemistry/Solution.cs index bbb9937249..e10d99f389 100644 --- a/Content.Shared/Chemistry/Solution.cs +++ b/Content.Shared/Chemistry/Solution.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using Content.Shared.Maths; using Robust.Shared.Interfaces.Serialization; using Robust.Shared.Serialization; using Robust.Shared.Utility; @@ -23,7 +24,7 @@ namespace Content.Shared.Chemistry /// The calculated total volume of all reagents in the solution (ex. Total volume of liquid in beaker). /// [ViewVariables] - public int TotalVolume { get; private set; } + public decimal TotalVolume { get; private set; } /// /// Constructs an empty solution (ex. an empty beaker). @@ -60,9 +61,10 @@ namespace Content.Shared.Chemistry /// /// The prototype ID of the reagent to add. /// The quantity in milli-units. - public void AddReagent(string reagentId, int quantity) + public void AddReagent(string reagentId, decimal quantity) { - if(quantity <= 0) + quantity = quantity.RoundForReagents(); + if (quantity <= 0) return; for (var i = 0; i < _contents.Count; i++) @@ -85,7 +87,7 @@ namespace Content.Shared.Chemistry /// /// The prototype ID of the reagent to add. /// The quantity in milli-units. - public int GetReagentQuantity(string reagentId) + public decimal GetReagentQuantity(string reagentId) { for (var i = 0; i < _contents.Count; i++) { @@ -96,7 +98,7 @@ namespace Content.Shared.Chemistry return 0; } - public void RemoveReagent(string reagentId, int quantity) + public void RemoveReagent(string reagentId, decimal quantity) { if(quantity <= 0) return; @@ -109,7 +111,7 @@ namespace Content.Shared.Chemistry var curQuantity = reagent.Quantity; - var newQuantity = curQuantity - quantity; + var newQuantity = (curQuantity - quantity).RoundForReagents(); if (newQuantity <= 0) { _contents.RemoveSwap(i); @@ -129,12 +131,12 @@ namespace Content.Shared.Chemistry /// Remove the specified quantity from this solution. /// /// The quantity of this solution to remove - public void RemoveSolution(int quantity) + public void RemoveSolution(decimal quantity) { if(quantity <= 0) return; - var ratio = (float)(TotalVolume - quantity) / TotalVolume; + var ratio = (TotalVolume - quantity).RoundForReagents() / TotalVolume; if (ratio <= 0) { @@ -149,12 +151,12 @@ namespace Content.Shared.Chemistry // 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 = (int)Math.Floor(oldQuantity * ratio); + var newQuantity = (oldQuantity * ratio).RoundForReagents(); _contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity); } - TotalVolume = (int)Math.Floor(TotalVolume * ratio); + TotalVolume = (TotalVolume * ratio).RoundForReagents(); } public void RemoveAllSolution() @@ -163,7 +165,7 @@ namespace Content.Shared.Chemistry TotalVolume = 0; } - public Solution SplitSolution(int quantity) + public Solution SplitSolution(decimal quantity) { if (quantity <= 0) return new Solution(); @@ -178,8 +180,8 @@ namespace Content.Shared.Chemistry } newSolution = new Solution(); - var newTotalVolume = 0; - var ratio = (float)(TotalVolume - quantity) / TotalVolume; + var newTotalVolume = 0M; + var ratio = (TotalVolume - quantity) / TotalVolume; for (var i = 0; i < _contents.Count; i++) { @@ -228,7 +230,7 @@ namespace Content.Shared.Chemistry public Solution Clone() { - var volume = 0; + var volume = 0M; var newSolution = new Solution(); for (var i = 0; i < _contents.Count; i++) @@ -246,9 +248,9 @@ namespace Content.Shared.Chemistry public readonly struct ReagentQuantity { public readonly string ReagentId; - public readonly int Quantity; + public readonly decimal Quantity; - public ReagentQuantity(string reagentId, int quantity) + public ReagentQuantity(string reagentId, decimal quantity) { ReagentId = reagentId; Quantity = quantity; diff --git a/Content.Shared/GameObjects/Components/Chemistry/SharedInjectorComponent.cs b/Content.Shared/GameObjects/Components/Chemistry/SharedInjectorComponent.cs index ebbb4b11e4..8bf232b96f 100644 --- a/Content.Shared/GameObjects/Components/Chemistry/SharedInjectorComponent.cs +++ b/Content.Shared/GameObjects/Components/Chemistry/SharedInjectorComponent.cs @@ -18,11 +18,11 @@ namespace Content.Shared.GameObjects.Components.Chemistry [Serializable, NetSerializable] protected sealed class InjectorComponentState : ComponentState { - public int CurrentVolume { get; } - public int TotalVolume { get; } + public decimal CurrentVolume { get; } + public decimal TotalVolume { get; } public InjectorToggleMode CurrentMode { get; } - public InjectorComponentState(int currentVolume, int totalVolume, InjectorToggleMode currentMode) : base(ContentNetIDs.REAGENT_INJECTOR) + public InjectorComponentState(decimal currentVolume, decimal totalVolume, InjectorToggleMode currentMode) : base(ContentNetIDs.REAGENT_INJECTOR) { CurrentVolume = currentVolume; TotalVolume = totalVolume; diff --git a/Content.Shared/GameObjects/Components/Chemistry/SharedReagentDispenserComponent.cs b/Content.Shared/GameObjects/Components/Chemistry/SharedReagentDispenserComponent.cs index 0066dc3d50..9907fb3550 100644 --- a/Content.Shared/GameObjects/Components/Chemistry/SharedReagentDispenserComponent.cs +++ b/Content.Shared/GameObjects/Components/Chemistry/SharedReagentDispenserComponent.cs @@ -26,8 +26,8 @@ namespace Content.Shared.GameObjects.Components.Chemistry public class ReagentDispenserBoundUserInterfaceState : BoundUserInterfaceState { public readonly bool HasBeaker; - public readonly int BeakerCurrentVolume; - public readonly int BeakerMaxVolume; + public readonly decimal BeakerCurrentVolume; + public readonly decimal BeakerMaxVolume; public readonly string ContainerName; /// /// A list of the reagents which this dispenser can dispense. @@ -38,10 +38,10 @@ namespace Content.Shared.GameObjects.Components.Chemistry /// public readonly List ContainerReagents; public readonly string DispenserName; - public readonly int SelectedDispenseAmount; + public readonly decimal SelectedDispenseAmount; - public ReagentDispenserBoundUserInterfaceState(bool hasBeaker, int beakerCurrentVolume, int beakerMaxVolume, string containerName, - List inventory, string dispenserName, List containerReagents, int selectedDispenseAmount) + public ReagentDispenserBoundUserInterfaceState(bool hasBeaker, decimal beakerCurrentVolume, decimal beakerMaxVolume, string containerName, + List inventory, string dispenserName, List containerReagents, decimal selectedDispenseAmount) { HasBeaker = hasBeaker; BeakerCurrentVolume = beakerCurrentVolume; diff --git a/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs index 426daa0639..71ba3c6c91 100644 --- a/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -18,7 +18,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry [ViewVariables] protected Solution _containedSolution = new Solution(); - protected int _maxVolume; + protected decimal _maxVolume; private SolutionCaps _capabilities; /// @@ -30,7 +30,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry /// The maximum volume of the container. /// [ViewVariables(VVAccess.ReadWrite)] - public int MaxVolume + public decimal MaxVolume { get => _maxVolume; set => _maxVolume = value; // Note that the contents won't spill out if the capacity is reduced. @@ -40,13 +40,13 @@ namespace Content.Shared.GameObjects.Components.Chemistry /// The total volume of all the of the reagents in the container. /// [ViewVariables] - public int CurrentVolume => _containedSolution.TotalVolume; + public decimal CurrentVolume => _containedSolution.TotalVolume; /// /// The volume without reagents remaining in the container. /// [ViewVariables] - public int EmptyVolume => MaxVolume - CurrentVolume; + public decimal EmptyVolume => MaxVolume - CurrentVolume; /// /// The current blended color of all the reagents in the container. @@ -122,7 +122,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry OnSolutionChanged(); } - public bool TryRemoveReagent(string reagentId, int quantity) + public bool TryRemoveReagent(string reagentId, decimal quantity) { if (!ContainsReagent(reagentId, out var currentQuantity)) return false; @@ -146,7 +146,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry return true; } - public Solution SplitSolution(int quantity) + public Solution SplitSolution(decimal quantity) { var solutionSplit = _containedSolution.SplitSolution(quantity); OnSolutionChanged(); @@ -159,7 +159,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry SubstanceColor = Color.White; Color mixColor = default; - float runningTotalQuantity = 0; + var runningTotalQuantity = 0M; foreach (var reagent in _containedSolution) { @@ -171,7 +171,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry if (mixColor == default) mixColor = proto.SubstanceColor; - mixColor = BlendRGB(mixColor, proto.SubstanceColor, reagent.Quantity / runningTotalQuantity); + mixColor = BlendRGB(mixColor, proto.SubstanceColor, (float) (reagent.Quantity / runningTotalQuantity)); } } @@ -216,7 +216,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry /// The reagent to check for. /// Output the quantity of the reagent if it is contained, 0 if it isn't. /// Return true if the solution contains the reagent. - public bool ContainsReagent(string reagentId, out int quantity) + public bool ContainsReagent(string reagentId, out decimal quantity) { foreach (var reagent in _containedSolution.Contents) { diff --git a/Content.Shared/Interfaces/Chemistry/IMetabolizable.cs b/Content.Shared/Interfaces/Chemistry/IMetabolizable.cs index 4b03ef51e2..3a55de645f 100644 --- a/Content.Shared/Interfaces/Chemistry/IMetabolizable.cs +++ b/Content.Shared/Interfaces/Chemistry/IMetabolizable.cs @@ -19,6 +19,6 @@ namespace Content.Shared.Interfaces.Chemistry /// The reagent id /// The time since the last metabolism tick in seconds. /// The amount of reagent to be removed. The metabolizing organ should handle removing the reagent. - int Metabolize(IEntity solutionEntity, string reagentId, float tickTime); + decimal Metabolize(IEntity solutionEntity, string reagentId, float tickTime); } } diff --git a/Content.Shared/Maths/Rounders.cs b/Content.Shared/Maths/Rounders.cs new file mode 100644 index 0000000000..4b07d84026 --- /dev/null +++ b/Content.Shared/Maths/Rounders.cs @@ -0,0 +1,12 @@ +using System; + +namespace Content.Shared.Maths +{ + public static class Rounders + { + public static decimal RoundForReagents(this decimal me) + { + return Math.Round(me, 2); + } + } +} From dc666218042ca474012c15bd987fc43b09cd7ee6 Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Sat, 14 Mar 2020 14:04:08 +0100 Subject: [PATCH 04/18] Replaced static Rounders with an impleneted interface --- Content.Client/ClientContentIoC.cs | 3 ++ .../Chemistry/Metabolism/DefaultFood.cs | 10 +++--- .../Components/Chemistry/SolutionComponent.cs | 26 +++++++------- Content.Server/ServerContentIoC.cs | 5 ++- .../Chemistry/DefaultMetabolizable.cs | 10 +++--- .../Chemistry/RounderForReagents.cs | 14 ++++++++ Content.Shared/Chemistry/Solution.cs | 24 +++++++------ .../Components/Chemistry/SolutionComponent.cs | 36 +++++++++---------- .../Chemistry/IRounderForReagents.cs | 7 ++++ Content.Shared/Maths/Rounders.cs | 12 ------- .../Prototypes/Entities/items/chemistry.yml | 14 ++++---- 11 files changed, 91 insertions(+), 70 deletions(-) create mode 100644 Content.Shared/Chemistry/RounderForReagents.cs create mode 100644 Content.Shared/Interfaces/Chemistry/IRounderForReagents.cs delete mode 100644 Content.Shared/Maths/Rounders.cs diff --git a/Content.Client/ClientContentIoC.cs b/Content.Client/ClientContentIoC.cs index 6a8e9ea10a..d26587104c 100644 --- a/Content.Client/ClientContentIoC.cs +++ b/Content.Client/ClientContentIoC.cs @@ -7,7 +7,9 @@ using Content.Client.Parallax; using Content.Client.Sandbox; using Content.Client.UserInterface; using Content.Client.Utility; +using Content.Shared.Chemistry; using Content.Shared.Interfaces; +using Content.Shared.Interfaces.Chemistry; using Robust.Shared.IoC; namespace Content.Client @@ -27,6 +29,7 @@ namespace Content.Client IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); + IoCManager.Register(); } } } diff --git a/Content.Server/Chemistry/Metabolism/DefaultFood.cs b/Content.Server/Chemistry/Metabolism/DefaultFood.cs index 4de983702a..aca26e2628 100644 --- a/Content.Server/Chemistry/Metabolism/DefaultFood.cs +++ b/Content.Server/Chemistry/Metabolism/DefaultFood.cs @@ -1,9 +1,8 @@ -using System; -using Content.Server.GameObjects.Components.Nutrition; +using Content.Server.GameObjects.Components.Nutrition; using Content.Shared.Interfaces.Chemistry; -using Content.Shared.Maths; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Serialization; +using Robust.Shared.IoC; using Robust.Shared.Serialization; namespace Content.Server.Chemistry.Metabolism @@ -14,6 +13,9 @@ namespace Content.Server.Chemistry.Metabolism /// class DefaultFood : IMetabolizable { +#pragma warning disable 649 + [Dependency] private readonly IRounderForReagents _rounder; +#pragma warning restore 649 //Rate of metabolism in units / second private decimal _metabolismRate; public decimal MetabolismRate => _metabolismRate; @@ -31,7 +33,7 @@ namespace Content.Server.Chemistry.Metabolism //Remove reagent at set rate, satiate hunger if a HungerComponent can be found decimal IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime) { - var metabolismAmount = (MetabolismRate * (decimal) tickTime).RoundForReagents(); + var metabolismAmount = _rounder.Round(MetabolismRate * (decimal) tickTime); if (solutionEntity.TryGetComponent(out HungerComponent hunger)) hunger.UpdateFood((float)(metabolismAmount * NutritionFactor)); diff --git a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs index b612c4052d..c5a815752a 100644 --- a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -1,13 +1,8 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.Design; -using Content.Server.Chemistry; -using Content.Server.GameObjects.Components.Nutrition; +using Content.Server.Chemistry; using Content.Server.GameObjects.EntitySystems; -using Content.Server.Interfaces; using Content.Shared.Chemistry; using Content.Shared.GameObjects; -using Content.Shared.Maths; +using Content.Shared.Interfaces.Chemistry; using Robust.Server.GameObjects.EntitySystems; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; @@ -15,6 +10,8 @@ using Robust.Shared.IoC; using Robust.Shared.Localization; using Robust.Shared.Prototypes; using Robust.Shared.Utility; +using System; +using System.Collections.Generic; namespace Content.Server.GameObjects.Components.Chemistry { @@ -25,6 +22,7 @@ namespace Content.Server.GameObjects.Components.Chemistry internal class SolutionComponent : Shared.GameObjects.Components.Chemistry.SolutionComponent, IExamine { #pragma warning disable 649 + [Dependency] private readonly IRounderForReagents _rounder; [Dependency] private readonly IPrototypeManager _prototypeManager; [Dependency] private readonly ILocalizationManager _loc; [Dependency] private readonly IEntitySystemManager _entitySystemManager; @@ -226,8 +224,8 @@ namespace Content.Server.GameObjects.Components.Chemistry public bool TryAddReagent(string reagentId, decimal quantity, out decimal acceptedQuantity, bool skipReactionCheck = false, bool skipColor = false) { - quantity = quantity.RoundForReagents(); - var toAcceptQuantity = (_maxVolume - _containedSolution.TotalVolume).RoundForReagents(); + quantity = _rounder.Round(quantity); + var toAcceptQuantity = _rounder.Round(MaxVolume - ContainedSolution.TotalVolume); if (quantity > toAcceptQuantity) { acceptedQuantity = toAcceptQuantity; @@ -238,7 +236,7 @@ namespace Content.Server.GameObjects.Components.Chemistry acceptedQuantity = quantity; } - _containedSolution.AddReagent(reagentId, acceptedQuantity); + ContainedSolution.AddReagent(reagentId, acceptedQuantity); if (!skipColor) { RecalculateColor(); } @@ -250,10 +248,10 @@ namespace Content.Server.GameObjects.Components.Chemistry public bool TryAddSolution(Solution solution, bool skipReactionCheck = false, bool skipColor = false) { - if (solution.TotalVolume > (_maxVolume - _containedSolution.TotalVolume)) + if (solution.TotalVolume > (MaxVolume - ContainedSolution.TotalVolume)) return false; - _containedSolution.AddSolution(solution); + ContainedSolution.AddSolution(solution); if (!skipColor) { RecalculateColor(); } @@ -279,7 +277,7 @@ namespace Content.Server.GameObjects.Components.Chemistry { return false; } - var currentUnitReactions = (reagentQuantity / reactant.Value.Amount).RoundForReagents(); + var currentUnitReactions = _rounder.Round(reagentQuantity / reactant.Value.Amount); if (currentUnitReactions < unitReactions) { unitReactions = currentUnitReactions; @@ -309,7 +307,7 @@ namespace Content.Server.GameObjects.Components.Chemistry { if (!reactant.Value.Catalyst) { - var amountToRemove = (unitReactions * reactant.Value.Amount).RoundForReagents(); + var amountToRemove = _rounder.Round(unitReactions * reactant.Value.Amount); TryRemoveReagent(reactant.Key, amountToRemove); } } diff --git a/Content.Server/ServerContentIoC.cs b/Content.Server/ServerContentIoC.cs index ec9ff2a876..1c5937c0fe 100644 --- a/Content.Server/ServerContentIoC.cs +++ b/Content.Server/ServerContentIoC.cs @@ -1,4 +1,4 @@ -using Content.Server.Cargo; +using Content.Server.Cargo; using Content.Server.Chat; using Content.Server.GameTicking; using Content.Server.Interfaces; @@ -7,7 +7,9 @@ using Content.Server.Interfaces.GameTicking; using Content.Server.Preferences; using Content.Server.Sandbox; using Content.Server.Utility; +using Content.Shared.Chemistry; using Content.Shared.Interfaces; +using Content.Shared.Interfaces.Chemistry; using Robust.Shared.IoC; namespace Content.Server @@ -26,6 +28,7 @@ namespace Content.Server IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); + IoCManager.Register(); } } } diff --git a/Content.Shared/Chemistry/DefaultMetabolizable.cs b/Content.Shared/Chemistry/DefaultMetabolizable.cs index 7ceb3372c4..31d91d351b 100644 --- a/Content.Shared/Chemistry/DefaultMetabolizable.cs +++ b/Content.Shared/Chemistry/DefaultMetabolizable.cs @@ -1,8 +1,7 @@ -using System; -using Content.Shared.Interfaces.Chemistry; -using Content.Shared.Maths; +using Content.Shared.Interfaces.Chemistry; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Serialization; +using Robust.Shared.IoC; using Robust.Shared.Serialization; namespace Content.Shared.Chemistry @@ -10,6 +9,9 @@ namespace Content.Shared.Chemistry //Default metabolism for reagents. Metabolizes the reagent with no effects class DefaultMetabolizable : IMetabolizable { +#pragma warning disable 649 + [Dependency] private readonly IRounderForReagents _rounder; +#pragma warning restore 649 //Rate of metabolism in units / second private decimal _metabolismRate = 1; public decimal MetabolismRate => _metabolismRate; @@ -21,7 +23,7 @@ namespace Content.Shared.Chemistry decimal IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime) { - var metabolismAmount = (MetabolismRate * (decimal)tickTime).RoundForReagents(); + var metabolismAmount = _rounder.Round(MetabolismRate * (decimal)tickTime); return metabolismAmount; } } diff --git a/Content.Shared/Chemistry/RounderForReagents.cs b/Content.Shared/Chemistry/RounderForReagents.cs new file mode 100644 index 0000000000..d7c6ccce11 --- /dev/null +++ b/Content.Shared/Chemistry/RounderForReagents.cs @@ -0,0 +1,14 @@ +using Content.Shared.Interfaces.Chemistry; +using System; + +namespace Content.Shared.Chemistry +{ + + public class RounderForReagents : IRounderForReagents + { + public decimal Round(decimal value) + { + return Math.Round(value, 2); + } + } +} diff --git a/Content.Shared/Chemistry/Solution.cs b/Content.Shared/Chemistry/Solution.cs index e10d99f389..109b780108 100644 --- a/Content.Shared/Chemistry/Solution.cs +++ b/Content.Shared/Chemistry/Solution.cs @@ -1,12 +1,13 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using Content.Shared.Maths; +using Content.Shared.Interfaces.Chemistry; using Robust.Shared.Interfaces.Serialization; +using Robust.Shared.IoC; using Robust.Shared.Serialization; using Robust.Shared.Utility; using Robust.Shared.ViewVariables; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace Content.Shared.Chemistry { @@ -15,6 +16,9 @@ namespace Content.Shared.Chemistry /// public class Solution : IExposeData, IEnumerable { +#pragma warning disable 649 + [Dependency] private readonly IRounderForReagents _rounder; +#pragma warning restore 649 // Most objects on the station hold only 1 or 2 reagents [ViewVariables] private List _contents = new List(2); @@ -63,7 +67,7 @@ namespace Content.Shared.Chemistry /// The quantity in milli-units. public void AddReagent(string reagentId, decimal quantity) { - quantity = quantity.RoundForReagents(); + quantity = _rounder.Round(quantity); if (quantity <= 0) return; @@ -111,7 +115,7 @@ namespace Content.Shared.Chemistry var curQuantity = reagent.Quantity; - var newQuantity = (curQuantity - quantity).RoundForReagents(); + var newQuantity = _rounder.Round(curQuantity - quantity); if (newQuantity <= 0) { _contents.RemoveSwap(i); @@ -136,7 +140,7 @@ namespace Content.Shared.Chemistry if(quantity <= 0) return; - var ratio = (TotalVolume - quantity).RoundForReagents() / TotalVolume; + var ratio = _rounder.Round(TotalVolume - quantity) / TotalVolume; if (ratio <= 0) { @@ -151,12 +155,12 @@ namespace Content.Shared.Chemistry // 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).RoundForReagents(); + var newQuantity = _rounder.Round(oldQuantity * ratio); _contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity); } - TotalVolume = (TotalVolume * ratio).RoundForReagents(); + TotalVolume = _rounder.Round(TotalVolume * ratio); } public void RemoveAllSolution() diff --git a/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs index 71ba3c6c91..8a1f2b8a58 100644 --- a/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -1,12 +1,12 @@ -using System; -using System.Collections.Generic; -using Content.Shared.Chemistry; +using Content.Shared.Chemistry; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Maths; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.ViewVariables; +using System; +using System.Collections.Generic; namespace Content.Shared.GameObjects.Components.Chemistry { @@ -17,8 +17,8 @@ namespace Content.Shared.GameObjects.Components.Chemistry #pragma warning restore 649 [ViewVariables] - protected Solution _containedSolution = new Solution(); - protected decimal _maxVolume; + protected Solution ContainedSolution = new Solution(); + private decimal _maxVolume; private SolutionCaps _capabilities; /// @@ -40,7 +40,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry /// The total volume of all the of the reagents in the container. /// [ViewVariables] - public decimal CurrentVolume => _containedSolution.TotalVolume; + public decimal CurrentVolume => ContainedSolution.TotalVolume; /// /// The volume without reagents remaining in the container. @@ -64,7 +64,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry set => _capabilities = value; } - public IReadOnlyList ReagentList => _containedSolution.Contents; + public IReadOnlyList ReagentList => ContainedSolution.Contents; /// /// Shortcut for Capabilities PourIn flag to avoid binary operators. @@ -94,8 +94,8 @@ namespace Content.Shared.GameObjects.Components.Chemistry { base.ExposeData(serializer); - serializer.DataField(ref _maxVolume, "maxVol", 0); - serializer.DataField(ref _containedSolution, "contents", _containedSolution); + serializer.DataField(ref _maxVolume, "maxVol", 0M); + serializer.DataField(ref ContainedSolution, "contents", ContainedSolution); serializer.DataField(ref _capabilities, "caps", SolutionCaps.None); } @@ -112,13 +112,13 @@ namespace Content.Shared.GameObjects.Components.Chemistry { base.Shutdown(); - _containedSolution.RemoveAllSolution(); - _containedSolution = new Solution(); + ContainedSolution.RemoveAllSolution(); + ContainedSolution = new Solution(); } public void RemoveAllSolution() { - _containedSolution.RemoveAllSolution(); + ContainedSolution.RemoveAllSolution(); OnSolutionChanged(); } @@ -126,7 +126,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry { if (!ContainsReagent(reagentId, out var currentQuantity)) return false; - _containedSolution.RemoveReagent(reagentId, quantity); + ContainedSolution.RemoveReagent(reagentId, quantity); OnSolutionChanged(); return true; } @@ -141,27 +141,27 @@ namespace Content.Shared.GameObjects.Components.Chemistry if (CurrentVolume == 0) return false; - _containedSolution.RemoveSolution(quantity); + ContainedSolution.RemoveSolution(quantity); OnSolutionChanged(); return true; } public Solution SplitSolution(decimal quantity) { - var solutionSplit = _containedSolution.SplitSolution(quantity); + var solutionSplit = ContainedSolution.SplitSolution(quantity); OnSolutionChanged(); return solutionSplit; } protected void RecalculateColor() { - if(_containedSolution.TotalVolume == 0) + if(ContainedSolution.TotalVolume == 0) SubstanceColor = Color.White; Color mixColor = default; var runningTotalQuantity = 0M; - foreach (var reagent in _containedSolution) + foreach (var reagent in ContainedSolution) { runningTotalQuantity += reagent.Quantity; @@ -218,7 +218,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry /// Return true if the solution contains the reagent. public bool ContainsReagent(string reagentId, out decimal quantity) { - foreach (var reagent in _containedSolution.Contents) + foreach (var reagent in ContainedSolution.Contents) { if (reagent.ReagentId == reagentId) { diff --git a/Content.Shared/Interfaces/Chemistry/IRounderForReagents.cs b/Content.Shared/Interfaces/Chemistry/IRounderForReagents.cs new file mode 100644 index 0000000000..da8a1871c7 --- /dev/null +++ b/Content.Shared/Interfaces/Chemistry/IRounderForReagents.cs @@ -0,0 +1,7 @@ +namespace Content.Shared.Interfaces.Chemistry +{ + public interface IRounderForReagents + { + decimal Round(decimal value); + } +} diff --git a/Content.Shared/Maths/Rounders.cs b/Content.Shared/Maths/Rounders.cs deleted file mode 100644 index 4b07d84026..0000000000 --- a/Content.Shared/Maths/Rounders.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace Content.Shared.Maths -{ - public static class Rounders - { - public static decimal RoundForReagents(this decimal me) - { - return Math.Round(me, 2); - } - } -} diff --git a/Resources/Prototypes/Entities/items/chemistry.yml b/Resources/Prototypes/Entities/items/chemistry.yml index cca0065844..cf5e63916f 100644 --- a/Resources/Prototypes/Entities/items/chemistry.yml +++ b/Resources/Prototypes/Entities/items/chemistry.yml @@ -9,10 +9,10 @@ - type: Icon texture: Objects/Chemistry/chemicals.rsi/beaker.png - type: Solution - maxVol: 50 + maxVol: 50.0 caps: 27 - type: Pourable - transferAmount: 5 + transferAmount: 5.0 - type: entity name: Large Beaker @@ -25,10 +25,10 @@ - type: Icon texture: Objects/Chemistry/chemicals.rsi/beakerlarge.png - type: Solution - maxVol: 100 + maxVol: 100.0 caps: 27 - type: Pourable - transferAmount: 5 + transferAmount: 5.0 - type: entity name: Dropper @@ -41,10 +41,10 @@ - type: Icon texture: Objects/Chemistry/chemicals.rsi/dropper.png - type: Solution - maxVol: 5 + maxVol: 5.0 caps: 19 - type: Pourable - transferAmount: 5 + transferAmount: 5.0 - type: entity name: Syringe @@ -57,7 +57,7 @@ - type: Icon texture: Objects/Chemistry/chemicals.rsi/syringeproj.png - type: Solution - maxVol: 15 + maxVol: 15.0 caps: 19 - type: Injector injectOnly: false From 539214b1ad33a41e6e735cd8ec65eeef02c79359 Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Sat, 21 Mar 2020 16:22:35 +0100 Subject: [PATCH 05/18] Some more int -> decimal conversions. Changed the use of the Solution constructor. --- .../Components/Chemistry/PourableComponent.cs | 6 +++--- .../Chemistry/ReagentDispenserComponent.cs | 20 +++++++++---------- .../Components/Chemistry/SolutionComponent.cs | 3 ++- .../Components/Nutrition/StomachComponent.cs | 2 +- Content.Shared/Chemistry/Solution.cs | 6 +++--- .../Components/Chemistry/SolutionComponent.cs | 13 ++++++++---- 6 files changed, 28 insertions(+), 22 deletions(-) diff --git a/Content.Server/GameObjects/Components/Chemistry/PourableComponent.cs b/Content.Server/GameObjects/Components/Chemistry/PourableComponent.cs index 1ad4386634..439fc69ac9 100644 --- a/Content.Server/GameObjects/Components/Chemistry/PourableComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/PourableComponent.cs @@ -28,13 +28,13 @@ namespace Content.Server.GameObjects.Components.Chemistry public override string Name => "Pourable"; - private int _transferAmount; + private decimal _transferAmount; /// /// The amount of solution to be transferred from this solution when clicking on other solutions with it. /// [ViewVariables] - public int TransferAmount + public decimal TransferAmount { get => _transferAmount; set => _transferAmount = value; @@ -43,7 +43,7 @@ namespace Content.Server.GameObjects.Components.Chemistry public override void ExposeData(ObjectSerializer serializer) { base.ExposeData(serializer); - serializer.DataField(ref _transferAmount, "transferAmount", 5); + serializer.DataField(ref _transferAmount, "transferAmount", 5.0M); } /// diff --git a/Content.Server/GameObjects/Components/Chemistry/ReagentDispenserComponent.cs b/Content.Server/GameObjects/Components/Chemistry/ReagentDispenserComponent.cs index fa3a65e6c5..1f8f13625e 100644 --- a/Content.Server/GameObjects/Components/Chemistry/ReagentDispenserComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/ReagentDispenserComponent.cs @@ -41,7 +41,7 @@ namespace Content.Server.GameObjects.Components.Chemistry [ViewVariables] private string _packPrototypeId; [ViewVariables] private bool HasBeaker => _beakerContainer.ContainedEntity != null; - [ViewVariables] private int DispenseAmount = 10; + [ViewVariables] private decimal _dispenseAmount = 10; [ViewVariables] private SolutionComponent Solution => _beakerContainer.ContainedEntity.GetComponent(); @@ -115,22 +115,22 @@ namespace Content.Server.GameObjects.Components.Chemistry TryClear(); break; case UiButton.SetDispenseAmount1: - DispenseAmount = 1; + _dispenseAmount = 1; break; case UiButton.SetDispenseAmount5: - DispenseAmount = 5; + _dispenseAmount = 5; break; case UiButton.SetDispenseAmount10: - DispenseAmount = 10; + _dispenseAmount = 10; break; case UiButton.SetDispenseAmount25: - DispenseAmount = 25; + _dispenseAmount = 25; break; case UiButton.SetDispenseAmount50: - DispenseAmount = 50; + _dispenseAmount = 50; break; case UiButton.SetDispenseAmount100: - DispenseAmount = 100; + _dispenseAmount = 100; break; case UiButton.Dispense: if (HasBeaker) @@ -173,12 +173,12 @@ namespace Content.Server.GameObjects.Components.Chemistry if (beaker == null) { return new ReagentDispenserBoundUserInterfaceState(false, 0, 0, - "", Inventory, Owner.Name, null, DispenseAmount); + "", Inventory, Owner.Name, null, _dispenseAmount); } var solution = beaker.GetComponent(); return new ReagentDispenserBoundUserInterfaceState(true, solution.CurrentVolume, solution.MaxVolume, - beaker.Name, Inventory, Owner.Name, solution.ReagentList.ToList(), DispenseAmount); + beaker.Name, Inventory, Owner.Name, solution.ReagentList.ToList(), _dispenseAmount); } private void UpdateUserInterface() @@ -228,7 +228,7 @@ namespace Content.Server.GameObjects.Components.Chemistry if (!HasBeaker) return; var solution = _beakerContainer.ContainedEntity.GetComponent(); - solution.TryAddReagent(Inventory[dispenseIndex].ID, DispenseAmount, out _); + solution.TryAddReagent(Inventory[dispenseIndex].ID, _dispenseAmount, out _); UpdateUserInterface(); } diff --git a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs index c5a815752a..9bb574a002 100644 --- a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -37,8 +37,9 @@ namespace Content.Server.GameObjects.Components.Chemistry Init(); } - public void Init() + public override void Init() { + base.Init(); _reactions = _prototypeManager.EnumeratePrototypes(); _audioSystem = _entitySystemManager.GetEntitySystem(); } diff --git a/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs b/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs index 294fc20fcb..9923767503 100644 --- a/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs +++ b/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs @@ -119,7 +119,7 @@ namespace Content.Server.GameObjects.Components.Nutrition } //Add reagents ready for transfer to bloodstream to transferSolution - var transferSolution = new Solution(); + var transferSolution = IoCManager.InjectDependencies(new Solution()); foreach (var delta in _reagentDeltas.ToList()) //Use ToList here to remove entries while iterating { //Increment lifetime of reagents diff --git a/Content.Shared/Chemistry/Solution.cs b/Content.Shared/Chemistry/Solution.cs index 109b780108..365372f91f 100644 --- a/Content.Shared/Chemistry/Solution.cs +++ b/Content.Shared/Chemistry/Solution.cs @@ -172,7 +172,7 @@ namespace Content.Shared.Chemistry public Solution SplitSolution(decimal quantity) { if (quantity <= 0) - return new Solution(); + return IoCManager.InjectDependencies(new Solution()); Solution newSolution; @@ -183,7 +183,7 @@ namespace Content.Shared.Chemistry return newSolution; } - newSolution = new Solution(); + newSolution = IoCManager.InjectDependencies(new Solution()); var newTotalVolume = 0M; var ratio = (TotalVolume - quantity) / TotalVolume; @@ -235,7 +235,7 @@ namespace Content.Shared.Chemistry public Solution Clone() { var volume = 0M; - var newSolution = new Solution(); + var newSolution = IoCManager.InjectDependencies(new Solution()); for (var i = 0; i < _contents.Count; i++) { diff --git a/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs index 8a1f2b8a58..81a91b3d38 100644 --- a/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -17,7 +17,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry #pragma warning restore 649 [ViewVariables] - protected Solution ContainedSolution = new Solution(); + protected Solution ContainedSolution; private decimal _maxVolume; private SolutionCaps _capabilities; @@ -95,15 +95,20 @@ namespace Content.Shared.GameObjects.Components.Chemistry base.ExposeData(serializer); serializer.DataField(ref _maxVolume, "maxVol", 0M); - serializer.DataField(ref ContainedSolution, "contents", ContainedSolution); + serializer.DataField(ref ContainedSolution, "contents", IoCManager.InjectDependencies(new Solution())); serializer.DataField(ref _capabilities, "caps", SolutionCaps.None); } + public virtual void Init() + { + ContainedSolution = IoCManager.InjectDependencies(new Solution()); + } + /// protected override void Startup() { base.Startup(); - + RecalculateColor(); } @@ -113,7 +118,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry base.Shutdown(); ContainedSolution.RemoveAllSolution(); - ContainedSolution = new Solution(); + ContainedSolution = IoCManager.InjectDependencies(new Solution()); } public void RemoveAllSolution() From 4e0242d47c44bbe2e254f21cce18988a8257c939 Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Sun, 5 Apr 2020 11:36:12 +0200 Subject: [PATCH 06/18] Replace decimal with ReagentUnit --- Content.Client/ClientContentIoC.cs | 1 - .../Components/Chemistry/InjectorComponent.cs | 5 +- .../Chemistry/ReagentDispenserWindow.cs | 2 +- .../Chemistry/Metabolism/DefaultDrink.cs | 13 +- .../Chemistry/Metabolism/DefaultFood.cs | 22 +-- Content.Server/Chemistry/ReactionPrototype.cs | 13 +- .../Components/Chemistry/InjectorComponent.cs | 14 +- .../Components/Chemistry/PourableComponent.cs | 9 +- .../Chemistry/ReagentDispenserComponent.cs | 16 +- .../Components/Chemistry/SolutionComponent.cs | 28 ++- .../Metabolism/BloodstreamComponent.cs | 6 +- .../Components/Nutrition/DrinkComponent.cs | 16 +- .../Components/Nutrition/FoodComponent.cs | 10 +- .../Components/Nutrition/StomachComponent.cs | 12 +- Content.Server/ServerContentIoC.cs | 1 - .../Chemistry/DefaultMetabolizable.cs | 8 +- Content.Shared/Chemistry/ReagentUnit.cs | 162 ++++++++++++++++ .../Chemistry/RounderForReagents.cs | 14 -- Content.Shared/Chemistry/Solution.cs | 52 +++--- .../Chemistry/SharedInjectorComponent.cs | 7 +- .../SharedReagentDispenserComponent.cs | 10 +- .../Components/Chemistry/SolutionComponent.cs | 30 +-- .../Interfaces/Chemistry/IMetabolizable.cs | 2 +- .../Chemistry/IRounderForReagents.cs | 7 - .../Shared/Chemistry/ReagentUnit_Tests.cs | 113 ++++++++++++ .../Shared/Chemistry/Solution_Tests.cs | 174 +++++++++--------- RobustToolbox | 2 +- 27 files changed, 496 insertions(+), 253 deletions(-) create mode 100644 Content.Shared/Chemistry/ReagentUnit.cs delete mode 100644 Content.Shared/Chemistry/RounderForReagents.cs delete mode 100644 Content.Shared/Interfaces/Chemistry/IRounderForReagents.cs create mode 100644 Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs diff --git a/Content.Client/ClientContentIoC.cs b/Content.Client/ClientContentIoC.cs index d26587104c..24f1738d00 100644 --- a/Content.Client/ClientContentIoC.cs +++ b/Content.Client/ClientContentIoC.cs @@ -29,7 +29,6 @@ namespace Content.Client IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); - IoCManager.Register(); } } } diff --git a/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs b/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs index 23e8c1a6ad..1d1a7f5dcb 100644 --- a/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs +++ b/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs @@ -7,6 +7,7 @@ using Robust.Client.UserInterface.Controls; using Robust.Shared.GameObjects; using Robust.Shared.Localization; using Robust.Shared.ViewVariables; +using Content.Shared.Chemistry; namespace Content.Client.GameObjects.Components.Chemistry { @@ -16,8 +17,8 @@ namespace Content.Client.GameObjects.Components.Chemistry [RegisterComponent] public class InjectorComponent : SharedInjectorComponent, IItemStatus { - [ViewVariables] private decimal CurrentVolume { get; set; } - [ViewVariables] private decimal TotalVolume { get; set; } + [ViewVariables] private ReagentUnit CurrentVolume { get; set; } + [ViewVariables] private ReagentUnit TotalVolume { get; set; } [ViewVariables] private InjectorToggleMode CurrentMode { get; set; } [ViewVariables(VVAccess.ReadWrite)] private bool _uiUpdateNeeded; diff --git a/Content.Client/GameObjects/Components/Chemistry/ReagentDispenserWindow.cs b/Content.Client/GameObjects/Components/Chemistry/ReagentDispenserWindow.cs index d35b18346c..e9c8903de3 100644 --- a/Content.Client/GameObjects/Components/Chemistry/ReagentDispenserWindow.cs +++ b/Content.Client/GameObjects/Components/Chemistry/ReagentDispenserWindow.cs @@ -170,7 +170,7 @@ namespace Content.Client.GameObjects.Components.Chemistry Title = castState.DispenserName; UpdateContainerInfo(castState); - switch (castState.SelectedDispenseAmount) + switch (castState.SelectedDispenseAmount.Int()) { case 1: DispenseButton1.Pressed = true; diff --git a/Content.Server/Chemistry/Metabolism/DefaultDrink.cs b/Content.Server/Chemistry/Metabolism/DefaultDrink.cs index 0839d6f164..232ea1e108 100644 --- a/Content.Server/Chemistry/Metabolism/DefaultDrink.cs +++ b/Content.Server/Chemistry/Metabolism/DefaultDrink.cs @@ -1,5 +1,6 @@ using System; using Content.Server.GameObjects.Components.Nutrition; +using Content.Shared.Chemistry; using Content.Shared.Interfaces.Chemistry; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Serialization; @@ -14,8 +15,8 @@ namespace Content.Server.Chemistry.Metabolism class DefaultDrink : IMetabolizable { //Rate of metabolism in units / second - private int _metabolismRate; - public int MetabolismRate => _metabolismRate; + private ReagentUnit _metabolismRate; + public ReagentUnit MetabolismRate => _metabolismRate; //How much thirst is satiated when 1u of the reagent is metabolized private float _hydrationFactor; @@ -23,16 +24,16 @@ namespace Content.Server.Chemistry.Metabolism void IExposeData.ExposeData(ObjectSerializer serializer) { - serializer.DataField(ref _metabolismRate, "rate", 1); + serializer.DataField(ref _metabolismRate, "rate", ReagentUnit.New(1)); serializer.DataField(ref _hydrationFactor, "nutrimentFactor", 30.0f); } //Remove reagent at set rate, satiate thirst if a ThirstComponent can be found - decimal IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime) + ReagentUnit IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime) { - int metabolismAmount = (int)Math.Round(MetabolismRate * tickTime); + var metabolismAmount = MetabolismRate * tickTime; if (solutionEntity.TryGetComponent(out ThirstComponent thirst)) - thirst.UpdateThirst(metabolismAmount * HydrationFactor); + thirst.UpdateThirst(metabolismAmount.Float() * HydrationFactor); //Return amount of reagent to be removed, remove reagent regardless of ThirstComponent presence return metabolismAmount; diff --git a/Content.Server/Chemistry/Metabolism/DefaultFood.cs b/Content.Server/Chemistry/Metabolism/DefaultFood.cs index aca26e2628..d220bb1805 100644 --- a/Content.Server/Chemistry/Metabolism/DefaultFood.cs +++ b/Content.Server/Chemistry/Metabolism/DefaultFood.cs @@ -1,4 +1,5 @@ using Content.Server.GameObjects.Components.Nutrition; +using Content.Shared.Chemistry; using Content.Shared.Interfaces.Chemistry; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Serialization; @@ -13,29 +14,26 @@ namespace Content.Server.Chemistry.Metabolism /// class DefaultFood : IMetabolizable { -#pragma warning disable 649 - [Dependency] private readonly IRounderForReagents _rounder; -#pragma warning restore 649 //Rate of metabolism in units / second - private decimal _metabolismRate; - public decimal MetabolismRate => _metabolismRate; + private ReagentUnit _metabolismRate; + public ReagentUnit MetabolismRate => _metabolismRate; //How much hunger is satiated when 1u of the reagent is metabolized - private decimal _nutritionFactor; - public decimal NutritionFactor => _nutritionFactor; + private float _nutritionFactor; + public float NutritionFactor => _nutritionFactor; void IExposeData.ExposeData(ObjectSerializer serializer) { - serializer.DataField(ref _metabolismRate, "rate", 1M); - serializer.DataField(ref _nutritionFactor, "nutrimentFactor", 30.0M); + serializer.DataField(ref _metabolismRate, "rate", ReagentUnit.New(1M)); + serializer.DataField(ref _nutritionFactor, "nutrimentFactor", 30.0f); } //Remove reagent at set rate, satiate hunger if a HungerComponent can be found - decimal IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime) + ReagentUnit IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime) { - var metabolismAmount = _rounder.Round(MetabolismRate * (decimal) tickTime); + var metabolismAmount = MetabolismRate * tickTime; if (solutionEntity.TryGetComponent(out HungerComponent hunger)) - hunger.UpdateFood((float)(metabolismAmount * NutritionFactor)); + hunger.UpdateFood(metabolismAmount.Float() * NutritionFactor); //Return amount of reagent to be removed, remove reagent regardless of HungerComponent presence return metabolismAmount; diff --git a/Content.Server/Chemistry/ReactionPrototype.cs b/Content.Server/Chemistry/ReactionPrototype.cs index f5c9c8974f..36e2304b16 100644 --- a/Content.Server/Chemistry/ReactionPrototype.cs +++ b/Content.Server/Chemistry/ReactionPrototype.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Content.Shared.Chemistry; using Content.Shared.Interfaces; using Robust.Shared.Interfaces.Serialization; using Robust.Shared.Prototypes; @@ -16,7 +17,7 @@ namespace Content.Server.Chemistry private string _id; private string _name; private Dictionary _reactants; - private Dictionary _products; + private Dictionary _products; private List _effects; public string ID => _id; @@ -28,7 +29,7 @@ namespace Content.Server.Chemistry /// /// Reagents created when the reaction occurs. /// - public IReadOnlyDictionary Products => _products; + public IReadOnlyDictionary Products => _products; /// /// Effects to be triggered when the reaction occurs. /// @@ -41,7 +42,7 @@ namespace Content.Server.Chemistry serializer.DataField(ref _id, "id", string.Empty); serializer.DataField(ref _name, "name", string.Empty); serializer.DataField(ref _reactants, "reactants", new Dictionary()); - serializer.DataField(ref _products, "products", new Dictionary()); + serializer.DataField(ref _products, "products", new Dictionary()); serializer.DataField(ref _effects, "effects", new List()); } } @@ -51,13 +52,13 @@ namespace Content.Server.Chemistry /// public class ReactantPrototype : IExposeData { - private int _amount; + private ReagentUnit _amount; private bool _catalyst; /// /// Minimum amount of the reactant needed for the reaction to occur. /// - public int Amount => _amount; + public ReagentUnit Amount => _amount; /// /// Whether or not the reactant is a catalyst. Catalysts aren't removed when a reaction occurs. /// @@ -65,7 +66,7 @@ namespace Content.Server.Chemistry public void ExposeData(ObjectSerializer serializer) { - serializer.DataField(ref _amount, "amount", 1); + serializer.DataField(ref _amount, "amount", ReagentUnit.New(1)); serializer.DataField(ref _catalyst, "catalyst", false); } } diff --git a/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs b/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs index b9f0527793..865191b580 100644 --- a/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/InjectorComponent.cs @@ -37,13 +37,13 @@ namespace Content.Server.GameObjects.Components.Chemistry /// attempt to inject it's entire contents upon use. /// [ViewVariables] - private decimal _transferAmount; + private ReagentUnit _transferAmount; /// /// Initial storage volume of the injector /// [ViewVariables] - private int _initialMaxVolume; + private ReagentUnit _initialMaxVolume; /// /// The state of the injector. Determines it's attack behavior. Containers must have the @@ -62,8 +62,8 @@ namespace Content.Server.GameObjects.Components.Chemistry { base.ExposeData(serializer); serializer.DataField(ref _injectOnly, "injectOnly", false); - serializer.DataField(ref _initialMaxVolume, "initialMaxVolume", 15); - serializer.DataField(ref _transferAmount, "transferAmount", 5); + serializer.DataField(ref _initialMaxVolume, "initialMaxVolume", ReagentUnit.New(15)); + serializer.DataField(ref _transferAmount, "transferAmount", ReagentUnit.New(5)); } public override void Initialize() @@ -165,7 +165,7 @@ namespace Content.Server.GameObjects.Components.Chemistry } //Get transfer amount. May be smaller than _transferAmount if not enough room - decimal realTransferAmount = Math.Min(_transferAmount, targetBloodstream.EmptyVolume); + var realTransferAmount = ReagentUnit.Min(_transferAmount, targetBloodstream.EmptyVolume); if (realTransferAmount <= 0) { _notifyManager.PopupMessage(Owner.Transform.GridPosition, user, @@ -193,7 +193,7 @@ namespace Content.Server.GameObjects.Components.Chemistry } //Get transfer amount. May be smaller than _transferAmount if not enough room - decimal realTransferAmount = Math.Min(_transferAmount, targetSolution.EmptyVolume); + var realTransferAmount = ReagentUnit.Min(_transferAmount, targetSolution.EmptyVolume); if (realTransferAmount <= 0) { _notifyManager.PopupMessage(Owner.Transform.GridPosition, user, @@ -221,7 +221,7 @@ namespace Content.Server.GameObjects.Components.Chemistry } //Get transfer amount. May be smaller than _transferAmount if not enough room - decimal realTransferAmount = Math.Min(_transferAmount, targetSolution.CurrentVolume); + var realTransferAmount = ReagentUnit.Min(_transferAmount, targetSolution.CurrentVolume); if (realTransferAmount <= 0) { _notifyManager.PopupMessage(Owner.Transform.GridPosition, user, diff --git a/Content.Server/GameObjects/Components/Chemistry/PourableComponent.cs b/Content.Server/GameObjects/Components/Chemistry/PourableComponent.cs index 439fc69ac9..f129e30321 100644 --- a/Content.Server/GameObjects/Components/Chemistry/PourableComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/PourableComponent.cs @@ -4,6 +4,7 @@ using System.Text; using Content.Server.GameObjects.Components.Nutrition; using Content.Server.GameObjects.EntitySystems; using Content.Server.Interfaces; +using Content.Shared.Chemistry; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Localization; @@ -28,13 +29,13 @@ namespace Content.Server.GameObjects.Components.Chemistry public override string Name => "Pourable"; - private decimal _transferAmount; + private ReagentUnit _transferAmount; /// /// The amount of solution to be transferred from this solution when clicking on other solutions with it. /// [ViewVariables] - public decimal TransferAmount + public ReagentUnit TransferAmount { get => _transferAmount; set => _transferAmount = value; @@ -43,7 +44,7 @@ namespace Content.Server.GameObjects.Components.Chemistry public override void ExposeData(ObjectSerializer serializer) { base.ExposeData(serializer); - serializer.DataField(ref _transferAmount, "transferAmount", 5.0M); + serializer.DataField(ref _transferAmount, "transferAmount", ReagentUnit.New(5.0M)); } /// @@ -69,7 +70,7 @@ namespace Content.Server.GameObjects.Components.Chemistry return false; //Get transfer amount. May be smaller than _transferAmount if not enough room - decimal realTransferAmount = Math.Min(attackPourable.TransferAmount, targetSolution.EmptyVolume); + var realTransferAmount = ReagentUnit.Min(attackPourable.TransferAmount, targetSolution.EmptyVolume); if (realTransferAmount <= 0) //Special message if container is full { _notifyManager.PopupMessage(Owner.Transform.GridPosition, eventArgs.User, diff --git a/Content.Server/GameObjects/Components/Chemistry/ReagentDispenserComponent.cs b/Content.Server/GameObjects/Components/Chemistry/ReagentDispenserComponent.cs index 1f8f13625e..78d22c0f65 100644 --- a/Content.Server/GameObjects/Components/Chemistry/ReagentDispenserComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/ReagentDispenserComponent.cs @@ -41,7 +41,7 @@ namespace Content.Server.GameObjects.Components.Chemistry [ViewVariables] private string _packPrototypeId; [ViewVariables] private bool HasBeaker => _beakerContainer.ContainedEntity != null; - [ViewVariables] private decimal _dispenseAmount = 10; + [ViewVariables] private ReagentUnit _dispenseAmount = ReagentUnit.New(10); [ViewVariables] private SolutionComponent Solution => _beakerContainer.ContainedEntity.GetComponent(); @@ -115,22 +115,22 @@ namespace Content.Server.GameObjects.Components.Chemistry TryClear(); break; case UiButton.SetDispenseAmount1: - _dispenseAmount = 1; + _dispenseAmount = ReagentUnit.New(1); break; case UiButton.SetDispenseAmount5: - _dispenseAmount = 5; + _dispenseAmount = ReagentUnit.New(5); break; case UiButton.SetDispenseAmount10: - _dispenseAmount = 10; + _dispenseAmount = ReagentUnit.New(10); break; case UiButton.SetDispenseAmount25: - _dispenseAmount = 25; + _dispenseAmount = ReagentUnit.New(25); break; case UiButton.SetDispenseAmount50: - _dispenseAmount = 50; + _dispenseAmount = ReagentUnit.New(50); break; case UiButton.SetDispenseAmount100: - _dispenseAmount = 100; + _dispenseAmount = ReagentUnit.New(100); break; case UiButton.Dispense: if (HasBeaker) @@ -172,7 +172,7 @@ namespace Content.Server.GameObjects.Components.Chemistry var beaker = _beakerContainer.ContainedEntity; if (beaker == null) { - return new ReagentDispenserBoundUserInterfaceState(false, 0, 0, + return new ReagentDispenserBoundUserInterfaceState(false, ReagentUnit.New(0), ReagentUnit.New(0), "", Inventory, Owner.Name, null, _dispenseAmount); } diff --git a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs index 9bb574a002..0328dcae67 100644 --- a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -22,7 +22,6 @@ namespace Content.Server.GameObjects.Components.Chemistry internal class SolutionComponent : Shared.GameObjects.Components.Chemistry.SolutionComponent, IExamine { #pragma warning disable 649 - [Dependency] private readonly IRounderForReagents _rounder; [Dependency] private readonly IPrototypeManager _prototypeManager; [Dependency] private readonly ILocalizationManager _loc; [Dependency] private readonly IEntitySystemManager _entitySystemManager; @@ -105,8 +104,7 @@ namespace Content.Server.GameObjects.Components.Chemistry if ((handSolutionComp.Capabilities & SolutionCaps.PourOut) == 0 || (component.Capabilities & SolutionCaps.PourIn) == 0) return; - var transferQuantity = Math.Min(component.MaxVolume - component.CurrentVolume, handSolutionComp.CurrentVolume); - transferQuantity = Math.Min(transferQuantity, 10); + var transferQuantity = ReagentUnit.Min(component.MaxVolume - component.CurrentVolume, handSolutionComp.CurrentVolume, ReagentUnit.New(10)); // nothing to transfer if (transferQuantity <= 0) @@ -185,8 +183,7 @@ namespace Content.Server.GameObjects.Components.Chemistry if ((handSolutionComp.Capabilities & SolutionCaps.PourIn) == 0 || (component.Capabilities & SolutionCaps.PourOut) == 0) return; - var transferQuantity = Math.Min(handSolutionComp.MaxVolume - handSolutionComp.CurrentVolume, component.CurrentVolume); - transferQuantity = Math.Min(transferQuantity, 10); + var transferQuantity = ReagentUnit.Min(handSolutionComp.MaxVolume - handSolutionComp.CurrentVolume, component.CurrentVolume, ReagentUnit.New(10)); // pulling from an empty container, pointless to continue if (transferQuantity <= 0) @@ -223,10 +220,9 @@ namespace Content.Server.GameObjects.Components.Chemistry } } - public bool TryAddReagent(string reagentId, decimal quantity, out decimal acceptedQuantity, bool skipReactionCheck = false, bool skipColor = false) + public bool TryAddReagent(string reagentId, ReagentUnit quantity, out ReagentUnit acceptedQuantity, bool skipReactionCheck = false, bool skipColor = false) { - quantity = _rounder.Round(quantity); - var toAcceptQuantity = _rounder.Round(MaxVolume - ContainedSolution.TotalVolume); + var toAcceptQuantity = MaxVolume - ContainedSolution.TotalVolume; if (quantity > toAcceptQuantity) { acceptedQuantity = toAcceptQuantity; @@ -269,16 +265,16 @@ namespace Content.Server.GameObjects.Components.Chemistry /// The reaction whose reactants will be checked for in the solution. /// The number of times the reaction can occur with the given solution. /// - private bool SolutionValidReaction(ReactionPrototype reaction, out decimal unitReactions) + private bool SolutionValidReaction(ReactionPrototype reaction, out ReagentUnit unitReactions) { - unitReactions = decimal.MaxValue; //Set to some impossibly large number initially + unitReactions = ReagentUnit.MaxValue; //Set to some impossibly large number initially foreach (var reactant in reaction.Reactants) { - if (!ContainsReagent(reactant.Key, out decimal reagentQuantity)) + if (!ContainsReagent(reactant.Key, out ReagentUnit reagentQuantity)) { return false; } - var currentUnitReactions = _rounder.Round(reagentQuantity / reactant.Value.Amount); + var currentUnitReactions = reagentQuantity / reactant.Value.Amount; if (currentUnitReactions < unitReactions) { unitReactions = currentUnitReactions; @@ -301,26 +297,26 @@ namespace Content.Server.GameObjects.Components.Chemistry /// Solution to be reacted. /// Reaction to occur. /// The number of times to cause this reaction. - private void PerformReaction(ReactionPrototype reaction, decimal unitReactions) + private void PerformReaction(ReactionPrototype reaction, ReagentUnit unitReactions) { //Remove non-catalysts foreach (var reactant in reaction.Reactants) { if (!reactant.Value.Catalyst) { - var amountToRemove = _rounder.Round(unitReactions * reactant.Value.Amount); + var amountToRemove = unitReactions * reactant.Value.Amount; TryRemoveReagent(reactant.Key, amountToRemove); } } //Add products foreach (var product in reaction.Products) { - TryAddReagent(product.Key, (int)(unitReactions * product.Value), out var acceptedQuantity, true); + TryAddReagent(product.Key, product.Value * unitReactions, out var acceptedQuantity, true); } //Trigger reaction effects foreach (var effect in reaction.Effects) { - effect.React(Owner, unitReactions); + effect.React(Owner, unitReactions.Decimal()); } //Play reaction sound client-side diff --git a/Content.Server/GameObjects/Components/Metabolism/BloodstreamComponent.cs b/Content.Server/GameObjects/Components/Metabolism/BloodstreamComponent.cs index ef4579331d..b384b525b8 100644 --- a/Content.Server/GameObjects/Components/Metabolism/BloodstreamComponent.cs +++ b/Content.Server/GameObjects/Components/Metabolism/BloodstreamComponent.cs @@ -34,17 +34,17 @@ namespace Content.Server.GameObjects.Components.Metabolism /// Max volume of internal solution storage /// [ViewVariables] - private int _initialMaxVolume; + private ReagentUnit _initialMaxVolume; /// /// Empty volume of internal solution /// - public decimal EmptyVolume => _internalSolution.EmptyVolume; + public ReagentUnit EmptyVolume => _internalSolution.EmptyVolume; public override void ExposeData(ObjectSerializer serializer) { base.ExposeData(serializer); - serializer.DataField(ref _initialMaxVolume, "maxVolume", 250); + serializer.DataField(ref _initialMaxVolume, "maxVolume", ReagentUnit.New(250)); } public override void Initialize() diff --git a/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs b/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs index 26d552f71f..76dfe6147d 100644 --- a/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs +++ b/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs @@ -33,18 +33,18 @@ namespace Content.Server.GameObjects.Components.Nutrition [ViewVariables] private string _finishPrototype; - public decimal TransferAmount => _transferAmount; + public ReagentUnit TransferAmount => _transferAmount; [ViewVariables] - private decimal _transferAmount = 2; + private ReagentUnit _transferAmount = ReagentUnit.New(2); - public decimal MaxVolume + public ReagentUnit MaxVolume { get => _contents.MaxVolume; set => _contents.MaxVolume = value; } private Solution _initialContents; // This is just for loading from yaml - private int _maxVolume; + private ReagentUnit _maxVolume; private bool _despawnOnFinish; @@ -57,7 +57,7 @@ namespace Content.Server.GameObjects.Components.Nutrition { return 0; } - return Math.Max(1, (int)Math.Ceiling(_contents.CurrentVolume / _transferAmount)); + return Math.Max(1, (int)Math.Ceiling((_contents.CurrentVolume / _transferAmount).Float())); } @@ -65,7 +65,7 @@ namespace Content.Server.GameObjects.Components.Nutrition { base.ExposeData(serializer); serializer.DataField(ref _initialContents, "contents", null); - serializer.DataField(ref _maxVolume, "max_volume", 0); + serializer.DataField(ref _maxVolume, "max_volume", ReagentUnit.New(0)); serializer.DataField(ref _useSound, "use_sound", "/Audio/items/drink.ogg"); // E.g. cola can when done or clear bottle, whatever // Currently this will enforce it has the same volume but this may change. @@ -91,7 +91,7 @@ namespace Content.Server.GameObjects.Components.Nutrition | SolutionCaps.Injectable; var pourable = Owner.AddComponent(); - pourable.TransferAmount = 5; + pourable.TransferAmount = ReagentUnit.New(5); } } @@ -150,7 +150,7 @@ namespace Content.Server.GameObjects.Components.Nutrition if (user.TryGetComponent(out StomachComponent stomachComponent)) { _drinking = true; - var transferAmount = Math.Min(_transferAmount, _contents.CurrentVolume); + var transferAmount = ReagentUnit.Min(_transferAmount, _contents.CurrentVolume); var split = _contents.SplitSolution(transferAmount); if (stomachComponent.TryTransferSolution(split)) { diff --git a/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs b/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs index a528c223b6..a0039b14f9 100644 --- a/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs +++ b/Content.Server/GameObjects/Components/Nutrition/FoodComponent.cs @@ -33,7 +33,7 @@ namespace Content.Server.GameObjects.Components.Nutrition [ViewVariables] private SolutionComponent _contents; [ViewVariables] - private int _transferAmount; + private ReagentUnit _transferAmount; private Solution _initialContents; // This is just for loading from yaml @@ -44,7 +44,7 @@ namespace Content.Server.GameObjects.Components.Nutrition serializer.DataField(ref _initialContents, "contents", null); serializer.DataField(ref _useSound, "use_sound", "/Audio/items/eatfood.ogg"); // Default is transfer 30 units - serializer.DataField(ref _transferAmount, "transfer_amount", 5); + serializer.DataField(ref _transferAmount, "transfer_amount", ReagentUnit.New(5)); // E.g. empty chip packet when done serializer.DataField(ref _finishPrototype, "spawn_on_finish", null); } @@ -78,7 +78,7 @@ namespace Content.Server.GameObjects.Components.Nutrition _initialContents = null; if (_contents.CurrentVolume == 0) { - _contents.TryAddReagent("chem.Nutriment", 5, out _); + _contents.TryAddReagent("chem.Nutriment", ReagentUnit.New(5), out _); } Owner.TryGetComponent(out AppearanceComponent appearance); _appearanceComponent = appearance; @@ -99,7 +99,7 @@ namespace Content.Server.GameObjects.Components.Nutrition { return 0; } - return Math.Max(1, (int)Math.Ceiling(_contents.CurrentVolume / _transferAmount)); + return Math.Max(1, (int)Math.Ceiling((_contents.CurrentVolume / _transferAmount).Float())); } bool IUse.UseEntity(UseEntityEventArgs eventArgs) @@ -130,7 +130,7 @@ namespace Content.Server.GameObjects.Components.Nutrition // TODO: Add putting food back in boxes here? if (user.TryGetComponent(out StomachComponent stomachComponent)) { - var transferAmount = Math.Min(_transferAmount, _contents.CurrentVolume); + var transferAmount = ReagentUnit.Min(_transferAmount, _contents.CurrentVolume); var split = _contents.SplitSolution(transferAmount); if (stomachComponent.TryTransferSolution(split)) { diff --git a/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs b/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs index 9923767503..83f07710dd 100644 --- a/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs +++ b/Content.Server/GameObjects/Components/Nutrition/StomachComponent.cs @@ -27,7 +27,7 @@ namespace Content.Server.GameObjects.Components.Nutrition /// /// Max volume of internal solution storage /// - public decimal MaxVolume + public ReagentUnit MaxVolume { get => _stomachContents.MaxVolume; set => _stomachContents.MaxVolume = value; @@ -43,7 +43,7 @@ namespace Content.Server.GameObjects.Components.Nutrition /// Initial internal solution storage volume /// [ViewVariables] - private int _initialMaxVolume; + private ReagentUnit _initialMaxVolume; /// /// Time in seconds between reagents being ingested and them being transferred to @@ -64,7 +64,7 @@ namespace Content.Server.GameObjects.Components.Nutrition public override void ExposeData(ObjectSerializer serializer) { base.ExposeData(serializer); - serializer.DataField(ref _initialMaxVolume, "maxVolume", 100); + serializer.DataField(ref _initialMaxVolume, "maxVolume", ReagentUnit.New(100)); serializer.DataField(ref _digestionDelay, "digestionDelay", 20); } @@ -119,7 +119,7 @@ namespace Content.Server.GameObjects.Components.Nutrition } //Add reagents ready for transfer to bloodstream to transferSolution - var transferSolution = IoCManager.InjectDependencies(new Solution()); + var transferSolution = new Solution(); foreach (var delta in _reagentDeltas.ToList()) //Use ToList here to remove entries while iterating { //Increment lifetime of reagents @@ -141,10 +141,10 @@ namespace Content.Server.GameObjects.Components.Nutrition private class ReagentDelta { public readonly string ReagentId; - public readonly decimal Quantity; + public readonly ReagentUnit Quantity; public float Lifetime { get; private set; } - public ReagentDelta(string reagentId, decimal quantity) + public ReagentDelta(string reagentId, ReagentUnit quantity) { ReagentId = reagentId; Quantity = quantity; diff --git a/Content.Server/ServerContentIoC.cs b/Content.Server/ServerContentIoC.cs index 1c5937c0fe..f4bb04b9cc 100644 --- a/Content.Server/ServerContentIoC.cs +++ b/Content.Server/ServerContentIoC.cs @@ -28,7 +28,6 @@ namespace Content.Server IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); - IoCManager.Register(); } } } diff --git a/Content.Shared/Chemistry/DefaultMetabolizable.cs b/Content.Shared/Chemistry/DefaultMetabolizable.cs index 31d91d351b..05686ccf07 100644 --- a/Content.Shared/Chemistry/DefaultMetabolizable.cs +++ b/Content.Shared/Chemistry/DefaultMetabolizable.cs @@ -9,9 +9,6 @@ namespace Content.Shared.Chemistry //Default metabolism for reagents. Metabolizes the reagent with no effects class DefaultMetabolizable : IMetabolizable { -#pragma warning disable 649 - [Dependency] private readonly IRounderForReagents _rounder; -#pragma warning restore 649 //Rate of metabolism in units / second private decimal _metabolismRate = 1; public decimal MetabolismRate => _metabolismRate; @@ -21,10 +18,9 @@ namespace Content.Shared.Chemistry serializer.DataField(ref _metabolismRate, "rate", 1); } - decimal IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime) + ReagentUnit IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime) { - var metabolismAmount = _rounder.Round(MetabolismRate * (decimal)tickTime); - return metabolismAmount; + return ReagentUnit.New(MetabolismRate * (decimal)tickTime); } } } diff --git a/Content.Shared/Chemistry/ReagentUnit.cs b/Content.Shared/Chemistry/ReagentUnit.cs new file mode 100644 index 0000000000..a9350c5b3c --- /dev/null +++ b/Content.Shared/Chemistry/ReagentUnit.cs @@ -0,0 +1,162 @@ +using System; +using System.Linq; + +namespace Content.Shared.Chemistry +{ + [Serializable] + public struct ReagentUnit + { + private int _value; + private static readonly int Shift = 2; + + public static ReagentUnit MaxValue => new ReagentUnit(int.MaxValue); + + private decimal ShiftDown() + { + return _value / (decimal)Math.Pow(10, Shift); + } + + private decimal ShiftUp() + { + return _value * (decimal)Math.Pow(10, Shift); + } + + private ReagentUnit(int value) + { + _value = value; + } + + public static ReagentUnit New(int value) + { + return new ReagentUnit(value * (int) Math.Pow(10, Shift)); + } + + public static ReagentUnit New(decimal value) + { + return new ReagentUnit((int) Math.Round(value * (decimal) Math.Pow(10, Shift))); + } + + public static ReagentUnit New(float value) + { + return new ReagentUnit((int) Math.Round(value * (float) Math.Pow(10, Shift))); + } + + public static ReagentUnit New(double value) + { + return new ReagentUnit((int) Math.Round(value * Math.Pow(10, Shift))); + } + + public static ReagentUnit operator +(ReagentUnit a) => a; + + public static ReagentUnit operator -(ReagentUnit a) => new ReagentUnit(-a._value); + + public static ReagentUnit operator +(ReagentUnit a, ReagentUnit b) + => new ReagentUnit(a._value + b._value); + + public static ReagentUnit operator -(ReagentUnit a, ReagentUnit b) + => a + -b; + + public static ReagentUnit operator *(ReagentUnit a, ReagentUnit b) + { + var aD = a.ShiftDown(); + var bD = b.ShiftDown(); + return New(aD * bD); + } + + public static ReagentUnit operator *(ReagentUnit a, float b) + { + var aD = (float) a.ShiftDown(); + return New(aD * b); + } + + public static ReagentUnit operator *(ReagentUnit a, decimal b) + { + var aD = a.ShiftDown(); + return New(aD * b); + } + + public static ReagentUnit operator /(ReagentUnit a, ReagentUnit b) + { + if (b._value == 0) + { + throw new DivideByZeroException(); + } + var aD = a.ShiftDown(); + var bD = b.ShiftDown(); + return New(aD / bD); + } + + public static bool operator <=(ReagentUnit a, int b) + { + return a.ShiftDown() <= b; + } + + public static bool operator >=(ReagentUnit a, int b) + { + return a.ShiftDown() >= b; + } + + public static bool operator ==(ReagentUnit a, int b) + { + return a.ShiftDown() == b; + } + + public static bool operator !=(ReagentUnit a, int b) + { + return a.ShiftDown() != b; + } + + public static bool operator <=(ReagentUnit a, ReagentUnit b) + { + return a._value <= b._value; + } + + public static bool operator >=(ReagentUnit a, ReagentUnit b) + { + return a._value >= b._value; + } + + public static bool operator <(ReagentUnit a, ReagentUnit b) + { + return a._value < b._value; + } + + public static bool operator >(ReagentUnit a, ReagentUnit b) + { + return a._value > b._value; + } + + public override string ToString() => $"{ShiftDown()}"; + + public float Float() + { + return (float) ShiftDown(); + } + + public decimal Decimal() + { + return (decimal) ShiftDown(); + } + + public int Int() + { + return (int) ShiftDown(); + } + + public static ReagentUnit Min(params ReagentUnit[] reagentUnits) + { + return reagentUnits.OrderBy(x => x._value).First(); + } + + public override bool Equals(object obj) + { + return obj is ReagentUnit unit && + _value == unit._value; + } + + public override int GetHashCode() + { + return HashCode.Combine(_value); + } + } +} diff --git a/Content.Shared/Chemistry/RounderForReagents.cs b/Content.Shared/Chemistry/RounderForReagents.cs deleted file mode 100644 index d7c6ccce11..0000000000 --- a/Content.Shared/Chemistry/RounderForReagents.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Content.Shared.Interfaces.Chemistry; -using System; - -namespace Content.Shared.Chemistry -{ - - public class RounderForReagents : IRounderForReagents - { - public decimal Round(decimal value) - { - return Math.Round(value, 2); - } - } -} diff --git a/Content.Shared/Chemistry/Solution.cs b/Content.Shared/Chemistry/Solution.cs index 365372f91f..13b15d94b4 100644 --- a/Content.Shared/Chemistry/Solution.cs +++ b/Content.Shared/Chemistry/Solution.cs @@ -16,9 +16,6 @@ namespace Content.Shared.Chemistry /// public class Solution : IExposeData, IEnumerable { -#pragma warning disable 649 - [Dependency] private readonly IRounderForReagents _rounder; -#pragma warning restore 649 // Most objects on the station hold only 1 or 2 reagents [ViewVariables] private List _contents = new List(2); @@ -28,7 +25,7 @@ namespace Content.Shared.Chemistry /// The calculated total volume of all reagents in the solution (ex. Total volume of liquid in beaker). /// [ViewVariables] - public decimal TotalVolume { get; private set; } + public ReagentUnit TotalVolume { get; private set; } /// /// Constructs an empty solution (ex. an empty beaker). @@ -40,7 +37,7 @@ namespace Content.Shared.Chemistry /// /// The prototype ID of the reagent to add. /// The quantity in milli-units. - public Solution(string reagentId, int quantity) + public Solution(string reagentId, ReagentUnit quantity) { AddReagent(reagentId, quantity); } @@ -52,7 +49,7 @@ namespace Content.Shared.Chemistry if (serializer.Reading) { - TotalVolume = 0; + TotalVolume = ReagentUnit.New(0); foreach (var reagent in _contents) { TotalVolume += reagent.Quantity; @@ -65,9 +62,8 @@ namespace Content.Shared.Chemistry /// /// The prototype ID of the reagent to add. /// The quantity in milli-units. - public void AddReagent(string reagentId, decimal quantity) + public void AddReagent(string reagentId, ReagentUnit quantity) { - quantity = _rounder.Round(quantity); if (quantity <= 0) return; @@ -91,7 +87,7 @@ namespace Content.Shared.Chemistry /// /// The prototype ID of the reagent to add. /// The quantity in milli-units. - public decimal GetReagentQuantity(string reagentId) + public ReagentUnit GetReagentQuantity(string reagentId) { for (var i = 0; i < _contents.Count; i++) { @@ -99,10 +95,10 @@ namespace Content.Shared.Chemistry return _contents[i].Quantity; } - return 0; + return ReagentUnit.New(0); } - public void RemoveReagent(string reagentId, decimal quantity) + public void RemoveReagent(string reagentId, ReagentUnit quantity) { if(quantity <= 0) return; @@ -115,7 +111,7 @@ namespace Content.Shared.Chemistry var curQuantity = reagent.Quantity; - var newQuantity = _rounder.Round(curQuantity - quantity); + var newQuantity = curQuantity - quantity; if (newQuantity <= 0) { _contents.RemoveSwap(i); @@ -135,12 +131,12 @@ namespace Content.Shared.Chemistry /// Remove the specified quantity from this solution. /// /// The quantity of this solution to remove - public void RemoveSolution(decimal quantity) + public void RemoveSolution(ReagentUnit quantity) { if(quantity <= 0) return; - var ratio = _rounder.Round(TotalVolume - quantity) / TotalVolume; + var ratio = (TotalVolume - quantity).Decimal() / TotalVolume.Decimal(); if (ratio <= 0) { @@ -155,24 +151,24 @@ namespace Content.Shared.Chemistry // 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 = _rounder.Round(oldQuantity * ratio); + var newQuantity = oldQuantity * ratio; _contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity); } - TotalVolume = _rounder.Round(TotalVolume * ratio); + TotalVolume = TotalVolume * ratio; } public void RemoveAllSolution() { _contents.Clear(); - TotalVolume = 0; + TotalVolume = ReagentUnit.New(0); } - public Solution SplitSolution(decimal quantity) + public Solution SplitSolution(ReagentUnit quantity) { if (quantity <= 0) - return IoCManager.InjectDependencies(new Solution()); + return new Solution(); Solution newSolution; @@ -183,15 +179,15 @@ namespace Content.Shared.Chemistry return newSolution; } - newSolution = IoCManager.InjectDependencies(new Solution()); - var newTotalVolume = 0M; - var ratio = (TotalVolume - quantity) / TotalVolume; + newSolution = new Solution(); + var newTotalVolume = ReagentUnit.New(0M); + var ratio = (TotalVolume - quantity).Decimal() / TotalVolume.Decimal(); for (var i = 0; i < _contents.Count; i++) { var reagent = _contents[i]; - var newQuantity = (int)Math.Floor(reagent.Quantity * ratio); + var newQuantity = reagent.Quantity * ratio; var splitQuantity = reagent.Quantity - newQuantity; _contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity); @@ -199,7 +195,7 @@ namespace Content.Shared.Chemistry newTotalVolume += splitQuantity; } - TotalVolume = (int)Math.Floor(TotalVolume * ratio); + TotalVolume = TotalVolume * ratio; newSolution.TotalVolume = newTotalVolume; return newSolution; @@ -234,8 +230,8 @@ namespace Content.Shared.Chemistry public Solution Clone() { - var volume = 0M; - var newSolution = IoCManager.InjectDependencies(new Solution()); + var volume = ReagentUnit.New(0); + var newSolution = new Solution(); for (var i = 0; i < _contents.Count; i++) { @@ -252,9 +248,9 @@ namespace Content.Shared.Chemistry public readonly struct ReagentQuantity { public readonly string ReagentId; - public readonly decimal Quantity; + public readonly ReagentUnit Quantity; - public ReagentQuantity(string reagentId, decimal quantity) + public ReagentQuantity(string reagentId, ReagentUnit quantity) { ReagentId = reagentId; Quantity = quantity; diff --git a/Content.Shared/GameObjects/Components/Chemistry/SharedInjectorComponent.cs b/Content.Shared/GameObjects/Components/Chemistry/SharedInjectorComponent.cs index 8bf232b96f..4fbef4ff26 100644 --- a/Content.Shared/GameObjects/Components/Chemistry/SharedInjectorComponent.cs +++ b/Content.Shared/GameObjects/Components/Chemistry/SharedInjectorComponent.cs @@ -1,4 +1,5 @@ using System; +using Content.Shared.Chemistry; using Robust.Shared.GameObjects; using Robust.Shared.Serialization; @@ -18,11 +19,11 @@ namespace Content.Shared.GameObjects.Components.Chemistry [Serializable, NetSerializable] protected sealed class InjectorComponentState : ComponentState { - public decimal CurrentVolume { get; } - public decimal TotalVolume { get; } + public ReagentUnit CurrentVolume { get; } + public ReagentUnit TotalVolume { get; } public InjectorToggleMode CurrentMode { get; } - public InjectorComponentState(decimal currentVolume, decimal totalVolume, InjectorToggleMode currentMode) : base(ContentNetIDs.REAGENT_INJECTOR) + public InjectorComponentState(ReagentUnit currentVolume, ReagentUnit totalVolume, InjectorToggleMode currentMode) : base(ContentNetIDs.REAGENT_INJECTOR) { CurrentVolume = currentVolume; TotalVolume = totalVolume; diff --git a/Content.Shared/GameObjects/Components/Chemistry/SharedReagentDispenserComponent.cs b/Content.Shared/GameObjects/Components/Chemistry/SharedReagentDispenserComponent.cs index 9907fb3550..d9ff0581dd 100644 --- a/Content.Shared/GameObjects/Components/Chemistry/SharedReagentDispenserComponent.cs +++ b/Content.Shared/GameObjects/Components/Chemistry/SharedReagentDispenserComponent.cs @@ -26,8 +26,8 @@ namespace Content.Shared.GameObjects.Components.Chemistry public class ReagentDispenserBoundUserInterfaceState : BoundUserInterfaceState { public readonly bool HasBeaker; - public readonly decimal BeakerCurrentVolume; - public readonly decimal BeakerMaxVolume; + public readonly ReagentUnit BeakerCurrentVolume; + public readonly ReagentUnit BeakerMaxVolume; public readonly string ContainerName; /// /// A list of the reagents which this dispenser can dispense. @@ -38,10 +38,10 @@ namespace Content.Shared.GameObjects.Components.Chemistry /// public readonly List ContainerReagents; public readonly string DispenserName; - public readonly decimal SelectedDispenseAmount; + public readonly ReagentUnit SelectedDispenseAmount; - public ReagentDispenserBoundUserInterfaceState(bool hasBeaker, decimal beakerCurrentVolume, decimal beakerMaxVolume, string containerName, - List inventory, string dispenserName, List containerReagents, decimal selectedDispenseAmount) + public ReagentDispenserBoundUserInterfaceState(bool hasBeaker, ReagentUnit beakerCurrentVolume, ReagentUnit beakerMaxVolume, string containerName, + List inventory, string dispenserName, List containerReagents, ReagentUnit selectedDispenseAmount) { HasBeaker = hasBeaker; BeakerCurrentVolume = beakerCurrentVolume; diff --git a/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs index 81a91b3d38..615c1d85b0 100644 --- a/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -18,7 +18,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry [ViewVariables] protected Solution ContainedSolution; - private decimal _maxVolume; + private ReagentUnit _maxVolume; private SolutionCaps _capabilities; /// @@ -30,7 +30,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry /// The maximum volume of the container. /// [ViewVariables(VVAccess.ReadWrite)] - public decimal MaxVolume + public ReagentUnit MaxVolume { get => _maxVolume; set => _maxVolume = value; // Note that the contents won't spill out if the capacity is reduced. @@ -40,13 +40,13 @@ namespace Content.Shared.GameObjects.Components.Chemistry /// The total volume of all the of the reagents in the container. /// [ViewVariables] - public decimal CurrentVolume => ContainedSolution.TotalVolume; + public ReagentUnit CurrentVolume => ContainedSolution.TotalVolume; /// /// The volume without reagents remaining in the container. /// [ViewVariables] - public decimal EmptyVolume => MaxVolume - CurrentVolume; + public ReagentUnit EmptyVolume => MaxVolume - CurrentVolume; /// /// The current blended color of all the reagents in the container. @@ -94,14 +94,14 @@ namespace Content.Shared.GameObjects.Components.Chemistry { base.ExposeData(serializer); - serializer.DataField(ref _maxVolume, "maxVol", 0M); - serializer.DataField(ref ContainedSolution, "contents", IoCManager.InjectDependencies(new Solution())); + serializer.DataField(ref _maxVolume, "maxVol", ReagentUnit.New(0M)); + serializer.DataField(ref ContainedSolution, "contents", new Solution()); serializer.DataField(ref _capabilities, "caps", SolutionCaps.None); } public virtual void Init() { - ContainedSolution = IoCManager.InjectDependencies(new Solution()); + ContainedSolution = new Solution(); } /// @@ -118,7 +118,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry base.Shutdown(); ContainedSolution.RemoveAllSolution(); - ContainedSolution = IoCManager.InjectDependencies(new Solution()); + ContainedSolution = new Solution(); } public void RemoveAllSolution() @@ -127,7 +127,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry OnSolutionChanged(); } - public bool TryRemoveReagent(string reagentId, decimal quantity) + public bool TryRemoveReagent(string reagentId, ReagentUnit quantity) { if (!ContainsReagent(reagentId, out var currentQuantity)) return false; @@ -141,7 +141,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry /// /// Quantity of this solution to remove /// Whether or not the solution was successfully removed - public bool TryRemoveSolution(int quantity) + public bool TryRemoveSolution(ReagentUnit quantity) { if (CurrentVolume == 0) return false; @@ -151,7 +151,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry return true; } - public Solution SplitSolution(decimal quantity) + public Solution SplitSolution(ReagentUnit quantity) { var solutionSplit = ContainedSolution.SplitSolution(quantity); OnSolutionChanged(); @@ -164,7 +164,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry SubstanceColor = Color.White; Color mixColor = default; - var runningTotalQuantity = 0M; + var runningTotalQuantity = ReagentUnit.New(0M); foreach (var reagent in ContainedSolution) { @@ -176,7 +176,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry if (mixColor == default) mixColor = proto.SubstanceColor; - mixColor = BlendRGB(mixColor, proto.SubstanceColor, (float) (reagent.Quantity / runningTotalQuantity)); + mixColor = BlendRGB(mixColor, proto.SubstanceColor, reagent.Quantity.Float() / runningTotalQuantity.Float()); } } @@ -221,7 +221,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry /// The reagent to check for. /// Output the quantity of the reagent if it is contained, 0 if it isn't. /// Return true if the solution contains the reagent. - public bool ContainsReagent(string reagentId, out decimal quantity) + public bool ContainsReagent(string reagentId, out ReagentUnit quantity) { foreach (var reagent in ContainedSolution.Contents) { @@ -231,7 +231,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry return true; } } - quantity = 0; + quantity = ReagentUnit.New(0); return false; } diff --git a/Content.Shared/Interfaces/Chemistry/IMetabolizable.cs b/Content.Shared/Interfaces/Chemistry/IMetabolizable.cs index 3a55de645f..1973012d52 100644 --- a/Content.Shared/Interfaces/Chemistry/IMetabolizable.cs +++ b/Content.Shared/Interfaces/Chemistry/IMetabolizable.cs @@ -19,6 +19,6 @@ namespace Content.Shared.Interfaces.Chemistry /// The reagent id /// The time since the last metabolism tick in seconds. /// The amount of reagent to be removed. The metabolizing organ should handle removing the reagent. - decimal Metabolize(IEntity solutionEntity, string reagentId, float tickTime); + ReagentUnit Metabolize(IEntity solutionEntity, string reagentId, float tickTime); } } diff --git a/Content.Shared/Interfaces/Chemistry/IRounderForReagents.cs b/Content.Shared/Interfaces/Chemistry/IRounderForReagents.cs deleted file mode 100644 index da8a1871c7..0000000000 --- a/Content.Shared/Interfaces/Chemistry/IRounderForReagents.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Content.Shared.Interfaces.Chemistry -{ - public interface IRounderForReagents - { - decimal Round(decimal value); - } -} diff --git a/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs b/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs new file mode 100644 index 0000000000..f2f11b4fdb --- /dev/null +++ b/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs @@ -0,0 +1,113 @@ +using Content.Shared.Chemistry; +using NUnit.Framework; +using System; + +namespace Content.Tests.Shared.Chemistry +{ + [TestFixture, TestOf(typeof(ReagentUnit))] + public class ReagentUnit_Tests + { + [Test] + [TestCase(1, "1")] + [TestCase(0, "0")] + [TestCase(-1, "-1")] + public void ReagentUnitIntegerTests(int value, string expected) + { + var result = ReagentUnit.New(value); + Assert.AreEqual(expected, $"{result}"); + } + + [Test] + [TestCase(1.001f, "1")] + [TestCase(0.999f, "1")] + public void ReagentUnitFloatTests(float value, string expected) + { + var result = ReagentUnit.New(value); + Assert.AreEqual(expected, $"{result}"); + } + + [Test] + [TestCase(1.001d, "1")] + [TestCase(0.999d, "1")] + public void ReagentUnitDoubleTests(double value, string expected) + { + var result = ReagentUnit.New(value); + Assert.AreEqual(expected, $"{result}"); + } + + [Test] + [TestCase("1.001", "1")] + [TestCase("0.999", "1")] + public void ReagentUnitDecimalTests(string valueAsString, string expected) + { + var value = decimal.Parse(valueAsString); + var result = ReagentUnit.New(value); + Assert.AreEqual(expected, $"{result}"); + } + + [Test] + [TestCase(1.001f, 1.001f, "2")] + [TestCase(1.001f, 1.004f, "2")] + [TestCase(1f, 2.005f, "3.01")] + public void CalculusPlus(float aFloat, float bFloat, string expected) + { + var a = ReagentUnit.New(aFloat); + var b = ReagentUnit.New(bFloat); + + var result = a + b; + + Assert.AreEqual(expected, $"{result}"); + } + + [Test] + [TestCase(1.001f, 1.001f, "0")] + [TestCase(1.001f, 1.004f, "0")] + [TestCase(1f, 2.005f, "-1.01")] + public void CalculusMinus(float aFloat, float bFloat, string expected) + { + var a = ReagentUnit.New(aFloat); + var b = ReagentUnit.New(bFloat); + + var result = a - b; + + Assert.AreEqual(expected, $"{result}"); + } + + [Test] + [TestCase(1.001f, 3f, "0.33")] + [TestCase(0.999f, 3f, "0.33")] + [TestCase(2.1f, 3f, "0.7")] + public void CalculusDivision(float aFloat, float bFloat, string expected) + { + var a = ReagentUnit.New(aFloat); + var b = ReagentUnit.New(bFloat); + + var result = a / b; + + Assert.AreEqual(expected, $"{result}"); + } + + [Test] + [TestCase(1.001f, 0.999f, "1")] + [TestCase(0.999f, 3f, "3")] + public void CalculusMultiplication(float aFloat, float bFloat, string expected) + { + var a = ReagentUnit.New(aFloat); + var b = ReagentUnit.New(bFloat); + + var result = a * b; + + Assert.AreEqual(expected, $"{result}"); + } + + [Test] + [TestCase(0.995f, 100)] + [TestCase(1.005f, 100)] + [TestCase(2.005f, 201)] + public void FloatRoundingTest(float a, int expected) + { + var result = (int) Math.Round(a * (float) Math.Pow(10, 2)); + Assert.AreEqual(expected, result); + } + } +} diff --git a/Content.Tests/Shared/Chemistry/Solution_Tests.cs b/Content.Tests/Shared/Chemistry/Solution_Tests.cs index cada149224..e3024d3a07 100644 --- a/Content.Tests/Shared/Chemistry/Solution_Tests.cs +++ b/Content.Tests/Shared/Chemistry/Solution_Tests.cs @@ -10,19 +10,19 @@ namespace Content.Tests.Shared.Chemistry public void AddReagentAndGetSolution() { var solution = new Solution(); - solution.AddReagent("water", 1000); + solution.AddReagent("water", ReagentUnit.New(1000)); var quantity = solution.GetReagentQuantity("water"); - Assert.That(quantity, Is.EqualTo(1000)); + Assert.That(quantity.Int(), Is.EqualTo(1000)); } [Test] public void ConstructorAddReagent() { - var solution = new Solution("water", 1000); + var solution = new Solution("water", ReagentUnit.New(1000)); var quantity = solution.GetReagentQuantity("water"); - Assert.That(quantity, Is.EqualTo(1000)); + Assert.That(quantity.Int(), Is.EqualTo(1000)); } [Test] @@ -31,223 +31,223 @@ namespace Content.Tests.Shared.Chemistry var solution = new Solution(); var quantity = solution.GetReagentQuantity("water"); - Assert.That(quantity, Is.EqualTo(0)); + Assert.That(quantity.Int(), Is.EqualTo(0)); } [Test] public void AddLessThanZeroReagentReturnsZero() { - var solution = new Solution("water", -1000); + var solution = new Solution("water", ReagentUnit.New(-1000)); var quantity = solution.GetReagentQuantity("water"); - Assert.That(quantity, Is.EqualTo(0)); + Assert.That(quantity.Int(), Is.EqualTo(0)); } [Test] public void AddingReagentsSumsProperly() { var solution = new Solution(); - solution.AddReagent("water", 1000); - solution.AddReagent("water", 2000); + solution.AddReagent("water", ReagentUnit.New(1000)); + solution.AddReagent("water", ReagentUnit.New(2000)); var quantity = solution.GetReagentQuantity("water"); - Assert.That(quantity, Is.EqualTo(3000)); + Assert.That(quantity.Int(), Is.EqualTo(3000)); } [Test] public void ReagentQuantitiesStayUnique() { var solution = new Solution(); - solution.AddReagent("water", 1000); - solution.AddReagent("fire", 2000); + solution.AddReagent("water", ReagentUnit.New(1000)); + solution.AddReagent("fire", ReagentUnit.New(2000)); - Assert.That(solution.GetReagentQuantity("water"), Is.EqualTo(1000)); - Assert.That(solution.GetReagentQuantity("fire"), Is.EqualTo(2000)); + Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(1000)); + Assert.That(solution.GetReagentQuantity("fire").Int(), Is.EqualTo(2000)); } [Test] public void TotalVolumeIsCorrect() { var solution = new Solution(); - solution.AddReagent("water", 1000); - solution.AddReagent("fire", 2000); + solution.AddReagent("water", ReagentUnit.New(1000)); + solution.AddReagent("fire", ReagentUnit.New(2000)); - Assert.That(solution.TotalVolume, Is.EqualTo(3000)); + Assert.That(solution.TotalVolume.Int(), Is.EqualTo(3000)); } [Test] public void CloningSolutionIsCorrect() { var solution = new Solution(); - solution.AddReagent("water", 1000); - solution.AddReagent("fire", 2000); + solution.AddReagent("water", ReagentUnit.New(1000)); + solution.AddReagent("fire", ReagentUnit.New(2000)); var newSolution = solution.Clone(); - Assert.That(newSolution.GetReagentQuantity("water"), Is.EqualTo(1000)); - Assert.That(newSolution.GetReagentQuantity("fire"), Is.EqualTo(2000)); - Assert.That(newSolution.TotalVolume, Is.EqualTo(3000)); + 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)); } [Test] public void RemoveSolutionRecalculatesProperly() { var solution = new Solution(); - solution.AddReagent("water", 1000); - solution.AddReagent("fire", 2000); + solution.AddReagent("water", ReagentUnit.New(1000)); + solution.AddReagent("fire", ReagentUnit.New(2000)); - solution.RemoveReagent("water", 500); + solution.RemoveReagent("water", ReagentUnit.New(500)); - Assert.That(solution.GetReagentQuantity("water"), Is.EqualTo(500)); - Assert.That(solution.GetReagentQuantity("fire"), Is.EqualTo(2000)); - Assert.That(solution.TotalVolume, Is.EqualTo(2500)); + 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)); } [Test] public void RemoveLessThanOneQuantityDoesNothing() { - var solution = new Solution("water", 100); + var solution = new Solution("water", ReagentUnit.New(100)); - solution.RemoveReagent("water", -100); + solution.RemoveReagent("water", ReagentUnit.New(-100)); - Assert.That(solution.GetReagentQuantity("water"), Is.EqualTo(100)); - Assert.That(solution.TotalVolume, Is.EqualTo(100)); + Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(100)); + Assert.That(solution.TotalVolume.Int(), Is.EqualTo(100)); } [Test] public void RemoveMoreThanTotalRemovesAllReagent() { - var solution = new Solution("water", 100); + var solution = new Solution("water", ReagentUnit.New(100)); - solution.RemoveReagent("water", 1000); + solution.RemoveReagent("water", ReagentUnit.New(1000)); - Assert.That(solution.GetReagentQuantity("water"), Is.EqualTo(0)); - Assert.That(solution.TotalVolume, Is.EqualTo(0)); + Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(0)); + Assert.That(solution.TotalVolume.Int(), Is.EqualTo(0)); } [Test] public void RemoveNonExistReagentDoesNothing() { - var solution = new Solution("water", 100); + var solution = new Solution("water", ReagentUnit.New(100)); - solution.RemoveReagent("fire", 1000); + solution.RemoveReagent("fire", ReagentUnit.New(1000)); - Assert.That(solution.GetReagentQuantity("water"), Is.EqualTo(100)); - Assert.That(solution.TotalVolume, Is.EqualTo(100)); + Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(100)); + Assert.That(solution.TotalVolume.Int(), Is.EqualTo(100)); } [Test] public void RemoveSolution() { - var solution = new Solution("water", 700); + var solution = new Solution("water", ReagentUnit.New(700)); - solution.RemoveSolution(500); + solution.RemoveSolution(ReagentUnit.New(500)); //Check that edited solution is correct - Assert.That(solution.GetReagentQuantity("water"), Is.EqualTo(200)); - Assert.That(solution.TotalVolume, Is.EqualTo(200)); + Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(200)); + Assert.That(solution.TotalVolume.Int(), Is.EqualTo(200)); } [Test] public void RemoveSolutionMoreThanTotalRemovesAll() { - var solution = new Solution("water", 800); + var solution = new Solution("water", ReagentUnit.New(800)); - solution.RemoveSolution(1000); + solution.RemoveSolution(ReagentUnit.New(1000)); //Check that edited solution is correct - Assert.That(solution.GetReagentQuantity("water"), Is.EqualTo(0)); - Assert.That(solution.TotalVolume, Is.EqualTo(0)); + Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(0)); + Assert.That(solution.TotalVolume.Int(), Is.EqualTo(0)); } [Test] public void RemoveSolutionRatioPreserved() { var solution = new Solution(); - solution.AddReagent("water", 1000); - solution.AddReagent("fire", 2000); + solution.AddReagent("water", ReagentUnit.New(1000)); + solution.AddReagent("fire", ReagentUnit.New(2000)); - solution.RemoveSolution(1500); + solution.RemoveSolution(ReagentUnit.New(1500)); - Assert.That(solution.GetReagentQuantity("water"), Is.EqualTo(500)); - Assert.That(solution.GetReagentQuantity("fire"), Is.EqualTo(1000)); - Assert.That(solution.TotalVolume, Is.EqualTo(1500)); + 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)); } [Test] public void RemoveSolutionLessThanOneDoesNothing() { - var solution = new Solution("water", 800); + var solution = new Solution("water", ReagentUnit.New(800)); - solution.RemoveSolution(-200); + solution.RemoveSolution(ReagentUnit.New(-200)); - Assert.That(solution.GetReagentQuantity("water"), Is.EqualTo(800)); - Assert.That(solution.TotalVolume, Is.EqualTo(800)); + Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(800)); + Assert.That(solution.TotalVolume.Int(), Is.EqualTo(800)); } [Test] public void SplitSolution() { var solution = new Solution(); - solution.AddReagent("water", 1000); - solution.AddReagent("fire", 2000); + solution.AddReagent("water", ReagentUnit.New(1000)); + solution.AddReagent("fire", ReagentUnit.New(2000)); - var splitSolution = solution.SplitSolution(750); + var splitSolution = solution.SplitSolution(ReagentUnit.New(750)); - Assert.That(solution.GetReagentQuantity("water"), Is.EqualTo(750)); - Assert.That(solution.GetReagentQuantity("fire"), Is.EqualTo(1500)); - Assert.That(solution.TotalVolume, Is.EqualTo(2250)); + 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(splitSolution.GetReagentQuantity("water"), Is.EqualTo(250)); - Assert.That(splitSolution.GetReagentQuantity("fire"), Is.EqualTo(500)); - Assert.That(splitSolution.TotalVolume, Is.EqualTo(750)); + 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)); } [Test] public void SplitSolutionMoreThanTotalRemovesAll() { - var solution = new Solution("water", 800); + var solution = new Solution("water", ReagentUnit.New(800)); - var splitSolution = solution.SplitSolution(1000); + var splitSolution = solution.SplitSolution(ReagentUnit.New(1000)); - Assert.That(solution.GetReagentQuantity("water"), Is.EqualTo(0)); - Assert.That(solution.TotalVolume, Is.EqualTo(0)); + Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(0)); + Assert.That(solution.TotalVolume.Int(), Is.EqualTo(0)); - Assert.That(splitSolution.GetReagentQuantity("water"), Is.EqualTo(800)); - Assert.That(splitSolution.TotalVolume, Is.EqualTo(800)); + Assert.That(splitSolution.GetReagentQuantity("water").Int(), Is.EqualTo(800)); + Assert.That(splitSolution.TotalVolume.Int(), Is.EqualTo(800)); } [Test] public void SplitSolutionLessThanOneDoesNothing() { - var solution = new Solution("water", 800); + var solution = new Solution("water", ReagentUnit.New(800)); - var splitSolution = solution.SplitSolution(-200); + var splitSolution = solution.SplitSolution(ReagentUnit.New(-200)); - Assert.That(solution.GetReagentQuantity("water"), Is.EqualTo(800)); - Assert.That(solution.TotalVolume, Is.EqualTo(800)); + Assert.That(solution.GetReagentQuantity("water").Int(), Is.EqualTo(800)); + Assert.That(solution.TotalVolume.Int(), Is.EqualTo(800)); - Assert.That(splitSolution.GetReagentQuantity("water"), Is.EqualTo(0)); - Assert.That(splitSolution.TotalVolume, Is.EqualTo(0)); + Assert.That(splitSolution.GetReagentQuantity("water").Int(), Is.EqualTo(0)); + Assert.That(splitSolution.TotalVolume.Int(), Is.EqualTo(0)); } [Test] public void AddSolution() { var solutionOne = new Solution(); - solutionOne.AddReagent("water", 1000); - solutionOne.AddReagent("fire", 2000); + solutionOne.AddReagent("water", ReagentUnit.New(1000)); + solutionOne.AddReagent("fire", ReagentUnit.New(2000)); var solutionTwo = new Solution(); - solutionTwo.AddReagent("water", 500); - solutionTwo.AddReagent("earth", 1000); + solutionTwo.AddReagent("water", ReagentUnit.New(500)); + solutionTwo.AddReagent("earth", ReagentUnit.New(1000)); solutionOne.AddSolution(solutionTwo); - Assert.That(solutionOne.GetReagentQuantity("water"), Is.EqualTo(1500)); - Assert.That(solutionOne.GetReagentQuantity("fire"), Is.EqualTo(2000)); - Assert.That(solutionOne.GetReagentQuantity("earth"), Is.EqualTo(1000)); - Assert.That(solutionOne.TotalVolume, Is.EqualTo(4500)); + 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)); } } } diff --git a/RobustToolbox b/RobustToolbox index 0a306514a2..ec52102d02 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 0a306514a2c927d3e19f2f6fcec9eee64b488ce4 +Subproject commit ec52102d0279281a00cc1c6811330a13ddaf975b From f8b73fdc3bbbdf707985798f724be92821f7b4f8 Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Sun, 5 Apr 2020 11:45:23 +0200 Subject: [PATCH 07/18] Fix null-state handling for InjectorComponent --- .../GameObjects/Components/Chemistry/InjectorComponent.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs b/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs index 272a67d964..4d46ddf5f0 100644 --- a/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs +++ b/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs @@ -30,6 +30,10 @@ namespace Content.Client.GameObjects.Components.Chemistry //Handle net updates public override void HandleComponentState(ComponentState curState, ComponentState nextState) { + if(curState == null) + { + return; + } if(curState.GetType() == typeof(InjectorComponentState)) { var cast = (InjectorComponentState) curState; From e7d121b6b0fea3ea551fc64c2d382f0bff066d22 Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Wed, 8 Apr 2020 16:08:16 +0200 Subject: [PATCH 08/18] Update InjectorComponent.cs --- .../GameObjects/Components/Chemistry/InjectorComponent.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs b/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs index 4d46ddf5f0..359dbeb282 100644 --- a/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs +++ b/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs @@ -30,13 +30,9 @@ namespace Content.Client.GameObjects.Components.Chemistry //Handle net updates public override void HandleComponentState(ComponentState curState, ComponentState nextState) { - if(curState == null) + var cast = (InjectorComponentState) curState; + if (cast != null) { - return; - } - if(curState.GetType() == typeof(InjectorComponentState)) - { - var cast = (InjectorComponentState) curState; CurrentVolume = cast.CurrentVolume; TotalVolume = cast.TotalVolume; CurrentMode = cast.CurrentMode; From a484b6fd52b9c40391b1ad17396722e0fc902144 Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Wed, 8 Apr 2020 19:07:33 +0200 Subject: [PATCH 09/18] Implementation of ISelfSerialize --- .../Components/Chemistry/SolutionComponent.cs | 1 + Content.Shared/Chemistry/ReagentUnit.cs | 40 ++++++++++++++----- .../Shared/Chemistry/ReagentUnit_Tests.cs | 9 +++++ 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs index 0328dcae67..4cd0a7d1ed 100644 --- a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -199,6 +199,7 @@ namespace Content.Server.GameObjects.Components.Chemistry bool checkForNewReaction = false; while (true) { + //TODO: make a hashmap at startup and then look up reagents in the contents for a reaction //Check the solution for every reaction foreach (var reaction in _reactions) { diff --git a/Content.Shared/Chemistry/ReagentUnit.cs b/Content.Shared/Chemistry/ReagentUnit.cs index a9350c5b3c..184f7aa5d7 100644 --- a/Content.Shared/Chemistry/ReagentUnit.cs +++ b/Content.Shared/Chemistry/ReagentUnit.cs @@ -1,10 +1,12 @@ -using System; +using Robust.Shared.Interfaces.Serialization; +using Robust.Shared.Serialization; +using System; using System.Linq; namespace Content.Shared.Chemistry { [Serializable] - public struct ReagentUnit + public struct ReagentUnit : ISelfSerialize { private int _value; private static readonly int Shift = 2; @@ -16,11 +18,6 @@ namespace Content.Shared.Chemistry return _value / (decimal)Math.Pow(10, Shift); } - private decimal ShiftUp() - { - return _value * (decimal)Math.Pow(10, Shift); - } - private ReagentUnit(int value) { _value = value; @@ -38,7 +35,12 @@ namespace Content.Shared.Chemistry public static ReagentUnit New(float value) { - return new ReagentUnit((int) Math.Round(value * (float) Math.Pow(10, Shift))); + return new ReagentUnit(FromFloat(value)); + } + + private static int FromFloat(float value) + { + return (int) Math.Round(value * (float) Math.Pow(10, Shift)); } public static ReagentUnit New(double value) @@ -46,6 +48,16 @@ namespace Content.Shared.Chemistry return new ReagentUnit((int) Math.Round(value * Math.Pow(10, Shift))); } + public static ReagentUnit New(string value) + { + return New(FloatFromString(value)); + } + + private static float FloatFromString(string value) + { + return float.Parse(value); + } + public static ReagentUnit operator +(ReagentUnit a) => a; public static ReagentUnit operator -(ReagentUnit a) => new ReagentUnit(-a._value); @@ -135,7 +147,7 @@ namespace Content.Shared.Chemistry public decimal Decimal() { - return (decimal) ShiftDown(); + return ShiftDown(); } public int Int() @@ -158,5 +170,15 @@ namespace Content.Shared.Chemistry { return HashCode.Combine(_value); } + + public void Deserialize(string value) + { + _value = FromFloat(FloatFromString(value)); + } + + public string Serialize() + { + return ToString(); + } } } diff --git a/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs b/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs index f2f11b4fdb..1cae6e1db2 100644 --- a/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs +++ b/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs @@ -45,6 +45,15 @@ namespace Content.Tests.Shared.Chemistry Assert.AreEqual(expected, $"{result}"); } + [Test] + [TestCase("1.005", "1")] + [TestCase("0.999", "1")] + public void ReagentUnitStringTests(string value, string expected) + { + var result = ReagentUnit.New(value); + Assert.AreEqual(expected, $"{result}"); + } + [Test] [TestCase(1.001f, 1.001f, "2")] [TestCase(1.001f, 1.004f, "2")] From 919469408567c41fb26a7d2895578876e0130427 Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Wed, 8 Apr 2020 19:43:16 +0200 Subject: [PATCH 10/18] Fix initialization change --- .../GameObjects/Components/Chemistry/SolutionComponent.cs | 3 +-- .../GameObjects/Components/Chemistry/SolutionComponent.cs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs index 4cd0a7d1ed..aaf78b9723 100644 --- a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -36,9 +36,8 @@ namespace Content.Server.GameObjects.Components.Chemistry Init(); } - public override void Init() + public void Init() { - base.Init(); _reactions = _prototypeManager.EnumeratePrototypes(); _audioSystem = _entitySystemManager.GetEntitySystem(); } diff --git a/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs index 615c1d85b0..1b65016975 100644 --- a/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -17,7 +17,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry #pragma warning restore 649 [ViewVariables] - protected Solution ContainedSolution; + protected Solution ContainedSolution = new Solution(); private ReagentUnit _maxVolume; private SolutionCaps _capabilities; From 66c6a9e3a68996374231a5283e50f0ef88bcd3dc Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Wed, 8 Apr 2020 22:39:46 +0200 Subject: [PATCH 11/18] ah yes a much need whitespace --- Content.Shared/EntryPoint.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Content.Shared/EntryPoint.cs b/Content.Shared/EntryPoint.cs index a9df20ff2a..5401603602 100644 --- a/Content.Shared/EntryPoint.cs +++ b/Content.Shared/EntryPoint.cs @@ -24,6 +24,7 @@ { base.PostInit(); + _initTileDefinitions(); } From 282ee61e99c5dbf4a496a1b911f8b20feadf0154 Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Wed, 8 Apr 2020 22:39:59 +0200 Subject: [PATCH 12/18] not so needed afterall --- Content.Shared/EntryPoint.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Content.Shared/EntryPoint.cs b/Content.Shared/EntryPoint.cs index 5401603602..a9df20ff2a 100644 --- a/Content.Shared/EntryPoint.cs +++ b/Content.Shared/EntryPoint.cs @@ -24,7 +24,6 @@ { base.PostInit(); - _initTileDefinitions(); } From 92e25524401f0d6c159698e4ce23517c875a6e81 Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Wed, 8 Apr 2020 22:40:29 +0200 Subject: [PATCH 13/18] ree travis --- Content.Shared/EntryPoint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/EntryPoint.cs b/Content.Shared/EntryPoint.cs index a9df20ff2a..0ada486216 100644 --- a/Content.Shared/EntryPoint.cs +++ b/Content.Shared/EntryPoint.cs @@ -17,7 +17,7 @@ public override void Init() { - IoCManager.InjectDependencies(this); + IoCManager.InjectDepe ndencies(this); } public override void PostInit() From a507a06916e803f770fd6971e6c22022977c4d54 Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Wed, 8 Apr 2020 22:40:43 +0200 Subject: [PATCH 14/18] fix compile error --- Content.Shared/EntryPoint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/EntryPoint.cs b/Content.Shared/EntryPoint.cs index 0ada486216..a9df20ff2a 100644 --- a/Content.Shared/EntryPoint.cs +++ b/Content.Shared/EntryPoint.cs @@ -17,7 +17,7 @@ public override void Init() { - IoCManager.InjectDepe ndencies(this); + IoCManager.InjectDependencies(this); } public override void PostInit() From 4e67f09488aede6959c8c526d2dc9b820396db22 Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Thu, 9 Apr 2020 20:40:59 +0200 Subject: [PATCH 15/18] Fixed comments ReagentUnit now implements IComparable and IEquateable. ReagentUnit now uses double internally. Added multiplication without shifting for ints. InvariantCulture for some string things. Added units tests for Equals and CompareTo. Added unit tests to Solution that deals with nasty fractionals. --- Content.Shared/Chemistry/ReagentUnit.cs | 55 ++++++++++++++++--- .../Shared/Chemistry/ReagentUnit_Tests.cs | 32 +++++++++++ .../Shared/Chemistry/Solution_Tests.cs | 35 ++++++++++++ 3 files changed, 115 insertions(+), 7 deletions(-) diff --git a/Content.Shared/Chemistry/ReagentUnit.cs b/Content.Shared/Chemistry/ReagentUnit.cs index 184f7aa5d7..63385fe297 100644 --- a/Content.Shared/Chemistry/ReagentUnit.cs +++ b/Content.Shared/Chemistry/ReagentUnit.cs @@ -1,21 +1,23 @@ using Robust.Shared.Interfaces.Serialization; using Robust.Shared.Serialization; using System; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Linq; namespace Content.Shared.Chemistry { [Serializable] - public struct ReagentUnit : ISelfSerialize + public struct ReagentUnit : ISelfSerialize, IComparable, IEquatable { private int _value; private static readonly int Shift = 2; public static ReagentUnit MaxValue => new ReagentUnit(int.MaxValue); - private decimal ShiftDown() + private double ShiftDown() { - return _value / (decimal)Math.Pow(10, Shift); + return _value / Math.Pow(10, Shift); } private ReagentUnit(int value) @@ -55,7 +57,7 @@ namespace Content.Shared.Chemistry private static float FloatFromString(string value) { - return float.Parse(value); + return float.Parse(value, CultureInfo.InvariantCulture); } public static ReagentUnit operator +(ReagentUnit a) => a; @@ -82,11 +84,22 @@ namespace Content.Shared.Chemistry } public static ReagentUnit operator *(ReagentUnit a, decimal b) + { + var aD = (decimal) a.ShiftDown(); + return New(aD * b); + } + + public static ReagentUnit operator *(ReagentUnit a, double b) { var aD = a.ShiftDown(); return New(aD * b); } + public static ReagentUnit operator *(ReagentUnit a, int b) + { + return new ReagentUnit(a._value * b); + } + public static ReagentUnit operator /(ReagentUnit a, ReagentUnit b) { if (b._value == 0) @@ -138,14 +151,17 @@ namespace Content.Shared.Chemistry return a._value > b._value; } - public override string ToString() => $"{ShiftDown()}"; - public float Float() { return (float) ShiftDown(); } public decimal Decimal() + { + return (decimal) ShiftDown(); + } + + public double Double() { return ShiftDown(); } @@ -157,7 +173,12 @@ namespace Content.Shared.Chemistry public static ReagentUnit Min(params ReagentUnit[] reagentUnits) { - return reagentUnits.OrderBy(x => x._value).First(); + return reagentUnits.Min(); + } + + public static ReagentUnit Min(ReagentUnit a, ReagentUnit b) + { + return a < b ? a : b; } public override bool Equals(object obj) @@ -176,9 +197,29 @@ namespace Content.Shared.Chemistry _value = FromFloat(FloatFromString(value)); } + public override string ToString() => $"{ShiftDown().ToString(CultureInfo.InvariantCulture)}"; + public string Serialize() { return ToString(); } + + public bool Equals([AllowNull] ReagentUnit other) + { + return _value == other._value; + } + + public int CompareTo([AllowNull] ReagentUnit other) + { + if(other._value > _value) + { + return -1; + } + if(other._value < _value) + { + return 1; + } + return 0; + } } } diff --git a/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs b/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs index 1cae6e1db2..46d391459b 100644 --- a/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs +++ b/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs @@ -118,5 +118,37 @@ namespace Content.Tests.Shared.Chemistry var result = (int) Math.Round(a * (float) Math.Pow(10, 2)); Assert.AreEqual(expected, result); } + + [Test] + public void ReagentUnitMin() + { + var unorderedList = new[] + { + ReagentUnit.New(5), + ReagentUnit.New(3), + ReagentUnit.New(1), + ReagentUnit.New(2), + ReagentUnit.New(4), + }; + var min = ReagentUnit.Min(unorderedList); + Assert.AreEqual(ReagentUnit.New(1), min); + } + + [Test] + [TestCase(1, 0, false)] + [TestCase(0, 0, true)] + [TestCase(-1, 0, false)] + [TestCase(null, 0, true)] + [TestCase(1, 1, true)] + [TestCase(0, 1, false)] + [TestCase(-1, 1, false)] + [TestCase(null, 1, false)] + public void ReagentUnitEquals(int a, int b, bool expected) + { + var parameter = ReagentUnit.New(a); + var comparison = ReagentUnit.New(b); + Assert.AreEqual(comparison.Equals(parameter), parameter.Equals(comparison)); + Assert.AreEqual(expected, comparison.Equals(parameter)); + } } } diff --git a/Content.Tests/Shared/Chemistry/Solution_Tests.cs b/Content.Tests/Shared/Chemistry/Solution_Tests.cs index e3024d3a07..2714ce2252 100644 --- a/Content.Tests/Shared/Chemistry/Solution_Tests.cs +++ b/Content.Tests/Shared/Chemistry/Solution_Tests.cs @@ -203,6 +203,41 @@ namespace Content.Tests.Shared.Chemistry Assert.That(splitSolution.TotalVolume.Int(), Is.EqualTo(750)); } + [Test] + public void SplitSolutionFractional() + { + var solution = new Solution(); + solution.AddReagent("water", ReagentUnit.New(1)); + solution.AddReagent("fire", ReagentUnit.New(2)); + + var splitSolution = solution.SplitSolution(ReagentUnit.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(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)); + } + + [Test] + [TestCase(0.03f, 0.01f, 0.02f)] + [TestCase(0.03f, 0.02f, 0.01f)] + public void SplitSolutionTinyFractionalBigSmall(float initial, float reduce, float remainder) + { + var solution = new Solution(); + solution.AddReagent("water", ReagentUnit.New(initial)); + + var splitSolution = solution.SplitSolution(ReagentUnit.New(reduce)); + + Assert.That(solution.GetReagentQuantity("water").Float(), Is.EqualTo(remainder)); + Assert.That(solution.TotalVolume.Float(), Is.EqualTo(remainder)); + + Assert.That(splitSolution.GetReagentQuantity("water").Float(), Is.EqualTo(reduce)); + Assert.That(splitSolution.TotalVolume.Float(), Is.EqualTo(reduce)); + } + [Test] public void SplitSolutionMoreThanTotalRemovesAll() { From 4aa6c5cc69d52068ad7ec321fe5f36c23de93cc7 Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Thu, 9 Apr 2020 20:43:45 +0200 Subject: [PATCH 16/18] Extra unit test --- .../Shared/Chemistry/Solution_Tests.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Content.Tests/Shared/Chemistry/Solution_Tests.cs b/Content.Tests/Shared/Chemistry/Solution_Tests.cs index 2714ce2252..f149d66043 100644 --- a/Content.Tests/Shared/Chemistry/Solution_Tests.cs +++ b/Content.Tests/Shared/Chemistry/Solution_Tests.cs @@ -221,6 +221,24 @@ namespace Content.Tests.Shared.Chemistry Assert.That(splitSolution.TotalVolume.Int(), Is.EqualTo(1)); } + [Test] + public void SplitSolutionFractionalOpposite() + { + var solution = new Solution(); + solution.AddReagent("water", ReagentUnit.New(1)); + solution.AddReagent("fire", ReagentUnit.New(2)); + + var splitSolution = solution.SplitSolution(ReagentUnit.New(2)); + + Assert.That(solution.GetReagentQuantity("water").Float(), Is.EqualTo(0.33f)); + Assert.That(solution.GetReagentQuantity("fire").Float(), Is.EqualTo(0.67f)); + Assert.That(solution.TotalVolume.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)); + } + [Test] [TestCase(0.03f, 0.01f, 0.02f)] [TestCase(0.03f, 0.02f, 0.01f)] From c18f318b806ceaf4e2d2deff4796c96c47337cdf Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Thu, 9 Apr 2020 21:29:34 +0200 Subject: [PATCH 17/18] MidpointRounding --- Content.Shared/Chemistry/ReagentUnit.cs | 6 +++--- Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Content.Shared/Chemistry/ReagentUnit.cs b/Content.Shared/Chemistry/ReagentUnit.cs index 63385fe297..43c2354f03 100644 --- a/Content.Shared/Chemistry/ReagentUnit.cs +++ b/Content.Shared/Chemistry/ReagentUnit.cs @@ -32,7 +32,7 @@ namespace Content.Shared.Chemistry public static ReagentUnit New(decimal value) { - return new ReagentUnit((int) Math.Round(value * (decimal) Math.Pow(10, Shift))); + return new ReagentUnit((int) Math.Round(value * (decimal) Math.Pow(10, Shift), MidpointRounding.AwayFromZero)); } public static ReagentUnit New(float value) @@ -42,12 +42,12 @@ namespace Content.Shared.Chemistry private static int FromFloat(float value) { - return (int) Math.Round(value * (float) Math.Pow(10, Shift)); + return (int) Math.Round(value * (float) Math.Pow(10, Shift), MidpointRounding.AwayFromZero); } public static ReagentUnit New(double value) { - return new ReagentUnit((int) Math.Round(value * Math.Pow(10, Shift))); + return new ReagentUnit((int) Math.Round(value * Math.Pow(10, Shift), MidpointRounding.AwayFromZero)); } public static ReagentUnit New(string value) diff --git a/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs b/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs index 46d391459b..003ecf4663 100644 --- a/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs +++ b/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs @@ -46,7 +46,7 @@ namespace Content.Tests.Shared.Chemistry } [Test] - [TestCase("1.005", "1")] + [TestCase("1.005", "1.01")] [TestCase("0.999", "1")] public void ReagentUnitStringTests(string value, string expected) { @@ -57,6 +57,7 @@ namespace Content.Tests.Shared.Chemistry [Test] [TestCase(1.001f, 1.001f, "2")] [TestCase(1.001f, 1.004f, "2")] + [TestCase(1f, 1.005f, "2.01")] [TestCase(1f, 2.005f, "3.01")] public void CalculusPlus(float aFloat, float bFloat, string expected) { @@ -111,11 +112,11 @@ namespace Content.Tests.Shared.Chemistry [Test] [TestCase(0.995f, 100)] - [TestCase(1.005f, 100)] + [TestCase(1.005f, 101)] [TestCase(2.005f, 201)] public void FloatRoundingTest(float a, int expected) { - var result = (int) Math.Round(a * (float) Math.Pow(10, 2)); + var result = (int) Math.Round(a * (float) Math.Pow(10, 2), MidpointRounding.AwayFromZero); Assert.AreEqual(expected, result); } From d261a21b5772087c15aded0831fac78d7b7bc90b Mon Sep 17 00:00:00 2001 From: PrPleGoo Date: Sun, 12 Apr 2020 14:44:14 +0200 Subject: [PATCH 18/18] Fix up SolutionComponent --- .../Components/Chemistry/SolutionComponent.cs | 47 ++++++++----------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs index e6a9713029..7e56d748ab 100644 --- a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -1,15 +1,8 @@ using Content.Server.Chemistry; -using System; -using System.Collections.Generic; -using System.ComponentModel.Design; -using System.Linq; -using Content.Server.Chemistry; -using Content.Shared.GameObjects.Components.Chemistry; -using Content.Server.GameObjects.Components.Nutrition; using Content.Server.GameObjects.EntitySystems; using Content.Shared.Chemistry; using Content.Shared.GameObjects; -using Content.Shared.Interfaces.Chemistry; +using Content.Shared.GameObjects.Components.Chemistry; using Content.Shared.Utility; using Robust.Server.GameObjects; using Robust.Server.GameObjects.EntitySystems; @@ -21,9 +14,9 @@ using Robust.Shared.Maths; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Utility; -using System; -using System.Collections.Generic; using Robust.Shared.ViewVariables; +using System.Collections.Generic; +using System.Linq; namespace Content.Server.GameObjects.Components.Chemistry { @@ -46,7 +39,7 @@ namespace Content.Server.GameObjects.Components.Chemistry private SpriteComponent _spriteComponent; private Solution _containedSolution = new Solution(); - private int _maxVolume; + private ReagentUnit _maxVolume; private SolutionCaps _capabilities; private string _fillInitState; private int _fillInitSteps; @@ -58,7 +51,7 @@ namespace Content.Server.GameObjects.Components.Chemistry /// The maximum volume of the container. /// [ViewVariables(VVAccess.ReadWrite)] - public int MaxVolume + public ReagentUnit MaxVolume { get => _maxVolume; set => _maxVolume = value; // Note that the contents won't spill out if the capacity is reduced. @@ -68,13 +61,13 @@ namespace Content.Server.GameObjects.Components.Chemistry /// The total volume of all the of the reagents in the container. /// [ViewVariables] - public int CurrentVolume => _containedSolution.TotalVolume; + public ReagentUnit CurrentVolume => _containedSolution.TotalVolume; /// /// The volume without reagents remaining in the container. /// [ViewVariables] - public int EmptyVolume => MaxVolume - CurrentVolume; + public ReagentUnit EmptyVolume => MaxVolume - CurrentVolume; /// /// The current blended color of all the reagents in the container. @@ -123,7 +116,7 @@ namespace Content.Server.GameObjects.Components.Chemistry { base.ExposeData(serializer); - serializer.DataField(ref _maxVolume, "maxVol", 0); + serializer.DataField(ref _maxVolume, "maxVol", ReagentUnit.New(0)); serializer.DataField(ref _containedSolution, "contents", _containedSolution); serializer.DataField(ref _capabilities, "caps", SolutionCaps.None); serializer.DataField(ref _fillInitState, "fillingState", ""); @@ -158,7 +151,7 @@ namespace Content.Server.GameObjects.Components.Chemistry OnSolutionChanged(false); } - public bool TryRemoveReagent(string reagentId, int quantity) + public bool TryRemoveReagent(string reagentId, ReagentUnit quantity) { if (!ContainsReagent(reagentId, out var currentQuantity)) return false; @@ -172,7 +165,7 @@ namespace Content.Server.GameObjects.Components.Chemistry /// /// Quantity of this solution to remove /// Whether or not the solution was successfully removed - public bool TryRemoveSolution(int quantity) + public bool TryRemoveSolution(ReagentUnit quantity) { if (CurrentVolume == 0) return false; @@ -182,7 +175,7 @@ namespace Content.Server.GameObjects.Components.Chemistry return true; } - public Solution SplitSolution(int quantity) + public Solution SplitSolution(ReagentUnit quantity) { var solutionSplit = _containedSolution.SplitSolution(quantity); OnSolutionChanged(false); @@ -198,7 +191,7 @@ namespace Content.Server.GameObjects.Components.Chemistry } Color mixColor = default; - float runningTotalQuantity = 0; + var runningTotalQuantity = ReagentUnit.New(0); foreach (var reagent in _containedSolution) { @@ -209,7 +202,7 @@ namespace Content.Server.GameObjects.Components.Chemistry if (mixColor == default) mixColor = proto.SubstanceColor; mixColor = Color.InterpolateBetween(mixColor, proto.SubstanceColor, - (1 / runningTotalQuantity) * reagent.Quantity); + (1 / runningTotalQuantity.Float()) * reagent.Quantity.Float()); } SubstanceColor = mixColor; @@ -389,7 +382,7 @@ namespace Content.Server.GameObjects.Components.Chemistry public bool TryAddReagent(string reagentId, ReagentUnit quantity, out ReagentUnit acceptedQuantity, bool skipReactionCheck = false, bool skipColor = false) { - var toAcceptQuantity = MaxVolume - ContainedSolution.TotalVolume; + var toAcceptQuantity = MaxVolume - _containedSolution.TotalVolume; if (quantity > toAcceptQuantity) { acceptedQuantity = toAcceptQuantity; @@ -400,7 +393,7 @@ namespace Content.Server.GameObjects.Components.Chemistry acceptedQuantity = quantity; } - ContainedSolution.AddReagent(reagentId, acceptedQuantity); + _containedSolution.AddReagent(reagentId, acceptedQuantity); if (!skipColor) { RecalculateColor(); } @@ -412,10 +405,10 @@ namespace Content.Server.GameObjects.Components.Chemistry public bool TryAddSolution(Solution solution, bool skipReactionCheck = false, bool skipColor = false) { - if (solution.TotalVolume > (MaxVolume - ContainedSolution.TotalVolume)) + if (solution.TotalVolume > (MaxVolume - _containedSolution.TotalVolume)) return false; - ContainedSolution.AddSolution(solution); + _containedSolution.AddSolution(solution); if (!skipColor) { RecalculateColor(); } @@ -496,7 +489,7 @@ namespace Content.Server.GameObjects.Components.Chemistry /// The reagent to check for. /// Output the quantity of the reagent if it is contained, 0 if it isn't. /// Return true if the solution contains the reagent. - public bool ContainsReagent(string reagentId, out int quantity) + public bool ContainsReagent(string reagentId, out ReagentUnit quantity) { foreach (var reagent in _containedSolution.Contents) { @@ -506,7 +499,7 @@ namespace Content.Server.GameObjects.Components.Chemistry return true; } } - quantity = 0; + quantity = ReagentUnit.New(0); return false; } @@ -524,7 +517,7 @@ namespace Content.Server.GameObjects.Components.Chemistry { if (string.IsNullOrEmpty(_fillInitState)) return; - var percentage = (double)CurrentVolume / MaxVolume; + var percentage = (CurrentVolume / MaxVolume).Double(); var level = ContentHelpers.RoundToLevels(percentage * 100, 100, _fillInitSteps); //Transformed glass uses special fancy sprites so we don't bother