use crate::app_interface::{ Ability, AbilityIndex, Battle, BattleSide, ClampedStatisticSet, DynamicLibrary, Form, Gender, Item, LearnedMove, LevelInt, Nature, Species, Statistic, StatisticSet, TypeIdentifier, WithVolatile, }; use crate::handling::Script; use alloc::boxed::Box; use alloc::rc::Rc; use cstr_core::c_char; #[cfg(not(feature = "mock_data"))] pub use implementation::*; pub trait PokemonTrait: WithVolatile { fn reference(&self) -> u32; fn species(&self) -> Species; fn form(&self) -> Form; fn active_ability(&self) -> Ability; fn nature(&self) -> Nature; fn display_species(&self) -> Species; fn display_form(&self) -> Form; fn held_item(&self) -> Option; fn battle(&self) -> Option; fn level(&self) -> LevelInt; fn experience(&self) -> u32; fn unique_identifier(&self) -> u32; fn gender(&self) -> Gender; fn coloring(&self) -> u8; fn current_health(&self) -> u32; fn weight(&self) -> f32; fn height(&self) -> f32; fn nickname(&self) -> *const c_char; fn real_ability(&self) -> AbilityIndex; fn types_length(&self) -> usize; fn battle_side_index(&self) -> u8; fn battle_index(&self) -> u8; fn is_ability_overriden(&self) -> u8; fn allowed_experience_gain(&self) -> bool; fn is_usable(&self) -> bool; fn library(&self) -> DynamicLibrary; fn flat_stats(&self) -> StatisticSet; fn stat_boosts(&self) -> ClampedStatisticSet; fn boosted_stats(&self) -> StatisticSet; fn individual_values(&self) -> ClampedStatisticSet; fn effort_values(&self) -> ClampedStatisticSet; fn has_held_item(&self, name: &str) -> bool; fn set_held_item(&self, item: &Item) -> Option; fn remove_held_item(&self) -> Option; fn consume_held_item(&self) -> bool; fn max_health(&self) -> u32; fn get_type(&self, index: usize) -> u8; fn has_type(&self, type_identifier: TypeIdentifier) -> bool; fn has_type_by_name(&self, type_name: &str) -> bool; fn get_learned_move(&self, index: usize) -> Option; fn change_stat_boost(&self, stat: Statistic, diff_amount: i8, self_inflicted: bool) -> bool; fn ability_script(&self) -> Option<&Box>; fn change_species(&self, species: Species, form: Form); fn change_form(&self, form: Form); fn is_fainted(&self) -> bool; fn damage(&self, damage: u32, source: DamageSource); fn heal(&self, amount: u32, allow_revive: bool) -> bool; fn set_weight(&self, weight: f32); fn clear_status(&self); fn battle_side(&self) -> BattleSide; fn equals(&self, other: &Pokemon) -> bool; } pub type Pokemon = Rc; /// A source of damage. This should be as unique as possible. #[derive(Debug, Clone, Copy)] #[repr(u8)] pub enum DamageSource { /// The damage is done by a move. MoveDamage = 0, /// The damage is done by something else. Misc = 1, /// The damage is done because of struggling. Struggle = 2, } #[cfg(not(feature = "mock_data"))] mod implementation { use super::*; use cstr_core::CString; use crate::app_interface::dynamic_data::dynamic_library::DynamicLibraryImpl; use crate::app_interface::{ AbilityImpl, BattleImpl, FormImpl, ItemImpl, LearnedMoveImpl, NatureImpl, SpeciesImpl, StatisticSetImpl, }; use crate::handling::cached_value::CachedValue; use crate::handling::Cacheable; use crate::implementation::ScriptPtr; use crate::{ cached_value, cached_value_getters, wasm_optional_reference_getters_extern, wasm_reference_getters_extern, wasm_value_getters_extern, wasm_value_getters_funcs, ExternRef, ExternalReferenceType, }; struct PokemonInner { reference: ExternRef, library: CachedValue, // We cache the reference to the data, not the values stored inside, which are dynamic. flat_stats: CachedValue>>, // We cache the reference to the data, not the values stored inside, which are dynamic. stat_boosts: CachedValue>, // We cache the reference to the data, not the values stored inside, which are dynamic. boosted_stats: CachedValue>>, // We cache the reference to the data, not the values stored inside, which are dynamic. individual_values: CachedValue>, // We cache the reference to the data, not the values stored inside, which are dynamic. effort_values: CachedValue>, } #[derive(Clone)] pub struct PokemonImpl { inner: Rc, } impl PokemonTrait for PokemonImpl { fn reference(&self) -> u32 { self.reference().get_internal_index() } cached_value_getters! { fn library(&self) -> DynamicLibrary; fn flat_stats(&self) -> StatisticSet; fn stat_boosts(&self) -> ClampedStatisticSet; fn boosted_stats(&self) -> StatisticSet; fn individual_values(&self) -> ClampedStatisticSet; fn effort_values(&self) -> ClampedStatisticSet; } fn active_ability(&self) -> Ability { unsafe { let implementation = pokemon_get_active_ability(self.reference()) .get_value() .unwrap(); Rc::new(implementation) } } fn held_item(&self) -> Option { unsafe { let i = pokemon_get_held_item(self.inner.reference).get_value(); if let Some(i) = i { Some(Rc::new(i)) } else { None } } } fn battle(&self) -> Option { unsafe { let b = pokemon_get_battle(self.reference()).get_value(); if let Some(b) = b { Some(Rc::new(b)) } else { None } } } fn has_held_item(&self, name: &str) -> bool { let cstr = CString::new(name).unwrap(); unsafe { pokemon_has_held_item(self.inner.reference, cstr.as_ptr()) } } fn set_held_item(&self, item: &Item) -> Option { unsafe { let i = pokemon_set_held_item(self.inner.reference, item.reference().into()) .get_value(); if let Some(i) = i { Some(Rc::new(i)) } else { None } } } fn remove_held_item(&self) -> Option { unsafe { let i = pokemon_remove_held_item(self.inner.reference).get_value(); if let Some(i) = i { Some(Rc::new(i)) } else { None } } } fn consume_held_item(&self) -> bool { unsafe { pokemon_consume_held_item(self.inner.reference) } } fn max_health(&self) -> u32 { self.boosted_stats().hp() } fn get_type(&self, index: usize) -> u8 { unsafe { pokemon_get_type(self.inner.reference, index) } } fn has_type(&self, type_identifier: TypeIdentifier) -> bool { unsafe { pokemon_has_type(self.inner.reference, type_identifier.into()) } } fn has_type_by_name(&self, type_name: &str) -> bool { let type_identifier = self .library() .data_library() .type_library() .get_type_from_name(type_name); if let Some(type_identifier) = type_identifier { return self.has_type(type_identifier); } false } fn get_learned_move(&self, index: usize) -> Option { unsafe { let v = pokemon_get_learned_move(self.inner.reference, index).get_value(); if let Some(v) = v { Some(Rc::new(v)) } else { None } } } fn change_stat_boost( &self, stat: Statistic, diff_amount: i8, self_inflicted: bool, ) -> bool { unsafe { pokemon_change_stat_boost(self.inner.reference, stat, diff_amount, self_inflicted) } } fn ability_script(&self) -> Option<&Box> { unsafe { pokemon_get_ability_script(self.inner.reference).as_ref() } } fn change_species(&self, species: Species, form: Form) { unsafe { let species_impl = species.as_any().downcast_ref_unchecked::(); let form_impl = form.as_any().downcast_ref_unchecked::(); pokemon_change_species( self.inner.reference, species_impl.reference(), form_impl.reference(), ); } } fn change_form(&self, form: Form) { unsafe { let form_impl = form.as_any().downcast_ref_unchecked::(); pokemon_change_form(self.inner.reference, form_impl.reference()); } } fn is_fainted(&self) -> bool { self.current_health() == 0 } fn damage(&self, damage: u32, source: DamageSource) { unsafe { pokemon_damage(self.inner.reference, damage, source) } } fn heal(&self, amount: u32, allow_revive: bool) -> bool { unsafe { pokemon_heal(self.inner.reference, amount, allow_revive) } } fn set_weight(&self, weight: f32) { unsafe { pokemon_set_weight(self.reference(), weight); } } fn nature(&self) -> Nature { unsafe { Rc::new( pokemon_get_nature(self.inner.reference) .get_value() .unwrap(), ) } } fn species(&self) -> Species { unsafe { Rc::new( pokemon_get_species(self.inner.reference) .get_value() .unwrap(), ) } } fn form(&self) -> Form { unsafe { Rc::new(pokemon_get_form(self.inner.reference).get_value().unwrap()) } } fn clear_status(&self) { unsafe { pokemon_clear_status(self.reference()); } } fn display_species(&self) -> Species { unsafe { Rc::new( pokemon_get_display_species(self.inner.reference) .get_value() .unwrap(), ) } } fn display_form(&self) -> Form { unsafe { Rc::new( pokemon_get_display_form(self.inner.reference) .get_value() .unwrap(), ) } } fn battle_side(&self) -> BattleSide { self.battle() .unwrap() .sides() .get(self.battle_side_index() as u32) .unwrap() } #[cfg(not(feature = "mock_data"))] wasm_value_getters_funcs! { Pokemon, fn level(&self) -> LevelInt; fn experience(&self) -> u32; fn unique_identifier(&self) -> u32; fn gender(&self) -> Gender; fn coloring(&self) -> u8; fn current_health(&self) -> u32; fn weight(&self) -> f32; fn height(&self) -> f32; fn nickname(&self) -> *const c_char; fn real_ability(&self) -> AbilityIndex; fn types_length(&self) -> usize; fn battle_side_index(&self) -> u8; fn battle_index(&self) -> u8; fn is_ability_overriden(&self) -> u8; fn allowed_experience_gain(&self) -> bool; fn is_usable(&self) -> bool; } fn equals(&self, other: &Pokemon) -> bool { self.inner .reference .get_internal_index() .eq(&other.reference()) } } #[cfg(not(feature = "mock_data"))] impl WithVolatile for PokemonImpl { fn has_volatile(&self, script_name: &str) -> bool { unsafe { let ptr = CString::new(script_name).unwrap(); pokemon_has_volatile(self.inner.reference, ptr.as_ptr()) } } fn add_volatile(&self, script: Box) -> &dyn Script { unsafe { pokemon_add_volatile(self.inner.reference, ScriptPtr::new(script)) .val() .unwrap() } } fn add_volatile_by_name(&self, script_name: &str) -> &dyn Script { unsafe { let ptr = CString::new(script_name).unwrap(); pokemon_add_volatile_by_name(self.inner.reference, ptr.as_ptr()) .val() .unwrap() } } fn remove_volatile(&self, script: &dyn Script) { unsafe { let name = CString::new(script.get_name()).unwrap(); pokemon_remove_volatile(self.inner.reference, name.as_ptr()); } } fn get_volatile_script(&self, script_name: &str) -> Option<&dyn Script> { unsafe { let script_name = CString::new(script_name).unwrap(); pokemon_get_volatile(self.inner.reference, script_name.as_ptr()).val() } } } impl PokemonImpl { pub(crate) fn new(reference: ExternRef) -> Self { Self::from_ref(reference, &|reference| Self { inner: Rc::new(PokemonInner { reference, library: cached_value!({ Rc::new(pokemon_get_library(reference).get_value().unwrap()) }), flat_stats: cached_value!({ Rc::new(pokemon_get_flat_stats(reference).get_value().unwrap()) }), stat_boosts: cached_value!({ pokemon_get_stat_boosts(reference).get_value().unwrap() }), boosted_stats: cached_value!({ Rc::new(pokemon_get_boosted_stats(reference).get_value().unwrap()) }), individual_values: cached_value!({ pokemon_get_individual_values(reference) .get_value() .unwrap() }), effort_values: cached_value!({ pokemon_get_effort_values(reference).get_value().unwrap() }), }), }) } pub(crate) fn reference(&self) -> ExternRef { self.inner.reference } } wasm_reference_getters_extern! { PokemonImpl, Pokemon, pub fn species(&self) -> SpeciesImpl; pub fn form(&self) -> FormImpl; pub fn active_ability(&self) -> AbilityImpl; pub fn nature(&self) -> NatureImpl; } wasm_optional_reference_getters_extern! { PokemonImpl, Pokemon, pub fn display_species(&self) -> Option; pub fn display_form(&self) -> Option; pub fn held_item(&self) -> Option; pub fn battle(&self) -> Option; } wasm_value_getters_extern! { PokemonImpl, Pokemon, pub fn level(&self) -> LevelInt; pub fn experience(&self) -> u32; pub fn unique_identifier(&self) -> u32; pub fn gender(&self) -> Gender; pub fn coloring(&self) -> u8; pub fn current_health(&self) -> u32; pub fn weight(&self) -> f32; pub fn height(&self) -> f32; pub fn nickname(&self) -> *const c_char; pub fn real_ability(&self) -> AbilityIndex; pub fn types_length(&self) -> usize; pub fn battle_side_index(&self) -> u8; pub fn battle_index(&self) -> u8; pub fn is_ability_overriden(&self) -> u8; pub fn allowed_experience_gain(&self) -> bool; pub fn is_usable(&self) -> bool; } impl PartialEq for PokemonImpl { fn eq(&self, other: &Self) -> bool { self.inner.reference == other.inner.reference } } crate::handling::cacheable::cacheable!(PokemonImpl); impl ExternalReferenceType for PokemonImpl { fn from_extern_value(reference: ExternRef) -> Self { Self::new(reference) } } extern "wasm" { fn pokemon_get_library(r: ExternRef) -> ExternRef; fn pokemon_get_flat_stats(r: ExternRef) -> ExternRef>; fn pokemon_get_stat_boosts(r: ExternRef) -> ExternRef>; fn pokemon_get_boosted_stats(r: ExternRef) -> ExternRef>; fn pokemon_get_individual_values( r: ExternRef, ) -> ExternRef>; fn pokemon_get_effort_values( r: ExternRef, ) -> ExternRef>; fn pokemon_has_held_item(r: ExternRef, name: *const c_char) -> bool; fn pokemon_set_held_item( r: ExternRef, item: ExternRef, ) -> ExternRef; fn pokemon_remove_held_item(r: ExternRef) -> ExternRef; fn pokemon_consume_held_item(r: ExternRef) -> bool; fn pokemon_get_type(r: ExternRef, index: usize) -> u8; fn pokemon_has_type(r: ExternRef, identifier: u8) -> bool; fn pokemon_get_learned_move( r: ExternRef, index: usize, ) -> ExternRef; fn pokemon_change_stat_boost( r: ExternRef, tat: Statistic, diff_amount: i8, self_inflicted: bool, ) -> bool; #[allow(improper_ctypes)] fn pokemon_get_ability_script(r: ExternRef) -> *const Box; fn pokemon_change_species( r: ExternRef, species: ExternRef, form: ExternRef, ); fn pokemon_change_form(r: ExternRef, form: ExternRef); fn pokemon_damage(r: ExternRef, damage: u32, source: DamageSource); fn pokemon_heal(r: ExternRef, amount: u32, allow_revive: bool) -> bool; fn pokemon_set_weight(r: ExternRef, weight: f32); fn pokemon_clear_status(r: ExternRef); fn pokemon_add_volatile_by_name( r: ExternRef, name: *const c_char, ) -> ScriptPtr; fn pokemon_add_volatile(r: ExternRef, script: ScriptPtr) -> ScriptPtr; fn pokemon_has_volatile(r: ExternRef, name: *const c_char) -> bool; fn pokemon_remove_volatile(r: ExternRef, name: *const c_char); fn pokemon_get_volatile(r: ExternRef, name: *const c_char) -> ScriptPtr; } } #[cfg(feature = "mock_data")] mockall::mock!( pub Pokemon {} impl PokemonTrait for Pokemon { fn reference(&self) -> u32; fn species(&self) -> Species; fn form(&self) -> Form; fn active_ability(&self) -> Ability; fn nature(&self) -> Nature; fn display_species(&self) -> Species; fn display_form(&self) -> Form; fn held_item(&self) -> Option; fn battle(&self) -> Option; fn level(&self) -> LevelInt; fn experience(&self) -> u32; fn unique_identifier(&self) -> u32; fn gender(&self) -> Gender; fn coloring(&self) -> u8; fn current_health(&self) -> u32; fn weight(&self) -> f32; fn height(&self) -> f32; fn nickname(&self) -> *const c_char; fn real_ability(&self) -> AbilityIndex; fn types_length(&self) -> usize; fn battle_side_index(&self) -> u8; fn battle_index(&self) -> u8; fn is_ability_overriden(&self) -> u8; fn allowed_experience_gain(&self) -> bool; fn is_usable(&self) -> bool; fn library(&self) -> DynamicLibrary; fn flat_stats(&self) -> StatisticSet; fn stat_boosts(&self) -> ClampedStatisticSet; fn boosted_stats(&self) -> StatisticSet; fn individual_values(&self) -> ClampedStatisticSet; fn effort_values(&self) -> ClampedStatisticSet; fn has_held_item(&self, name: &str) -> bool; fn set_held_item(&self, item: &Item) -> Option; fn remove_held_item(&self) -> Option; fn consume_held_item(&self) -> bool; fn max_health(&self) -> u32; fn get_type(&self, index: usize) -> u8; fn has_type(&self, type_identifier: TypeIdentifier) -> bool; fn has_type_by_name(&self, type_name: &str) -> bool; fn get_learned_move(&self, index: usize) -> Option; fn change_stat_boost(&self, stat: Statistic, diff_amount: i8, self_inflicted: bool) -> bool; fn ability_script<'a>(&'a self) -> Option<&'a Box>; fn change_species(&self, species: Species, form: Form); fn change_form(&self, form: Form); fn is_fainted(&self) -> bool; fn damage(&self, damage: u32, source: DamageSource); fn heal(&self, amount: u32, allow_revive: bool) -> bool; fn set_weight(&self, weight: f32); fn clear_status(&self); fn battle_side(&self) -> BattleSide; fn equals(&self, other: &Pokemon) -> bool; } ); #[cfg(feature = "mock_data")] impl WithVolatile for MockPokemon { fn has_volatile(&self, _script_name: &str) -> bool { unimplemented!() } fn add_volatile(&self, _script: Box) -> &dyn Script { unimplemented!() } fn add_volatile_by_name(&self, _script_name: &str) -> &dyn Script { unimplemented!() } fn remove_volatile(&self, _script: &dyn Script) { unimplemented!() } fn get_volatile_script<'a>(&'a self, _script_name: &str) -> Option<&'a dyn Script> { unimplemented!() } }