Revert "Add YAML gas reactions (#22803)" (#22939)

This reverts commit 054321d2c2.

Co-authored-by: Kevin Zheng <kevinz5000@gmail.com>
This commit is contained in:
Kara
2023-12-24 23:50:42 -07:00
committed by GitHub
parent 0baffe3243
commit ba88a9a022
9 changed files with 269 additions and 192 deletions

View File

@@ -10,7 +10,6 @@ namespace Content.Server.Atmos.EntitySystems
public sealed partial class AtmosphereSystem public sealed partial class AtmosphereSystem
{ {
[Dependency] private readonly IPrototypeManager _protoMan = default!; [Dependency] private readonly IPrototypeManager _protoMan = default!;
[Dependency] private readonly GenericGasReactionSystem _reaction = default!;
private GasReactionPrototype[] _gasReactions = Array.Empty<GasReactionPrototype>(); private GasReactionPrototype[] _gasReactions = Array.Empty<GasReactionPrototype>();
private float[] _gasSpecificHeats = new float[Atmospherics.TotalNumberOfGases]; private float[] _gasSpecificHeats = new float[Atmospherics.TotalNumberOfGases];
@@ -347,7 +346,7 @@ namespace Content.Server.Atmos.EntitySystems
break; break;
} }
return _reaction.ReactAll(GasReactions, mixture, holder); return reaction;
} }
public enum GasCompareResult public enum GasCompareResult

View File

@@ -1,133 +0,0 @@
using Content.Server.Atmos.Reactions;
using Content.Shared.Atmos;
using JetBrains.Annotations;
using System.Collections;
using System.Linq;
namespace Content.Server.Atmos.EntitySystems;
public sealed class GenericGasReactionSystem : EntitySystem
{
[Dependency] private readonly AtmosphereSystem _atmosphere = default!;
/// <summary>
/// Return a reaction rate (in units reactants per second) for a given reaction. Based on the
/// Arrhenius equation (https://en.wikipedia.org/wiki/Arrhenius_equation).
///
/// This means that most reactions scale exponentially above the MinimumTemperatureRequirement.
/// </summary>
private float ReactionRate(GasReactionPrototype reaction, GasMixture mix, float dE)
{
float temp = mix.Temperature;
// Gas reactions have a MinimumEnergyRequirement which is in spirit activiation energy (Ea),
// but no reactions define it. So we have to calculate one to use. One way is to assume that
// Ea = 10*R*MinimumTemperatureRequirement such that Ea >> RT.
float TScaleFactor = 10;
float Ea = TScaleFactor*Atmospherics.R*reaction.MinimumTemperatureRequirement + dE;
// To compute initial rate coefficient A, assume that at temp = min temp we return 1/10.
float RateScaleFactor = 10; // not necessarily the same as TScaleFactor! Don't get confused!
float A = MathF.Exp(TScaleFactor) / RateScaleFactor;
return reaction.RateMultiplier*A*MathF.Exp(-Ea/(Atmospherics.R*temp));
}
/// <summary>
/// Run all of the reactions given on the given gas mixture located in the given container.
/// </summary>
public ReactionResult ReactAll(IEnumerable<GasReactionPrototype> reactions, GasMixture mix, IGasMixtureHolder? holder)
{
// It is possible for reactions to change the specific heat capacity, so we need to save initial
// internal energy so that we can conserve energy at the end
float initialE = _atmosphere.GetThermalEnergy(mix);
float reactionE = 0; // heat added by reaction enthalpy
foreach (var reaction in reactions)
{
float rate = 1f; // rate of this reaction
int reactants = 0;
// Reactions that have a maximum temperature really don't make physical sense since increasing
// kinetic energy always increases reaction rate. But begrudgingly implement this anyway.
if (mix.Temperature > reaction.MaximumTemperatureRequirement)
continue;
// Add concentration-dependent reaction rate
// For 1A + 2B -> 3C, the concentration-dependence is [A]^1 * [B]^2
float nTotal = mix.TotalMoles;
if (nTotal < Atmospherics.GasMinMoles)
continue;
foreach (var (reactant, num) in reaction.Reactants)
{
rate *= MathF.Pow(mix.GetMoles(reactant)/nTotal, num);
reactants++;
}
// No reactants; this is not a generic reaction.
if (reactants == 0)
continue;
// Sum catalysts
float catalystEnergy = 0;
foreach (var (catalyst, dE) in reaction.Catalysts)
{
catalystEnergy += dE;
}
// Now apply temperature-dependent reaction rate scaling
rate *= ReactionRate(reaction, mix, catalystEnergy);
// Nothing to do
if (rate <= 0)
continue;
// Pass to check the maximum rate, limited by the minimum available
// reactant to avoid going negative
float rateLim = rate;
foreach (var (reactant, num) in reaction.Reactants)
{
rateLim = MathF.Min(mix.GetMoles(reactant)/num, rateLim);
}
rate = rateLim;
// Go through and remove all the reactants
foreach (var (reactant, num) in reaction.Reactants)
{
mix.AdjustMoles(reactant, -num*rate);
}
// Go through and add products
foreach (var (product, num) in reaction.Products)
{
mix.AdjustMoles(product, num*rate);
}
// Add heat from the reaction
if (reaction.Enthalpy != 0)
{
reactionE += reaction.Enthalpy/_atmosphere.HeatScale * rate;
if (reaction.Enthalpy > 0)
mix.ReactionResults[GasReaction.Fire] += rate;
}
}
float newHeatCapacity = _atmosphere.GetHeatCapacity(mix, true);
if (newHeatCapacity > Atmospherics.MinimumHeatCapacity)
{
mix.Temperature = (initialE + reactionE)/newHeatCapacity;
}
if (reactionE > 0)
{
var location = holder as TileAtmosphere;
if (location != null)
{
if (mix.Temperature > Atmospherics.FireMinimumTemperatureToExist)
{
_atmosphere.HotspotExpose(location.GridIndex, location.GridIndices, mix.Temperature, mix.Volume);
}
}
}
return ReactionResult.Reacting;
}
}

