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::{ExternPointer, IdentifiablePointer, NativeResult, OwnedPtr}; use anyhow::anyhow; use std::ffi::{c_char, CStr, CString}; use std::sync::Arc; /// Initializes a new battle. #[no_mangle] extern "C" fn battle_new( library: ExternPointer>, parties: *const OwnedPtr, 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, ) -> IdentifiablePointer> { let parties = unsafe { std::slice::from_raw_parts(parties, parties_length) .iter() .map(|x| *Box::from_raw(*x)) .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) }; Battle::new( library.as_ref().clone(), 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: ExternPointer>) -> IdentifiablePointer> { ptr.as_ref().library().clone().into() } /// The length of the list of all different parties in the battle. #[no_mangle] extern "C" fn battle_parties_length(ptr: ExternPointer>) -> usize { ptr.as_ref().parties().len() } /// Get a party in the battle. #[no_mangle] extern "C" fn battle_parties_get(ptr: ExternPointer>, index: usize) -> IdentifiablePointer { if let Some(v) = ptr.as_ref().parties().get(index) { (v as *const BattleParty).into() } else { IdentifiablePointer::none() } } /// Whether or not Pokemon can flee from the battle. #[no_mangle] extern "C" fn battle_can_flee(ptr: ExternPointer>) -> u8 { u8::from(ptr.as_ref().can_flee()) } /// The number of sides in the battle. Typically 2. #[no_mangle] extern "C" fn battle_number_of_sides(ptr: ExternPointer>) -> u8 { ptr.as_ref().number_of_sides() } /// The number of Pokemon that can be on each side. #[no_mangle] extern "C" fn battle_pokemon_per_side(ptr: ExternPointer>) -> u8 { ptr.as_ref().pokemon_per_side() } /// The length of the list of all different sides in the battle. #[no_mangle] extern "C" fn battle_sides_length(ptr: ExternPointer>) -> usize { ptr.as_ref().sides().len() } /// Get a side in the battle. #[no_mangle] extern "C" fn battle_sides_get(ptr: ExternPointer>, index: usize) -> IdentifiablePointer { if let Some(v) = ptr.as_ref().sides().get(index) { (v as *const BattleSide).into() } else { IdentifiablePointer::none() } } /// The RNG used for the battle. #[no_mangle] extern "C" fn battle_random(ptr: ExternPointer>) -> IdentifiablePointer { (ptr.as_ref().random() as *const BattleRandom).into() } /// Whether or not the battle has ended. #[no_mangle] extern "C" fn battle_has_ended(ptr: ExternPointer>) -> u8 { u8::from(ptr.as_ref().has_ended()) } /// Whether or not we have a conclusive winner #[no_mangle] extern "C" fn battle_has_conclusive_result(ptr: ExternPointer>) -> u8 { u8::from(ptr.as_ref().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: ExternPointer>) -> u8 { if let BattleResult::Conclusive(winner) = ptr.as_ref().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: ExternPointer>, f: NativeEventHook) { ptr.as_ref().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: ExternPointer>) -> u32 { ptr.as_ref().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: ExternPointer>) -> u64 { ptr.as_ref().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: ExternPointer>, side: u8, index: u8, ) -> IdentifiablePointer> { if let Some(v) = ptr.as_ref().get_pokemon(side, index) { v.into() } else { IdentifiablePointer::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: ExternPointer>, side: u8, index: u8) -> u8 { u8::from(ptr.as_ref().can_slot_be_filled(side, index)) } /// Checks whether a choice is actually possible. #[no_mangle] extern "C" fn battle_can_use(ptr: ExternPointer>, choice: ExternPointer) -> u8 { u8::from(ptr.as_ref().can_use(choice.as_ref())) } /// 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: ExternPointer>, choice: OwnedPtr) -> NativeResult { let choice = unsafe { choice.read() }; let result = ptr.as_ref().try_set_choice(choice); match result { Ok(b) => NativeResult::ok(u8::from(b)), Err(e) => NativeResult::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: ExternPointer>, weather: *const c_char) -> NativeResult<()> { if weather.is_null() { ptr.as_ref().set_weather(None).into() } else { unsafe { ptr.as_ref().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: ExternPointer>) -> NativeResult<*mut c_char> { match ptr.as_ref().weather_name() { Ok(Some(w)) => match CString::new(w.str()) { Ok(s) => s.into_raw().into(), Err(e) => NativeResult::err(anyhow!("Failed to convert weather name to CString: {}", e)), }, Ok(None) => std::ptr::null_mut::().into(), Err(e) => NativeResult::err(e), } }