Gen7ScriptsRs/pkmn_lib_interface/src/app_interface/dynamic_data/pokemon.rs

612 lines
23 KiB
Rust
Executable File

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<Item>;
fn battle(&self) -> Option<Battle>;
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<u32>;
fn stat_boosts(&self) -> ClampedStatisticSet<i8>;
fn boosted_stats(&self) -> StatisticSet<u32>;
fn individual_values(&self) -> ClampedStatisticSet<u8>;
fn effort_values(&self) -> ClampedStatisticSet<u8>;
fn has_held_item(&self, name: &str) -> bool;
fn set_held_item(&self, item: &Item) -> Option<Item>;
fn remove_held_item(&self) -> Option<Item>;
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<LearnedMove>;
fn change_stat_boost(&self, stat: Statistic, diff_amount: i8, self_inflicted: bool) -> bool;
fn ability_script(&self) -> Option<&Box<dyn Script>>;
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<dyn PokemonTrait>;
/// 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<PokemonImpl>,
library: CachedValue<DynamicLibrary>,
// We cache the reference to the data, not the values stored inside, which are dynamic.
flat_stats: CachedValue<Rc<StatisticSetImpl<u32>>>,
// We cache the reference to the data, not the values stored inside, which are dynamic.
stat_boosts: CachedValue<ClampedStatisticSet<i8>>,
// We cache the reference to the data, not the values stored inside, which are dynamic.
boosted_stats: CachedValue<Rc<StatisticSetImpl<u32>>>,
// We cache the reference to the data, not the values stored inside, which are dynamic.
individual_values: CachedValue<ClampedStatisticSet<u8>>,
// We cache the reference to the data, not the values stored inside, which are dynamic.
effort_values: CachedValue<ClampedStatisticSet<u8>>,
}
#[derive(Clone)]
pub struct PokemonImpl {
inner: Rc<PokemonInner>,
}
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<u32>;
fn stat_boosts(&self) -> ClampedStatisticSet<i8>;
fn boosted_stats(&self) -> StatisticSet<u32>;
fn individual_values(&self) -> ClampedStatisticSet<u8>;
fn effort_values(&self) -> ClampedStatisticSet<u8>;
}
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<Item> {
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<Battle> {
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<Item> {
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<Item> {
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<LearnedMove> {
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<dyn Script>> {
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::<SpeciesImpl>();
let form_impl = form.as_any().downcast_ref_unchecked::<FormImpl>();
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::<FormImpl>();
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>) -> &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 {
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> {
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<SpeciesImpl>;
pub fn display_form(&self) -> Option<FormImpl>;
pub fn held_item(&self) -> Option<ItemImpl>;
pub fn battle(&self) -> Option<BattleImpl>;
}
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 {
Self::new(reference)
}
}
extern "wasm" {
fn pokemon_get_library(r: ExternRef<PokemonImpl>) -> ExternRef<DynamicLibraryImpl>;
fn pokemon_get_flat_stats(r: ExternRef<PokemonImpl>) -> ExternRef<StatisticSetImpl<u32>>;
fn pokemon_get_stat_boosts(r: ExternRef<PokemonImpl>)
-> ExternRef<ClampedStatisticSet<i8>>;
fn pokemon_get_boosted_stats(r: ExternRef<PokemonImpl>)
-> ExternRef<StatisticSetImpl<u32>>;
fn pokemon_get_individual_values(
r: ExternRef<PokemonImpl>,
) -> ExternRef<ClampedStatisticSet<u8>>;
fn pokemon_get_effort_values(
r: ExternRef<PokemonImpl>,
) -> ExternRef<ClampedStatisticSet<u8>>;
fn pokemon_has_held_item(r: ExternRef<PokemonImpl>, name: *const c_char) -> bool;
fn pokemon_set_held_item(
r: ExternRef<PokemonImpl>,
item: ExternRef<ItemImpl>,
) -> ExternRef<ItemImpl>;
fn pokemon_remove_held_item(r: ExternRef<PokemonImpl>) -> ExternRef<ItemImpl>;
fn pokemon_consume_held_item(r: ExternRef<PokemonImpl>) -> bool;
fn pokemon_get_type(r: ExternRef<PokemonImpl>, index: usize) -> u8;
fn pokemon_has_type(r: ExternRef<PokemonImpl>, identifier: u8) -> bool;
fn pokemon_get_learned_move(
r: ExternRef<PokemonImpl>,
index: usize,
) -> ExternRef<LearnedMoveImpl>;
fn pokemon_change_stat_boost(
r: ExternRef<PokemonImpl>,
tat: Statistic,
diff_amount: i8,
self_inflicted: bool,
) -> bool;
#[allow(improper_ctypes)]
fn pokemon_get_ability_script(r: ExternRef<PokemonImpl>) -> *const Box<dyn Script>;
fn pokemon_change_species(
r: ExternRef<PokemonImpl>,
species: ExternRef<SpeciesImpl>,
form: ExternRef<FormImpl>,
);
fn pokemon_change_form(r: ExternRef<PokemonImpl>, form: ExternRef<FormImpl>);
fn pokemon_damage(r: ExternRef<PokemonImpl>, damage: u32, source: DamageSource);
fn pokemon_heal(r: ExternRef<PokemonImpl>, amount: u32, allow_revive: bool) -> bool;
fn pokemon_set_weight(r: ExternRef<PokemonImpl>, weight: f32);
fn pokemon_clear_status(r: ExternRef<PokemonImpl>);
fn pokemon_add_volatile_by_name(
r: ExternRef<PokemonImpl>,
name: *const c_char,
) -> ScriptPtr;
fn pokemon_add_volatile(r: ExternRef<PokemonImpl>, script: ScriptPtr) -> ScriptPtr;
fn pokemon_has_volatile(r: ExternRef<PokemonImpl>, name: *const c_char) -> bool;
fn pokemon_remove_volatile(r: ExternRef<PokemonImpl>, name: *const c_char);
fn pokemon_get_volatile(r: ExternRef<PokemonImpl>, 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<Item>;
fn battle(&self) -> Option<Battle>;
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<u32>;
fn stat_boosts(&self) -> ClampedStatisticSet<i8>;
fn boosted_stats(&self) -> StatisticSet<u32>;
fn individual_values(&self) -> ClampedStatisticSet<u8>;
fn effort_values(&self) -> ClampedStatisticSet<u8>;
fn has_held_item(&self, name: &str) -> bool;
fn set_held_item(&self, item: &Item) -> Option<Item>;
fn remove_held_item(&self) -> Option<Item>;
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<LearnedMove>;
fn change_stat_boost(&self, stat: Statistic, diff_amount: i8, self_inflicted: bool) -> bool;
fn ability_script<'a>(&'a self) -> Option<&'a Box<dyn Script>>;
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>) -> &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!()
}
}