View File

@@ -0,0 +1,33 @@
using Content.Server.Atmos.EntitySystems;
using Content.Shared.Atmos;
using JetBrains.Annotations;
namespace Content.Server.Atmos.Reactions;
[UsedImplicitly]
public sealed partial class AmmoniaOxygenReaction : IGasReactionEffect
{
public ReactionResult React(GasMixture mixture, IGasMixtureHolder? holder, AtmosphereSystem atmosphereSystem, float heatScale)
{
var nAmmonia = mixture.GetMoles(Gas.Ammonia);
var nOxygen = mixture.GetMoles(Gas.Oxygen);
var nTotal = mixture.TotalMoles;
// Concentration-dependent reaction rate
var fAmmonia = nAmmonia/nTotal;
var fOxygen = nOxygen/nTotal;
var rate = MathF.Pow(fAmmonia, 2) * MathF.Pow(fOxygen, 2);
var deltaMoles = nAmmonia / Atmospherics.AmmoniaOxygenReactionRate * 2 * rate;
if (deltaMoles <= 0 || nAmmonia - deltaMoles < 0)
return ReactionResult.NoReaction;
mixture.AdjustMoles(Gas.Ammonia, -deltaMoles);
mixture.AdjustMoles(Gas.Oxygen, -deltaMoles);
mixture.AdjustMoles(Gas.NitrousOxide, deltaMoles / 2);
mixture.AdjustMoles(Gas.WaterVapor, deltaMoles * 1.5f);
return ReactionResult.Reacting;
}
}

View File

