ed_journals/modules/exobiology/models/
spawn_source.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
pub mod spawn_source_star;
pub mod target_planet;
pub mod target_system;

use strum::IntoEnumIterator;

use crate::exobiology::models::spawn_source::spawn_source_star::SpawnSourceStar;
use crate::exobiology::models::spawn_source::target_planet::TargetPlanet;
use crate::exobiology::models::spawn_source::target_system::TargetSystem;
use crate::exobiology::{SpawnCondition, Species};
use crate::galaxy::{AtmosphereDensity, AtmosphereType, Nebula, VolcanismType};
use crate::logs::scan_event::ScanEventParent;

#[derive(Debug)]
pub struct SpawnSource<'a> {
    pub target_system: &'a TargetSystem,
    pub target_planet: &'a TargetPlanet,
}

impl SpawnSource<'_> {
    /// Returns a list of species that could spawn on this spawn source.
    pub fn get_spawnable_species(&self) -> Vec<Species> {
        Species::iter()
            .filter(|species| self.can_spawn_species(species))
            .collect()
    }

    /// Checks if the given species can spawn on this spawn source.
    pub fn can_spawn_species(&self, species: &Species) -> bool {
        species
            .spawn_conditions()
            .iter()
            .all(|condition| self.satisfies_spawn_condition(condition))
    }

    /// Checks if the spawn source satisfies the given condition.
    pub fn satisfies_spawn_condition(&self, condition: &SpawnCondition) -> bool {
        match condition {
            SpawnCondition::MinMeanTemperature(min_temp) => {
                &self.target_planet.surface_temperature >= min_temp
            }
            SpawnCondition::MaxMeanTemperature(max_temp) => {
                &self.target_planet.surface_temperature <= max_temp
            }
            SpawnCondition::NoAtmosphere => {
                self.target_planet.atmosphere.kind == AtmosphereType::None
            }
            SpawnCondition::AnyThinAtmosphere => {
                self.target_planet.atmosphere.density == AtmosphereDensity::Thin
            }
            SpawnCondition::ThinAtmosphere(atmosphere_type) => {
                self.target_planet.atmosphere.density == AtmosphereDensity::Thin
                    && &self.target_planet.atmosphere.kind == atmosphere_type
            }
            SpawnCondition::MinGravity(min_gravity) => {
                &self.target_planet.gravity.as_g() >= min_gravity
            }
            SpawnCondition::MaxGravity(max_gravity) => {
                &self.target_planet.gravity.as_g() <= max_gravity
            }
            SpawnCondition::PlanetClass(planet_class) => &self.target_planet.class == planet_class,
            SpawnCondition::ParentStarClass(star_class) => {
                self.parent_stars().any(|star| &star.class == star_class)
            }
            SpawnCondition::ParentStarLuminosity(star_luminosity) => self
                .parent_stars()
                .any(|star| &star.luminosity == star_luminosity),
            SpawnCondition::MinOrEqualParentStarLuminosity(star_luminosity) => self
                .parent_stars()
                .any(|star| &star.luminosity >= star_luminosity),
            SpawnCondition::SystemContainsPlanetClass(planet_class) => self
                .target_system
                .planet_classes_in_system
                .contains(planet_class),
            SpawnCondition::VolcanismType(volcanism_type) => {
                &self.target_planet.volcanism.kind == volcanism_type
            }
            SpawnCondition::MinDistanceFromParentSun(min_distance) => {
                // TODO not sure, but Eccentricity might need to be taken into account as well
                &(self.target_planet.semi_major_axis / 149597870700.0) >= min_distance
            }
            SpawnCondition::AnyVolcanism => {
                self.target_planet.volcanism.kind != VolcanismType::None
            }
            SpawnCondition::WithinNebulaRange(nebula_range) => {
                &Nebula::closest_to(self.target_system.star_system_position)
                    .distance_to(self.target_system.star_system_position)
                    .as_ly()
                    <= nebula_range
            }
            SpawnCondition::GeologicalSignalsPresent => {
                self.target_planet.geological_signals_present
            }
            SpawnCondition::MaterialPresence(material) => {
                self.target_planet.materials.contains(material)
            }
            SpawnCondition::RockyComposition => {
                let Some(composition) = &self.target_planet.composition else {
                    return false;
                };

                composition.rock > 0.0
            }
            SpawnCondition::IcyComposition => {
                let Some(composition) = &self.target_planet.composition else {
                    return false;
                };

                composition.ice > 0.0
            }
            SpawnCondition::MetalComposition => {
                let Some(composition) = &self.target_planet.composition else {
                    return false;
                };

                composition.metal > 0.0
            }

            SpawnCondition::Special => false,

            SpawnCondition::Any(conditions) => conditions
                .iter()
                .any(|condition| self.satisfies_spawn_condition(condition)),
            SpawnCondition::All(conditions) => conditions
                .iter()
                .all(|condition| self.satisfies_spawn_condition(condition)),
        }
    }

    pub fn parent_stars(&self) -> impl Iterator<Item = &SpawnSourceStar> {
        self.target_planet.parents.iter().filter_map(|parent| {
            if let ScanEventParent::Star(id) = parent {
                return self.target_system.stars_in_system.get(id);
            }

            None
        })
    }
}