2022-10-15 15:21:24 +00:00
|
|
|
use crate::dynamic_data::{
|
|
|
|
Battle, BattleParty, BattleRandom, BattleResult, BattleSide, DynamicLibrary, Pokemon, TurnChoice,
|
|
|
|
};
|
|
|
|
use crate::ffi::dynamic_data::models::native_event_hook::NativeEventHook;
|
2023-04-15 12:34:42 +00:00
|
|
|
use crate::ffi::{ExternPointer, IdentifiablePointer, NativeResult, OwnedPtr};
|
2023-04-16 17:57:21 +00:00
|
|
|
use anyhow::anyhow;
|
2022-10-15 15:21:24 +00:00
|
|
|
use std::ffi::{c_char, CStr, CString};
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
|
|
/// Initializes a new battle.
|
|
|
|
#[no_mangle]
|
|
|
|
extern "C" fn battle_new(
|
2022-12-24 11:00:50 +00:00
|
|
|
library: ExternPointer<Arc<dyn DynamicLibrary>>,
|
2022-10-15 15:21:24 +00:00
|
|
|
parties: *const OwnedPtr<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,
|
|
|
|
) -> IdentifiablePointer<Battle> {
|
|
|
|
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) };
|
|
|
|
|
|
|
|
Box::new(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]
|
2022-12-24 11:00:50 +00:00
|
|
|
extern "C" fn battle_library(ptr: ExternPointer<Arc<Battle>>) -> IdentifiablePointer<Arc<dyn DynamicLibrary>> {
|
2022-10-15 15:21:24 +00:00
|
|
|
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<Arc<Battle>>) -> usize {
|
|
|
|
ptr.as_ref().parties().len()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a party in the battle.
|
|
|
|
#[no_mangle]
|
|
|
|
extern "C" fn battle_parties_get(ptr: ExternPointer<Arc<Battle>>, index: usize) -> IdentifiablePointer<BattleParty> {
|
|
|
|
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<Arc<Battle>>) -> 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<Arc<Battle>>) -> 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<Arc<Battle>>) -> 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<Arc<Battle>>) -> usize {
|
|
|
|
ptr.as_ref().sides().len()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a side in the battle.
|
|
|
|
#[no_mangle]
|
|
|
|
extern "C" fn battle_sides_get(ptr: ExternPointer<Arc<Battle>>, index: usize) -> IdentifiablePointer<BattleSide> {
|
|
|
|
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<Arc<Battle>>) -> IdentifiablePointer<BattleRandom> {
|
|
|
|
(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<Arc<Battle>>) -> 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<Arc<Battle>>) -> 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<Arc<Battle>>) -> 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<Arc<Battle>>, 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<Arc<Battle>>) -> 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<Arc<Battle>>) -> 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<Arc<Battle>>,
|
|
|
|
side: u8,
|
|
|
|
index: u8,
|
|
|
|
) -> IdentifiablePointer<Arc<Pokemon>> {
|
|
|
|
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<Arc<Battle>>, 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<Arc<Battle>>, choice: ExternPointer<TurnChoice>) -> 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]
|
2023-04-16 17:57:21 +00:00
|
|
|
extern "C" fn battle_try_set_choice(ptr: ExternPointer<Arc<Battle>>, choice: OwnedPtr<TurnChoice>) -> NativeResult<u8> {
|
2022-10-15 15:21:24 +00:00
|
|
|
let choice = unsafe { choice.read() };
|
|
|
|
let result = ptr.as_ref().try_set_choice(choice);
|
|
|
|
match result {
|
2023-04-16 17:57:21 +00:00
|
|
|
Ok(b) => NativeResult::ok(u8::from(b)),
|
|
|
|
Err(e) => NativeResult::err(e),
|
2022-10-15 15:21:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Sets the current weather for the battle. If nullptr is passed, this clears the weather.
|
|
|
|
#[no_mangle]
|
2023-04-15 12:34:42 +00:00
|
|
|
extern "C" fn battle_set_weather(ptr: ExternPointer<Arc<Battle>>, weather: *const c_char) -> NativeResult<()> {
|
2022-10-15 15:21:24 +00:00
|
|
|
if weather.is_null() {
|
2023-04-15 12:34:42 +00:00
|
|
|
ptr.as_ref().set_weather(None).into()
|
2022-10-15 15:21:24 +00:00
|
|
|
} else {
|
2023-04-15 12:34:42 +00:00
|
|
|
unsafe { ptr.as_ref().set_weather(Some(CStr::from_ptr(weather).into())).into() }
|
2022-10-15 15:21:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the current weather of the battle. If no weather is present, this returns nullptr.
|
|
|
|
#[no_mangle]
|
2023-04-15 12:34:42 +00:00
|
|
|
extern "C" fn battle_weather_name(ptr: ExternPointer<Arc<Battle>>) -> NativeResult<*mut c_char> {
|
|
|
|
match ptr.as_ref().weather_name() {
|
2023-04-16 17:57:21 +00:00
|
|
|
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)),
|
|
|
|
},
|
2023-04-15 12:34:42 +00:00
|
|
|
Ok(None) => std::ptr::null_mut::<c_char>().into(),
|
|
|
|
Err(e) => NativeResult::err(e),
|
2022-10-15 15:21:24 +00:00
|
|
|
}
|
|
|
|
}
|