289 lines
10 KiB
Rust
Executable File
289 lines
10 KiB
Rust
Executable File
use crate::app_interface::ability::{Ability, AbilityIndex};
|
|
use crate::app_interface::{
|
|
Battle, BattleSide, ClampedStatisticSet, Form, Gender, Item, LearnedMove, LevelInt, Nature,
|
|
Species, Statistic, StatisticSet,
|
|
};
|
|
use crate::handling::cached_value::CachedValue;
|
|
use crate::handling::Cacheable;
|
|
use crate::{
|
|
cached_value, cached_value_getters, wasm_optional_reference_getters, wasm_reference_getters,
|
|
wasm_value_getters, DynamicLibrary, ExternRef, ExternalReferenceType, Script, TypeIdentifier,
|
|
};
|
|
use alloc::boxed::Box;
|
|
use alloc::rc::Rc;
|
|
use cstr_core::{c_char, CString};
|
|
|
|
struct PokemonInner {
|
|
reference: ExternRef<Pokemon>,
|
|
library: CachedValue<DynamicLibrary>,
|
|
// We cache the reference to the data, not the values stored inside, which are dynamic.
|
|
flat_stats: CachedValue<StatisticSet<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<StatisticSet<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 Pokemon {
|
|
inner: Rc<PokemonInner>,
|
|
}
|
|
|
|
impl Pokemon {
|
|
#[cfg(not(feature = "mock_data"))]
|
|
pub(crate) fn new(reference: ExternRef<Self>) -> Self {
|
|
Self::from_ref(reference, &|reference| Self {
|
|
inner: Rc::new(PokemonInner {
|
|
reference,
|
|
library: cached_value!({ pokemon_get_library(reference).get_value().unwrap() }),
|
|
flat_stats: cached_value!({
|
|
pokemon_get_flat_stats(reference).get_value().unwrap()
|
|
}),
|
|
stat_boosts: cached_value!({
|
|
pokemon_get_stat_boosts(reference).get_value().unwrap()
|
|
}),
|
|
boosted_stats: cached_value!({
|
|
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
|
|
}
|
|
|
|
cached_value_getters! {
|
|
pub fn library(&self) -> DynamicLibrary;
|
|
pub fn flat_stats(&self) -> StatisticSet<u32>;
|
|
pub fn stat_boosts(&self) -> ClampedStatisticSet<i8>;
|
|
pub fn boosted_stats(&self) -> StatisticSet<u32>;
|
|
pub fn individual_values(&self) -> ClampedStatisticSet<u8>;
|
|
pub fn effort_values(&self) -> ClampedStatisticSet<u8>;
|
|
}
|
|
|
|
#[cfg(not(feature = "mock_data"))]
|
|
pub 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()) }
|
|
}
|
|
|
|
#[cfg(not(feature = "mock_data"))]
|
|
pub fn set_held_item(&self, item: &Item) -> Option<Item> {
|
|
unsafe { pokemon_set_held_item(self.inner.reference, item.reference()).get_value() }
|
|
}
|
|
|
|
#[cfg(not(feature = "mock_data"))]
|
|
pub fn remove_held_item(&self) -> Option<Item> {
|
|
unsafe { pokemon_remove_held_item(self.inner.reference).get_value() }
|
|
}
|
|
|
|
#[cfg(not(feature = "mock_data"))]
|
|
pub fn consume_held_item(&self) -> bool {
|
|
unsafe { pokemon_consume_held_item(self.inner.reference) }
|
|
}
|
|
|
|
#[cfg(not(feature = "mock_data"))]
|
|
pub fn max_health(&self) -> u32 {
|
|
self.boosted_stats().hp()
|
|
}
|
|
|
|
#[cfg(not(feature = "mock_data"))]
|
|
pub fn get_type(&self, index: usize) -> u8 {
|
|
unsafe { pokemon_get_type(self.inner.reference, index) }
|
|
}
|
|
#[cfg(not(feature = "mock_data"))]
|
|
pub fn has_type(&self, type_identifier: TypeIdentifier) -> bool {
|
|
unsafe { pokemon_has_type(self.inner.reference, type_identifier.into()) }
|
|
}
|
|
#[cfg(not(feature = "mock_data"))]
|
|
pub 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
|
|
}
|
|
|
|
#[cfg(not(feature = "mock_data"))]
|
|
pub fn get_learned_move(&self, index: usize) -> Option<LearnedMove> {
|
|
unsafe { pokemon_get_learned_move(self.inner.reference, index).get_value() }
|
|
}
|
|
#[cfg(not(feature = "mock_data"))]
|
|
pub 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)
|
|
}
|
|
}
|
|
|
|
#[cfg(not(feature = "mock_data"))]
|
|
pub fn ability_script(&self) -> Option<&Box<dyn Script>> {
|
|
unsafe { pokemon_get_ability_script(self.inner.reference).as_ref() }
|
|
}
|
|
|
|
#[cfg(not(feature = "mock_data"))]
|
|
pub fn change_species(&self, species: Species, form: Form) {
|
|
unsafe {
|
|
pokemon_change_species(self.inner.reference, species.reference(), form.reference());
|
|
}
|
|
}
|
|
|
|
#[cfg(not(feature = "mock_data"))]
|
|
pub fn change_form(&self, form: Form) {
|
|
unsafe {
|
|
pokemon_change_form(self.inner.reference, form.reference());
|
|
}
|
|
}
|
|
|
|
pub fn is_fainted(&self) -> bool {
|
|
self.current_health() == 0
|
|
}
|
|
|
|
#[cfg(not(feature = "mock_data"))]
|
|
pub fn damage(&self, damage: u32, source: DamageSource) {
|
|
unsafe { pokemon_damage(self.inner.reference, damage, source) }
|
|
}
|
|
|
|
#[cfg(not(feature = "mock_data"))]
|
|
pub fn heal(&self, amount: u32, allow_revive: bool) -> bool {
|
|
unsafe { pokemon_heal(self.inner.reference, amount, allow_revive) }
|
|
}
|
|
|
|
#[cfg(not(feature = "mock_data"))]
|
|
pub fn battle_side(&self) -> BattleSide {
|
|
self.battle()
|
|
.unwrap()
|
|
.sides()
|
|
.get(self.battle_side_index() as u32)
|
|
.unwrap()
|
|
}
|
|
}
|
|
|
|
#[cfg(not(feature = "mock_data"))]
|
|
wasm_reference_getters! {
|
|
Pokemon,
|
|
pub fn species(&self) -> Species;
|
|
pub fn form(&self) -> Form;
|
|
pub fn active_ability(&self) -> Ability;
|
|
pub fn nature(&self) -> Nature;
|
|
}
|
|
|
|
#[cfg(not(feature = "mock_data"))]
|
|
wasm_optional_reference_getters! {
|
|
Pokemon,
|
|
pub fn display_species(&self) -> Option<Species>;
|
|
pub fn display_form(&self) -> Option<Form>;
|
|
pub fn held_item(&self) -> Option<Item>;
|
|
pub fn battle(&self) -> Option<Battle>;
|
|
}
|
|
|
|
#[cfg(not(feature = "mock_data"))]
|
|
wasm_value_getters! {
|
|
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 Pokemon {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
self.inner.reference == other.inner.reference
|
|
}
|
|
}
|
|
|
|
/// 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,
|
|
}
|
|
|
|
crate::handling::cacheable::cacheable!(Pokemon);
|
|
|
|
#[cfg(not(feature = "mock_data"))]
|
|
impl ExternalReferenceType for Pokemon {
|
|
fn from_extern_value(reference: ExternRef<Self>) -> Self {
|
|
Self::new(reference)
|
|
}
|
|
}
|
|
|
|
#[cfg(not(feature = "mock_data"))]
|
|
extern "wasm" {
|
|
fn pokemon_get_library(r: ExternRef<Pokemon>) -> ExternRef<DynamicLibrary>;
|
|
fn pokemon_get_flat_stats(r: ExternRef<Pokemon>) -> ExternRef<StatisticSet<u32>>;
|
|
fn pokemon_get_stat_boosts(r: ExternRef<Pokemon>) -> ExternRef<ClampedStatisticSet<i8>>;
|
|
fn pokemon_get_boosted_stats(r: ExternRef<Pokemon>) -> ExternRef<StatisticSet<u32>>;
|
|
fn pokemon_get_individual_values(r: ExternRef<Pokemon>) -> ExternRef<ClampedStatisticSet<u8>>;
|
|
fn pokemon_get_effort_values(r: ExternRef<Pokemon>) -> ExternRef<ClampedStatisticSet<u8>>;
|
|
fn pokemon_has_held_item(r: ExternRef<Pokemon>, name: *const c_char) -> bool;
|
|
fn pokemon_set_held_item(r: ExternRef<Pokemon>, item: ExternRef<Item>) -> ExternRef<Item>;
|
|
fn pokemon_remove_held_item(r: ExternRef<Pokemon>) -> ExternRef<Item>;
|
|
fn pokemon_consume_held_item(r: ExternRef<Pokemon>) -> bool;
|
|
fn pokemon_get_type(r: ExternRef<Pokemon>, index: usize) -> u8;
|
|
fn pokemon_has_type(r: ExternRef<Pokemon>, identifier: u8) -> bool;
|
|
fn pokemon_get_learned_move(r: ExternRef<Pokemon>, index: usize) -> ExternRef<LearnedMove>;
|
|
fn pokemon_change_stat_boost(
|
|
r: ExternRef<Pokemon>,
|
|
tat: Statistic,
|
|
diff_amount: i8,
|
|
self_inflicted: bool,
|
|
) -> bool;
|
|
#[allow(improper_ctypes)]
|
|
fn pokemon_get_ability_script(r: ExternRef<Pokemon>) -> *const Box<dyn Script>;
|
|
fn pokemon_change_species(
|
|
r: ExternRef<Pokemon>,
|
|
species: ExternRef<Species>,
|
|
form: ExternRef<Form>,
|
|
);
|
|
fn pokemon_change_form(r: ExternRef<Pokemon>, form: ExternRef<Form>);
|
|
fn pokemon_damage(r: ExternRef<Pokemon>, damage: u32, source: DamageSource);
|
|
fn pokemon_heal(r: ExternRef<Pokemon>, amount: u32, allow_revive: bool) -> bool;
|
|
}
|
|
|
|
#[cfg(feature = "mock_data")]
|
|
impl Pokemon {
|
|
pub fn current_health(&self) -> u32 {
|
|
unimplemented!()
|
|
}
|
|
pub fn species(&self) -> Species {
|
|
unimplemented!()
|
|
}
|
|
}
|