diff --git a/Content.Client/ClientContentIoC.cs b/Content.Client/ClientContentIoC.cs index 05dfbfa957..690de5c18a 100644 --- a/Content.Client/ClientContentIoC.cs +++ b/Content.Client/ClientContentIoC.cs @@ -8,7 +8,9 @@ using Content.Client.Sandbox; using Content.Client.UserInterface; using Content.Client.UserInterface.Stylesheets; using Content.Client.Utility; +using Content.Shared.Chemistry; using Content.Shared.Interfaces; +using Content.Shared.Interfaces.Chemistry; using Robust.Shared.IoC; namespace Content.Client diff --git a/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs b/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs index bc064e32e6..359dbeb282 100644 --- a/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs +++ b/Content.Client/GameObjects/Components/Chemistry/InjectorComponent.cs @@ -8,6 +8,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 { @@ -17,8 +18,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 ReagentUnit CurrentVolume { get; set; } + [ViewVariables] private ReagentUnit TotalVolume { get; set; } [ViewVariables] private InjectorToggleMode CurrentMode { get; set; } [ViewVariables(VVAccess.ReadWrite)] private bool _uiUpdateNeeded; @@ -29,7 +30,7 @@ namespace Content.Client.GameObjects.Components.Chemistry //Handle net updates public override void HandleComponentState(ComponentState curState, ComponentState nextState) { - var cast = (InjectorComponentState)curState; + var cast = (InjectorComponentState) curState; if (cast != null) { CurrentVolume = cast.CurrentVolume; diff --git a/Content.Client/GameObjects/Components/Chemistry/ReagentDispenserWindow.cs b/Content.Client/GameObjects/Components/Chemistry/ReagentDispenserWindow.cs index 8461d0ac8b..997b487560 100644 --- a/Content.Client/GameObjects/Components/Chemistry/ReagentDispenserWindow.cs +++ b/Content.Client/GameObjects/Components/Chemistry/ReagentDispenserWindow.cs @@ -171,7 +171,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 e43a1d7dd3..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 - int 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 824721acfd..d220bb1805 100644 --- a/Content.Server/Chemistry/Metabolism/DefaultFood.cs +++ b/Content.Server/Chemistry/Metabolism/DefaultFood.cs @@ -1,8 +1,9 @@ -using System; -using Content.Server.GameObjects.Components.Nutrition; +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; +using Robust.Shared.IoC; using Robust.Shared.Serialization; namespace Content.Server.Chemistry.Metabolism @@ -14,8 +15,8 @@ namespace Content.Server.Chemistry.Metabolism class DefaultFood : IMetabolizable { //Rate of metabolism in units / second - private int _metabolismRate; - public int MetabolismRate => _metabolismRate; + private ReagentUnit _metabolismRate; + public ReagentUnit MetabolismRate => _metabolismRate; //How much hunger is satiated when 1u of the reagent is metabolized private float _nutritionFactor; @@ -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(1M)); serializer.DataField(ref _nutritionFactor, "nutrimentFactor", 30.0f); } //Remove reagent at set rate, satiate hunger if a HungerComponent can be found - int 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 HungerComponent hunger)) - hunger.UpdateFood(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/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/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 b0126dbd10..1b33bd2583 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 int _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)); } protected override void Startup() { @@ -157,7 +157,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); + var realTransferAmount = ReagentUnit.Min(_transferAmount, targetBloodstream.EmptyVolume); if (realTransferAmount <= 0) { _notifyManager.PopupMessage(Owner.Transform.GridPosition, user, @@ -185,7 +185,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); + var realTransferAmount = ReagentUnit.Min(_transferAmount, targetSolution.EmptyVolume); if (realTransferAmount <= 0) { _notifyManager.PopupMessage(Owner.Transform.GridPosition, user, @@ -213,7 +213,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); + 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 b0f47233b0..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 int _transferAmount; + private ReagentUnit _transferAmount; /// /// The amount of solution to be transferred from this solution when clicking on other solutions with it. /// [ViewVariables] - public int 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); + 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 - int 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 ab39fcad69..829edbab1a 100644 --- a/Content.Server/GameObjects/Components/Chemistry/ReagentDispenserComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/ReagentDispenserComponent.cs @@ -42,7 +42,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 ReagentUnit _dispenseAmount = ReagentUnit.New(10); [ViewVariables] private SolutionComponent Solution => _beakerContainer.ContainedEntity.GetComponent(); @@ -122,22 +122,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) @@ -182,13 +182,13 @@ namespace Content.Server.GameObjects.Components.Chemistry var beaker = _beakerContainer.ContainedEntity; if (beaker == null) { - return new ReagentDispenserBoundUserInterfaceState(false, 0, 0, - "", Inventory, Owner.Name, null, DispenseAmount); + return new ReagentDispenserBoundUserInterfaceState(false, ReagentUnit.New(0), ReagentUnit.New(0), + "", 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() @@ -237,7 +237,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 81f8656a0e..7e56d748ab 100644 --- a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -1,14 +1,8 @@ -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.Chemistry; using Content.Server.GameObjects.EntitySystems; -using Content.Server.Interfaces; using Content.Shared.Chemistry; using Content.Shared.GameObjects; +using Content.Shared.GameObjects.Components.Chemistry; using Content.Shared.Utility; using Robust.Server.GameObjects; using Robust.Server.GameObjects.EntitySystems; @@ -21,6 +15,8 @@ using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Utility; using Robust.Shared.ViewVariables; +using System.Collections.Generic; +using System.Linq; namespace Content.Server.GameObjects.Components.Chemistry { @@ -43,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; @@ -55,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. @@ -65,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. @@ -120,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", ""); @@ -155,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; @@ -169,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; @@ -179,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); @@ -195,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) { @@ -206,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; @@ -263,8 +259,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) @@ -347,8 +342,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) @@ -364,10 +358,11 @@ 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) { - if (SolutionValidReaction(reaction, out int unitReactions)) + if (SolutionValidReaction(reaction, out var unitReactions)) { PerformReaction(reaction, unitReactions); checkForNewReaction = true; @@ -385,11 +380,12 @@ 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, ReagentUnit quantity, out ReagentUnit acceptedQuantity, bool skipReactionCheck = false, bool skipColor = false) { - if (quantity > _maxVolume - _containedSolution.TotalVolume) + var toAcceptQuantity = MaxVolume - _containedSolution.TotalVolume; + if (quantity > toAcceptQuantity) { - acceptedQuantity = _maxVolume - _containedSolution.TotalVolume; + acceptedQuantity = toAcceptQuantity; if (acceptedQuantity == 0) return false; } else @@ -398,6 +394,9 @@ namespace Content.Server.GameObjects.Components.Chemistry } _containedSolution.AddReagent(reagentId, acceptedQuantity); + if (!skipColor) { + RecalculateColor(); + } if(!skipReactionCheck) CheckForReaction(); OnSolutionChanged(skipColor); @@ -406,10 +405,13 @@ 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); + if (!skipColor) { + RecalculateColor(); + } if(!skipReactionCheck) CheckForReaction(); OnSolutionChanged(skipColor); @@ -423,16 +425,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 ReagentUnit unitReactions) { - unitReactions = int.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 int reagentQuantity)) + if (!ContainsReagent(reactant.Key, out ReagentUnit reagentQuantity)) { return false; } - int currentUnitReactions = reagentQuantity / reactant.Value.Amount; + var currentUnitReactions = reagentQuantity / reactant.Value.Amount; if (currentUnitReactions < unitReactions) { unitReactions = currentUnitReactions; @@ -455,26 +457,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, int unitReactions) + private void PerformReaction(ReactionPrototype reaction, ReagentUnit 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; 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, 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 @@ -487,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) { @@ -497,7 +499,7 @@ namespace Content.Server.GameObjects.Components.Chemistry return true; } } - quantity = 0; + quantity = ReagentUnit.New(0); return false; } @@ -515,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 diff --git a/Content.Server/GameObjects/Components/Metabolism/BloodstreamComponent.cs b/Content.Server/GameObjects/Components/Metabolism/BloodstreamComponent.cs index 9ce1dffacb..e428c72f17 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 int 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)); } protected override void Startup() @@ -93,7 +93,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 14356f8cea..3a3886f52b 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 ReagentUnit TransferAmount => _transferAmount; [ViewVariables] - private int _transferAmount = 2; + private ReagentUnit _transferAmount = ReagentUnit.New(2); - public int MaxVolume + public ReagentUnit MaxVolume { get => _contents.MaxVolume; set => _contents.MaxVolume = value; @@ -53,7 +54,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).Float())); } @@ -114,7 +115,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 df990b392c..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, _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 d8eb1a899c..db6e16831e 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 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); } @@ -134,10 +134,10 @@ namespace Content.Server.GameObjects.Components.Nutrition private class ReagentDelta { public readonly string ReagentId; - public readonly int Quantity; + public readonly ReagentUnit Quantity; public float Lifetime { get; private set; } - public ReagentDelta(string reagentId, int quantity) + public ReagentDelta(string reagentId, ReagentUnit 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.Server/ServerContentIoC.cs b/Content.Server/ServerContentIoC.cs index ec9ff2a876..f4bb04b9cc 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 diff --git a/Content.Shared/Chemistry/DefaultMetabolizable.cs b/Content.Shared/Chemistry/DefaultMetabolizable.cs index ab5816d9f0..05686ccf07 100644 --- a/Content.Shared/Chemistry/DefaultMetabolizable.cs +++ b/Content.Shared/Chemistry/DefaultMetabolizable.cs @@ -1,7 +1,7 @@ -using System; -using Content.Shared.Interfaces.Chemistry; +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,18 +10,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) + ReagentUnit IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime) { - int metabolismAmount = (int)Math.Round(MetabolismRate * 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..43c2354f03 --- /dev/null +++ b/Content.Shared/Chemistry/ReagentUnit.cs @@ -0,0 +1,225 @@ +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, IComparable, IEquatable + { + private int _value; + private static readonly int Shift = 2; + + public static ReagentUnit MaxValue => new ReagentUnit(int.MaxValue); + + private double ShiftDown() + { + return _value / 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), MidpointRounding.AwayFromZero)); + } + + public static ReagentUnit New(float value) + { + return new ReagentUnit(FromFloat(value)); + } + + private static int FromFloat(float value) + { + 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), MidpointRounding.AwayFromZero)); + } + + public static ReagentUnit New(string value) + { + return New(FloatFromString(value)); + } + + private static float FloatFromString(string value) + { + return float.Parse(value, CultureInfo.InvariantCulture); + } + + 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 = (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) + { + 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 float Float() + { + return (float) ShiftDown(); + } + + public decimal Decimal() + { + return (decimal) ShiftDown(); + } + + public double Double() + { + return ShiftDown(); + } + + public int Int() + { + return (int) ShiftDown(); + } + + public static ReagentUnit Min(params ReagentUnit[] reagentUnits) + { + return reagentUnits.Min(); + } + + public static ReagentUnit Min(ReagentUnit a, ReagentUnit b) + { + return a < b ? a : b; + } + + public override bool Equals(object obj) + { + return obj is ReagentUnit unit && + _value == unit._value; + } + + public override int GetHashCode() + { + return HashCode.Combine(_value); + } + + public void Deserialize(string value) + { + _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.Shared/Chemistry/Solution.cs b/Content.Shared/Chemistry/Solution.cs index bbb9937249..13b15d94b4 100644 --- a/Content.Shared/Chemistry/Solution.cs +++ b/Content.Shared/Chemistry/Solution.cs @@ -1,11 +1,13 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; +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 { @@ -23,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 int TotalVolume { get; private set; } + public ReagentUnit TotalVolume { get; private set; } /// /// Constructs an empty solution (ex. an empty beaker). @@ -35,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); } @@ -47,7 +49,7 @@ namespace Content.Shared.Chemistry if (serializer.Reading) { - TotalVolume = 0; + TotalVolume = ReagentUnit.New(0); foreach (var reagent in _contents) { TotalVolume += reagent.Quantity; @@ -60,9 +62,9 @@ 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, ReagentUnit quantity) { - if(quantity <= 0) + 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 ReagentUnit GetReagentQuantity(string reagentId) { for (var i = 0; i < _contents.Count; i++) { @@ -93,10 +95,10 @@ namespace Content.Shared.Chemistry return _contents[i].Quantity; } - return 0; + return ReagentUnit.New(0); } - public void RemoveReagent(string reagentId, int quantity) + public void RemoveReagent(string reagentId, ReagentUnit quantity) { if(quantity <= 0) return; @@ -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(ReagentUnit quantity) { if(quantity <= 0) return; - var ratio = (float)(TotalVolume - quantity) / TotalVolume; + var ratio = (TotalVolume - quantity).Decimal() / TotalVolume.Decimal(); if (ratio <= 0) { @@ -149,21 +151,21 @@ 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; _contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity); } - TotalVolume = (int)Math.Floor(TotalVolume * ratio); + TotalVolume = TotalVolume * ratio; } public void RemoveAllSolution() { _contents.Clear(); - TotalVolume = 0; + TotalVolume = ReagentUnit.New(0); } - public Solution SplitSolution(int quantity) + public Solution SplitSolution(ReagentUnit quantity) { if (quantity <= 0) return new Solution(); @@ -178,14 +180,14 @@ namespace Content.Shared.Chemistry } newSolution = new Solution(); - var newTotalVolume = 0; - var ratio = (float)(TotalVolume - quantity) / TotalVolume; + 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); @@ -193,7 +195,7 @@ namespace Content.Shared.Chemistry newTotalVolume += splitQuantity; } - TotalVolume = (int)Math.Floor(TotalVolume * ratio); + TotalVolume = TotalVolume * ratio; newSolution.TotalVolume = newTotalVolume; return newSolution; @@ -228,7 +230,7 @@ namespace Content.Shared.Chemistry public Solution Clone() { - var volume = 0; + var volume = ReagentUnit.New(0); 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 ReagentUnit Quantity; - public ReagentQuantity(string reagentId, int 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 ebbb4b11e4..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 int CurrentVolume { get; } - public int TotalVolume { get; } + public ReagentUnit CurrentVolume { get; } + public ReagentUnit TotalVolume { get; } public InjectorToggleMode CurrentMode { get; } - public InjectorComponentState(int currentVolume, int 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 0066dc3d50..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 int BeakerCurrentVolume; - public readonly int 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 int SelectedDispenseAmount; + public readonly ReagentUnit SelectedDispenseAmount; - public ReagentDispenserBoundUserInterfaceState(bool hasBeaker, int beakerCurrentVolume, int beakerMaxVolume, string containerName, - List inventory, string dispenserName, List containerReagents, int 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/Interfaces/Chemistry/IMetabolizable.cs b/Content.Shared/Interfaces/Chemistry/IMetabolizable.cs index 4b03ef51e2..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. - int Metabolize(IEntity solutionEntity, string reagentId, float tickTime); + ReagentUnit Metabolize(IEntity solutionEntity, string reagentId, float tickTime); } } diff --git a/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs b/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs new file mode 100644 index 0000000000..003ecf4663 --- /dev/null +++ b/Content.Tests/Shared/Chemistry/ReagentUnit_Tests.cs @@ -0,0 +1,155 @@ +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.005", "1.01")] + [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")] + [TestCase(1f, 1.005f, "2.01")] + [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, 101)] + [TestCase(2.005f, 201)] + public void FloatRoundingTest(float a, int expected) + { + var result = (int) Math.Round(a * (float) Math.Pow(10, 2), MidpointRounding.AwayFromZero); + 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 cada149224..f149d66043 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,276 @@ 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 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] + 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)] + 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() { - 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/Resources/Prototypes/Entities/items/chemistry.yml b/Resources/Prototypes/Entities/items/chemistry.yml index be043ad008..b13c4bae14 100644 --- a/Resources/Prototypes/Entities/items/chemistry.yml +++ b/Resources/Prototypes/Entities/items/chemistry.yml @@ -13,7 +13,7 @@ maxVol: 50 caps: 27 - type: Pourable - transferAmount: 5 + transferAmount: 5.0 - type: entity name: Large Beaker @@ -30,7 +30,7 @@ maxVol: 100 caps: 27 - type: Pourable - transferAmount: 5 + transferAmount: 5.0 - type: entity name: Dropper @@ -48,7 +48,7 @@ maxVol: 5 caps: 19 - type: Pourable - transferAmount: 5 + transferAmount: 5.0 - type: entity name: Syringe