@@ -0,0 +1,58 @@
using Content.Server.Atmos.EntitySystems;
using Content.Shared.Atmos;
using JetBrains.Annotations;
namespace Content.Server.Atmos.Reactions;
/// <summary>
/// Takes in nitrogen and frezon and cools down the surrounding area.
/// </summary>
[UsedImplicitly]
public sealed partial class FrezonCoolantReaction : IGasReactionEffect
{
public ReactionResult React(GasMixture mixture, IGasMixtureHolder? holder, AtmosphereSystem atmosphereSystem, float heatScale)
{
var oldHeatCapacity = atmosphereSystem.GetHeatCapacity(mixture, true);
var temperature = mixture.Temperature;
var energyModifier = 1f;
var scale = (temperature - Atmospherics.FrezonCoolLowerTemperature) /
(Atmospherics.FrezonCoolMidTemperature - Atmospherics.FrezonCoolLowerTemperature);
if (scale > 1f)
{
// Scale energy but not frezon usage if we're in a very, very hot place
energyModifier = Math.Min(scale, Atmospherics.FrezonCoolMaximumEnergyModifier);
scale = 1f;
}
if (scale <= 0)
return ReactionResult.NoReaction;
var initialNit = mixture.GetMoles(Gas.Nitrogen);
var initialFrezon = mixture.GetMoles(Gas.Frezon);
var burnRate = initialFrezon * scale / Atmospherics.FrezonCoolRateModifier;
var energyReleased = 0f;
if (burnRate > Atmospherics.MinimumHeatCapacity)
{
var nitAmt = Math.Min(burnRate * Atmospherics.FrezonNitrogenCoolRatio, initialNit);
var frezonAmt = Math.Min(burnRate, initialFrezon);
mixture.AdjustMoles(Gas.Nitrogen, -nitAmt);
mixture.AdjustMoles(Gas.Frezon, -frezonAmt);
mixture.AdjustMoles(Gas.NitrousOxide, nitAmt + frezonAmt);
energyReleased = burnRate * Atmospherics.FrezonCoolEnergyReleased * energyModifier;
}
energyReleased /= heatScale; // adjust energy to make sure speedup doesn't cause mega temperature rise
if (energyReleased >= 0f)
return ReactionResult.NoReaction;
var newHeatCapacity = atmosphereSystem.GetHeatCapacity(mixture, true);
if (newHeatCapacity > Atmospherics.MinimumHeatCapacity)
mixture.Temperature = (temperature * oldHeatCapacity + energyReleased) / newHeatCapacity;
return ReactionResult.Reacting;
}
}

View File

@@ -25,8 +25,7 @@ namespace Content.Server.Atmos.Reactions
public string ID { get; private set; } = default!; public string ID { get; private set; } = default!;
/// <summary> /// <summary>
/// Minimum gas amount requirements. Reactions that meet these minimum mole requirements /// Minimum gas amount requirements.
/// have their reaction effects run. Generic gas reactions do not have minimum requirements.
/// </summary> /// </summary>
[DataField("minimumRequirements")] [DataField("minimumRequirements")]
public float[] MinimumRequirements { get; private set; } = new float[Atmospherics.TotalNumberOfGases]; public float[] MinimumRequirements { get; private set; } = new float[Atmospherics.TotalNumberOfGases];
@@ -43,13 +42,6 @@ namespace Content.Server.Atmos.Reactions
[DataField("minimumTemperature")] [DataField("minimumTemperature")]
public float MinimumTemperatureRequirement { get; private set; } = Atmospherics.TCMB; public float MinimumTemperatureRequirement { get; private set; } = Atmospherics.TCMB;
/// <summary>
/// If this is a generic gas reaction, multiply the initial rate by this. The default is reasonable for
/// synthesis reactions. Consider raising this for fires.
/// </summary>
[DataField("rateMultiplier")]
public float RateMultiplier = 1f;
/// <summary> /// <summary>
/// Minimum energy requirement. /// Minimum energy requirement.
/// </summary> /// </summary>
@@ -68,31 +60,6 @@ namespace Content.Server.Atmos.Reactions
/// </summary> /// </summary>
[DataField("effects")] private List<IGasReactionEffect> _effects = new(); [DataField("effects")] private List<IGasReactionEffect> _effects = new();
/// <summary>
/// Energy released by the reaction.
/// </summary>
[DataField("enthalpy")]
public float Enthalpy;
/// <summary>
/// Integer gas IDs and integer ratios required in the reaction. If this is defined, the
/// generic gas reaction will run.
/// </summary>
[DataField("reactants")]
public Dictionary<Gas, int> Reactants = new();
/// <summary>
/// Integer gas IDs and integer ratios of reaction products.
/// </summary>
[DataField("products")]
public Dictionary<Gas, int> Products = new();
/// <summary>
/// Integer gas IDs and how much they modify the activation energy (J/mol).
/// </summary>
[DataField("catalysts")]
public Dictionary<Gas, int> Catalysts = new();
/// <summary> /// <summary>
/// Process all reaction effects. /// Process all reaction effects.
/// </summary> /// </summary>

View File

