use anyhow_ext::{ensure, Result}; use hashbrown::HashSet; use std::fmt::Debug; use std::sync::Arc; use crate::static_data::Statistic; use crate::static_data::TypeIdentifier; use crate::static_data::{Ability, StaticStatisticSet}; use crate::static_data::{AbilityIndex, LearnableMoves}; use crate::Random; use crate::{StringKey, VecExt}; /// A form is a variant of a specific species. A species always has at least one form, but can have /// many more. pub trait Form: Debug { /// The name of the form. fn name(&self) -> &StringKey; /// The height of the form in meters. fn height(&self) -> f32; /// The weight of the form in kilograms. fn weight(&self) -> f32; /// The base amount of experience that is gained when beating a Pokemon with this form. fn base_experience(&self) -> u32; /// The normal types a Pokemon with this form has. fn types(&self) -> &Vec; /// The inherent values of a form of species that are used for the stats of a Pokemon. fn base_stats(&self) -> &Arc>; /// The possible abilities a Pokemon with this form can have. fn abilities(&self) -> &Vec; /// The possible hidden abilities a Pokemon with this form can have. fn hidden_abilities(&self) -> &Vec; /// The moves a Pokemon with this form can learn. fn moves(&self) -> &Arc; /// Arbitrary flags can be set on a form for scripting use. fn flags(&self) -> &HashSet; /// Get a type of the move at a certain index. fn get_type(&self, index: usize) -> Result; /// Gets a single base stat value. fn get_base_stat(&self, stat: Statistic) -> u16; /// Find the index of an ability that can be on this form. fn find_ability_index(&self, ability: &dyn Ability) -> Option; /// Gets an ability from the form. fn get_ability(&self, index: AbilityIndex) -> Result<&StringKey>; /// Gets a random ability from the form. fn get_random_ability(&self, rand: &mut Random) -> Result<&StringKey>; /// Gets a random hidden ability from the form. fn get_random_hidden_ability(&self, rand: &mut Random) -> Result<&StringKey>; /// Check if the form has a specific flag set. fn has_flag(&self, key: &StringKey) -> bool; /// Arbitrary flags that can be applied to the move. fn has_flag_by_hash(&self, key_hash: u32) -> bool; /// Check if two forms are equal. fn eq(&self, other: &dyn Form) -> bool; } /// A form is a variant of a specific species. A species always has at least one form, but can have /// many more. #[derive(Debug)] pub struct FormImpl { /// The name of the form. name: StringKey, /// The height of the form in meters. height: f32, /// The weight of the form in kilograms. weight: f32, /// The base amount of experience that is gained when beating a Pokemon with this form. base_experience: u32, /// The normal types a Pokemon with this form has. types: Vec, /// The inherent values of a form of species that are used for the stats of a Pokemon. base_stats: Arc>, /// The possible abilities a Pokemon with this form can have. abilities: Vec, /// The possible hidden abilities a Pokemon with this form can have. hidden_abilities: Vec, /// The moves a Pokemon with this form can learn. moves: Arc, /// Arbitrary flags can be set on a form for scripting use. flags: HashSet, } impl FormImpl { /// Instantiates a new form. pub fn new( name: &StringKey, height: f32, weight: f32, base_experience: u32, types: Vec, base_stats: Arc>, abilities: Vec, hidden_abilities: Vec, moves: Arc, flags: HashSet, ) -> Self { Self { name: name.clone(), height, weight, base_experience, types, base_stats, abilities, hidden_abilities, moves, flags, } } } impl Form for FormImpl { /// The name of the form. fn name(&self) -> &StringKey { &self.name } /// The height of the form in meters. fn height(&self) -> f32 { self.height } /// The weight of the form in kilograms. fn weight(&self) -> f32 { self.weight } /// The base amount of experience that is gained when beating a Pokemon with this form. fn base_experience(&self) -> u32 { self.base_experience } /// The normal types a Pokemon with this form has. fn types(&self) -> &Vec { &self.types } /// The inherent values of a form of species that are used for the stats of a Pokemon. fn base_stats(&self) -> &Arc> { &self.base_stats } /// The possible abilities a Pokemon with this form can have. fn abilities(&self) -> &Vec { &self.abilities } /// The possible hidden abilities a Pokemon with this form can have. fn hidden_abilities(&self) -> &Vec { &self.hidden_abilities } /// The moves a Pokemon with this form can learn. fn moves(&self) -> &Arc { &self.moves } /// Arbitrary flags can be set on a form for scripting use. fn flags(&self) -> &HashSet { &self.flags } /// Get a type of the move at a certain index. fn get_type(&self, index: usize) -> Result { Ok(*self.types.get_res(index)?) } /// Gets a single base stat value. fn get_base_stat(&self, stat: Statistic) -> u16 { self.base_stats.get_stat(stat) } /// Find the index of an ability that can be on this form. fn find_ability_index(&self, ability: &dyn Ability) -> Option { for (index, a) in self.abilities.iter().enumerate() { if a == ability.name() { return Some(AbilityIndex { hidden: false, index: index as u8, }); } } for (index, a) in self.hidden_abilities.iter().enumerate() { if a == ability.name() { return Some(AbilityIndex { hidden: true, index: index as u8, }); } } None } /// Gets an ability from the form. fn get_ability(&self, index: AbilityIndex) -> Result<&StringKey> { if index.hidden { Ok(self.hidden_abilities.get_res(index.index as usize)?) } else { Ok(self.abilities.get_res(index.index as usize)?) } } /// Gets a random ability from the form. fn get_random_ability(&self, rand: &mut Random) -> Result<&StringKey> { ensure!(!self.abilities.is_empty(), "No abilities on form"); self.abilities .get_res(rand.get_between_unsigned(0, self.abilities.len() as u32) as usize) } /// Gets a random hidden ability from the form. fn get_random_hidden_ability(&self, rand: &mut Random) -> Result<&StringKey> { ensure!(!self.hidden_abilities.is_empty(), "No hidden abilities on form"); self.hidden_abilities .get_res(rand.get_between_unsigned(0, self.hidden_abilities.len() as u32) as usize) } /// Check if the form has a specific flag set. fn has_flag(&self, key: &StringKey) -> bool { self.flags.contains(key) } fn has_flag_by_hash(&self, key_hash: u32) -> bool { self.flags.contains::(&key_hash) } fn eq(&self, other: &dyn Form) -> bool { std::ptr::eq(self, other as *const dyn Form as *const Self) } } #[cfg(test)] #[allow(clippy::indexing_slicing)] #[allow(clippy::unwrap_used)] pub(crate) mod tests { use super::*; mockall::mock! { #[derive(Debug)] pub Form {} impl Form for Form { fn name(&self) -> &StringKey; fn height(&self) -> f32; fn weight(&self) -> f32; fn base_experience(&self) -> u32; fn types(&self) -> &Vec; fn base_stats(&self) -> &Arc>; fn abilities(&self) -> &Vec; fn hidden_abilities(&self) -> &Vec; fn moves(&self) -> &Arc; fn flags(&self) -> &HashSet; fn get_type(&self, index: usize) -> Result; fn get_base_stat(&self, stat: Statistic) -> u16; fn find_ability_index(&self, ability: &dyn Ability) -> Option; fn get_ability<'a>(&'a self, index: AbilityIndex) -> Result<&'a StringKey>; fn get_random_ability<'a>(&'a self, rand: &mut Random) -> Result<&'a StringKey>; fn get_random_hidden_ability<'a>(&'a self, rand: &mut Random) -> Result<&'a StringKey>; fn has_flag(&self, key: &StringKey) -> bool; fn has_flag_by_hash(&self, key_hash: u32) -> bool; fn eq(&self, other: &dyn Form) -> bool; } } }