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; 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>, parties: *const FFIHandle>, 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 { 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) -> FFIHandle> { 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) -> usize { ptr.from_ffi_handle().parties().len() } /// Get a party in the battle. #[no_mangle] extern "C" fn battle_parties_get(ptr: FFIHandle, index: usize) -> FFIHandle> { 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) -> 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) -> 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) -> 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) -> usize { ptr.from_ffi_handle().sides().len() } /// Get a side in the battle. #[no_mangle] extern "C" fn battle_sides_get(ptr: FFIHandle, index: usize) -> FFIHandle { 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) -> FFIHandle> { 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) -> 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) -> 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) -> 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, 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) -> 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) -> 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, side: u8, index: u8) -> FFIHandle { 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, 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, choice: FFIHandle>) -> 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, choice: FFIHandle>) -> FFIResult { 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, 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) -> FFIResult<*mut c_char> { match ptr.from_ffi_handle().weather_name() { Ok(Some(w)) => match CString::new(w.str()) { Ok(s) => s.into_raw().into(), Err(e) => FFIResult::err(anyhow!("Failed to convert weather name to CString: {}", e)), }, Ok(None) => std::ptr::null_mut::().into(), Err(e) => FFIResult::err(e), } }