@@ -0,0 +1,28 @@
using Content.Server.Atmos.EntitySystems;
using Content.Shared.Atmos;
using JetBrains.Annotations;
namespace Content.Server.Atmos.Reactions;
/// <summary>
/// Decomposes Nitrous Oxide into Nitrogen and Oxygen.
/// </summary>
[UsedImplicitly]
public sealed partial class N2ODecompositionReaction : IGasReactionEffect
{
public ReactionResult React(GasMixture mixture, IGasMixtureHolder? holder, AtmosphereSystem atmosphereSystem, float heatScale)
{
var cacheN2O = mixture.GetMoles(Gas.NitrousOxide);
var burnedFuel = cacheN2O / Atmospherics.N2ODecompositionRate;
if (burnedFuel <= 0 || cacheN2O - burnedFuel < 0)
return ReactionResult.NoReaction;
mixture.AdjustMoles(Gas.NitrousOxide, -burnedFuel);
mixture.AdjustMoles(Gas.Nitrogen, burnedFuel);
mixture.AdjustMoles(Gas.Oxygen, burnedFuel / 2);
return ReactionResult.Reacting;
}
}

View File

@@ -0,0 +1,70 @@
using Content.Server.Atmos.EntitySystems;
using Content.Shared.Atmos;
using JetBrains.Annotations;
namespace Content.Server.Atmos.Reactions
{
[UsedImplicitly]
[DataDefinition]
public sealed partial class TritiumFireReaction : IGasReactionEffect
{
public ReactionResult React(GasMixture mixture, IGasMixtureHolder? holder, AtmosphereSystem atmosphereSystem, float heatScale)
{
var energyReleased = 0f;
var oldHeatCapacity = atmosphereSystem.GetHeatCapacity(mixture, true);
var temperature = mixture.Temperature;
var location = holder as TileAtmosphere;
mixture.ReactionResults[GasReaction.Fire] = 0f;
var burnedFuel = 0f;
var initialTrit = mixture.GetMoles(Gas.Tritium);
if (mixture.GetMoles(Gas.Oxygen) < initialTrit ||
Atmospherics.MinimumTritiumOxyburnEnergy > (temperature * oldHeatCapacity))
{
burnedFuel = mixture.GetMoles(Gas.Oxygen) / Atmospherics.TritiumBurnOxyFactor;
if (burnedFuel > initialTrit)
burnedFuel = initialTrit;
mixture.AdjustMoles(Gas.Tritium, -burnedFuel);
}
else
{
burnedFuel = initialTrit;
mixture.SetMoles(Gas.Tritium, mixture.GetMoles(Gas.Tritium ) * (1 - 1 / Atmospherics.TritiumBurnTritFactor));
mixture.AdjustMoles(Gas.Oxygen, -mixture.GetMoles(Gas.Tritium));
energyReleased += (Atmospherics.FireHydrogenEnergyReleased * burnedFuel * (Atmospherics.TritiumBurnTritFactor - 1));
}
if (burnedFuel > 0)
{
energyReleased += (Atmospherics.FireHydrogenEnergyReleased * burnedFuel);
// TODO ATMOS Radiation pulse here!
// Conservation of mass is important.
mixture.AdjustMoles(Gas.WaterVapor, burnedFuel);
mixture.ReactionResults[GasReaction.Fire] += burnedFuel;
}
energyReleased /= heatScale; // adjust energy to make sure speedup doesn't cause mega temperature rise
if (energyReleased > 0)
{
var newHeatCapacity = atmosphereSystem.GetHeatCapacity(mixture, true);
if (newHeatCapacity > Atmospherics.MinimumHeatCapacity)
mixture.Temperature = ((temperature * oldHeatCapacity + energyReleased) / newHeatCapacity);
}
if (location != null)
{
temperature = mixture.Temperature;
if (temperature > Atmospherics.FireMinimumTemperatureToExist)
{
atmosphereSystem.HotspotExpose(location.GridIndex, location.GridIndices, temperature, mixture.Volume);
}
}
return mixture.ReactionResults[GasReaction.Fire] != 0 ? ReactionResult.Reacting : ReactionResult.NoReaction;
}
}
}

View File

