#![feature(core_panic)] #![feature(alloc_error_handler)] #![feature(fn_traits)] #![feature(const_for)] #![feature(const_mut_refs)] #![feature(inline_const)] #![feature(inline_const_pat)] #![feature(repr128)] #![feature(downcast_unchecked)] #![feature(panic_info_message)] #![feature(const_btree_new)] #![feature(wasm_abi)] #![feature(thread_local)] #![feature(build_hasher_simple_hash_one)] #![feature(adt_const_params)] #![cfg_attr(not(feature = "mock_data"), no_std)] #![allow(incomplete_features)] extern crate alloc; extern crate core; extern crate wee_alloc; #[global_allocator] static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; use crate::app_interface::list::ImmutableList; use crate::app_interface::Statistic; use crate::app_interface::{ Battle, DamageSource, DynamicLibrary, EffectParameter, ExecutingMove, Item, StringKey, TurnChoice, }; use crate::app_interface::{Pokemon, TypeIdentifier}; pub(crate) use crate::handling::extern_ref::*; use crate::handling::ffi_array::FFIArray; #[cfg(not(feature = "mock_data"))] use crate::handling::ScriptCapabilities; use crate::handling::{Script, ScriptCategory}; use alloc::boxed::Box; use core::sync::atomic::{AtomicU32, Ordering}; use cstr_core::{c_char, CString}; use hashbrown::HashMap; #[macro_use] #[allow(dead_code)] pub mod app_interface; pub mod handling; pub mod utils; pub type LoadScriptFnType = Box Option>>; static mut LOAD_SCRIPT_FN: Option = None; pub fn set_load_script_fn(f: LoadScriptFnType) { unsafe { LOAD_SCRIPT_FN = Some(f); } } macro_rules! exported_functions { ( $( fn $func_name:ident($($par_name:ident: $par_type:ty),*$(,)?) $(-> $return_type:ty)? $func_body:block )* ) => { $( #[no_mangle] #[cfg(not(feature = "mock_data"))] unsafe extern "wasm" fn $func_name( $( $par_name: $par_type, )* ) $(-> $return_type)* $func_body )* }; } static mut script_ptr_cache: Option> = None; static mut script_ptr_reverse_cache: Option>> = None; static mut script_index_counter: AtomicU32 = AtomicU32::new(1); #[repr(C)] pub struct ScriptPtr { index: u32, } impl ScriptPtr { fn get_cache<'a>() -> &'a mut HashMap<*const dyn Script, u32> { unsafe { if let None = script_ptr_cache { script_ptr_cache = Some(hashbrown::HashMap::new()); } let cache = script_ptr_cache.as_mut().unwrap(); cache } } fn get_reverse_cache<'a>() -> &'a mut HashMap> { unsafe { if let None = script_ptr_reverse_cache { script_ptr_reverse_cache = Some(hashbrown::HashMap::new()); } let cache = script_ptr_reverse_cache.as_mut().unwrap(); cache } } pub fn from_existing(ptr: &dyn Script) -> Self { let cache = Self::get_cache(); let index = *cache.get(&(ptr as *const dyn Script)).unwrap(); Self { index } } pub fn new(ptr: Box) -> Self { unsafe { let cache = Self::get_cache(); let mut index = cache.get(&(ptr.as_ref() as *const dyn Script)).cloned(); if index.is_none() { index = Some(script_index_counter.fetch_add(1, Ordering::SeqCst)); let reverse_cache = Self::get_reverse_cache(); reverse_cache.insert(index.unwrap(), ptr); let v = reverse_cache.get(&index.unwrap()).unwrap(); cache.insert(v.as_ref(), index.unwrap()); } Self { index: index.unwrap(), } } } pub fn null() -> Self { Self { index: 0 } } pub fn val<'a, 'b>(&'a self) -> Option<&'b dyn Script> { unsafe { if self.index == 0 { return None; } let cache = Self::get_reverse_cache(); if let Some(c) = cache.get(&self.index) { Some(c.as_ref()) } else { None } } } } exported_functions! { fn load_script(category: ScriptCategory, name: ExternRef) -> ScriptPtr { let name_c = StringKey::new(name); let boxed_script = unsafe { &LOAD_SCRIPT_FN }.as_ref().unwrap()(category, &name_c); if boxed_script.is_none() { return ScriptPtr::null(); } let b = boxed_script.unwrap(); ScriptPtr::new(b) } fn destroy_script(script: *mut Box) { // By turning it from a raw pointer back into a Box with from_raw, we give ownership back to rust. // This lets Rust do the cleanup. let boxed_script = Box::from_raw(script); drop(boxed_script); } fn script_get_name(script: ScriptPtr) -> *mut c_char { let c = script.val().unwrap().get_name(); CString::new(c).unwrap().into_raw() } fn get_script_capabilities(script: ScriptPtr) -> FFIArray { let c = script.val().unwrap().get_capabilities(); FFIArray::new(c) } fn script_stack(script: ScriptPtr) { script.val().unwrap().stack(); } fn script_on_remove(script: ScriptPtr) { script.val().unwrap().on_remove(); } fn script_on_initialize( script: ScriptPtr, library: ExternRef, parameters: VecExternRef, ) { let parameters = ImmutableList::from_ref(parameters); script.val().unwrap().on_initialize(&library.not_null(), Some(parameters)); } fn script_on_before_turn( script: ScriptPtr, choice: ExternRef, ) { script.val().unwrap().on_before_turn(choice.not_null()) } fn script_change_speed( script:ScriptPtr, choice: ExternRef, speed: *mut u32, ) { script.val().unwrap().change_speed(choice.not_null(), speed.as_mut().unwrap()) } fn script_change_priority( script: ScriptPtr, choice: ExternRef, priority: *mut i8, ) { script.val().unwrap().change_priority(choice.not_null(), priority.as_mut().unwrap()) } fn script_change_move( script: ScriptPtr, choice: ExternRef, mv: *mut ExternRef, ) { let old = mv.as_ref().unwrap().not_null(); let mut new = old.clone(); script.val().unwrap().change_move(choice.not_null(), &mut new); if old != new { *mv = new.ptr(); } } fn script_change_number_of_hits( script: ScriptPtr, choice: ExternRef, out: *mut u8, ) { script.val().unwrap().change_number_of_hits(choice.not_null(), out.as_mut().unwrap()); } fn script_prevent_move( script: ScriptPtr, mv: ExternRef, out: *mut bool, ) { script.val().unwrap().prevent_move(mv.not_null(), out.as_mut().unwrap()); } fn script_fail_move( script: ScriptPtr, mv: ExternRef, out: *mut bool, ) { script.val().unwrap().fail_move(mv.not_null(), out.as_mut().unwrap()); } fn script_stop_before_move( script: ScriptPtr, mv: ExternRef, out: *mut bool, ) { script.val().unwrap().stop_before_move(mv.not_null(), out.as_mut().unwrap()); } fn script_on_before_move( script: ScriptPtr, mv: ExternRef, ) { script.val().unwrap().on_before_move(mv.not_null()); } fn script_fail_incoming_move( script: ScriptPtr, mv: ExternRef, target: ExternRef, out: *mut bool, ) { script.val().unwrap().fail_incoming_move(mv.not_null(), target.not_null(), out.as_mut().unwrap()); } fn script_is_invulnerable( script: ScriptPtr, mv: ExternRef, target: ExternRef, out: *mut bool, ) { script.val().unwrap().is_invulnerable(mv.not_null(), target.not_null(), out.as_mut().unwrap()); } fn script_on_move_miss( script: ScriptPtr, mv: ExternRef, target: ExternRef, ) { script.val().unwrap().on_move_miss(mv.not_null(), target.not_null()); } fn script_change_move_type( script: ScriptPtr, mv: ExternRef, target: ExternRef, hit: u8, out: *mut TypeIdentifier, ) { script.val().unwrap().change_move_type(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap()); } fn script_change_effectiveness( script: ScriptPtr, mv: ExternRef, target: ExternRef, hit: u8, out: *mut f32, ) { script.val().unwrap().change_effectiveness(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap()); } fn script_block_critical( script: ScriptPtr, mv: ExternRef, target: ExternRef, hit: u8, out: *mut bool, ) { script.val().unwrap().block_critical(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap()); } fn script_block_incoming_critical( script: ScriptPtr, mv: ExternRef, target: ExternRef, hit: u8, out: *mut bool, ) { script.val().unwrap().block_incoming_critical(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap()); } fn script_change_accuracy( script: ScriptPtr, mv: ExternRef, target: ExternRef, hit: u8, out: *mut u8, ) { script.val().unwrap().change_accuracy(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap()); } fn script_change_critical_stage( script: ScriptPtr, mv: ExternRef, target: ExternRef, hit: u8, out: *mut u8, ) { script.val().unwrap().change_critical_stage(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap()); } fn script_change_critical_modifier( script: ScriptPtr, mv: ExternRef, target: ExternRef, hit: u8, out: *mut f32, ) { script.val().unwrap().change_critical_modifier(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap()); } fn script_change_stab_modifier( script: ScriptPtr, mv: ExternRef, target: ExternRef, hit: u8, out: *mut f32, ) { script.val().unwrap().change_stab_modifier(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap()); } fn script_change_base_power( script: ScriptPtr, mv: ExternRef, target: ExternRef, hit: u8, out: *mut u8, ) { script.val().unwrap().change_base_power(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap()); } fn script_bypass_defensive_stat_boost( script: ScriptPtr, mv: ExternRef, target: ExternRef, hit: u8, out: *mut bool, ) { script.val().unwrap().bypass_defensive_stat_boost(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap()); } fn script_bypass_offensive_stat_boost( script: ScriptPtr, mv: ExternRef, target: ExternRef, hit: u8, out: *mut bool, ) { script.val().unwrap().bypass_offensive_stat_boost(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap()); } fn script_change_defensive_stat_value( script: ScriptPtr, mv: ExternRef, target: ExternRef, hit: u8, out: *mut u32, ) { script.val().unwrap().change_defensive_stat_value(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap()); } fn script_change_offensive_stat_value( script: ScriptPtr, mv: ExternRef, target: ExternRef, hit: u8, out: *mut u32, ) { script.val().unwrap().change_offensive_stat_value(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap()); } fn script_change_damage_stat_modifier( script: ScriptPtr, mv: ExternRef, target: ExternRef, hit: u8, out: *mut f32, ) { script.val().unwrap().change_damage_stat_modifier(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap()); } fn script_change_damage_modifier( script: ScriptPtr, mv: ExternRef, target: ExternRef, hit: u8, out: *mut f32, ) { script.val().unwrap().change_damage_modifier(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap()); } fn script_change_damage( script: ScriptPtr, mv: ExternRef, target: ExternRef, hit: u8, out: *mut u32, ) { script.val().unwrap().change_damage(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap()); } fn script_change_incoming_damage( script: ScriptPtr, mv: ExternRef, target: ExternRef, hit: u8, out: *mut u32, ) { script.val().unwrap().change_incoming_damage(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap()); } fn script_on_incoming_hit( script: ScriptPtr, mv: ExternRef, target: ExternRef, hit: u8, ) { script.val().unwrap().on_incoming_hit(mv.not_null(), target.not_null(), hit); } fn script_on_opponent_faints( script: ScriptPtr, mv: ExternRef, target: ExternRef, hit: u8, ) { script.val().unwrap().on_opponent_faints(mv.not_null(), target.not_null(), hit); } fn script_prevent_stat_boost_change( script: ScriptPtr, target: ExternRef, stat: Statistic, amount: i8, self_inflicted: u8, out: *mut bool, ) { script.val().unwrap().prevent_stat_boost_change(target.not_null(), stat, amount, self_inflicted == 1, out.as_mut().unwrap()); } fn script_prevent_secondary_effect( script: ScriptPtr, mv: ExternRef, target: ExternRef, hit: u8, out: *mut bool, ) { script.val().unwrap().prevent_secondary_effect(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap()); } fn script_change_effect_chance( script: ScriptPtr, mv: ExternRef, target: ExternRef, hit: u8, out: *mut f32, ) { script.val().unwrap().change_effect_chance(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap()); } fn script_change_incoming_effect_chance( script: ScriptPtr, mv: ExternRef, target: ExternRef, hit: u8, out: *mut f32, ) { script.val().unwrap().change_incoming_effect_chance(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap()); } fn script_on_secondary_effect( script: ScriptPtr, mv: ExternRef, target: ExternRef, hit: u8, ) { script.val().unwrap().on_secondary_effect(mv.not_null(), target.not_null(), hit); } fn script_on_after_hits( script: ScriptPtr, mv: ExternRef, target: ExternRef, ) { script.val().unwrap().on_after_hits(mv.not_null(), target.not_null()); } fn script_prevent_self_switch( script: ScriptPtr, choice: ExternRef, out: *mut bool, ) { script.val().unwrap().prevent_self_switch(choice.not_null(), out.as_mut().unwrap()); } fn script_prevent_opponent_switch( script: ScriptPtr, choice: ExternRef, out: *mut bool, ) { script.val().unwrap().prevent_opponent_switch(choice.not_null(), out.as_mut().unwrap()); } fn script_on_fail( script: ScriptPtr, pokemon: ExternRef, ) { script.val().unwrap().on_fail(pokemon.not_null()); } fn script_on_opponent_fail( script: ScriptPtr, pokemon: ExternRef, ) { script.val().unwrap().on_opponent_fail(pokemon.not_null()); } fn script_prevent_self_run_away( script: ScriptPtr, choice: ExternRef, out: *mut bool, ) { script.val().unwrap().prevent_self_run_away(choice.not_null(), out.as_mut().unwrap()); } fn script_prevent_opponent_run_away( script: ScriptPtr, choice: ExternRef, out: *mut bool, ) { script.val().unwrap().prevent_opponent_run_away(choice.not_null(), out.as_mut().unwrap()); } fn script_on_end_turn( script: ScriptPtr, ) { script.val().unwrap().on_end_turn(); } fn script_on_damage( script: ScriptPtr, pokemon: ExternRef, source: DamageSource, old_health: u32, new_health: u32, ) { script.val().unwrap().on_damage(pokemon.not_null(), source, old_health, new_health); } fn script_on_faint( script: ScriptPtr, pokemon: ExternRef, source: DamageSource, ) { script.val().unwrap().on_faint(pokemon.not_null(), source); } fn script_on_switch_in( script: ScriptPtr, pokemon: ExternRef, ) { script.val().unwrap().on_switch_in(pokemon.not_null()); } fn script_on_after_held_item_consume( script: ScriptPtr, pokemon: ExternRef, item: ExternRef ) { script.val().unwrap().on_after_held_item_consume(pokemon.not_null(), &item.not_null()); } fn script_change_experience_gained( script: ScriptPtr, fainted_pokemon: ExternRef, winning_pokemon: ExternRef, out: *mut u32 ) { script.val().unwrap().change_experience_gained(fainted_pokemon.not_null(), winning_pokemon.not_null(), out.as_mut().unwrap()); } fn script_share_experience( script: ScriptPtr, fainted_pokemon: ExternRef, winning_pokemon: ExternRef, out: *mut bool, ) { script.val().unwrap().share_experience(fainted_pokemon.not_null(), winning_pokemon.not_null(), out.as_mut().unwrap()); } fn script_block_weather( script: ScriptPtr, battle: ExternRef, out: *mut bool, ) { script.val().unwrap().block_weather(battle.not_null(), out.as_mut().unwrap()); } fn script_change_capture_rate_bonus( script: ScriptPtr, target: ExternRef, pokeball: ExternRef, out: *mut u8, ) { script.val().unwrap().change_capture_rate_bonus(target.not_null(), pokeball.not_null(), out.as_mut().unwrap()); } }