use strum::IntoEnumIterator;
#[cfg(not(feature = "std"))]
use num_traits::Float;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::{
config::{R, T0C},
gases::{Gas, GasMixture},
reactions::{GasReactionEffectSignature, ReactionResult},
};
#[derive(Debug, Default, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum GasReactionEffectSerialized {
AmmoniaOxygen,
FrezonCoolant,
FrezonProduction,
N2ODecomposition,
PlasmaFire,
TritiumFire,
TritiumFireOld,
N2OFormation,
BZFormation,
HealiumProduction,
NitriumProduction,
NitriumDecomposition,
PluoxiumProduction,
HydrogenFire,
HyperNobliumProduction,
HalonOxygenAbsorbtion,
ZaukerProduction,
ZaukerDecomposition,
ProtoNitrateProduction,
ProtoNitrateConversion,
ProtoNitrateBZConversion,
ZipionFormation,
ArgonFormation,
ArgonAbsorbtion,
#[default]
#[cfg_attr(feature = "serde", serde(skip_serializing))]
NoReaction,
}
pub(crate) const fn dispatch_reaction_effect(
effect: GasReactionEffectSerialized,
) -> GasReactionEffectSignature {
match effect {
GasReactionEffectSerialized::AmmoniaOxygen => ammonia_oxygen_reaction,
GasReactionEffectSerialized::FrezonCoolant => frezon_coolant_reaction,
GasReactionEffectSerialized::FrezonProduction => frezon_production_reaction,
GasReactionEffectSerialized::N2ODecomposition => n2o_decomposition_reaction,
GasReactionEffectSerialized::PlasmaFire => plasma_fire_reaction,
GasReactionEffectSerialized::TritiumFire => tritium_fire_reaction,
GasReactionEffectSerialized::TritiumFireOld => old_tritium_fire_reaction,
GasReactionEffectSerialized::N2OFormation => n2o_formation_reaction,
GasReactionEffectSerialized::BZFormation => bz_formation_reaction,
GasReactionEffectSerialized::HealiumProduction => healium_production_reaction,
GasReactionEffectSerialized::NitriumProduction => nitrium_production_reaction,
GasReactionEffectSerialized::NitriumDecomposition => nitrium_decomposition_reaction,
GasReactionEffectSerialized::PluoxiumProduction => pluoxium_production_reaction,
GasReactionEffectSerialized::HydrogenFire => hydrogen_fire_reaction,
GasReactionEffectSerialized::HyperNobliumProduction => hyper_noblium_production_reaction,
GasReactionEffectSerialized::HalonOxygenAbsorbtion => halon_oxygen_absorbtion_reaction,
GasReactionEffectSerialized::ZaukerProduction => zauker_production_reaction,
GasReactionEffectSerialized::ZaukerDecomposition => zauker_decomposition_reaction,
GasReactionEffectSerialized::ProtoNitrateProduction => proto_nitrate_production_reaction,
GasReactionEffectSerialized::ProtoNitrateConversion => proto_nitrate_conversion_reaction,
GasReactionEffectSerialized::ProtoNitrateBZConversion => {
proto_nitrate_bz_conversion_reaction
}
GasReactionEffectSerialized::ZipionFormation => zipion_formation_reaction,
GasReactionEffectSerialized::ArgonFormation => argon_formation_reaction,
GasReactionEffectSerialized::ArgonAbsorbtion => argon_absorbtion_reaction,
GasReactionEffectSerialized::NoReaction => panic!("Invalid reaction in the config"),
}
}
fn ammonia_oxygen_reaction(mixture: &mut GasMixture) -> ReactionResult {
if check_hyper_noblium(mixture) {
return ReactionResult::NoReaction;
}
let g = &mixture.engine.game;
let n_ammonia = mixture.get_moles(Gas::Ammonia);
let n_oxygen = mixture.get_moles(Gas::Oxygen);
let n_total = mixture.total_moles();
let rate = (n_ammonia * n_oxygen).powi(2) / n_total.powi(4);
let delta_moles = n_ammonia * 2. / g.atmospherics.ammonia_oxygen_reaction_rate * rate;
if delta_moles <= 0. || n_ammonia - delta_moles < 0. {
return ReactionResult::NoReaction;
}
mixture.adjust_moles(Gas::Ammonia, -delta_moles);
mixture.adjust_moles(Gas::Oxygen, -delta_moles);
mixture.adjust_moles(Gas::NitrousOxide, delta_moles * 0.5);
mixture.adjust_moles(Gas::WaterVapor, delta_moles * 1.5);
ReactionResult::Reacting
}
fn frezon_coolant_reaction(mixture: &mut GasMixture) -> ReactionResult {
if check_hyper_noblium(mixture) {
return ReactionResult::NoReaction;
}
let g = &mixture.engine.game;
let i = &mixture.engine.inverses;
let old_geat_capacity = mixture.get_heat_capacity();
let temperature = mixture.temperature();
let mut energy_modifier = 1.;
let mut scale = (temperature - g.atmospherics.frezon_cool_lower_temperature)
* i.frez_cool_mid_minus_lower_temp;
if scale > 1. {
energy_modifier = scale.min(g.atmospherics.frezon_cool_maximum_energy_modifier);
scale = 1.;
}
if scale <= 0. {
return ReactionResult::NoReaction;
}
let initial_nit = mixture.get_moles(Gas::Nitrogen);
let initial_frezon = mixture.get_moles(Gas::Frezon);
let burn_rate = initial_frezon * scale / g.atmospherics.frezon_cool_rate_modifier;
let mut energy_released = 0.;
if burn_rate > g.atmospherics.minimum_heat_capacity {
let nit_amt = (burn_rate * g.atmospherics.frezon_nitrogen_cool_ratio).min(initial_nit);
let frezon_amt = burn_rate.min(initial_frezon);
mixture.adjust_moles(Gas::Nitrogen, -nit_amt);
mixture.adjust_moles(Gas::Frezon, -frezon_amt);
mixture.adjust_moles(Gas::NitrousOxide, nit_amt + frezon_amt);
energy_released = burn_rate * g.atmospherics.frezon_cool_energy_released * energy_modifier;
}
energy_released *= i.heat_scale;
if energy_released >= 0. {
return ReactionResult::NoReaction;
}
let new_heat_capacity = mixture.get_heat_capacity();
if new_heat_capacity > g.atmospherics.minimum_heat_capacity {
mixture.set_temperature(
(temperature * old_geat_capacity + energy_released) / new_heat_capacity,
);
}
ReactionResult::Reacting
}
fn frezon_production_reaction(mixture: &mut GasMixture) -> ReactionResult {
if check_hyper_noblium(mixture) {
return ReactionResult::NoReaction;
}
let g = &mixture.engine.game;
let initial_n2 = mixture.get_moles(Gas::Nitrogen);
let initial_oxy = mixture.get_moles(Gas::Oxygen);
let initial_trit = mixture.get_moles(Gas::Tritium);
let efficiency =
mixture.temperature() / g.atmospherics.frezon_production_max_efficiency_temperature;
let loss = 1. - efficiency;
let catalyst_limit =
initial_n2 * (g.atmospherics.frezon_production_nitrogen_ratio / efficiency);
let oxy_limit = initial_oxy.min(catalyst_limit) / g.atmospherics.frezon_production_trit_ratio;
let trit_burned = oxy_limit.min(initial_trit);
let oxy_burned = trit_burned * g.atmospherics.frezon_production_trit_ratio;
let oxy_conversion = oxy_burned / g.atmospherics.frezon_production_conversion_rate;
let trit_conversion = trit_burned / g.atmospherics.frezon_production_conversion_rate;
let total = oxy_conversion + trit_conversion;
mixture.adjust_moles(Gas::Oxygen, -oxy_conversion);
mixture.adjust_moles(Gas::Tritium, -trit_conversion);
mixture.adjust_moles(Gas::Frezon, total * efficiency);
mixture.adjust_moles(Gas::Nitrogen, total * loss);
ReactionResult::Reacting
}
fn n2o_decomposition_reaction(mixture: &mut GasMixture) -> ReactionResult {
if check_hyper_noblium(mixture) {
return ReactionResult::NoReaction;
}
let g = &mixture.engine.game;
let cache_n2o = mixture.get_moles(Gas::NitrousOxide);
let burned_fuel = cache_n2o / g.atmospherics.n2o_decomposition_rate;
if burned_fuel <= 0. || cache_n2o - burned_fuel < 0. {
return ReactionResult::NoReaction;
}
mixture.adjust_moles(Gas::NitrousOxide, -burned_fuel);
mixture.adjust_moles(Gas::Nitrogen, burned_fuel);
mixture.adjust_moles(Gas::Oxygen, burned_fuel * 0.5);
ReactionResult::Reacting
}
fn plasma_fire_reaction(mixture: &mut GasMixture) -> ReactionResult {
if check_hyper_noblium(mixture) {
return ReactionResult::NoReaction;
}
let g = &mixture.engine.game;
let i = &mixture.engine.inverses;
let mut energy_released = 0.;
let old_heat_capacity = mixture.get_heat_capacity();
let temperature = mixture.temperature();
let mut fire = false;
let temperature_scale = if temperature > g.atmospherics.plasma_upper_temperature {
1.
} else {
(temperature - g.atmospherics.plasma_minimum_burn_temperature)
* i.plasma_upper_minus_min_burn_temp
};
if temperature_scale > 0. {
let oxygen_burn_rate = g.atmospherics.oxygen_burn_rate_base - temperature_scale;
let mut plasma_burn_rate;
let initial_oxygen_moles = mixture.get_moles(Gas::Oxygen);
let initial_plasma_moles = mixture.get_moles(Gas::Plasma);
let oxy_ratio = initial_oxygen_moles / initial_plasma_moles;
let supersaturation = ((oxy_ratio - g.atmospherics.plasma_super_saturation_ends)
* i.plasma_supsat_thres_minus_ends)
.clamp(0., 1.);
if initial_oxygen_moles > initial_plasma_moles * g.atmospherics.plasma_oxygen_fullburn {
plasma_burn_rate = initial_plasma_moles * temperature_scale * i.plasma_burn_rate_delta;
} else {
plasma_burn_rate = temperature_scale
* (initial_oxygen_moles * i.plasma_o2_full_burn)
* i.plasma_burn_rate_delta;
}
if plasma_burn_rate > g.atmospherics.minimum_heat_capacity {
plasma_burn_rate = plasma_burn_rate
.min(initial_plasma_moles.min(initial_oxygen_moles / oxygen_burn_rate));
mixture.set_moles(Gas::Plasma, initial_plasma_moles - plasma_burn_rate);
mixture.set_moles(
Gas::Oxygen,
initial_oxygen_moles - plasma_burn_rate * oxygen_burn_rate,
);
mixture.adjust_moles(Gas::Tritium, plasma_burn_rate * supersaturation);
mixture.adjust_moles(
Gas::CarbonDioxide,
plasma_burn_rate * (1. - supersaturation),
);
energy_released += g.atmospherics.plasma_fire_energy_released * plasma_burn_rate;
energy_released *= i.heat_scale;
fire = true;
}
if energy_released > 0. {
let new_heat_capacity = mixture.get_heat_capacity();
if new_heat_capacity > g.atmospherics.minimum_heat_capacity {
mixture.set_temperature(
(temperature * old_heat_capacity + energy_released) / new_heat_capacity,
);
}
}
}
if fire {
ReactionResult::Reacting
} else {
ReactionResult::NoReaction
}
}
fn tritium_fire_reaction(mixture: &mut GasMixture) -> ReactionResult {
if check_hyper_noblium(mixture) {
return ReactionResult::NoReaction;
}
let g = &mixture.engine.game;
let i = &mixture.engine.inverses;
let mut energy_released = 0.;
let old_heat_capacity = mixture.get_heat_capacity();
let temperature = mixture.temperature();
let mut fire = ReactionResult::NoReaction;
let mut burned_fuel;
let initial_trit = mixture.get_moles(Gas::Tritium);
if mixture.get_moles(Gas::Oxygen) < initial_trit
|| g.atmospherics.tritium_minimum_oxyburn_energy
> (temperature * old_heat_capacity * g.heat_scale)
{
burned_fuel = mixture.get_moles(Gas::Oxygen) * i.tritium_burn_oxy_factor;
if burned_fuel > initial_trit {
burned_fuel = initial_trit;
}
mixture.adjust_moles(Gas::Tritium, -burned_fuel);
mixture.adjust_moles(Gas::Oxygen, -burned_fuel * i.tritium_burn_fuel_ratio);
} else {
burned_fuel = initial_trit.min(mixture.get_moles(Gas::Oxygen) * i.tritium_burn_fuel_ratio)
* i.tritium_burn_trit_factor;
mixture.adjust_moles(Gas::Tritium, -burned_fuel);
mixture.adjust_moles(Gas::Oxygen, -burned_fuel * i.tritium_burn_fuel_ratio);
energy_released += g.atmospherics.hydrogen_fire_energy_released
* burned_fuel
* (g.atmospherics.tritium_burn_trit_factor - 1.);
}
if burned_fuel > 0. {
energy_released += g.atmospherics.hydrogen_fire_energy_released * burned_fuel;
mixture.adjust_moles(Gas::WaterVapor, burned_fuel);
fire = ReactionResult::Reacting;
}
energy_released *= i.heat_scale;
if energy_released > 0. {
let new_heat_capacity = mixture.get_heat_capacity();
if new_heat_capacity > g.atmospherics.minimum_heat_capacity {
mixture.set_temperature(
(temperature * old_heat_capacity + energy_released) / new_heat_capacity,
);
}
}
fire
}
fn old_tritium_fire_reaction(mixture: &mut GasMixture) -> ReactionResult {
if check_hyper_noblium(mixture) {
return ReactionResult::NoReaction;
}
let g = &mixture.engine.game;
let i = &mixture.engine.inverses;
let mut energy_released = 0.;
let old_heat_capacity = mixture.get_heat_capacity();
let temperature = mixture.temperature();
let mut fire = ReactionResult::NoReaction;
let mut burned_fuel;
let initial_trit = mixture.get_moles(Gas::Tritium);
if mixture.get_moles(Gas::Oxygen) < initial_trit
|| g.atmospherics.tritium_minimum_oxyburn_energy
> (temperature * old_heat_capacity * g.heat_scale)
{
burned_fuel = mixture.get_moles(Gas::Oxygen) * i.tritium_burn_oxy_factor;
if burned_fuel > initial_trit {
burned_fuel = initial_trit;
}
mixture.adjust_moles(Gas::Tritium, -burned_fuel);
} else {
burned_fuel = initial_trit;
mixture.set_moles(
Gas::Tritium,
mixture.get_moles(Gas::Tritium) * (1. - i.tritium_burn_trit_factor),
);
mixture.adjust_moles(Gas::Oxygen, -mixture.get_moles(Gas::Tritium));
energy_released += g.atmospherics.hydrogen_fire_energy_released
* burned_fuel
* (g.atmospherics.tritium_burn_trit_factor - 1.);
}
if burned_fuel > 0. {
energy_released += g.atmospherics.hydrogen_fire_energy_released * burned_fuel;
mixture.adjust_moles(Gas::WaterVapor, burned_fuel);
fire = ReactionResult::Reacting;
}
energy_released *= i.heat_scale;
if energy_released > 0. {
let new_heat_capacity = mixture.get_heat_capacity();
if new_heat_capacity > g.atmospherics.minimum_heat_capacity {
mixture.set_temperature(
(temperature * old_heat_capacity + energy_released) / new_heat_capacity,
);
}
}
fire
}
fn n2o_formation_reaction(mixture: &mut GasMixture) -> ReactionResult {
let g = &mixture.engine.game;
let init_oxygen = mixture.get_moles(Gas::Oxygen);
let init_nitrogen = mixture.get_moles(Gas::Nitrogen);
let n2o_added = (init_oxygen * 0.5).min(init_nitrogen);
if init_nitrogen < n2o_added || init_oxygen * 0.5 < n2o_added {
return ReactionResult::NoReaction;
}
mixture.adjust_moles(Gas::NitrousOxide, n2o_added);
mixture.adjust_moles(Gas::Nitrogen, -n2o_added);
mixture.adjust_moles(Gas::Oxygen, -0.5 * n2o_added);
let heat_cap = mixture.get_heat_capacity();
if heat_cap > g.atmospherics.minimum_heat_capacity {
mixture.set_temperature(
mixture.temperature() + (n2o_added * g.atmospherics.n2o_formation_energy),
);
}
ReactionResult::Reacting
}
fn bz_formation_reaction(mixture: &mut GasMixture) -> ReactionResult {
if check_hyper_noblium(mixture) {
return ReactionResult::NoReaction;
}
let g = &mixture.engine.game;
let init_n2o = mixture.get_moles(Gas::NitrousOxide);
let init_plasma = mixture.get_moles(Gas::Plasma);
let environment_efficiency = mixture.engine.container().volume.powi(2)
/ (mixture.total_moles() * R * mixture.temperature());
let ratio_efficiency = (init_n2o / init_plasma).min(1.0);
let bz_formed = (0.01 * ratio_efficiency * environment_efficiency)
.min(init_n2o * 2.5)
.min(init_plasma * 1.25);
let nitrous_oxide_decomposed = (4. * (init_plasma / (init_n2o + init_plasma) - 0.75)).max(0.);
let mut nitrogen_added = 0.;
let mut oxygen_added = 0.;
if nitrous_oxide_decomposed > 0. {
let amount_decomposed = 0.4 * bz_formed * nitrous_oxide_decomposed;
nitrogen_added = amount_decomposed;
oxygen_added = 0.5 * amount_decomposed;
}
let bz_added = bz_formed * (1. - nitrous_oxide_decomposed);
let n2o_removed = 0.4 * bz_formed;
let plasma_removed = 0.8 * bz_formed * (1. - nitrous_oxide_decomposed);
if n2o_removed > init_n2o || plasma_removed > init_plasma {
return ReactionResult::NoReaction;
}
mixture.adjust_moles(Gas::NitrousOxide, -n2o_removed);
mixture.adjust_moles(Gas::Plasma, -plasma_removed);
mixture.adjust_moles(Gas::Nitrogen, nitrogen_added);
mixture.adjust_moles(Gas::Oxygen, oxygen_added);
mixture.adjust_moles(Gas::BZ, bz_added);
let energy_released =
bz_formed * (g.atmospherics.bz_production_energy + nitrous_oxide_decomposed);
let heat_cap = mixture.get_heat_capacity();
if heat_cap > g.atmospherics.minimum_heat_capacity {
mixture.set_temperature((mixture.temperature() * heat_cap + energy_released) / heat_cap);
}
ReactionResult::Reacting
}
fn healium_production_reaction(mixture: &mut GasMixture) -> ReactionResult {
if check_hyper_noblium(mixture) {
return ReactionResult::NoReaction;
}
let g = &mixture.engine.game;
let init_bz = mixture.get_moles(Gas::BZ);
let init_frezon = mixture.get_moles(Gas::Frezon);
let efficiency = (mixture.temperature() * 0.3)
.min(init_frezon * 0.36)
.min(init_bz * 4.);
let bz_removed = efficiency * 0.25;
let frezon_removed = efficiency * 2.75;
let healium_produced = efficiency * 3.;
if efficiency <= 0. || init_frezon < frezon_removed || init_bz < bz_removed {
return ReactionResult::NoReaction;
}
mixture.adjust_moles(Gas::BZ, -bz_removed);
mixture.adjust_moles(Gas::Frezon, -frezon_removed);
mixture.adjust_moles(Gas::Healium, healium_produced);
let energy_released = efficiency * g.atmospherics.healium_production_energy;
let heat_cap = mixture.get_heat_capacity();
if heat_cap > g.atmospherics.minimum_heat_capacity {
mixture.set_temperature((mixture.temperature() * heat_cap + energy_released) / heat_cap);
}
ReactionResult::Reacting
}
fn nitrium_production_reaction(mixture: &mut GasMixture) -> ReactionResult {
if check_hyper_noblium(mixture) {
return ReactionResult::NoReaction;
}
let g = &mixture.engine.game;
let init_tritium = mixture.get_moles(Gas::Tritium);
let init_nitrogen = mixture.get_moles(Gas::Nitrogen);
let init_bz = mixture.get_moles(Gas::BZ);
let efficiency = (mixture.temperature() * (1. / 2984.))
.min(init_bz * 20.)
.min(init_tritium)
.min(init_nitrogen);
let tritium_removed = efficiency;
let nitrogen_removed = efficiency;
let bz_removed = efficiency * 0.05;
let nitrium_produced = efficiency;
if efficiency <= 0.
|| init_tritium < tritium_removed
|| init_nitrogen < nitrogen_removed
|| init_bz < bz_removed
{
return ReactionResult::NoReaction;
}
mixture.adjust_moles(Gas::Tritium, -tritium_removed);
mixture.adjust_moles(Gas::Nitrogen, -nitrogen_removed);
mixture.adjust_moles(Gas::BZ, -bz_removed);
mixture.adjust_moles(Gas::Nitrium, nitrium_produced);
let energy_released = efficiency * g.atmospherics.nitrium_production_energy;
let heat_cap = mixture.get_heat_capacity();
if heat_cap > g.atmospherics.minimum_heat_capacity {
mixture.set_temperature((mixture.temperature() * heat_cap + energy_released) / heat_cap);
}
ReactionResult::Reacting
}
fn nitrium_decomposition_reaction(mixture: &mut GasMixture) -> ReactionResult {
if check_hyper_noblium(mixture) {
return ReactionResult::NoReaction;
}
let g = &mixture.engine.game;
let init_nitrium = mixture.get_moles(Gas::Nitrium);
let efficiency = (mixture.temperature() * (1. / 2984.)).min(init_nitrium);
let nitrium_removed = efficiency;
let h2_produced = efficiency;
let nitrogen_produced = efficiency;
if efficiency <= 0. || init_nitrium < nitrium_removed {
return ReactionResult::NoReaction;
}
mixture.adjust_moles(Gas::Nitrium, -nitrium_removed);
mixture.adjust_moles(g.hydrogen_substitute, h2_produced);
mixture.adjust_moles(Gas::Nitrogen, nitrogen_produced);
let energy_released = efficiency * g.atmospherics.nitrium_decomposition_energy;
let heat_cap = mixture.get_heat_capacity();
if heat_cap > g.atmospherics.minimum_heat_capacity {
mixture.set_temperature(
((mixture.temperature() * heat_cap + energy_released) / heat_cap).min(T0C + 98.8),
);
}
ReactionResult::Reacting
}
fn pluoxium_production_reaction(mixture: &mut GasMixture) -> ReactionResult {
if check_hyper_noblium(mixture) {
return ReactionResult::NoReaction;
}
let g = &mixture.engine.game;
let init_o2 = mixture.get_moles(Gas::Oxygen);
let init_co2 = mixture.get_moles(Gas::CarbonDioxide);
let init_trit = mixture.get_moles(Gas::Tritium);
let produced_amount = 5f32.min(init_co2).min(init_o2 * 2.).min(init_trit * 100.);
if produced_amount <= 0. {
return ReactionResult::NoReaction;
}
let co2_removed = produced_amount;
let oxy_removed = produced_amount * 0.5;
let trit_removed = produced_amount * 0.01;
let pluox_produced = produced_amount;
let hydro_produced = produced_amount * 0.01;
mixture.adjust_moles(Gas::CarbonDioxide, -co2_removed);
mixture.adjust_moles(Gas::Oxygen, -oxy_removed);
mixture.adjust_moles(Gas::Tritium, -trit_removed);
mixture.adjust_moles(Gas::Pluoxium, pluox_produced);
mixture.adjust_moles(g.hydrogen_substitute, hydro_produced);
let energy_released = produced_amount * g.atmospherics.pluoxium_production_energy;
let heat_cap = mixture.get_heat_capacity();
if heat_cap > g.atmospherics.minimum_heat_capacity {
mixture.set_temperature((mixture.temperature() * heat_cap + energy_released) / heat_cap);
}
ReactionResult::Reacting
}
fn hydrogen_fire_reaction(mixture: &mut GasMixture) -> ReactionResult {
if check_hyper_noblium(mixture) {
return ReactionResult::NoReaction;
}
let g = &mixture.engine.game;
let i = &mixture.engine.inverses;
let mut energy_released = 0.;
let old_heat_capacity = mixture.get_heat_capacity();
let temperature = mixture.temperature();
let mut fire = ReactionResult::NoReaction;
let mut burned_fuel;
let initial_h2 = mixture.get_moles(Gas::Hydrogen);
let initial_o2 = mixture.get_moles(Gas::Oxygen);
if initial_o2 < initial_h2
|| g.atmospherics.minimum_hydrogen_oxyburn_energy
> (temperature * old_heat_capacity * g.heat_scale)
{
burned_fuel = initial_o2 / g.atmospherics.hydrogen_burn_oxy_factor;
if burned_fuel > initial_h2 {
burned_fuel = initial_h2;
}
} else {
burned_fuel =
initial_h2.min(initial_o2 * i.tritium_burn_fuel_ratio) * i.tritium_burn_trit_factor;
}
if burned_fuel <= 0. {
return ReactionResult::NoReaction;
}
let oxygen_consumed = burned_fuel * i.tritium_burn_fuel_ratio;
if initial_h2 < burned_fuel || initial_o2 < oxygen_consumed {
return ReactionResult::NoReaction;
}
mixture.adjust_moles(Gas::Hydrogen, -burned_fuel);
mixture.adjust_moles(Gas::Oxygen, -oxygen_consumed);
if burned_fuel > 0. {
energy_released += g.atmospherics.hydrogen_fire_energy_released * burned_fuel;
mixture.adjust_moles(Gas::WaterVapor, burned_fuel);
fire = ReactionResult::Reacting;
}
energy_released *= i.heat_scale;
if energy_released > 0. {
let new_heat_capacity = mixture.get_heat_capacity();
if new_heat_capacity > g.atmospherics.minimum_heat_capacity {
mixture.set_temperature(
(mixture.temperature() * new_heat_capacity + energy_released) / new_heat_capacity,
);
}
}
fire
}
fn hyper_noblium_production_reaction(mixture: &mut GasMixture) -> ReactionResult {
if check_hyper_noblium(mixture) {
return ReactionResult::NoReaction;
}
let g = &mixture.engine.game;
let init_n2 = mixture.get_moles(Gas::Nitrogen);
let init_trit = mixture.get_moles(Gas::Tritium);
let init_bz = mixture.get_moles(Gas::BZ);
let reduction_factor = (init_trit / (init_trit + init_bz)).clamp(0.01, 1.);
let nob_formed = ((init_n2 + init_trit) * 0.01)
.min(init_trit * 1. / (5. * reduction_factor))
.min(init_n2 * 0.1);
if nob_formed < 0.
|| init_trit < 5. * nob_formed * reduction_factor
|| init_n2 < 10. * nob_formed
{
return ReactionResult::NoReaction;
}
mixture.adjust_moles(Gas::Tritium, -5. * nob_formed * reduction_factor);
mixture.adjust_moles(Gas::Nitrogen, -10. * nob_formed);
mixture.adjust_moles(Gas::HyperNoblium, nob_formed);
let energy_released =
nob_formed * (g.atmospherics.hyper_noblium_production_energy / init_bz.max(1.));
let heat_cap = mixture.get_heat_capacity();
if heat_cap > g.atmospherics.minimum_heat_capacity {
mixture.set_temperature((mixture.temperature() * heat_cap + energy_released) / heat_cap);
}
ReactionResult::Reacting
}
fn halon_oxygen_absorbtion_reaction(mixture: &mut GasMixture) -> ReactionResult {
if check_hyper_noblium(mixture) {
return ReactionResult::NoReaction;
}
let g = &mixture.engine.game;
let init_halon = mixture.get_moles(Gas::Halon);
let init_oxy = mixture.get_moles(Gas::Oxygen);
let temperature = mixture.temperature();
let heat_efficiency = (temperature / (g.atmospherics.fire_minimum_temperature_to_exist * 10.))
.min(init_halon)
.min(init_oxy * 0.05);
if heat_efficiency < 0. {
return ReactionResult::NoReaction;
}
mixture.adjust_moles(Gas::Halon, -heat_efficiency);
mixture.adjust_moles(Gas::Oxygen, -heat_efficiency * 20.);
mixture.adjust_moles(Gas::Halon, heat_efficiency * 2.5);
let energy_released = heat_efficiency * g.atmospherics.halon_combustion_energy;
let heat_cap = mixture.get_heat_capacity();
if heat_cap > g.atmospherics.minimum_heat_capacity {
mixture.set_temperature((mixture.temperature() * heat_cap + energy_released) / heat_cap);
}
ReactionResult::Reacting
}
fn zauker_production_reaction(mixture: &mut GasMixture) -> ReactionResult {
if check_hyper_noblium(mixture) {
return ReactionResult::NoReaction;
}
let g = &mixture.engine.game;
let init_hyper_nob = mixture.get_moles(Gas::HyperNoblium);
let init_nitrium = mixture.get_moles(Gas::Nitrium);
let temperature = mixture.temperature();
let heat_efficiency = (temperature * g.atmospherics.zauker_temperature_scale)
.min(init_hyper_nob * 100.)
.min(init_nitrium * 20.);
if heat_efficiency < 0.
|| init_hyper_nob < heat_efficiency * 0.01
|| init_nitrium < heat_efficiency * 0.5
{
return ReactionResult::NoReaction;
}
mixture.adjust_moles(Gas::HyperNoblium, -heat_efficiency * 0.01);
mixture.adjust_moles(Gas::Nitrium, -heat_efficiency * 0.5);
mixture.adjust_moles(Gas::Zauker, heat_efficiency * 0.5);
let energy_released = heat_efficiency * g.atmospherics.zauker_production_energy;
let heat_cap = mixture.get_heat_capacity();
if heat_cap > g.atmospherics.minimum_heat_capacity {
mixture.set_temperature((mixture.temperature() * heat_cap + energy_released) / heat_cap);
}
ReactionResult::Reacting
}
fn zauker_decomposition_reaction(mixture: &mut GasMixture) -> ReactionResult {
if check_hyper_noblium(mixture) {
return ReactionResult::NoReaction;
}
let g = &mixture.engine.game;
let init_zauker = mixture.get_moles(Gas::Zauker);
let init_n2 = mixture.get_moles(Gas::Nitrogen);
let burned_fuel = g
.atmospherics
.zauker_decomposition_max_rate
.min(init_n2)
.min(init_zauker);
if burned_fuel <= 0. || init_zauker < burned_fuel {
return ReactionResult::NoReaction;
}
mixture.adjust_moles(Gas::Zauker, -burned_fuel);
mixture.adjust_moles(Gas::Oxygen, burned_fuel * 0.3);
mixture.adjust_moles(Gas::Nitrogen, burned_fuel * 0.7);
let energy_released = burned_fuel * g.atmospherics.zauker_decomposition_energy;
let heat_cap = mixture.get_heat_capacity();
if heat_cap > g.atmospherics.minimum_heat_capacity {
mixture.set_temperature((mixture.temperature() * heat_cap + energy_released) / heat_cap);
}
ReactionResult::Reacting
}
fn proto_nitrate_production_reaction(mixture: &mut GasMixture) -> ReactionResult {
if check_hyper_noblium(mixture) {
return ReactionResult::NoReaction;
}
let g = &mixture.engine.game;
let init_pluox = mixture.get_moles(Gas::Pluoxium);
let init_h2 = mixture.get_moles(Gas::Hydrogen);
let temperature = mixture.temperature();
let heat_efficiency = (temperature * 0.005)
.min(init_pluox * 5.)
.min(init_h2 * 0.5);
if heat_efficiency <= 0. || init_pluox < heat_efficiency * 0.2 || init_h2 < heat_efficiency * 2.
{
return ReactionResult::NoReaction;
}
mixture.adjust_moles(Gas::Hydrogen, -heat_efficiency * 2.);
mixture.adjust_moles(Gas::Pluoxium, -heat_efficiency * 0.2);
mixture.adjust_moles(Gas::ProtoNitrate, heat_efficiency * 2.2);
let energy_released = heat_efficiency * g.atmospherics.proto_nitrate_production_energy;
let heat_cap = mixture.get_heat_capacity();
if heat_cap > g.atmospherics.minimum_heat_capacity {
mixture.set_temperature((mixture.temperature() * heat_cap + energy_released) / heat_cap);
}
ReactionResult::Reacting
}
fn proto_nitrate_conversion_reaction(mixture: &mut GasMixture) -> ReactionResult {
if check_hyper_noblium(mixture) {
return ReactionResult::NoReaction;
}
let g = &mixture.engine.game;
let init_pn = mixture.get_moles(Gas::ProtoNitrate);
let init_trit = mixture.get_moles(Gas::Tritium);
let init_h2 = mixture.get_moles(Gas::Hydrogen);
let temperature = mixture.temperature();
let common_factor = 0.25 * (temperature * (1. / 250.) - 1.2).sin();
let burned_h2 = init_h2 * common_factor;
let burned_trit = init_trit * -common_factor;
let produced_amount = burned_h2 + burned_trit;
if init_trit < burned_trit && init_h2 < burned_h2 || init_pn < produced_amount * 0.01 {
return ReactionResult::NoReaction;
}
mixture.adjust_moles(Gas::ProtoNitrate, -produced_amount * 0.01);
mixture.adjust_moles(Gas::Plasma, produced_amount * 0.05);
mixture.adjust_moles(Gas::Tritium, burned_h2 * 0.95 - burned_trit);
mixture.adjust_moles(Gas::Hydrogen, burned_trit * 0.95 - burned_h2);
let energy_released = (burned_trit * 0.95 - burned_h2 * 0.95)
* g.atmospherics.proto_nitrate_conversion_energy
+ produced_amount * 2.5;
let heat_cap = mixture.get_heat_capacity();
if heat_cap > g.atmospherics.minimum_heat_capacity {
mixture.set_temperature((mixture.temperature() * heat_cap + energy_released) / heat_cap);
}
ReactionResult::Reacting
}
fn proto_nitrate_bz_conversion_reaction(mixture: &mut GasMixture) -> ReactionResult {
if check_hyper_noblium(mixture) {
return ReactionResult::NoReaction;
}
let g = &mixture.engine.game;
let init_pn = mixture.get_moles(Gas::ProtoNitrate);
let init_bz = mixture.get_moles(Gas::BZ);
let temperature = mixture.temperature();
let consumed_amount = (temperature / 2240. * init_bz * init_pn / (init_bz + init_pn))
.min(init_bz)
.min(init_pn);
if consumed_amount <= 0. || init_bz < consumed_amount {
return ReactionResult::NoReaction;
}
mixture.adjust_moles(Gas::BZ, -consumed_amount);
mixture.adjust_moles(Gas::Nitrogen, consumed_amount * 0.4);
mixture.adjust_moles(Gas::Helium, consumed_amount * 1.6);
mixture.adjust_moles(Gas::Plasma, consumed_amount * 0.8);
let energy_released = consumed_amount * g.atmospherics.proto_nitrate_bz_conversion_energy;
let heat_cap = mixture.get_heat_capacity();
if heat_cap > g.atmospherics.minimum_heat_capacity {
mixture.set_temperature((mixture.temperature() * heat_cap + energy_released) / heat_cap);
}
ReactionResult::Reacting
}
fn zipion_formation_reaction(mixture: &mut GasMixture) -> ReactionResult {
let g = &mixture.engine.game;
let initial_vapor = mixture.get_moles(Gas::WaterVapor);
let initial_plasma = mixture.get_moles(Gas::Plasma);
let consumed_plasma = (initial_plasma / g.atmospherics.zipion_production_conversion_rate)
.min(initial_vapor - (initial_vapor % 9.) / 9.);
let consumed_vapor = consumed_plasma * 9.;
let produced_zipion = consumed_plasma + consumed_vapor;
mixture.adjust_moles(Gas::Plasma, -consumed_plasma);
mixture.adjust_moles(Gas::WaterVapor, -consumed_vapor);
mixture.adjust_moles(Gas::Zipion, produced_zipion);
ReactionResult::Reacting
}
fn argon_formation_reaction(mixture: &mut GasMixture) -> ReactionResult {
let initial_frezon = mixture.get_moles(Gas::Frezon);
let initial_plasma = mixture.get_moles(Gas::Plasma);
let consumed_frezon = initial_frezon.min(initial_plasma * 0.5);
let consumed_plasma = consumed_frezon * 2.;
let produced_argon = consumed_frezon * 3.;
mixture.adjust_moles(Gas::Frezon, -consumed_frezon);
mixture.adjust_moles(Gas::Plasma, -consumed_plasma);
mixture.adjust_moles(Gas::Argon, produced_argon);
ReactionResult::Reacting
}
fn argon_absorbtion_reaction(mixture: &mut GasMixture) -> ReactionResult {
const ABSORBTION_RATE: f32 = 2.;
let mut total_absorbed = 0.;
for gas in Gas::iter() {
if gas == Gas::Argon {
continue;
}
let moles = mixture.get_moles(gas);
if moles <= 0. {
continue;
}
let absorb = ABSORBTION_RATE.min(moles);
mixture.adjust_moles(gas, -absorb);
total_absorbed += absorb;
}
if total_absorbed > 0. {
ReactionResult::StopReactions
} else {
ReactionResult::NoReaction }
}
fn check_hyper_noblium(mixture: &GasMixture) -> bool {
mixture.temperature() > 20. && mixture.get_moles(Gas::HyperNoblium) >= 5.
}