@@ -204,6 +204,30 @@ namespace Content.Shared.Atmos
public const float PlasmaOxygenFullburn = 10f; public const float PlasmaOxygenFullburn = 10f;
public const float PlasmaBurnRateDelta = 9f; public const float PlasmaBurnRateDelta = 9f;
/// <summary>
/// This is calculated to help prevent singlecap bombs (Overpowered tritium/oxygen single tank bombs)
/// </summary>
public const float MinimumTritiumOxyburnEnergy = 143000f;
public const float TritiumBurnOxyFactor = 100f;
public const float TritiumBurnTritFactor = 10f;
public const float FrezonCoolLowerTemperature = 23.15f;
/// <summary>
/// Frezon cools better at higher temperatures.
/// </summary>
public const float FrezonCoolMidTemperature = 373.15f;
public const float FrezonCoolMaximumEnergyModifier = 10f;
/// <summary>
/// Remove X mol of nitrogen for each mol of frezon.
/// </summary>
public const float FrezonNitrogenCoolRatio = 5;
public const float FrezonCoolEnergyReleased = -600e3f;
public const float FrezonCoolRateModifier = 20f;
public const float FrezonProductionMaxEfficiencyTemperature = 73.15f; public const float FrezonProductionMaxEfficiencyTemperature = 73.15f;
/// <summary> /// <summary>
@@ -221,6 +245,16 @@ namespace Content.Shared.Atmos
/// </summary> /// </summary>
public const float FrezonProductionConversionRate = 50f; public const float FrezonProductionConversionRate = 50f;
/// <summary>
/// The maximum portion of the N2O that can decompose each reaction tick. (50%)
/// </summary>
public const float N2ODecompositionRate = 2f;
/// <summary>
/// Divisor for Ammonia Oxygen reaction so that it doesn't happen instantaneously.
/// </summary>
public const float AmmoniaOxygenReactionRate = 10f;
/// <summary> /// <summary>
/// Determines at what pressure the ultra-high pressure red icon is displayed. /// Determines at what pressure the ultra-high pressure red icon is displayed.
/// </summary> /// </summary>

View File

@@ -14,23 +14,31 @@
id: TritiumFire id: TritiumFire
priority: -1 priority: -1
minimumTemperature: 373.149 # Same as Atmospherics.FireMinimumTemperatureToExist minimumTemperature: 373.149 # Same as Atmospherics.FireMinimumTemperatureToExist
enthalpy: 284000 minimumRequirements: # In this case, same as minimum mole count.
reactants: - 0.01 # oxygen
Tritium: 2 - 0 # nitrogen
Oxygen: 1 - 0 # carbon dioxide
products: - 0 # plasma
WaterVapor: 2 - 0.01 # tritium
effects:
- !type:TritiumFireReaction {}
- type: gasReaction - type: gasReaction
id: FrezonCoolant id: FrezonCoolant
priority: 1 priority: 1
minimumTemperature: 23.15 minimumTemperature: 23.15
enthalpy: -600000 minimumRequirements:
reactants: - 0 # oxygen
Frezon: 1 - 0.01 # nitrogen
Nitrogen: 5 - 0 # carbon dioxide
products: - 0 # plasma
NitrousOxide: 6 - 0 # tritium
- 0 # vapor
- 0 # ammonia
- 0 # n2o
- 0.01 # frezon
effects:
- !type:FrezonCoolantReaction {}
- type: gasReaction - type: gasReaction
id: FrezonProduction id: FrezonProduction
@@ -53,22 +61,35 @@
id: AmmoniaOxygenReaction id: AmmoniaOxygenReaction
priority: 2 priority: 2
minimumTemperature: 323.149 minimumTemperature: 323.149
reactants: minimumRequirements:
Ammonia: 2 - 0.01 # oxygen
Oxygen: 2 - 0 # nitrogen
products: - 0 # carbon dioxide
NitrousOxide: 1 - 0 # plasma
WaterVapor: 3 - 0 # tritium
- 0 # vapor
- 0.01 # ammonia
- 0 # n2o
- 0 # frezon
effects:
- !type:AmmoniaOxygenReaction {}
- type: gasReaction - type: gasReaction
id: N2ODecomposition id: N2ODecomposition
priority: 0 priority: 0
minimumTemperature: 850 minimumTemperature: 850
reactants: minimumRequirements:
NitrousOxide: 2 - 0 # oxygen
products: - 0 # nitrogen
Nitrogen: 2 - 0 # carbon dioxide
Oxygen: 1 - 0 # plasma
- 0 # tritium
- 0 # vapor
- 0 # ammonia
- 0.01 # n2o
- 0 # frezon
effects:
- !type:N2ODecompositionReaction {}
#- type: gasReaction #- type: gasReaction
# id: WaterVaporPuddle # id: WaterVaporPuddle