use crate::dynamic_data::{ItemScript, Pokemon}; use crate::script_implementations::wasm::export_registry::WasmVoidResultExtension; use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnvironmentData; use crate::static_data::Parameter; use crate::StringKey; use std::sync::Arc; /// A web assembly implementation of the item script. pub struct WebAssemblyItemScript { /// The unique identifier of the script. name: StringKey, /// Pointer inside WebAssembly memory where the data is for this script. self_ptr: u32, /// Cached value for the is_usable function. is_usable: Option, /// Cached value for the requires_target function. requires_target: Option, /// Cached value for the is_holdable function. is_holdable: Option, /// The global runtime environment data. environment: Arc, } impl WebAssemblyItemScript { /// Create a new instance of the script. pub fn new(name: StringKey, self_ptr: u32, environment: Arc) -> Self { Self { name, self_ptr, is_usable: None, requires_target: None, is_holdable: None, environment, } } /// Get the name of the script. pub fn name(&self) -> &StringKey { &self.name } } /// Util macro to reduce function call verbosity. macro_rules! call_func { ($func:ident, $env:ident, $self:ident $(, $par_name:expr)*) => { match $func.call(&mut $env.store_mut(), $self.self_ptr $(, $par_name)*) { Ok(res) => res.into_result_env($env)?, Err(e) => return Err(e.into()), }; } } /// Util macro to reduce extern ref instantiation verbosity. macro_rules! ex_ref { ($env:ident, $value:expr) => { ExternRef::new($env.as_ref(), $value.into()) }; } impl ItemScript for WebAssemblyItemScript { fn on_initialize(&self, pars: Vec>) -> anyhow_ext::Result<()> { let env = &self.environment; if let Some(func) = env.script_function_cache().item_on_initialize(env) { let pars = pars .into_iter() .map(|p| ExternRef::::new(env, (&p).into()).index() as u32) .collect::>(); let wasm_ptr = env.copy_value_vec_to_wasm(&pars)?; let r: u64 = unsafe { std::mem::transmute((wasm_ptr, pars.len() as u32)) }; call_func!(func, env, self, r); } Ok(()) } fn is_item_usable(&self) -> anyhow_ext::Result { if let Some(is_usable) = self.is_usable { return Ok(is_usable); } let env = &self.environment; if let Some(func) = env.script_function_cache().item_is_usable(env) { let res_ptr = env.allocate_temp::(false); call_func!(func, env, self, res_ptr.wasm_ptr); let res = *res_ptr.value(); let self_mut = unsafe { (self as *const Self as *mut Self).as_mut().unwrap_unchecked() }; self_mut.is_usable = Some(res); return Ok(res); } Ok(true) } fn requires_target(&self) -> anyhow_ext::Result { if let Some(requires_target) = self.requires_target { return Ok(requires_target); } let env = &self.environment; if let Some(func) = env.script_function_cache().item_requires_target(env) { let res_ptr = env.allocate_temp::(false); call_func!(func, env, self, res_ptr.wasm_ptr); let res = *res_ptr.value(); let self_mut = unsafe { (self as *const Self as *mut Self).as_mut().unwrap_unchecked() }; self_mut.requires_target = Some(res); return Ok(res); } Ok(false) } fn is_target_valid(&self, target: &Pokemon) -> anyhow::Result { let env = &self.environment; if let Some(func) = env.script_function_cache().item_is_target_valid(env) { let target = ex_ref!(env, target); let res_ptr = env.allocate_temp::(false); call_func!(func, env, self, target, res_ptr.wasm_ptr); return Ok(*res_ptr.value()); } Ok(false) } fn is_holdable(&self) -> anyhow::Result { if let Some(is_holdable) = self.is_holdable { return Ok(is_holdable); } let env = &self.environment; if let Some(func) = env.script_function_cache().item_is_holdable(env) { let res_ptr = env.allocate_temp::(false); call_func!(func, env, self, res_ptr.wasm_ptr); let res = *res_ptr.value(); let self_mut = unsafe { (self as *const Self as *mut Self).as_mut().unwrap_unchecked() }; self_mut.is_holdable = Some(res); return Ok(res); } Ok(false) } fn can_target_hold(&self, target: &Pokemon) -> anyhow::Result { let env = &self.environment; if let Some(func) = env.script_function_cache().item_can_target_hold(env) { let target = ex_ref!(env, target); let res_ptr = env.allocate_temp::(false); call_func!(func, env, self, target, res_ptr.wasm_ptr); return Ok(*res_ptr.value()); } Ok(false) } fn on_use(&self) -> anyhow::Result<()> { let env = &self.environment; if let Some(func) = env.script_function_cache().item_on_use(env) { call_func!(func, env, self); } Ok(()) } fn on_use_with_target(&self, target: &Pokemon) -> anyhow::Result<()> { let env = &self.environment; if let Some(func) = env.script_function_cache().item_on_use_with_target(env) { let target = ex_ref!(env, target); call_func!(func, env, self, target); } Ok(()) } }