453 lines
15 KiB
Rust
453 lines
15 KiB
Rust
use crate::defines::LevelInt;
|
|
use crate::dynamic_data::{Battle, DamageSource, DynamicLibrary, EventBatchId, LearnedMove, MoveLearnMethod, Pokemon};
|
|
use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle};
|
|
use crate::ffi::{FFIResult, OwnedPtrString};
|
|
use crate::static_data::{
|
|
Ability, AbilityIndex, Form, Gender, Item, Nature, Species, Statistic, StatisticSet, TypeIdentifier,
|
|
};
|
|
use crate::VecExt;
|
|
use anyhow::anyhow;
|
|
use std::ffi::{c_char, CStr, CString};
|
|
use std::sync::Arc;
|
|
|
|
/// Instantiates a new Pokemon.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_new(
|
|
library: FFIHandle<Arc<dyn DynamicLibrary>>,
|
|
species: FFIHandle<Arc<dyn Species>>,
|
|
form: FFIHandle<Arc<dyn Form>>,
|
|
hidden_ability: u8,
|
|
ability_index: u8,
|
|
level: LevelInt,
|
|
unique_identifier: u32,
|
|
gender: Gender,
|
|
coloring: u8,
|
|
nature: *const c_char,
|
|
) -> FFIResult<FFIHandle<Pokemon>> {
|
|
let nature = unsafe { CStr::from_ptr(nature) }.into();
|
|
let pokemon = Pokemon::new(
|
|
library.from_ffi_handle(),
|
|
species.from_ffi_handle(),
|
|
&form.from_ffi_handle(),
|
|
AbilityIndex {
|
|
hidden: hidden_ability == 1,
|
|
index: ability_index,
|
|
},
|
|
level,
|
|
unique_identifier,
|
|
gender,
|
|
coloring,
|
|
&nature,
|
|
);
|
|
match pokemon {
|
|
Ok(pokemon) => FFIResult::ok(FFIHandle::get_handle(pokemon.into())),
|
|
Err(err) => FFIResult::err(err),
|
|
}
|
|
}
|
|
|
|
/// The library data of the Pokemon.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_library(handle: FFIHandle<Pokemon>) -> FFIHandle<Arc<dyn DynamicLibrary>> {
|
|
FFIHandle::get_handle(handle.from_ffi_handle().library().clone().into())
|
|
}
|
|
|
|
/// The species of the Pokemon.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_species(handle: FFIHandle<Pokemon>) -> FFIHandle<Arc<dyn Species>> {
|
|
FFIHandle::get_handle(handle.from_ffi_handle().species().into())
|
|
}
|
|
|
|
/// The form of the Pokemon.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_form(handle: FFIHandle<Pokemon>) -> FFIHandle<Arc<dyn Form>> {
|
|
FFIHandle::get_handle(handle.from_ffi_handle().form().into())
|
|
}
|
|
|
|
/// The species that should be displayed to the user. This handles stuff like the Illusion ability.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_display_species(handle: FFIHandle<Pokemon>) -> FFIHandle<Arc<dyn Species>> {
|
|
FFIHandle::get_handle(handle.from_ffi_handle().display_species().into())
|
|
}
|
|
|
|
/// The form that should be displayed to the user. This handles stuff like the Illusion ability.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_display_form(handle: FFIHandle<Pokemon>) -> FFIHandle<Arc<dyn Form>> {
|
|
FFIHandle::get_handle(handle.from_ffi_handle().display_form().into())
|
|
}
|
|
|
|
/// The level of the Pokemon.
|
|
/// [See also](https://bulbapedia.bulbagarden.net/wiki/Level)
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_level(handle: FFIHandle<Pokemon>) -> LevelInt {
|
|
handle.from_ffi_handle().level()
|
|
}
|
|
|
|
/// The experience of the Pokemon.
|
|
/// [See also](https://bulbapedia.bulbagarden.net/wiki/Experience)
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_experience(handle: FFIHandle<Pokemon>) -> u32 {
|
|
handle.from_ffi_handle().experience()
|
|
}
|
|
|
|
/// The personality value of the Pokemon.
|
|
/// [See also](https://bulbapedia.bulbagarden.net/wiki/Personality_value)
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_personality_value(handle: FFIHandle<Pokemon>) -> u32 {
|
|
handle.from_ffi_handle().personality_value()
|
|
}
|
|
|
|
/// The gender of the Pokemon.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_gender(handle: FFIHandle<Pokemon>) -> Gender {
|
|
handle.from_ffi_handle().gender()
|
|
}
|
|
|
|
/// The coloring of the Pokemon. If this is 1, the Pokemon is shiny, otherwise it is not. This can
|
|
/// also be used for other custom coloring schemes.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_coloring(handle: FFIHandle<Pokemon>) -> u8 {
|
|
handle.from_ffi_handle().coloring()
|
|
}
|
|
|
|
/// Gets the held item of a Pokemon
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_held_item(handle: FFIHandle<Pokemon>) -> FFIHandle<Arc<dyn Item>> {
|
|
if let Some(v) = handle.from_ffi_handle().held_item().read().as_ref() {
|
|
FFIHandle::get_handle(v.clone().into())
|
|
} else {
|
|
FFIHandle::none()
|
|
}
|
|
}
|
|
|
|
/// Checks whether the Pokemon is holding a specific item.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_has_held_item(handle: FFIHandle<Pokemon>, name: *const c_char) -> u8 {
|
|
let name = unsafe { CStr::from_ptr(name) }.into();
|
|
u8::from(handle.from_ffi_handle().has_held_item(&name))
|
|
}
|
|
|
|
/// Changes the held item of the Pokemon. Returns the previously held item.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_set_held_item(
|
|
handle: FFIHandle<Pokemon>,
|
|
item: FFIHandle<Arc<dyn Item>>,
|
|
) -> FFIHandle<Arc<dyn Item>> {
|
|
if let Some(v) = handle.from_ffi_handle().set_held_item(&item.from_ffi_handle()) {
|
|
FFIHandle::get_handle(v.into())
|
|
} else {
|
|
FFIHandle::none()
|
|
}
|
|
}
|
|
|
|
/// Removes the held item from the Pokemon. Returns the previously held item.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_remove_held_item(handle: FFIHandle<Pokemon>) -> FFIHandle<Arc<dyn Item>> {
|
|
if let Some(v) = handle.from_ffi_handle().remove_held_item() {
|
|
FFIHandle::get_handle(v.into())
|
|
} else {
|
|
FFIHandle::none()
|
|
}
|
|
}
|
|
|
|
/// Makes the Pokemon uses its held item.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_consume_held_item(handle: FFIHandle<Pokemon>) -> FFIResult<u8> {
|
|
match handle.from_ffi_handle().consume_held_item() {
|
|
Ok(v) => FFIResult::ok(u8::from(v)),
|
|
Err(err) => FFIResult::err(err),
|
|
}
|
|
}
|
|
|
|
/// The current health of the Pokemon.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_current_health(handle: FFIHandle<Pokemon>) -> u32 {
|
|
handle.from_ffi_handle().current_health()
|
|
}
|
|
|
|
/// The max health of the Pokemon.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_max_health(handle: FFIHandle<Pokemon>) -> u32 {
|
|
handle.from_ffi_handle().max_health()
|
|
}
|
|
|
|
/// The current weight of the Pokemon.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_weight(handle: FFIHandle<Pokemon>) -> f32 {
|
|
handle.from_ffi_handle().weight()
|
|
}
|
|
|
|
/// The current height of the Pokemon.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_height(handle: FFIHandle<Pokemon>) -> f32 {
|
|
handle.from_ffi_handle().height()
|
|
}
|
|
|
|
/// An optional nickname of the Pokemon.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_nickname(handle: FFIHandle<Pokemon>) -> FFIResult<OwnedPtrString> {
|
|
let form = handle.from_ffi_handle();
|
|
let name = form.nickname();
|
|
if let Some(v) = name {
|
|
match CString::new(v.as_str()) {
|
|
Ok(v) => FFIResult::ok(OwnedPtrString(v.into_raw())),
|
|
Err(err) => FFIResult::err(anyhow!("Could not convert nickname to CString: {}", err)),
|
|
}
|
|
} else {
|
|
FFIResult::ok(OwnedPtrString(std::ptr::null_mut()))
|
|
}
|
|
}
|
|
|
|
/// Whether the actual ability the Pokemon has (so not its potentially overriden ability) is a hidden ability.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_real_ability_is_hidden(handle: FFIHandle<Pokemon>) -> u8 {
|
|
u8::from(handle.from_ffi_handle().real_ability().hidden)
|
|
}
|
|
|
|
/// The index of the actual ability the Pokemon has (so not its potentially overriden ability).
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_real_ability_index(handle: FFIHandle<Pokemon>) -> u8 {
|
|
handle.from_ffi_handle().real_ability().index
|
|
}
|
|
|
|
/// The amount of types the Pokemon has.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_types_length(ptr: FFIHandle<Pokemon>) -> usize {
|
|
ptr.from_ffi_handle().types().len()
|
|
}
|
|
|
|
/// Gets a type of the Pokemon.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_types_get(ptr: FFIHandle<Pokemon>, index: usize) -> FFIResult<TypeIdentifier> {
|
|
match ptr.from_ffi_handle().types().get_res(index) {
|
|
Ok(v) => FFIResult::ok(*v),
|
|
Err(err) => FFIResult::err(err),
|
|
}
|
|
}
|
|
|
|
/// Gets a learned move of the Pokemon. Index should generally be below [`MAX_MOVES`], you will get
|
|
/// a null pointer otherwise.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_learned_move_get(handle: FFIHandle<Pokemon>, index: usize) -> FFIHandle<Arc<LearnedMove>> {
|
|
if let Some(Some(v)) = handle.from_ffi_handle().learned_moves().read().get(index) {
|
|
FFIHandle::get_handle(v.clone().into())
|
|
} else {
|
|
FFIHandle::none()
|
|
}
|
|
}
|
|
|
|
/// The stats of the Pokemon when disregarding any stat boosts.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_flat_stats(handle: FFIHandle<Pokemon>) -> FFIHandle<Arc<StatisticSet<u32>>> {
|
|
FFIHandle::get_handle(handle.from_ffi_handle().flat_stats().clone().into())
|
|
}
|
|
|
|
/// The stats of the Pokemon including the stat boosts.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_boosted_stats(handle: FFIHandle<Pokemon>) -> FFIHandle<Arc<StatisticSet<u32>>> {
|
|
FFIHandle::get_handle(handle.from_ffi_handle().boosted_stats().clone().into())
|
|
}
|
|
|
|
/// Get the stat boosts for a specific stat. This is a value between -6 and 6.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_get_stat_boost(handle: FFIHandle<Pokemon>, statistic: Statistic) -> i8 {
|
|
handle.from_ffi_handle().stat_boost(statistic)
|
|
}
|
|
|
|
/// Change a boosted stat by a certain amount.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_change_stat_boost(
|
|
handle: FFIHandle<Pokemon>,
|
|
stat: Statistic,
|
|
diff_amount: i8,
|
|
self_inflicted: u8,
|
|
) -> FFIResult<u8> {
|
|
match handle
|
|
.from_ffi_handle()
|
|
.change_stat_boost(stat, diff_amount, self_inflicted == 1)
|
|
{
|
|
Ok(v) => FFIResult::ok(u8::from(v)),
|
|
Err(e) => FFIResult::err(e),
|
|
}
|
|
}
|
|
|
|
/// Gets an individual value of the Pokemon.
|
|
/// [See also](https://bulbapedia.bulbagarden.net/wiki/Individual_values)
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_get_individual_value(handle: FFIHandle<Pokemon>, stat: Statistic) -> u8 {
|
|
handle.from_ffi_handle().individual_values().get_stat(stat)
|
|
}
|
|
|
|
/// Modifies an individual value of the Pokemon.
|
|
/// [See also](https://bulbapedia.bulbagarden.net/wiki/Individual_values)
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_set_individual_value(handle: FFIHandle<Pokemon>, stat: Statistic, value: u8) -> FFIResult<()> {
|
|
handle.from_ffi_handle().individual_values().set_stat(stat, value);
|
|
handle.from_ffi_handle().recalculate_flat_stats().into()
|
|
}
|
|
|
|
/// Gets an effort value of the Pokemon.
|
|
/// [See also](https://bulbapedia.bulbagarden.net/wiki/Effort_values)
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_get_effort_value(handle: FFIHandle<Pokemon>, stat: Statistic) -> u8 {
|
|
handle.from_ffi_handle().effort_values().get_stat(stat)
|
|
}
|
|
|
|
/// Modifies a effort value of the Pokemon.
|
|
/// [See also](https://bulbapedia.bulbagarden.net/wiki/Effort_values)
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_set_effort_value(handle: FFIHandle<Pokemon>, stat: Statistic, value: u8) -> FFIResult<()> {
|
|
handle.from_ffi_handle().effort_values().set_stat(stat, value);
|
|
handle.from_ffi_handle().recalculate_flat_stats().into()
|
|
}
|
|
|
|
/// Gets the data for the battle the Pokemon is currently in. If the Pokemon is not in a battle, this
|
|
/// returns null.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_get_battle(handle: FFIHandle<Pokemon>) -> FFIHandle<Battle> {
|
|
if let Some(v) = handle.from_ffi_handle().get_battle() {
|
|
FFIHandle::get_handle(v.into())
|
|
} else {
|
|
FFIHandle::none()
|
|
}
|
|
}
|
|
|
|
/// Get the index of the side of the battle the Pokemon is in. If the Pokemon
|
|
/// is not on the battlefield, this always returns 0.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_get_battle_side_index(handle: FFIHandle<Pokemon>) -> u8 {
|
|
handle.from_ffi_handle().get_battle_side_index().unwrap_or_default()
|
|
}
|
|
|
|
/// Get the index of the slot on the side of the battle the Pokemon is in. If the Pokemon
|
|
/// is not on the battlefield, this always returns 0.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_get_battle_index(handle: FFIHandle<Pokemon>) -> u8 {
|
|
handle.from_ffi_handle().get_battle_index().unwrap_or_default()
|
|
}
|
|
|
|
/// Returns whether something overrides the ability.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_is_ability_overriden(handle: FFIHandle<Pokemon>) -> u8 {
|
|
u8::from(handle.from_ffi_handle().is_ability_overriden())
|
|
}
|
|
|
|
/// Returns the currently active ability.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_active_ability(handle: FFIHandle<Pokemon>) -> FFIResult<FFIHandle<Arc<dyn Ability>>> {
|
|
match handle.from_ffi_handle().active_ability() {
|
|
Ok(v) => FFIResult::ok(FFIHandle::get_handle(v.clone().into())),
|
|
Err(e) => FFIResult::err(e),
|
|
}
|
|
}
|
|
|
|
/// Whether or not the Pokemon is allowed to gain experience.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_allowed_experience_gain(handle: FFIHandle<Pokemon>) -> u8 {
|
|
u8::from(handle.from_ffi_handle().allowed_experience_gain())
|
|
}
|
|
|
|
/// The nature of the Pokemon.
|
|
/// [See also](https://bulbapedia.bulbagarden.net/wiki/Nature)
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_nature(handle: FFIHandle<Pokemon>) -> FFIHandle<Arc<dyn Nature>> {
|
|
FFIHandle::get_handle(handle.from_ffi_handle().nature().clone().into())
|
|
}
|
|
|
|
/// Calculates the flat stats on the Pokemon. This should be called when for example the base
|
|
/// stats, level, nature, IV, or EV changes. This has a side effect of recalculating the boosted
|
|
/// stats, as those depend on the flat stats.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_recalculate_flat_stats(handle: FFIHandle<Pokemon>) -> FFIResult<()> {
|
|
handle.from_ffi_handle().recalculate_flat_stats().into()
|
|
}
|
|
|
|
/// Calculates the boosted stats on the Pokemon. This should be called when a stat boost changes.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_recalculate_boosted_stats(handle: FFIHandle<Pokemon>) -> FFIResult<()> {
|
|
handle.from_ffi_handle().recalculate_boosted_stats().into()
|
|
}
|
|
|
|
/// Change the species of the Pokemon.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_change_species(
|
|
handle: FFIHandle<Pokemon>,
|
|
species: FFIHandle<Arc<dyn Species>>,
|
|
form: FFIHandle<Arc<dyn Form>>,
|
|
) -> FFIResult<()> {
|
|
handle
|
|
.from_ffi_handle()
|
|
.change_species(species.from_ffi_handle().clone(), form.from_ffi_handle().clone())
|
|
.into()
|
|
}
|
|
|
|
/// Change the form of the Pokemon.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_change_form(handle: FFIHandle<Pokemon>, form: FFIHandle<Arc<dyn Form>>) -> FFIResult<()> {
|
|
handle.from_ffi_handle().change_form(&form.from_ffi_handle()).into()
|
|
}
|
|
|
|
/// Whether or not the Pokemon is useable in a battle.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_is_usable(handle: FFIHandle<Pokemon>) -> u8 {
|
|
u8::from(handle.from_ffi_handle().is_usable())
|
|
}
|
|
|
|
/// Returns whether the Pokemon is fainted.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_is_fainted(handle: FFIHandle<Pokemon>) -> u8 {
|
|
u8::from(handle.from_ffi_handle().is_fainted())
|
|
}
|
|
|
|
/// Whether or not the Pokemon is on the battlefield.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_is_on_battlefield(handle: FFIHandle<Pokemon>) -> u8 {
|
|
u8::from(handle.from_ffi_handle().is_on_battlefield())
|
|
}
|
|
|
|
/// Damages the Pokemon by a certain amount of damage, from a damage source.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_damage(
|
|
handle: FFIHandle<Pokemon>,
|
|
damage: u32,
|
|
source: DamageSource,
|
|
evt_batch_id_1: u64,
|
|
evt_batch_id_2: u64,
|
|
) -> FFIResult<()> {
|
|
handle
|
|
.from_ffi_handle()
|
|
.damage(
|
|
damage,
|
|
source,
|
|
EventBatchId::from_u64_pair(evt_batch_id_1, evt_batch_id_2),
|
|
)
|
|
.into()
|
|
}
|
|
|
|
/// Heals the Pokemon by a specific amount. Unless allow_revive is set to true, this will not
|
|
/// heal if the Pokemon has 0 health. If the amount healed is 0, this will return false.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_heal(handle: FFIHandle<Pokemon>, amount: u32, allow_revive: u8) -> bool {
|
|
handle.from_ffi_handle().heal(amount, allow_revive == 1)
|
|
}
|
|
|
|
/// Learn a move.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_learn_move(
|
|
handle: FFIHandle<Pokemon>,
|
|
move_name: *const c_char,
|
|
learn_method: MoveLearnMethod,
|
|
) -> FFIResult<()> {
|
|
unsafe {
|
|
handle
|
|
.from_ffi_handle()
|
|
.learn_move(&CStr::from_ptr(move_name).into(), learn_method)
|
|
.into()
|
|
}
|
|
}
|
|
|
|
/// Removes the current non-volatile status from the Pokemon.
|
|
#[no_mangle]
|
|
extern "C" fn pokemon_clear_status(handle: FFIHandle<Pokemon>) {
|
|
handle.from_ffi_handle().clear_status()
|
|
}
|