PkmnLib_rs/src/ffi/dynamic_data/models/battle.rs

217 lines
7.2 KiB
Rust

use crate::dynamic_data::{
Battle, BattleParty, BattleRandom, BattleResult, BattleSide, DynamicLibrary, Pokemon, TurnChoice,
};
use crate::ffi::dynamic_data::models::native_event_hook::NativeEventHook;
use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle};
use crate::ffi::{FFIResult, OwnedPtrString};
use anyhow::anyhow;
use std::ffi::{c_char, CStr, CString};
use std::ops::Deref;
use std::sync::Arc;
/// Initializes a new battle.
#[no_mangle]
extern "C" fn battle_new(
library: FFIHandle<Arc<dyn DynamicLibrary>>,
parties: *const FFIHandle<Arc<BattleParty>>,
parties_length: usize,
can_flee: u8,
number_of_sides: u8,
pokemon_per_side: u8,
// NOTE: Split into two due to u128 not being ABI safe: https://github.com/rust-lang/rust/issues/54341
random_seed_1: u64,
random_seed_2: u64,
) -> FFIHandle<Battle> {
let parties = unsafe {
std::slice::from_raw_parts(parties, parties_length)
.iter()
.map(|x| x.from_ffi_handle())
.collect()
};
let random_seed = if cfg!(target_endian = "big") {
((random_seed_1 as u128) << 64) + (random_seed_2 as u128)
} else {
(random_seed_1 as u128) + ((random_seed_2 as u128) << 64)
};
let random_seed = if random_seed == 0 { None } else { Some(random_seed) };
FFIHandle::get_handle(
Battle::new(
library.from_ffi_handle(),
parties,
can_flee == 1,
number_of_sides,
pokemon_per_side,
random_seed,
)
.into(),
)
}
/// The library the battle uses for handling.
#[no_mangle]
extern "C" fn battle_library(ptr: FFIHandle<Battle>) -> FFIHandle<Arc<dyn DynamicLibrary>> {
FFIHandle::get_handle(ptr.from_ffi_handle().library().clone().into())
}
/// The length of the list of all different parties in the battle.
#[no_mangle]
extern "C" fn battle_parties_length(ptr: FFIHandle<Battle>) -> usize {
ptr.from_ffi_handle().parties().len()
}
/// Get a party in the battle.
#[no_mangle]
extern "C" fn battle_parties_get(ptr: FFIHandle<Battle>, index: usize) -> FFIHandle<Arc<BattleParty>> {
if let Some(v) = ptr.from_ffi_handle().parties().get(index) {
FFIHandle::get_handle(v.clone().into())
} else {
FFIHandle::none()
}
}
/// Whether or not Pokemon can flee from the battle.
#[no_mangle]
extern "C" fn battle_can_flee(ptr: FFIHandle<Battle>) -> u8 {
u8::from(ptr.from_ffi_handle().can_flee())
}
/// The number of sides in the battle. Typically 2.
#[no_mangle]
extern "C" fn battle_number_of_sides(ptr: FFIHandle<Battle>) -> u8 {
ptr.from_ffi_handle().number_of_sides()
}
/// The number of Pokemon that can be on each side.
#[no_mangle]
extern "C" fn battle_pokemon_per_side(ptr: FFIHandle<Battle>) -> u8 {
ptr.from_ffi_handle().pokemon_per_side()
}
/// The length of the list of all different sides in the battle.
#[no_mangle]
extern "C" fn battle_sides_length(ptr: FFIHandle<Battle>) -> usize {
ptr.from_ffi_handle().sides().len()
}
/// Get a side in the battle.
#[no_mangle]
extern "C" fn battle_sides_get(ptr: FFIHandle<Battle>, index: usize) -> FFIHandle<BattleSide> {
if let Some(v) = ptr.from_ffi_handle().sides().get(index) {
FFIHandle::get_handle(v.clone().into())
} else {
FFIHandle::none()
}
}
/// The RNG used for the battle.
#[no_mangle]
extern "C" fn battle_random(ptr: FFIHandle<Battle>) -> FFIHandle<Arc<BattleRandom>> {
FFIHandle::get_handle(ptr.from_ffi_handle().random().clone().into())
}
/// Whether or not the battle has ended.
#[no_mangle]
extern "C" fn battle_has_ended(ptr: FFIHandle<Battle>) -> u8 {
u8::from(ptr.from_ffi_handle().has_ended())
}
/// Whether or not we have a conclusive winner
#[no_mangle]
extern "C" fn battle_has_conclusive_result(ptr: FFIHandle<Battle>) -> u8 {
u8::from(ptr.from_ffi_handle().result() != BattleResult::Inconclusive)
}
/// If we have a conclusive winner, the side that has won. If we don't have a conclusive winner, this
/// always returns 0.
#[no_mangle]
extern "C" fn battle_winning_side(ptr: FFIHandle<Battle>) -> u8 {
if let BattleResult::Conclusive(winner) = ptr.from_ffi_handle().result() {
winner
} else {
0
}
}
/// Register a function to be triggered when an event in a battle occurs.
#[no_mangle]
extern "C" fn battle_register_event_hook(ptr: FFIHandle<Battle>, f: NativeEventHook) {
ptr.from_ffi_handle().event_hook().register_listener(Box::new(f))
}
/// The index of the current turn. 0 until all choices
#[no_mangle]
extern "C" fn battle_current_turn(ptr: FFIHandle<Battle>) -> u32 {
ptr.from_ffi_handle().current_turn()
}
/// The time in nanoseconds the last turn took to run. Defaults to 0.
#[no_mangle]
extern "C" fn battle_last_turn_time(ptr: FFIHandle<Battle>) -> u64 {
ptr.from_ffi_handle().last_turn_time()
}
/// Get a Pokemon on the battlefield, on a specific side and an index on that side.
#[no_mangle]
extern "C" fn battle_get_pokemon(ptr: FFIHandle<Battle>, side: u8, index: u8) -> FFIHandle<Pokemon> {
if let Some(v) = ptr.from_ffi_handle().get_pokemon(side, index) {
FFIHandle::get_handle(v.into())
} else {
FFIHandle::none()
}
}
/// Returns whether a slot on the battlefield can still be filled. If no party is responsible
/// for that slot, or a party is responsible, but has no remaining Pokemon to throw out anymore,
/// this returns false.
#[no_mangle]
extern "C" fn battle_can_slot_be_filled(ptr: FFIHandle<Battle>, side: u8, index: u8) -> u8 {
u8::from(ptr.from_ffi_handle().can_slot_be_filled(side, index))
}
/// Checks whether a choice is actually possible.
#[no_mangle]
extern "C" fn battle_can_use(ptr: FFIHandle<Battle>, choice: FFIHandle<Arc<TurnChoice>>) -> u8 {
u8::from(ptr.from_ffi_handle().can_use(choice.from_ffi_handle().deref()))
}
/// Checks to see whether all Pokemon on the field have set their choices. If so, we then run
/// the turn.
#[no_mangle]
extern "C" fn battle_try_set_choice(ptr: FFIHandle<Battle>, choice: FFIHandle<Arc<TurnChoice>>) -> FFIResult<u8> {
let choice = choice.from_ffi_handle();
let result = ptr.from_ffi_handle().try_set_choice(choice);
match result {
Ok(b) => FFIResult::ok(u8::from(b)),
Err(e) => FFIResult::err(e),
}
}
/// Sets the current weather for the battle. If nullptr is passed, this clears the weather.
#[no_mangle]
extern "C" fn battle_set_weather(ptr: FFIHandle<Battle>, weather: *const c_char) -> FFIResult<()> {
if weather.is_null() {
ptr.from_ffi_handle().set_weather(None).into()
} else {
unsafe {
ptr.from_ffi_handle()
.set_weather(Some(CStr::from_ptr(weather).into()))
.into()
}
}
}
/// Gets the current weather of the battle. If no weather is present, this returns nullptr.
#[no_mangle]
extern "C" fn battle_weather_name(ptr: FFIHandle<Battle>) -> FFIResult<OwnedPtrString> {
match ptr.from_ffi_handle().weather_name() {
Ok(Some(w)) => match CString::new(w.str()) {
Ok(s) => OwnedPtrString(s.into_raw()).into(),
Err(e) => FFIResult::err(anyhow!("Failed to convert weather name to CString: {}", e)),
},
Ok(None) => OwnedPtrString(std::ptr::null_mut()).into(),
Err(e) => FFIResult::err(e),
}
}