Gen7ScriptsRs/pkmn_lib_interface/src/lib.rs

681 lines
18 KiB
Rust
Executable File

#![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<dyn Fn(ScriptCategory, &StringKey) -> Option<Box<dyn Script>>>;
static mut LOAD_SCRIPT_FN: Option<LoadScriptFnType> = 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<hashbrown::HashMap<*const dyn Script, u32>> = None;
static mut script_ptr_reverse_cache: Option<hashbrown::HashMap<u32, Box<dyn Script>>> = 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<u32, Box<dyn Script>> {
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<dyn Script>) -> 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<StringKey>) -> 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<dyn Script>) {
// 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<ScriptCapabilities> {
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<DynamicLibrary>,
parameters: VecExternRef<EffectParameter>,
) {
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<TurnChoice>,
) {
script.val().unwrap().on_before_turn(choice.not_null())
}
fn script_change_speed(
script:ScriptPtr,
choice: ExternRef<TurnChoice>,
speed: *mut u32,
) {
script.val().unwrap().change_speed(choice.not_null(), speed.as_mut().unwrap())
}
fn script_change_priority(
script: ScriptPtr,
choice: ExternRef<TurnChoice>,
priority: *mut i8,
) {
script.val().unwrap().change_priority(choice.not_null(), priority.as_mut().unwrap())
}
fn script_change_move(
script: ScriptPtr,
choice: ExternRef<TurnChoice>,
mv: *mut ExternRef<StringKey>,
) {
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<TurnChoice>,
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<ExecutingMove>,
out: *mut bool,
) {
script.val().unwrap().prevent_move(mv.not_null(), out.as_mut().unwrap());
}
fn script_fail_move(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
out: *mut bool,
) {
script.val().unwrap().fail_move(mv.not_null(), out.as_mut().unwrap());
}
fn script_stop_before_move(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
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<ExecutingMove>,
) {
script.val().unwrap().on_before_move(mv.not_null());
}
fn script_fail_incoming_move(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
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<ExecutingMove>,
target: ExternRef<Pokemon>,
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<ExecutingMove>,
target: ExternRef<Pokemon>,
) {
script.val().unwrap().on_move_miss(mv.not_null(), target.not_null());
}
fn script_change_move_type(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
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<ExecutingMove>,
target: ExternRef<Pokemon>,
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<ExecutingMove>,
target: ExternRef<Pokemon>,
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<ExecutingMove>,
target: ExternRef<Pokemon>,
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<ExecutingMove>,
target: ExternRef<Pokemon>,
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<ExecutingMove>,
target: ExternRef<Pokemon>,
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<ExecutingMove>,
target: ExternRef<Pokemon>,
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<ExecutingMove>,
target: ExternRef<Pokemon>,
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<ExecutingMove>,
target: ExternRef<Pokemon>,
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<ExecutingMove>,
target: ExternRef<Pokemon>,
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<ExecutingMove>,
target: ExternRef<Pokemon>,
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<ExecutingMove>,
target: ExternRef<Pokemon>,
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<ExecutingMove>,
target: ExternRef<Pokemon>,
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<ExecutingMove>,
target: ExternRef<Pokemon>,
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<ExecutingMove>,
target: ExternRef<Pokemon>,
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<ExecutingMove>,
target: ExternRef<Pokemon>,
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<ExecutingMove>,
target: ExternRef<Pokemon>,
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<ExecutingMove>,
target: ExternRef<Pokemon>,
hit: u8,
) {
script.val().unwrap().on_incoming_hit(mv.not_null(), target.not_null(), hit);
}
fn script_on_opponent_faints(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
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<Pokemon>,
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<ExecutingMove>,
target: ExternRef<Pokemon>,
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<ExecutingMove>,
target: ExternRef<Pokemon>,
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<ExecutingMove>,
target: ExternRef<Pokemon>,
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<ExecutingMove>,
target: ExternRef<Pokemon>,
hit: u8,
) {
script.val().unwrap().on_secondary_effect(mv.not_null(), target.not_null(), hit);
}
fn script_on_after_hits(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
) {
script.val().unwrap().on_after_hits(mv.not_null(), target.not_null());
}
fn script_prevent_self_switch(
script: ScriptPtr,
choice: ExternRef<TurnChoice>,
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<TurnChoice>,
out: *mut bool,
) {
script.val().unwrap().prevent_opponent_switch(choice.not_null(), out.as_mut().unwrap());
}
fn script_on_fail(
script: ScriptPtr,
pokemon: ExternRef<Pokemon>,
) {
script.val().unwrap().on_fail(pokemon.not_null());
}
fn script_on_opponent_fail(
script: ScriptPtr,
pokemon: ExternRef<Pokemon>,
) {
script.val().unwrap().on_opponent_fail(pokemon.not_null());
}
fn script_prevent_self_run_away(
script: ScriptPtr,
choice: ExternRef<TurnChoice>,
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<TurnChoice>,
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<Pokemon>,
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<Pokemon>,
source: DamageSource,
) {
script.val().unwrap().on_faint(pokemon.not_null(), source);
}
fn script_on_switch_in(
script: ScriptPtr,
pokemon: ExternRef<Pokemon>,
) {
script.val().unwrap().on_switch_in(pokemon.not_null());
}
fn script_on_after_held_item_consume(
script: ScriptPtr,
pokemon: ExternRef<Pokemon>,
item: ExternRef<Item>
) {
script.val().unwrap().on_after_held_item_consume(pokemon.not_null(), &item.not_null());
}
fn script_change_experience_gained(
script: ScriptPtr,
fainted_pokemon: ExternRef<Pokemon>,
winning_pokemon: ExternRef<Pokemon>,
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<Pokemon>,
winning_pokemon: ExternRef<Pokemon>,
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<Battle>,
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<Pokemon>,
pokeball: ExternRef<Item>,
out: *mut u8,
) {
script.val().unwrap().change_capture_rate_bonus(target.not_null(), pokeball.not_null(), out.as_mut().unwrap());
}
}