poke_data/models/
species.rs

1use crate::data::link_context::LinkContext;
2use crate::data::linkable::Linkable;
3use crate::models::color::{Color, ColorId};
4use crate::models::egg_group::{EggGroup, EggGroupId};
5use crate::models::generation::{Generation, GenerationId};
6use crate::models::growth_rate::{GrowthRate, GrowthRateId};
7use crate::models::habitat::{Habitat, HabitatId};
8use crate::models::item::{Item, ItemId};
9use crate::models::language::LanguageId;
10use crate::models::localized_names::LocalizedStrings;
11use crate::models::shape::{Shape, ShapeId};
12use crate::models::species::evolution::{Evolution, UnlinkedEvolution};
13use crate::models::version::VersionId;
14use crate::traits::has_identifier::HasIdentifier;
15use crate::traits::has_localized_names::HasLocalizedNames;
16use crate::types::capture_rate::CaptureRate;
17use crate::types::gender_rate::GenderRate;
18use crate::types::language::Language;
19use serde::{Deserialize, Serialize};
20use std::collections::HashMap;
21use std::sync::Arc;
22
23pub mod evolution;
24
25pub type SpeciesId = u16;
26
27#[derive(Debug)]
28pub struct Species {
29    pub id: SpeciesId,
30    pub identifier: String,
31    pub names: LocalizedStrings,
32    pub flavor_texts: SpeciesFlavorTexts,
33    pub form_description: Option<LocalizedStrings>,
34    pub generation: Arc<Generation>,
35    pub evolves_from_species_id: Option<SpeciesId>,
36    pub baby_trigger_item: Option<Arc<Item>>,
37    pub evolutions: Vec<Evolution>,
38    pub color: Arc<Color>,
39    pub shape: Arc<Shape>,
40    pub habitat: Option<Arc<Habitat>>,
41    pub gender_rate: GenderRate,
42    pub capture_rate: CaptureRate,
43    pub base_happiness: u8,
44    pub is_baby: bool,
45    pub hatch_counter: u8,
46    pub has_gender_differences: bool,
47    pub growth_rate: Arc<GrowthRate>,
48    pub forms_switchable: bool,
49    pub is_legendary: bool,
50    pub is_mythical: bool,
51    pub order: u16,
52    pub egg_groups: Vec<Arc<EggGroup>>,
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct UnlinkedSpecies {
57    pub id: SpeciesId,
58    pub identifier: String,
59    pub names: LocalizedStrings,
60    pub flavor_texts: SpeciesFlavorTexts,
61    pub form_description: Option<LocalizedStrings>,
62    pub generation_id: GenerationId,
63    pub evolves_from_species_id: Option<SpeciesId>,
64    pub baby_trigger_item_id: Option<ItemId>,
65    pub evolutions: Vec<UnlinkedEvolution>,
66    pub color_id: ColorId,
67    pub shape_id: ShapeId,
68    pub habitat_id: Option<HabitatId>,
69    pub gender_rate: GenderRate,
70    pub capture_rate: CaptureRate,
71    pub base_happiness: u8,
72    pub is_baby: bool,
73    pub hatch_counter: u8,
74    pub has_gender_differences: bool,
75    pub growth_rate_id: GrowthRateId,
76    pub forms_switchable: bool,
77    pub is_legendary: bool,
78    pub is_mythical: bool,
79    pub order: u16,
80    pub egg_group_ids: Vec<EggGroupId>,
81}
82
83impl Linkable for UnlinkedSpecies {
84    type Linked = Arc<Species>;
85
86    fn link(&self, context: &LinkContext) -> Self::Linked {
87        let generation = context
88            .generations
89            .get(&self.generation_id)
90            .unwrap_or_else(|| {
91                panic!(
92                    "No generation '{}' found for species '{}'",
93                    self.generation_id, self.id
94                )
95            })
96            .clone();
97
98        let baby_trigger_item = self.baby_trigger_item_id.map(|id| {
99            context
100                .items
101                .get(&id)
102                .unwrap_or_else(|| {
103                    panic!(
104                        "No baby trigger item '{}' found for species '{}'",
105                        id, self.id
106                    )
107                })
108                .clone()
109        });
110
111        let evolutions = self
112            .evolutions
113            .iter()
114            .map(|evolution| evolution.link(context))
115            .collect();
116
117        let color = context
118            .colors
119            .get(&self.color_id)
120            .unwrap_or_else(|| {
121                panic!(
122                    "No color '{}' found for species '{}'",
123                    self.color_id, self.id
124                )
125            })
126            .clone();
127
128        let shape = context
129            .shapes
130            .get(&self.shape_id)
131            .unwrap_or_else(|| {
132                panic!(
133                    "No shape '{}' found for species '{}'",
134                    self.shape_id, self.id
135                )
136            })
137            .clone();
138
139        let habitat = self.habitat_id.map(|id| {
140            context
141                .habitats
142                .get(&id)
143                .unwrap_or_else(|| panic!("No habitat '{}' found for species '{}'", id, self.id))
144                .clone()
145        });
146
147        let growth_rate = context
148            .growth_rates
149            .get(&self.growth_rate_id)
150            .unwrap_or_else(|| {
151                panic!(
152                    "No growth rate '{}' found for species '{}'",
153                    self.growth_rate_id, self.id
154                )
155            })
156            .clone();
157
158        let egg_groups = self
159            .egg_group_ids
160            .iter()
161            .map(|egg_group_id| {
162                context
163                    .egg_groups
164                    .get(egg_group_id)
165                    .unwrap_or_else(|| {
166                        panic!(
167                            "No egg group '{}' found for pokemon '{}'",
168                            egg_group_id, self.id
169                        )
170                    })
171                    .clone()
172            })
173            .collect();
174
175        let species = Species {
176            id: self.id,
177            identifier: self.identifier.clone(),
178            names: self.names.clone(),
179            flavor_texts: self.flavor_texts.clone(),
180            form_description: self.form_description.clone(),
181            generation,
182            evolves_from_species_id: self.evolves_from_species_id,
183            baby_trigger_item,
184            evolutions,
185            color,
186            shape,
187            habitat,
188            gender_rate: self.gender_rate,
189            capture_rate: self.capture_rate,
190            base_happiness: self.base_happiness,
191            is_baby: self.is_baby,
192            hatch_counter: self.hatch_counter,
193            has_gender_differences: self.has_gender_differences,
194            growth_rate,
195            forms_switchable: self.forms_switchable,
196            is_legendary: self.is_legendary,
197            is_mythical: self.is_mythical,
198            order: self.order,
199            egg_groups,
200        };
201
202        Arc::new(species)
203    }
204}
205
206#[derive(Debug, Clone, Serialize, Deserialize)]
207pub struct SpeciesFlavorTexts(HashMap<LanguageId, Vec<SpeciesFlavorText>>);
208
209impl SpeciesFlavorTexts {
210    pub fn new(texts: HashMap<LanguageId, Vec<SpeciesFlavorText>>) -> Self {
211        Self(texts)
212    }
213
214    pub fn get_by_language(&self, language: Language) -> Option<&Vec<SpeciesFlavorText>> {
215        let language_id = language as LanguageId;
216        if let Some(target) = self.0.get(&language_id) {
217            return Some(target);
218        }
219
220        let default_language_id = Language::default() as LanguageId;
221        if let Some(default) = self.0.get(&default_language_id) {
222            return Some(default);
223        }
224
225        None
226    }
227}
228
229#[derive(Debug, Clone, Serialize, Deserialize)]
230pub struct SpeciesFlavorText {
231    pub version_id: VersionId,
232    pub flavor_text: String,
233}
234
235impl HasIdentifier for Species {
236    fn identifier(&self) -> &str {
237        &self.identifier
238    }
239}
240
241impl HasLocalizedNames for Species {
242    fn localized_names(&self) -> &LocalizedStrings {
243        &self.names
244    }
245}