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>, species: FFIHandle>, form: FFIHandle>, hidden_ability: u8, ability_index: u8, level: LevelInt, unique_identifier: u32, gender: Gender, coloring: u8, nature: *const c_char, ) -> FFIResult> { 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) -> FFIHandle> { FFIHandle::get_handle(handle.from_ffi_handle().library().clone().into()) } /// The species of the Pokemon. #[no_mangle] extern "C" fn pokemon_species(handle: FFIHandle) -> FFIHandle> { FFIHandle::get_handle(handle.from_ffi_handle().species().into()) } /// The form of the Pokemon. #[no_mangle] extern "C" fn pokemon_form(handle: FFIHandle) -> FFIHandle> { 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) -> FFIHandle> { 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) -> FFIHandle> { FFIHandle::get_handle(handle.from_ffi_handle().display_form().into()) } /// The level of the Pokemon. #[no_mangle] extern "C" fn pokemon_level(handle: FFIHandle) -> LevelInt { handle.from_ffi_handle().level() } /// The experience of the Pokemon. #[no_mangle] extern "C" fn pokemon_experience(handle: FFIHandle) -> u32 { handle.from_ffi_handle().experience() } /// The unique identifier of the Pokemon. #[no_mangle] extern "C" fn pokemon_unique_identifier(handle: FFIHandle) -> u32 { handle.from_ffi_handle().unique_identifier() } /// The gender of the Pokemon. #[no_mangle] extern "C" fn pokemon_gender(handle: FFIHandle) -> Gender { handle.from_ffi_handle().gender() } /// The coloring of the Pokemon. #[no_mangle] extern "C" fn pokemon_coloring(handle: FFIHandle) -> u8 { handle.from_ffi_handle().coloring() } /// Gets the held item of a Pokemon #[no_mangle] extern "C" fn pokemon_held_item(handle: FFIHandle) -> FFIHandle> { 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, 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, item: FFIHandle>, ) -> FFIHandle> { 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) -> FFIHandle> { 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) -> FFIResult { 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) -> u32 { handle.from_ffi_handle().current_health() } /// The max health of the Pokemon. #[no_mangle] extern "C" fn pokemon_max_health(handle: FFIHandle) -> u32 { handle.from_ffi_handle().max_health() } /// The current weight of the Pokemon. #[no_mangle] extern "C" fn pokemon_weight(handle: FFIHandle) -> f32 { handle.from_ffi_handle().weight() } /// The current height of the Pokemon. #[no_mangle] extern "C" fn pokemon_height(handle: FFIHandle) -> f32 { handle.from_ffi_handle().height() } /// An optional nickname of the Pokemon. #[no_mangle] extern "C" fn pokemon_nickname(handle: FFIHandle) -> FFIResult { 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 on the form is a hidden ability. #[no_mangle] extern "C" fn pokemon_real_ability_is_hidden(handle: FFIHandle) -> u8 { u8::from(handle.from_ffi_handle().real_ability().hidden) } /// The index of the actual ability on the form. #[no_mangle] extern "C" fn pokemon_real_ability_index(handle: FFIHandle) -> 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) -> usize { ptr.from_ffi_handle().types().len() } /// Gets a type of the Pokemon. #[no_mangle] extern "C" fn pokemon_types_get(ptr: FFIHandle, index: usize) -> FFIResult { 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, index: usize) -> FFIHandle> { 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) -> FFIHandle>> { 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) -> FFIHandle>> { FFIHandle::get_handle(handle.from_ffi_handle().boosted_stats().clone().into()) } /// Get the stat boosts for a specific stat. #[no_mangle] extern "C" fn pokemon_get_stat_boost(handle: FFIHandle, 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, stat: Statistic, diff_amount: i8, self_inflicted: u8, ) -> FFIResult { 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 a [individual value](https://bulbapedia.bulbagarden.net/wiki/Individual_values) of the Pokemon. #[no_mangle] extern "C" fn pokemon_get_individual_value(handle: FFIHandle, stat: Statistic) -> u8 { handle.from_ffi_handle().individual_values().get_stat(stat) } /// Modifies a [individual value](https://bulbapedia.bulbagarden.net/wiki/Individual_values) of the Pokemon. #[no_mangle] extern "C" fn pokemon_set_individual_value(handle: FFIHandle, stat: Statistic, value: u8) -> FFIResult<()> { handle.from_ffi_handle().individual_values().set_stat(stat, value); handle.from_ffi_handle().recalculate_flat_stats().into() } /// Gets a [effort value](https://bulbapedia.bulbagarden.net/wiki/Effort_values) of the Pokemon. #[no_mangle] extern "C" fn pokemon_get_effort_value(handle: FFIHandle, stat: Statistic) -> u8 { handle.from_ffi_handle().effort_values().get_stat(stat) } /// Modifies a [effort value](https://bulbapedia.bulbagarden.net/wiki/Effort_values) of the Pokemon. #[no_mangle] extern "C" fn pokemon_set_effort_value(handle: FFIHandle, 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) -> FFIHandle { 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) -> 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) -> 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) -> 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) -> FFIResult>> { 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) -> u8 { u8::from(handle.from_ffi_handle().allowed_experience_gain()) } /// The [nature](https://bulbapedia.bulbagarden.net/wiki/Nature) of the Pokemon. #[no_mangle] extern "C" fn pokemon_nature(handle: FFIHandle) -> FFIHandle> { 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) -> 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) -> 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, species: FFIHandle>, form: FFIHandle>, ) -> 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, form: FFIHandle>) -> 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) -> 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) -> 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) -> 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, 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, 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, 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) { handle.from_ffi_handle().clear_status() }