Adds temperature to solutions (#5834)
This commit is contained in:
@@ -7,7 +7,14 @@
|
|||||||
<Label Text ="{Loc 'admin-solutions-window-solution-label'}" Margin="0 0 10 0"/>
|
<Label Text ="{Loc 'admin-solutions-window-solution-label'}" Margin="0 0 10 0"/>
|
||||||
<OptionButton Name="SolutionOption" HorizontalExpand="True"/>
|
<OptionButton Name="SolutionOption" HorizontalExpand="True"/>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<Label Name="TotalLabel" HorizontalExpand="True" Margin="0 4"/>
|
|
||||||
|
<!-- The total volume / capacity of the solution -->
|
||||||
|
<BoxContainer Name="VolumeBox" Orientation="Vertical" HorizontalExpand="True" Margin="0 4"/>
|
||||||
|
|
||||||
|
<!-- The temperature / heat capacity / thermal energy of the solution -->
|
||||||
|
<BoxContainer Name="ThermalBox" Orientation="Vertical" HorizontalExpand="True" Margin="0 4"/>
|
||||||
|
|
||||||
|
<!-- The reagents in the solution -->
|
||||||
<ScrollContainer HorizontalExpand="True" VerticalExpand="True" Margin="0 4">
|
<ScrollContainer HorizontalExpand="True" VerticalExpand="True" Margin="0 4">
|
||||||
<BoxContainer Name="ReagentList" Orientation="Vertical"/>
|
<BoxContainer Name="ReagentList" Orientation="Vertical"/>
|
||||||
</ScrollContainer>
|
</ScrollContainer>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using Robust.Client.UserInterface.XAML;
|
|||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Localization;
|
using Robust.Shared.Localization;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
|
||||||
namespace Content.Client.Administration.UI.ManageSolutions
|
namespace Content.Client.Administration.UI.ManageSolutions
|
||||||
{
|
{
|
||||||
@@ -66,9 +67,8 @@ namespace Content.Client.Administration.UI.ManageSolutions
|
|||||||
if (!_solutions.TryGetValue(_selectedSolution, out var solution))
|
if (!_solutions.TryGetValue(_selectedSolution, out var solution))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
TotalLabel.Text = Loc.GetString("admin-solutions-window-capacity-label",
|
UpdateVolumeBox(solution);
|
||||||
("currentVolume", solution.TotalVolume),
|
UpdateThermalBox(solution);
|
||||||
("maxVolume",solution.MaxVolume));
|
|
||||||
|
|
||||||
foreach (var reagent in solution)
|
foreach (var reagent in solution)
|
||||||
{
|
{
|
||||||
@@ -76,6 +76,109 @@ namespace Content.Client.Administration.UI.ManageSolutions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the entry displaying the current and maximum volume of the selected solution.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="solution">The selected solution.</param>
|
||||||
|
private void UpdateVolumeBox(Solution solution)
|
||||||
|
{
|
||||||
|
VolumeBox.DisposeAllChildren();
|
||||||
|
|
||||||
|
var volumeLabel = new Label();
|
||||||
|
volumeLabel.HorizontalExpand = true;
|
||||||
|
volumeLabel.Margin = new Thickness(0, 4);
|
||||||
|
volumeLabel.Text = Loc.GetString("admin-solutions-window-volume-label",
|
||||||
|
("currentVolume", solution.CurrentVolume),
|
||||||
|
("maxVolume", solution.MaxVolume));
|
||||||
|
|
||||||
|
var capacityBox = new BoxContainer();
|
||||||
|
capacityBox.Orientation = BoxContainer.LayoutOrientation.Horizontal;
|
||||||
|
capacityBox.HorizontalExpand = true;
|
||||||
|
capacityBox.Margin = new Thickness(0, 4);
|
||||||
|
|
||||||
|
var capacityLabel = new Label();
|
||||||
|
capacityLabel.HorizontalExpand = true;
|
||||||
|
capacityLabel.Margin = new Thickness(0, 1);
|
||||||
|
capacityLabel.Text = Loc.GetString("admin-solutions-window-capacity-label");
|
||||||
|
|
||||||
|
var capacitySpin = new FloatSpinBox(1, 2);
|
||||||
|
capacitySpin.HorizontalExpand = true;
|
||||||
|
capacitySpin.Margin = new Thickness(0, 1);
|
||||||
|
capacitySpin.Value = (float) solution.MaxVolume;
|
||||||
|
capacitySpin.OnValueChanged += SetCapacity;
|
||||||
|
|
||||||
|
capacityBox.AddChild(capacityLabel);
|
||||||
|
capacityBox.AddChild(capacitySpin);
|
||||||
|
|
||||||
|
VolumeBox.AddChild(volumeLabel);
|
||||||
|
VolumeBox.AddChild(capacityBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the entry displaying the current specific heat, heat capacity, temperature, and thermal energy
|
||||||
|
/// of the selected solution.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="solution">The selected solution.</param>
|
||||||
|
private void UpdateThermalBox(Solution solution)
|
||||||
|
{
|
||||||
|
ThermalBox.DisposeAllChildren();
|
||||||
|
|
||||||
|
var specificHeatLabel = new Label();
|
||||||
|
specificHeatLabel.HorizontalExpand = true;
|
||||||
|
specificHeatLabel.Margin = new Thickness(0, 1);
|
||||||
|
specificHeatLabel.Text = Loc.GetString("admin-solutions-window-specific-heat-label", ("specificHeat", solution.SpecificHeat));
|
||||||
|
|
||||||
|
var heatCapacityLabel = new Label();
|
||||||
|
heatCapacityLabel.HorizontalExpand = true;
|
||||||
|
heatCapacityLabel.Margin = new Thickness(0, 1);
|
||||||
|
heatCapacityLabel.Text = Loc.GetString("admin-solutions-window-heat-capacity-label", ("heatCapacity", solution.HeatCapacity));
|
||||||
|
|
||||||
|
// Temperature entry:
|
||||||
|
var temperatureBox = new BoxContainer();
|
||||||
|
temperatureBox.Orientation = BoxContainer.LayoutOrientation.Horizontal;
|
||||||
|
temperatureBox.HorizontalExpand = true;
|
||||||
|
temperatureBox.Margin = new Thickness(0, 1);
|
||||||
|
|
||||||
|
var temperatureLabel = new Label();
|
||||||
|
temperatureLabel.HorizontalExpand = true;
|
||||||
|
temperatureLabel.Margin = new Thickness(0, 1);
|
||||||
|
temperatureLabel.Text = Loc.GetString("admin-solutions-window-temperature-label");
|
||||||
|
|
||||||
|
var temperatureSpin = new FloatSpinBox(1, 2);
|
||||||
|
temperatureSpin.HorizontalExpand = true;
|
||||||
|
temperatureSpin.Margin = new Thickness(0, 1);
|
||||||
|
temperatureSpin.Value = solution.Temperature;
|
||||||
|
temperatureSpin.OnValueChanged += SetTemperature;
|
||||||
|
|
||||||
|
temperatureBox.AddChild(temperatureLabel);
|
||||||
|
temperatureBox.AddChild(temperatureSpin);
|
||||||
|
|
||||||
|
// Thermal energy entry:
|
||||||
|
var thermalEnergyBox = new BoxContainer();
|
||||||
|
thermalEnergyBox.Orientation = BoxContainer.LayoutOrientation.Horizontal;
|
||||||
|
thermalEnergyBox.HorizontalExpand = true;
|
||||||
|
thermalEnergyBox.Margin = new Thickness(0, 1);
|
||||||
|
|
||||||
|
var thermalEnergyLabel = new Label();
|
||||||
|
thermalEnergyLabel.HorizontalExpand = true;
|
||||||
|
thermalEnergyLabel.Margin = new Thickness(0, 1);
|
||||||
|
thermalEnergyLabel.Text = Loc.GetString("admin-solutions-window-thermal-energy-label");
|
||||||
|
|
||||||
|
var thermalEnergySpin = new FloatSpinBox(1, 2);
|
||||||
|
thermalEnergySpin.HorizontalExpand = true;
|
||||||
|
thermalEnergySpin.Margin = new Thickness(0, 1);
|
||||||
|
thermalEnergySpin.Value = solution.ThermalEnergy;
|
||||||
|
thermalEnergySpin.OnValueChanged += SetThermalEnergy;
|
||||||
|
|
||||||
|
thermalEnergyBox.AddChild(thermalEnergyLabel);
|
||||||
|
thermalEnergyBox.AddChild(thermalEnergySpin);
|
||||||
|
|
||||||
|
ThermalBox.AddChild(specificHeatLabel);
|
||||||
|
ThermalBox.AddChild(heatCapacityLabel);
|
||||||
|
ThermalBox.AddChild(temperatureBox);
|
||||||
|
ThermalBox.AddChild(thermalEnergyBox);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add a single reagent entry to the list
|
/// Add a single reagent entry to the list
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -112,6 +215,41 @@ namespace Content.Client.Administration.UI.ManageSolutions
|
|||||||
_consoleHost.ExecuteCommand(command);
|
_consoleHost.ExecuteCommand(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SetCapacity(FloatSpinBox.FloatSpinBoxEventArgs args)
|
||||||
|
{
|
||||||
|
if (_solutions == null || _selectedSolution == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var command = $"setsolutioncapacity {_target} {_selectedSolution} {args.Value}";
|
||||||
|
_consoleHost.ExecuteCommand(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the temperature of the selected solution to a value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="args">An argument struct containing the value to set the temperature to.</param>
|
||||||
|
private void SetTemperature(FloatSpinBox.FloatSpinBoxEventArgs args)
|
||||||
|
{
|
||||||
|
if (_solutions == null || _selectedSolution == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var command = $"setsolutiontemperature {_target} {_selectedSolution} {args.Value}";
|
||||||
|
_consoleHost.ExecuteCommand(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the thermal energy of the selected solution to a value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="args">An argument struct containing the value to set the thermal energy to.</param>
|
||||||
|
private void SetThermalEnergy(FloatSpinBox.FloatSpinBoxEventArgs args)
|
||||||
|
{
|
||||||
|
if (_solutions == null || _selectedSolution == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var command = $"setsolutionthermalenergy {_target} {_selectedSolution} {args.Value}";
|
||||||
|
_consoleHost.ExecuteCommand(command);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Open a new window that has options to add new reagents to the solution.
|
/// Open a new window that has options to add new reagents to the solution.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
using Content.Server.Chemistry.Components.SolutionManager;
|
||||||
|
using Content.Server.Chemistry.EntitySystems;
|
||||||
|
using Content.Shared.Administration;
|
||||||
|
using Content.Shared.FixedPoint;
|
||||||
|
using Robust.Shared.Console;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
|
||||||
|
namespace Content.Server.Administration.Commands
|
||||||
|
{
|
||||||
|
[AdminCommand(AdminFlags.Fun)]
|
||||||
|
public class SetSolutionCapacity : IConsoleCommand
|
||||||
|
{
|
||||||
|
public string Command => "setsolutioncapacity";
|
||||||
|
public string Description => "Set the capacity (maximum volume) of some solution.";
|
||||||
|
public string Help => $"Usage: {Command} <target> <solution> <new capacity>";
|
||||||
|
|
||||||
|
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length < 3)
|
||||||
|
{
|
||||||
|
shell.WriteLine($"Not enough arguments.\n{Help}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!EntityUid.TryParse(args[0], out var uid))
|
||||||
|
{
|
||||||
|
shell.WriteLine($"Invalid entity id.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IoCManager.Resolve<IEntityManager>().TryGetComponent(uid, out SolutionContainerManagerComponent man))
|
||||||
|
{
|
||||||
|
shell.WriteLine($"Entity does not have any solutions.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!man.Solutions.ContainsKey(args[1]))
|
||||||
|
{
|
||||||
|
var validSolutions = string.Join(", ", man.Solutions.Keys);
|
||||||
|
shell.WriteLine($"Entity does not have a \"{args[1]}\" solution. Valid solutions are:\n{validSolutions}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var solution = man.Solutions[args[1]];
|
||||||
|
|
||||||
|
if (!float.TryParse(args[2], out var quantityFloat))
|
||||||
|
{
|
||||||
|
shell.WriteLine($"Failed to parse new capacity.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(quantityFloat < 0.0f)
|
||||||
|
{
|
||||||
|
shell.WriteLine($"Cannot set the maximum volume of a solution to a negative number.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var quantity = FixedPoint2.New(quantityFloat);
|
||||||
|
EntitySystem.Get<SolutionContainerSystem>().SetCapacity(uid, solution, quantity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
using Content.Server.Chemistry.Components.SolutionManager;
|
||||||
|
using Content.Server.Chemistry.EntitySystems;
|
||||||
|
using Content.Shared.Administration;
|
||||||
|
using Robust.Shared.Console;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
|
||||||
|
namespace Content.Server.Administration.Commands
|
||||||
|
{
|
||||||
|
[AdminCommand(AdminFlags.Fun)]
|
||||||
|
public class SetSolutionTemperature : IConsoleCommand
|
||||||
|
{
|
||||||
|
public string Command => "setsolutiontemperature";
|
||||||
|
public string Description => "Set the temperature of some solution.";
|
||||||
|
public string Help => $"Usage: {Command} <target> <solution> <new temperature>";
|
||||||
|
|
||||||
|
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length < 3)
|
||||||
|
{
|
||||||
|
shell.WriteLine($"Not enough arguments.\n{Help}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!EntityUid.TryParse(args[0], out var uid))
|
||||||
|
{
|
||||||
|
shell.WriteLine($"Invalid entity id.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IoCManager.Resolve<IEntityManager>().TryGetComponent(uid, out SolutionContainerManagerComponent man))
|
||||||
|
{
|
||||||
|
shell.WriteLine($"Entity does not have any solutions.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!man.Solutions.ContainsKey(args[1]))
|
||||||
|
{
|
||||||
|
var validSolutions = string.Join(", ", man.Solutions.Keys);
|
||||||
|
shell.WriteLine($"Entity does not have a \"{args[1]}\" solution. Valid solutions are:\n{validSolutions}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var solution = man.Solutions[args[1]];
|
||||||
|
|
||||||
|
if (!float.TryParse(args[2], out var quantity))
|
||||||
|
{
|
||||||
|
shell.WriteLine($"Failed to parse new temperature.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (quantity <= 0.0f)
|
||||||
|
{
|
||||||
|
shell.WriteLine($"Cannot set the temperature of a solution to a non-positive number.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EntitySystem.Get<SolutionContainerSystem>().SetTemperature(uid, solution, quantity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
using Content.Server.Chemistry.Components.SolutionManager;
|
||||||
|
using Content.Server.Chemistry.EntitySystems;
|
||||||
|
using Content.Shared.Administration;
|
||||||
|
using Robust.Shared.Console;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
|
||||||
|
namespace Content.Server.Administration.Commands
|
||||||
|
{
|
||||||
|
[AdminCommand(AdminFlags.Fun)]
|
||||||
|
public class SetSolutionThermalEnergy : IConsoleCommand
|
||||||
|
{
|
||||||
|
public string Command => "setsolutionthermalenergy";
|
||||||
|
public string Description => "Set the thermal energy of some solution.";
|
||||||
|
public string Help => $"Usage: {Command} <target> <solution> <new thermal energy>";
|
||||||
|
|
||||||
|
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length < 3)
|
||||||
|
{
|
||||||
|
shell.WriteLine($"Not enough arguments.\n{Help}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!EntityUid.TryParse(args[0], out var uid))
|
||||||
|
{
|
||||||
|
shell.WriteLine($"Invalid entity id.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IoCManager.Resolve<IEntityManager>().TryGetComponent(uid, out SolutionContainerManagerComponent man))
|
||||||
|
{
|
||||||
|
shell.WriteLine($"Entity does not have any solutions.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!man.Solutions.ContainsKey(args[1]))
|
||||||
|
{
|
||||||
|
var validSolutions = string.Join(", ", man.Solutions.Keys);
|
||||||
|
shell.WriteLine($"Entity does not have a \"{args[1]}\" solution. Valid solutions are:\n{validSolutions}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var solution = man.Solutions[args[1]];
|
||||||
|
|
||||||
|
if (!float.TryParse(args[2], out var quantity))
|
||||||
|
{
|
||||||
|
shell.WriteLine($"Failed to parse new thermal energy.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (solution.HeatCapacity <= 0.0f)
|
||||||
|
{
|
||||||
|
if(quantity != 0.0f)
|
||||||
|
{
|
||||||
|
shell.WriteLine($"Cannot set the thermal energy of a solution with 0 heat capacity to a non-zero number.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if(quantity <= 0.0f)
|
||||||
|
{
|
||||||
|
shell.WriteLine($"Cannot set the thermal energy of a solution with heat capacity to a non-positive number.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EntitySystem.Get<SolutionContainerSystem>().SetThermalEnergy(uid, solution, quantity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -153,6 +153,24 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the capacity (maximum volume) of a solution to a new value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="targetUid">The entity containing the solution.</param>
|
||||||
|
/// <param name="targetSolution">The solution to set the capacity of.</param>
|
||||||
|
/// <param name="capacity">The value to set the capacity of the solution to.</param>
|
||||||
|
public void SetCapacity(EntityUid targetUid, Solution targetSolution, FixedPoint2 capacity)
|
||||||
|
{
|
||||||
|
if (targetSolution.MaxVolume == capacity)
|
||||||
|
return;
|
||||||
|
|
||||||
|
targetSolution.MaxVolume = capacity;
|
||||||
|
if (capacity < targetSolution.CurrentVolume)
|
||||||
|
targetSolution.RemoveSolution(targetSolution.CurrentVolume - capacity);
|
||||||
|
|
||||||
|
UpdateChemicals(targetUid, targetSolution);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds reagent of an Id to the container.
|
/// Adds reagent of an Id to the container.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -163,10 +181,10 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||||||
/// <param name="acceptedQuantity">The amount of reagent successfully added.</param>
|
/// <param name="acceptedQuantity">The amount of reagent successfully added.</param>
|
||||||
/// <returns>If all the reagent could be added.</returns>
|
/// <returns>If all the reagent could be added.</returns>
|
||||||
public bool TryAddReagent(EntityUid targetUid, Solution targetSolution, string reagentId, FixedPoint2 quantity,
|
public bool TryAddReagent(EntityUid targetUid, Solution targetSolution, string reagentId, FixedPoint2 quantity,
|
||||||
out FixedPoint2 acceptedQuantity)
|
out FixedPoint2 acceptedQuantity, float? temperature = null)
|
||||||
{
|
{
|
||||||
acceptedQuantity = targetSolution.AvailableVolume > quantity ? quantity : targetSolution.AvailableVolume;
|
acceptedQuantity = targetSolution.AvailableVolume > quantity ? quantity : targetSolution.AvailableVolume;
|
||||||
targetSolution.AddReagent(reagentId, acceptedQuantity);
|
targetSolution.AddReagent(reagentId, acceptedQuantity, temperature);
|
||||||
|
|
||||||
if (acceptedQuantity > 0)
|
if (acceptedQuantity > 0)
|
||||||
UpdateChemicals(targetUid, targetSolution, true);
|
UpdateChemicals(targetUid, targetSolution, true);
|
||||||
@@ -257,6 +275,8 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||||||
{
|
{
|
||||||
var (reagentId, curQuantity) = solution.Contents[i];
|
var (reagentId, curQuantity) = solution.Contents[i];
|
||||||
removedReagent[pos++] = reagentId;
|
removedReagent[pos++] = reagentId;
|
||||||
|
if (!_prototypeManager.TryIndex(reagentId, out ReagentPrototype? proto))
|
||||||
|
proto = new ReagentPrototype();
|
||||||
|
|
||||||
var newQuantity = curQuantity - quantity;
|
var newQuantity = curQuantity - quantity;
|
||||||
if (newQuantity <= 0)
|
if (newQuantity <= 0)
|
||||||
@@ -302,5 +322,56 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||||||
|
|
||||||
return reagentQuantity;
|
return reagentQuantity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Thermal energy and temperature management.
|
||||||
|
#region Thermal Energy and Temperature
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the temperature of a solution to a new value and then checks for reaction processing.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="owner">The entity in which the solution is located.</param>
|
||||||
|
/// <param name="solution">The solution to set the temperature of.</param>
|
||||||
|
/// <param name="temperature">The new value to set the temperature to.</param>
|
||||||
|
public void SetTemperature(EntityUid owner, Solution solution, float temperature)
|
||||||
|
{
|
||||||
|
if (temperature == solution.Temperature)
|
||||||
|
return;
|
||||||
|
|
||||||
|
solution.Temperature = temperature;
|
||||||
|
UpdateChemicals(owner, solution, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the thermal energy of a solution to a new value and then checks for reaction processing.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="owner">The entity in which the solution is located.</param>
|
||||||
|
/// <param name="solution">The solution to set the thermal energy of.</param>
|
||||||
|
/// <param name="thermalEnergy">The new value to set the thermal energy to.</param>
|
||||||
|
public void SetThermalEnergy(EntityUid owner, Solution solution, float thermalEnergy)
|
||||||
|
{
|
||||||
|
if (thermalEnergy == solution.ThermalEnergy)
|
||||||
|
return;
|
||||||
|
|
||||||
|
solution.ThermalEnergy = thermalEnergy;
|
||||||
|
UpdateChemicals(owner, solution, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds some thermal energy to a solution and then checks for reaction processing.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="owner">The entity in which the solution is located.</param>
|
||||||
|
/// <param name="solution">The solution to set the thermal energy of.</param>
|
||||||
|
/// <param name="thermalEnergy">The new value to set the thermal energy to.</param>
|
||||||
|
public void AddThermalEnergy(EntityUid owner, Solution solution, float thermalEnergy)
|
||||||
|
{
|
||||||
|
if (thermalEnergy == 0.0f)
|
||||||
|
return;
|
||||||
|
|
||||||
|
solution.ThermalEnergy += thermalEnergy;
|
||||||
|
UpdateChemicals(owner, solution, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Thermal Energy and Temperature
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
using System;
|
||||||
|
using Content.Shared.Chemistry.Components;
|
||||||
|
using Content.Shared.Chemistry.Reagent;
|
||||||
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
|
||||||
|
namespace Content.Server.Chemistry.ReactionEffects
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the temperature of the solution involved with the reaction to a new value.
|
||||||
|
/// </summary>
|
||||||
|
[DataDefinition]
|
||||||
|
public class SetSolutionTemperatureEffect : ReagentEffect
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The temperature to set the solution to.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("temperature", required: true)] private float _temperature;
|
||||||
|
|
||||||
|
public override void Effect(ReagentEffectArgs args)
|
||||||
|
{
|
||||||
|
var solution = args.Source;
|
||||||
|
if (solution == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
solution.Temperature = _temperature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adjusts the temperature of the solution involved in the reaction.
|
||||||
|
/// </summary>
|
||||||
|
[DataDefinition]
|
||||||
|
public class AdjustSolutionTemperatureEffect : ReagentEffect
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The total change in the thermal energy of the solution.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("delta", required: true)] protected float Delta;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The minimum temperature this effect can reach.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("minTemp")] private float _minTemp = 0.0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum temperature this effect can reach.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("maxTemp")] private float _maxTemp = float.PositiveInfinity;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, then scale ranges by intensity. If not, the ranges are the same regardless of reactant amount.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("scaled")] private bool _scaled;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="solution"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected virtual float GetDeltaT(Solution solution) => Delta;
|
||||||
|
|
||||||
|
public override void Effect(ReagentEffectArgs args)
|
||||||
|
{
|
||||||
|
var solution = args.Source;
|
||||||
|
if (solution == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var deltaT = GetDeltaT(solution);
|
||||||
|
if (_scaled)
|
||||||
|
deltaT = deltaT * (float) args.Quantity;
|
||||||
|
|
||||||
|
if (deltaT == 0.0d)
|
||||||
|
return;
|
||||||
|
if (deltaT > 0.0d && solution.Temperature >= _maxTemp)
|
||||||
|
return;
|
||||||
|
if (deltaT < 0.0d && solution.Temperature <= _minTemp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
solution.Temperature = MathF.Max(MathF.Min(solution.Temperature + deltaT, _minTemp), _maxTemp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adjusts the thermal energy of the solution involved in the reaction.
|
||||||
|
/// </summary>
|
||||||
|
public class AdjustSolutionThermalEnergyEffect : AdjustSolutionTemperatureEffect
|
||||||
|
{
|
||||||
|
protected override float GetDeltaT(Solution solution)
|
||||||
|
{
|
||||||
|
var heatCapacity = solution.HeatCapacity;
|
||||||
|
if (heatCapacity == 0.0f)
|
||||||
|
return 0.0f;
|
||||||
|
return Delta / heatCapacity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using Content.Shared.Chemistry.Components;
|
||||||
|
using Content.Shared.Chemistry.Reagent;
|
||||||
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
|
||||||
|
namespace Content.Server.Chemistry.ReagentEffectConditions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Requires the solution to be above or below a certain temperature.
|
||||||
|
/// Used for things like explosives.
|
||||||
|
/// </summary>
|
||||||
|
public class SolutionTemperature : ReagentEffectCondition
|
||||||
|
{
|
||||||
|
[DataField("min")]
|
||||||
|
public float Min = 0.0f;
|
||||||
|
|
||||||
|
[DataField("max")]
|
||||||
|
public float Max = float.PositiveInfinity;
|
||||||
|
public override bool Condition(ReagentEffectArgs args)
|
||||||
|
{
|
||||||
|
if (args.Source == null)
|
||||||
|
return false;
|
||||||
|
if (args.Source.Temperature < Min)
|
||||||
|
return false;
|
||||||
|
if (args.Source.Temperature > Max)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using Content.Shared.Chemistry.Components;
|
||||||
|
using Content.Shared.Chemistry.Reagent;
|
||||||
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
|
||||||
|
namespace Content.Server.Chemistry.ReagentEffectConditions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Requires the solution to be above or below a certain thermal energy.
|
||||||
|
/// Used for things like explosives.
|
||||||
|
/// </summary>
|
||||||
|
public class SolutionThermalEnergy : ReagentEffectCondition
|
||||||
|
{
|
||||||
|
[DataField("min")]
|
||||||
|
public float Min = 0.0f;
|
||||||
|
|
||||||
|
[DataField("max")]
|
||||||
|
public float Max = float.PositiveInfinity;
|
||||||
|
public override bool Condition(ReagentEffectArgs args)
|
||||||
|
{
|
||||||
|
if (args.Source == null)
|
||||||
|
return false;
|
||||||
|
if (args.Source.ThermalEnergy < Min)
|
||||||
|
return false;
|
||||||
|
if (args.Source.ThermalEnergy > Max)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
using Content.Shared.Chemistry.Reagent;
|
using Content.Shared.Chemistry.Reagent;
|
||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
@@ -48,5 +50,45 @@ namespace Content.Shared.Chemistry.Components
|
|||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public FixedPoint2 CurrentVolume => TotalVolume;
|
public FixedPoint2 CurrentVolume => TotalVolume;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The total heat capacity of all reagents in the solution.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public float HeatCapacity => GetHeatCapacity();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The average specific heat of all reagents in the solution.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public float SpecificHeat => HeatCapacity / (float) TotalVolume;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The total thermal energy of the reagents in the solution.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float ThermalEnergy {
|
||||||
|
get { return Temperature * HeatCapacity; }
|
||||||
|
set { Temperature = ((HeatCapacity == 0.0f) ? 0.0f : (value / HeatCapacity)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the total heat capacity of the reagents in this solution.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The total heat capacity of the reagents in this solution.</returns>
|
||||||
|
private float GetHeatCapacity()
|
||||||
|
{
|
||||||
|
var heatCapacity = 0.0f;
|
||||||
|
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||||
|
foreach(var reagent in Contents)
|
||||||
|
{
|
||||||
|
if (!prototypeManager.TryIndex(reagent.ReagentId, out ReagentPrototype? proto))
|
||||||
|
proto = new ReagentPrototype();
|
||||||
|
|
||||||
|
heatCapacity += (float) reagent.Quantity * proto.SpecificHeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
return heatCapacity;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,13 @@ namespace Content.Shared.Chemistry.Components
|
|||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public FixedPoint2 TotalVolume { get; set; }
|
public FixedPoint2 TotalVolume { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The temperature of the reagents in the solution.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
[DataField("temperature")]
|
||||||
|
public float Temperature { get; set; } = 293.15f;
|
||||||
|
|
||||||
public Color Color => GetColor();
|
public Color Color => GetColor();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -94,11 +101,18 @@ namespace Content.Shared.Chemistry.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="reagentId">The prototype ID of the reagent to add.</param>
|
/// <param name="reagentId">The prototype ID of the reagent to add.</param>
|
||||||
/// <param name="quantity">The quantity in milli-units.</param>
|
/// <param name="quantity">The quantity in milli-units.</param>
|
||||||
public void AddReagent(string reagentId, FixedPoint2 quantity)
|
public void AddReagent(string reagentId, FixedPoint2 quantity, float? temperature = null)
|
||||||
{
|
{
|
||||||
if (quantity <= 0)
|
if (quantity <= 0)
|
||||||
return;
|
return;
|
||||||
|
if (!IoCManager.Resolve<IPrototypeManager>().TryIndex(reagentId, out ReagentPrototype? proto))
|
||||||
|
proto = new ReagentPrototype();
|
||||||
|
|
||||||
|
if (temperature == null)
|
||||||
|
temperature = Temperature;
|
||||||
|
|
||||||
|
var oldThermalEnergy = Temperature * GetHeatCapacity();
|
||||||
|
var addedThermalEnergy = (float) ((float) quantity * proto.SpecificHeat * temperature);
|
||||||
for (var i = 0; i < Contents.Count; i++)
|
for (var i = 0; i < Contents.Count; i++)
|
||||||
{
|
{
|
||||||
var reagent = Contents[i];
|
var reagent = Contents[i];
|
||||||
@@ -106,12 +120,16 @@ namespace Content.Shared.Chemistry.Components
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
Contents[i] = new ReagentQuantity(reagentId, reagent.Quantity + quantity);
|
Contents[i] = new ReagentQuantity(reagentId, reagent.Quantity + quantity);
|
||||||
|
|
||||||
TotalVolume += quantity;
|
TotalVolume += quantity;
|
||||||
|
ThermalEnergy = oldThermalEnergy + addedThermalEnergy;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Contents.Add(new ReagentQuantity(reagentId, quantity));
|
Contents.Add(new ReagentQuantity(reagentId, quantity));
|
||||||
|
|
||||||
TotalVolume += quantity;
|
TotalVolume += quantity;
|
||||||
|
ThermalEnergy = oldThermalEnergy + addedThermalEnergy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -161,9 +179,10 @@ namespace Content.Shared.Chemistry.Components
|
|||||||
var reagent = Contents[i];
|
var reagent = Contents[i];
|
||||||
if(reagent.ReagentId != reagentId)
|
if(reagent.ReagentId != reagentId)
|
||||||
continue;
|
continue;
|
||||||
|
if (!IoCManager.Resolve<IPrototypeManager>().TryIndex(reagentId, out ReagentPrototype? proto))
|
||||||
|
proto = new ReagentPrototype();
|
||||||
|
|
||||||
var curQuantity = reagent.Quantity;
|
var curQuantity = reagent.Quantity;
|
||||||
|
|
||||||
var newQuantity = curQuantity - quantity;
|
var newQuantity = curQuantity - quantity;
|
||||||
if (newQuantity <= 0)
|
if (newQuantity <= 0)
|
||||||
{
|
{
|
||||||
@@ -234,7 +253,9 @@ namespace Content.Shared.Chemistry.Components
|
|||||||
|
|
||||||
newSolution = new Solution();
|
newSolution = new Solution();
|
||||||
var newTotalVolume = FixedPoint2.New(0);
|
var newTotalVolume = FixedPoint2.New(0);
|
||||||
|
var newHeatCapacity = 0.0d;
|
||||||
var remainingVolume = TotalVolume;
|
var remainingVolume = TotalVolume;
|
||||||
|
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||||
|
|
||||||
for (var i = Contents.Count - 1; i >= 0; i--)
|
for (var i = Contents.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
@@ -244,6 +265,9 @@ namespace Content.Shared.Chemistry.Components
|
|||||||
|
|
||||||
var reagent = Contents[i];
|
var reagent = Contents[i];
|
||||||
var ratio = (remainingVolume - quantity).Double() / remainingVolume.Double();
|
var ratio = (remainingVolume - quantity).Double() / remainingVolume.Double();
|
||||||
|
if(!prototypeManager.TryIndex(reagent.ReagentId, out ReagentPrototype? proto))
|
||||||
|
proto = new ReagentPrototype();
|
||||||
|
|
||||||
remainingVolume -= reagent.Quantity;
|
remainingVolume -= reagent.Quantity;
|
||||||
|
|
||||||
var newQuantity = reagent.Quantity * ratio;
|
var newQuantity = reagent.Quantity * ratio;
|
||||||
@@ -258,10 +282,12 @@ namespace Content.Shared.Chemistry.Components
|
|||||||
newSolution.Contents.Add(new ReagentQuantity(reagent.ReagentId, splitQuantity));
|
newSolution.Contents.Add(new ReagentQuantity(reagent.ReagentId, splitQuantity));
|
||||||
|
|
||||||
newTotalVolume += splitQuantity;
|
newTotalVolume += splitQuantity;
|
||||||
|
newHeatCapacity += (float) splitQuantity * proto.SpecificHeat;
|
||||||
quantity -= splitQuantity;
|
quantity -= splitQuantity;
|
||||||
}
|
}
|
||||||
|
|
||||||
newSolution.TotalVolume = newTotalVolume;
|
newSolution.TotalVolume = newTotalVolume;
|
||||||
|
newSolution.Temperature = Temperature;
|
||||||
TotalVolume -= newTotalVolume;
|
TotalVolume -= newTotalVolume;
|
||||||
|
|
||||||
return newSolution;
|
return newSolution;
|
||||||
@@ -269,6 +295,8 @@ namespace Content.Shared.Chemistry.Components
|
|||||||
|
|
||||||
public void AddSolution(Solution otherSolution)
|
public void AddSolution(Solution otherSolution)
|
||||||
{
|
{
|
||||||
|
var oldThermalEnergy = Temperature * GetHeatCapacity();
|
||||||
|
var addedThermalEnergy = otherSolution.Temperature * otherSolution.GetHeatCapacity();
|
||||||
for (var i = 0; i < otherSolution.Contents.Count; i++)
|
for (var i = 0; i < otherSolution.Contents.Count; i++)
|
||||||
{
|
{
|
||||||
var otherReagent = otherSolution.Contents[i];
|
var otherReagent = otherSolution.Contents[i];
|
||||||
@@ -292,6 +320,7 @@ namespace Content.Shared.Chemistry.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
TotalVolume += otherSolution.TotalVolume;
|
TotalVolume += otherSolution.TotalVolume;
|
||||||
|
ThermalEnergy = oldThermalEnergy + addedThermalEnergy;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color GetColor()
|
private Color GetColor()
|
||||||
@@ -329,16 +358,23 @@ namespace Content.Shared.Chemistry.Components
|
|||||||
public Solution Clone()
|
public Solution Clone()
|
||||||
{
|
{
|
||||||
var volume = FixedPoint2.New(0);
|
var volume = FixedPoint2.New(0);
|
||||||
|
var heatCapacity = 0.0d;
|
||||||
var newSolution = new Solution();
|
var newSolution = new Solution();
|
||||||
|
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||||
|
|
||||||
for (var i = 0; i < Contents.Count; i++)
|
for (var i = 0; i < Contents.Count; i++)
|
||||||
{
|
{
|
||||||
var reagent = Contents[i];
|
var reagent = Contents[i];
|
||||||
|
if (!prototypeManager.TryIndex(reagent.ReagentId, out ReagentPrototype? proto))
|
||||||
|
proto = new ReagentPrototype();
|
||||||
|
|
||||||
newSolution.Contents.Add(reagent);
|
newSolution.Contents.Add(reagent);
|
||||||
volume += reagent.Quantity;
|
volume += reagent.Quantity;
|
||||||
|
heatCapacity += (float) reagent.Quantity * proto.SpecificHeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
newSolution.TotalVolume = volume;
|
newSolution.TotalVolume = volume;
|
||||||
|
newSolution.Temperature = Temperature;
|
||||||
return newSolution;
|
return newSolution;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,18 @@ namespace Content.Shared.Chemistry.Reaction
|
|||||||
[DataField("reactants", customTypeSerializer:typeof(PrototypeIdDictionarySerializer<ReactantPrototype, ReagentPrototype>))]
|
[DataField("reactants", customTypeSerializer:typeof(PrototypeIdDictionarySerializer<ReactantPrototype, ReagentPrototype>))]
|
||||||
public Dictionary<string, ReactantPrototype> Reactants = new();
|
public Dictionary<string, ReactantPrototype> Reactants = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The minimum temperature the reaction can occur at.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("minTemp")]
|
||||||
|
public float MinimumTemperature = 0.0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum temperature the reaction can occur at.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("maxTemp")]
|
||||||
|
public float MaximumTemperature = float.PositiveInfinity;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reagents created when the reaction occurs.
|
/// Reagents created when the reaction occurs.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -109,6 +109,15 @@ namespace Content.Shared.Chemistry.Reaction
|
|||||||
private static bool CanReact(Solution solution, ReactionPrototype reaction, out FixedPoint2 lowestUnitReactions)
|
private static bool CanReact(Solution solution, ReactionPrototype reaction, out FixedPoint2 lowestUnitReactions)
|
||||||
{
|
{
|
||||||
lowestUnitReactions = FixedPoint2.MaxValue;
|
lowestUnitReactions = FixedPoint2.MaxValue;
|
||||||
|
if (solution.Temperature < reaction.MinimumTemperature)
|
||||||
|
{
|
||||||
|
lowestUnitReactions = FixedPoint2.Zero;
|
||||||
|
return false;
|
||||||
|
} else if(solution.Temperature > reaction.MaximumTemperature)
|
||||||
|
{
|
||||||
|
lowestUnitReactions = FixedPoint2.Zero;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var reactantData in reaction.Reactants)
|
foreach (var reactantData in reaction.Reactants)
|
||||||
{
|
{
|
||||||
@@ -202,7 +211,7 @@ namespace Content.Shared.Chemistry.Reaction
|
|||||||
/// Removes the reactants from the solution, then returns a solution with all products.
|
/// Removes the reactants from the solution, then returns a solution with all products.
|
||||||
/// WARNING: Does not trigger reactions between solution and new products.
|
/// WARNING: Does not trigger reactions between solution and new products.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private bool ProcessReactions(Solution solution, EntityUid Owner, [MaybeNullWhen(false)] out Solution productSolution)
|
private bool ProcessReactions(Solution solution, EntityUid owner, [MaybeNullWhen(false)] out Solution productSolution)
|
||||||
{
|
{
|
||||||
foreach(var reactant in solution.Contents)
|
foreach(var reactant in solution.Contents)
|
||||||
{
|
{
|
||||||
@@ -214,7 +223,7 @@ namespace Content.Shared.Chemistry.Reaction
|
|||||||
if (!CanReact(solution, reaction, out var unitReactions))
|
if (!CanReact(solution, reaction, out var unitReactions))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
productSolution = PerformReaction(solution, Owner, reaction, unitReactions);
|
productSolution = PerformReaction(solution, owner, reaction, unitReactions);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,13 @@ namespace Content.Shared.Chemistry.Reagent
|
|||||||
[DataField("color")]
|
[DataField("color")]
|
||||||
public Color SubstanceColor { get; } = Color.White;
|
public Color SubstanceColor { get; } = Color.White;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The specific heat of the reagent.
|
||||||
|
/// How much energy it takes to heat one unit of this reagent by one Kelvin.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("specificHeat")]
|
||||||
|
public float SpecificHeat { get; } = 1.0f;
|
||||||
|
|
||||||
[DataField("boilingPoint")]
|
[DataField("boilingPoint")]
|
||||||
public float? BoilingPoint { get; }
|
public float? BoilingPoint { get; }
|
||||||
|
|
||||||
|
|||||||
@@ -112,12 +112,22 @@ namespace Content.Shared.Localizations
|
|||||||
|
|
||||||
public static readonly TypeTable Energy = new TypeTable
|
public static readonly TypeTable Energy = new TypeTable
|
||||||
(
|
(
|
||||||
new TypeTable.Entry(range: (null, 1e-3), factor: 1e6, unit: "u--joule"),
|
new TypeTable.Entry(range: ( null, 1e-3), factor: 1e6, unit: "u--joule"),
|
||||||
new TypeTable.Entry(range: (1e-3, 1), factor: 1e3, unit: "m--joule"),
|
new TypeTable.Entry(range: ( 1e-3, 1), factor: 1e3, unit: "m--joule"),
|
||||||
new TypeTable.Entry(range: ( 1, 1000), factor: 1, unit: "joule"),
|
new TypeTable.Entry(range: ( 1, 1000), factor: 1, unit: "joule"),
|
||||||
new TypeTable.Entry(range: (1000, 1e6), factor: 1e-4, unit: "k-joule"),
|
new TypeTable.Entry(range: ( 1000, 1e6), factor: 1e-4, unit: "k-joule"),
|
||||||
new TypeTable.Entry(range: ( 1e6, 1e9), factor: 1e-6, unit: "m-joule"),
|
new TypeTable.Entry(range: ( 1e6, 1e9), factor: 1e-6, unit: "m-joule"),
|
||||||
new TypeTable.Entry(range: ( 1e9, null), factor: 1e-9, unit: "g-joule")
|
new TypeTable.Entry(range: ( 1e9, null), factor: 1e-9, unit: "g-joule")
|
||||||
|
);
|
||||||
|
|
||||||
|
public static readonly TypeTable Temperature = new TypeTable
|
||||||
|
(
|
||||||
|
new TypeTable.Entry(range: ( null, 1e-3), factor: 1e6, unit: "u--kelvin"),
|
||||||
|
new TypeTable.Entry(range: ( 1e-3, 1), factor: 1e3, unit: "m--kelvin"),
|
||||||
|
new TypeTable.Entry(range: ( 1, 1e3), factor: 1, unit: "kelvin"),
|
||||||
|
new TypeTable.Entry(range: ( 1e3, 1e6), factor: 1e-3, unit: "k-kelvin"),
|
||||||
|
new TypeTable.Entry(range: ( 1e6, 1e9), factor: 1e-6, unit: "m-kelvin"),
|
||||||
|
new TypeTable.Entry(range: ( 1e9, null), factor: 1e-9, unit: "g-kelvin")
|
||||||
);
|
);
|
||||||
|
|
||||||
public readonly static Dictionary<string, TypeTable> Types = new Dictionary<string, TypeTable>
|
public readonly static Dictionary<string, TypeTable> Types = new Dictionary<string, TypeTable>
|
||||||
@@ -125,7 +135,8 @@ namespace Content.Shared.Localizations
|
|||||||
["generic"] = Generic!,
|
["generic"] = Generic!,
|
||||||
["pressure"] = Pressure!,
|
["pressure"] = Pressure!,
|
||||||
["power"] = Power!,
|
["power"] = Power!,
|
||||||
["energy"] = Energy!
|
["energy"] = Energy!,
|
||||||
|
["temperature"] = Temperature!
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,21 @@
|
|||||||
using Content.Shared.Chemistry.Components;
|
using Content.Shared.Chemistry.Components;
|
||||||
using Content.Shared.Chemistry.Reagent;
|
using Content.Shared.Chemistry.Reagent;
|
||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
|
||||||
namespace Content.Tests.Shared.Chemistry
|
namespace Content.Tests.Shared.Chemistry
|
||||||
{
|
{
|
||||||
[TestFixture, Parallelizable, TestOf(typeof(Solution))]
|
[TestFixture, Parallelizable, TestOf(typeof(Solution))]
|
||||||
public class Solution_Tests
|
public class Solution_Tests : ContentUnitTest
|
||||||
{
|
{
|
||||||
|
[OneTimeSetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
IoCManager.Resolve<IPrototypeManager>().Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void AddReagentAndGetSolution()
|
public void AddReagentAndGetSolution()
|
||||||
{
|
{
|
||||||
@@ -343,5 +351,134 @@ namespace Content.Tests.Shared.Chemistry
|
|||||||
Assert.That(solutionOne.GetReagentQuantity("earth").Int(), Is.EqualTo(1000));
|
Assert.That(solutionOne.GetReagentQuantity("earth").Int(), Is.EqualTo(1000));
|
||||||
Assert.That(solutionOne.TotalVolume.Int(), Is.EqualTo(4500));
|
Assert.That(solutionOne.TotalVolume.Int(), Is.EqualTo(4500));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests concerning thermal energy and temperature.
|
||||||
|
#region Thermal Energy and Temperature
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void EmptySolutionHasNoHeatCapacity()
|
||||||
|
{
|
||||||
|
var solution = new Solution();
|
||||||
|
Assert.That(solution.HeatCapacity, Is.EqualTo(0.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void EmptySolutionHasNoThermalEnergy()
|
||||||
|
{
|
||||||
|
var solution = new Solution();
|
||||||
|
Assert.That(solution.ThermalEnergy, Is.EqualTo(0.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AddReagentToEmptySolutionSetsTemperature()
|
||||||
|
{
|
||||||
|
const float testTemp = 100.0f;
|
||||||
|
|
||||||
|
var solution = new Solution();
|
||||||
|
solution.AddReagent("water", FixedPoint2.New(100), testTemp);
|
||||||
|
Assert.That(solution.Temperature, Is.EqualTo(testTemp));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AddReagentWithNullTemperatureDoesNotEffectTemperature()
|
||||||
|
{
|
||||||
|
const float initialTemp = 100.0f;
|
||||||
|
|
||||||
|
var solution = new Solution();
|
||||||
|
solution.AddReagent("water", FixedPoint2.New(100), initialTemp);
|
||||||
|
|
||||||
|
solution.AddReagent("water", FixedPoint2.New(100));
|
||||||
|
Assert.That(solution.Temperature, Is.EqualTo(initialTemp));
|
||||||
|
|
||||||
|
solution.AddReagent("earth", FixedPoint2.New(100));
|
||||||
|
Assert.That(solution.Temperature, Is.EqualTo(initialTemp));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AddSolutionWithEqualTemperatureDoesNotChangeTemperature()
|
||||||
|
{
|
||||||
|
const float initialTemp = 100.0f;
|
||||||
|
|
||||||
|
var solutionOne = new Solution();
|
||||||
|
solutionOne.AddReagent("water", FixedPoint2.New(100));
|
||||||
|
solutionOne.Temperature = initialTemp;
|
||||||
|
|
||||||
|
var solutionTwo = new Solution();
|
||||||
|
solutionTwo.AddReagent("water", FixedPoint2.New(100));
|
||||||
|
solutionTwo.AddReagent("earth", FixedPoint2.New(100));
|
||||||
|
solutionTwo.Temperature = initialTemp;
|
||||||
|
|
||||||
|
solutionOne.AddSolution(solutionTwo);
|
||||||
|
Assert.That(solutionOne.Temperature, Is.EqualTo(initialTemp));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void RemoveReagentDoesNotEffectTemperature()
|
||||||
|
{
|
||||||
|
const float initialTemp = 100.0f;
|
||||||
|
|
||||||
|
var solution = new Solution();
|
||||||
|
solution.AddReagent("water", FixedPoint2.New(100), initialTemp);
|
||||||
|
solution.RemoveReagent("water", FixedPoint2.New(50));
|
||||||
|
Assert.That(solution.Temperature, Is.EqualTo(initialTemp));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void RemoveSolutionDoesNotEffectTemperature()
|
||||||
|
{
|
||||||
|
const float initialTemp = 100.0f;
|
||||||
|
|
||||||
|
var solution = new Solution();
|
||||||
|
solution.AddReagent("water", FixedPoint2.New(100), initialTemp);
|
||||||
|
solution.RemoveSolution(FixedPoint2.New(50));
|
||||||
|
Assert.That(solution.Temperature, Is.EqualTo(initialTemp));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SplitSolutionDoesNotEffectTemperature()
|
||||||
|
{
|
||||||
|
const float initialTemp = 100.0f;
|
||||||
|
|
||||||
|
var solution = new Solution();
|
||||||
|
solution.AddReagent("water", FixedPoint2.New(100), initialTemp);
|
||||||
|
solution.SplitSolution(FixedPoint2.New(50));
|
||||||
|
Assert.That(solution.Temperature, Is.EqualTo(initialTemp));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AddReagentWithSetTemperatureAdjustsTemperature()
|
||||||
|
{
|
||||||
|
const float temp = 100.0f;
|
||||||
|
|
||||||
|
var solution = new Solution();
|
||||||
|
solution.AddReagent("water", FixedPoint2.New(100), temp * 1);
|
||||||
|
Assert.That(solution.Temperature, Is.EqualTo(temp * 1));
|
||||||
|
|
||||||
|
solution.AddReagent("water", FixedPoint2.New(100), temp * 3);
|
||||||
|
Assert.That(solution.Temperature, Is.EqualTo(temp * 2));
|
||||||
|
|
||||||
|
solution.AddReagent("earth", FixedPoint2.New(100), temp * 5);
|
||||||
|
Assert.That(solution.Temperature, Is.EqualTo(temp * 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AddSolutionCombinesThermalEnergy()
|
||||||
|
{
|
||||||
|
const float initialTemp = 100.0f;
|
||||||
|
|
||||||
|
var solutionOne = new Solution();
|
||||||
|
solutionOne.AddReagent("water", FixedPoint2.New(100), initialTemp);
|
||||||
|
|
||||||
|
var solutionTwo = new Solution();
|
||||||
|
solutionTwo.AddReagent("water", FixedPoint2.New(100), initialTemp);
|
||||||
|
solutionTwo.AddReagent("earth", FixedPoint2.New(100));
|
||||||
|
|
||||||
|
var thermalEnergyOne = solutionOne.ThermalEnergy;
|
||||||
|
var thermalEnergyTwo = solutionTwo.ThermalEnergy;
|
||||||
|
solutionOne.AddSolution(solutionTwo);
|
||||||
|
Assert.That(solutionOne.ThermalEnergy, Is.EqualTo(thermalEnergyOne + thermalEnergyTwo));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Thermal Energy and Temperature
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,3 +78,18 @@ units-m--joule-long = Millijoule
|
|||||||
units-joule-long = Joule
|
units-joule-long = Joule
|
||||||
units-k-joule-long = Kilojoule
|
units-k-joule-long = Kilojoule
|
||||||
units-m-joule-long = Megajoule
|
units-m-joule-long = Megajoule
|
||||||
|
|
||||||
|
## Kelvin (Temperature)
|
||||||
|
units-u--kelvin = µK
|
||||||
|
units-m--kelvin = mK
|
||||||
|
units-kelvin = K
|
||||||
|
units-k-kelvin = kK
|
||||||
|
units-m-kelvin = MK
|
||||||
|
units-g-kelvin = GK
|
||||||
|
|
||||||
|
units-u--kelvin-long = Microkelvin
|
||||||
|
units-m--kelvin-long = Millikelvin
|
||||||
|
units-kelvin-long = Kelvin
|
||||||
|
units-k-kelvin-long = Kilokelvin
|
||||||
|
units-m-kelvin-long = Megakelvin
|
||||||
|
units-g-kelvin-long = Gigakelvin
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
admin-solutions-window-title = Solution Editor - {$targetName}
|
admin-solutions-window-title = Solution Editor - {$targetName}
|
||||||
admin-solutions-window-solution-label = Target solution:
|
admin-solutions-window-solution-label = Target solution:
|
||||||
admin-solutions-window-add-new-button = Add new reagent
|
admin-solutions-window-add-new-button = Add new reagent
|
||||||
admin-solutions-window-capacity-label = Capacity {$currentVolume}/{$maxVolume}u
|
admin-solutions-window-volume-label = Volume {$currentVolume}/{$maxVolume}u
|
||||||
|
admin-solutions-window-capacity-label = Capacity (u):
|
||||||
|
admin-solutions-window-specific-heat-label = Specific Heat: {$specificHeat} J/(K*u)
|
||||||
|
admin-solutions-window-heat-capacity-label = Heat Capacity: {$heatCapacity} J/K
|
||||||
|
admin-solutions-window-temperature-label = Temperature (K):
|
||||||
|
admin-solutions-window-thermal-energy-label = Thermal Energy (J):
|
||||||
|
|||||||
Reference in New Issue
Block a user