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}