use std::ops::Deref;
use indexmap::IndexMap;
use reikland::Ignored;
use serde::{Deserialize, Deserializer, Serialize};
use crate::species::{
base_stats::BaseStats, color::Color, egg_group::EggGroup, evolution::Evolution,
gender_ratio::GenderRatio, growth_rate::GrowthRate, habitat::Habitat, shape::Shape,
};
pub mod base_stats;
pub mod color;
pub mod egg_group;
pub mod evolution;
pub mod gender_ratio;
pub mod growth_rate;
pub mod habitat;
pub mod shape;
pub type EffortValues = BaseStats;
pub type Level = u8;
#[derive(Debug, Clone, Deserialize, Serialize)]
#[cfg_attr(test, serde(deny_unknown_fields))]
pub struct Species {
#[serde(rename = "@id")]
pub id: String,
#[serde(rename = "@id_number")]
pub id_number: u32, #[serde(rename = "@species")]
pub species: String, #[serde(rename = "@form")]
pub form: u8, #[serde(rename = "@real_name")]
pub real_name: String,
#[serde(rename = "@real_form_name")]
pub real_form_name: Option<String>,
#[serde(rename = "@real_category")]
pub real_category: Option<String>,
#[serde(rename = "@real_pokedex_entry")]
pub real_pokedex_entry: Option<String>,
#[serde(rename = "@pokedex_form")]
pub pokedex_form: u8,
#[serde(rename = "@type1")]
pub type1: String, #[serde(rename = "@type2")]
pub type2: Option<String>,
#[serde(rename = "@base_stats")]
pub base_stats: BaseStats,
#[serde(rename = "@evs")]
pub evs: EffortValues,
#[serde(rename = "@base_exp")]
pub base_exp: u16, #[serde(rename = "@growth_rate")]
pub growth_rate: GrowthRate,
#[serde(rename = "@gender_ratio")]
pub gender_ratio: GenderRatio,
#[serde(rename = "@catch_rate")]
pub catch_rate: u8,
#[serde(rename = "@happiness")]
pub happiness: u8,
#[serde(rename = "@moves")]
pub moves: Vec<(Level, String)>,
#[serde(rename = "@tutor_moves")]
pub tutor_moves: Vec<String>,
#[serde(rename = "@egg_moves")]
pub egg_moves: Vec<String>,
#[serde(rename = "@abilities")]
pub abilities: Vec<String>,
#[serde(rename = "@hidden_abilities")]
pub hidden_abilities: Vec<String>,
#[serde(rename = "@wild_item_common")]
pub wild_item_common: Option<String>,
#[serde(rename = "@wild_item_uncommon")]
pub wild_item_uncommon: Option<String>,
#[serde(rename = "@wild_item_rare")]
pub wild_item_rare: Option<String>,
#[serde(rename = "@egg_groups")]
pub egg_groups: Vec<EggGroup>,
#[serde(rename = "@hatch_steps")]
pub hatch_steps: u16, #[serde(rename = "@incense")]
pub incense: Option<String>,
#[serde(rename = "@evolutions")]
pub evolutions: Vec<Evolution>,
#[serde(rename = "@height")]
pub height: u8,
#[serde(rename = "@weight")]
pub weight: u16,
#[serde(rename = "@color")]
pub color: Color,
#[serde(rename = "@shape")]
pub shape: Shape,
#[serde(rename = "@habitat")]
pub habitat: Habitat,
#[serde(rename = "@generation")]
pub generation: u8,
#[serde(rename = "@mega_stone")]
pub mega_stone: Option<String>,
#[serde(rename = "@mega_move")]
pub mega_move: Option<String>,
#[serde(rename = "@unmega_form")]
pub unmega_form: u8,
#[serde(rename = "@mega_message")]
pub mega_message: u8,
#[serde(rename = "@back_sprite_x")]
pub back_sprite_x: i8,
#[serde(rename = "@back_sprite_y")]
pub back_sprite_y: i8,
#[serde(rename = "@front_sprite_x")]
pub front_sprite_x: i8,
#[serde(rename = "@front_sprite_y")]
pub front_sprite_y: i8,
#[serde(rename = "@front_sprite_altitude")]
pub front_sprite_altitude: u8,
#[serde(rename = "@shadow_x")]
pub shadow_x: i8,
#[serde(rename = "@shadow_size")]
pub shadow_size: u8,
#[serde(rename = "@alwaysUseGeneratedSprite")]
pub always_use_generated_sprite: bool,
}
impl Species {
pub fn abilities_iter(&self) -> impl Iterator<Item = &str> {
self.abilities
.iter()
.map(Deref::deref)
.chain(self.hidden_abilities.iter().map(String::as_str))
}
}
#[derive(Debug, Clone, Serialize)]
pub struct SpeciesDex(pub Vec<Species>);
impl<'de> serde::Deserialize<'de> for SpeciesDex {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
reikland::DualKeyVec::<(Species, Ignored)>::deserialize(deserializer)
.map(|reikland::DualKeyVec(mut d)| {
d.sort_by_key(|(s, _)| s.id_number);
d.into_iter()
.map(|(mut s, _)| {
s.type2.take_if(|type2| type2 == &s.type1); s
})
.collect()
})
.map(SpeciesDex)
}
}
#[derive(Debug, Clone, Serialize)]
pub struct SpeciesMap(pub IndexMap<String, Species>);
impl<'de> serde::Deserialize<'de> for SpeciesMap {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
SpeciesDex::deserialize(deserializer)
.map(|SpeciesDex(v)| SpeciesMap(v.into_iter().map(|s| (s.id.clone(), s)).collect()))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test::PathTracked;
const SPECIES_DAT: &[u8] = include_bytes!("../InfiniteFusion/Data/species.dat");
#[test]
fn deserialize_species_dat_vec() {
let PathTracked(dex) = reikland::from_bytes::<PathTracked<SpeciesDex>>(SPECIES_DAT)
.unwrap_or_else(|e| panic!("species.dat failed to deserialize: {e}"));
assert!(!dex.0.is_empty(), "SpeciesDex should contain entries");
assert_bulbasaur(dex.0.first().unwrap());
}
#[test]
fn deserialize_species_dat_map() {
let PathTracked(dex) = reikland::from_bytes::<PathTracked<SpeciesMap>>(SPECIES_DAT)
.unwrap_or_else(|e| panic!("species.dat failed to deserialize: {e}"));
assert!(!dex.0.is_empty(), "SpeciesDex should contain entries");
assert_bulbasaur(dex.0.first().unwrap().1);
}
fn assert_bulbasaur(species: &Species) {
assert_eq!(species.id, "BULBASAUR");
assert_eq!(species.type1, "GRASS");
assert_eq!(species.type2.as_deref(), Some("POISON"));
assert_eq!(species.id_number, 1);
assert_eq!(
species.base_stats,
BaseStats {
hp: 45,
atk: 49,
def: 49,
spa: 65,
spd: 65,
spe: 45,
}
);
assert_eq!(
species.evs,
BaseStats {
hp: 0,
atk: 0,
def: 0,
spa: 1,
spd: 0,
spe: 0,
}
);
assert_eq!(species.evolutions.len(), 1);
let evo = &species.evolutions[0];
assert_eq!(
evo.target,
evolution::EvolutionTarget::Into {
species: String::from("IVYSAUR")
},
);
assert_eq!(evo.kind, evolution::EvolutionKind::Level { level: 16 });
}
}