Skip to main content

atmosim/
config.rs

1//! Handles game config. If you want to add new variable, put it into `GameConfig` struct,
2//! then put its Wizden (or whatever server uses it) value into `GameConfig`'s Default implementation
3//! Oh, and if the variable you add can be transformed for performance, add it into `ConfigCache`
4//! and its Default impl. Though do it wisely as extra memory usage can degrade performance.
5// Ideally we can make yet another struct that drops unneeded variables but lazy ^
6//! Finally, if you want to create/edit a preset, just create/edit its function in impl `GameConfig`.
7
8use core::cmp::Reverse;
9
10use arrayvec::ArrayVec;
11#[cfg(feature = "serde")]
12use serde::{Deserialize, Serialize};
13
14use crate::{
15    Atmosim,
16    gases::{Gas, GasProperties, MoleProperties, gases},
17    maxcap::GasContainerPrototype,
18    reactions::{GasReaction, GasReactionPrototype, ReactionBuffer},
19};
20
21// Basic atmos constants no server would change
22pub(crate) const R: f32 = 8.314_463; // anything more precise is just truncated anyway
23pub(crate) const ONE_ATMOSPHERE: f32 = 101.325;
24pub(crate) const TCMB: f32 = 2.7;
25pub(crate) const T0C: f32 = 273.15;
26pub(crate) const MIN_MOLES: f32 = 0.000_000_05;
27
28#[derive(Debug)]
29#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
30#[cfg_attr(feature = "serde", serde(default))]
31pub struct GameConfig {
32    pub atmospherics: Atmospherics,
33
34    // CVars
35    pub heat_scale: f32,
36
37    /// goob and mono have some reactions from funky that should produce hydrogen.
38    /// they, however, don't have hydrogen. so they produce water vapour instead.
39    /// yes, this field is giga spaghetti but still better than copy-pasting.
40    pub hydrogen_substitute: Gas,
41
42    pub gas_properties: GasProperties,
43    pub container: GasContainerPrototype,
44    #[cfg_attr(feature = "serde", serde(flatten))]
45    pub reactions: ReactionBuffer,
46}
47
48// Default fields in structs when
49impl Default for GameConfig {
50    fn default() -> Self {
51        Self {
52            atmospherics: Atmospherics::default(),
53            // cvars
54            heat_scale: 8.,
55
56            hydrogen_substitute: Gas::Hydrogen,
57
58            gas_properties: GasProperties::default(),
59            reactions: ReactionBuffer::default(),
60            container: GasContainerPrototype::default(),
61        }
62    }
63}
64
65#[derive(Debug)]
66#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
67#[cfg_attr(feature = "serde", serde(default))]
68pub struct Atmospherics {
69    pub tmax: f32,
70    pub minimum_heat_capacity: f32,
71
72    pub ammonia_oxygen_reaction_rate: f32,
73
74    pub n2o_decomposition_rate: f32,
75
76    pub frezon_cool_lower_temperature: f32,
77    pub frezon_cool_mid_temperature: f32,
78    pub frezon_cool_maximum_energy_modifier: f32,
79    pub frezon_cool_rate_modifier: f32,
80    pub frezon_nitrogen_cool_ratio: f32,
81    pub frezon_cool_energy_released: f32,
82
83    pub frezon_production_max_efficiency_temperature: f32,
84    pub frezon_production_nitrogen_ratio: f32,
85    pub frezon_production_trit_ratio: f32,
86    pub frezon_production_conversion_rate: f32,
87
88    pub fire_minimum_temperature_to_exist: f32,
89    pub oxygen_burn_rate_base: f32,
90
91    pub plasma_upper_temperature: f32,
92    pub plasma_minimum_burn_temperature: f32,
93    pub plasma_super_saturation_ends: f32,
94    pub plasma_super_saturation_threshold: f32,
95    pub plasma_oxygen_fullburn: f32,
96    pub plasma_burn_rate_delta: f32,
97    pub plasma_fire_energy_released: f32,
98
99    pub tritium_minimum_oxyburn_energy: f32,
100    pub tritium_burn_oxy_factor: f32,
101    pub tritium_burn_fuel_ratio: f32,
102    pub tritium_burn_trit_factor: f32,
103    pub hydrogen_fire_energy_released: f32,
104
105    // goob
106    pub n2o_formation_energy: f32,
107
108    // funky and others
109    pub bz_production_energy: f32,
110    pub healium_production_energy: f32,
111    pub nitrium_production_energy: f32,
112    pub nitrium_decomposition_energy: f32,
113    pub pluoxium_production_energy: f32,
114
115    // funky only
116    pub minimum_hydrogen_oxyburn_energy: f32,
117    pub hydrogen_burn_oxy_factor: f32,
118    pub hyper_noblium_production_energy: f32,
119    pub halon_combustion_energy: f32,
120    pub zauker_production_energy: f32,
121    pub zauker_temperature_scale: f32,
122    pub zauker_decomposition_energy: f32,
123    pub zauker_decomposition_max_rate: f32,
124    pub proto_nitrate_production_energy: f32,
125    pub proto_nitrate_conversion_energy: f32,
126    pub proto_nitrate_bz_conversion_energy: f32,
127
128    // klovn
129    pub zipion_production_conversion_rate: f32,
130}
131
132impl Default for Atmospherics {
133    fn default() -> Self {
134        let sup_sat_thr = 96.;
135        Self {
136            tmax: 262_144.,
137            minimum_heat_capacity: 0.0003,
138
139            ammonia_oxygen_reaction_rate: 2.,
140
141            n2o_decomposition_rate: 2.,
142
143            frezon_cool_lower_temperature: 23.15,
144            frezon_cool_mid_temperature: 373.15,
145
146            frezon_cool_maximum_energy_modifier: 10.,
147            frezon_cool_rate_modifier: 20.,
148            frezon_nitrogen_cool_ratio: 5.,
149            frezon_cool_energy_released: -600e3,
150            frezon_production_max_efficiency_temperature: 73.15,
151            frezon_production_nitrogen_ratio: 10.,
152            frezon_production_trit_ratio: 50.,
153            frezon_production_conversion_rate: 50.,
154            fire_minimum_temperature_to_exist: T0C + 100.,
155            oxygen_burn_rate_base: 1.4,
156
157            plasma_upper_temperature: 1370. + T0C,
158            plasma_minimum_burn_temperature: 100. + T0C,
159            plasma_super_saturation_ends: sup_sat_thr / 3.,
160            plasma_super_saturation_threshold: sup_sat_thr,
161            plasma_oxygen_fullburn: 10.,
162            plasma_burn_rate_delta: 9.,
163            plasma_fire_energy_released: 160e3,
164
165            tritium_minimum_oxyburn_energy: 143_000.,
166            tritium_burn_oxy_factor: 100.,
167            tritium_burn_fuel_ratio: 2.,
168            tritium_burn_trit_factor: 10.,
169            hydrogen_fire_energy_released: 284e3,
170
171            n2o_formation_energy: 10e3,
172            bz_production_energy: 80e3,
173            healium_production_energy: 9e3,
174
175            nitrium_production_energy: -100e3,
176            nitrium_decomposition_energy: 30e3,
177
178            pluoxium_production_energy: 250.,
179
180            minimum_hydrogen_oxyburn_energy: 143_000.,
181            hydrogen_burn_oxy_factor: 100.,
182
183            hyper_noblium_production_energy: 2e7,
184
185            halon_combustion_energy: -2500.,
186            zauker_production_energy: 5000.,
187            zauker_temperature_scale: 5e-6,
188            zauker_decomposition_energy: 460.,
189            zauker_decomposition_max_rate: 20.,
190
191            proto_nitrate_production_energy: 650.,
192            proto_nitrate_conversion_energy: 2000.,
193            proto_nitrate_bz_conversion_energy: -10_000.,
194
195            zipion_production_conversion_rate: 40.,
196        }
197    }
198}
199
200impl Atmosim {
201    #[must_use]
202    pub fn new(mut game: GameConfig) -> Self {
203        // init reactions
204        if let ReactionBuffer::Protos(protos) = game.reactions {
205            let mut reactions = protos
206                .iter()
207                .map(|&proto| GasReaction::cache(proto))
208                .collect::<ArrayVec<_, _>>();
209
210            reactions.sort_unstable_by_key(|reaction| Reverse(reaction.priority)); // probably needs some tweaking to perfectly emulate C#
211            game.reactions = ReactionBuffer::Inited(reactions);
212        }
213
214        // inverse stuff
215        let inverses = Inverses {
216            heat_scale: 1. / game.heat_scale,
217            frez_cool_mid_minus_lower_temp: 1.
218                / (game.atmospherics.frezon_cool_mid_temperature
219                    - game.atmospherics.frezon_cool_lower_temperature),
220            plasma_upper_minus_min_burn_temp: 1.
221                / (game.atmospherics.plasma_upper_temperature
222                    - game.atmospherics.plasma_minimum_burn_temperature),
223            plasma_supsat_thres_minus_ends: 1.
224                / (game.atmospherics.plasma_super_saturation_threshold
225                    - game.atmospherics.plasma_super_saturation_ends),
226            plasma_burn_rate_delta: 1. / game.atmospherics.plasma_burn_rate_delta,
227            plasma_o2_full_burn: 1. / game.atmospherics.plasma_oxygen_fullburn,
228            tritium_burn_fuel_ratio: 1. / game.atmospherics.tritium_burn_fuel_ratio,
229            tritium_burn_trit_factor: 1. / game.atmospherics.tritium_burn_trit_factor,
230            tritium_burn_oxy_factor: 1. / game.atmospherics.tritium_burn_oxy_factor,
231        };
232
233        // divide all HCs by heat scale
234        for (_, property) in &mut game.gas_properties.0 {
235            property.specific_heat *= inverses.heat_scale;
236        }
237
238        Self { inverses, game }
239    }
240}
241
242impl GameConfig {
243    #[must_use]
244    pub fn wizden_canister() -> Self {
245        Self {
246            atmospherics: Atmospherics {
247                hydrogen_fire_energy_released: 284e4,
248                ..Default::default()
249            },
250            container: GasContainerPrototype::default_canister(),
251            ..Default::default()
252        }
253    }
254
255    #[must_use]
256    pub fn wizden_pmr() -> Self {
257        Self {
258            atmospherics: Atmospherics {
259                hydrogen_fire_energy_released: 284e4,
260                ..Default::default()
261            },
262            container: GasContainerPrototype::old_default_tank(),
263            ..Default::default()
264        }
265    }
266
267    #[must_use]
268    pub fn goob() -> Self {
269        Self {
270            atmospherics: Atmospherics {
271                tmax: 262_144_000_000.,
272                ..Default::default()
273            },
274            hydrogen_substitute: Gas::WaterVapor,
275            reactions: ReactionBuffer::Protos(
276                [
277                    GasReactionPrototype::plasma_fire(),
278                    GasReactionPrototype::old_tritium_fire(),
279                    GasReactionPrototype::frezon_coolant(),
280                    GasReactionPrototype::frezon_production(),
281                    GasReactionPrototype::ammonia_oxygen(),
282                    GasReactionPrototype::n2o_decomposition(),
283                    GasReactionPrototype::funky_bz_formation(),
284                    GasReactionPrototype::goob_n2o_formation(),
285                    GasReactionPrototype::funky_nitrium_production(),
286                    GasReactionPrototype::funky_nitrium_decomposition(),
287                    GasReactionPrototype::funky_pluoxium_production(),
288                ]
289                .into_iter()
290                .collect(),
291            ),
292            container: GasContainerPrototype::old_default_tank(),
293            ..Default::default()
294        }
295    }
296
297    #[must_use]
298    pub fn frontier() -> Self {
299        let sup_sat_thr = 30.;
300        Self {
301            atmospherics: Atmospherics {
302                plasma_super_saturation_threshold: sup_sat_thr,
303                plasma_super_saturation_ends: sup_sat_thr / 3.,
304                plasma_upper_temperature: 700.,
305                ..Default::default()
306            },
307            reactions: ReactionBuffer::Protos(
308                [
309                    GasReactionPrototype::plasma_fire(),
310                    GasReactionPrototype::frontier_tritium_fire(),
311                    GasReactionPrototype::frezon_coolant(),
312                    GasReactionPrototype::frezon_production(),
313                    GasReactionPrototype::ammonia_oxygen(),
314                    GasReactionPrototype::n2o_decomposition(),
315                ]
316                .into_iter()
317                .collect(),
318            ),
319            container: GasContainerPrototype::old_default_tank(),
320            ..Default::default()
321        }
322    }
323
324    #[must_use]
325    pub fn monolith() -> Self {
326        Self {
327            hydrogen_substitute: Gas::WaterVapor,
328            reactions: ReactionBuffer::Protos(
329                [
330                    GasReactionPrototype::plasma_fire(),
331                    GasReactionPrototype::frontier_tritium_fire(),
332                    GasReactionPrototype::frezon_coolant(),
333                    GasReactionPrototype::frezon_production(),
334                    GasReactionPrototype::ammonia_oxygen(),
335                    GasReactionPrototype::n2o_decomposition(),
336                    GasReactionPrototype::funky_bz_formation(),
337                    GasReactionPrototype::mono_nitrium_production(),
338                    GasReactionPrototype::funky_nitrium_decomposition(),
339                    GasReactionPrototype::funky_pluoxium_production(),
340                ]
341                .into_iter()
342                .collect(),
343            ),
344            gas_properties: GasProperties(gases! {
345                // for some reason on mono healium's specific heat is 20
346                Gas::Healium => MoleProperties { specific_heat: 20., mass: 15. },
347                other => GasProperties::default().0[other] // other gases are the same
348            }),
349            container: GasContainerPrototype {
350                volume: 10.,
351                ..GasContainerPrototype::old_default_tank()
352            },
353            ..Self::frontier() // inherits frontier
354        }
355    }
356    #[must_use]
357    pub fn funky() -> Self {
358        Self {
359            atmospherics: Atmospherics {
360                tmax: 262_144_000.,
361                ..Default::default()
362            },
363            reactions: ReactionBuffer::Protos(
364                [
365                    GasReactionPrototype::plasma_fire(),
366                    GasReactionPrototype::tritium_fire(),
367                    GasReactionPrototype::frezon_coolant(),
368                    GasReactionPrototype::frezon_production(),
369                    GasReactionPrototype::ammonia_oxygen(),
370                    GasReactionPrototype::n2o_decomposition(),
371                    GasReactionPrototype::funky_bz_formation(),
372                    GasReactionPrototype::funky_nitrium_production(),
373                    GasReactionPrototype::funky_nitrium_decomposition(),
374                    GasReactionPrototype::funky_pluoxium_production(),
375                    GasReactionPrototype::funky_hydrogen_fire(),
376                    GasReactionPrototype::funky_hyper_noblium_production(),
377                    GasReactionPrototype::funky_halon_oxygen_absorbtion(),
378                    GasReactionPrototype::funky_zauker_production(),
379                    GasReactionPrototype::funky_zauker_decomposition(),
380                    GasReactionPrototype::funky_proto_nitrate_production(),
381                    GasReactionPrototype::funky_proto_nitrate_conversion(),
382                    GasReactionPrototype::funky_proto_nitrate_bz_conversion(),
383                ]
384                .into_iter()
385                .collect(),
386            ),
387            container: GasContainerPrototype::old_default_tank(),
388            ..Default::default()
389        }
390    }
391    // just funky as far as i can tell, except one number
392    #[must_use]
393    pub fn starlight() -> Self {
394        Self {
395            atmospherics: Atmospherics {
396                hydrogen_fire_energy_released: 284e4,
397                ..Self::funky().atmospherics
398            },
399            ..Self::funky()
400        }
401    }
402    /// basically wizden PMR but with extra reactions
403    #[must_use]
404    pub fn klovn() -> Self {
405        Self {
406            reactions: ReactionBuffer::Protos(
407                [
408                    GasReactionPrototype::plasma_fire(),
409                    GasReactionPrototype::tritium_fire(),
410                    GasReactionPrototype::frezon_coolant(),
411                    GasReactionPrototype::frezon_production(),
412                    GasReactionPrototype::ammonia_oxygen(),
413                    GasReactionPrototype::n2o_decomposition(),
414                    GasReactionPrototype::klovn_zipion_formation(),
415                    GasReactionPrototype::klovn_argon_formation(),
416                    GasReactionPrototype::klovn_argon_absorbtion(),
417                ]
418                .into_iter()
419                .collect(),
420            ),
421            ..Self::wizden_pmr()
422        }
423    }
424}
425
426// Stuff that is commonly used as denominator, because division is expensive
427#[derive(Debug)]
428pub(crate) struct Inverses {
429    pub heat_scale: f32,
430
431    pub frez_cool_mid_minus_lower_temp: f32,
432
433    pub plasma_upper_minus_min_burn_temp: f32,
434    pub plasma_supsat_thres_minus_ends: f32,
435    pub plasma_burn_rate_delta: f32,
436    pub plasma_o2_full_burn: f32,
437
438    pub tritium_burn_fuel_ratio: f32,
439    pub tritium_burn_trit_factor: f32,
440    pub tritium_burn_oxy_factor: f32,
441}